diff --git a/docs/design/coreclr/botr/readytorun-format.md b/docs/design/coreclr/botr/readytorun-format.md index 24af8f1641220d..c19150ea667ee0 100644 --- a/docs/design/coreclr/botr/readytorun-format.md +++ b/docs/design/coreclr/botr/readytorun-format.md @@ -135,6 +135,7 @@ struct READYTORUN_CORE_HEADER | READYTORUN_FLAG_COMPONENT | 0x00000020 | This is a component assembly of a composite R2R image | READYTORUN_FLAG_MULTIMODULE_VERSION_BUBBLE | 0x00000040 | This R2R module has multiple modules within its version bubble (For versions before version 6.3, all modules are assumed to possibly have this characteristic) | READYTORUN_FLAG_UNRELATED_R2R_CODE | 0x00000080 | This R2R module has code in it that would not be naturally encoded into this module +| READYTORUN_FLAG_PLATFORM_NATIVE_IMAGE | 0x00000100 | The owning composite executable is in the platform native format ## READYTORUN_SECTION diff --git a/docs/design/coreclr/botr/readytorun-platform-native-envelope.md b/docs/design/coreclr/botr/readytorun-platform-native-envelope.md index 78787048cf8537..7f13348ec172a3 100644 --- a/docs/design/coreclr/botr/readytorun-platform-native-envelope.md +++ b/docs/design/coreclr/botr/readytorun-platform-native-envelope.md @@ -22,6 +22,44 @@ Mach‑O support will only be supported for composite ReadyToRun when the target `crossgen2` does not produce the final shared library. A separate SDK / build linking step must preserve the `RTR_HEADER` export in the final `dylib`. +### Mach-O Emitter Decisions + +There's a few cases in the R2R format that are not natively represented in the Mach-O format that have to be emulated. This section will describe some of the design decisions for the Mach-O R2R format. + +#### 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. + +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` + +#### Relocations + +Symbol ranges are represented differently in Mach-O than other platforms. Apple linkers have issues when multiple symbols are defined at the same location. Additionally, the Mach format natively supports a "subtractor" reloc to represent the distance between two symbols. As a result, we can represent the start of the symbol range as the start symbol of the range. We can represent the size of the range we can represent as "end symbol location - start symbol location + end symbol size". + +#### Base Symbol and RVAs + +The R2R format, like the PE format, is heavily based around having RVAs emitted into the image that can be added to the base symbol of the image. The COFF object file format natively supports such a concept, and the PE format uses such a concept in the PE header. However, other formats do not natively support such a concept. + +The Apple linker does provide a base symbol for the Mach format, but the base symbol depends on the output type, generally in the form `__mh__header`. For dylibs, the symbol is `__mh_dylib_header`. This symbol is located at the address returned by `dlinfo` and `dladdr` for the base address. It also points to the Mach header, which can be used to find the size of the image to bound reads of the R2R data. + +As a result, we can emulate this support in the Mach format with ease: + +1. The base symbol that we use in the object writer will be `__mh_dylib_header`. +2. To emit the distance from the base symbol, we will use a subtractor relocation to represent "symbol location - `__mh_dylib_header` location". + ## Runtime: consuming a platform-native R2R image The runtime will be updated to handle platform-native R2R images during assembly load. diff --git a/docs/project/library-servicing.md b/docs/project/library-servicing.md index ee579c133b1886..9e2bc5c64e4d06 100644 --- a/docs/project/library-servicing.md +++ b/docs/project/library-servicing.md @@ -7,7 +7,7 @@ Servicing branches represent shipped versions of .NET. The branch naming convent - **For .NET 8 and .NET 9**: Branch names use the format `release/X.0-staging`. Examples: - `release/9.0-staging` - `release/8.0-staging` - + - **For .NET 10+**: Branch names use the format `release/X.0` (no `-staging` suffix). Examples: - `release/10.0` - `release/11.0` @@ -36,7 +36,7 @@ All the servicing change must go through an approval process. You have two ways For .NET 8 and .NET 9 (use `-staging` suffix): - `/backport to release/9.0-staging` - `/backport to release/8.0-staging` - + For .NET 10+ (no `-staging` suffix): - `/backport to release/10.0` - `/backport to release/11.0` @@ -55,4 +55,4 @@ For all cases, you must: The area owner can then merge the PR once the CI looks good (it's either green or the failures are investigated and determined to be unrelated to the PR). -**Note**: Applying the `Servicing-approved` label ensures the `check-service-labels` CI job passes, which is a mandatory requirement for merging a PR in a servicing branch. \ No newline at end of file +**Note**: Applying the `Servicing-approved` label ensures the `check-service-labels` CI job passes, which is a mandatory requirement for merging a PR in a servicing branch. diff --git a/src/coreclr/crossgen-corelib.proj b/src/coreclr/crossgen-corelib.proj index c30a458f5bdbfa..977ea09b5c2f8d 100644 --- a/src/coreclr/crossgen-corelib.proj +++ b/src/coreclr/crossgen-corelib.proj @@ -17,18 +17,35 @@ - true - false + InvokeCrossgen2 + + true + + false + + false + + CopyILCoreLib false - true + true false - true + true <_MergeMibcFilesCacheFile>$(MibcOptimizationDataDir)/$(TargetOS)/$(TargetArchitecture)/merge_mibc_files.cache + + macho + true + $(CrossgenTargetName);LinkCoreLibMachO + + + + $(CrossgenTargetName);CopyR2RComponentCoreLib + + @@ -36,6 +53,8 @@ System.Private.CoreLib $([MSBuild]::NormalizePath('$(BinDir)', '$(CoreLibAssemblyName).dll')) + $(CoreLibOutputPath) + $([MSBuild]::NormalizePath('$(BinDir)', 'PDB', '$(CoreLibAssemblyName).ni.pdb')) @@ -43,6 +62,16 @@ $([MSBuild]::NormalizePath('$(BinDir)', 'StandardOptimizationData.mibc')) + + $([MSBuild]::NormalizePath('$(IntermediateOutputPath)', '$(CoreLibAssemblyName).o')) + $(CoreLibObjOutputPath) + $([MSBuild]::NormalizePath('$(BinDir)', '$(CoreLibAssemblyName).dylib')) + + + + $([MSBuild]::NormalizePath('$(IntermediateOutputPath)', '$(CoreLibAssemblyName).dll')) + + @@ -88,7 +117,7 @@ - + @@ -96,22 +125,17 @@ - - + - + Outputs="$(CoreLibOutputPath);$(CoreLibNiPdbPath);$(CoreLibPerfMapPath);$(CrossgenCoreLibOutputPath);$(CrossgenCoreLibComponentOutputPath)"> @@ -121,9 +145,11 @@ @(Crossgen2Files->Metadata('OutputPath')->WithMetadataValue('Filename','crossgen2')->WithMetadataValue('Extension','$(ExeSuffix)')) - $(CrossGenDllCmd) -o:$(CoreLibOutputPath) + $(CrossGenDllCmd) -o:$(CrossgenCoreLibOutputPath) $(CrossGenDllCmd) -r:$([MSBuild]::NormalizePath('$(BinDir)', 'IL', '*.dll')) $(CrossGenDllCmd) --targetarch:$(TargetArchitecture) + $(CrossGenDllCmd) --obj-format:$(PublishReadyToRunContainerFormat) + $(CrossGenDllCmd) --composite $(CrossGenDllCmd) --targetos:$(TargetOS) $(CrossGenDllCmd) --targetos:linux @@ -145,21 +171,113 @@ $(CrossGenDllCmd) --perfmap --perfmap-path:$(BinDir) - + + + + + + + + + + + + + + + <_AppleTargetArchitecture Condition="'$(TargetArchitecture)' == 'x64'">x86_64 + <_AppleTargetArchitecture Condition="'$(TargetArchitecture)' == 'arm64'">arm64 + + + + 12.0 + 15.0 + 12.2 + + <_AppleSdkName Condition="'$(TargetOS)' == 'ios'">iphoneos + <_AppleSdkName Condition="'$(TargetOS)' == 'iossimulator'">iphonesimulator + <_AppleSdkName Condition="'$(TargetOS)' == 'tvos'">appletvos + <_AppleSdkName Condition="'$(TargetOS)' == 'tvossimulator'">appletvsimulator + <_AppleSdkName Condition="'$(TargetOS)' == 'maccatalyst' or '$(TargetOS)' == 'osx'">macosx + + <_AppleTripleOS Condition="'$(TargetOS)' == 'osx'">macos + <_AppleTripleOS Condition="'$(TargetOS)' == 'maccatalyst' or $(TargetOS.StartsWith('ios'))">ios + <_AppleTripleOS Condition="$(TargetOS.StartsWith('tvos'))">tvos + + <_AppleTripleAbi Condition="'$(TargetOS)' == 'ios' or '$(TargetOS)' == 'tvos'">macho + <_AppleTripleAbi Condition="'$(TargetOS)' == 'maccatalyst'">macabi + <_AppleTripleAbi Condition="$(TargetOS.EndsWith('simulator'))">simulator + + $(_AppleTargetArchitecture)-apple-$(_AppleTripleOS)$(AppleMinOSVersion) + $(_AppleTargetArchitecture)-apple-$(_AppleTripleOS)$(AppleMinOSVersion)-$(_AppleTripleAbi) + - + + xcrun + <_WhereXcrun>0 + - + + + + - + + + - + - + + + - + - - + + <_MachLinkerArg Include="-gz=zlib" /> + <_MachLinkerArg Include="-isysroot "$(SysRoot)"" /> + <_MachLinkerArg Include="--target=$(TargetTriple)" /> + <_MachLinkerArg Include="-g" /> + <_MachLinkerArg Include="-dynamiclib" /> + <_MachLinkerArg Include="-Wl,-dead_strip" /> + + + + + + + + + <_XcodeVersion>$([System.Text.RegularExpressions.Regex]::Match($(_XcodeVersionString), '[1-9]\d*')) + + + + <_MachLinkerArg Condition="'$(UseLdClassicXCodeLinker)' == 'true' or '$(_XcodeVersion)' == '15' or '$(_XcodeVersion)' == '16'" Include="-ld_classic" /> + + + + <_MachLinkerArg Include="-Wl,-install_name,"@rpath/$(CoreLibAssemblyName).dylib"" /> + <_MachLinkerArg Include="$(CoreLibObjOutputPath)" /> + <_MachLinkerArg Include="-o $(CoreLibDylibOutputPath)" /> + + + + + + + + + + diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h index 93826f5b8ca771..2f2ec3a0a577ac 100644 --- a/src/coreclr/inc/readytorun.h +++ b/src/coreclr/inc/readytorun.h @@ -20,7 +20,7 @@ // If you update this, ensure you run `git grep MINIMUM_READYTORUN_MAJOR_VERSION` // and handle pending work. #define READYTORUN_MAJOR_VERSION 17 -#define READYTORUN_MINOR_VERSION 0x0000 +#define READYTORUN_MINOR_VERSION 0x0001 #define MINIMUM_READYTORUN_MAJOR_VERSION 17 @@ -47,7 +47,10 @@ // R2R Version 14 changed x86 code generation to use funclets // R2R Version 15 removes double to int/uint helper calls // R2R Version 16 replaces the compression format for debug boundaries with a new format that is smaller and more efficient to parse +// R2R 16 is not backward compatible with 15.x or earlier. // R2R Version 17 adds support for producing "fat" debug information (that e.g. can include async debug info) +// R2R 17 is not backward compatible with 16.x or earlier. +// R2R Version 17.1 adds the READYTORUN_FLAG_PLATFORM_NATIVE_IMAGE flag to specify that the R2R image pointed to by OwnerCompositeExecutable is in the platform native format. struct READYTORUN_CORE_HEADER { @@ -84,6 +87,7 @@ enum ReadyToRunFlag READYTORUN_FLAG_COMPONENT = 0x00000020, // This is the header describing a component assembly of composite R2R READYTORUN_FLAG_MULTIMODULE_VERSION_BUBBLE = 0x00000040, // This R2R module has multiple modules within its version bubble (For versions before version 6.2, all modules are assumed to possibly have this characteristic) READYTORUN_FLAG_UNRELATED_R2R_CODE = 0x00000080, // This R2R module has code in it that would not be naturally encoded into this module + READYTORUN_FLAG_PLATFORM_NATIVE_IMAGE = 0x00000100, // The owning composite executable is in the platform native format }; enum class ReadyToRunSectionType : uint32_t diff --git a/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h b/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h index f634629cfbada3..a5c6e14845f1ec 100644 --- a/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h +++ b/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h @@ -12,7 +12,7 @@ struct ReadyToRunHeaderConstants static const uint32_t Signature = 0x00525452; // 'RTR' static const uint32_t CurrentMajorVersion = 17; - static const uint32_t CurrentMinorVersion = 0; + static const uint32_t CurrentMinorVersion = 1; }; struct ReadyToRunHeader diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs index d826a0a6248142..1aa07caf730cd6 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs @@ -65,9 +65,15 @@ internal sealed partial class MachObjectWriter : UnixObjectWriter private readonly Dictionary _symbolNameToIndex = new(); private readonly List _symbolTable = new(); private readonly MachDynamicLinkEditSymbolTable _dySymbolTable = new(); + private readonly Dictionary _rangeSymbols = new(); - public MachObjectWriter(NodeFactory factory, ObjectWritingOptions options) - : base(factory, options) + /// + /// Base symbol to use for relocations. + /// + private readonly string _baseSymbolName; + + public MachObjectWriter(NodeFactory factory, ObjectWritingOptions options, OutputInfoBuilder outputInfoBuilder = null) + : base(factory, options, outputInfoBuilder) { switch (factory.Target.Architecture) { @@ -92,6 +98,12 @@ public MachObjectWriter(NodeFactory factory, ObjectWritingOptions options) _targetOS = factory.Target.OperatingSystem; } + public MachObjectWriter(NodeFactory factory, ObjectWritingOptions options, OutputInfoBuilder outputInfoBuilder, string baseSymbolName) + : this(factory, options, outputInfoBuilder) + { + _baseSymbolName = baseSymbolName; + } + private protected override bool UsesSubsectionsViaSymbols => true; private protected override void EmitSectionsAndLayout() @@ -372,6 +384,16 @@ protected internal override void UpdateSectionAlignment(int sectionIndex, int al machSection.Log2Alignment = Math.Max(machSection.Log2Alignment, (uint)BitOperations.Log2((uint)alignment)); } + private protected override void EmitSymbolRangeDefinition(string rangeNodeName, string startSymbolName, string endSymbolName, SymbolDefinition endSymbol) + { + // Mach has a few characteristics that make range symbols more difficult to emit: + // - Emitting two symbols in the same location is not well supported by the Apple linker. + // - Mach-O makes it more difficult to preserve symbol layout in the way we like. + // However, it also has one thing that makes it easier: + // - Mach-O natively supports a relocation type that can represents the distance between two symbols. + _rangeSymbols.Add(rangeNodeName, (startSymbolName, endSymbolName, endSymbol.Size)); + } + protected internal override unsafe void EmitRelocation( int sectionIndex, long offset, @@ -380,6 +402,24 @@ protected internal override unsafe void EmitRelocation( string symbolName, long addend) { + // We don't emit the range node name into the image as it overlaps with another symbol. + // For relocs to it, instead target the start symbol. + // For the "symbol size" reloc, we'll handle it later when we emit relocations in the Mach format. + if (_rangeSymbols.TryGetValue(symbolName, out (string StartNode, string, int Size) range)) + { + if (relocType == RelocType.IMAGE_REL_SYMBOL_SIZE) + { + // In this case, we will later emit the reloc as a pair of SUBTRACTOR + UNSIGNED relocs. + // However, we need to adjust for the size of the end symbol here. + BinaryPrimitives.WriteUInt32LittleEndian(data, (uint)addend + (uint)range.Size); + } + else + { + // Otherwise, point relocs to the range as a reloc to the start symbol (as these locations are identical). + symbolName = range.StartNode; + } + } + // Mach-O doesn't use relocations between DWARF sections, so embed the offsets directly if (relocType is IMAGE_REL_BASED_DIR64 or IMAGE_REL_BASED_HIGHLOW && _sections[sectionIndex].IsDwarfSection) @@ -502,6 +542,13 @@ private protected override void EmitSymbolTable( _dySymbolTable.ExternalSymbolsCount = (uint)definedSymbols.Count; uint savedSymbolIndex = symbolIndex; + + // Add the base symbol as an undefined symbol. + if (_baseSymbolName is not null) + { + undefinedSymbols.Add(_baseSymbolName); + } + foreach (string externSymbol in undefinedSymbols) { if (!_symbolNameToIndex.ContainsKey(externSymbol)) @@ -549,6 +596,36 @@ private void EmitRelocationsX64(int sectionIndex, List reloc relocationList.Reverse(); foreach (SymbolicRelocation symbolicRelocation in relocationList) { + if (symbolicRelocation.Type == RelocType.IMAGE_REL_SYMBOL_SIZE + && _rangeSymbols.TryGetValue(symbolicRelocation.SymbolName, out (string, string, int) range)) + { + // Represent as X86_64_RELOC_SUBTRACTOR + X86_64_RELOC_UNSIGNED. + (string StartNode, string EndNode, int Size) = range; + uint startSymbolIndex = _symbolNameToIndex[StartNode]; + uint endSymbolIndex = _symbolNameToIndex[EndNode]; + sectionRelocations.Add( + new MachRelocation + { + Address = (int)symbolicRelocation.Offset, + SymbolOrSectionIndex = endSymbolIndex, + Length = 4, + RelocationType = X86_64_RELOC_SUBTRACTOR, + IsExternal = true, + IsPCRelative = false, + }); + sectionRelocations.Add( + new MachRelocation + { + Address = (int)symbolicRelocation.Offset, + SymbolOrSectionIndex = startSymbolIndex, + Length = 4, + RelocationType = X86_64_RELOC_UNSIGNED, + IsExternal = true, + IsPCRelative = false, + }); + continue; + } + uint symbolIndex = _symbolNameToIndex[symbolicRelocation.SymbolName]; if (symbolicRelocation.Type == IMAGE_REL_BASED_DIR64) @@ -601,6 +678,37 @@ private void EmitRelocationsX64(int sectionIndex, List reloc IsPCRelative = true, }); } + else if (symbolicRelocation.Type == IMAGE_REL_BASED_ADDR32NB) + { + if (_baseSymbolName is null) + { + throw new NotSupportedException("A base symbol name must be provided for IMAGE_REL_BASED_ADDR32NB relocations."); + } + + Debug.Assert(_symbolNameToIndex.ContainsKey(_baseSymbolName)); + + // Represent as X86_64_RELOC_SUBTRACTOR + X86_64_RELOC_UNSIGNED against the base symbol. + sectionRelocations.Add( + new MachRelocation + { + Address = (int)symbolicRelocation.Offset, + SymbolOrSectionIndex = (uint)symbolIndex, + Length = 4, + RelocationType = X86_64_RELOC_SUBTRACTOR, + IsExternal = true, + IsPCRelative = false, + }); + sectionRelocations.Add( + new MachRelocation + { + Address = (int)symbolicRelocation.Offset, + SymbolOrSectionIndex = _symbolNameToIndex[_baseSymbolName], + Length = 4, + RelocationType = X86_64_RELOC_UNSIGNED, + IsExternal = true, + IsPCRelative = false, + }); + } else { throw new NotSupportedException("Unknown relocation type: " + symbolicRelocation.Type); @@ -615,6 +723,36 @@ private void EmitRelocationsArm64(int sectionIndex, List rel relocationList.Reverse(); foreach (SymbolicRelocation symbolicRelocation in relocationList) { + if (symbolicRelocation.Type == RelocType.IMAGE_REL_SYMBOL_SIZE + && _rangeSymbols.TryGetValue(symbolicRelocation.SymbolName, out (string, string, int) range)) + { + // Represent as ARM64_RELOC_SUBTRACTOR + ARM64_RELOC_UNSIGNED. + (string StartNode, string EndNode, _) = range; + uint startSymbolIndex = _symbolNameToIndex[StartNode]; + uint endSymbolIndex = _symbolNameToIndex[EndNode]; + sectionRelocations.Add( + new MachRelocation + { + Address = (int)symbolicRelocation.Offset, + SymbolOrSectionIndex = endSymbolIndex, + Length = 4, + RelocationType = ARM64_RELOC_SUBTRACTOR, + IsExternal = true, + IsPCRelative = false, + }); + sectionRelocations.Add( + new MachRelocation + { + Address = (int)symbolicRelocation.Offset, + SymbolOrSectionIndex = startSymbolIndex, + Length = 4, + RelocationType = ARM64_RELOC_UNSIGNED, + IsExternal = true, + IsPCRelative = false, + }); + continue; + } + uint symbolIndex = _symbolNameToIndex[symbolicRelocation.SymbolName]; if (symbolicRelocation.Type == IMAGE_REL_BASED_ARM64_BRANCH26) @@ -703,6 +841,37 @@ private void EmitRelocationsArm64(int sectionIndex, List rel IsPCRelative = false, }); } + else if (symbolicRelocation.Type == IMAGE_REL_BASED_ADDR32NB) + { + if (_baseSymbolName is null) + { + throw new NotSupportedException("A base symbol name must be provided for IMAGE_REL_BASED_ADDR32NB relocations."); + } + + Debug.Assert(_symbolNameToIndex.ContainsKey(_baseSymbolName)); + + // Represent as ARM64_RELOC_SUBTRACTOR + ARM64_RELOC_UNSIGNED against the base symbol. + sectionRelocations.Add( + new MachRelocation + { + Address = (int)symbolicRelocation.Offset, + SymbolOrSectionIndex = (uint)symbolIndex, + Length = 4, + RelocationType = ARM64_RELOC_SUBTRACTOR, + IsExternal = true, + IsPCRelative = false, + }); + sectionRelocations.Add( + new MachRelocation + { + Address = (int)symbolicRelocation.Offset, + SymbolOrSectionIndex = _symbolNameToIndex[_baseSymbolName], + Length = 4, + RelocationType = ARM64_RELOC_UNSIGNED, + IsExternal = true, + IsPCRelative = false, + }); + } else { throw new NotSupportedException("Unknown relocation type: " + symbolicRelocation.Type); diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs index 6cf90bb47ccb97..ce8347cd072975 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs @@ -506,19 +506,12 @@ public void EmitObject(Stream outputFileStream, IReadOnlyCollection CreatePEObjectWriter(), + ReadyToRunContainerFormat.MachO => CreateMachObjectWriter(), + _ => throw new UnreachableException() + }; using FileStream stream = new FileStream(_objectFilePath, FileMode.Create); objectWriter.EmitObject(stream, _nodes, dumper: null, logger); @@ -283,6 +269,34 @@ public void EmitReadyToRunObjects(ReadyToRunContainerFormat format, Logger logge } } + private PEObjectWriter CreatePEObjectWriter() + { + int? timeDateStamp; + + if (_nodeFactory.CompilationModuleGroup.IsCompositeBuildMode && _componentModule == null) + { + timeDateStamp = null; + } + else + { + PEReader inputPeReader = (_componentModule != null ? _componentModule.PEReader : _nodeFactory.CompilationModuleGroup.CompilationModuleSet.First().PEReader); + timeDateStamp = inputPeReader.PEHeaders.CoffHeader.TimeDateStamp; + } + + PEObjectWriter objectWriter = new(_nodeFactory, ObjectWritingOptions.None, _outputInfoBuilder, _objectFilePath, _customPESectionAlignment, timeDateStamp); + + if (_nodeFactory.CompilationModuleGroup.IsCompositeBuildMode && _componentModule == null) + { + objectWriter.AddExportedSymbol("RTR_HEADER"); + } + return objectWriter; + } + + private MachObjectWriter CreateMachObjectWriter() + { + return new MachObjectWriter(_nodeFactory, ObjectWritingOptions.None, _outputInfoBuilder, baseSymbolName: "__mh_dylib_header"); + } + public static void EmitObject( string objectFilePath, EcmaModule componentModule, @@ -302,7 +316,7 @@ public static void EmitObject( int customPESectionAlignment, Logger logger) { - Console.WriteLine($@"Emitting R2R PE file: {objectFilePath}"); + Console.WriteLine($@"Emitting R2R {format} file: {objectFilePath}"); ReadyToRunObjectWriter objectWriter = new ReadyToRunObjectWriter( objectFilePath, componentModule, 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 aa31162f89cd63..5b4df6872e3ba9 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,7 +24,14 @@ public CopiedCorHeaderNode(EcmaModule sourceModule) public override ObjectNodeSection GetSection(NodeFactory factory) { - return ObjectNodeSection.CorMetaSection; + // 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 + }; } 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 6eb60c8fe05c62..4accb7487377e4 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,7 +26,14 @@ public CopiedFieldRvaNode(EcmaModule module, int rva) public override ObjectNodeSection GetSection(NodeFactory factory) { - return ObjectNodeSection.CorMetaSection; + // 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 + }; } 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 0756da31a76cb1..a0367e0dec2b36 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,7 +20,14 @@ public CopiedManagedResourcesNode(EcmaModule module) public override ObjectNodeSection GetSection(NodeFactory factory) { - return ObjectNodeSection.CorMetaSection; + // 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 + }; } 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 ae609407f97236..e18fd800555f9d 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,7 +27,14 @@ public CopiedMetadataBlobNode(EcmaModule sourceModule) public override ObjectNodeSection GetSection(NodeFactory factory) { - return ObjectNodeSection.CorMetaSection; + // 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 + }; } 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 94c60cfc84639d..d35993fdf0441f 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,7 +24,14 @@ public CopiedMethodILNode(EcmaMethod method) public override ObjectNodeSection GetSection(NodeFactory factory) { - return ObjectNodeSection.CorMetaSection; + // 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 + }; } 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 8398909174018a..124a21ca1abe85 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,7 +23,14 @@ public CopiedStrongNameSignatureNode(EcmaModule module) public override ObjectNodeSection GetSection(NodeFactory factory) { - return ObjectNodeSection.CorMetaSection; + // 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 + }; } public override bool IsShareable => false; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ImportThunk.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ImportThunk.cs index a1dda9c6f8da3e..84589fb5bf11db 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ImportThunk.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ImportThunk.cs @@ -11,7 +11,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun /// This node emits a thunk calling DelayLoad_Helper with a given instance signature /// to populate its indirection cell. /// - public partial class ImportThunk : AssemblyStubNode, ISymbolDefinitionNode + public partial class ImportThunk : AssemblyStubNode, ISymbolDefinitionNode, ISortableSymbolNode { enum Kind { @@ -117,7 +117,7 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact protected override void OnMarked(NodeFactory factory) { - factory.DelayLoadMethodCallThunks.OnImportThunkMarked(this); + factory.DelayLoadMethodCallThunks.OnNodeInRangeMarked(this); } } } 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 d9d593d176a363..80047a4c6b0789 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,7 +24,15 @@ public MethodColdCodeNode(MethodDesc owningMethod) public override ObjectNodeSection GetSection(NodeFactory factory) { - return ObjectNodeSection.TextSection; + // 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, + _ => ObjectNodeSection.ManagedCodeUnixContentSection + }; } public override bool IsShareable => false; 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 ae83abd5507695..79e6076914004b 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,7 +303,15 @@ protected override string GetName(NodeFactory factory) public override ObjectNodeSection GetSection(NodeFactory factory) { - return ObjectNodeSection.TextSection; + // 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, + _ => ObjectNodeSection.ManagedCodeUnixContentSection + }; } public FrameInfo[] FrameInfos => _frameInfos; 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 bf2aa3bd0622e7..caec889983266e 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 @@ -18,7 +18,15 @@ public RuntimeFunctionsGCInfoNode() public override ObjectNodeSection GetSection(NodeFactory factory) { - return ObjectNodeSection.XDataSection; + // 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 + }; } 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 66aaa88514b476..7f9cb32327fc88 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,13 @@ public RuntimeFunctionsTableNode(NodeFactory nodeFactory) public override ObjectNodeSection GetSection(NodeFactory factory) { - return ObjectNodeSection.PDataSection; + // 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 + }; } public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodCallThunkNodeRange.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SymbolNodeRange.cs similarity index 64% rename from src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodCallThunkNodeRange.cs rename to src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SymbolNodeRange.cs index 0b777f2b233ded..99847780826dfe 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodCallThunkNodeRange.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SymbolNodeRange.cs @@ -10,48 +10,47 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun { /// - /// Provides an ISymbolNode for the R2R header table to relocate against when looking up the delay load method call thunks. - /// They are emitted in a contiguous run of object nodes. This symbol is used in the object writer to represent the range - /// of bytes containing all the thunks. + /// Provides a machanism to represent a contiguous range of object nodes as a single node where the reloc can refer to the range between two nodes. /// - public class DelayLoadMethodCallThunkNodeRange : DependencyNodeCore, ISymbolRangeNode + public sealed class SymbolNodeRange(string name) : DependencyNodeCore, ISymbolRangeNode { - private const string NodeName = "DelayLoadMethodCallThunkNodeRange"; - private ImportThunk _startNode; - private ImportThunk _endNode; + private ISortableSymbolNode _startNode; + private ISortableSymbolNode _endNode; - public override bool InterestingForDynamicDependencyAnalysis => false; - public override bool HasDynamicDependencies => false; - public override bool HasConditionalStaticDependencies => false; - public override bool StaticDependenciesAreComputed => true; - public int Offset => 0; - public bool RepresentsIndirectionCell => false; - public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => []; - public override IEnumerable GetStaticDependencies(NodeFactory context) => []; - public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => []; - protected override string GetName(NodeFactory context) => NodeName; + protected override string GetName(NodeFactory context) => name; public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { - sb.Append($"__{NodeName}"); + sb.Append($"__{name}"); } - public void OnImportThunkMarked(ImportThunk thunk) + public void OnNodeInRangeMarked(ISortableSymbolNode node) { if (_startNode is null - || CompilerComparer.Instance.Compare(thunk, _startNode) <= 0) + || CompilerComparer.Instance.Compare(node, _startNode) <= 0) { - _startNode = thunk; + _startNode = node; } if (_endNode is null - || CompilerComparer.Instance.Compare(thunk, _endNode) > 0) + || CompilerComparer.Instance.Compare(node, _endNode) > 0) { - _endNode = thunk; + _endNode = node; } } public ISymbolNode StartNode(NodeFactory factory) => _startNode; public ISymbolNode EndNode(NodeFactory factory) => _endNode; + + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => false; + public override bool StaticDependenciesAreComputed => true; + public int Offset => 0; + public bool RepresentsIndirectionCell => false; + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => []; + public override IEnumerable GetStaticDependencies(NodeFactory context) => []; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => []; + } } 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 343a860dddd9f4..75b3ffc0fb2582 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,7 +21,14 @@ public Win32ResourcesNode(ResourceData resourceData) public override ObjectNodeSection GetSection(NodeFactory factory) { - return ObjectNodeSection.Win32ResourcesSection; + // 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 + }; } public override bool IsShareable => false; 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 e1c1e9a2448b10..238de065635ef8 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs @@ -72,6 +72,8 @@ public sealed class NodeFactory public TargetDetails Target { get; } + public ReadyToRunContainerFormat Format { get; } + public ReadyToRunCompilationModuleGroupBase CompilationModuleGroup { get; } public ProfileDataManager ProfileDataManager { get; } @@ -199,10 +201,10 @@ public NodeFactory( ResourceData win32Resources, ReadyToRunFlags flags, NodeFactoryOptimizationFlags nodeFactoryOptimizationFlags, + ReadyToRunContainerFormat format, ulong imageBase, EcmaModule associatedModule, - int genericCycleDepthCutoff, - int genericCycleBreadthCutoff) + int genericCycleDepthCutoff, int genericCycleBreadthCutoff) { OptimizationFlags = nodeFactoryOptimizationFlags; TypeSystemContext = context; @@ -214,6 +216,7 @@ public NodeFactory( CopiedCorHeaderNode = corHeaderNode; DebugDirectoryNode = debugDirectoryNode; Resolver = compilationModuleGroup.Resolver; + Format = format; Header = new GlobalHeaderNode(flags, associatedModule); ImageBase = imageBase; @@ -376,7 +379,7 @@ private void CreateNodeCaches() public RuntimeFunctionsGCInfoNode RuntimeFunctionsGCInfo; - public DelayLoadMethodCallThunkNodeRange DelayLoadMethodCallThunks; + public SymbolNodeRange DelayLoadMethodCallThunks; public InstanceEntryPointTableNode InstanceEntryPointTable; @@ -699,7 +702,7 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase graph, I RuntimeFunctionsGCInfo = new RuntimeFunctionsGCInfoNode(); graph.AddRoot(RuntimeFunctionsGCInfo, "GC info is always generated"); - DelayLoadMethodCallThunks = new DelayLoadMethodCallThunkNodeRange(); + DelayLoadMethodCallThunks = new SymbolNodeRange("DelayLoadMethodCallThunkNodeRange"); graph.AddRoot(DelayLoadMethodCallThunks, "DelayLoadMethodCallThunks header entry is always generated"); Header.Add(Internal.Runtime.ReadyToRunSectionType.DelayLoadMethodCallThunks, DelayLoadMethodCallThunks, DelayLoadMethodCallThunks); @@ -778,7 +781,7 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase graph, I } } - InliningInfoNode crossModuleInliningInfoTable = new InliningInfoNode(null, + InliningInfoNode crossModuleInliningInfoTable = new InliningInfoNode(null, CompilationModuleGroup.IsCompositeBuildMode ? InliningInfoNode.InfoType.CrossModuleInliningForCrossModuleDataOnly : InliningInfoNode.InfoType.CrossModuleAllMethods); Header.Add(Internal.Runtime.ReadyToRunSectionType.CrossModuleInlineInfo, crossModuleInliningInfoTable, crossModuleInliningInfoTable); this.CrossModuleInlningInfo = crossModuleInliningInfoTable; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs index e7e4039f95071f..b47818b744ba5e 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs @@ -469,6 +469,21 @@ private void RewriteComponentFile(string inputFile, string outputFile, string ow flags |= _nodeFactory.CompilationModuleGroup.GetReadyToRunFlags() & ReadyToRunFlags.READYTORUN_FLAG_MultiModuleVersionBubble; + bool isNativeCompositeImage = false; + if (NodeFactory.Target.IsWindows && NodeFactory.Format == ReadyToRunContainerFormat.PE) + { + isNativeCompositeImage = true; + } + else if (NodeFactory.Target.IsApplePlatform && NodeFactory.Format == ReadyToRunContainerFormat.MachO) + { + isNativeCompositeImage = true; + } + + if (isNativeCompositeImage) + { + flags |= ReadyToRunFlags.READYTORUN_FLAG_PlatformNativeImage; + } + CopiedCorHeaderNode copiedCorHeader = new CopiedCorHeaderNode(inputModule); // Re-written components shouldn't have any additional diagnostic information - only information about the forwards. // Even with all of this, we might be modifying the image in a silly manner - adding a directory when if didn't have one. @@ -481,10 +496,11 @@ private void RewriteComponentFile(string inputFile, string outputFile, string ow copiedCorHeader, debugDirectory, win32Resources: new Win32Resources.ResourceData(inputModule), - flags, - optimizationFlags, - _nodeFactory.ImageBase, - automaticTypeValidation ? inputModule : null, + flags: flags, + nodeFactoryOptimizationFlags: optimizationFlags, + format: ReadyToRunContainerFormat.PE, + imageBase: _nodeFactory.ImageBase, + associatedModule: automaticTypeValidation ? inputModule : null, genericCycleDepthCutoff: -1, // We don't need generic cycle detection when rewriting component assemblies genericCycleBreadthCutoff: -1); // as we're not actually compiling anything diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs index 17dede9e9adebe..1501401effba15 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs @@ -276,6 +276,7 @@ public override ICompilation ToCompilation() win32Resources, flags, _nodeFactoryOptimizationFlags, + _format, _imageBase, automaticTypeValidation ? singleModule : null, genericCycleDepthCutoff: _genericCycleDetectionDepthCutoff, diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index f145cbeae61297..4023d4399f8266 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -178,7 +178,7 @@ - + diff --git a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs index dc07e5abc4c43f..3f9d6d2506765b 100644 --- a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs +++ b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs @@ -415,6 +415,7 @@ private static ReadyToRunContainerFormat MakeOutputFormat(ArgumentResult result) return result.Tokens[0].Value.ToLowerInvariant() switch { "pe" => ReadyToRunContainerFormat.PE, + "macho" => ReadyToRunContainerFormat.MachO, _ => throw new CommandLineException(SR.InvalidOutputFormat) }; } diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index 231f2f637ee50e..dfc8dd737d358c 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -401,6 +401,12 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru throw new Exception(string.Format(SR.ErrorMultipleInputFilesCompositeModeOnly, string.Join("; ", inputModules))); } + ReadyToRunContainerFormat format = Get(_command.OutputFormat); + if (!composite && format != ReadyToRunContainerFormat.PE) + { + throw new Exception(string.Format(SR.ErrorContainerFormatRequiresComposite, format)); + } + bool compileBubbleGenerics = Get(_command.CompileBubbleGenerics); ReadyToRunCompilationModuleGroupBase compilationGroup; List compilationRoots = new List(); @@ -616,7 +622,7 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru .UseHotColdSplitting(Get(_command.HotColdSplitting)) .GenerateOutputFile(outFile) .UseImageBase(_imageBase) - .UseContainerFormat(Get(_command.OutputFormat)) + .UseContainerFormat(format) .UseILProvider(ilProvider) .UseBackendOptions(Get(_command.CodegenOptions)) .UseLogger(logger) diff --git a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx index 341842b3bb94ff..e1f59a69a5cff7 100644 --- a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx +++ b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx @@ -432,4 +432,7 @@ Format must be PE + + Error: Producing ReadyToRun output in the {0} format is only supported for Composite ReadyToRun images. + \ No newline at end of file diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props index 7222998213ee3e..abab8e9fa58795 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props @@ -73,6 +73,8 @@ + + diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.CoreCLR.sfxproj b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.CoreCLR.sfxproj index 69d97018166e70..f8e03ff1dc0b71 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.CoreCLR.sfxproj +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.CoreCLR.sfxproj @@ -32,6 +32,12 @@ $(PublishReadyToRun) + + + true + macho + + @@ -52,6 +58,10 @@ TargetOS="$(_CrossGenTargetOS)" PerfmapFormatVersion="$(PublishReadyToRunPerfmapFormatVersion)"/> + + + false + + + + + @rpath/%(_ReadyToRunNativeObjectOutputs.Filename)%(_ReadyToRunNativeObjectOutputs.Extension) + + + + <_MachLinkerArg Include="-Wl,-install_name,"$(SharedLibraryInstallName)"" /> + <_MachLinkerArg Include="%(_ReadyToRunNativeObjectOutputs.NativeLinkerInputPath)" /> + <_MachLinkerArg Include="-o %(_ReadyToRunNativeObjectOutputs.Identity)" /> + + + + + + + + + + + diff --git a/src/tasks/Crossgen2Tasks/PrepareForReadyToRunCompilation.cs b/src/tasks/Crossgen2Tasks/PrepareForReadyToRunCompilation.cs index b990120d246e48..423e5e6e1aeac7 100644 --- a/src/tasks/Crossgen2Tasks/PrepareForReadyToRunCompilation.cs +++ b/src/tasks/Crossgen2Tasks/PrepareForReadyToRunCompilation.cs @@ -20,6 +20,7 @@ public class PrepareForReadyToRunCompilation : TaskBase public bool EmitSymbols { get; set; } public bool ReadyToRunUseCrossgen2 { get; set; } public bool Crossgen2Composite { get; set; } + public string Crossgen2ContainerFormat { get; set; } [Required] public string OutputPath { get; set; } @@ -278,7 +279,21 @@ private void ProcessInputFileList( var compositeR2RImageRelativePath = MainAssembly.GetMetadata(MetadataKeys.RelativePath); compositeR2RImageRelativePath = Path.ChangeExtension(compositeR2RImageRelativePath, "r2r" + Path.GetExtension(compositeR2RImageRelativePath)); + + // For non-PE formats, we may need to do a post-processing step to get the final R2R image + // after running crossgen2. In this case, compositeR2RImageRelativePath is the intermediate file + // produced by crossgen2, and compositeR2RFinalImageRelativePath is the final file to be published + // by any post-crossgen2 linking steps and used at runtime. + var compositeR2RFinalImageRelativePath = compositeR2RImageRelativePath; + + if (Crossgen2ContainerFormat == "macho") + { + compositeR2RImageRelativePath = Path.ChangeExtension(compositeR2RImageRelativePath, ".o"); + compositeR2RFinalImageRelativePath = Path.ChangeExtension(compositeR2RImageRelativePath, ".dylib"); + } + var compositeR2RImage = Path.Combine(OutputPath, compositeR2RImageRelativePath); + var compositeR2RImageFinal = Path.Combine(OutputPath, compositeR2RFinalImageRelativePath); TaskItem r2rCompilationEntry = new(MainAssembly) { @@ -333,10 +348,17 @@ private void ProcessInputFileList( // Publish it TaskItem compositeR2RFileToPublish = new(MainAssembly) { - ItemSpec = compositeR2RImage + ItemSpec = compositeR2RImageFinal }; compositeR2RFileToPublish.RemoveMetadata(MetadataKeys.OriginalItemSpec); - compositeR2RFileToPublish.SetMetadata(MetadataKeys.RelativePath, compositeR2RImageRelativePath); + compositeR2RFileToPublish.SetMetadata(MetadataKeys.RelativePath, compositeR2RFinalImageRelativePath); + + if (compositeR2RImageFinal != compositeR2RImage) + { + compositeR2RFileToPublish.SetMetadata(MetadataKeys.RequiresNativeLink, "true"); + compositeR2RFileToPublish.SetMetadata(MetadataKeys.NativeLinkerInputPath, compositeR2RImage); + } + r2rFilesPublishList.Add(compositeR2RFileToPublish); } } diff --git a/src/tasks/Crossgen2Tasks/RunReadyToRunCompiler.cs b/src/tasks/Crossgen2Tasks/RunReadyToRunCompiler.cs index feca628b33d50c..c5c7523099e721 100644 --- a/src/tasks/Crossgen2Tasks/RunReadyToRunCompiler.cs +++ b/src/tasks/Crossgen2Tasks/RunReadyToRunCompiler.cs @@ -24,6 +24,7 @@ public class RunReadyToRunCompiler : ToolTask public bool UseCrossgen2 { get; set; } public string Crossgen2ExtraCommandLineArgs { get; set; } public ITaskItem[] Crossgen2PgoFiles { get; set; } + public string Crossgen2ContainerFormat { get; set; } [Output] public bool WarningsDetected { get; set; } @@ -342,6 +343,11 @@ private string GenerateCrossgen2ResponseFile() } } + if (!string.IsNullOrEmpty(Crossgen2ContainerFormat)) + { + result.AppendLine($"--obj-format:{Crossgen2ContainerFormat}"); + } + if (!string.IsNullOrEmpty(Crossgen2ExtraCommandLineArgs)) { foreach (string extraArg in Crossgen2ExtraCommandLineArgs.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))