From ee338cfc6ea2734861ebb717d2bc30d327e2ff80 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Apr 2026 18:46:46 +0000 Subject: [PATCH 1/4] Add --rtr-header-symbol-name option to crossgen2 for custom ReadyToRun header symbol name Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/861662d6-3208-41e7-a555-c9e87de7181f Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com> --- .../CodeGen/ReadyToRunObjectWriter.cs | 3 ++- .../Compiler/CompositeImageSettings.cs | 1 + .../ReadyToRunCodegenNodeFactory.cs | 3 ++- .../tools/aot/crossgen2/Crossgen2RootCommand.cs | 3 +++ src/coreclr/tools/aot/crossgen2/Program.cs | 11 +++++++++++ .../tools/aot/crossgen2/Properties/Resources.resx | 6 ++++++ 6 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs index d0cd1542163e5c..5af778849ac1ec 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs @@ -288,7 +288,8 @@ private PEObjectWriter CreatePEObjectWriter() if (_nodeFactory.CompilationModuleGroup.IsCompositeBuildMode && _componentModule == null) { - objectWriter.AddExportedSymbol("RTR_HEADER"); + string symbolName = _nodeFactory.CompositeImageSettings?.ReadyToRunHeaderSymbolName ?? "RTR_HEADER"; + objectWriter.AddExportedSymbol(symbolName); } return objectWriter; } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/CompositeImageSettings.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/CompositeImageSettings.cs index 7a54bf7fefc982..9057dc6eed0828 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/CompositeImageSettings.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/CompositeImageSettings.cs @@ -10,5 +10,6 @@ public class CompositeImageSettings { public ImmutableArray PublicKey; public Version AssemblyVersion; + public string ReadyToRunHeaderSymbolName; } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs index 88278fc10f707f..44eda5183f5496 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs @@ -1179,7 +1179,8 @@ public Utf8String GetSymbolAlternateName(ISymbolNode node, out bool isHidden) isHidden = false; if (node == Header) { - return new Utf8String("RTR_HEADER"u8); + string symbolName = CompositeImageSettings?.ReadyToRunHeaderSymbolName ?? "RTR_HEADER"; + return new Utf8String(symbolName); } return default; } diff --git a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs index ada92cd4d77f93..41f5967f980bd1 100644 --- a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs +++ b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs @@ -54,6 +54,8 @@ internal class Crossgen2RootCommand : RootCommand new("--composite") { Description = SR.CompositeBuildMode }; public Option CompositeKeyFile { get; } = new("--compositekeyfile") { Description = SR.CompositeKeyFile }; + public Option ReadyToRunHeaderSymbolName { get; } = + new("--rtr-header-symbol-name") { Description = SR.ReadyToRunHeaderSymbolName }; public Option CompileNoMethods { get; } = new("--compile-no-methods") { Description = SR.CompileNoMethodsOption }; public Option OutNearInput { get; } = @@ -179,6 +181,7 @@ public Crossgen2RootCommand(string[] args) : base(SR.Crossgen2BannerText) Options.Add(InputBubbleReferenceFilePaths); Options.Add(Composite); Options.Add(CompositeKeyFile); + Options.Add(ReadyToRunHeaderSymbolName); Options.Add(CompileNoMethods); Options.Add(OutNearInput); Options.Add(SingleFileCompilation); diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index 0f2a6e027f3a71..6f0b72dd7230b0 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -411,6 +411,12 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru throw new Exception(string.Format(SR.ErrorMultipleInputFilesCompositeModeOnly, string.Join("; ", inputModules))); } + string rtrHeaderSymbolName = Get(_command.ReadyToRunHeaderSymbolName); + if (rtrHeaderSymbolName != null && !composite) + { + throw new Exception(SR.ErrorReadyToRunHeaderSymbolNameRequiresComposite); + } + ReadyToRunContainerFormat format = Get(_command.OutputFormat); if (!composite && format != ReadyToRunContainerFormat.PE && format != ReadyToRunContainerFormat.Wasm) { @@ -604,6 +610,11 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru compositeImageSettings.PublicKey = compositeStrongNameKey.ToImmutableArray(); } + if (rtrHeaderSymbolName != null) + { + compositeImageSettings.ReadyToRunHeaderSymbolName = rtrHeaderSymbolName; + } + // // Compile // diff --git a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx index b21edaf9f22af1..211980073ad988 100644 --- a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx +++ b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx @@ -168,6 +168,12 @@ --CompositeKeyFile does not specify a valid public key + + For custom hosts only: the name to use for the exported ReadyToRun header symbol (default: RTR_HEADER). Requires --composite. + + + Error: --rtr-header-symbol-name requires --composite. + Input file(s) to compile From 3665b44bcb4a63e72665721fc9b0c14cb8ef0d0d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Apr 2026 21:45:16 +0000 Subject: [PATCH 2/4] Address review feedback: validate rtr-header-symbol-name is non-empty and PE format, fix IsNullOrEmpty fallbacks Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/66091b3b-2d80-4df7-bf1a-1a5bb3944465 Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com> --- .../CodeGen/ReadyToRunObjectWriter.cs | 3 ++- .../ReadyToRunCodegenNodeFactory.cs | 4 ++-- src/coreclr/tools/aot/crossgen2/Program.cs | 22 +++++++++++++++---- .../aot/crossgen2/Properties/Resources.resx | 6 +++++ 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs index 5af778849ac1ec..c83fce292bf3f0 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs @@ -288,7 +288,8 @@ private PEObjectWriter CreatePEObjectWriter() if (_nodeFactory.CompilationModuleGroup.IsCompositeBuildMode && _componentModule == null) { - string symbolName = _nodeFactory.CompositeImageSettings?.ReadyToRunHeaderSymbolName ?? "RTR_HEADER"; + string configuredSymbolName = _nodeFactory.CompositeImageSettings?.ReadyToRunHeaderSymbolName; + string symbolName = string.IsNullOrWhiteSpace(configuredSymbolName) ? "RTR_HEADER" : configuredSymbolName; objectWriter.AddExportedSymbol(symbolName); } return objectWriter; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs index 44eda5183f5496..3236e93c9e888d 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs @@ -1179,8 +1179,8 @@ public Utf8String GetSymbolAlternateName(ISymbolNode node, out bool isHidden) isHidden = false; if (node == Header) { - string symbolName = CompositeImageSettings?.ReadyToRunHeaderSymbolName ?? "RTR_HEADER"; - return new Utf8String(symbolName); + string symbolName = CompositeImageSettings?.ReadyToRunHeaderSymbolName; + return new Utf8String(string.IsNullOrEmpty(symbolName) ? "RTR_HEADER" : symbolName); } return default; } diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index 6f0b72dd7230b0..6b74c976204e67 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -412,10 +412,6 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru } string rtrHeaderSymbolName = Get(_command.ReadyToRunHeaderSymbolName); - if (rtrHeaderSymbolName != null && !composite) - { - throw new Exception(SR.ErrorReadyToRunHeaderSymbolNameRequiresComposite); - } ReadyToRunContainerFormat format = Get(_command.OutputFormat); if (!composite && format != ReadyToRunContainerFormat.PE && format != ReadyToRunContainerFormat.Wasm) @@ -423,6 +419,24 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru throw new Exception(string.Format(SR.ErrorContainerFormatRequiresComposite, format)); } + if (rtrHeaderSymbolName is not null) + { + if (!composite) + { + throw new Exception(SR.ErrorReadyToRunHeaderSymbolNameRequiresComposite); + } + + if (string.IsNullOrWhiteSpace(rtrHeaderSymbolName)) + { + throw new Exception(SR.ErrorReadyToRunHeaderSymbolNameEmpty); + } + + if (format != ReadyToRunContainerFormat.PE) + { + throw new Exception(SR.ErrorReadyToRunHeaderSymbolNameRequiresPE); + } + } + bool compileBubbleGenerics = Get(_command.CompileBubbleGenerics); ReadyToRunCompilationModuleGroupBase compilationGroup; List compilationRoots = new List(); diff --git a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx index 211980073ad988..16d13b2e894202 100644 --- a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx +++ b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx @@ -174,6 +174,12 @@ Error: --rtr-header-symbol-name requires --composite. + + Error: --rtr-header-symbol-name must not be empty or whitespace. + + + Error: --rtr-header-symbol-name is only supported when --obj-format is PE. + Input file(s) to compile From e846f4d6d8a77105555f287bf25f706a89ef4c3a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Apr 2026 02:12:08 +0000 Subject: [PATCH 3/4] docs: document RTR_HEADER customizability via --rtr-header-symbol-name in R2R docs Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/53565a7b-a2b2-4e0b-8a66-ea67cc354f8c Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com> --- docs/design/coreclr/botr/readytorun-format.md | 9 ++++++--- .../coreclr/botr/readytorun-platform-native-envelope.md | 8 ++++++-- .../features/readytorun-composite-format-design.md | 4 ++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/docs/design/coreclr/botr/readytorun-format.md b/docs/design/coreclr/botr/readytorun-format.md index 47894bb514aae5..6541e16b383cfb 100644 --- a/docs/design/coreclr/botr/readytorun-format.md +++ b/docs/design/coreclr/botr/readytorun-format.md @@ -34,7 +34,7 @@ native envelope. Moving forward we [plan to gradually add support for platform-n executable formats](./readytorun-platform-native-envelope.md) (ELF on Linux, MachO on OSX) as the native envelopes. There is a global CLI / COR header in the file, but it only exists to facilitate pdb generation, and does not participate in any usages by the CoreCLR runtime. The ReadyToRun header structure is pointed to -by the well-known export symbol `RTR_HEADER` and has the `READYTORUN_FLAG_COMPOSITE` flag set. +by the well-known export symbol `RTR_HEADER` (customizable via the `--rtr-header-symbol-name` crossgen2 option — see below) and has the `READYTORUN_FLAG_COMPOSITE` flag set. Input MSIL metadata and IL streams can be either embedded in the composite R2R file or left as separate files on disk. In case of embedded MSIL, the "actual" metadata for the individual @@ -71,8 +71,11 @@ The structures and accompanying constants are defined in the [readytorun.h](https://github.com/dotnet/runtime/blob/main/src/coreclr/inc/readytorun.h) header file. Basically the entire R2R executable image is addressed through the READYTORUN_HEADER singleton -pointed to by the well-known export RTR_HEADER in the export section of the native executable -envelope. +pointed to by the well-known export `RTR_HEADER` in the export section of the native executable +envelope. For composite images, this export symbol name can be customized using the +`--rtr-header-symbol-name` option in `crossgen2`, which is useful for custom hosts that +directly link against multiple R2R images (instead of loading them dynamically via `dlopen` or +equivalent) and therefore need distinct symbol names to avoid collisions. For single-file R2R executables, there's just one header representing all image sections. For composite and single exe, the global `READYTORUN_HEADER` includes a section of the type diff --git a/docs/design/coreclr/botr/readytorun-platform-native-envelope.md b/docs/design/coreclr/botr/readytorun-platform-native-envelope.md index 9bd1410420ff37..84031b2d5008c0 100644 --- a/docs/design/coreclr/botr/readytorun-platform-native-envelope.md +++ b/docs/design/coreclr/botr/readytorun-platform-native-envelope.md @@ -18,11 +18,15 @@ Mach‑O support will only be supported for composite ReadyToRun when the target `crossgen2` will: -- Produce a Mach-O object file as the composite R2R image with the `RTR_HEADER` export for the `READYTORUN_HEADER`. +- Produce a Mach-O object file as the composite R2R image with the `RTR_HEADER` export for the `READYTORUN_HEADER`. The export symbol name defaults to `RTR_HEADER` but can be overridden with the `--rtr-header-symbol-name` option (see below). - 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` -`crossgen2` does not produce the final shared library. A separate SDK / build linking step must preserve the `RTR_HEADER` export in the final `dylib`. +`crossgen2` does not produce the final shared library. A separate SDK / build linking step must preserve the `RTR_HEADER` export (or the custom name if `--rtr-header-symbol-name` was used) in the final `dylib`. + +### Customizing the RTR_HEADER symbol name + +The `crossgen2` `--rtr-header-symbol-name ` option (requires `--composite`, PE output only) overrides the exported symbol name for the `READYTORUN_HEADER`. This is intended for custom hosts that directly link against multiple R2R images — rather than loading them dynamically via `dlopen` or equivalent — where each image needs a distinct export symbol name to avoid collisions. The runtime or host must then use the matching custom name when locating the header. ### Mach-O Emitter Decisions diff --git a/docs/design/features/readytorun-composite-format-design.md b/docs/design/features/readytorun-composite-format-design.md index afa7a4ed157450..40306af9cfa08b 100644 --- a/docs/design/features/readytorun-composite-format-design.md +++ b/docs/design/features/readytorun-composite-format-design.md @@ -77,7 +77,7 @@ COR header of the input MSIL metadata which then points at the R2R header (via t `ManagedNativeHeaderDirectory` field). In composite R2R files there is no global COR header and the R2R header is located through the -well-known export symbol `RTR_HEADER`. The "actual" input assemblies are tracked under the new +well-known export symbol `RTR_HEADER` (customizable via the `--rtr-header-symbol-name` crossgen2 option). The "actual" input assemblies are tracked under the new R2R header table `READYTORUN_SECTION_ASSEMBLIES`. ## Manifest metadata and component assembly table @@ -128,7 +128,7 @@ properly look up methods stored in this section in the composite R2R case. # CoreCLR runtime changes CoreCLR runtime will need to become able to recognize the new composite R2R format by means -of locating the well-known export `RTR_HEADER` and validating the ReadyToRun header (magic +of locating the well-known export `RTR_HEADER` (or a custom symbol name if `--rtr-header-symbol-name` was specified) and validating the ReadyToRun header (magic constant, version number and the `READYTORUN_FLAG_COMPOSITE` flag) and behave accordingly: * For composite files with embedded MSIL, we shouldn't need MVID checks for reference From 4650e6797ff5b27a22ea250b6b71a634511a7b0f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Apr 2026 02:30:15 +0000 Subject: [PATCH 4/4] Add MachO support for --rtr-header-symbol-name; update docs Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/874de5dc-b706-4578-8581-2385601704ab Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com> --- .../coreclr/botr/readytorun-platform-native-envelope.md | 2 +- src/coreclr/tools/aot/crossgen2/Program.cs | 5 ----- src/coreclr/tools/aot/crossgen2/Properties/Resources.resx | 3 --- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/docs/design/coreclr/botr/readytorun-platform-native-envelope.md b/docs/design/coreclr/botr/readytorun-platform-native-envelope.md index 84031b2d5008c0..6fcca59bf56cdf 100644 --- a/docs/design/coreclr/botr/readytorun-platform-native-envelope.md +++ b/docs/design/coreclr/botr/readytorun-platform-native-envelope.md @@ -26,7 +26,7 @@ Mach‑O support will only be supported for composite ReadyToRun when the target ### Customizing the RTR_HEADER symbol name -The `crossgen2` `--rtr-header-symbol-name ` option (requires `--composite`, PE output only) overrides the exported symbol name for the `READYTORUN_HEADER`. This is intended for custom hosts that directly link against multiple R2R images — rather than loading them dynamically via `dlopen` or equivalent — where each image needs a distinct export symbol name to avoid collisions. The runtime or host must then use the matching custom name when locating the header. +The `crossgen2` `--rtr-header-symbol-name ` option overrides the exported symbol name for the `READYTORUN_HEADER`. This is intended for custom hosts that directly link against multiple R2R images — rather than loading them dynamically via `dlopen` or equivalent — where each image needs a distinct export symbol name to avoid collisions. The runtime or host must then use the matching custom name when locating the header. ### Mach-O Emitter Decisions diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index 6b74c976204e67..03181e2ade4c7c 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -430,11 +430,6 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru { throw new Exception(SR.ErrorReadyToRunHeaderSymbolNameEmpty); } - - if (format != ReadyToRunContainerFormat.PE) - { - throw new Exception(SR.ErrorReadyToRunHeaderSymbolNameRequiresPE); - } } bool compileBubbleGenerics = Get(_command.CompileBubbleGenerics); diff --git a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx index 16d13b2e894202..2e25120f950e7e 100644 --- a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx +++ b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx @@ -177,9 +177,6 @@ Error: --rtr-header-symbol-name must not be empty or whitespace. - - Error: --rtr-header-symbol-name is only supported when --obj-format is PE. - Input file(s) to compile