From 0dc1dd7e65dd8c21f44eff11519cbf7d22735f16 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 12 Dec 2025 15:04:00 -0800 Subject: [PATCH 1/9] Reduce the number of sections in R2R PE files Use ObjectNodePhase.Ordered + ObjectNodeOrder to identify "well-known" nodes that need to go into PE data directory entries. Also provide a mechanism to fold together sections in the ObjectWriter so we can fold rdata into text and use the "managed code" section for managed code in general and only fold to text in emit. In the process, fix some implicit dependencies that were hidden by "good" ClassCode choices. --- .../DependencyAnalysis/ObjectNodeSection.cs | 6 - .../SortableDependencyNode.cs | 8 +- .../Compiler/ObjectWriter/CoffObjectWriter.cs | 4 +- .../Compiler/ObjectWriter/ObjectWriter.cs | 23 ++++ .../Compiler/ObjectWriter/PEObjectWriter.cs | 112 +++++++++++------- .../ObjectWriter/CoffObjectWriter.Aot.cs | 10 +- .../ReadyToRun/CopiedCorHeaderNode.cs | 9 +- .../ReadyToRun/CopiedFieldRvaNode.cs | 9 +- .../ReadyToRun/CopiedManagedResourcesNode.cs | 9 +- .../ReadyToRun/CopiedMetadataBlobNode.cs | 9 +- .../ReadyToRun/CopiedMethodILNode.cs | 9 +- .../CopiedStrongNameSignatureNode.cs | 9 +- .../ReadyToRun/DebugDirectoryNode.cs | 4 +- .../ReadyToRun/MethodColdCodeNode.cs | 6 +- .../ReadyToRun/MethodWithGCInfo.cs | 6 +- .../ReadyToRun/RuntimeFunctionsGCInfoNode.cs | 14 +-- .../ReadyToRun/RuntimeFunctionsTableNode.cs | 12 +- .../ReadyToRun/Win32ResourcesNode.cs | 9 +- 18 files changed, 127 insertions(+), 141 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNodeSection.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNodeSection.cs index a226d8f4af2e1f..edc11ea248cf43 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNodeSection.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNodeSection.cs @@ -32,7 +32,6 @@ public ObjectNodeSection(string name, SectionType type, string comdatName) public ObjectNodeSection(string name, SectionType type) : this(name, type, null) { } - public static readonly ObjectNodeSection XDataSection = new ObjectNodeSection("xdata", SectionType.ReadOnly); public static readonly ObjectNodeSection DataSection = new ObjectNodeSection("data", SectionType.Writeable); public static readonly ObjectNodeSection ReadOnlyDataSection = new ObjectNodeSection("rdata", SectionType.ReadOnly); public static readonly ObjectNodeSection FoldableReadOnlyDataSection = new ObjectNodeSection("rdata", SectionType.ReadOnly); @@ -51,10 +50,5 @@ public ObjectNodeSection(string name, SectionType type) : this(name, type, null) public static readonly ObjectNodeSection ModulesWindowsContentSection = new ObjectNodeSection(".modules$I", SectionType.ReadOnly); public static readonly ObjectNodeSection ModulesUnixContentSection = new ObjectNodeSection("__modules", SectionType.Writeable); - - public static readonly ObjectNodeSection DebugDirectorySection = new ObjectNodeSection("debug", SectionType.Debug); - public static readonly ObjectNodeSection CorMetaSection = new ObjectNodeSection("cormeta", SectionType.ReadOnly); - public static readonly ObjectNodeSection Win32ResourcesSection = new ObjectNodeSection("rsrc", SectionType.ReadOnly); - public static readonly ObjectNodeSection PDataSection = new ObjectNodeSection("pdata", SectionType.ReadOnly); } } diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs index 62297efb6973e0..aa1bf19be261c5 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs @@ -38,7 +38,7 @@ public virtual int CompareToImpl(ISortableNode other, CompilerComparer comparer) throw new NotImplementedException("Multiple nodes of this type are not supported"); } - protected enum ObjectNodePhase + protected internal enum ObjectNodePhase { /// /// Nodes should only be placed in this phase if they have strict output ordering requirements that @@ -49,7 +49,7 @@ protected enum ObjectNodePhase Late, } - protected enum ObjectNodeOrder + protected internal enum ObjectNodeOrder { // // The ordering of this sequence of nodes is deliberate and currently required for @@ -66,7 +66,9 @@ protected enum ObjectNodeOrder ImportSectionsTableNode, ImportSectionNode, MethodEntrypointTableNode, - + DebugDirectoryNode, + RuntimeFunctionsGCInfoNode, + RuntimeFunctionsTableNode, // // NativeAOT Nodes diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/CoffObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/CoffObjectWriter.cs index dda356a81abd5a..525a32bcf408b7 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/CoffObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/CoffObjectWriter.cs @@ -89,6 +89,8 @@ private protected override void CreateSection(ObjectNodeSection section, Utf8Str { SectionType.ReadOnly => SectionCharacteristics.MemRead | SectionCharacteristics.ContainsInitializedData, + SectionType.UnwindData => + SectionCharacteristics.MemRead | SectionCharacteristics.ContainsInitializedData, SectionType.Writeable => SectionCharacteristics.MemRead | SectionCharacteristics.MemWrite | SectionCharacteristics.ContainsInitializedData, @@ -102,7 +104,7 @@ private protected override void CreateSection(ObjectNodeSection section, Utf8Str } }; - if (section == DebugTypesSection || section == ObjectNodeSection.DebugDirectorySection) + if (section == DebugTypesSection) { sectionHeader.SectionCharacteristics = SectionCharacteristics.MemRead | SectionCharacteristics.ContainsInitializedData | diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs index 02046f9358fa97..1afd4adbabc39d 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs @@ -65,6 +65,18 @@ private protected ObjectWriter(NodeFactory factory, ObjectWritingOptions options protected internal abstract void UpdateSectionAlignment(int sectionIndex, int alignment); + /// + /// Get the section in the image where nodes in the passed in section should actually be emitted. + /// + /// A node's requested section. + /// The section to actually emit the node into. + /// + /// Sections in an image can be very expensive, and unlike linkable formats, + /// sections cannot be merged after the fact. + /// This method allows formats that want to merge sections during emit to do so. + /// + private protected virtual ObjectNodeSection GetEmitSection(ObjectNodeSection section) => section; + private protected SectionWriter GetOrCreateSection(ObjectNodeSection section) => GetOrCreateSection(section, default, default); @@ -87,6 +99,8 @@ private protected SectionWriter GetOrCreateSection(ObjectNodeSection section, Ut int sectionIndex; SectionData sectionData; + section = GetEmitSection(section); + if (!comdatName.IsNull || !_sectionNameToSectionIndex.TryGetValue(section.Name, out sectionIndex)) { sectionData = new SectionData(section.Type == SectionType.Executable ? _insPaddingByte : (byte)0); @@ -426,6 +440,11 @@ public virtual void EmitObject(Stream outputFileStream, IReadOnlyCollection> _resolvableRelocations = []; - private int _pdataSectionIndex = NoSectionIndex; - private int _debugSectionIndex = NoSectionIndex; - private int _exportSectionIndex = NoSectionIndex; private int _baseRelocSectionIndex = NoSectionIndex; - private int _corMetaSectionIndex = NoSectionIndex; - private int _rsrcSectionIndex = NoSectionIndex; // Base relocation (.reloc) bookkeeping private readonly SortedDictionary> _baseRelocMap = new(); private Dictionary _definedSymbols = []; private HashSet _exportedSymbolNames = new(); + private Dictionary _wellKnownSymbols = new(); private long _coffHeaderOffset; public PEObjectWriter(NodeFactory factory, ObjectWritingOptions options, OutputInfoBuilder outputInfoBuilder, string outputPath, int sectionAlignment, int? coffTimestamp) @@ -102,6 +97,26 @@ public void AddExportedSymbol(string symbol) } } + private protected override ObjectNodeSection GetEmitSection(ObjectNodeSection section) + { + // Put executable code into .text for PE files as AV software really + // doesn't like executable code in non-standard sections. + if (section == ObjectNodeSection.ManagedCodeWindowsContentSection) + { + return ObjectNodeSection.TextSection; + } + + // We want to reduce the number of sections in the PE files we emit, + // so merge the read-only data section into the .text section. + if (section == ObjectNodeSection.ReadOnlyDataSection) + { + return ObjectNodeSection.TextSection; + } + + // Otherwise, use the requested section. + return section; + } + private protected override void CreateSection(ObjectNodeSection section, Utf8String comdatName, Utf8String symbolName, int sectionIndex, Stream sectionStream) { // COMDAT sections are not supported in PE files @@ -340,6 +355,19 @@ private enum ImageDirectoryEntry Reserved = 15, } + private protected override void RecordWellKnownSymbol(Utf8String currentSymbolName, SortableDependencyNode.ObjectNodeOrder classCode) + { + if (classCode is SortableDependencyNode.ObjectNodeOrder.Win32ResourcesNode + or SortableDependencyNode.ObjectNodeOrder.CorHeaderNode + or SortableDependencyNode.ObjectNodeOrder.DebugDirectoryNode + or SortableDependencyNode.ObjectNodeOrder.RuntimeFunctionsTableNode) + { + // These nodes represent directories in the PE header. + // We need to know what symbol name they have so we know where they are located during emit. + _wellKnownSymbols.Add(classCode, currentSymbolName); + } + } + private protected override void EmitSymbolTable(IDictionary definedSymbols, SortedSet undefinedSymbols) { if (undefinedSymbols.Count > 0) @@ -353,16 +381,19 @@ private protected override void EmitSymbolTable(IDictionary $"{_nodeFactory.NameMangler.CompilationUnitPrefix}__ExportDirectory"; + private void LayoutSections(bool recordFinalLayout, out ushort numberOfSections, out uint sizeOfHeaders, out uint sizeOfImage, out uint sizeOfInitializedData, out uint sizeOfCode) { bool isPE32Plus = _nodeFactory.Target.PointerSize == 8; @@ -542,12 +575,6 @@ private protected override unsafe void EmitRelocations(int sectionIndex, List exports = [.._exportedSymbolNames]; exports.Sort(StringComparer.Ordinal); @@ -683,7 +710,7 @@ private protected override void EmitObjectFile(Stream outputFileStream) #if READYTORUN // On R2R, we encode the target OS into the machine bits to ensure we don't try running // linux or mac R2R code on Windows, or vice versa. - machine = (Machine) ((ushort)machine ^ (ushort)_nodeFactory.Target.MachineOSOverrideFromTarget()); + machine = (Machine)((ushort)machine ^ (ushort)_nodeFactory.Target.MachineOSOverrideFromTarget()); #endif // COFF File Header @@ -737,30 +764,17 @@ private protected override void EmitObjectFile(Stream outputFileStream) // before writing if needed. var dataDirs = new OptionalHeaderDataDirectories(); // Populate data directories if present. - if (_rsrcSectionIndex != NoSectionIndex) - { - dataDirs.SetIfNonEmpty((int)ImageDirectoryEntry.Resource, (uint)_outputSectionLayout[_rsrcSectionIndex].VirtualAddress, (uint)_outputSectionLayout[_rsrcSectionIndex].Length); - } - if (_pdataSectionIndex != NoSectionIndex) - { - dataDirs.SetIfNonEmpty((int)ImageDirectoryEntry.Exception, (uint)_outputSectionLayout[_pdataSectionIndex].VirtualAddress, (uint)_outputSectionLayout[_pdataSectionIndex].Length); - } - if (_exportSectionIndex != NoSectionIndex) - { - dataDirs.SetIfNonEmpty((int)ImageDirectoryEntry.Export, (uint)_outputSectionLayout[_exportSectionIndex].VirtualAddress, (uint)_outputSectionLayout[_exportSectionIndex].Length); - } + PopulateDataDirectoryForWellKnownSymbolfPresent(dataDirs, ImageDirectoryEntry.Resource, SortableDependencyNode.ObjectNodeOrder.Win32ResourcesNode); + PopulateDataDirectoryForWellKnownSymbolfPresent(dataDirs, ImageDirectoryEntry.Debug, SortableDependencyNode.ObjectNodeOrder.DebugDirectoryNode); + PopulateDataDirectoryForWellKnownSymbolfPresent(dataDirs, ImageDirectoryEntry.CLRRuntimeHeader, SortableDependencyNode.ObjectNodeOrder.CorHeaderNode); + PopulateDataDirectoryForWellKnownSymbolfPresent(dataDirs, ImageDirectoryEntry.Exception, SortableDependencyNode.ObjectNodeOrder.RuntimeFunctionsTableNode); + PopulateDataDirectoryForWellKnownSymbolfPresent(dataDirs, ImageDirectoryEntry.Export, ExportDirectorySymbol); + if (_baseRelocSectionIndex != NoSectionIndex) { dataDirs.SetIfNonEmpty((int)ImageDirectoryEntry.BaseRelocation, (uint)_outputSectionLayout[_baseRelocSectionIndex].VirtualAddress, (uint)_outputSectionLayout[_baseRelocSectionIndex].Length); } - if (_debugSectionIndex != NoSectionIndex) - { - dataDirs.SetIfNonEmpty((int)ImageDirectoryEntry.Debug, (uint)_outputSectionLayout[_debugSectionIndex].VirtualAddress, (uint)_outputSectionLayout[_debugSectionIndex].Length); - } - if (_corMetaSectionIndex != NoSectionIndex) - { - dataDirs.SetIfNonEmpty((int)ImageDirectoryEntry.CLRRuntimeHeader, (uint)_outputSectionLayout[_corMetaSectionIndex].VirtualAddress, (uint)_outputSectionLayout[_corMetaSectionIndex].Length); - } + peOptional.Write(outputFileStream, dataDirs); CoffStringTable stringTable = new(); @@ -811,6 +825,22 @@ private protected override void EmitObjectFile(Stream outputFileStream) outputFileStream.SetLength(sizeOfImage); } + private void PopulateDataDirectoryForWellKnownSymbolfPresent(OptionalHeaderDataDirectories dataDirs, ImageDirectoryEntry directory, SortableDependencyNode.ObjectNodeOrder wellKnownSymbol) + { + if (_wellKnownSymbols.TryGetValue(wellKnownSymbol, out Utf8String symbolName)) + { + PopulateDataDirectoryForWellKnownSymbolfPresent(dataDirs, directory, symbolName); + } + } + + private void PopulateDataDirectoryForWellKnownSymbolfPresent(OptionalHeaderDataDirectories dataDirs, ImageDirectoryEntry directory, Utf8String symbolName) + { + if (_definedSymbols.TryGetValue(symbolName, out SymbolDefinition symbol)) + { + dataDirs.SetIfNonEmpty((int)directory, checked((uint)(_outputSectionLayout[symbol.SectionIndex].VirtualAddress + (ulong)symbol.Value)), (uint)symbol.Size); + } + } + private protected override void EmitChecksumsForObject(Stream outputFileStream, List checksumRelocations, ReadOnlySpan originalOutput) { base.EmitChecksumsForObject(outputFileStream, checksumRelocations, originalOutput); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CoffObjectWriter.Aot.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CoffObjectWriter.Aot.cs index ebc36125afe188..8975efcde17728 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CoffObjectWriter.Aot.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CoffObjectWriter.Aot.cs @@ -56,12 +56,14 @@ internal partial class CoffObjectWriter : ObjectWriter private CodeViewTypesBuilder _debugTypesBuilder; // Exception handling + private static readonly ObjectNodeSection XDataSection = new ObjectNodeSection("xdata", SectionType.ReadOnly); + private static readonly ObjectNodeSection PDataSection = new ObjectNodeSection("pdata", SectionType.UnwindData); private SectionWriter _pdataSectionWriter; private protected override void CreateEhSections() { // Create .pdata - _pdataSectionWriter = GetOrCreateSection(ObjectNodeSection.PDataSection); + _pdataSectionWriter = GetOrCreateSection(PDataSection); } private protected override void EmitUnwindInfo( @@ -95,14 +97,14 @@ private protected override void EmitUnwindInfo( if (shareSymbol) { // Produce an associative COMDAT symbol. - xdataSectionWriter = GetOrCreateSection(ObjectNodeSection.XDataSection, currentSymbolName, unwindSymbolName); - pdataSectionWriter = GetOrCreateSection(ObjectNodeSection.PDataSection, currentSymbolName, default); + xdataSectionWriter = GetOrCreateSection(XDataSection, currentSymbolName, unwindSymbolName); + pdataSectionWriter = GetOrCreateSection(PDataSection, currentSymbolName, default); } else { // Produce a COMDAT section for each unwind symbol and let linker // do the deduplication across the ones with identical content. - xdataSectionWriter = GetOrCreateSection(ObjectNodeSection.XDataSection, unwindSymbolName, unwindSymbolName); + xdataSectionWriter = GetOrCreateSection(XDataSection, unwindSymbolName, unwindSymbolName); pdataSectionWriter = _pdataSectionWriter; } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedCorHeaderNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedCorHeaderNode.cs index 5b4df6872e3ba9..233407d2208045 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedCorHeaderNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedCorHeaderNode.cs @@ -24,14 +24,7 @@ public CopiedCorHeaderNode(EcmaModule sourceModule) public override ObjectNodeSection GetSection(NodeFactory factory) { - // Put the CLR metadata into the correct section for the PE writer to - // hook up the CLR header entry in the PE header. - // Don't emit a separate section for other formats to reduce cost. - return factory.Format switch - { - ReadyToRunContainerFormat.PE => ObjectNodeSection.CorMetaSection, - _ => ObjectNodeSection.ReadOnlyDataSection - }; + return ObjectNodeSection.ReadOnlyDataSection; } public override bool IsShareable => false; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedFieldRvaNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedFieldRvaNode.cs index 4accb7487377e4..cc1d3737ac16e1 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedFieldRvaNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedFieldRvaNode.cs @@ -26,14 +26,7 @@ public CopiedFieldRvaNode(EcmaModule module, int rva) public override ObjectNodeSection GetSection(NodeFactory factory) { - // Put the CLR metadata into the correct section for the PE writer to - // hook up the CLR header entry in the PE header. - // Don't emit a separate section for other formats to reduce cost. - return factory.Format switch - { - ReadyToRunContainerFormat.PE => ObjectNodeSection.CorMetaSection, - _ => ObjectNodeSection.ReadOnlyDataSection - }; + return ObjectNodeSection.ReadOnlyDataSection; } public override bool IsShareable => false; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedManagedResourcesNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedManagedResourcesNode.cs index a0367e0dec2b36..e046ce8dc31966 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedManagedResourcesNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedManagedResourcesNode.cs @@ -20,14 +20,7 @@ public CopiedManagedResourcesNode(EcmaModule module) public override ObjectNodeSection GetSection(NodeFactory factory) { - // Put the CLR metadata into the correct section for the PE writer to - // hook up the CLR header entry in the PE header. - // Don't emit a separate section for other formats to reduce cost. - return factory.Format switch - { - ReadyToRunContainerFormat.PE => ObjectNodeSection.CorMetaSection, - _ => ObjectNodeSection.ReadOnlyDataSection - }; + return ObjectNodeSection.ReadOnlyDataSection; } public override bool IsShareable => false; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMetadataBlobNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMetadataBlobNode.cs index e18fd800555f9d..b760eb4490ffaa 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMetadataBlobNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMetadataBlobNode.cs @@ -27,14 +27,7 @@ public CopiedMetadataBlobNode(EcmaModule sourceModule) public override ObjectNodeSection GetSection(NodeFactory factory) { - // Put the CLR metadata into the correct section for the PE writer to - // hook up the CLR header entry in the PE header. - // Don't emit a separate section for other formats to reduce cost. - return factory.Format switch - { - ReadyToRunContainerFormat.PE => ObjectNodeSection.CorMetaSection, - _ => ObjectNodeSection.ReadOnlyDataSection - }; + return ObjectNodeSection.ReadOnlyDataSection; } public override bool IsShareable => false; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs index d35993fdf0441f..3054c704883a3d 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedMethodILNode.cs @@ -24,14 +24,7 @@ public CopiedMethodILNode(EcmaMethod method) public override ObjectNodeSection GetSection(NodeFactory factory) { - // Put the CLR metadata into the correct section for the PE writer to - // hook up the CLR header entry in the PE header. - // Don't emit a separate section for other formats to reduce cost. - return factory.Format switch - { - ReadyToRunContainerFormat.PE => ObjectNodeSection.CorMetaSection, - _ => ObjectNodeSection.ReadOnlyDataSection - }; + return ObjectNodeSection.ReadOnlyDataSection; } public override bool IsShareable => false; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedStrongNameSignatureNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedStrongNameSignatureNode.cs index 124a21ca1abe85..f3582103052621 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedStrongNameSignatureNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/CopiedStrongNameSignatureNode.cs @@ -23,14 +23,7 @@ public CopiedStrongNameSignatureNode(EcmaModule module) public override ObjectNodeSection GetSection(NodeFactory factory) { - // Put the CLR metadata into the correct section for the PE writer to - // hook up the CLR header entry in the PE header. - // Don't emit a separate section for other formats to reduce cost. - return factory.Format switch - { - ReadyToRunContainerFormat.PE => ObjectNodeSection.CorMetaSection, - _ => ObjectNodeSection.ReadOnlyDataSection - }; + return ObjectNodeSection.ReadOnlyDataSection; } public override bool IsShareable => false; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugDirectoryNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugDirectoryNode.cs index 7c304148cfee81..ec252eef7be1c7 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugDirectoryNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugDirectoryNode.cs @@ -54,14 +54,14 @@ public DebugDirectoryNode(EcmaModule sourceModule, string outputFileName, bool s public override ObjectNodeSection GetSection(NodeFactory factory) { - return ObjectNodeSection.DebugDirectorySection; + return ObjectNodeSection.TextSection; } public override bool IsShareable => false; protected internal override int Phase => (int)ObjectNodePhase.Ordered; - public override int ClassCode => 315358387; + public override int ClassCode => (int)ObjectNodeOrder.DebugDirectoryNode; public override bool StaticDependenciesAreComputed => true; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodColdCodeNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodColdCodeNode.cs index 80047a4c6b0789..4b75d00300cd0f 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodColdCodeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodColdCodeNode.cs @@ -24,13 +24,9 @@ public MethodColdCodeNode(MethodDesc owningMethod) public override ObjectNodeSection GetSection(NodeFactory factory) { - // Put executable code into .text for PE files as AV software really - // doesn't like executable code in non-standard sections. - // - // For other formats, use the managed code section for managed code. return factory.Format switch { - ReadyToRunContainerFormat.PE => ObjectNodeSection.TextSection, + ReadyToRunContainerFormat.PE => ObjectNodeSection.ManagedCodeWindowsContentSection, _ => ObjectNodeSection.ManagedCodeUnixContentSection }; } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodWithGCInfo.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodWithGCInfo.cs index 79e6076914004b..ee86a7943a67dc 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodWithGCInfo.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodWithGCInfo.cs @@ -303,13 +303,9 @@ protected override string GetName(NodeFactory factory) public override ObjectNodeSection GetSection(NodeFactory factory) { - // Put executable code into .text for PE files as AV software really - // doesn't like executable code in non-standard sections. - // - // For other formats, use the managed code section for managed code. return factory.Format switch { - ReadyToRunContainerFormat.PE => ObjectNodeSection.TextSection, + ReadyToRunContainerFormat.PE => ObjectNodeSection.ManagedCodeWindowsContentSection, _ => ObjectNodeSection.ManagedCodeUnixContentSection }; } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/RuntimeFunctionsGCInfoNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/RuntimeFunctionsGCInfoNode.cs index caec889983266e..aad3718edb6938 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/RuntimeFunctionsGCInfoNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/RuntimeFunctionsGCInfoNode.cs @@ -14,19 +14,13 @@ public RuntimeFunctionsGCInfoNode() public HashSet Deduplicator; - public override int ClassCode => 316678892; + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + + public override int ClassCode => (int)ObjectNodeOrder.RuntimeFunctionsGCInfoNode; public override ObjectNodeSection GetSection(NodeFactory factory) { - // We may want to emit info into the XData section if we produce native - // unwind info for another format. Don't put this into the XData section - // unless we're producing PEs, where we will also emit the unwind info - // into the PData section. - return factory.Format switch - { - ReadyToRunContainerFormat.PE => ObjectNodeSection.XDataSection, - _ => ObjectNodeSection.ReadOnlyDataSection - }; + return ObjectNodeSection.ReadOnlyDataSection; } public override bool StaticDependenciesAreComputed => true; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/RuntimeFunctionsTableNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/RuntimeFunctionsTableNode.cs index 7f9cb32327fc88..30495650e69c8b 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/RuntimeFunctionsTableNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/RuntimeFunctionsTableNode.cs @@ -25,13 +25,7 @@ public RuntimeFunctionsTableNode(NodeFactory nodeFactory) public override ObjectNodeSection GetSection(NodeFactory factory) { - // This table is always in the Windows UnwindInfo format. - // As a result, we can't put it in the PData section for non-PE formats. - return factory.Format switch - { - ReadyToRunContainerFormat.PE => ObjectNodeSection.PDataSection, - _ => ObjectNodeSection.ReadOnlyDataSection - }; + return ObjectNodeSection.TextSection; } public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) @@ -174,7 +168,9 @@ public int TableSizeExcludingSentinel } } - public override int ClassCode => -855231428; + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + + public override int ClassCode => (int)ObjectNodeOrder.RuntimeFunctionsTableNode; internal const int SentinelSizeAdjustment = -4; } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Win32ResourcesNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Win32ResourcesNode.cs index 75b3ffc0fb2582..376cb4312979b8 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Win32ResourcesNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Win32ResourcesNode.cs @@ -21,14 +21,7 @@ public Win32ResourcesNode(ResourceData resourceData) public override ObjectNodeSection GetSection(NodeFactory factory) { - // Don't emit Win32 resources into a special section unless we're producing PEs. - // The PE writer knows how to hook up the lookup for these resources, but other - // formats don't need the cost of an additional section. - return factory.Format switch - { - ReadyToRunContainerFormat.PE => ObjectNodeSection.Win32ResourcesSection, - _ => ObjectNodeSection.ReadOnlyDataSection - }; + return ObjectNodeSection.ReadOnlyDataSection; } public override bool IsShareable => false; From 27c518f825e6ac3f0c00db793b4f9aca9bdfd880 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 12 Dec 2025 15:29:41 -0800 Subject: [PATCH 2/9] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Compiler/ObjectWriter/PEObjectWriter.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs index 7e185d2409522d..95f302a187151b 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs @@ -764,11 +764,11 @@ private protected override void EmitObjectFile(Stream outputFileStream) // before writing if needed. var dataDirs = new OptionalHeaderDataDirectories(); // Populate data directories if present. - PopulateDataDirectoryForWellKnownSymbolfPresent(dataDirs, ImageDirectoryEntry.Resource, SortableDependencyNode.ObjectNodeOrder.Win32ResourcesNode); - PopulateDataDirectoryForWellKnownSymbolfPresent(dataDirs, ImageDirectoryEntry.Debug, SortableDependencyNode.ObjectNodeOrder.DebugDirectoryNode); - PopulateDataDirectoryForWellKnownSymbolfPresent(dataDirs, ImageDirectoryEntry.CLRRuntimeHeader, SortableDependencyNode.ObjectNodeOrder.CorHeaderNode); - PopulateDataDirectoryForWellKnownSymbolfPresent(dataDirs, ImageDirectoryEntry.Exception, SortableDependencyNode.ObjectNodeOrder.RuntimeFunctionsTableNode); - PopulateDataDirectoryForWellKnownSymbolfPresent(dataDirs, ImageDirectoryEntry.Export, ExportDirectorySymbol); + PopulateDataDirectoryForWellKnownSymbolIfPresent(dataDirs, ImageDirectoryEntry.Resource, SortableDependencyNode.ObjectNodeOrder.Win32ResourcesNode); + PopulateDataDirectoryForWellKnownSymbolIfPresent(dataDirs, ImageDirectoryEntry.Debug, SortableDependencyNode.ObjectNodeOrder.DebugDirectoryNode); + PopulateDataDirectoryForWellKnownSymbolIfPresent(dataDirs, ImageDirectoryEntry.CLRRuntimeHeader, SortableDependencyNode.ObjectNodeOrder.CorHeaderNode); + PopulateDataDirectoryForWellKnownSymbolIfPresent(dataDirs, ImageDirectoryEntry.Exception, SortableDependencyNode.ObjectNodeOrder.RuntimeFunctionsTableNode); + PopulateDataDirectoryForWellKnownSymbolIfPresent(dataDirs, ImageDirectoryEntry.Export, ExportDirectorySymbol); if (_baseRelocSectionIndex != NoSectionIndex) { @@ -825,15 +825,15 @@ private protected override void EmitObjectFile(Stream outputFileStream) outputFileStream.SetLength(sizeOfImage); } - private void PopulateDataDirectoryForWellKnownSymbolfPresent(OptionalHeaderDataDirectories dataDirs, ImageDirectoryEntry directory, SortableDependencyNode.ObjectNodeOrder wellKnownSymbol) + private void PopulateDataDirectoryForWellKnownSymbolIfPresent(OptionalHeaderDataDirectories dataDirs, ImageDirectoryEntry directory, SortableDependencyNode.ObjectNodeOrder wellKnownSymbol) { if (_wellKnownSymbols.TryGetValue(wellKnownSymbol, out Utf8String symbolName)) { - PopulateDataDirectoryForWellKnownSymbolfPresent(dataDirs, directory, symbolName); + PopulateDataDirectoryForWellKnownSymbolIfPresent(dataDirs, directory, symbolName); } } - private void PopulateDataDirectoryForWellKnownSymbolfPresent(OptionalHeaderDataDirectories dataDirs, ImageDirectoryEntry directory, Utf8String symbolName) + private void PopulateDataDirectoryForWellKnownSymbolIfPresent(OptionalHeaderDataDirectories dataDirs, ImageDirectoryEntry directory, Utf8String symbolName) { if (_definedSymbols.TryGetValue(symbolName, out SymbolDefinition symbol)) { From 21974db11c72a6e625c8b122d2417a7902f54c11 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 12 Dec 2025 18:34:20 -0800 Subject: [PATCH 3/9] Update PEObjectWriter.cs Remove now-invalid assert --- .../tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs index 95f302a187151b..4fbf72ea8f7d71 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs @@ -594,8 +594,6 @@ private void EmitExportDirectory(SectionWriter sectionWriter) string namePointerTableSymbol = GenerateSymbolNameForReloc("namePointerTable"); string ordinalPointerTableSymbol = GenerateSymbolNameForReloc("ordinalPointerTable"); - Debug.Assert(sectionWriter.Position == 0); - // +0x00: reserved sectionWriter.WriteLittleEndian(0); // +0x04: time/date stamp From 3777455c3833d196f9148cfcc4d805e66aa94ab8 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 15 Dec 2025 11:45:06 -0800 Subject: [PATCH 4/9] Move some nodes to go into rdata and don't merge rdata and text on riscv or loongarch64 to avoid issues with relative jump distance. --- .../tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs | 8 ++++++++ .../DependencyAnalysis/ReadyToRun/DebugDirectoryNode.cs | 2 +- .../ReadyToRun/ManifestAssemblyMvidHeaderNode.cs | 2 +- .../ReadyToRun/RuntimeFunctionsTableNode.cs | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs index 7e185d2409522d..a539fe078f05bc 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs @@ -99,6 +99,14 @@ public void AddExportedSymbol(string symbol) private protected override ObjectNodeSection GetEmitSection(ObjectNodeSection section) { + // RISC-V and LoongArch64 have a limited addressing range (±2GB). + // Don't merge sections to avoid relocation overflow. + if (_nodeFactory.Target.Architecture == TargetArchitecture.RiscV64 + || _nodeFactory.Target.Architecture == TargetArchitecture.LoongArch64) + { + return section; + } + // Put executable code into .text for PE files as AV software really // doesn't like executable code in non-standard sections. if (section == ObjectNodeSection.ManagedCodeWindowsContentSection) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugDirectoryNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugDirectoryNode.cs index ec252eef7be1c7..b1246973b0bd81 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugDirectoryNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugDirectoryNode.cs @@ -54,7 +54,7 @@ public DebugDirectoryNode(EcmaModule sourceModule, string outputFileName, bool s public override ObjectNodeSection GetSection(NodeFactory factory) { - return ObjectNodeSection.TextSection; + return ObjectNodeSection.ReadOnlyDataSection; } public override bool IsShareable => false; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestAssemblyMvidHeaderNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestAssemblyMvidHeaderNode.cs index 20240ee78f3ee2..96ce435250cdcb 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestAssemblyMvidHeaderNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestAssemblyMvidHeaderNode.cs @@ -21,7 +21,7 @@ public ManifestAssemblyMvidHeaderNode(ManifestMetadataTableNode manifestNode) _manifestNode = manifestNode; } - public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.TextSection; + public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.ReadOnlyDataSection; public override bool IsShareable => false; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/RuntimeFunctionsTableNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/RuntimeFunctionsTableNode.cs index 30495650e69c8b..bd2f062fc59be4 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/RuntimeFunctionsTableNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/RuntimeFunctionsTableNode.cs @@ -25,7 +25,7 @@ public RuntimeFunctionsTableNode(NodeFactory nodeFactory) public override ObjectNodeSection GetSection(NodeFactory factory) { - return ObjectNodeSection.TextSection; + return ObjectNodeSection.ReadOnlyDataSection; } public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) From def5ccfed80fec8f4ad5c59d4aa57c76418af89f Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 15 Dec 2025 12:35:49 -0800 Subject: [PATCH 5/9] Update src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs --- .../Compiler/ObjectWriter/PEObjectWriter.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs index 96f599326c1487..516ee7a6290b08 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs @@ -99,14 +99,6 @@ public void AddExportedSymbol(string symbol) private protected override ObjectNodeSection GetEmitSection(ObjectNodeSection section) { - // RISC-V and LoongArch64 have a limited addressing range (±2GB). - // Don't merge sections to avoid relocation overflow. - if (_nodeFactory.Target.Architecture == TargetArchitecture.RiscV64 - || _nodeFactory.Target.Architecture == TargetArchitecture.LoongArch64) - { - return section; - } - // Put executable code into .text for PE files as AV software really // doesn't like executable code in non-standard sections. if (section == ObjectNodeSection.ManagedCodeWindowsContentSection) @@ -114,6 +106,14 @@ private protected override ObjectNodeSection GetEmitSection(ObjectNodeSection se return ObjectNodeSection.TextSection; } + // RISC-V and LoongArch64 have a limited addressing range (±2GB) for relocs. + // Don't merge rdata into text to avoid relocation overflow. + if (_nodeFactory.Target.Architecture == TargetArchitecture.RiscV64 + || _nodeFactory.Target.Architecture == TargetArchitecture.LoongArch64) + { + return section; + } + // We want to reduce the number of sections in the PE files we emit, // so merge the read-only data section into the .text section. if (section == ObjectNodeSection.ReadOnlyDataSection) From d31e1b1915929b39e49709675065e03257f51be6 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 15 Dec 2025 13:15:21 -0800 Subject: [PATCH 6/9] Update src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs Co-authored-by: Adeel Mujahid <3840695+am11@users.noreply.github.com> --- .../tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs index 516ee7a6290b08..04586c4f23f73c 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs @@ -106,10 +106,9 @@ private protected override ObjectNodeSection GetEmitSection(ObjectNodeSection se return ObjectNodeSection.TextSection; } - // RISC-V and LoongArch64 have a limited addressing range (±2GB) for relocs. + // RISC-V has a limited addressing range (±2GB) for relocs. // Don't merge rdata into text to avoid relocation overflow. - if (_nodeFactory.Target.Architecture == TargetArchitecture.RiscV64 - || _nodeFactory.Target.Architecture == TargetArchitecture.LoongArch64) + if (_nodeFactory.Target.Architecture == TargetArchitecture.RiscV64) { return section; } From 20174cf8c3a53ec5361623f668ebc3b70a6479bc Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 5 Jan 2026 23:27:32 +0000 Subject: [PATCH 7/9] Fix utf8 string conversion --- .../tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs index 16fc765c9dd9e5..afd4436a9862f1 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs @@ -447,7 +447,10 @@ private protected override void EmitSectionsAndLayout() LayoutSections(recordFinalLayout: false, out _, out _, out _, out _, out _); } - private string ExportDirectorySymbol => $"{_nodeFactory.NameMangler.CompilationUnitPrefix}__ExportDirectory"; + private Utf8String ExportDirectorySymbol + => field.IsNull + ? (field = Utf8String.Concat(_nodeFactory.NameMangler.CompilationUnitPrefix.AsSpan(), "__ExportDirectory"u8)) + : field; private void LayoutSections(bool recordFinalLayout, out ushort numberOfSections, out uint sizeOfHeaders, out uint sizeOfImage, out uint sizeOfInitializedData, out uint sizeOfCode) { From daba6cbf16e8d5155c353240e55e87c036174cec Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 6 Jan 2026 20:15:39 +0000 Subject: [PATCH 8/9] Ensure we sign-extend when calculating the delta for a resolved reloc. --- .../Common/Compiler/ObjectWriter/PEObjectWriter.cs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs index afd4436a9862f1..5b9748f299a884 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs @@ -108,13 +108,6 @@ private protected override ObjectNodeSection GetEmitSection(ObjectNodeSection se return ObjectNodeSection.TextSection; } - // RISC-V has a limited addressing range (±2GB) for relocs. - // Don't merge rdata into text to avoid relocation overflow. - if (_nodeFactory.Target.Architecture == TargetArchitecture.RiscV64) - { - return section; - } - // We want to reduce the number of sections in the PE files we emit, // so merge the read-only data section into the .text section. if (section == ObjectNodeSection.ReadOnlyDataSection) @@ -933,7 +926,7 @@ private unsafe void ResolveRelocations(int sectionIndex, List> 12) & 0x1f_ffff; + long delta = ((long)symbolImageOffset - sourcePageRVA >> 12) & 0x1f_ffff; Relocation.WriteValue(reloc.Type, pData, delta); break; } @@ -951,7 +944,7 @@ private unsafe void ResolveRelocations(int sectionIndex, List Date: Tue, 6 Jan 2026 15:44:42 -0800 Subject: [PATCH 9/9] Update markdown --- .../botr/readytorun-platform-native-envelope.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/docs/design/coreclr/botr/readytorun-platform-native-envelope.md b/docs/design/coreclr/botr/readytorun-platform-native-envelope.md index 7f13348ec172a3..9bd1410420ff37 100644 --- a/docs/design/coreclr/botr/readytorun-platform-native-envelope.md +++ b/docs/design/coreclr/botr/readytorun-platform-native-envelope.md @@ -13,9 +13,11 @@ The tentative high-level design is outlined below. As we implement this support, ## crossgen2: producing Mach-O object files Mach‑O support will only be supported for composite ReadyToRun when the target OS is macOS. It will be opt-in via a new `crossgen2` flag: + - `--obj-format macho` `crossgen2` will: + - Produce a Mach-O object file as the composite R2R image with the `RTR_HEADER` export for the `READYTORUN_HEADER`. - Mark each input IL assembly as a component R2R assembly: `READYTORUN_FLAG_COMPONENT`. - Mark each input IL assembly with a new flag indicating that the associated composite image is in the platform-native format: `READYTORUN_FLAG_PLATFORM_NATIVE_IMAGE` @@ -28,20 +30,14 @@ There's a few cases in the R2R format that are not natively represented in the M #### Sections -Sections folded into `__TEXT,__text` that is in other sections in the PE envelope: - -- CLR metadata: In the PE format, put in .cormeta, corresponds to the PE Header's "COR Header directory" -- Win32 Resources: In the PE format, put in .rsrc, corresponds to the PE Header's "Win32 Resources Header directory" -- Managed Unwind Info: In the Mach-O format, this section is expected to be in the Mach-O unwind format. The unwind info used by the runtime must be in the Windows unwind format. -- GC Info: Entries correspond to the unwind info. Data moved out of `__TEXT,__text`: - Precompiled managed code has been moved into `__TEXT,__managedcode`. `__TEXT,__text` gets special treatment by the linker and `__TEXT,__managedcode` matches NativeAOT. +- Read-only data such as jump tables, CLR metadata, Win32 Resources, managed unwind info, gc info, and the R2R headers are moved to `__TEXT,__const` Data that stays in the corresponding locations as the PE envelope: -- Read-only data such as jump tables and the R2R headers: `__TEXT,__const` - Read-write data, such as fixup tables: `__DATA,__data` - Import thunks: `__TEXT,__text`