| File: parent\ILCompiler.Reflection.ReadyToRun\PEReaderExtensions.cs | Web Access |
| Project: ILCompiler.ReadyToRun.csproj (ILCompiler.ReadyToRun) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.Collections.Immutable; using System.IO; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Text; using Internal.Runtime; using Internal.ReadyToRunConstants; namespace ILCompiler.Reflection.ReadyToRun { public class PEExportTable { public readonly bool HasExportTable; public readonly int ExportTableHeaderLength; private readonly Dictionary<string, int> _namedExportRva; private readonly Dictionary<int, int> _ordinalRva; private PEExportTable(PEReader peReader) { _namedExportRva = new Dictionary<string, int>(); _ordinalRva = new Dictionary<int, int>(); DirectoryEntry exportTable = peReader.PEHeaders.PEHeader.ExportTableDirectory; if ((exportTable.Size == 0) || (exportTable.RelativeVirtualAddress == 0)) return; HasExportTable = true; PEMemoryBlock peImage = peReader.GetEntireImage(); BlobReader exportTableHeader = peImage.GetReader(peReader.GetOffset(exportTable.RelativeVirtualAddress), exportTable.Size); if (exportTableHeader.Length == 0) { return; } ExportTableHeaderLength = exportTableHeader.Length; // +0x00: reserved exportTableHeader.ReadUInt32(); // +0x04: TODO: time/date stamp exportTableHeader.ReadUInt32(); // +0x08: major version exportTableHeader.ReadUInt16(); // +0x0A: minor version exportTableHeader.ReadUInt16(); // +0x0C: DLL name RVA exportTableHeader.ReadUInt32(); // +0x10: ordinal base int minOrdinal = exportTableHeader.ReadInt32(); // +0x14: number of entries in the address table int addressEntryCount = exportTableHeader.ReadInt32(); // +0x18: number of name pointers int namePointerCount = exportTableHeader.ReadInt32(); // +0x1C: export address table RVA int addressTableRVA = exportTableHeader.ReadInt32(); // +0x20: name pointer RVA int namePointerRVA = exportTableHeader.ReadInt32(); // +0x24: ordinal table RVA int ordinalTableRVA = exportTableHeader.ReadInt32(); int[] addressTable = new int[addressEntryCount]; BlobReader addressTableReader = peImage.GetReader(peReader.GetOffset(addressTableRVA), sizeof(int) * addressEntryCount); for (int entryIndex = 0; entryIndex < addressEntryCount; entryIndex++) { addressTable[entryIndex] = addressTableReader.ReadInt32(); } ushort[] ordinalTable = new ushort[namePointerCount]; BlobReader ordinalTableReader = peImage.GetReader(peReader.GetOffset(ordinalTableRVA), sizeof(ushort) * namePointerCount); for (int entryIndex = 0; entryIndex < namePointerCount; entryIndex++) { ushort ordinalIndex = ordinalTableReader.ReadUInt16(); ordinalTable[entryIndex] = ordinalIndex; _ordinalRva.Add(entryIndex + minOrdinal, addressTable[ordinalIndex]); } BlobReader namePointerReader = peImage.GetReader(peReader.GetOffset(namePointerRVA), sizeof(int) * namePointerCount); for (int entryIndex = 0; entryIndex < namePointerCount; entryIndex++) { int nameRVA = namePointerReader.ReadInt32(); if (nameRVA != 0) { int nameOffset = peReader.GetOffset(nameRVA); BlobReader nameReader = peImage.GetReader(nameOffset, peImage.Length - nameOffset); StringBuilder nameBuilder = new StringBuilder(); for (byte ascii; (ascii = nameReader.ReadByte()) != 0;) { nameBuilder.Append((char)ascii); } _namedExportRva.Add(nameBuilder.ToString(), addressTable[ordinalTable[entryIndex]]); } else { Console.Error.WriteLine($"Found a zero RVA when reading name pointers for entry #{entryIndex}/{namePointerCount}"); } } } public static PEExportTable Parse(PEReader peReader) { return new PEExportTable(peReader); } public void DumpToConsoleError() { Console.Error.WriteLine($"HasExportTable: {HasExportTable}"); Console.Error.WriteLine($"ExportTableHeaderLength: {ExportTableHeaderLength}"); Console.Error.WriteLine($"_namedExportRva: {_namedExportRva.Count} item(s)"); int i = 0; foreach (var kvp in _namedExportRva) { Console.Error.WriteLine($" '{kvp.Key}': {kvp.Value}"); if (i++ > 64) { Console.Error.WriteLine(" ... stopped dumping named exports because there are too many."); break; } } } public bool TryGetValue(string exportName, out int rva) => _namedExportRva.TryGetValue(exportName, out rva); public bool TryGetValue(int ordinal, out int rva) => _ordinalRva.TryGetValue(ordinal, out rva); } public static class PEReaderExtensions { /// <summary> /// Get the index in the image byte array corresponding to the RVA /// </summary> /// <param name="reader">PE reader representing the executable image to parse</param> /// <param name="rva">The relative virtual address</param> public static int GetOffset(this PEReader reader, int rva) { int index = reader.PEHeaders.GetContainingSectionIndex(rva); if (index == -1) { throw new BadImageFormatException("Failed to convert invalid RVA to offset: " + rva); } SectionHeader containingSection = reader.PEHeaders.SectionHeaders[index]; return rva - containingSection.VirtualAddress + containingSection.PointerToRawData; } /// <summary> /// Parse export table directory for a given PE reader. /// </summary> /// <param name="reader">PE reader representing the executable image to parse</param> public static PEExportTable GetExportTable(this PEReader reader) { return PEExportTable.Parse(reader); } /// <summary> /// Check whether the file is a composite ReadyToRun image and returns the RVA of its ReadyToRun header if positive. /// </summary> /// <param name="reader">PEReader representing the executable to check for the presence of ReadyToRun header</param> /// <param name="rva">RVA of the ReadyToRun header if available, 0 when not</param> /// <returns>true when the PEReader represents a ReadyToRun image, false otherwise</returns> public static bool TryGetCompositeReadyToRunHeader(this PEReader reader, out int rva) { return reader.GetExportTable().TryGetValue("RTR_HEADER", out rva); } /// <summary> /// Check whether the file is a ReadyToRun image created from platform neutral (AnyCPU) IL image. /// </summary> /// <param name="reader">PEReader representing the executable to check</param> /// <returns>true when the PEReader represents a ReadyToRun image created from AnyCPU IL image, false otherwise</returns> public static bool IsReadyToRunPlatformNeutralSource(this PEReader peReader) { var managedNativeDirectory = peReader.PEHeaders.CorHeader.ManagedNativeHeaderDirectory; if (managedNativeDirectory.Size < 16 /* sizeof(ReadyToRunHeader) */) return false; var reader = peReader.GetSectionData(managedNativeDirectory.RelativeVirtualAddress).GetReader(); if (reader.ReadUInt32() != ReadyToRunHeaderConstants.Signature) return false; reader.ReadUInt16(); // MajorVersion reader.ReadUInt16(); // MinorVersion return (reader.ReadUInt32() & (uint)ReadyToRunFlags.READYTORUN_FLAG_PlatformNeutralSource) != 0; } } }