From c83a999603dbd5447af90d3a2d48acb50fea412c Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Tue, 11 Nov 2025 23:58:12 -0800 Subject: [PATCH 1/8] Add R2RDump support for Mach-O images TODO: - Move shared code out of HostModel, separate into shared/HostModel/R2RDump parts - Print Mach-O header info when dumping image - Handle diff for Mach-O sections --- .../CodeGen/ReadyToRunContainerFormat.cs | 6 +- .../IBinaryImageReader.cs | 67 +++++ .../ILCompiler.Reflection.ReadyToRun.csproj | 14 +- .../MachOImageReader.cs | 283 ++++++++++++++++++ .../ManifestAssemblyMetadata.cs | 7 +- .../PEImageReader.cs | 111 +++++++ .../ReadyToRunMethod.cs | 1 + .../ReadyToRunReader.cs | 86 +++--- src/coreclr/tools/r2rdump/R2RDiff.cs | 15 +- .../MachO/BinaryFormat/NList64.cs | 23 ++ .../BinaryFormat/Segment64LoadCommand.cs | 1 + .../MachO/MachObjectFile.cs | 2 + 12 files changed, 547 insertions(+), 69 deletions(-) create mode 100644 src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/IBinaryImageReader.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachOImageReader.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PEImageReader.cs create mode 100644 src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/NList64.cs diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunContainerFormat.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunContainerFormat.cs index 403fc97e4c1cd6..cf1221e59c680d 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunContainerFormat.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunContainerFormat.cs @@ -1,11 +1,7 @@ // 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.Text; - -namespace ILCompiler.DependencyAnalysis +namespace ILCompiler { public enum ReadyToRunContainerFormat { diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/IBinaryImageReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/IBinaryImageReader.cs new file mode 100644 index 00000000000000..2279a732859967 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/IBinaryImageReader.cs @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Reflection.PortableExecutable; + +namespace ILCompiler.Reflection.ReadyToRun +{ + /// + /// Interface for abstracting binary image reading across different formats (PE, MachO) + /// + public interface IBinaryImageReader + { + /// + /// Get the entire image content + /// + ImmutableArray GetEntireImage(); + + /// + /// Get the index in the image byte array corresponding to the RVA + /// + /// The relative virtual address + int GetOffset(int rva); + + /// + /// Check whether the file is a composite ReadyToRun image and returns the RVA of its ReadyToRun header if so. + /// + /// RVA of the ReadyToRun header if available, 0 when not + /// true when the reader represents a composite ReadyToRun image, false otherwise + bool TryGetCompositeReadyToRunHeader(out int rva); + + /// + /// Write out image information using the specified writer + /// + /// The writer to use + void DumpImageInformation(TextWriter writer); + + Dictionary GetSections(); + + /// + /// Gets the machine type of the binary image + /// + Machine Machine { get; } + + /// + /// Gets the operating system of the binary image + /// + OperatingSystem OperatingSystem { get; } + + /// + /// Gets whether the image has metadata + /// + bool HasMetadata { get; } + + /// + /// Gets the image base address + /// + ulong ImageBase { get; } + + /// + /// Get whether the image is marked as an IL-only library + /// + bool IsILLibrary { get; } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj index 6274faad0c9b0c..e5e9257ae3f90a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj @@ -1,4 +1,4 @@ - + ILCompiler.Reflection.ReadyToRun 1.0.0.0 @@ -33,4 +33,16 @@ + + + + + + + + + + + + diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachOImageReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachOImageReader.cs new file mode 100644 index 00000000000000..7464100743a590 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachOImageReader.cs @@ -0,0 +1,283 @@ +// 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.Buffers.Binary; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Reflection.PortableExecutable; +using System.Runtime.CompilerServices; +using Microsoft.NET.HostModel.MachO; + +namespace ILCompiler.Reflection.ReadyToRun +{ + /// + /// Wrapper around Mach-O file that implements IBinaryImageReader + /// + public class MachOImageReader : IBinaryImageReader + { + private readonly byte[] _image; + private readonly Machine _machine; + private readonly ulong _imageBase; + private int? _rtrHeaderRva; + + public MachOImageReader(byte[] image) + { + _image = image; + + // Determine machine type from MachO CPU type + // Note: MachO files use their own CPU type encoding + _machine = DetermineMachineType(); + _imageBase = 0; // MachO typically uses 0 as base for .dylib files + } + + private Machine DetermineMachineType() + { + // For now, we'll need to read the MachO header directly from the image + // to determine the CPU type since MachObjectFile doesn't expose this + unsafe + { + fixed (byte* imagePtr = _image) + { + uint magic = BinaryPrimitives.ReadUInt32LittleEndian(new ReadOnlySpan(_image, 0, 4)); + + // Check if this is a 64-bit Mach-O + bool is64Bit = (MachMagic)magic is MachMagic.MachHeader64CurrentEndian or MachMagic.MachHeader64OppositeEndian; + + if (!is64Bit) + { + throw new BadImageFormatException("Only 64-bit Mach-O files are supported"); + } + + // Read CPU type at offset 4 + uint cpuType = BinaryPrimitives.ReadUInt32LittleEndian(new ReadOnlySpan(_image, 4, 4)); + + // CPU_TYPE constants from mach/machine.h + // const uint CPU_TYPE_ARM64 = 0x0100000C; + // const uint CPU_TYPE_X86_64 = 0x01000007; + + return (cpuType & 0x00FFFFFF) switch + { + 0x0C => Machine.Arm64, // ARM64 + 0x07 => Machine.Amd64, // X86_64 + _ => throw new NotSupportedException($"Unsupported MachO CPU type: {cpuType:X8}") + }; + } + } + } + + public ImmutableArray GetEntireImage() + { + return Unsafe.As>(ref Unsafe.AsRef(in _image)); + } + + public int GetOffset(int rva) + { + if (TryGetContainingSegment((ulong)rva, out Segment64LoadCommand segment, out MachHeader header)) + { + // Calculate file offset from segment base and RVA + ulong offsetWithinSegment = (ulong)rva - segment.GetVMAddress(header); + ulong fileOffset = segment.GetFileOffset(header) + offsetWithinSegment; + return (int)fileOffset; + } + else + { + throw new BadImageFormatException("Failed to convert RVA to offset: " + rva); + } + } + + public bool TryGetCompositeReadyToRunHeader(out int rva) + { + if (!_rtrHeaderRva.HasValue) + { + // Look for RTR_HEADER symbol in the Mach-O symbol table + _rtrHeaderRva = FindRTRHeaderSymbol(); + } + + rva = _rtrHeaderRva.Value; + return rva != 0; + } + + private int FindRTRHeaderSymbol() + { + if (TryFindSymbol("RTR_HEADER", out ulong symbolValue)) + { + return (int)symbolValue; + } + + return 0; + } + + public Machine Machine => _machine; + + public OperatingSystem OperatingSystem => OperatingSystem.Apple; + + public bool HasMetadata => false; // Mach-O composite R2R images don't have embedded ECMA metadata + + public ulong ImageBase => _imageBase; + + public bool IsILLibrary => false; + + public void DumpImageInformation(TextWriter writer) + { + // TODO: Print information from the Mach-O header + } + + public Dictionary GetSections() + { + // TODO: Get all the sections + return []; + } + + /// + /// Finds a symbol in the symbol table by name. + /// + /// The name of the symbol to find (without leading underscore). + /// The value of the symbol if found. + /// True if the symbol was found, false otherwise. + private unsafe bool TryFindSymbol(string symbolName, out ulong symbolValue) + { + symbolValue = 0; + + // Read the header + Read(0, out MachHeader header); + + // Find the symbol table load command + long commandsPtr = sizeof(MachHeader); + SymbolTableLoadCommand symtabCommand = default; + bool foundSymtab = false; + + for (int i = 0; i < header.NumberOfCommands; i++) + { + Read(commandsPtr, out LoadCommand loadCommand); + + if (loadCommand.GetCommandType(header) == MachLoadCommandType.SymbolTable) + { + Read(commandsPtr, out symtabCommand); + foundSymtab = true; + break; + } + + commandsPtr += loadCommand.GetCommandSize(header); + } + + if (!foundSymtab || symtabCommand.IsDefault) + { + return false; + } + + uint symbolTableOffset = symtabCommand.GetSymbolTableOffset(header); + uint symbolsCount = symtabCommand.GetSymbolsCount(header); + uint stringTableOffset = symtabCommand.GetStringTableOffset(header); + uint stringTableSize = symtabCommand.GetStringTableSize(header); + + for (uint i = 0; i < symbolsCount; i++) + { + long symOffset = symbolTableOffset + (i * sizeof(NList64)); + + // Read the symbol table entry + Read(symOffset, out NList64 symbol); + + uint strIndex = symbol.GetStringTableIndex(header); + if (strIndex >= stringTableSize) + { + continue; + } + + // Read symbol name from string table + string name = ReadCString(stringTableOffset + strIndex, stringTableSize - strIndex); + + // Symbol names in Mach-O can have a leading underscore + if (name == symbolName || (name.Length > 0 && name[0] == '_' && name.Substring(1) == symbolName)) + { + symbolValue = symbol.GetValue(header); + return true; + } + } + + return false; + } + + /// + /// Finds the segment that contains the specified virtual memory address. + /// + /// The virtual memory address to find. + /// The segment containing the VM address if found. + /// The Mach-O header (output parameter). + /// True if a containing segment was found, false otherwise. + private unsafe bool TryGetContainingSegment(ulong vmAddress, out Segment64LoadCommand segment, out MachHeader header) + { + segment = default; + + // Read the header + Read(0, out header); + + // Iterate through all load commands to find segments + long commandsPtr = sizeof(MachHeader); + + for (int i = 0; i < header.NumberOfCommands; i++) + { + Read(commandsPtr, out LoadCommand loadCommand); + + if (loadCommand.GetCommandType(header) == MachLoadCommandType.Segment64) + { + Read(commandsPtr, out Segment64LoadCommand seg); + + // Check if the VM address falls within this segment + ulong segmentVMAddr = seg.GetVMAddress(header); + ulong segmentVMSize = seg.GetVMSize(header); + if (vmAddress >= segmentVMAddr && vmAddress < segmentVMAddr + segmentVMSize) + { + segment = seg; + return true; + } + } + + commandsPtr += loadCommand.GetCommandSize(header); + } + + return false; + } + + /// + /// Reads a null-terminated C string from the image. + /// + private string ReadCString(long offset, uint maxLength) + { + if (offset < 0 || offset >= _image.Length) + { + throw new ArgumentOutOfRangeException(nameof(offset)); + } + + // Find the null terminator in the image array + int length; + int end = (int)Math.Min(offset + maxLength, _image.Length); + for (length = 0; offset + length < end; length++) + { + if (_image[offset + length] == 0) + { + break; + } + } + + return System.Text.Encoding.UTF8.GetString(_image, (int)offset, length); + } + + public void Read(long offset, out T result) where T : unmanaged + { + unsafe + { + if (offset < 0 || offset + sizeof(T) > _image.Length) + { + throw new ArgumentOutOfRangeException(nameof(offset)); + } + + fixed (byte* ptr = &_image[offset]) + { + result = *(T*)ptr; + } + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ManifestAssemblyMetadata.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ManifestAssemblyMetadata.cs index a85237661fc0f9..e6c0032a9109cc 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ManifestAssemblyMetadata.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ManifestAssemblyMetadata.cs @@ -25,10 +25,15 @@ internal class ManifestAssemblyMetadata : IAssemblyMetadata /// private readonly MetadataReader _metadataReader; + public ManifestAssemblyMetadata(MetadataReader metadataReader) + { + _metadataReader = metadataReader; + } + public ManifestAssemblyMetadata(PEReader peReader, MetadataReader metadataReader) + : this(metadataReader) { _peReader = peReader; - _metadataReader = metadataReader; } public PEReader ImageReader => _peReader; diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PEImageReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PEImageReader.cs new file mode 100644 index 00000000000000..81f6986efb98a7 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PEImageReader.cs @@ -0,0 +1,111 @@ +// 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.PortableExecutable; + +namespace ILCompiler.Reflection.ReadyToRun +{ + /// + /// Wrapper around PEReader that implements IBinaryImageReader + /// + public class PEImageReader : IBinaryImageReader + { + private readonly PEReader _peReader; + private readonly Machine _machine; + private readonly OperatingSystem _operatingSystem; + + public PEImageReader(PEReader peReader) + { + _peReader = peReader; + + // Extract machine and OS from PE header + // The OS is encoded in the machine type + uint rawMachine = (uint)_peReader.PEHeaders.CoffHeader.Machine; + _operatingSystem = OperatingSystem.Unknown; + + foreach (OperatingSystem os in System.Enum.GetValues(typeof(OperatingSystem))) + { + Machine candidateMachine = (Machine)(rawMachine ^ (uint)os); + if (System.Enum.IsDefined(typeof(Machine), candidateMachine)) + { + _machine = candidateMachine; + _operatingSystem = os; + break; + } + } + + if (_operatingSystem == OperatingSystem.Unknown) + { + throw new BadImageFormatException($"Invalid PE Machine type: {rawMachine}"); + } + } + + public ImmutableArray GetEntireImage() + { + return _peReader.GetEntireImage().GetContent(); + } + + public int GetOffset(int rva) + { + return _peReader.GetOffset(rva); + } + + public bool TryGetCompositeReadyToRunHeader(out int rva) + { + return _peReader.TryGetCompositeReadyToRunHeader(out rva); + } + + public Machine Machine => _machine; + + public OperatingSystem OperatingSystem => _operatingSystem; + + public bool HasMetadata => _peReader.HasMetadata; + + public ulong ImageBase => _peReader.PEHeaders.PEHeader.ImageBase; + + public PEReader PEReader => _peReader; + + public bool IsILLibrary => _peReader.PEHeaders.CorHeader?.Flags.HasFlag(CorFlags.ILLibrary) ?? false; + + public void DumpImageInformation(TextWriter writer) + { + writer.WriteLine($"MetadataSize: {PEReader.PEHeaders.MetadataSize} byte(s)"); + + if (PEReader.PEHeaders.PEHeader is PEHeader header) + { + writer.WriteLine($"SizeOfImage: {header.SizeOfImage} byte(s)"); + writer.WriteLine($"ImageBase: 0x{header.ImageBase:X}"); + writer.WriteLine($"FileAlignment: 0x{header.FileAlignment:X}"); + writer.WriteLine($"SectionAlignment: 0x{header.SectionAlignment:X}"); + } + else + { + writer.WriteLine("No PEHeader"); + } + + writer.WriteLine($"CorHeader.Flags: {PEReader.PEHeaders.CorHeader?.Flags}"); + + writer.WriteLine("Sections:"); + foreach (var section in PEReader.PEHeaders.SectionHeaders) + writer.WriteLine($" {section.Name} {section.VirtualAddress} - {(section.VirtualAddress + section.VirtualSize)}"); + + var exportTable = PEReader.GetExportTable(); + exportTable.DumpToConsoleError(); + } + + public Dictionary GetSections() + { + Dictionary sectionMap = []; + foreach (SectionHeader sectionHeader in PEReader.PEHeaders.SectionHeaders) + { + sectionMap.Add(sectionHeader.Name, sectionHeader.SizeOfRawData); + } + + return sectionMap; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunMethod.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunMethod.cs index 10ab13a6e9eaad..b5c7b3b6e10436 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunMethod.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunMethod.cs @@ -403,6 +403,7 @@ public ReadyToRunMethod( MethodDefinition methodDef = ComponentReader.MetadataReader.GetMethodDefinition((MethodDefinitionHandle)MethodHandle); if (methodDef.RelativeVirtualAddress != 0) { + System.Diagnostics.Debug.Assert(ComponentReader.ImageReader != null, "Component should be a PE and have an associated PEReader"); MethodBodyBlock mbb = ComponentReader.ImageReader.GetMethodBody(methodDef.RelativeVirtualAddress); if (!mbb.LocalSignature.IsNil) { diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs index 3e5341c03f83bc..7b80f9dec0c416 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs @@ -15,6 +15,7 @@ using Internal.ReadyToRunConstants; using Internal.Runtime; +using Microsoft.NET.HostModel.MachO; using Debug = System.Diagnostics.Debug; @@ -132,10 +133,10 @@ public sealed class ReadyToRunReader private string _compilerIdentifier; /// - /// Underlying PE image reader is used to access raw PE structures like header - /// or section list. + /// Underlying binary image reader is used to access raw structures like headers + /// and sections. This can be either a PE or MachO image. /// - public PEReader CompositeReader { get; private set; } + public IBinaryImageReader CompositeReader { get; private set; } /// /// Byte array containing the ReadyToRun image @@ -394,7 +395,7 @@ internal IAssemblyMetadata R2RManifestMetadata public ReadyToRunReader(IAssemblyResolver assemblyResolver, IAssemblyMetadata metadata, PEReader peReader, string filename) { _assemblyResolver = assemblyResolver; - CompositeReader = peReader; + CompositeReader = new PEImageReader(peReader); Filename = filename; Initialize(metadata); } @@ -410,7 +411,7 @@ public ReadyToRunReader(IAssemblyResolver assemblyResolver, IAssemblyMetadata me public ReadyToRunReader(IAssemblyResolver assemblyResolver, IAssemblyMetadata metadata, PEReader peReader, string filename, ReadOnlyMemory content) { _assemblyResolver = assemblyResolver; - CompositeReader = peReader; + CompositeReader = new PEImageReader(peReader); Filename = filename; Image = ConvertToArray(content); ImageReader = new NativeReader(new MemoryStream(Image)); @@ -421,7 +422,7 @@ public ReadyToRunReader(IAssemblyResolver assemblyResolver, IAssemblyMetadata me /// Minimally initializes the R2R reader. /// /// Assembly resolver - /// PE file name + /// Binary image file name public unsafe ReadyToRunReader(IAssemblyResolver assemblyResolver, string filename) { _assemblyResolver = assemblyResolver; @@ -433,8 +434,8 @@ public unsafe ReadyToRunReader(IAssemblyResolver assemblyResolver, string filena /// Minimally initializes the R2R reader. /// /// Assembly resolver - /// PE file name - /// PE image content + /// Binary image file name + /// Binary image content public unsafe ReadyToRunReader(IAssemblyResolver assemblyResolver, string filename, ReadOnlyMemory content) { _assemblyResolver = assemblyResolver; @@ -497,25 +498,34 @@ private unsafe void Initialize(IAssemblyMetadata metadata) ImageReader = new NativeReader(new MemoryStream(Image)); byte[] image = Image; ImagePin = new PinningReference(image); - CompositeReader = new PEReader(Unsafe.As>(ref image)); + if (MachObjectFile.IsMachOImage(Filename)) + { + CompositeReader = new MachOImageReader(image); + } + else + { + CompositeReader = new PEImageReader(new PEReader(Unsafe.As>(ref image))); + } } else { - ImmutableArray content = CompositeReader.GetEntireImage().GetContent(); + ImmutableArray content = CompositeReader.GetEntireImage(); Image = Unsafe.As, byte[]>(ref content); ImageReader = new NativeReader(new MemoryStream(Image)); ImagePin = new PinningReference(Image); } - if (metadata == null && CompositeReader.HasMetadata) + if (metadata == null && ContainerFormat == ReadyToRunContainerFormat.PE && CompositeReader.HasMetadata) { - metadata = new StandaloneAssemblyMetadata(CompositeReader); + Debug.Assert(CompositeReader is PEImageReader); + metadata = new StandaloneAssemblyMetadata(((PEImageReader)CompositeReader).PEReader); } if (metadata != null) { - if ((CompositeReader.PEHeaders.CorHeader.Flags & CorFlags.ILLibrary) == 0) + if (!CompositeReader.IsILLibrary) { + // Composite image if (!TryLocateNativeReadyToRunHeader()) { DumpImageInformation(); @@ -527,9 +537,11 @@ private unsafe void Initialize(IAssemblyMetadata metadata) } else { + // Regular PE R2R assembly _assemblyCache.Add(metadata); - DirectoryEntry r2rHeaderDirectory = CompositeReader.PEHeaders.CorHeader.ManagedNativeHeaderDirectory; + Debug.Assert(CompositeReader is PEImageReader); + DirectoryEntry r2rHeaderDirectory = ((PEImageReader)CompositeReader).PEReader.PEHeaders.CorHeader.ManagedNativeHeaderDirectory; _readyToRunHeaderRVA = r2rHeaderDirectory.RelativeVirtualAddress; Debug.Assert(!Composite); } @@ -554,26 +566,7 @@ internal void DumpImageInformation() { Console.Error.WriteLine($"Image file '{Filename}' information:"); Console.Error.WriteLine($"Size: {Image.Length} byte(s)"); - Console.Error.WriteLine($"MetadataSize: {CompositeReader.PEHeaders.MetadataSize} byte(s)"); - - if (CompositeReader.PEHeaders.PEHeader is PEHeader header) - { - Console.Error.WriteLine($"SizeOfImage: {header.SizeOfImage} byte(s)"); - Console.Error.WriteLine($"ImageBase: 0x{header.ImageBase:X}"); - Console.Error.WriteLine($"FileAlignment: 0x{header.FileAlignment:X}"); - Console.Error.WriteLine($"SectionAlignment: 0x{header.SectionAlignment:X}"); - } - else - Console.Error.WriteLine("No PEHeader"); - - Console.Error.WriteLine($"CorHeader.Flags: {CompositeReader.PEHeaders.CorHeader?.Flags}"); - - Console.Error.WriteLine("Sections:"); - foreach (var section in CompositeReader.PEHeaders.SectionHeaders) - Console.Error.WriteLine($" {section.Name} {section.VirtualAddress} - {(section.VirtualAddress + section.VirtualSize)}"); - - var exportTable = CompositeReader.GetExportTable(); - exportTable.DumpToConsoleError(); + CompositeReader.DumpImageInformation(Console.Error); } catch (Exception exc) { @@ -702,21 +695,9 @@ private unsafe void EnsureHeader() { return; } - uint machine = (uint)CompositeReader.PEHeaders.CoffHeader.Machine; - _operatingSystem = OperatingSystem.Unknown; - foreach (OperatingSystem os in Enum.GetValues(typeof(OperatingSystem))) - { - _machine = (Machine)(machine ^ (uint)os); - if (Enum.IsDefined(typeof(Machine), _machine)) - { - _operatingSystem = os; - break; - } - } - if (_operatingSystem == OperatingSystem.Unknown) - { - throw new BadImageFormatException($"Invalid Machine: {machine}"); - } + + _machine = CompositeReader.Machine; + _operatingSystem = CompositeReader.OperatingSystem; switch (_machine) { @@ -738,7 +719,7 @@ private unsafe void EnsureHeader() throw new NotImplementedException(Machine.ToString()); } - _imageBase = CompositeReader.PEHeaders.PEHeader.ImageBase; + _imageBase = CompositeReader.ImageBase; // Initialize R2RHeader Debug.Assert(_readyToRunHeaderRVA != 0); @@ -797,7 +778,10 @@ private unsafe void EnsureManifestReferences() fixed (byte* image = Image) { _manifestReader = new MetadataReader(image + GetOffset(manifestMetadata.RelativeVirtualAddress), manifestMetadata.Size); - _manifestAssemblyMetadata = new ManifestAssemblyMetadata(CompositeReader, _manifestReader); + _manifestAssemblyMetadata = ContainerFormat == ReadyToRunContainerFormat.PE + ? new ManifestAssemblyMetadata(((PEImageReader)CompositeReader).PEReader, _manifestReader) + : new ManifestAssemblyMetadata(_manifestReader); + int assemblyRefCount = _manifestReader.GetTableRowCount(TableIndex.AssemblyRef); for (int assemblyRefIndex = 1; assemblyRefIndex <= assemblyRefCount; assemblyRefIndex++) { diff --git a/src/coreclr/tools/r2rdump/R2RDiff.cs b/src/coreclr/tools/r2rdump/R2RDiff.cs index 8e3edd189cf3a8..1101703941919f 100644 --- a/src/coreclr/tools/r2rdump/R2RDiff.cs +++ b/src/coreclr/tools/r2rdump/R2RDiff.cs @@ -133,7 +133,7 @@ private void DiffTitle() /// private void DiffPESections() { - ShowDiff(GetPESectionMap(_leftDumper.Reader), GetPESectionMap(_rightDumper.Reader), "PE sections"); + ShowDiff(GetImageSectionMap(_leftDumper.Reader), GetImageSectionMap(_rightDumper.Reader), "PE sections"); } /// @@ -312,20 +312,13 @@ private void ShowDiff(Dictionary leftObjects, Dictionary - /// Read the PE file section map for a given R2R image. + /// Get the sections for a given R2R image. /// /// R2R image to scan /// - private Dictionary GetPESectionMap(ReadyToRunReader reader) + private Dictionary GetImageSectionMap(ReadyToRunReader reader) { - Dictionary sectionMap = new Dictionary(); - - foreach (SectionHeader sectionHeader in reader.CompositeReader.PEHeaders.SectionHeaders) - { - sectionMap.Add(sectionHeader.Name, sectionHeader.SizeOfRawData); - } - - return sectionMap; + return reader.CompositeReader.GetSections(); } /// diff --git a/src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/NList64.cs b/src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/NList64.cs new file mode 100644 index 00000000000000..837d47d287eacc --- /dev/null +++ b/src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/NList64.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace Microsoft.NET.HostModel.MachO; + +/// +/// Represents a 64-bit symbol table entry. +/// See https://github.com/apple-oss-distributions/cctools/blob/7a5450708479bbff61527d5e0c32a3f7b7e4c1d0/include/mach-o/nlist.h#L92 for reference. +/// +[StructLayout(LayoutKind.Sequential)] +internal readonly struct NList64 +{ + private readonly uint _stringTableIndex; + private readonly byte _type; + private readonly byte _section; + private readonly ushort _description; + private readonly ulong _value; + + public uint GetStringTableIndex(MachHeader header) => header.ConvertValue(_stringTableIndex); + public ulong GetValue(MachHeader header) => header.ConvertValue(_value); +} diff --git a/src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/Segment64LoadCommand.cs b/src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/Segment64LoadCommand.cs index e731508ab2e47f..3adc24e3b33b7f 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/Segment64LoadCommand.cs +++ b/src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/Segment64LoadCommand.cs @@ -29,6 +29,7 @@ internal struct Segment64LoadCommand public ulong GetFileOffset(MachHeader header) => header.ConvertValue(_fileOffset); public ulong GetFileSize(MachHeader header) => header.ConvertValue(_fileSize); public void SetFileSize(ulong value, MachHeader header) => _fileSize = header.ConvertValue(value); + public ulong GetVMAddress(MachHeader header) => header.ConvertValue(_address); public void SetVMSize(ulong value, MachHeader header) => _size = header.ConvertValue(value); public ulong GetVMSize(MachHeader header) => header.ConvertValue(_size); public uint GetSectionsCount(MachHeader header) => header.ConvertValue(_numberOfSections); diff --git a/src/installer/managed/Microsoft.NET.HostModel/MachO/MachObjectFile.cs b/src/installer/managed/Microsoft.NET.HostModel/MachO/MachObjectFile.cs index 0129a3d00cbe94..e8484dbac69c56 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/MachO/MachObjectFile.cs +++ b/src/installer/managed/Microsoft.NET.HostModel/MachO/MachObjectFile.cs @@ -38,6 +38,8 @@ internal unsafe partial class MachObjectFile internal EmbeddedSignatureBlob? EmbeddedSignatureBlob => _codeSignatureBlob; + internal MachHeader Header => _header; + private MachObjectFile( MachHeader header, (LinkEditLoadCommand Command, long FileOffset) codeSignatureLC, From 57dde51ca279bec82edc9556f8e46a324dc6dfb7 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Wed, 12 Nov 2025 12:50:21 -0800 Subject: [PATCH 2/8] Move shared files under tools/Common/MachO --- .../Common}/MachO/BinaryFormat/LoadCommand.cs | 4 +++ .../Common}/MachO/BinaryFormat/MachHeader.cs | 4 +++ .../Common}/MachO/BinaryFormat/NameBuffer.cs | 4 +++ .../BinaryFormat/Section64LoadCommand.cs | 4 +++ .../BinaryFormat/Segment64LoadCommand.cs | 4 +++ .../BinaryFormat/SymbolTableLoadCommand.cs | 4 +++ .../tools/Common}/MachO/Enums/MachFileType.cs | 4 +++ .../MachO/Enums/MachLoadCommandType.cs | 4 +++ .../tools/Common}/MachO/Enums/MachMagic.cs | 4 +++ .../Common}/MachO/MachMagicExtensions.cs | 4 +++ .../tools/Common/MachO/MachObjectFile.cs | 32 +++++++++++++++++++ .../ILCompiler.Reflection.ReadyToRun.csproj | 12 +------ .../MachO/BinaryFormat/NList64.cs | 2 +- .../{ => MachO}/MachOImageReader.cs | 17 ++++------ .../ReadyToRunReader.cs | 5 ++- .../MachO/MachObjectFile.cs | 13 -------- .../Microsoft.NET.HostModel.csproj | 1 + 17 files changed, 84 insertions(+), 38 deletions(-) rename src/{installer/managed/Microsoft.NET.HostModel => coreclr/tools/Common}/MachO/BinaryFormat/LoadCommand.cs (92%) rename src/{installer/managed/Microsoft.NET.HostModel => coreclr/tools/Common}/MachO/BinaryFormat/MachHeader.cs (94%) rename src/{installer/managed/Microsoft.NET.HostModel => coreclr/tools/Common}/MachO/BinaryFormat/NameBuffer.cs (93%) rename src/{installer/managed/Microsoft.NET.HostModel => coreclr/tools/Common}/MachO/BinaryFormat/Section64LoadCommand.cs (93%) rename src/{installer/managed/Microsoft.NET.HostModel => coreclr/tools/Common}/MachO/BinaryFormat/Segment64LoadCommand.cs (95%) rename src/{installer/managed/Microsoft.NET.HostModel => coreclr/tools/Common}/MachO/BinaryFormat/SymbolTableLoadCommand.cs (95%) rename src/{installer/managed/Microsoft.NET.HostModel => coreclr/tools/Common}/MachO/Enums/MachFileType.cs (75%) rename src/{installer/managed/Microsoft.NET.HostModel => coreclr/tools/Common}/MachO/Enums/MachLoadCommandType.cs (86%) rename src/{installer/managed/Microsoft.NET.HostModel => coreclr/tools/Common}/MachO/Enums/MachMagic.cs (89%) rename src/{installer/managed/Microsoft.NET.HostModel => coreclr/tools/Common}/MachO/MachMagicExtensions.cs (94%) create mode 100644 src/coreclr/tools/Common/MachO/MachObjectFile.cs rename src/{installer/managed/Microsoft.NET.HostModel => coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun}/MachO/BinaryFormat/NList64.cs (94%) rename src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/{ => MachO}/MachOImageReader.cs (94%) diff --git a/src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/LoadCommand.cs b/src/coreclr/tools/Common/MachO/BinaryFormat/LoadCommand.cs similarity index 92% rename from src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/LoadCommand.cs rename to src/coreclr/tools/Common/MachO/BinaryFormat/LoadCommand.cs index c2ff7988d2ea63..c0cebf73e569b9 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/LoadCommand.cs +++ b/src/coreclr/tools/Common/MachO/BinaryFormat/LoadCommand.cs @@ -3,7 +3,11 @@ using System.Runtime.InteropServices; +#if HOST_MODEL namespace Microsoft.NET.HostModel.MachO; +#else +namespace ILCompiler.Reflection.ReadyToRun.MachO; +#endif /// /// The base structure for all load commands in a Mach-O binary. diff --git a/src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/MachHeader.cs b/src/coreclr/tools/Common/MachO/BinaryFormat/MachHeader.cs similarity index 94% rename from src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/MachHeader.cs rename to src/coreclr/tools/Common/MachO/BinaryFormat/MachHeader.cs index b2fd21a5eb1dd3..062f4bd8e4e2e4 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/MachHeader.cs +++ b/src/coreclr/tools/Common/MachO/BinaryFormat/MachHeader.cs @@ -3,7 +3,11 @@ using System.Runtime.InteropServices; +#if HOST_MODEL namespace Microsoft.NET.HostModel.MachO; +#else +namespace ILCompiler.Reflection.ReadyToRun.MachO; +#endif /// /// The Mach-O header is the first data in a Mach-O file. diff --git a/src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/NameBuffer.cs b/src/coreclr/tools/Common/MachO/BinaryFormat/NameBuffer.cs similarity index 93% rename from src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/NameBuffer.cs rename to src/coreclr/tools/Common/MachO/BinaryFormat/NameBuffer.cs index 37e188e9e9f721..f44389326b69d2 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/NameBuffer.cs +++ b/src/coreclr/tools/Common/MachO/BinaryFormat/NameBuffer.cs @@ -4,7 +4,11 @@ using System; using System.Runtime.InteropServices; +#if HOST_MODEL namespace Microsoft.NET.HostModel.MachO; +#else +namespace ILCompiler.Reflection.ReadyToRun.MachO; +#endif /// /// A 16 byte buffer used to store names in Mach-O load commands. diff --git a/src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/Section64LoadCommand.cs b/src/coreclr/tools/Common/MachO/BinaryFormat/Section64LoadCommand.cs similarity index 93% rename from src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/Section64LoadCommand.cs rename to src/coreclr/tools/Common/MachO/BinaryFormat/Section64LoadCommand.cs index f2cb79b243c9e4..6b6dcae882e2f8 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/Section64LoadCommand.cs +++ b/src/coreclr/tools/Common/MachO/BinaryFormat/Section64LoadCommand.cs @@ -3,7 +3,11 @@ using System.Runtime.InteropServices; +#if HOST_MODEL namespace Microsoft.NET.HostModel.MachO; +#else +namespace ILCompiler.Reflection.ReadyToRun.MachO; +#endif /// /// A load command that provides information about a section in a segment. diff --git a/src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/Segment64LoadCommand.cs b/src/coreclr/tools/Common/MachO/BinaryFormat/Segment64LoadCommand.cs similarity index 95% rename from src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/Segment64LoadCommand.cs rename to src/coreclr/tools/Common/MachO/BinaryFormat/Segment64LoadCommand.cs index 3adc24e3b33b7f..14def55928a22a 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/Segment64LoadCommand.cs +++ b/src/coreclr/tools/Common/MachO/BinaryFormat/Segment64LoadCommand.cs @@ -3,7 +3,11 @@ using System.Runtime.InteropServices; +#if HOST_MODEL namespace Microsoft.NET.HostModel.MachO; +#else +namespace ILCompiler.Reflection.ReadyToRun.MachO; +#endif /// /// The structure for the 64-bit segment load command in a Mach-O binary. diff --git a/src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/SymbolTableLoadCommand.cs b/src/coreclr/tools/Common/MachO/BinaryFormat/SymbolTableLoadCommand.cs similarity index 95% rename from src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/SymbolTableLoadCommand.cs rename to src/coreclr/tools/Common/MachO/BinaryFormat/SymbolTableLoadCommand.cs index 33c719db071945..e97294d0511099 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/SymbolTableLoadCommand.cs +++ b/src/coreclr/tools/Common/MachO/BinaryFormat/SymbolTableLoadCommand.cs @@ -3,7 +3,11 @@ using System.Runtime.InteropServices; +#if HOST_MODEL namespace Microsoft.NET.HostModel.MachO; +#else +namespace ILCompiler.Reflection.ReadyToRun.MachO; +#endif /// /// A load command with info about the location of the symbol table and string table. diff --git a/src/installer/managed/Microsoft.NET.HostModel/MachO/Enums/MachFileType.cs b/src/coreclr/tools/Common/MachO/Enums/MachFileType.cs similarity index 75% rename from src/installer/managed/Microsoft.NET.HostModel/MachO/Enums/MachFileType.cs rename to src/coreclr/tools/Common/MachO/Enums/MachFileType.cs index 711b48597ab41b..01555e59fc3d69 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/MachO/Enums/MachFileType.cs +++ b/src/coreclr/tools/Common/MachO/Enums/MachFileType.cs @@ -1,7 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if HOST_MODEL namespace Microsoft.NET.HostModel.MachO; +#else +namespace ILCompiler.Reflection.ReadyToRun.MachO; +#endif internal enum MachFileType : uint { diff --git a/src/installer/managed/Microsoft.NET.HostModel/MachO/Enums/MachLoadCommandType.cs b/src/coreclr/tools/Common/MachO/Enums/MachLoadCommandType.cs similarity index 86% rename from src/installer/managed/Microsoft.NET.HostModel/MachO/Enums/MachLoadCommandType.cs rename to src/coreclr/tools/Common/MachO/Enums/MachLoadCommandType.cs index b827f6a2f7e7ea..1418eb664c7308 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/MachO/Enums/MachLoadCommandType.cs +++ b/src/coreclr/tools/Common/MachO/Enums/MachLoadCommandType.cs @@ -1,7 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if HOST_MODEL namespace Microsoft.NET.HostModel.MachO; +#else +namespace ILCompiler.Reflection.ReadyToRun.MachO; +#endif /// /// See https://github.com/apple-oss-distributions/cctools/blob/7a5450708479bbff61527d5e0c32a3f7b7e4c1d0/include/mach-o/loader.h#L282 for reference. diff --git a/src/installer/managed/Microsoft.NET.HostModel/MachO/Enums/MachMagic.cs b/src/coreclr/tools/Common/MachO/Enums/MachMagic.cs similarity index 89% rename from src/installer/managed/Microsoft.NET.HostModel/MachO/Enums/MachMagic.cs rename to src/coreclr/tools/Common/MachO/Enums/MachMagic.cs index f36766cf58e41c..56e123c8e3ca12 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/MachO/Enums/MachMagic.cs +++ b/src/coreclr/tools/Common/MachO/Enums/MachMagic.cs @@ -1,7 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if HOST_MODEL namespace Microsoft.NET.HostModel.MachO; +#else +namespace ILCompiler.Reflection.ReadyToRun.MachO; +#endif /// /// See https://github.com/apple-oss-distributions/cctools/blob/7a5450708479bbff61527d5e0c32a3f7b7e4c1d0/tests/include/MachO/MachHeader.pm#L12 for definitions. diff --git a/src/installer/managed/Microsoft.NET.HostModel/MachO/MachMagicExtensions.cs b/src/coreclr/tools/Common/MachO/MachMagicExtensions.cs similarity index 94% rename from src/installer/managed/Microsoft.NET.HostModel/MachO/MachMagicExtensions.cs rename to src/coreclr/tools/Common/MachO/MachMagicExtensions.cs index 2235664e195f01..08fe1a9dcfa9a6 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/MachO/MachMagicExtensions.cs +++ b/src/coreclr/tools/Common/MachO/MachMagicExtensions.cs @@ -4,7 +4,11 @@ using System.Buffers.Binary; using System.IO; +#if HOST_MODEL namespace Microsoft.NET.HostModel.MachO; +#else +namespace ILCompiler.Reflection.ReadyToRun.MachO; +#endif internal static class MachMagicExtensions { diff --git a/src/coreclr/tools/Common/MachO/MachObjectFile.cs b/src/coreclr/tools/Common/MachO/MachObjectFile.cs new file mode 100644 index 00000000000000..0ce744ed80ffe0 --- /dev/null +++ b/src/coreclr/tools/Common/MachO/MachObjectFile.cs @@ -0,0 +1,32 @@ +// 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.IO; + +#if HOST_MODEL +namespace Microsoft.NET.HostModel.MachO; +#else +namespace ILCompiler.Reflection.ReadyToRun.MachO; +#endif + +/// +/// A managed object containing relevant information for AdHoc signing a Mach-O file. +/// The object is created from a memory mapped file, and a signature can be calculated from the memory mapped file. +/// However, since a memory mapped file cannot be extended, the signature is written to a file stream. +/// +internal unsafe partial class MachObjectFile +{ + public static bool IsMachOImage(string filePath) + { + using (BinaryReader reader = new BinaryReader(File.OpenRead(filePath))) + { + if (reader.BaseStream.Length < 256) // Header size + { + return false; + } + uint magic = reader.ReadUInt32(); + return Enum.IsDefined(typeof(MachMagic), magic); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj index e5e9257ae3f90a..061cad8eb38812 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj @@ -31,18 +31,8 @@ + - - - - - - - - - - - diff --git a/src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/NList64.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/BinaryFormat/NList64.cs similarity index 94% rename from src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/NList64.cs rename to src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/BinaryFormat/NList64.cs index 837d47d287eacc..c534ea8f16b590 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/MachO/BinaryFormat/NList64.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/BinaryFormat/NList64.cs @@ -3,7 +3,7 @@ using System.Runtime.InteropServices; -namespace Microsoft.NET.HostModel.MachO; +namespace ILCompiler.Reflection.ReadyToRun.MachO; /// /// Represents a 64-bit symbol table entry. diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachOImageReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs similarity index 94% rename from src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachOImageReader.cs rename to src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs index 7464100743a590..f80d8030953ed3 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachOImageReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs @@ -8,9 +8,8 @@ using System.IO; using System.Reflection.PortableExecutable; using System.Runtime.CompilerServices; -using Microsoft.NET.HostModel.MachO; -namespace ILCompiler.Reflection.ReadyToRun +namespace ILCompiler.Reflection.ReadyToRun.MachO { /// /// Wrapper around Mach-O file that implements IBinaryImageReader @@ -27,7 +26,6 @@ public MachOImageReader(byte[] image) _image = image; // Determine machine type from MachO CPU type - // Note: MachO files use their own CPU type encoding _machine = DetermineMachineType(); _imageBase = 0; // MachO typically uses 0 as base for .dylib files } @@ -53,14 +51,13 @@ private Machine DetermineMachineType() // Read CPU type at offset 4 uint cpuType = BinaryPrimitives.ReadUInt32LittleEndian(new ReadOnlySpan(_image, 4, 4)); - // CPU_TYPE constants from mach/machine.h - // const uint CPU_TYPE_ARM64 = 0x0100000C; - // const uint CPU_TYPE_X86_64 = 0x01000007; - - return (cpuType & 0x00FFFFFF) switch + // https://github.com/apple-oss-distributions/xnu/blob/f6217f891ac0bb64f3d375211650a4c1ff8ca1ea/osfmk/mach/machine.h + const uint CPU_TYPE_ARM64 = 0x0100000C; + const uint CPU_TYPE_X86_64 = 0x01000007; + return cpuType switch { - 0x0C => Machine.Arm64, // ARM64 - 0x07 => Machine.Amd64, // X86_64 + CPU_TYPE_ARM64 => Machine.Arm64, + CPU_TYPE_X86_64 => Machine.Amd64, _ => throw new NotSupportedException($"Unsupported MachO CPU type: {cpuType:X8}") }; } diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs index 7b80f9dec0c416..baacd48e812ce8 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs @@ -15,7 +15,6 @@ using Internal.ReadyToRunConstants; using Internal.Runtime; -using Microsoft.NET.HostModel.MachO; using Debug = System.Diagnostics.Debug; @@ -498,9 +497,9 @@ private unsafe void Initialize(IAssemblyMetadata metadata) ImageReader = new NativeReader(new MemoryStream(Image)); byte[] image = Image; ImagePin = new PinningReference(image); - if (MachObjectFile.IsMachOImage(Filename)) + if (MachO.MachObjectFile.IsMachOImage(Filename)) { - CompositeReader = new MachOImageReader(image); + CompositeReader = new MachO.MachOImageReader(image); } else { diff --git a/src/installer/managed/Microsoft.NET.HostModel/MachO/MachObjectFile.cs b/src/installer/managed/Microsoft.NET.HostModel/MachO/MachObjectFile.cs index e8484dbac69c56..407eeeaf022d3b 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/MachO/MachObjectFile.cs +++ b/src/installer/managed/Microsoft.NET.HostModel/MachO/MachObjectFile.cs @@ -193,19 +193,6 @@ or MachMagic.MachHeader64CurrentEndian or MachMagic.MachHeader64OppositeEndian or MachMagic.FatMagicCurrentEndian or MachMagic.FatMagicOppositeEndian; } - public static bool IsMachOImage(string filePath) - { - using (BinaryReader reader = new BinaryReader(File.OpenRead(filePath))) - { - if (reader.BaseStream.Length < 256) // Header size - { - return false; - } - uint magic = reader.ReadUInt32(); - return Enum.IsDefined(typeof(MachMagic), magic); - } - } - /// /// Removes the code signature load command and signature blob from the file if present. /// Returns true and sets to a non-null value if the file is a MachO file and the signature was removed. diff --git a/src/installer/managed/Microsoft.NET.HostModel/Microsoft.NET.HostModel.csproj b/src/installer/managed/Microsoft.NET.HostModel/Microsoft.NET.HostModel.csproj index af27dd32dcc9a8..c5eaaeb864f4a1 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/Microsoft.NET.HostModel.csproj +++ b/src/installer/managed/Microsoft.NET.HostModel/Microsoft.NET.HostModel.csproj @@ -34,6 +34,7 @@ + From 37a1764161451200deb5f5769d7b612f62ceae0c Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Wed, 12 Nov 2025 13:27:13 -0800 Subject: [PATCH 3/8] Simplify getting Machine, store MachHeader --- .../Common/MachO/BinaryFormat/MachHeader.cs | 1 + .../MachO/MachOImageReader.cs | 117 +++++++----------- 2 files changed, 46 insertions(+), 72 deletions(-) diff --git a/src/coreclr/tools/Common/MachO/BinaryFormat/MachHeader.cs b/src/coreclr/tools/Common/MachO/BinaryFormat/MachHeader.cs index 062f4bd8e4e2e4..6f2d569e72d1ff 100644 --- a/src/coreclr/tools/Common/MachO/BinaryFormat/MachHeader.cs +++ b/src/coreclr/tools/Common/MachO/BinaryFormat/MachHeader.cs @@ -29,6 +29,7 @@ internal struct MachHeader public uint SizeOfCommands { get => _magic.ConvertValue(_sizeOfCommands); set => _sizeOfCommands = _magic.ConvertValue(value); } public bool Is64Bit => _magic is MachMagic.MachHeader64CurrentEndian or MachMagic.MachHeader64OppositeEndian; public MachFileType FileType => (MachFileType)_magic.ConvertValue((uint)_fileType); + public uint CpuType => _magic.ConvertValue(_cpuType); public uint ConvertValue(uint value) => _magic.ConvertValue(value); public ulong ConvertValue(ulong value) => _magic.ConvertValue(value); diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs index f80d8030953ed3..e590399876a013 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs @@ -17,51 +17,29 @@ namespace ILCompiler.Reflection.ReadyToRun.MachO public class MachOImageReader : IBinaryImageReader { private readonly byte[] _image; + private readonly MachHeader _header; private readonly Machine _machine; - private readonly ulong _imageBase; private int? _rtrHeaderRva; - public MachOImageReader(byte[] image) - { - _image = image; + public Machine Machine => _machine; - // Determine machine type from MachO CPU type - _machine = DetermineMachineType(); - _imageBase = 0; // MachO typically uses 0 as base for .dylib files - } + public OperatingSystem OperatingSystem => OperatingSystem.Apple; - private Machine DetermineMachineType() - { - // For now, we'll need to read the MachO header directly from the image - // to determine the CPU type since MachObjectFile doesn't expose this - unsafe - { - fixed (byte* imagePtr = _image) - { - uint magic = BinaryPrimitives.ReadUInt32LittleEndian(new ReadOnlySpan(_image, 0, 4)); + public bool HasMetadata => false; - // Check if this is a 64-bit Mach-O - bool is64Bit = (MachMagic)magic is MachMagic.MachHeader64CurrentEndian or MachMagic.MachHeader64OppositeEndian; + public ulong ImageBase => 0; - if (!is64Bit) - { - throw new BadImageFormatException("Only 64-bit Mach-O files are supported"); - } + public MachOImageReader(byte[] image) + { + _image = image; - // Read CPU type at offset 4 - uint cpuType = BinaryPrimitives.ReadUInt32LittleEndian(new ReadOnlySpan(_image, 4, 4)); + // Read the MachO header + Read(0, out _header); + if (!_header.Is64Bit) + throw new BadImageFormatException("Only 64-bit Mach-O files are supported"); - // https://github.com/apple-oss-distributions/xnu/blob/f6217f891ac0bb64f3d375211650a4c1ff8ca1ea/osfmk/mach/machine.h - const uint CPU_TYPE_ARM64 = 0x0100000C; - const uint CPU_TYPE_X86_64 = 0x01000007; - return cpuType switch - { - CPU_TYPE_ARM64 => Machine.Arm64, - CPU_TYPE_X86_64 => Machine.Amd64, - _ => throw new NotSupportedException($"Unsupported MachO CPU type: {cpuType:X8}") - }; - } - } + // Determine machine type from CPU type + _machine = GetMachineType(_header.CpuType); } public ImmutableArray GetEntireImage() @@ -71,11 +49,11 @@ public ImmutableArray GetEntireImage() public int GetOffset(int rva) { - if (TryGetContainingSegment((ulong)rva, out Segment64LoadCommand segment, out MachHeader header)) + if (TryGetContainingSegment((ulong)rva, out Segment64LoadCommand segment)) { // Calculate file offset from segment base and RVA - ulong offsetWithinSegment = (ulong)rva - segment.GetVMAddress(header); - ulong fileOffset = segment.GetFileOffset(header) + offsetWithinSegment; + ulong offsetWithinSegment = (ulong)rva - segment.GetVMAddress(_header); + ulong fileOffset = segment.GetFileOffset(_header) + offsetWithinSegment; return (int)fileOffset; } else @@ -106,16 +84,6 @@ private int FindRTRHeaderSymbol() return 0; } - public Machine Machine => _machine; - - public OperatingSystem OperatingSystem => OperatingSystem.Apple; - - public bool HasMetadata => false; // Mach-O composite R2R images don't have embedded ECMA metadata - - public ulong ImageBase => _imageBase; - - public bool IsILLibrary => false; - public void DumpImageInformation(TextWriter writer) { // TODO: Print information from the Mach-O header @@ -127,6 +95,19 @@ public Dictionary GetSections() return []; } + private static Machine GetMachineType(uint cpuType) + { + // https://github.com/apple-oss-distributions/xnu/blob/f6217f891ac0bb64f3d375211650a4c1ff8ca1ea/osfmk/mach/machine.h + const uint CPU_TYPE_ARM64 = 0x0100000C; + const uint CPU_TYPE_X86_64 = 0x01000007; + return cpuType switch + { + CPU_TYPE_ARM64 => Machine.Arm64, + CPU_TYPE_X86_64 => Machine.Amd64, + _ => throw new NotSupportedException($"Unsupported MachO CPU type: {cpuType:X8}") + }; + } + /// /// Finds a symbol in the symbol table by name. /// @@ -137,26 +118,23 @@ private unsafe bool TryFindSymbol(string symbolName, out ulong symbolValue) { symbolValue = 0; - // Read the header - Read(0, out MachHeader header); - // Find the symbol table load command long commandsPtr = sizeof(MachHeader); SymbolTableLoadCommand symtabCommand = default; bool foundSymtab = false; - for (int i = 0; i < header.NumberOfCommands; i++) + for (int i = 0; i < _header.NumberOfCommands; i++) { Read(commandsPtr, out LoadCommand loadCommand); - if (loadCommand.GetCommandType(header) == MachLoadCommandType.SymbolTable) + if (loadCommand.GetCommandType(_header) == MachLoadCommandType.SymbolTable) { Read(commandsPtr, out symtabCommand); foundSymtab = true; break; } - commandsPtr += loadCommand.GetCommandSize(header); + commandsPtr += loadCommand.GetCommandSize(_header); } if (!foundSymtab || symtabCommand.IsDefault) @@ -164,10 +142,10 @@ private unsafe bool TryFindSymbol(string symbolName, out ulong symbolValue) return false; } - uint symbolTableOffset = symtabCommand.GetSymbolTableOffset(header); - uint symbolsCount = symtabCommand.GetSymbolsCount(header); - uint stringTableOffset = symtabCommand.GetStringTableOffset(header); - uint stringTableSize = symtabCommand.GetStringTableSize(header); + uint symbolTableOffset = symtabCommand.GetSymbolTableOffset(_header); + uint symbolsCount = symtabCommand.GetSymbolsCount(_header); + uint stringTableOffset = symtabCommand.GetStringTableOffset(_header); + uint stringTableSize = symtabCommand.GetStringTableSize(_header); for (uint i = 0; i < symbolsCount; i++) { @@ -176,19 +154,18 @@ private unsafe bool TryFindSymbol(string symbolName, out ulong symbolValue) // Read the symbol table entry Read(symOffset, out NList64 symbol); - uint strIndex = symbol.GetStringTableIndex(header); + uint strIndex = symbol.GetStringTableIndex(_header); if (strIndex >= stringTableSize) { continue; } - // Read symbol name from string table string name = ReadCString(stringTableOffset + strIndex, stringTableSize - strIndex); // Symbol names in Mach-O can have a leading underscore if (name == symbolName || (name.Length > 0 && name[0] == '_' && name.Substring(1) == symbolName)) { - symbolValue = symbol.GetValue(header); + symbolValue = symbol.GetValue(_header); return true; } } @@ -201,29 +178,25 @@ private unsafe bool TryFindSymbol(string symbolName, out ulong symbolValue) /// /// The virtual memory address to find. /// The segment containing the VM address if found. - /// The Mach-O header (output parameter). /// True if a containing segment was found, false otherwise. - private unsafe bool TryGetContainingSegment(ulong vmAddress, out Segment64LoadCommand segment, out MachHeader header) + private unsafe bool TryGetContainingSegment(ulong vmAddress, out Segment64LoadCommand segment) { segment = default; - // Read the header - Read(0, out header); - // Iterate through all load commands to find segments long commandsPtr = sizeof(MachHeader); - for (int i = 0; i < header.NumberOfCommands; i++) + for (int i = 0; i < _header.NumberOfCommands; i++) { Read(commandsPtr, out LoadCommand loadCommand); - if (loadCommand.GetCommandType(header) == MachLoadCommandType.Segment64) + if (loadCommand.GetCommandType(_header) == MachLoadCommandType.Segment64) { Read(commandsPtr, out Segment64LoadCommand seg); // Check if the VM address falls within this segment - ulong segmentVMAddr = seg.GetVMAddress(header); - ulong segmentVMSize = seg.GetVMSize(header); + ulong segmentVMAddr = seg.GetVMAddress(_header); + ulong segmentVMSize = seg.GetVMSize(_header); if (vmAddress >= segmentVMAddr && vmAddress < segmentVMAddr + segmentVMSize) { segment = seg; @@ -231,7 +204,7 @@ private unsafe bool TryGetContainingSegment(ulong vmAddress, out Segment64LoadCo } } - commandsPtr += loadCommand.GetCommandSize(header); + commandsPtr += loadCommand.GetCommandSize(_header); } return false; From 9d07517517cb9b80fb0f498d17125d0bb7b2b64f Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Wed, 12 Nov 2025 13:30:45 -0800 Subject: [PATCH 4/8] Combine getting composite or regular header into one function --- .../IBinaryImageReader.cs | 12 ++--- .../MachO/MachOImageReader.cs | 6 ++- .../PEImageReader.cs | 48 +++++++++++++------ .../ReadyToRunReader.cs | 37 +++----------- 4 files changed, 48 insertions(+), 55 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/IBinaryImageReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/IBinaryImageReader.cs index 2279a732859967..48a2ed92b7557c 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/IBinaryImageReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/IBinaryImageReader.cs @@ -25,11 +25,12 @@ public interface IBinaryImageReader int GetOffset(int rva); /// - /// Check whether the file is a composite ReadyToRun image and returns the RVA of its ReadyToRun header if so. + /// Try to get the ReadyToRun header RVA for this image. /// /// RVA of the ReadyToRun header if available, 0 when not - /// true when the reader represents a composite ReadyToRun image, false otherwise - bool TryGetCompositeReadyToRunHeader(out int rva); + /// true when the reader represents a composite ReadyToRun image, false for regular R2R + /// true when the reader represents a ReadyToRun image (composite or regular), false otherwise + bool TryGetReadyToRunHeader(out int rva, out bool isComposite); /// /// Write out image information using the specified writer @@ -58,10 +59,5 @@ public interface IBinaryImageReader /// Gets the image base address /// ulong ImageBase { get; } - - /// - /// Get whether the image is marked as an IL-only library - /// - bool IsILLibrary { get; } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs index e590399876a013..c905fa9017eabd 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs @@ -36,7 +36,7 @@ public MachOImageReader(byte[] image) // Read the MachO header Read(0, out _header); if (!_header.Is64Bit) - throw new BadImageFormatException("Only 64-bit Mach-O files are supported"); + throw new BadImageFormatException("Only 64-bit Mach-O files are supported"); // Determine machine type from CPU type _machine = GetMachineType(_header.CpuType); @@ -62,15 +62,17 @@ public int GetOffset(int rva) } } - public bool TryGetCompositeReadyToRunHeader(out int rva) + public bool TryGetReadyToRunHeader(out int rva, out bool isComposite) { if (!_rtrHeaderRva.HasValue) { // Look for RTR_HEADER symbol in the Mach-O symbol table + // Mach-O R2R images are always composite (no regular R2R format) _rtrHeaderRva = FindRTRHeaderSymbol(); } rva = _rtrHeaderRva.Value; + isComposite = rva != 0; // Mach-O R2R images are always composite return rva != 0; } diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PEImageReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PEImageReader.cs index 81f6986efb98a7..ef8bf1cb2fe992 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PEImageReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PEImageReader.cs @@ -18,6 +18,16 @@ public class PEImageReader : IBinaryImageReader private readonly Machine _machine; private readonly OperatingSystem _operatingSystem; + public Machine Machine => _machine; + + public OperatingSystem OperatingSystem => _operatingSystem; + + public bool HasMetadata => _peReader.HasMetadata; + + public ulong ImageBase => _peReader.PEHeaders.PEHeader.ImageBase; + + public PEReader PEReader => _peReader; + public PEImageReader(PEReader peReader) { _peReader = peReader; @@ -54,22 +64,32 @@ public int GetOffset(int rva) return _peReader.GetOffset(rva); } - public bool TryGetCompositeReadyToRunHeader(out int rva) + public bool TryGetReadyToRunHeader(out int rva, out bool isComposite) { - return _peReader.TryGetCompositeReadyToRunHeader(out rva); - } - - public Machine Machine => _machine; - - public OperatingSystem OperatingSystem => _operatingSystem; - - public bool HasMetadata => _peReader.HasMetadata; - - public ulong ImageBase => _peReader.PEHeaders.PEHeader.ImageBase; - - public PEReader PEReader => _peReader; + if ((_peReader.PEHeaders.CorHeader.Flags & CorFlags.ILLibrary) == 0) + { + // Composite R2R - check for RTR_HEADER export + if (_peReader.TryGetCompositeReadyToRunHeader(out rva)) + { + isComposite = true; + return true; + } + } + else + { + var r2rHeaderDirectory = _peReader.PEHeaders.CorHeader.ManagedNativeHeaderDirectory; + if (r2rHeaderDirectory.Size != 0) + { + rva = r2rHeaderDirectory.RelativeVirtualAddress; + isComposite = false; + return true; + } + } - public bool IsILLibrary => _peReader.PEHeaders.CorHeader?.Flags.HasFlag(CorFlags.ILLibrary) ?? false; + rva = 0; + isComposite = false; + return false; + } public void DumpImageInformation(TextWriter writer) { diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs index baacd48e812ce8..bffb746a827e8a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs @@ -520,35 +520,15 @@ private unsafe void Initialize(IAssemblyMetadata metadata) metadata = new StandaloneAssemblyMetadata(((PEImageReader)CompositeReader).PEReader); } - if (metadata != null) + if (!CompositeReader.TryGetReadyToRunHeader(out _readyToRunHeaderRVA, out _composite)) { - if (!CompositeReader.IsILLibrary) - { - // Composite image - if (!TryLocateNativeReadyToRunHeader()) - { - DumpImageInformation(); - - throw new BadImageFormatException("The file is not a ReadyToRun image"); - } - - Debug.Assert(Composite); - } - else - { - // Regular PE R2R assembly - _assemblyCache.Add(metadata); - - Debug.Assert(CompositeReader is PEImageReader); - DirectoryEntry r2rHeaderDirectory = ((PEImageReader)CompositeReader).PEReader.PEHeaders.CorHeader.ManagedNativeHeaderDirectory; - _readyToRunHeaderRVA = r2rHeaderDirectory.RelativeVirtualAddress; - Debug.Assert(!Composite); - } - + throw new BadImageFormatException("The file is not a ReadyToRun image"); } - else if (!TryLocateNativeReadyToRunHeader()) + + Debug.Assert(metadata != null || _composite); + if (metadata != null && !_composite) { - throw new BadImageFormatException($"ECMA metadata / RTR_HEADER not found in file '{Filename}'"); + _assemblyCache.Add(metadata); } } catch (BadImageFormatException) @@ -657,12 +637,7 @@ public IReadOnlyDictionary GetCustomMethodToRuntimeFu return customMethods; } - private bool TryLocateNativeReadyToRunHeader() - { - _composite = CompositeReader.TryGetCompositeReadyToRunHeader(out _readyToRunHeaderRVA); - return _composite; - } private IAssemblyMetadata GetSystemModuleMetadataReader() { From 7ab47c20a916753571dfb601fdfd513ecbfb62fc Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Wed, 12 Nov 2025 14:18:24 -0800 Subject: [PATCH 5/8] Move getting r2r manifest / standalone assembly metadata to image readers --- .../IBinaryImageReader.cs | 19 ++++++-- .../MachO/MachOImageReader.cs | 18 ++++--- .../PEImageReader.cs | 48 ++++++++----------- .../ReadyToRunReader.cs | 9 ++-- 4 files changed, 46 insertions(+), 48 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/IBinaryImageReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/IBinaryImageReader.cs index 48a2ed92b7557c..e01f5a6d52cb7c 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/IBinaryImageReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/IBinaryImageReader.cs @@ -38,6 +38,9 @@ public interface IBinaryImageReader /// The writer to use void DumpImageInformation(TextWriter writer); + /// + /// Gets the sections (name and size) of the binary image + /// Dictionary GetSections(); /// @@ -51,13 +54,21 @@ public interface IBinaryImageReader OperatingSystem OperatingSystem { get; } /// - /// Gets whether the image has metadata + /// Gets the image base address /// - bool HasMetadata { get; } + ulong ImageBase { get; } /// - /// Gets the image base address + /// Creates standalone assembly metadata from the image's embedded metadata. /// - ulong ImageBase { get; } + /// Assembly metadata, or null if the image has no embedded metadata + IAssemblyMetadata GetStandaloneAssemblyMetadata(); + + /// + /// Creates manifest assembly metadata the R2R manifest + /// + /// Manifest metadata reader + /// Manifest assembly metadata + IAssemblyMetadata GetManifestAssemblyMetadata(System.Reflection.Metadata.MetadataReader manifestReader); } } diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs index c905fa9017eabd..ef36a2fa08f07d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs @@ -18,15 +18,10 @@ public class MachOImageReader : IBinaryImageReader { private readonly byte[] _image; private readonly MachHeader _header; - private readonly Machine _machine; private int? _rtrHeaderRva; - public Machine Machine => _machine; - + public Machine Machine { get; } public OperatingSystem OperatingSystem => OperatingSystem.Apple; - - public bool HasMetadata => false; - public ulong ImageBase => 0; public MachOImageReader(byte[] image) @@ -39,13 +34,11 @@ public MachOImageReader(byte[] image) throw new BadImageFormatException("Only 64-bit Mach-O files are supported"); // Determine machine type from CPU type - _machine = GetMachineType(_header.CpuType); + Machine = GetMachineType(_header.CpuType); } public ImmutableArray GetEntireImage() - { - return Unsafe.As>(ref Unsafe.AsRef(in _image)); - } + => Unsafe.As>(ref Unsafe.AsRef(in _image)); public int GetOffset(int rva) { @@ -97,6 +90,11 @@ public Dictionary GetSections() return []; } + public IAssemblyMetadata GetStandaloneAssemblyMetadata() => null; + + public IAssemblyMetadata GetManifestAssemblyMetadata(System.Reflection.Metadata.MetadataReader manifestReader) + => new ManifestAssemblyMetadata(manifestReader); + private static Machine GetMachineType(uint cpuType) { // https://github.com/apple-oss-distributions/xnu/blob/f6217f891ac0bb64f3d375211650a4c1ff8ca1ea/osfmk/mach/machine.h diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PEImageReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PEImageReader.cs index ef8bf1cb2fe992..7cd4a5354acddc 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PEImageReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PEImageReader.cs @@ -15,19 +15,11 @@ namespace ILCompiler.Reflection.ReadyToRun public class PEImageReader : IBinaryImageReader { private readonly PEReader _peReader; - private readonly Machine _machine; - private readonly OperatingSystem _operatingSystem; - - public Machine Machine => _machine; - - public OperatingSystem OperatingSystem => _operatingSystem; - - public bool HasMetadata => _peReader.HasMetadata; + public Machine Machine { get; } + public OperatingSystem OperatingSystem { get; } public ulong ImageBase => _peReader.PEHeaders.PEHeader.ImageBase; - public PEReader PEReader => _peReader; - public PEImageReader(PEReader peReader) { _peReader = peReader; @@ -35,34 +27,28 @@ public PEImageReader(PEReader peReader) // Extract machine and OS from PE header // The OS is encoded in the machine type uint rawMachine = (uint)_peReader.PEHeaders.CoffHeader.Machine; - _operatingSystem = OperatingSystem.Unknown; + OperatingSystem = OperatingSystem.Unknown; foreach (OperatingSystem os in System.Enum.GetValues(typeof(OperatingSystem))) { Machine candidateMachine = (Machine)(rawMachine ^ (uint)os); if (System.Enum.IsDefined(typeof(Machine), candidateMachine)) { - _machine = candidateMachine; - _operatingSystem = os; + Machine = candidateMachine; + OperatingSystem = os; break; } } - if (_operatingSystem == OperatingSystem.Unknown) + if (OperatingSystem == OperatingSystem.Unknown) { throw new BadImageFormatException($"Invalid PE Machine type: {rawMachine}"); } } - public ImmutableArray GetEntireImage() - { - return _peReader.GetEntireImage().GetContent(); - } + public ImmutableArray GetEntireImage() => _peReader.GetEntireImage().GetContent(); - public int GetOffset(int rva) - { - return _peReader.GetOffset(rva); - } + public int GetOffset(int rva) => _peReader.GetOffset(rva); public bool TryGetReadyToRunHeader(out int rva, out bool isComposite) { @@ -93,9 +79,9 @@ public bool TryGetReadyToRunHeader(out int rva, out bool isComposite) public void DumpImageInformation(TextWriter writer) { - writer.WriteLine($"MetadataSize: {PEReader.PEHeaders.MetadataSize} byte(s)"); + writer.WriteLine($"MetadataSize: {_peReader.PEHeaders.MetadataSize} byte(s)"); - if (PEReader.PEHeaders.PEHeader is PEHeader header) + if (_peReader.PEHeaders.PEHeader is PEHeader header) { writer.WriteLine($"SizeOfImage: {header.SizeOfImage} byte(s)"); writer.WriteLine($"ImageBase: 0x{header.ImageBase:X}"); @@ -107,25 +93,31 @@ public void DumpImageInformation(TextWriter writer) writer.WriteLine("No PEHeader"); } - writer.WriteLine($"CorHeader.Flags: {PEReader.PEHeaders.CorHeader?.Flags}"); + writer.WriteLine($"CorHeader.Flags: {_peReader.PEHeaders.CorHeader?.Flags}"); writer.WriteLine("Sections:"); - foreach (var section in PEReader.PEHeaders.SectionHeaders) + foreach (var section in _peReader.PEHeaders.SectionHeaders) writer.WriteLine($" {section.Name} {section.VirtualAddress} - {(section.VirtualAddress + section.VirtualSize)}"); - var exportTable = PEReader.GetExportTable(); + var exportTable = _peReader.GetExportTable(); exportTable.DumpToConsoleError(); } public Dictionary GetSections() { Dictionary sectionMap = []; - foreach (SectionHeader sectionHeader in PEReader.PEHeaders.SectionHeaders) + foreach (SectionHeader sectionHeader in _peReader.PEHeaders.SectionHeaders) { sectionMap.Add(sectionHeader.Name, sectionHeader.SizeOfRawData); } return sectionMap; } + + public IAssemblyMetadata GetStandaloneAssemblyMetadata() + => _peReader.HasMetadata ? new StandaloneAssemblyMetadata(_peReader) : null; + + public IAssemblyMetadata GetManifestAssemblyMetadata(System.Reflection.Metadata.MetadataReader manifestReader) + => new ManifestAssemblyMetadata(_peReader, manifestReader); } } diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs index bffb746a827e8a..1b184a1a8b61f1 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs @@ -514,10 +514,9 @@ private unsafe void Initialize(IAssemblyMetadata metadata) ImagePin = new PinningReference(Image); } - if (metadata == null && ContainerFormat == ReadyToRunContainerFormat.PE && CompositeReader.HasMetadata) + if (metadata == null) { - Debug.Assert(CompositeReader is PEImageReader); - metadata = new StandaloneAssemblyMetadata(((PEImageReader)CompositeReader).PEReader); + metadata = CompositeReader.GetStandaloneAssemblyMetadata(); } if (!CompositeReader.TryGetReadyToRunHeader(out _readyToRunHeaderRVA, out _composite)) @@ -752,9 +751,7 @@ private unsafe void EnsureManifestReferences() fixed (byte* image = Image) { _manifestReader = new MetadataReader(image + GetOffset(manifestMetadata.RelativeVirtualAddress), manifestMetadata.Size); - _manifestAssemblyMetadata = ContainerFormat == ReadyToRunContainerFormat.PE - ? new ManifestAssemblyMetadata(((PEImageReader)CompositeReader).PEReader, _manifestReader) - : new ManifestAssemblyMetadata(_manifestReader); + _manifestAssemblyMetadata = CompositeReader.GetManifestAssemblyMetadata(_manifestReader); int assemblyRefCount = _manifestReader.GetTableRowCount(TableIndex.AssemblyRef); for (int assemblyRefIndex = 1; assemblyRefIndex <= assemblyRefCount; assemblyRefIndex++) From 3e514207e76221bb9ec6365817f38abeb0607a87 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Wed, 12 Nov 2025 15:35:38 -0800 Subject: [PATCH 6/8] Handle DumpImageInformation and GetSections for Mach-O --- .../Common/MachO/BinaryFormat/NameBuffer.cs | 19 ++++++- .../BinaryFormat/Section64LoadCommand.cs | 3 + .../IBinaryImageReader.cs | 53 +++++++++--------- .../MachO/MachOImageReader.cs | 56 +++++++++++++++++-- .../PEImageReader.cs | 12 ++-- 5 files changed, 104 insertions(+), 39 deletions(-) diff --git a/src/coreclr/tools/Common/MachO/BinaryFormat/NameBuffer.cs b/src/coreclr/tools/Common/MachO/BinaryFormat/NameBuffer.cs index f44389326b69d2..fd8e1f2a1433f8 100644 --- a/src/coreclr/tools/Common/MachO/BinaryFormat/NameBuffer.cs +++ b/src/coreclr/tools/Common/MachO/BinaryFormat/NameBuffer.cs @@ -19,9 +19,11 @@ internal struct NameBuffer private ulong _nameLower; private ulong _nameUpper; + private const int BufferLength = 16; + private NameBuffer(ReadOnlySpan nameBytes) { - byte[] buffer = new byte[16]; + byte[] buffer = new byte[BufferLength]; nameBytes.CopyTo(buffer); if (BitConverter.IsLittleEndian) @@ -38,4 +40,19 @@ private NameBuffer(ReadOnlySpan nameBytes) public static NameBuffer __TEXT = new NameBuffer("__TEXT"u8); public static NameBuffer __LINKEDIT = new NameBuffer("__LINKEDIT"u8); + + public unsafe string GetString() + { + fixed (ulong* ptr = &_nameLower) + { + byte* bytePtr = (byte*)ptr; + int length = 0; + while (length < BufferLength && bytePtr[length] != 0) + { + length++; + } + + return System.Text.Encoding.UTF8.GetString(bytePtr, length); + } + } } diff --git a/src/coreclr/tools/Common/MachO/BinaryFormat/Section64LoadCommand.cs b/src/coreclr/tools/Common/MachO/BinaryFormat/Section64LoadCommand.cs index 6b6dcae882e2f8..c878507522d595 100644 --- a/src/coreclr/tools/Common/MachO/BinaryFormat/Section64LoadCommand.cs +++ b/src/coreclr/tools/Common/MachO/BinaryFormat/Section64LoadCommand.cs @@ -29,5 +29,8 @@ internal readonly struct Section64LoadCommand private readonly uint _reserved2; private readonly uint _reserved3; + internal NameBuffer SectionName => _sectionName; + internal ulong GetVMAddress(MachHeader header) => header.ConvertValue(_address); + internal ulong GetSize(MachHeader header) => header.ConvertValue(_size); internal uint GetFileOffset(MachHeader header) => header.ConvertValue(_fileOffset); } diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/IBinaryImageReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/IBinaryImageReader.cs index e01f5a6d52cb7c..27a149a1616c3f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/IBinaryImageReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/IBinaryImageReader.cs @@ -13,6 +13,21 @@ namespace ILCompiler.Reflection.ReadyToRun /// public interface IBinaryImageReader { + /// + /// Gets the machine type of the binary image + /// + Machine Machine { get; } + + /// + /// Gets the operating system of the binary image + /// + OperatingSystem OperatingSystem { get; } + + /// + /// Gets the image base address + /// + ulong ImageBase { get; } + /// /// Get the entire image content /// @@ -32,32 +47,6 @@ public interface IBinaryImageReader /// true when the reader represents a ReadyToRun image (composite or regular), false otherwise bool TryGetReadyToRunHeader(out int rva, out bool isComposite); - /// - /// Write out image information using the specified writer - /// - /// The writer to use - void DumpImageInformation(TextWriter writer); - - /// - /// Gets the sections (name and size) of the binary image - /// - Dictionary GetSections(); - - /// - /// Gets the machine type of the binary image - /// - Machine Machine { get; } - - /// - /// Gets the operating system of the binary image - /// - OperatingSystem OperatingSystem { get; } - - /// - /// Gets the image base address - /// - ulong ImageBase { get; } - /// /// Creates standalone assembly metadata from the image's embedded metadata. /// @@ -70,5 +59,17 @@ public interface IBinaryImageReader /// Manifest metadata reader /// Manifest assembly metadata IAssemblyMetadata GetManifestAssemblyMetadata(System.Reflection.Metadata.MetadataReader manifestReader); + + /// + /// Write out image information using the specified writer + /// + /// The writer to use + void DumpImageInformation(TextWriter writer); + + /// + /// Gets the sections (name and size) of the binary image + /// + Dictionary GetSections(); + } } diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs index ef36a2fa08f07d..108dbd31cacafe 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs @@ -79,21 +79,65 @@ private int FindRTRHeaderSymbol() return 0; } + public IAssemblyMetadata GetStandaloneAssemblyMetadata() => null; + + public IAssemblyMetadata GetManifestAssemblyMetadata(System.Reflection.Metadata.MetadataReader manifestReader) + => new ManifestAssemblyMetadata(manifestReader); + public void DumpImageInformation(TextWriter writer) { - // TODO: Print information from the Mach-O header + writer.WriteLine($"FileType: {_header.FileType}"); + writer.WriteLine($"CpuType: 0x{_header.CpuType:X}"); + writer.WriteLine($"NumberOfCommands: {_header.NumberOfCommands}"); + writer.WriteLine($"SizeOfCommands: {_header.SizeOfCommands} byte(s)"); + + writer.WriteLine("Sections:"); + EnumerateSections((segmentName, section) => + { + string sectionName = section.SectionName.GetString(); + ulong vmAddr = section.GetVMAddress(_header); + ulong size = section.GetSize(_header); + writer.WriteLine($" {segmentName},{sectionName,-16} 0x{vmAddr:X8} - 0x{vmAddr + size:X8}"); + }); } public Dictionary GetSections() { - // TODO: Get all the sections - return []; + Dictionary sectionMap = []; + EnumerateSections((segmentName, section) => + { + string sectionName = section.SectionName.GetString(); + sectionMap[$"{segmentName},{sectionName}"] = (int)section.GetSize(_header); + }); + return sectionMap; } - public IAssemblyMetadata GetStandaloneAssemblyMetadata() => null; + private unsafe void EnumerateSections(Action callback) + { + long commandsPtr = sizeof(MachHeader); + for (int i = 0; i < _header.NumberOfCommands; i++) + { + Read(commandsPtr, out LoadCommand loadCommand); - public IAssemblyMetadata GetManifestAssemblyMetadata(System.Reflection.Metadata.MetadataReader manifestReader) - => new ManifestAssemblyMetadata(manifestReader); + if (loadCommand.GetCommandType(_header) == MachLoadCommandType.Segment64) + { + Read(commandsPtr, out Segment64LoadCommand segment); + uint sectionsCount = segment.GetSectionsCount(_header); + string segmentName = segment.Name.GetString(); + + // Sections come immediately after the segment load command + long sectionPtr = commandsPtr + sizeof(Segment64LoadCommand); + for (uint j = 0; j < sectionsCount; j++) + { + Read(sectionPtr, out Section64LoadCommand section); + callback(segmentName, section); + sectionPtr += sizeof(Section64LoadCommand); + } + } + + commandsPtr += loadCommand.GetCommandSize(_header); + } + } private static Machine GetMachineType(uint cpuType) { diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PEImageReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PEImageReader.cs index 7cd4a5354acddc..739b9022e22e6b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PEImageReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PEImageReader.cs @@ -77,6 +77,12 @@ public bool TryGetReadyToRunHeader(out int rva, out bool isComposite) return false; } + public IAssemblyMetadata GetStandaloneAssemblyMetadata() + => _peReader.HasMetadata ? new StandaloneAssemblyMetadata(_peReader) : null; + + public IAssemblyMetadata GetManifestAssemblyMetadata(System.Reflection.Metadata.MetadataReader manifestReader) + => new ManifestAssemblyMetadata(_peReader, manifestReader); + public void DumpImageInformation(TextWriter writer) { writer.WriteLine($"MetadataSize: {_peReader.PEHeaders.MetadataSize} byte(s)"); @@ -113,11 +119,5 @@ public Dictionary GetSections() return sectionMap; } - - public IAssemblyMetadata GetStandaloneAssemblyMetadata() - => _peReader.HasMetadata ? new StandaloneAssemblyMetadata(_peReader) : null; - - public IAssemblyMetadata GetManifestAssemblyMetadata(System.Reflection.Metadata.MetadataReader manifestReader) - => new ManifestAssemblyMetadata(_peReader, manifestReader); } } From dd169c82ac2a7a5da4ca0df5884e6714cf03bd6e Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Thu, 13 Nov 2025 16:26:08 -0800 Subject: [PATCH 7/8] Copilot comments --- .../IBinaryImageReader.cs | 2 +- .../MachO/MachOImageReader.cs | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/IBinaryImageReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/IBinaryImageReader.cs index 27a149a1616c3f..54b20e0012d6b3 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/IBinaryImageReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/IBinaryImageReader.cs @@ -54,7 +54,7 @@ public interface IBinaryImageReader IAssemblyMetadata GetStandaloneAssemblyMetadata(); /// - /// Creates manifest assembly metadata the R2R manifest + /// Creates manifest assembly metadata from the R2R manifest /// /// Manifest metadata reader /// Manifest assembly metadata diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs index 108dbd31cacafe..6796674b415317 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs @@ -47,6 +47,7 @@ public int GetOffset(int rva) // Calculate file offset from segment base and RVA ulong offsetWithinSegment = (ulong)rva - segment.GetVMAddress(_header); ulong fileOffset = segment.GetFileOffset(_header) + offsetWithinSegment; + System.Diagnostics.Debug.Assert(fileOffset <= int.MaxValue); return (int)fileOffset; } else @@ -107,7 +108,9 @@ public Dictionary GetSections() EnumerateSections((segmentName, section) => { string sectionName = section.SectionName.GetString(); - sectionMap[$"{segmentName},{sectionName}"] = (int)section.GetSize(_header); + ulong size = section.GetSize(_header); + System.Diagnostics.Debug.Assert(size <= int.MaxValue); + sectionMap[$"{segmentName},{sectionName}"] = (int)size; }); return sectionMap; } @@ -203,6 +206,7 @@ private unsafe bool TryFindSymbol(string symbolName, out ulong symbolValue) { continue; } + // Read symbol name from string table string name = ReadCString(stringTableOffset + strIndex, stringTableSize - strIndex); @@ -257,7 +261,7 @@ private unsafe bool TryGetContainingSegment(ulong vmAddress, out Segment64LoadCo /// /// Reads a null-terminated C string from the image. /// - private string ReadCString(long offset, uint maxLength) + private string ReadCString(uint offset, uint maxLength) { if (offset < 0 || offset >= _image.Length) { @@ -266,7 +270,7 @@ private string ReadCString(long offset, uint maxLength) // Find the null terminator in the image array int length; - int end = (int)Math.Min(offset + maxLength, _image.Length); + long end = Math.Min(offset + maxLength, _image.Length); for (length = 0; offset + length < end; length++) { if (_image[offset + length] == 0) @@ -275,6 +279,7 @@ private string ReadCString(long offset, uint maxLength) } } + System.Diagnostics.Debug.Assert(offset <= int.MaxValue); return System.Text.Encoding.UTF8.GetString(_image, (int)offset, length); } @@ -289,7 +294,7 @@ public void Read(long offset, out T result) where T : unmanaged fixed (byte* ptr = &_image[offset]) { - result = *(T*)ptr; + result = Unsafe.ReadUnaligned(ptr); } } } From ff0656d5bd2bbc8a0628a2805d60b1c89deb2405 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Thu, 13 Nov 2025 16:32:38 -0800 Subject: [PATCH 8/8] Use AsSpan instead of Substring --- .../MachO/MachOImageReader.cs | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs index 6796674b415317..1e65f55f0ce3e5 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/MachO/MachOImageReader.cs @@ -62,7 +62,15 @@ public bool TryGetReadyToRunHeader(out int rva, out bool isComposite) { // Look for RTR_HEADER symbol in the Mach-O symbol table // Mach-O R2R images are always composite (no regular R2R format) - _rtrHeaderRva = FindRTRHeaderSymbol(); + if (TryFindSymbol("RTR_HEADER", out ulong symbolValue)) + { + System.Diagnostics.Debug.Assert(symbolValue <= int.MaxValue); + _rtrHeaderRva = (int)symbolValue; + } + else + { + _rtrHeaderRva = 0; + } } rva = _rtrHeaderRva.Value; @@ -70,16 +78,6 @@ public bool TryGetReadyToRunHeader(out int rva, out bool isComposite) return rva != 0; } - private int FindRTRHeaderSymbol() - { - if (TryFindSymbol("RTR_HEADER", out ulong symbolValue)) - { - return (int)symbolValue; - } - - return 0; - } - public IAssemblyMetadata GetStandaloneAssemblyMetadata() => null; public IAssemblyMetadata GetManifestAssemblyMetadata(System.Reflection.Metadata.MetadataReader manifestReader) @@ -211,7 +209,7 @@ private unsafe bool TryFindSymbol(string symbolName, out ulong symbolValue) string name = ReadCString(stringTableOffset + strIndex, stringTableSize - strIndex); // Symbol names in Mach-O can have a leading underscore - if (name == symbolName || (name.Length > 0 && name[0] == '_' && name.Substring(1) == symbolName)) + if (name == symbolName || (name.Length > 0 && name[0] == '_' && name.AsSpan(1).SequenceEqual(symbolName))) { symbolValue = symbol.GetValue(_header); return true;