diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IObjectDumper.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/IObjectDumper.cs
similarity index 100%
rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IObjectDumper.cs
rename to src/coreclr/tools/Common/Compiler/DependencyAnalysis/IObjectDumper.cs
diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ISymbolNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ISymbolNode.cs
index 336c0121502952..742e0bd9505f6b 100644
--- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ISymbolNode.cs
+++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ISymbolNode.cs
@@ -73,6 +73,40 @@ public interface ISymbolDefinitionNode : ISymbolNode
new int Offset { get; }
}
+ ///
+ /// Represents a symbol that should encompass the full range between the start and end symbols specified.
+ ///
+ public interface ISymbolRangeNode : ISymbolNode
+ {
+ ///
+ /// Return a node that determines the start of the range.
+ /// This node will be used for linkage.
+ ///
+ ISymbolNode StartNode(NodeFactory factory);
+
+ ///
+ /// Return a node that is used to determine the symbol size.
+ ///
+ ISymbolNode EndNode(NodeFactory factory);
+ }
+
+ ///
+ /// Represents a node that generates a checksum for the resulting output blob.
+ ///
+ public interface IChecksumNode : ISymbolNode
+ {
+ int ChecksumSize { get; }
+
+ void EmitChecksum(ReadOnlySpan outputBlob, Span checksumLocation);
+ }
+
+ ///
+ /// Represents a symbol that should not be shared with another symbol during writing of the object file.
+ ///
+ public interface IUniqueSymbolNode : ISymbolNode
+ {
+ }
+
public static class ISymbolNodeExtensions
{
[ThreadStatic]
diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs
index 53e4671b57a58f..484792d0497ba9 100644
--- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs
+++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs
@@ -305,6 +305,10 @@ public void EmitReloc(ISymbolNode symbol, RelocType relocType, int delta = 0)
// Do not vacate space for this kind of relocation, because
// the space is embedded in the instruction.
break;
+
+ case RelocType.IMAGE_REL_FILE_CHECKSUM_CALLBACK:
+ EmitZeros(delta);
+ break;
default:
throw new NotImplementedException();
}
@@ -315,6 +319,11 @@ public void EmitPointerReloc(ISymbolNode symbol, int delta = 0)
EmitReloc(symbol, (_target.PointerSize == 8) ? RelocType.IMAGE_REL_BASED_DIR64 : RelocType.IMAGE_REL_BASED_HIGHLOW, delta);
}
+ public void EmitChecksumReloc(IChecksumNode checksum)
+ {
+ EmitReloc(checksum, RelocType.IMAGE_REL_FILE_CHECKSUM_CALLBACK, checksum.ChecksumSize);
+ }
+
public ObjectNode.ObjectData ToObjectData()
{
#if DEBUG
diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNodeSection.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNodeSection.cs
index 9444cdb58aadfb..a226d8f4af2e1f 100644
--- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNodeSection.cs
+++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNodeSection.cs
@@ -51,5 +51,10 @@ 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/Relocation.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs
index 91991a6882ebd4..7cff8eef4b5ecb 100644
--- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs
+++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs
@@ -8,11 +8,16 @@ namespace ILCompiler.DependencyAnalysis
{
public enum RelocType
{
+ // PE base relocation types.
IMAGE_REL_BASED_ABSOLUTE = 0x00, // No relocation required
- IMAGE_REL_BASED_ADDR32NB = 0x02, // The 32-bit address without an image base (RVA)
IMAGE_REL_BASED_HIGHLOW = 0x03, // 32 bit address base
IMAGE_REL_BASED_THUMB_MOV32 = 0x07, // Thumb2: based MOVW/MOVT
IMAGE_REL_BASED_DIR64 = 0x0A, // 64 bit address base
+
+ // COFF relocation types
+ IMAGE_REL_BASED_ADDR32NB = 0x0B, // The 32-bit address without an image base (RVA)
+
+ // General relocation types
IMAGE_REL_BASED_REL32 = 0x10, // 32-bit relative address from byte following reloc
IMAGE_REL_BASED_THUMB_BRANCH24 = 0x13, // Thumb2: based B, BL
IMAGE_REL_BASED_THUMB_MOV32_PCREL = 0x14, // Thumb2: based MOVW/MOVT
@@ -24,6 +29,7 @@ public enum RelocType
// This is a special NGEN-specific relocation type
// for relative pointer (used to make NGen relocation
// section smaller)
+
IMAGE_REL_SECTION = 0x79, // 16 bit section index containing target
IMAGE_REL_BASED_ARM64_PAGEBASE_REL21 = 0x81, // ADRP
@@ -63,9 +69,12 @@ public enum RelocType
//
// Relocations for R2R image production
+ // None of these are "real" relocations that map to an object file's relocation.
+ // All must be emulated by the object writer.
//
IMAGE_REL_SYMBOL_SIZE = 0x1000, // The size of data in the image represented by the target symbol node
IMAGE_REL_FILE_ABSOLUTE = 0x1001, // 32 bit offset from beginning of image
+ IMAGE_REL_FILE_CHECKSUM_CALLBACK = 0x1002, // After the image has been emitted, call the IChecksumNode.EmitChecksum method on the target symbol to emit the checksum data.
}
public struct Relocation
@@ -560,7 +569,20 @@ public static int GetSize(RelocType relocType)
{
RelocType.IMAGE_REL_BASED_DIR64 => 8,
RelocType.IMAGE_REL_BASED_HIGHLOW => 4,
+ RelocType.IMAGE_REL_BASED_ADDR32NB => 4,
+ RelocType.IMAGE_REL_BASED_REL32 => 4,
RelocType.IMAGE_REL_BASED_RELPTR32 => 4,
+ RelocType.IMAGE_REL_FILE_ABSOLUTE => 4,
+ // The relocation itself aren't these sizes, but their values
+ // are immediates in instructions within
+ // a span of this many bytes.
+ RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21 => 4,
+ RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A => 4,
+ RelocType.IMAGE_REL_BASED_THUMB_MOV32 => 8,
+ RelocType.IMAGE_REL_BASED_THUMB_MOV32_PCREL => 8,
+ RelocType.IMAGE_REL_BASED_LOONGARCH64_PC => 16,
+ RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR => 16,
+ RelocType.IMAGE_REL_BASED_RISCV64_PC => 16,
_ => throw new NotSupportedException(),
};
}
diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_ARM64/ARM64Emitter.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_ARM64/ARM64Emitter.cs
index e7cbce99e5ea9c..d1bd8b917f1f82 100644
--- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_ARM64/ARM64Emitter.cs
+++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_ARM64/ARM64Emitter.cs
@@ -150,6 +150,14 @@ public void EmitJMP(ISymbolNode symbol)
{
if (symbol.RepresentsIndirectionCell)
{
+ Builder.RequireInitialPointerAlignment();
+
+ if (Builder.CountBytes % Builder.TargetPointerSize == 0)
+ {
+ // Emit a NOP instruction to align the 64-bit reloc below.
+ EmitNOP();
+ }
+
// ldr x12, [PC+0xc]
EmitLDR(Register.X12, 0xc);
@@ -157,7 +165,7 @@ public void EmitJMP(ISymbolNode symbol)
EmitLDR(Register.X12, Register.X12);
// br x12
- Builder.EmitUInt(0xd61f0180);
+ EmitJMP(Register.X12);
Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_DIR64);
}
@@ -215,6 +223,11 @@ public void EmitJNE(ISymbolNode symbol)
EmitJMP(symbol);
}
+ public void EmitNOP()
+ {
+ Builder.EmitUInt(0xD503201F);
+ }
+
private static bool InSignedByteRange(int i)
{
return i == (int)(sbyte)i;
diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64Emitter.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64Emitter.cs
index 47f8f471a172a2..f5df5286d44785 100644
--- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64Emitter.cs
+++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64Emitter.cs
@@ -107,6 +107,14 @@ public void EmitJMP(ISymbolNode symbol)
{
if (symbol.RepresentsIndirectionCell)
{
+ Builder.RequireInitialPointerAlignment();
+
+ if (Builder.CountBytes % Builder.TargetPointerSize != 0)
+ {
+ // Emit a NOP instruction to align the 64-bit reloc below.
+ EmitNOP();
+ }
+
// pcaddi R21, 0
EmitPCADDI(Register.R21);
@@ -150,5 +158,11 @@ public void EmitDBAR()
{
Builder.EmitUInt(0x38720000);
}
+
+ // nop
+ public void EmitNOP()
+ {
+ Builder.EmitUInt(0x03400000);
+ }
}
}
diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs
index 380163ed2fbf20..86f7528c7262aa 100644
--- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs
+++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs
@@ -103,6 +103,14 @@ public void EmitJMP(ISymbolNode symbol)
{
if (symbol.RepresentsIndirectionCell)
{
+ Builder.RequireInitialPointerAlignment();
+
+ if (Builder.CountBytes % Builder.TargetPointerSize != 0)
+ {
+ // Emit a NOP instruction to align the 64-bit reloc below.
+ EmitNOP();
+ }
+
// auipc x29, 0
EmitPC(Register.X29);
// ld x29,16(x29)
@@ -137,5 +145,10 @@ public void EmitJMPIfZero(Register regSrc, ISymbolNode symbol)
Builder.EmitUInt((uint)(0x00001063 | ((uint)regSrc << 15) | encodedOffset));
EmitJMP(symbol);
}
+
+ public void EmitNOP()
+ {
+ Builder.EmitUInt(0x00000013);
+ }
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CoffObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/CoffObjectWriter.cs
similarity index 79%
rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CoffObjectWriter.cs
rename to src/coreclr/tools/Common/Compiler/ObjectWriter/CoffObjectWriter.cs
index d6deb27606a4a1..74e999317eec9e 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CoffObjectWriter.cs
+++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/CoffObjectWriter.cs
@@ -15,7 +15,6 @@
using ILCompiler.DependencyAnalysis;
using ILCompiler.DependencyAnalysisFramework;
using Internal.TypeSystem;
-using Internal.TypeSystem.TypesDebugInfo;
using static ILCompiler.DependencyAnalysis.RelocType;
using static ILCompiler.ObjectWriter.CoffObjectWriter.CoffRelocationType;
@@ -45,12 +44,12 @@ namespace ILCompiler.ObjectWriter
/// the number of sections exceeds 2^16 the same file format is still used.
/// The linker treats the CodeView relocations symbolically.
///
- internal sealed class CoffObjectWriter : ObjectWriter
+ internal partial class CoffObjectWriter : ObjectWriter
{
- private sealed record SectionDefinition(CoffSectionHeader Header, Stream Stream, List Relocations, string ComdatName, string SymbolName);
+ protected sealed record SectionDefinition(CoffSectionHeader Header, Stream Stream, List Relocations, string ComdatName, string SymbolName);
- private readonly Machine _machine;
- private readonly List _sections = new();
+ protected readonly Machine _machine;
+ protected readonly List _sections = new();
// Symbol table
private readonly List _symbols = new();
@@ -58,34 +57,26 @@ private sealed record SectionDefinition(CoffSectionHeader Header, Stream Stream,
private readonly Dictionary _sectionNumberToComdatAuxRecord = new();
private readonly HashSet _referencedMethods = new();
- // Exception handling
- private SectionWriter _pdataSectionWriter;
-
- // Debugging
- private SectionWriter _debugTypesSectionWriter;
- private SectionWriter _debugSymbolSectionWriter;
- private CodeViewFileTableBuilder _debugFileTableBuilder;
- private CodeViewSymbolsBuilder _debugSymbolsBuilder;
- private CodeViewTypesBuilder _debugTypesBuilder;
-
- private static readonly ObjectNodeSection PDataSection = new ObjectNodeSection("pdata", SectionType.ReadOnly);
private static readonly ObjectNodeSection GfidsSection = new ObjectNodeSection(".gfids$y", SectionType.ReadOnly);
private static readonly ObjectNodeSection DebugTypesSection = new ObjectNodeSection(".debug$T", SectionType.ReadOnly);
private static readonly ObjectNodeSection DebugSymbolSection = new ObjectNodeSection(".debug$S", SectionType.ReadOnly);
- public CoffObjectWriter(NodeFactory factory, ObjectWritingOptions options)
- : base(factory, options)
+ public CoffObjectWriter(NodeFactory factory, ObjectWritingOptions options, OutputInfoBuilder outputInfoBuilder = null)
+ : base(factory, options, outputInfoBuilder)
{
_machine = factory.Target.Architecture switch
{
TargetArchitecture.X86 => Machine.I386,
TargetArchitecture.X64 => Machine.Amd64,
TargetArchitecture.ARM64 => Machine.Arm64,
+ TargetArchitecture.ARM => Machine.ArmThumb2,
+ TargetArchitecture.LoongArch64 => Machine.LoongArch64,
+ TargetArchitecture.RiscV64 => Machine.RiscV64,
_ => throw new NotSupportedException("Unsupported architecture")
};
}
- private protected override void CreateSection(ObjectNodeSection section, string comdatName, string symbolName, Stream sectionStream)
+ private protected override void CreateSection(ObjectNodeSection section, string comdatName, string symbolName, int sectionIndex, Stream sectionStream)
{
var sectionHeader = new CoffSectionHeader
{
@@ -110,7 +101,7 @@ private protected override void CreateSection(ObjectNodeSection section, string
}
};
- if (section == DebugTypesSection)
+ if (section == DebugTypesSection || section == ObjectNodeSection.DebugDirectorySection)
{
sectionHeader.SectionCharacteristics =
SectionCharacteristics.MemRead | SectionCharacteristics.ContainsInitializedData |
@@ -124,8 +115,8 @@ private protected override void CreateSection(ObjectNodeSection section, string
// We find the defining section of the COMDAT symbol. That one is marked
// as "ANY" selection type. All the other ones are marked as associated.
bool isPrimary = Equals(comdatName, symbolName);
- uint sectionIndex = (uint)_sections.Count + 1u;
- uint definingSectionIndex = isPrimary ? sectionIndex : ((CoffSymbol)_symbols[(int)_symbolNameToIndex[comdatName]]).SectionIndex;
+ uint coffSectionIndex = (uint)sectionIndex + 1u; // COFF section index is 1-based
+ uint definingSectionIndex = isPrimary ? coffSectionIndex : ((CoffSymbol)_symbols[(int)_symbolNameToIndex[comdatName]]).SectionIndex;
var auxRecord = new CoffSectionSymbol
{
@@ -143,7 +134,7 @@ private protected override void CreateSection(ObjectNodeSection section, string
{
Name = sectionHeader.Name,
Value = 0,
- SectionIndex = sectionIndex,
+ SectionIndex = coffSectionIndex,
StorageClass = CoffSymbolClass.IMAGE_SYM_CLASS_STATIC,
NumberOfAuxiliaryRecords = 1,
});
@@ -156,7 +147,7 @@ private protected override void CreateSection(ObjectNodeSection section, string
{
Name = symbolName,
Value = 0,
- SectionIndex = sectionIndex,
+ SectionIndex = coffSectionIndex,
StorageClass = isPrimary ? CoffSymbolClass.IMAGE_SYM_CLASS_EXTERNAL : CoffSymbolClass.IMAGE_SYM_CLASS_STATIC,
});
}
@@ -179,6 +170,17 @@ protected internal override void UpdateSectionAlignment(int sectionIndex, int al
}
}
+ protected static uint GetSectionAlignment(CoffSectionHeader header)
+ {
+ SectionCharacteristics alignmentFlag = (header.SectionCharacteristics & SectionCharacteristics.AlignMask);
+ if (alignmentFlag == 0)
+ {
+ return 1;
+ }
+ uint alignment = (uint)(1 << (((int)alignmentFlag >> 20) - 1));
+ return alignment;
+ }
+
protected internal override unsafe void EmitRelocation(
int sectionIndex,
long offset,
@@ -194,7 +196,7 @@ protected internal override unsafe void EmitRelocation(
if (addend != 0)
{
- fixed (byte *pData = data)
+ fixed (byte* pData = data)
{
long inlineValue = Relocation.ReadValue(relocType, (void*)pData);
Relocation.WriteValue(relocType, (void*)pData, inlineValue + addend);
@@ -370,105 +372,8 @@ private protected override void EmitRelocations(int sectionIndex, List 0 ? dataOffset : 0;
dataOffset += (uint)(section.Relocations.Count * CoffRelocation.Size);
+ // Record the section layout
+ _outputSectionLayout.Add(new OutputSection(section.Header.Name, section.Header.PointerToRawData, section.Header.VirtualAddress, section.Header.SizeOfRawData));
+
sectionIndex++;
}
@@ -530,7 +438,7 @@ private protected override void EmitObjectFile(string objectFilePath)
sectionIndex++;
}
- // Writer section content and relocations
+ // Write section content and relocations
foreach (SectionDefinition section in _sections)
{
if (!section.Header.SectionCharacteristics.HasFlag(SectionCharacteristics.ContainsUninitializedData))
@@ -569,110 +477,7 @@ private protected override void EmitObjectFile(string objectFilePath)
stringTable.Write(outputFileStream);
}
- private protected override void CreateEhSections()
- {
- // Create .pdata
- _pdataSectionWriter = GetOrCreateSection(PDataSection);
- }
-
- private protected override ITypesDebugInfoWriter CreateDebugInfoBuilder()
- {
- _debugFileTableBuilder = new CodeViewFileTableBuilder();
-
- _debugSymbolSectionWriter = GetOrCreateSection(DebugSymbolSection);
- _debugSymbolSectionWriter.EmitAlignment(4);
- _debugSymbolsBuilder = new CodeViewSymbolsBuilder(
- _nodeFactory.Target.Architecture,
- _debugSymbolSectionWriter);
-
- _debugTypesSectionWriter = GetOrCreateSection(DebugTypesSection);
- _debugTypesSectionWriter.EmitAlignment(4);
- _debugTypesBuilder = new CodeViewTypesBuilder(
- _nodeFactory.NameMangler, _nodeFactory.Target.PointerSize,
- _debugTypesSectionWriter);
- return _debugTypesBuilder;
- }
-
- private protected override void EmitDebugFunctionInfo(
- uint methodTypeIndex,
- string methodName,
- SymbolDefinition methodSymbol,
- INodeWithDebugInfo debugNode,
- bool hasSequencePoints)
- {
- DebugEHClauseInfo[] clauses = null;
- CodeViewSymbolsBuilder debugSymbolsBuilder;
-
- if (debugNode is INodeWithCodeInfo nodeWithCodeInfo)
- {
- clauses = nodeWithCodeInfo.DebugEHClauseInfos;
- }
-
- if (ShouldShareSymbol((ObjectNode)debugNode))
- {
- // If the method is emitted in COMDAT section then we need to create an
- // associated COMDAT section for the debugging symbols.
- var sectionWriter = GetOrCreateSection(DebugSymbolSection, methodName, null);
- debugSymbolsBuilder = new CodeViewSymbolsBuilder(_nodeFactory.Target.Architecture, sectionWriter);
- }
- else
- {
- debugSymbolsBuilder = _debugSymbolsBuilder;
- }
-
- debugSymbolsBuilder.EmitSubprogramInfo(
- methodName,
- methodSymbol.Size,
- methodTypeIndex,
- debugNode.GetDebugVars().Select(debugVar => (debugVar, GetVarTypeIndex(debugNode.IsStateMachineMoveNextMethod, debugVar))),
- clauses ?? Array.Empty());
-
- if (hasSequencePoints)
- {
- debugSymbolsBuilder.EmitLineInfo(
- _debugFileTableBuilder,
- methodName,
- methodSymbol.Size,
- debugNode.GetNativeSequencePoints());
- }
- }
-
- private protected override void EmitDebugThunkInfo(
- string methodName,
- SymbolDefinition methodSymbol,
- INodeWithDebugInfo debugNode)
- {
- if (!debugNode.GetNativeSequencePoints().Any())
- return;
-
- CodeViewSymbolsBuilder debugSymbolsBuilder;
-
- if (ShouldShareSymbol((ObjectNode)debugNode))
- {
- // If the method is emitted in COMDAT section then we need to create an
- // associated COMDAT section for the debugging symbols.
- var sectionWriter = GetOrCreateSection(DebugSymbolSection, methodName, null);
- debugSymbolsBuilder = new CodeViewSymbolsBuilder(_nodeFactory.Target.Architecture, sectionWriter);
- }
- else
- {
- debugSymbolsBuilder = _debugSymbolsBuilder;
- }
-
- debugSymbolsBuilder.EmitLineInfo(
- _debugFileTableBuilder,
- methodName,
- methodSymbol.Size,
- debugNode.GetNativeSequencePoints());
- }
-
- private protected override void EmitDebugSections(IDictionary definedSymbols)
- {
- _debugSymbolsBuilder.WriteUserDefinedTypes(_debugTypesBuilder.UserDefinedTypes);
- _debugFileTableBuilder.Write(_debugSymbolSectionWriter);
- }
-
- private struct CoffHeader
+ protected struct CoffHeader
{
public Machine Machine { get; set; }
public uint NumberOfSections { get; set; }
@@ -680,18 +485,23 @@ private struct CoffHeader
public uint PointerToSymbolTable { get; set; }
public uint NumberOfSymbols { get; set; }
public ushort SizeOfOptionalHeader { get; set; }
- public ushort Characteristics { get; set; }
+ public Characteristics Characteristics { get; set; }
// Maximum number of section that can be handled Microsoft linker
// before it bails out. We automatically switch to big object file
// layout after that.
- public bool IsBigObj => NumberOfSections > 65279;
+ public readonly bool IsBigObj => NumberOfSections > 65279;
- private static ReadOnlySpan BigObjMagic => new byte[]
- {
+ private static ReadOnlySpan BigObjMagic =>
+ [
0xC7, 0xA1, 0xBA, 0xD1, 0xEE, 0xBA, 0xA9, 0x4B,
0xAF, 0x20, 0xFA, 0xF6, 0x6A, 0xA4, 0xDC, 0xB8,
- };
+ ];
+
+ public static int TimeDateStampOffset(bool bigObj)
+ {
+ return bigObj ? 8 : 4;
+ }
private const int RegularSize =
sizeof(ushort) + // Machine
@@ -719,7 +529,7 @@ private struct CoffHeader
public int Size => IsBigObj ? BigObjSize : RegularSize;
- public void Write(FileStream stream)
+ public void Write(Stream stream)
{
if (!IsBigObj)
{
@@ -731,7 +541,7 @@ public void Write(FileStream stream)
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(8), PointerToSymbolTable);
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(12), NumberOfSymbols);
BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(16), SizeOfOptionalHeader);
- BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(18), Characteristics);
+ BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(18), (ushort)Characteristics);
stream.Write(buffer);
}
@@ -757,7 +567,7 @@ public void Write(FileStream stream)
}
}
- private sealed class CoffSectionHeader
+ protected sealed class CoffSectionHeader
{
public string Name { get; set; }
public uint VirtualSize { get; set; }
@@ -784,7 +594,7 @@ private sealed class CoffSectionHeader
sizeof(ushort) + // NumberOfLineNumbers
sizeof(uint); // SectionCharacteristics
- public void Write(FileStream stream, CoffStringTable stringTable)
+ public void Write(Stream stream, CoffStringTable stringTable)
{
Span buffer = stackalloc byte[Size];
@@ -886,7 +696,7 @@ internal enum CoffRelocationType
IMAGE_REL_ARM64_REL32 = 17,
}
- private sealed class CoffRelocation
+ protected sealed class CoffRelocation
{
public uint VirtualAddress { get; set; }
public uint SymbolTableIndex { get; set; }
@@ -897,7 +707,7 @@ private sealed class CoffRelocation
sizeof(uint) + // SymbolTableIndex
sizeof(ushort); // Type
- public void Write(FileStream stream)
+ public void Write(Stream stream)
{
Span buffer = stackalloc byte[Size];
@@ -1038,7 +848,7 @@ public override void Write(Stream stream, CoffStringTable stringTable, bool isBi
}
}
- private sealed class CoffStringTable : StringTableBuilder
+ protected sealed class CoffStringTable : StringTableBuilder
{
public new uint Size => (uint)(base.Size + 4);
@@ -1047,7 +857,7 @@ private sealed class CoffStringTable : StringTableBuilder
return base.GetStringOffset(text) + 4;
}
- public new void Write(FileStream stream)
+ public new void Write(Stream stream)
{
Span stringTableSize = stackalloc byte[4];
BinaryPrimitives.WriteUInt32LittleEndian(stringTableSize, Size);
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfHelper.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/Dwarf/DwarfHelper.cs
similarity index 100%
rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfHelper.cs
rename to src/coreclr/tools/Common/Compiler/ObjectWriter/Dwarf/DwarfHelper.cs
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Eabi/EabiAttributesBuilder.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/Eabi/EabiAttributesBuilder.cs
similarity index 100%
rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Eabi/EabiAttributesBuilder.cs
rename to src/coreclr/tools/Common/Compiler/ObjectWriter/Eabi/EabiAttributesBuilder.cs
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Eabi/EabiNative.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/Eabi/EabiNative.cs
similarity index 100%
rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Eabi/EabiNative.cs
rename to src/coreclr/tools/Common/Compiler/ObjectWriter/Eabi/EabiNative.cs
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfNative.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/ElfNative.cs
similarity index 100%
rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfNative.cs
rename to src/coreclr/tools/Common/Compiler/ObjectWriter/ElfNative.cs
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/ElfObjectWriter.cs
similarity index 87%
rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfObjectWriter.cs
rename to src/coreclr/tools/Common/Compiler/ObjectWriter/ElfObjectWriter.cs
index 640cced9dd6bf9..d2c3b99efbd77b 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfObjectWriter.cs
+++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/ElfObjectWriter.cs
@@ -32,7 +32,7 @@ namespace ILCompiler.ObjectWriter
/// to accomodate the section indexes that don't fit within the regular
/// section number field.
///
- internal sealed class ElfObjectWriter : UnixObjectWriter
+ internal sealed partial class ElfObjectWriter : UnixObjectWriter
{
private readonly bool _useInlineRelocationAddends;
private readonly ushort _machine;
@@ -45,9 +45,6 @@ internal sealed class ElfObjectWriter : UnixObjectWriter
// Symbol table
private readonly Dictionary _symbolNameToIndex = new();
- private Dictionary _armUnwindSections;
- private static readonly ObjectNodeSection ArmUnwindIndexSection = new ObjectNodeSection(".ARM.exidx", SectionType.UnwindData);
- private static readonly ObjectNodeSection ArmUnwindTableSection = new ObjectNodeSection(".ARM.extab", SectionType.ReadOnly);
private static readonly ObjectNodeSection ArmAttributesSection = new ObjectNodeSection(".ARM.attributes", SectionType.ReadOnly);
private static readonly ObjectNodeSection ArmTextThunkSection = new ObjectNodeSection(".text.thunks", SectionType.Executable);
private static readonly ObjectNodeSection CommentSection = new ObjectNodeSection(".comment", SectionType.ReadOnly);
@@ -72,12 +69,11 @@ public ElfObjectWriter(NodeFactory factory, ObjectWritingOptions options)
_symbols.Add(new ElfSymbol {});
}
- private protected override void CreateSection(ObjectNodeSection section, string comdatName, string symbolName, Stream sectionStream)
+ private protected override void CreateSection(ObjectNodeSection section, string comdatName, string symbolName, int sectionIndex, Stream sectionStream)
{
string sectionName =
section.Name == "rdata" ? ".rodata" :
(section.Name.StartsWith('_') || section.Name.StartsWith('.') ? section.Name : "." + section.Name);
- int sectionIndex = _sections.Count;
uint type = 0;
uint flags = 0;
ElfSectionDefinition groupSection = null;
@@ -178,7 +174,7 @@ private protected override void CreateSection(ObjectNodeSection section, string
});
}
- base.CreateSection(section, comdatName, symbolName ?? sectionName, sectionStream);
+ base.CreateSection(section, comdatName, symbolName ?? sectionName, sectionIndex, sectionStream);
}
protected internal override void UpdateSectionAlignment(int sectionIndex, int alignment)
@@ -604,128 +600,8 @@ private protected override void EmitSectionsAndLayout()
}
}
- private protected override void CreateEhSections()
+ private protected override void EmitObjectFile(Stream outputFileStream)
{
- // ARM creates the EHABI sections lazily in EmitUnwindInfo
- if (_machine is not EM_ARM)
- {
- base.CreateEhSections();
- }
- }
-
- private protected override void EmitUnwindInfo(
- SectionWriter sectionWriter,
- INodeWithCodeInfo nodeWithCodeInfo,
- string currentSymbolName)
- {
- if (_machine is not EM_ARM)
- {
- base.EmitUnwindInfo(sectionWriter, nodeWithCodeInfo, currentSymbolName);
- return;
- }
-
- if (nodeWithCodeInfo.FrameInfos is FrameInfo[] frameInfos &&
- nodeWithCodeInfo is ISymbolDefinitionNode)
- {
- SectionWriter exidxSectionWriter;
- SectionWriter extabSectionWriter;
-
- if (ShouldShareSymbol((ObjectNode)nodeWithCodeInfo))
- {
- exidxSectionWriter = GetOrCreateSection(ArmUnwindIndexSection, currentSymbolName, $"_unwind0{currentSymbolName}");
- extabSectionWriter = GetOrCreateSection(ArmUnwindTableSection, currentSymbolName, $"_extab0{currentSymbolName}");
- _sections[exidxSectionWriter.SectionIndex].LinkSection = _sections[sectionWriter.SectionIndex];
- }
- else
- {
- _armUnwindSections ??= new();
- if (_armUnwindSections.TryGetValue(sectionWriter.SectionIndex, out var unwindSections))
- {
- exidxSectionWriter = unwindSections.ExidxSectionWriter;
- extabSectionWriter = unwindSections.ExtabSectionWriter;
- }
- else
- {
- string sectionName = _sections[sectionWriter.SectionIndex].Name;
- exidxSectionWriter = GetOrCreateSection(new ObjectNodeSection($"{ArmUnwindIndexSection.Name}{sectionName}", ArmUnwindIndexSection.Type));
- extabSectionWriter = GetOrCreateSection(new ObjectNodeSection($"{ArmUnwindTableSection.Name}{sectionName}", ArmUnwindTableSection.Type));
- _sections[exidxSectionWriter.SectionIndex].LinkSection = _sections[sectionWriter.SectionIndex];
- _armUnwindSections.Add(sectionWriter.SectionIndex, (exidxSectionWriter, extabSectionWriter));
- }
- }
-
- long mainLsdaOffset = 0;
- Span unwindWord = stackalloc byte[4];
- for (int i = 0; i < frameInfos.Length; i++)
- {
- FrameInfo frameInfo = frameInfos[i];
- int start = frameInfo.StartOffset;
- int end = frameInfo.EndOffset;
- byte[] blob = frameInfo.BlobData;
-
- string framSymbolName = $"_fram{i}{currentSymbolName}";
- string extabSymbolName = $"_extab{i}{currentSymbolName}";
-
- sectionWriter.EmitSymbolDefinition(framSymbolName, start);
-
- // Emit the index info
- exidxSectionWriter.EmitSymbolReference(IMAGE_REL_ARM_PREL31, framSymbolName);
- exidxSectionWriter.EmitSymbolReference(IMAGE_REL_ARM_PREL31, extabSymbolName);
-
- Span armUnwindInfo = EabiUnwindConverter.ConvertCFIToEabi(blob);
- string personalitySymbolName;
-
- if (armUnwindInfo.Length <= 3)
- {
- personalitySymbolName = "__aeabi_unwind_cpp_pr0";
- unwindWord[3] = 0x80;
- unwindWord[2] = (byte)(armUnwindInfo.Length > 0 ? armUnwindInfo[0] : 0xB0);
- unwindWord[1] = (byte)(armUnwindInfo.Length > 1 ? armUnwindInfo[1] : 0xB0);
- unwindWord[0] = (byte)(armUnwindInfo.Length > 2 ? armUnwindInfo[2] : 0xB0);
- armUnwindInfo = Span.Empty;
- }
- else
- {
- Debug.Assert(armUnwindInfo.Length <= 1024);
- personalitySymbolName = "__aeabi_unwind_cpp_pr1";
- unwindWord[3] = 0x81;
- unwindWord[2] = (byte)(((armUnwindInfo.Length - 2) + 3) / 4);
- unwindWord[1] = armUnwindInfo[0];
- unwindWord[0] = armUnwindInfo[1];
- armUnwindInfo = armUnwindInfo.Slice(2);
- }
-
- extabSectionWriter.EmitAlignment(4);
- extabSectionWriter.EmitSymbolDefinition(extabSymbolName);
-
- // ARM EHABI requires emitting a dummy relocation to the personality routine
- // to tell the linker to preserve it.
- extabSectionWriter.EmitRelocation(0, unwindWord, IMAGE_REL_BASED_ABSOLUTE, personalitySymbolName, 0);
-
- // Emit the unwinding code. First word specifies the personality routine,
- // format and first few bytes of the unwind code. For longer unwind codes
- // the other words follow. They are padded with the "finish" instruction
- // (0xB0).
- extabSectionWriter.Write(unwindWord);
- while (armUnwindInfo.Length > 0)
- {
- unwindWord[3] = (byte)(armUnwindInfo.Length > 0 ? armUnwindInfo[0] : 0xB0);
- unwindWord[2] = (byte)(armUnwindInfo.Length > 1 ? armUnwindInfo[1] : 0xB0);
- unwindWord[1] = (byte)(armUnwindInfo.Length > 2 ? armUnwindInfo[2] : 0xB0);
- unwindWord[0] = (byte)(armUnwindInfo.Length > 3 ? armUnwindInfo[3] : 0xB0);
- extabSectionWriter.Write(unwindWord);
- armUnwindInfo = armUnwindInfo.Length > 3 ? armUnwindInfo.Slice(4) : Span.Empty;
- }
-
- // Emit our LSDA info directly into the exception table
- EmitLsda(nodeWithCodeInfo, frameInfos, i, extabSectionWriter, ref mainLsdaOffset);
- }
- }
- }
-
- private protected override void EmitObjectFile(string objectFilePath)
- {
- using var outputFileStream = new FileStream(objectFilePath, FileMode.Create);
switch (_machine)
{
case EM_386:
@@ -738,7 +614,7 @@ private protected override void EmitObjectFile(string objectFilePath)
}
}
- private void EmitObjectFile(FileStream outputFileStream)
+ private void EmitObjectFile(Stream outputFileStream)
where TSize : struct, IBinaryInteger
{
ElfStringTable _stringTable = new();
@@ -782,6 +658,9 @@ private void EmitObjectFile(FileStream outputFileStream)
section.SectionHeader.Offset = currentOffset;
section.SectionHeader.Size = (ulong)section.Stream.Length;
+ // Record the section layout
+ _outputSectionLayout.Add(new OutputSection(section.Name, currentOffset, currentOffset, (ulong)section.Stream.Length));
+
if (section.SectionHeader.Type != SHT_NOBITS)
{
currentOffset += (ulong)section.Stream.Length;
@@ -1046,7 +925,7 @@ public static int GetSize()
sizeof(ushort); // String table index
}
- public void Write(FileStream stream)
+ public void Write(Stream stream)
where TSize : struct, IBinaryInteger
{
Span buffer = stackalloc byte[GetSize()];
@@ -1104,7 +983,7 @@ public static int GetSize()
default(TSize).GetByteCount(); // Entry size
}
- public void Write(FileStream stream)
+ public void Write(Stream stream)
where TSize : struct, IBinaryInteger
{
Span buffer = stackalloc byte[GetSize()];
@@ -1140,7 +1019,7 @@ public static int GetSize()
return typeof(TSize) == typeof(uint) ? 16 : 24;
}
- public void Write(FileStream stream, ElfStringTable stringTable)
+ public void Write(Stream stream, ElfStringTable stringTable)
where TSize : struct, IBinaryInteger
{
Span buffer = stackalloc byte[GetSize()];
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachNative.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachNative.cs
similarity index 100%
rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachNative.cs
rename to src/coreclr/tools/Common/Compiler/ObjectWriter/MachNative.cs
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs
similarity index 79%
rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.cs
rename to src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs
index 4a1742c36b8f58..fec3165308290d 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.cs
+++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs
@@ -54,21 +54,13 @@ namespace ILCompiler.ObjectWriter
/// The Apple linker is extremely picky in which relocation types are allowed
/// inside the DWARF sections, both for debugging and exception handling.
///
- internal sealed class MachObjectWriter : UnixObjectWriter
+ internal sealed partial class MachObjectWriter : UnixObjectWriter
{
- private sealed record CompactUnwindCode(string PcStartSymbolName, uint PcLength, uint Code, string LsdaSymbolName = null, string PersonalitySymbolName = null);
-
private readonly TargetOS _targetOS;
private readonly uint _cpuType;
private readonly uint _cpuSubType;
private readonly List _sections = new();
- // Exception handling sections
- private MachSection _compactUnwindSection;
- private MemoryStream _compactUnwindStream;
- private readonly List _compactUnwindCodes = new();
- private readonly uint _compactUnwindDwarfCode;
-
// Symbol table
private readonly Dictionary _symbolNameToIndex = new();
private readonly List _symbolTable = new();
@@ -82,12 +74,16 @@ public MachObjectWriter(NodeFactory factory, ObjectWritingOptions options)
case TargetArchitecture.ARM64:
_cpuType = CPU_TYPE_ARM64;
_cpuSubType = CPU_SUBTYPE_ARM64_ALL;
+#if !READYTORUN
_compactUnwindDwarfCode = 0x3_00_00_00u;
+#endif
break;
case TargetArchitecture.X64:
_cpuType = CPU_TYPE_X86_64;
_cpuSubType = CPU_SUBTYPE_X86_64_ALL;
+#if !READYTORUN
_compactUnwindDwarfCode = 0x4_00_00_00u;
+#endif
break;
default:
throw new NotSupportedException("Unsupported architecture");
@@ -96,12 +92,14 @@ public MachObjectWriter(NodeFactory factory, ObjectWritingOptions options)
_targetOS = factory.Target.OperatingSystem;
}
+ private protected override bool UsesSubsectionsViaSymbols => true;
+
private protected override void EmitSectionsAndLayout()
{
// Layout sections. At this point we don't really care if the file offsets are correct
// but we need to compute the virtual addresses to populate the symbol table.
uint fileOffset = 0;
- LayoutSections(ref fileOffset, out _, out _);
+ LayoutSections(recordFinalLayout: false, ref fileOffset, out _, out _);
// Generate section base symbols. The section symbols are used for PC relative relocations
// to subtract the base of the section, and in DWARF to emit section relative relocations.
@@ -122,7 +120,7 @@ private protected override void EmitSectionsAndLayout()
}
}
- private void LayoutSections(ref uint fileOffset, out uint segmentFileSize, out ulong segmentSize)
+ private void LayoutSections(bool recordFinalLayout, ref uint fileOffset, out uint segmentFileSize, out ulong segmentSize)
{
ulong virtualAddress = 0;
byte sectionIndex = 1;
@@ -155,6 +153,11 @@ private void LayoutSections(ref uint fileOffset, out uint segmentFileSize, out u
sectionIndex++;
segmentSize = Math.Max(segmentSize, virtualAddress);
+
+ if (recordFinalLayout)
+ {
+ _outputSectionLayout.Add(new OutputSection($"{section.SectionName}{section.SegmentName}", section.VirtualAddress, section.FileOffset, (ulong)section.Stream.Length));
+ }
}
// ...and the relocation tables
@@ -165,9 +168,11 @@ private void LayoutSections(ref uint fileOffset, out uint segmentFileSize, out u
}
}
- private protected override void EmitObjectFile(string objectFilePath)
+ private protected override void EmitObjectFile(Stream outputFileStream)
{
+#if !READYTORUN
_sections.Add(_compactUnwindSection);
+#endif
// Segment + sections
uint loadCommandsCount = 1;
@@ -183,9 +188,7 @@ private protected override void EmitObjectFile(string objectFilePath)
// so re-run the layout and this time calculate with the correct file offsets.
uint fileOffset = (uint)MachHeader64.HeaderSize + loadCommandsSize;
uint segmentFileOffset = fileOffset;
- LayoutSections(ref fileOffset, out uint segmentFileSize, out ulong segmentSize);
-
- using var outputFileStream = new FileStream(objectFilePath, FileMode.Create);
+ LayoutSections(recordFinalLayout: true, ref fileOffset, out uint segmentFileSize, out ulong segmentSize);
MachHeader64 machHeader = new MachHeader64
{
@@ -301,7 +304,7 @@ private protected override void EmitObjectFile(string objectFilePath)
stringTable.Write(outputFileStream);
}
- private protected override void CreateSection(ObjectNodeSection section, string comdatName, string symbolName, Stream sectionStream)
+ private protected override void CreateSection(ObjectNodeSection section, string comdatName, string symbolName, int sectionIndex, Stream sectionStream)
{
string segmentName = section.Name switch
{
@@ -357,10 +360,9 @@ private protected override void CreateSection(ObjectNodeSection section, string
Flags = flags,
};
- int sectionIndex = _sections.Count;
_sections.Add(machSection);
- base.CreateSection(section, comdatName, symbolName ?? $"lsection{sectionIndex}", sectionStream);
+ base.CreateSection(section, comdatName, symbolName ?? $"lsection{sectionIndex}", sectionIndex, sectionStream);
}
protected internal override void UpdateSectionAlignment(int sectionIndex, int alignment)
@@ -419,7 +421,7 @@ protected internal override unsafe void EmitRelocation(
break;
case IMAGE_REL_BASED_RELPTR32:
- if (_cpuType == CPU_TYPE_ARM64 || sectionIndex == EhFrameSectionIndex)
+ if (_cpuType == CPU_TYPE_ARM64 || IsEhFrameSection(sectionIndex))
{
// On ARM64 we need to represent PC relative relocations as
// subtraction and the PC offset is baked into the addend.
@@ -535,6 +537,10 @@ private protected override void EmitRelocations(int sectionIndex, List false;
+#endif
+
private void EmitRelocationsX64(int sectionIndex, List relocationList)
{
ICollection sectionRelocations = _sections[sectionIndex].Relocations;
@@ -558,7 +564,7 @@ private void EmitRelocationsX64(int sectionIndex, List reloc
IsPCRelative = false,
});
}
- else if (symbolicRelocation.Type == IMAGE_REL_BASED_RELPTR32 && sectionIndex == EhFrameSectionIndex)
+ else if (symbolicRelocation.Type == IMAGE_REL_BASED_RELPTR32 && IsEhFrameSection(sectionIndex))
{
sectionRelocations.Add(
new MachRelocation
@@ -702,236 +708,10 @@ private void EmitRelocationsArm64(int sectionIndex, List rel
}
}
- private void EmitCompactUnwindTable(IDictionary definedSymbols)
- {
- _compactUnwindStream = new MemoryStream(32 * _compactUnwindCodes.Count);
- // Preset the size of the compact unwind section which is not generated yet
- _compactUnwindStream.SetLength(32 * _compactUnwindCodes.Count);
-
- _compactUnwindSection = new MachSection("__LD", "__compact_unwind", _compactUnwindStream)
- {
- Log2Alignment = 3,
- Flags = S_REGULAR | S_ATTR_DEBUG,
- };
-
- IList symbols = _symbolTable;
- Span tempBuffer = stackalloc byte[8];
- foreach (var cu in _compactUnwindCodes)
- {
- EmitCompactUnwindSymbol(cu.PcStartSymbolName);
- BinaryPrimitives.WriteUInt32LittleEndian(tempBuffer, cu.PcLength);
- BinaryPrimitives.WriteUInt32LittleEndian(tempBuffer.Slice(4), cu.Code);
- _compactUnwindStream.Write(tempBuffer);
- EmitCompactUnwindSymbol(cu.PersonalitySymbolName);
- EmitCompactUnwindSymbol(cu.LsdaSymbolName);
- }
-
- void EmitCompactUnwindSymbol(string symbolName)
- {
- Span tempBuffer = stackalloc byte[8];
- if (symbolName is not null)
- {
- SymbolDefinition symbol = definedSymbols[symbolName];
- MachSection section = _sections[symbol.SectionIndex];
- BinaryPrimitives.WriteUInt64LittleEndian(tempBuffer, section.VirtualAddress + (ulong)symbol.Value);
- _compactUnwindSection.Relocations.Add(
- new MachRelocation
- {
- Address = (int)_compactUnwindStream.Position,
- SymbolOrSectionIndex = (byte)(1 + symbol.SectionIndex), // 1-based
- Length = 8,
- RelocationType = ARM64_RELOC_UNSIGNED,
- IsExternal = false,
- IsPCRelative = false,
- }
- );
- }
- _compactUnwindStream.Write(tempBuffer);
- }
- }
+ partial void EmitCompactUnwindTable(IDictionary definedSymbols);
private protected override string ExternCName(string name) => "_" + name;
- private static uint GetArm64CompactUnwindCode(byte[] blobData)
- {
- if (blobData == null || blobData.Length == 0)
- {
- return UNWIND_ARM64_MODE_FRAMELESS;
- }
-
- Debug.Assert(blobData.Length % 8 == 0);
-
- short spReg = -1;
-
- int codeOffset = 0;
- short cfaRegister = spReg;
- int cfaOffset = 0;
- int spOffset = 0;
-
- const int REG_DWARF_X19 = 19;
- const int REG_DWARF_X30 = 30;
- const int REG_DWARF_FP = 29;
- const int REG_DWARF_D8 = 72;
- const int REG_DWARF_D15 = 79;
- const int REG_IDX_X19 = 0;
- const int REG_IDX_X28 = 9;
- const int REG_IDX_FP = 10;
- const int REG_IDX_LR = 11;
- const int REG_IDX_D8 = 12;
- const int REG_IDX_D15 = 19;
- Span registerOffset = stackalloc int[20];
-
- registerOffset.Fill(int.MinValue);
-
- // First process all the CFI codes to figure out the layout of X19-X28, FP, LR, and
- // D8-D15 on the stack.
- int offset = 0;
- while (offset < blobData.Length)
- {
- codeOffset = Math.Max(codeOffset, blobData[offset++]);
- CFI_OPCODE opcode = (CFI_OPCODE)blobData[offset++];
- short dwarfReg = BinaryPrimitives.ReadInt16LittleEndian(blobData.AsSpan(offset));
- offset += sizeof(short);
- int cfiOffset = BinaryPrimitives.ReadInt32LittleEndian(blobData.AsSpan(offset));
- offset += sizeof(int);
-
- switch (opcode)
- {
- case CFI_OPCODE.CFI_DEF_CFA_REGISTER:
- cfaRegister = dwarfReg;
-
- if (spOffset != 0)
- {
- for (int i = 0; i < registerOffset.Length; i++)
- if (registerOffset[i] != int.MinValue)
- registerOffset[i] -= spOffset;
-
- cfaOffset += spOffset;
- spOffset = 0;
- }
-
- break;
-
- case CFI_OPCODE.CFI_REL_OFFSET:
- Debug.Assert(cfaRegister == spReg);
- if (dwarfReg >= REG_DWARF_X19 && dwarfReg <= REG_DWARF_X30) // X19 - X28, FP, LR
- {
- registerOffset[dwarfReg - REG_DWARF_X19 + REG_IDX_X19] = cfiOffset;
- }
- else if (dwarfReg >= REG_DWARF_D8 && dwarfReg <= REG_DWARF_D15) // D8 - D15
- {
- registerOffset[dwarfReg - REG_DWARF_D8 + REG_IDX_D8] = cfiOffset;
- }
- else
- {
- // We cannot represent this register in the compact unwinding format,
- // fallback to DWARF immediately.
- return UNWIND_ARM64_MODE_DWARF;
- }
- break;
-
- case CFI_OPCODE.CFI_ADJUST_CFA_OFFSET:
- if (cfaRegister != spReg)
- {
- cfaOffset += cfiOffset;
- }
- else
- {
- spOffset += cfiOffset;
-
- for (int i = 0; i < registerOffset.Length; i++)
- if (registerOffset[i] != int.MinValue)
- registerOffset[i] += cfiOffset;
- }
- break;
- }
- }
-
- uint unwindCode;
- int nextOffset;
-
- if (cfaRegister == REG_DWARF_FP &&
- cfaOffset == 16 &&
- registerOffset[REG_IDX_FP] == -16 &&
- registerOffset[REG_IDX_LR] == -8)
- {
- // Frame format - FP/LR are saved on the top. SP is restored to FP+16
- unwindCode = UNWIND_ARM64_MODE_FRAME;
- nextOffset = -24;
- }
- else if (cfaRegister == -1 && spOffset <= 65520 &&
- registerOffset[REG_IDX_FP] == int.MinValue && registerOffset[REG_IDX_LR] == int.MinValue)
- {
- // Frameless format - FP/LR are not saved, SP must fit within the representable range
- uint encodedSpOffset = (uint)(spOffset / 16) << 12;
- unwindCode = UNWIND_ARM64_MODE_FRAMELESS | encodedSpOffset;
- nextOffset = spOffset - 8;
- }
- else
- {
- return UNWIND_ARM64_MODE_DWARF;
- }
-
- // Check that the integer register pairs are in the right order and mark
- // a flag for each successive pair that is present.
- for (int i = REG_IDX_X19; i < REG_IDX_X28; i += 2)
- {
- if (registerOffset[i] == int.MinValue)
- {
- if (registerOffset[i + 1] != int.MinValue)
- return UNWIND_ARM64_MODE_DWARF;
- }
- else if (registerOffset[i] == nextOffset)
- {
- if (registerOffset[i + 1] != nextOffset - 8)
- return UNWIND_ARM64_MODE_DWARF;
- nextOffset -= 16;
- unwindCode |= UNWIND_ARM64_FRAME_X19_X20_PAIR << (i >> 1);
- }
- }
-
- // Check that the floating point register pairs are in the right order and mark
- // a flag for each successive pair that is present.
- for (int i = REG_IDX_D8; i < REG_IDX_D15; i += 2)
- {
- if (registerOffset[i] == int.MinValue)
- {
- if (registerOffset[i + 1] != int.MinValue)
- return UNWIND_ARM64_MODE_DWARF;
- }
- else if (registerOffset[i] == nextOffset)
- {
- if (registerOffset[i + 1] != nextOffset - 8)
- return UNWIND_ARM64_MODE_DWARF;
- nextOffset -= 16;
- unwindCode |= UNWIND_ARM64_FRAME_D8_D9_PAIR << (i >> 1);
- }
- }
-
- return unwindCode;
- }
-
- private protected override bool EmitCompactUnwinding(string startSymbolName, ulong length, string lsdaSymbolName, byte[] blob)
- {
- uint encoding = _compactUnwindDwarfCode;
-
- if (_cpuType == CPU_TYPE_ARM64)
- {
- encoding = GetArm64CompactUnwindCode(blob);
- }
-
- _compactUnwindCodes.Add(new CompactUnwindCode(
- PcStartSymbolName: startSymbolName,
- PcLength: (uint)length,
- Code: encoding | (encoding != _compactUnwindDwarfCode && lsdaSymbolName is not null ? 0x40000000u : 0), // UNWIND_HAS_LSDA
- LsdaSymbolName: encoding != _compactUnwindDwarfCode ? lsdaSymbolName : null
- ));
-
- return encoding != _compactUnwindDwarfCode;
- }
-
- private protected override bool UseFrameNames => true;
-
private static bool IsSectionSymbolName(string symbolName) => symbolName.StartsWith('l');
private struct MachHeader64
@@ -946,7 +726,7 @@ private struct MachHeader64
public static int HeaderSize => 32;
- public void Write(FileStream stream)
+ public void Write(Stream stream)
{
Span buffer = stackalloc byte[HeaderSize];
@@ -977,7 +757,7 @@ public struct MachSegment64Header
public static int HeaderSize => 72;
- public void Write(FileStream stream)
+ public void Write(Stream stream)
{
Span buffer = stackalloc byte[HeaderSize];
@@ -1036,7 +816,7 @@ public MachSection(string segmentName, string sectionName, Stream stream)
this.relocationCollection = null;
}
- public void WriteHeader(FileStream stream)
+ public void WriteHeader(Stream stream)
{
Span buffer = stackalloc byte[HeaderSize];
@@ -1066,7 +846,7 @@ private sealed class MachRelocation
public byte Length { get; init; }
public byte RelocationType { get; init; }
- public void Write(FileStream stream)
+ public void Write(Stream stream)
{
Span relocationBuffer = stackalloc byte[8];
uint info = SymbolOrSectionIndex;
@@ -1088,7 +868,7 @@ private sealed class MachSymbol
public ushort Descriptor { get; init; }
public ulong Value { get; init; }
- public void Write(FileStream stream, MachStringTable stringTable)
+ public void Write(Stream stream, MachStringTable stringTable)
{
Span buffer = stackalloc byte[16];
uint nameIndex = stringTable.GetStringOffset(Name);
@@ -1112,7 +892,7 @@ private sealed class MachSymbolTableCommandHeader
public static int HeaderSize => 24;
- public void Write(FileStream stream)
+ public void Write(Stream stream)
{
Span buffer = stackalloc byte[HeaderSize];
@@ -1150,7 +930,7 @@ private sealed class MachDynamicLinkEditSymbolTable
public static int HeaderSize => 80;
- public void Write(FileStream stream)
+ public void Write(Stream stream)
{
Span buffer = stackalloc byte[HeaderSize];
@@ -1187,7 +967,7 @@ private struct MachBuildVersionCommandHeader
public static int HeaderSize => 24;
- public void Write(FileStream stream)
+ public void Write(Stream stream)
{
Span buffer = stackalloc byte[HeaderSize];
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs
similarity index 67%
rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs
rename to src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs
index 6db2bc20b01b25..91b51c384919a9 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs
+++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs
@@ -7,25 +7,28 @@
using System.Diagnostics;
using System.IO;
using System.Linq;
+using System.Xml.Linq;
using ILCompiler.DependencyAnalysis;
using ILCompiler.DependencyAnalysisFramework;
+using Internal.Text;
using Internal.TypeSystem;
-using Internal.TypeSystem.TypesDebugInfo;
-using ObjectData = ILCompiler.DependencyAnalysis.ObjectNode.ObjectData;
+using static ILCompiler.DependencyAnalysis.ObjectNode;
using static ILCompiler.DependencyAnalysis.RelocType;
+using ObjectData = ILCompiler.DependencyAnalysis.ObjectNode.ObjectData;
namespace ILCompiler.ObjectWriter
{
- public abstract class ObjectWriter
+ public abstract partial class ObjectWriter
{
private protected sealed record SymbolDefinition(int SectionIndex, long Value, int Size = 0, bool Global = false);
- private protected sealed record SymbolicRelocation(long Offset, RelocType Type, string SymbolName, long Addend = 0);
- private protected sealed record BlockToRelocate(int SectionIndex, long Offset, byte[] Data, Relocation[] Relocations);
+ protected sealed record SymbolicRelocation(long Offset, RelocType Type, string SymbolName, long Addend = 0);
+ private sealed record BlockToRelocate(int SectionIndex, long Offset, byte[] Data, Relocation[] Relocations);
+ private protected sealed record ChecksumsToCalculate(int SectionIndex, long Offset, Relocation[] ChecksumRelocations);
private protected readonly NodeFactory _nodeFactory;
private protected readonly ObjectWritingOptions _options;
+ private protected readonly OutputInfoBuilder _outputInfoBuilder;
private readonly bool _isSingleFileCompilation;
- private readonly bool _usesSubsectionsViaSymbols;
private readonly Dictionary _mangledNameMap = new();
@@ -35,19 +38,17 @@ private protected sealed record BlockToRelocate(int SectionIndex, long Offset, b
private readonly Dictionary _sectionNameToSectionIndex = new(StringComparer.Ordinal);
private readonly List _sectionIndexToData = new();
private readonly List> _sectionIndexToRelocations = new();
+ private protected readonly List _outputSectionLayout = [];
// Symbol table
private readonly Dictionary _definedSymbols = new(StringComparer.Ordinal);
- // Debugging
- private UserDefinedTypeDescriptor _userDefinedTypeDescriptor;
-
- private protected ObjectWriter(NodeFactory factory, ObjectWritingOptions options)
+ private protected ObjectWriter(NodeFactory factory, ObjectWritingOptions options, OutputInfoBuilder outputInfoBuilder = null)
{
_nodeFactory = factory;
_options = options;
+ _outputInfoBuilder = outputInfoBuilder;
_isSingleFileCompilation = _nodeFactory.CompilationModuleGroup.IsSingleFileCompilation;
- _usesSubsectionsViaSymbols = factory.Target.IsApplePlatform;
// Padding byte for code sections (NOP for x86/x64)
_insPaddingByte = factory.Target.Architecture switch
@@ -57,8 +58,9 @@ private protected ObjectWriter(NodeFactory factory, ObjectWritingOptions options
_ => 0
};
}
+ private protected virtual bool UsesSubsectionsViaSymbols => false;
- private protected abstract void CreateSection(ObjectNodeSection section, string comdatName, string symbolName, Stream sectionStream);
+ private protected abstract void CreateSection(ObjectNodeSection section, string comdatName, string symbolName, int sectionIndex, Stream sectionStream);
protected internal abstract void UpdateSectionAlignment(int sectionIndex, int alignment);
@@ -85,7 +87,7 @@ private protected SectionWriter GetOrCreateSection(ObjectNodeSection section, st
{
sectionData = new SectionData(section.Type == SectionType.Executable ? _insPaddingByte : (byte)0);
sectionIndex = _sectionIndexToData.Count;
- CreateSection(section, comdatName, symbolName, sectionData.GetReadStream());
+ CreateSection(section, comdatName, symbolName, sectionIndex, sectionData.GetReadStream());
_sectionIndexToData.Add(sectionData);
_sectionIndexToRelocations.Add(new());
if (comdatName is null)
@@ -106,7 +108,7 @@ private protected SectionWriter GetOrCreateSection(ObjectNodeSection section, st
private protected bool ShouldShareSymbol(ObjectNode node)
{
- if (_usesSubsectionsViaSymbols)
+ if (UsesSubsectionsViaSymbols)
return false;
return ShouldShareSymbol(node, node.GetSection(_nodeFactory));
@@ -114,7 +116,7 @@ private protected bool ShouldShareSymbol(ObjectNode node)
private protected bool ShouldShareSymbol(ObjectNode node, ObjectNodeSection section)
{
- if (_usesSubsectionsViaSymbols)
+ if (UsesSubsectionsViaSymbols)
return false;
// Foldable sections are always COMDATs
@@ -127,8 +129,7 @@ private protected bool ShouldShareSymbol(ObjectNode node, ObjectNodeSection sect
if (node is not ISymbolNode)
return false;
- // These intentionally clash with one another, but are merged with linker directives so should not be COMDAT folded
- if (node is ModulesSectionNode)
+ if (node is IUniqueSymbolNode)
return false;
return true;
@@ -142,7 +143,7 @@ private unsafe void EmitOrResolveRelocation(
string symbolName,
long addend)
{
- if (!_usesSubsectionsViaSymbols &&
+ if (!UsesSubsectionsViaSymbols &&
relocType is IMAGE_REL_BASED_REL32 or IMAGE_REL_BASED_RELPTR32 or IMAGE_REL_BASED_ARM64_BRANCH26
or IMAGE_REL_BASED_THUMB_BRANCH24 or IMAGE_REL_BASED_THUMB_MOV32_PCREL &&
_definedSymbols.TryGetValue(symbolName, out SymbolDefinition definedSymbol) &&
@@ -156,8 +157,15 @@ or IMAGE_REL_BASED_THUMB_BRANCH24 or IMAGE_REL_BASED_THUMB_MOV32_PCREL &&
// and R_ARM_THM_MOVW_PREL_NC relocations using the formula ((S + A) | T) – P.
// The thumb bit is thus supposed to be only added once.
// For R_ARM_THM_JUMP24 the thumb bit cannot be encoded, so mask it out.
+ //
+ // R2R doesn't use add the thumb bit to the symbol value, so we don't need to do this here.
+#if !READYTORUN
long maskThumbBitOut = relocType is IMAGE_REL_BASED_THUMB_BRANCH24 or IMAGE_REL_BASED_THUMB_MOV32_PCREL ? 1 : 0;
long maskThumbBitIn = relocType is IMAGE_REL_BASED_THUMB_MOV32_PCREL ? 1 : 0;
+#else
+ long maskThumbBitOut = 0;
+ long maskThumbBitIn = 0;
+#endif
long adjustedAddend = addend;
adjustedAddend -= relocType switch
@@ -183,6 +191,14 @@ or IMAGE_REL_BASED_THUMB_BRANCH24 or IMAGE_REL_BASED_THUMB_MOV32_PCREL &&
}
}
}
+ else if (relocType is IMAGE_REL_SYMBOL_SIZE &&
+ _definedSymbols.TryGetValue(symbolName, out definedSymbol))
+ {
+ fixed (byte* pData = data)
+ {
+ Relocation.WriteValue(relocType, (void*)pData, definedSymbol.Size);
+ }
+ }
else
{
EmitRelocation(sectionIndex, offset, data, relocType, symbolName, addend);
@@ -266,43 +282,13 @@ private protected string GetMangledName(ISymbolNode symbolNode)
return symbolName;
}
- private protected abstract void EmitUnwindInfo(
- SectionWriter sectionWriter,
- INodeWithCodeInfo nodeWithCodeInfo,
- string currentSymbolName);
-
- private protected uint GetVarTypeIndex(bool isStateMachineMoveNextMethod, DebugVarInfoMetadata debugVar)
- {
- uint typeIndex;
- try
- {
- if (isStateMachineMoveNextMethod && debugVar.DebugVarInfo.VarNumber == 0)
- {
- typeIndex = _userDefinedTypeDescriptor.GetStateMachineThisVariableTypeIndex(debugVar.Type);
- // FIXME
- // varName = "locals";
- }
- else
- {
- typeIndex = _userDefinedTypeDescriptor.GetVariableTypeIndex(debugVar.Type);
- }
- }
- catch (TypeSystemException)
- {
- typeIndex = 0; // T_NOTYPE
- // FIXME
- // Debug.Fail();
- }
- return typeIndex;
- }
-
private protected virtual void EmitSectionsAndLayout()
{
}
- private protected abstract void EmitObjectFile(string objectFilePath);
+ private protected abstract void EmitObjectFile(Stream outputFileStream);
- private protected abstract void CreateEhSections();
+ partial void EmitDebugInfo(IReadOnlyCollection nodes, Logger logger);
private SortedSet GetUndefinedSymbols()
{
@@ -318,25 +304,7 @@ private SortedSet GetUndefinedSymbols()
return undefinedSymbolSet;
}
- private protected abstract ITypesDebugInfoWriter CreateDebugInfoBuilder();
-
- private protected abstract void EmitDebugFunctionInfo(
- uint methodTypeIndex,
- string methodName,
- SymbolDefinition methodSymbol,
- INodeWithDebugInfo debugNode,
- bool hasSequencePoints);
-
- private protected virtual void EmitDebugThunkInfo(
- string methodName,
- SymbolDefinition methodSymbol,
- INodeWithDebugInfo debugNode)
- {
- }
-
- private protected abstract void EmitDebugSections(IDictionary definedSymbols);
-
- private void EmitObject(string objectFilePath, IReadOnlyCollection nodes, IObjectDumper dumper, Logger logger)
+ public void EmitObject(Stream outputFileStream, IReadOnlyCollection nodes, IObjectDumper dumper, Logger logger)
{
// Pre-create some of the sections
GetOrCreateSection(ObjectNodeSection.TextSection);
@@ -350,12 +318,9 @@ private void EmitObject(string objectFilePath, IReadOnlyCollection blocksToRelocate = new();
+ List symbolRangeNodes = [];
+ List blocksToRelocate = [];
+ List checksumRelocations = [];
foreach (DependencyNode depNode in nodes)
{
- ObjectNode node = depNode as ObjectNode;
- if (node is null)
+ if (depNode is ISymbolRangeNode symbolRange)
+ {
+ symbolRangeNodes.Add(symbolRange);
+ continue;
+ }
+
+ if (depNode is not ObjectNode node)
continue;
if (logger.IsVerbose)
@@ -385,12 +357,15 @@ private void EmitObject(string objectFilePath, IReadOnlyCollection checksumRelocationsBuilder = default;
foreach (Relocation reloc in blockToRelocate.Relocations)
{
+#if READYTORUN
+ ISymbolNode relocTarget = reloc.Target;
+#else
ISymbolNode relocTarget = _nodeFactory.ObjectInterner.GetDeduplicatedSymbol(_nodeFactory, reloc.Target);
+#endif
+
+ if (reloc.RelocType == RelocType.IMAGE_REL_FILE_CHECKSUM_CALLBACK)
+ {
+ // Checksum relocations don't get emitted into the image.
+ // We manually proces them after we do all other object emission.
+ checksumRelocationsBuilder.Add(reloc);
+ continue;
+ }
string relocSymbolName = GetMangledName(relocTarget);
@@ -490,15 +550,12 @@ private void EmitObject(string objectFilePath, IReadOnlyCollection relocationList in _sectionIndexToRelocations)
+ {
+ EmitRelocations(relocSectionIndex, relocationList);
+ relocSectionIndex++;
+ }
- // Ensure any allocated MethodTables have debug info
- if (node is ConstructedEETypeNode methodTable)
- {
- _userDefinedTypeDescriptor.GetTypeIndex(methodTable.Type, needsCompleteType: true);
- }
+ EmitObjectFile(outputFileStream);
- if (node is INodeWithDebugInfo debugNode and ISymbolDefinitionNode symbolDefinitionNode)
- {
- string methodName = GetMangledName(symbolDefinitionNode);
- if (_definedSymbols.TryGetValue(methodName, out var methodSymbol))
- {
- if (node is IMethodNode methodNode)
- {
- bool hasSequencePoints = debugNode.GetNativeSequencePoints().Any();
- uint methodTypeIndex = hasSequencePoints ? _userDefinedTypeDescriptor.GetMethodFunctionIdTypeIndex(methodNode.Method) : 0;
- EmitDebugFunctionInfo(methodTypeIndex, methodName, methodSymbol, debugNode, hasSequencePoints);
- }
- else
- {
- EmitDebugThunkInfo(methodName, methodSymbol, debugNode);
- }
- }
- }
- }
+ if (checksumRelocations.Count > 0)
+ {
+ EmitChecksums(outputFileStream, checksumRelocations);
+ }
- // Ensure all fields associated with generated static bases have debug info
- foreach (MetadataType typeWithStaticBase in _nodeFactory.MetadataManager.GetTypesWithStaticBases())
+ if (_outputInfoBuilder is not null)
+ {
+ foreach (var outputSection in _outputSectionLayout)
{
- _userDefinedTypeDescriptor.GetTypeIndex(typeWithStaticBase, needsCompleteType: true);
+ _outputInfoBuilder.AddSection(outputSection);
}
+ }
+ }
- EmitDebugSections(_definedSymbols);
+ private static string GetNodeTypeName(Type nodeType)
+ {
+ string name = nodeType.ToString();
+ int firstGeneric = name.IndexOf('[');
+
+ if (firstGeneric < 0)
+ {
+ firstGeneric = name.Length;
}
- EmitSymbolTable(_definedSymbols, GetUndefinedSymbols());
+ int lastDot = name.LastIndexOf('.', firstGeneric - 1, firstGeneric);
- int relocSectionIndex = 0;
- foreach (List relocationList in _sectionIndexToRelocations)
+ if (lastDot > 0)
{
- EmitRelocations(relocSectionIndex, relocationList);
- relocSectionIndex++;
+ name = name.Substring(lastDot + 1);
}
- EmitObjectFile(objectFilePath);
+ return name;
}
+ private void EmitChecksums(Stream outputFileStream, List checksumRelocations)
+ {
+ MemoryStream originalOutputStream = new();
+ outputFileStream.Seek(0, SeekOrigin.Begin);
+ outputFileStream.CopyTo(originalOutputStream);
+ byte[] originalOutput = originalOutputStream.ToArray();
+ EmitChecksumsForObject(outputFileStream, checksumRelocations, originalOutput);
+ }
+
+ private protected virtual void EmitChecksumsForObject(Stream outputFileStream, List checksumRelocations, ReadOnlySpan originalOutput)
+ {
+ foreach (var block in checksumRelocations)
+ {
+ foreach (var reloc in block.ChecksumRelocations)
+ {
+ IChecksumNode checksum = (IChecksumNode)reloc.Target;
+
+ byte[] checksumValue = new byte[checksum.ChecksumSize];
+ checksum.EmitChecksum(originalOutput, checksumValue);
+
+ var checksumOffset = (long)_outputSectionLayout[block.SectionIndex].FilePosition + block.Offset + reloc.Offset;
+ outputFileStream.Seek(checksumOffset, SeekOrigin.Begin);
+ outputFileStream.Write(checksumValue);
+ }
+ }
+ }
+
+ partial void HandleControlFlowForRelocation(ISymbolNode relocTarget, string relocSymbolName);
+
+ partial void PrepareForUnwindInfo();
+
+ partial void EmitUnwindInfoForNode(ObjectNode node, string currentSymbolName, SectionWriter sectionWriter);
+
public static void EmitObject(string objectFilePath, IReadOnlyCollection nodes, NodeFactory factory, ObjectWritingOptions options, IObjectDumper dumper, Logger logger)
{
var stopwatch = Stopwatch.StartNew();
@@ -578,7 +652,9 @@ public static void EmitObject(string objectFilePath, IReadOnlyCollection
/// Base class for symbols and nodes in the output file implements common logic
/// for section / offset ordering.
///
- public class OutputItem
+ public abstract class OutputItem
{
public class Comparer : IComparer
{
- public readonly static Comparer Instance = new Comparer();
+ public static readonly Comparer Instance = new Comparer();
public int Compare([AllowNull] OutputItem x, [AllowNull] OutputItem y)
{
@@ -44,14 +42,14 @@ public int Compare([AllowNull] OutputItem x, [AllowNull] OutputItem y)
///
/// Offset relative to section beginning
///
- public readonly int Offset;
+ public readonly ulong Offset;
///
/// Item name
///
public readonly string Name;
- public OutputItem(int sectionIndex, int offset, string name)
+ public OutputItem(int sectionIndex, ulong offset, string name)
{
SectionIndex = sectionIndex;
Offset = offset;
@@ -62,7 +60,7 @@ public OutputItem(int sectionIndex, int offset, string name)
///
/// This class represents a single node (contiguous block of data) in the output R2R PE file.
///
- public class OutputNode : OutputItem
+ public sealed class OutputNode : OutputItem
{
///
/// Node length (number of bytes). This doesn't include any external alignment
@@ -75,7 +73,7 @@ public class OutputNode : OutputItem
///
public int Relocations { get; private set; }
- public OutputNode(int sectionIndex, int offset, int length, string name)
+ public OutputNode(int sectionIndex, ulong offset, int length, string name)
: base(sectionIndex, offset, name)
{
Length = length;
@@ -93,41 +91,44 @@ public void AddRelocation()
/// node beginnings (most nodes have a "start symbol" representing the beginning
/// of the node).
///
- public class OutputSymbol : OutputItem
+ public sealed class OutputSymbol : OutputItem
{
- public OutputSymbol(int sectionIndex, int offset, string name)
+ public OutputSymbol(int sectionIndex, ulong offset, string name)
: base(sectionIndex, offset, name)
{
}
}
+ public sealed class OutputSection
+ {
+ public OutputSection(string name, ulong virtualAddress, ulong filePosition, ulong length)
+ {
+ Name = name;
+ VirtualAddress = virtualAddress;
+ FilePosition = filePosition;
+ Length = length;
+ }
+
+ public string Name { get; }
+ public ulong VirtualAddress { get; }
+ public ulong FilePosition { get; }
+ public ulong Length { get; }
+ }
+
///
/// Common class used to collect information to use when emitting map files and symbol files.
///
public class OutputInfoBuilder
{
- private readonly List _inputModules;
- private readonly List _nodes;
- private readonly List _symbols;
- private readonly List _sections;
-
- private readonly Dictionary _nodeSymbolMap;
- private readonly Dictionary _methodSymbolMap;
+ private readonly List _inputModules = [];
+ private readonly List _nodes = [];
+ private readonly List _symbols = [];
+ private readonly List _sections = [];
- private readonly Dictionary _relocCounts;
+ private readonly Dictionary _nodeSymbolMap = [];
+ private readonly Dictionary _methodSymbolMap = [];
- public OutputInfoBuilder()
- {
- _inputModules = new List();
- _nodes = new List();
- _symbols = new List();
- _sections = new List();
-
- _nodeSymbolMap = new Dictionary();
- _methodSymbolMap = new Dictionary();
-
- _relocCounts = new Dictionary();
- }
+ private readonly Dictionary _relocCounts = [];
public void AddInputModule(EcmaModule module)
{
@@ -152,12 +153,12 @@ public void AddSymbol(OutputSymbol symbol)
_symbols.Add(symbol);
}
- public void AddSection(Section section)
+ public void AddSection(OutputSection section)
{
_sections.Add(section);
}
- public void AddMethod(MethodWithGCInfo method, ISymbolDefinitionNode symbol)
+ public void AddMethod(IMethodNode method, ISymbolDefinitionNode symbol)
{
_methodSymbolMap.Add(symbol, method);
}
@@ -181,11 +182,10 @@ public bool FindSymbol(OutputItem item, out int index)
public IEnumerable EnumerateMethods()
{
- DebugNameFormatter nameFormatter = new DebugNameFormatter();
TypeNameFormatter typeNameFormatter = TypeString.Instance;
- foreach (KeyValuePair symbolMethodPair in _methodSymbolMap)
+ foreach (KeyValuePair symbolMethodPair in _methodSymbolMap)
{
- MethodInfo methodInfo = new MethodInfo();
+ MethodInfo methodInfo = default;
if (symbolMethodPair.Value.Method.GetTypicalMethodDefinition() is EcmaMethod ecmaMethod)
{
methodInfo.MethodToken = (uint)MetadataTokens.GetToken(ecmaMethod.Handle);
@@ -193,8 +193,8 @@ public IEnumerable EnumerateMethods()
}
methodInfo.Name = FormatMethodName(symbolMethodPair.Value.Method, typeNameFormatter);
OutputNode node = _nodeSymbolMap[symbolMethodPair.Key];
- Section section = _sections[node.SectionIndex];
- methodInfo.HotRVA = (uint)(section.RVAWhenPlaced + node.Offset);
+ OutputSection section = _sections[node.SectionIndex];
+ methodInfo.HotRVA = (uint)(section.VirtualAddress + (ulong)node.Offset);
methodInfo.HotLength = (uint)node.Length;
methodInfo.ColdRVA = 0;
methodInfo.ColdLength = 0;
@@ -212,18 +212,18 @@ public IEnumerable EnumerateInputAssemblies()
}
}
- private string FormatMethodName(MethodDesc method, TypeNameFormatter typeNameFormatter)
+ private static string FormatMethodName(MethodDesc method, TypeNameFormatter typeNameFormatter)
{
StringBuilder output = new StringBuilder();
if (!method.Signature.ReturnType.IsVoid)
{
output.Append(typeNameFormatter.FormatName(method.Signature.ReturnType));
- output.Append(" ");
+ output.Append(' ');
}
output.Append(typeNameFormatter.FormatName(method.OwningType));
output.Append("::");
output.Append(method.GetName());
- output.Append("(");
+ output.Append('(');
for (int paramIndex = 0; paramIndex < method.Signature.Length; paramIndex++)
{
if (paramIndex != 0)
@@ -232,16 +232,16 @@ private string FormatMethodName(MethodDesc method, TypeNameFormatter typeNameFor
}
output.Append(typeNameFormatter.FormatName(method.Signature[paramIndex]));
}
- output.Append(")");
+ output.Append(')');
return output.ToString();
}
public IReadOnlyList Nodes => _nodes;
- public IReadOnlyList Sections => _sections;
+ public IReadOnlyList Sections => _sections;
public IReadOnlyList Symbols => _symbols;
public IReadOnlyDictionary NodeSymbolMap => _nodeSymbolMap;
- public IReadOnlyDictionary MethodSymbolMap => _methodSymbolMap;
+ public IReadOnlyDictionary MethodSymbolMap => _methodSymbolMap;
public IReadOnlyDictionary RelocCounts => _relocCounts;
}
diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs
new file mode 100644
index 00000000000000..6e28b753d2ce88
--- /dev/null
+++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs
@@ -0,0 +1,934 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Buffers.Binary;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Numerics;
+using System.Reflection.Metadata;
+using System.Reflection.PortableExecutable;
+using System.Security.Cryptography;
+using System.Text;
+using ILCompiler.DependencyAnalysis;
+using Internal.TypeSystem;
+
+namespace ILCompiler.ObjectWriter
+{
+ ///
+ /// PE image file writer built on top of the COFF object writer infrastructure.
+ /// This writer reuses sections prepared by the base class and emits a minimal
+ /// PE/PE+ image containing those sections.
+ ///
+ internal sealed class PEObjectWriter : CoffObjectWriter
+ {
+ ///
+ /// Number of low-order RVA bits that must match file position on Linux.
+ ///
+ ///
+ /// This is because the CoreCLR runtime on Linux requires the 12-16 low-order bits of section RVAs
+ /// (the number of bits corresponds to the page size) to be identical to the file offset,
+ /// otherwise memory mapping of the file fails.
+ ///
+ private const int RVABitsToMatchFilePos = 16;
+ internal const int DosHeaderSize = 0x80;
+ private const int NoSectionIndex = -1;
+
+ private static ReadOnlySpan DosHeader => // DosHeaderSize
+ [
+ 0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
+ 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+
+ 0x80, 0x00, 0x00, 0x00, // NT Header offset (0x80 == DosHeader.Length)
+
+ 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd,
+ 0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21, 0x54, 0x68,
+ 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72,
+ 0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f,
+ 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6e,
+ 0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20,
+ 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x0d, 0x0d, 0x0a,
+ 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ ];
+
+ private static ObjectNodeSection ExportDirectorySection = new ObjectNodeSection("edata", SectionType.ReadOnly);
+ private static ObjectNodeSection BaseRelocSection = new ObjectNodeSection("reloc", SectionType.ReadOnly);
+
+ private uint _peSectionAlignment;
+ private uint _peFileAlignment;
+ private readonly string _outputPath;
+ private readonly int _requestedSectionAlignment;
+ private readonly int? _coffTimestamp;
+
+ // Relocations that we can resolve at emit time (ie not file-based relocations).
+ private Dictionary> _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 long _coffHeaderOffset;
+
+ public PEObjectWriter(NodeFactory factory, ObjectWritingOptions options, OutputInfoBuilder outputInfoBuilder, string outputPath, int sectionAlignment, int? coffTimestamp)
+ : base(factory, options, outputInfoBuilder)
+ {
+ _outputPath = outputPath;
+ _requestedSectionAlignment = sectionAlignment;
+ _coffTimestamp = coffTimestamp;
+ }
+
+ public void AddExportedSymbol(string symbol)
+ {
+ if (!string.IsNullOrEmpty(symbol))
+ {
+ _exportedSymbolNames.Add(symbol);
+ }
+ }
+
+ private protected override void CreateSection(ObjectNodeSection section, string comdatName, string symbolName, int sectionIndex, Stream sectionStream)
+ {
+ // COMDAT sections are not supported in PE files
+ base.CreateSection(section, comdatName: null, symbolName, sectionIndex, sectionStream);
+
+ if (_requestedSectionAlignment != 0)
+ {
+ UpdateSectionAlignment(sectionIndex, _requestedSectionAlignment);
+ }
+ }
+
+ private struct PEOptionalHeader
+ {
+ public bool IsPE32Plus { get; }
+
+ // Standard fields
+ public byte MajorLinkerVersion { get; set; }
+ public byte MinorLinkerVersion { get; set; }
+ public uint SizeOfCode { get; set; }
+ public uint SizeOfInitializedData { get; set; }
+ public uint SizeOfUninitializedData { get; set; }
+ public uint AddressOfEntryPoint { get; set; }
+ public uint BaseOfCode { get; set; }
+ public uint BaseOfData { get; set; }
+
+ // Windows-specific fields
+ public ulong ImageBase { get; set; }
+ public uint SectionAlignment { get; set; }
+ public uint FileAlignment { get; set; }
+ public ushort MajorOperatingSystemVersion { get; set; }
+ public ushort MinorOperatingSystemVersion { get; set; }
+ public ushort MajorImageVersion { get; set; }
+ public ushort MinorImageVersion { get; set; }
+ public ushort MajorSubsystemVersion { get; set; }
+ public ushort MinorSubsystemVersion { get; set; }
+ public Subsystem Subsystem { get; set; }
+ public DllCharacteristics DllCharacteristics { get; set; }
+ public uint Win32VersionValue { get; set; }
+ public uint SizeOfImage { get; set; }
+ public uint SizeOfHeaders { get; set; }
+ public uint CheckSum { get; set; }
+ public ulong SizeOfStackReserve { get; set; }
+ public ulong SizeOfStackCommit { get; set; }
+ public ulong SizeOfHeapReserve { get; set; }
+ public ulong SizeOfHeapCommit { get; set; }
+ public uint LoaderFlags { get; set; }
+ public uint NumberOfRvaAndSizes { get; set; }
+
+ public PEOptionalHeader(bool isPe32Plus)
+ {
+ IsPE32Plus = isPe32Plus;
+
+ // Defaults taken from PETargetExtensions (PEHeaderConstants / PE32/PE64 constants)
+ MajorLinkerVersion = PEHeaderConstants.MajorLinkerVersion;
+ MinorLinkerVersion = PEHeaderConstants.MinorLinkerVersion;
+
+ SizeOfCode = 0;
+ SizeOfInitializedData = 0;
+ SizeOfUninitializedData = 0;
+ AddressOfEntryPoint = 0;
+ BaseOfCode = 0;
+ BaseOfData = 0;
+
+ ImageBase = IsPE32Plus ? PE64HeaderConstants.ExeImageBase : PE32HeaderConstants.ImageBase;
+ // Use PETargetExtensions defaults for alignments and versions
+ SectionAlignment = (uint)PEHeaderConstants.SectionAlignment;
+ FileAlignment = 0x200;
+
+ MajorOperatingSystemVersion = PEHeaderConstants.MajorOperatingSystemVersion;
+ MinorOperatingSystemVersion = PEHeaderConstants.MinorOperatingSystemVersion;
+
+ MajorImageVersion = PEHeaderConstants.MajorImageVersion;
+ MinorImageVersion = PEHeaderConstants.MinorImageVersion;
+
+ MajorSubsystemVersion = PEHeaderConstants.MajorSubsystemVersion;
+ MinorSubsystemVersion = PEHeaderConstants.MinorSubsystemVersion;
+
+ Subsystem = Subsystem.WindowsCui;
+ DllCharacteristics = DllCharacteristics.DynamicBase | DllCharacteristics.NxCompatible | DllCharacteristics.NoSeh | DllCharacteristics.TerminalServerAware;
+
+ Win32VersionValue = 0;
+ SizeOfImage = 0;
+ SizeOfHeaders = 0;
+ CheckSum = 0;
+
+ // Use PE32/PE64 per-target defaults for stack/heap
+ SizeOfStackReserve = IsPE32Plus ? PE64HeaderConstants.SizeOfStackReserve : PE32HeaderConstants.SizeOfStackReserve;
+ SizeOfStackCommit = IsPE32Plus ? PE64HeaderConstants.SizeOfStackCommit : PE32HeaderConstants.SizeOfStackCommit;
+ SizeOfHeapReserve = IsPE32Plus ? PE64HeaderConstants.SizeOfHeapReserve : PE32HeaderConstants.SizeOfHeapReserve;
+ SizeOfHeapCommit = IsPE32Plus ? PE64HeaderConstants.SizeOfHeapCommit : PE32HeaderConstants.SizeOfHeapCommit;
+
+ LoaderFlags = 0;
+ NumberOfRvaAndSizes = 16u;
+ }
+
+ public void Write(Stream stream, OptionalHeaderDataDirectories dataDirectories)
+ {
+ // Write the optional header fields directly to the stream in the
+ // correct order for PE32 / PE32+ without allocating a giant buffer.
+
+ // Magic
+ WriteLittleEndian(stream, (ushort)(IsPE32Plus ? 0x20b : 0x10b));
+
+ // Linker versions
+ stream.WriteByte(MajorLinkerVersion);
+ stream.WriteByte(MinorLinkerVersion);
+
+ // SizeOfCode
+ WriteLittleEndian(stream, SizeOfCode);
+
+ // SizeOfInitializedData
+ WriteLittleEndian(stream, SizeOfInitializedData);
+
+ // SizeOfUninitializedData
+ WriteLittleEndian(stream, SizeOfUninitializedData);
+
+ // AddressOfEntryPoint
+ WriteLittleEndian(stream, AddressOfEntryPoint);
+
+ // BaseOfCode
+ WriteLittleEndian(stream, BaseOfCode);
+
+ if (!IsPE32Plus)
+ {
+ WriteLittleEndian(stream, BaseOfData);
+ WriteLittleEndian(stream, (uint)ImageBase);
+ }
+ else
+ {
+ WriteLittleEndian(stream, ImageBase);
+ }
+
+ // SectionAlignment and FileAlignment
+ WriteLittleEndian(stream, SectionAlignment);
+ WriteLittleEndian(stream, FileAlignment);
+
+ // Versioning
+ WriteLittleEndian(stream, MajorOperatingSystemVersion);
+ WriteLittleEndian(stream, MinorOperatingSystemVersion);
+ WriteLittleEndian(stream, MajorImageVersion);
+ WriteLittleEndian(stream, MinorImageVersion);
+ WriteLittleEndian(stream, MajorSubsystemVersion);
+ WriteLittleEndian(stream, MinorSubsystemVersion);
+
+ // Win32VersionValue
+ WriteLittleEndian(stream, 0);
+
+ // SizeOfImage
+ WriteLittleEndian(stream, SizeOfImage);
+
+ // SizeOfHeaders
+ WriteLittleEndian(stream, SizeOfHeaders);
+
+ // CheckSum
+ WriteLittleEndian(stream, CheckSum);
+
+ // Subsystem
+ WriteLittleEndian(stream, (ushort)Subsystem);
+
+ // DllCharacteristics
+ WriteLittleEndian(stream, (ushort)DllCharacteristics);
+
+ if (!IsPE32Plus)
+ {
+ WriteLittleEndian(stream, (uint)SizeOfStackReserve);
+ WriteLittleEndian(stream, (uint)SizeOfStackCommit);
+ WriteLittleEndian(stream, (uint)SizeOfHeapReserve);
+ WriteLittleEndian(stream, (uint)SizeOfHeapCommit);
+ }
+ else
+ {
+ WriteLittleEndian(stream, SizeOfStackReserve);
+ WriteLittleEndian(stream, SizeOfStackCommit);
+ WriteLittleEndian(stream, SizeOfHeapReserve);
+ WriteLittleEndian(stream, SizeOfHeapCommit);
+ }
+
+ WriteLittleEndian(stream, LoaderFlags);
+ WriteLittleEndian(stream, NumberOfRvaAndSizes);
+
+ // Data directories start after NumberOfRvaAndSizes for PE32+
+ dataDirectories.WriteTo(stream, (int)NumberOfRvaAndSizes);
+ }
+ }
+
+ private sealed class OptionalHeaderDataDirectories
+ {
+ private readonly (uint VirtualAddress, uint Size)[] _entries;
+
+ public OptionalHeaderDataDirectories()
+ {
+ _entries = new (uint, uint)[16];
+ }
+
+ public void SetIfNonEmpty(int index, uint virtualAddress, uint size)
+ {
+ if ((uint)index >= (uint)_entries.Length)
+ throw new ArgumentOutOfRangeException(nameof(index));
+
+ if (size != 0)
+ {
+ _entries[index] = (virtualAddress, size);
+ }
+ }
+
+ public void WriteTo(Stream stream, int count)
+ {
+ int max = Math.Min(count, _entries.Length);
+ for (int i = 0; i < max; i++)
+ {
+ WriteLittleEndian(stream, _entries[i].VirtualAddress);
+ WriteLittleEndian(stream, _entries[i].Size);
+ }
+ }
+ }
+
+ // Named image directory indices for the PE optional header. This avoids
+ // using magic numbers when populating data directories.
+ private enum ImageDirectoryEntry
+ {
+ Export = 0,
+ Import = 1,
+ Resource = 2,
+ Exception = 3,
+ Security = 4,
+ BaseRelocation = 5,
+ Debug = 6,
+ Architecture = 7,
+ GlobalPtr = 8,
+ TLS = 9,
+ LoadConfig = 10,
+ BoundImport = 11,
+ IAT = 12,
+ DelayImport = 13,
+ CLRRuntimeHeader = 14,
+ Reserved = 15,
+ }
+
+ private protected override void EmitSymbolTable(IDictionary definedSymbols, SortedSet undefinedSymbols)
+ {
+ if (undefinedSymbols.Count > 0)
+ {
+ throw new NotSupportedException("PEObjectWriter does not support undefined symbols");
+ }
+
+ // Grab the defined symbols to resolve relocs during emit.
+ _definedSymbols = new Dictionary(definedSymbols);
+ }
+
+ private protected override void EmitSectionsAndLayout()
+ {
+ SectionWriter exportDirectory = GetOrCreateSection(ExportDirectorySection);
+
+ EmitExportDirectory(exportDirectory);
+
+ // Grab section indicies.
+ _pdataSectionIndex = GetOrCreateSection(ObjectNodeSection.PDataSection).SectionIndex;
+ _debugSectionIndex = GetOrCreateSection(ObjectNodeSection.DebugDirectorySection).SectionIndex;
+ _corMetaSectionIndex = GetOrCreateSection(ObjectNodeSection.CorMetaSection).SectionIndex;
+ _rsrcSectionIndex = GetOrCreateSection(ObjectNodeSection.Win32ResourcesSection).SectionIndex;
+ _exportSectionIndex = exportDirectory.SectionIndex;
+
+ // Create the reloc section last. We write page offsets into it based on the virtual addresses of other sections
+ // and we write it after the initial layout. Therefore, we need to have it after all other sections that it may reference,
+ // as we can't move the emitted values later.
+ _baseRelocSectionIndex = GetOrCreateSection(BaseRelocSection).SectionIndex;
+
+ uint fileAlignment = 0x200;
+ bool isWindowsOr32bit = _nodeFactory.Target.IsWindows || _nodeFactory.Target.PointerSize != 8;
+ if (isWindowsOr32bit)
+ {
+ // To minimize wasted VA space on 32-bit systems (regardless of OS),
+ // align file to page boundaries (presumed to be 4K)
+ //
+ // On Windows we use 4K file alignment (regardless of ptr size),
+ // per requirements of memory mapping API (MapViewOfFile3, et al).
+ // The alternative could be using the same approach as on Unix, but that would result in PEs
+ // incompatible with OS loader. While that is not a problem on Unix, we do not want that on Windows.
+ fileAlignment = PEHeaderConstants.SectionAlignment;
+ }
+
+ uint sectionAlignment = (uint)PEHeaderConstants.SectionAlignment;
+ if (!isWindowsOr32bit)
+ {
+ // On 64bit Linux, we must match the bottom 12 bits of section RVA's to their file offsets. For this reason
+ // we need the same alignment for both.
+ //
+ // In addition to that we specify section RVAs to be at least 64K apart, which is > page on most systems.
+ // It ensures that the sections will not overlap when mapped from a singlefile bundle, which introduces a sub-page skew.
+ //
+ // Such format would not be accepted by OS loader on Windows, but it is not a problem on Unix.
+ sectionAlignment = fileAlignment;
+ }
+
+ if (_requestedSectionAlignment != 0)
+ {
+ fileAlignment = (uint)_requestedSectionAlignment;
+ sectionAlignment = (uint)_requestedSectionAlignment;
+ }
+
+ _peFileAlignment = fileAlignment;
+ _peSectionAlignment = sectionAlignment;
+ LayoutSections(recordFinalLayout: false, out _, out _, out _, out _, out _);
+ }
+
+ 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;
+ ushort sizeOfOptionalHeader = (ushort)(isPE32Plus ? 0xF0 : 0xE0);
+
+ numberOfSections = (ushort)_sections.Count;
+
+ ushort numNonEmptySections = 0;
+ foreach (SectionDefinition section in _sections)
+ {
+ // Only count sections with data or that contain uninitialized data
+ if (section.Header.SectionCharacteristics.HasFlag(SectionCharacteristics.ContainsUninitializedData))
+ {
+ numNonEmptySections++;
+ }
+ else if (section.Stream.Length != 0)
+ {
+ numNonEmptySections++;
+ }
+ }
+
+ numberOfSections = numNonEmptySections;
+
+ // Compute headers size and align to file alignment
+ uint sizeOfHeadersUnaligned = (uint)(DosHeaderSize + 4 + 20 + sizeOfOptionalHeader + 40 * numberOfSections);
+ sizeOfHeaders = (uint)AlignmentHelper.AlignUp((int)sizeOfHeadersUnaligned, (int)_peFileAlignment);
+
+ // Calculate layout for sections: raw file offsets and virtual addresses
+ uint pointerToRawData = sizeOfHeaders;
+ uint virtualAddress = (uint)AlignmentHelper.AlignUp((int)sizeOfHeaders, (int)_peSectionAlignment);
+
+ sizeOfCode = 0;
+ sizeOfInitializedData = 0;
+
+ bool firstSection = true;
+ foreach (SectionDefinition s in _sections)
+ {
+ CoffSectionHeader h = s.Header;
+ h.SizeOfRawData = (uint)s.Stream.Length;
+ uint requestedAlignment = GetSectionAlignment(h);
+ uint rawAligned = h.SectionCharacteristics.HasFlag(SectionCharacteristics.ContainsUninitializedData)
+ ? 0u
+ : (uint)AlignmentHelper.AlignUp((int)h.SizeOfRawData, (int)uint.Max(requestedAlignment, _peFileAlignment));
+
+ if (rawAligned != 0)
+ {
+ h.PointerToRawData = pointerToRawData;
+ pointerToRawData += rawAligned;
+ h.SizeOfRawData = rawAligned;
+ }
+ else
+ {
+ h.PointerToRawData = 0;
+ }
+
+ uint sectionAlignment = uint.Max(requestedAlignment, _peSectionAlignment);
+ if (!_nodeFactory.Target.IsWindows)
+ {
+ const int RVAAlign = 1 << RVABitsToMatchFilePos;
+ if (!firstSection)
+ {
+ // when assembly is stored in a singlefile bundle, an additional skew is introduced
+ // as the streams inside the bundle are not necessarily page aligned as we do not
+ // know the actual page size on the target system.
+ // We may need one page gap of unused VA space before the next section starts.
+ // We will assume the page size is <= RVAAlign
+ virtualAddress += RVAAlign;
+ }
+
+ virtualAddress = (uint)AlignmentHelper.AlignUp((int)virtualAddress, RVAAlign);
+
+ uint rvaAdjust = (h.PointerToRawData - virtualAddress) & (RVAAlign - 1);
+ virtualAddress += rvaAdjust;
+ }
+ else
+ {
+ virtualAddress = (uint)AlignmentHelper.AlignUp((int)virtualAddress, (int)sectionAlignment);
+ }
+
+ uint virtualSize = (uint)AlignmentHelper.AlignUp((int)h.SizeOfRawData, (int)_peSectionAlignment);
+
+ h.VirtualAddress = virtualAddress;
+ h.VirtualSize = virtualSize;
+
+ virtualAddress += virtualSize;
+
+ if (h.SectionCharacteristics.HasFlag(SectionCharacteristics.ContainsCode))
+ sizeOfCode += h.SizeOfRawData;
+ else if (!h.SectionCharacteristics.HasFlag(SectionCharacteristics.ContainsUninitializedData))
+ sizeOfInitializedData += h.SizeOfRawData;
+
+ if (recordFinalLayout)
+ {
+ // Use the stream length so we don't include any space that's appended just for alignment purposes.
+ // To ensure that we match the section indexes in _sections, we don't skip empty sections here
+ // even though we omit them in EmitObjectFile.
+ _outputSectionLayout.Add(new OutputSection(h.Name, h.VirtualAddress, h.PointerToRawData, (uint)s.Stream.Length));
+ }
+ firstSection = false;
+ }
+
+ sizeOfImage = (uint)AlignmentHelper.AlignUp((int)virtualAddress, (int)_peSectionAlignment);
+ }
+
+ private protected override unsafe void EmitRelocations(int sectionIndex, List relocationList)
+ {
+ foreach (var reloc in relocationList)
+ {
+ if (!_resolvableRelocations.TryGetValue(sectionIndex, out List resolvable))
+ {
+ _resolvableRelocations[sectionIndex] = resolvable = [];
+ }
+ resolvable.Add(reloc);
+ if (Relocation.GetFileRelocationType(reloc.Type) == reloc.Type)
+ {
+ // Gather file-level relocations that need to go into the .reloc
+ // section. We collect entries grouped by 4KB page (page RVA ->
+ // list of (type<<12 | offsetInPage) WORD entries).
+ uint targetRva = _sections[sectionIndex].Header.VirtualAddress + (uint)reloc.Offset;
+ uint pageRva = targetRva & ~(PEHeaderConstants.SectionAlignment - 1);
+ ushort offsetInPage = (ushort)(targetRva & (PEHeaderConstants.SectionAlignment - 1));
+ ushort entry = (ushort)(((ushort)reloc.Type << 12) | offsetInPage);
+
+ if (!_baseRelocMap.TryGetValue(pageRva, out var list))
+ {
+ list = new List();
+ _baseRelocMap.Add(pageRva, list);
+ }
+ list.Add(entry);
+ }
+ }
+ }
+
+ private void EmitExportDirectory(SectionWriter sectionWriter)
+ {
+ if (_exportedSymbolNames.Count == 0)
+ {
+ // No exports to emit.
+ return;
+ }
+
+ List exports = [.._exportedSymbolNames];
+
+ exports.Sort(StringComparer.Ordinal);
+ string moduleName = Path.GetFileName(_outputPath);
+ const int minOrdinal = 1;
+
+ StringTableBuilder exportsStringTable = new();
+
+ exportsStringTable.ReserveString(moduleName);
+ foreach (var exportName in exports)
+ {
+ exportsStringTable.ReserveString(exportName);
+ }
+
+ string exportsStringTableSymbol = GenerateSymbolNameForReloc("exportsStringTable");
+ string addressTableSymbol = GenerateSymbolNameForReloc("addressTable");
+ string namePointerTableSymbol = GenerateSymbolNameForReloc("namePointerTable");
+ string ordinalPointerTableSymbol = GenerateSymbolNameForReloc("ordinalPointerTable");
+
+ Debug.Assert(sectionWriter.Position == 0);
+
+ // +0x00: reserved
+ sectionWriter.WriteLittleEndian(0);
+ // +0x04: time/date stamp
+ sectionWriter.WriteLittleEndian(0);
+ // +0x08: major version
+ sectionWriter.WriteLittleEndian(0);
+ // +0x0A: minor version
+ sectionWriter.WriteLittleEndian(0);
+ // +0x0C: DLL name RVA
+ sectionWriter.EmitSymbolReference(RelocType.IMAGE_REL_BASED_ADDR32NB, exportsStringTableSymbol, exportsStringTable.GetStringOffset(moduleName));
+ // +0x10: ordinal base
+ sectionWriter.WriteLittleEndian(minOrdinal);
+ // +0x14: number of entries in the address table
+ sectionWriter.WriteLittleEndian(exports.Count);
+ // +0x18: number of name pointers
+ sectionWriter.WriteLittleEndian(exports.Count);
+ // +0x1C: export address table RVA
+ sectionWriter.EmitSymbolReference(RelocType.IMAGE_REL_BASED_ADDR32NB, addressTableSymbol);
+ // +0x20: name pointer RVA
+ sectionWriter.EmitSymbolReference(RelocType.IMAGE_REL_BASED_ADDR32NB, namePointerTableSymbol);
+ // +0x24: ordinal table RVA
+ sectionWriter.EmitSymbolReference(RelocType.IMAGE_REL_BASED_ADDR32NB, ordinalPointerTableSymbol);
+
+
+ sectionWriter.EmitAlignment(4);
+ sectionWriter.EmitSymbolDefinition(addressTableSymbol);
+ foreach (var exportName in exports)
+ {
+ sectionWriter.EmitSymbolReference(RelocType.IMAGE_REL_BASED_ADDR32NB, exportName);
+ }
+
+ sectionWriter.EmitAlignment(4);
+ sectionWriter.EmitSymbolDefinition(namePointerTableSymbol);
+
+ foreach (var exportName in exports)
+ {
+ sectionWriter.EmitSymbolReference(RelocType.IMAGE_REL_BASED_ADDR32NB, exportsStringTableSymbol, exportsStringTable.GetStringOffset(exportName));
+ }
+
+ sectionWriter.EmitAlignment(4);
+ sectionWriter.EmitSymbolDefinition(ordinalPointerTableSymbol);
+ for (int i = 0; i < exports.Count; i++)
+ {
+ sectionWriter.WriteLittleEndian(checked((ushort)i));
+ }
+
+ sectionWriter.EmitSymbolDefinition(exportsStringTableSymbol);
+ MemoryStream ms = new();
+ exportsStringTable.Write(ms);
+ sectionWriter.Write(ms.ToArray());
+
+ string GenerateSymbolNameForReloc(string name)
+ {
+ return $"{_nodeFactory.NameMangler.CompilationUnitPrefix}__ExportDirectory__{name}";
+ }
+ }
+
+ private void EmitRelocSectionData()
+ {
+ var writer = GetOrCreateSection(BaseRelocSection);
+ Debug.Assert(writer.SectionIndex == _sections.Count - 1, "The .reloc section must be the last section we emit.");
+
+ foreach (var kv in _baseRelocMap)
+ {
+ uint pageRva = kv.Key;
+ List entries = kv.Value;
+ entries.Sort();
+
+ int entriesSize = entries.Count * 2;
+ int sizeOfBlock = 8 + entriesSize;
+ sizeOfBlock = AlignmentHelper.AlignUp(sizeOfBlock, 4);
+
+ writer.WriteLittleEndian(pageRva);
+ writer.WriteLittleEndian((uint)sizeOfBlock);
+
+ // Emit entries
+ foreach (ushort e in entries)
+ {
+ writer.WriteLittleEndian(e);
+ }
+
+ // Ensure block is 4-byte aligned
+ writer.EmitAlignment(4);
+ }
+
+ CoffSectionHeader relocHeader = _sections[_baseRelocSectionIndex].Header;
+
+ relocHeader.SectionCharacteristics |= SectionCharacteristics.MemDiscardable;
+ }
+
+ private protected override void EmitObjectFile(Stream outputFileStream)
+ {
+ if (_baseRelocMap.Count > 0)
+ {
+ EmitRelocSectionData();
+ }
+
+ // redo layout in case we made any additions during inital layout.
+ LayoutSections(recordFinalLayout: true, out ushort numberOfSections, out uint sizeOfHeaders, out uint sizeOfImage, out uint sizeOfInitializedData, out uint sizeOfCode);
+
+ outputFileStream.Write(DosHeader);
+ Debug.Assert(DosHeader.Length == DosHeaderSize);
+ outputFileStream.Write("PE\0\0"u8);
+
+ bool isPE32Plus = _nodeFactory.Target.PointerSize == 8;
+ ushort sizeOfOptionalHeader = (ushort)(isPE32Plus ? 0xF0 : 0xE0);
+
+ Characteristics characteristics = Characteristics.ExecutableImage | Characteristics.Dll;
+ characteristics |= isPE32Plus ? Characteristics.LargeAddressAware : Characteristics.Bit32Machine;
+
+ Machine machine = _machine;
+#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());
+#endif
+
+ // COFF File Header
+ var coffHeader = new CoffHeader
+ {
+ Machine = machine,
+ NumberOfSections = (uint)numberOfSections,
+ TimeDateStamp = (uint)(_coffTimestamp ?? 0),
+ PointerToSymbolTable = 0,
+ NumberOfSymbols = 0,
+ SizeOfOptionalHeader = sizeOfOptionalHeader,
+ Characteristics = characteristics,
+ };
+
+ _coffHeaderOffset = outputFileStream.Position;
+
+ coffHeader.Write(outputFileStream);
+
+ var peOptional = new PEOptionalHeader(isPE32Plus)
+ {
+ SizeOfCode = sizeOfCode,
+ SizeOfInitializedData = sizeOfInitializedData,
+ AddressOfEntryPoint = 0u,
+ BaseOfCode = 0u,
+ BaseOfData = 0u,
+ SizeOfImage = sizeOfImage,
+ SizeOfHeaders = sizeOfHeaders,
+ NumberOfRvaAndSizes = 16u,
+ };
+
+ // Set DLL characteristics similar to PEHeaderProvider.Create
+ DllCharacteristics dllCharacteristics =
+ DllCharacteristics.DynamicBase |
+ DllCharacteristics.NxCompatible |
+ DllCharacteristics.TerminalServerAware;
+ if (isPE32Plus)
+ {
+ dllCharacteristics |= DllCharacteristics.HighEntropyVirtualAddressSpace;
+ }
+ else
+ {
+ dllCharacteristics |= DllCharacteristics.NoSeh;
+ }
+
+ peOptional.DllCharacteristics = dllCharacteristics;
+ peOptional.SectionAlignment = _peSectionAlignment;
+ peOptional.FileAlignment = _peFileAlignment;
+
+ // Create data directories object and pass it to the optional header writer.
+ // Entries are zeroed by default; callers may populate particular directories
+ // 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);
+ }
+ 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();
+
+ // Emit headers for each section in the order they appear in _sections
+ for (int i = 0; i < _sections.Count; i++)
+ {
+ var hdr = _sections[i].Header;
+ if (hdr.VirtualSize == 0 && !hdr.SectionCharacteristics.HasFlag(SectionCharacteristics.ContainsUninitializedData))
+ {
+ // Don't emit empty sections into the binary
+ continue;
+ }
+
+ // The alignment flags aren't valid in PE image files, only in COFF object files.
+ hdr.SectionCharacteristics &= ~SectionCharacteristics.AlignMask;
+ hdr.Write(outputFileStream, stringTable);
+ }
+
+ // Write section content
+ for (int i = 0; i < _sections.Count; i++)
+ {
+ SectionDefinition section = _sections[i];
+ if (section.Header.VirtualSize != 0 && !section.Header.SectionCharacteristics.HasFlag(SectionCharacteristics.ContainsUninitializedData))
+ {
+ Debug.Assert(outputFileStream.Position <= section.Header.PointerToRawData);
+ outputFileStream.Position = section.Header.PointerToRawData; // Pad to alignment
+ if (_resolvableRelocations.TryGetValue(i, out List relocationsToResolve))
+ {
+ // Resolve all relocations we can't represent in a PE as we write out the data.
+ MemoryStream stream = new((int)section.Stream.Length);
+ section.Stream.Position = 0;
+ section.Stream.CopyTo(stream);
+ ResolveRelocations(i, relocationsToResolve, (long)peOptional.ImageBase, stream);
+ stream.Position = 0;
+ stream.CopyTo(outputFileStream);
+ }
+ else
+ {
+ section.Stream.Position = 0;
+ section.Stream.CopyTo(outputFileStream);
+ }
+ }
+ }
+
+ // Align the output file size with the image (including trailing padding for section and file alignment).
+ Debug.Assert(outputFileStream.Position <= sizeOfImage);
+ outputFileStream.SetLength(sizeOfImage);
+ }
+
+ private protected override void EmitChecksumsForObject(Stream outputFileStream, List checksumRelocations, ReadOnlySpan originalOutput)
+ {
+ base.EmitChecksumsForObject(outputFileStream, checksumRelocations, originalOutput);
+
+ if (_coffTimestamp is null)
+ {
+ // If we were not provided a deterministic timestamp, generate one from a hash of the content.
+ outputFileStream.Seek(_coffHeaderOffset + CoffHeader.TimeDateStampOffset(bigObj: false), SeekOrigin.Begin);
+ using BinaryWriter writer = new(outputFileStream, Encoding.UTF8, leaveOpen: true);
+ writer.Write(BlobContentId.FromHash(SHA256.HashData(originalOutput)).Stamp);
+ }
+ }
+
+ private unsafe void ResolveRelocations(int sectionIndex, List symbolicRelocations, long imageBase, MemoryStream stream)
+ {
+ foreach (SymbolicRelocation reloc in symbolicRelocations)
+ {
+ SymbolDefinition definedSymbol = _definedSymbols[reloc.SymbolName];
+ uint relocOffset = checked((uint)(_sections[sectionIndex].Header.VirtualAddress + reloc.Offset));
+ uint relocLength = (uint)Relocation.GetSize(reloc.Type);
+ uint symbolImageOffset = checked((uint)(_sections[definedSymbol.SectionIndex].Header.VirtualAddress + definedSymbol.Value));
+
+ fixed (byte* pData = GetRelocDataSpan(reloc))
+ {
+ long addend = Relocation.ReadValue(reloc.Type, pData);
+ switch (reloc.Type)
+ {
+ case RelocType.IMAGE_REL_BASED_ABSOLUTE:
+ // No action required
+ break;
+
+ case RelocType.IMAGE_REL_BASED_THUMB_MOV32:
+ case RelocType.IMAGE_REL_BASED_DIR64:
+ case RelocType.IMAGE_REL_BASED_HIGHLOW:
+ // Write the ImageBase-relative value to be relocated at load time.
+ Relocation.WriteValue(reloc.Type, pData, symbolImageOffset + imageBase + addend);
+ break;
+ case RelocType.IMAGE_REL_BASED_ADDR32NB:
+ Relocation.WriteValue(reloc.Type, pData, symbolImageOffset + addend);
+ break;
+ case RelocType.IMAGE_REL_BASED_REL32:
+ case RelocType.IMAGE_REL_BASED_RELPTR32:
+ Relocation.WriteValue(reloc.Type, pData, symbolImageOffset - (relocOffset + relocLength) + addend);
+ break;
+ case RelocType.IMAGE_REL_FILE_ABSOLUTE:
+ long fileOffset = _sections[definedSymbol.SectionIndex].Header.PointerToRawData + definedSymbol.Value;
+ Relocation.WriteValue(reloc.Type, pData, fileOffset + addend);
+ break;
+ case RelocType.IMAGE_REL_BASED_THUMB_MOV32_PCREL:
+ const uint offsetCorrection = 12;
+ Relocation.WriteValue(reloc.Type, pData, symbolImageOffset - (relocOffset + offsetCorrection) + addend);
+ break;
+ case RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21:
+ {
+ if (addend != 0)
+ {
+ throw new NotSupportedException();
+ }
+ int sourcePageRVA = (int)(relocOffset & ~0xFFF);
+ long delta = (symbolImageOffset - sourcePageRVA >> 12) & 0x1f_ffff;
+ Relocation.WriteValue(reloc.Type, pData, delta);
+ break;
+ }
+ case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A:
+ if (addend != 0)
+ {
+ throw new NotSupportedException();
+ }
+ Relocation.WriteValue(reloc.Type, pData, symbolImageOffset & 0xfff);
+ break;
+ case RelocType.IMAGE_REL_BASED_LOONGARCH64_PC:
+ {
+ if (addend != 0)
+ {
+ throw new NotSupportedException();
+ }
+ long delta = (symbolImageOffset - (relocOffset & ~0xfff) + ((symbolImageOffset & 0x800) << 1));
+ Relocation.WriteValue(reloc.Type, pData, delta);
+ break;
+ }
+ case RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR:
+ case RelocType.IMAGE_REL_BASED_RISCV64_PC:
+ {
+ if (addend != 0)
+ {
+ throw new NotSupportedException();
+ }
+ long delta = symbolImageOffset - relocOffset;
+ Relocation.WriteValue(reloc.Type, pData, delta);
+ break;
+ }
+ default:
+ throw new NotSupportedException($"Unsupported relocation: {reloc.Type}");
+ }
+ WriteRelocDataSpan(reloc, pData);
+ }
+
+ Span GetRelocDataSpan(SymbolicRelocation reloc)
+ {
+ stream.Position = reloc.Offset;
+ Span data = new byte[Relocation.GetSize(reloc.Type)];
+ stream.ReadExactly(data);
+ return data;
+ }
+
+ void WriteRelocDataSpan(SymbolicRelocation reloc, byte* data)
+ {
+ stream.Position = reloc.Offset;
+ stream.Write(new Span(data, Relocation.GetSize(reloc.Type)));
+ }
+ }
+ }
+
+ private static unsafe void WriteLittleEndian(Stream stream, T value)
+ where T : IBinaryInteger
+ {
+ Span buffer = stackalloc byte[sizeof(T)];
+ value.WriteLittleEndian(buffer);
+ stream.Write(buffer);
+ }
+ }
+}
diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/PETargetExtensions.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/PETargetExtensions.cs
new file mode 100644
index 00000000000000..12f992c29677b8
--- /dev/null
+++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/PETargetExtensions.cs
@@ -0,0 +1,133 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Reflection.PortableExecutable;
+
+using Internal.TypeSystem;
+
+namespace ILCompiler.ObjectWriter
+{
+ ///
+ /// Per-OS machine overrides. Corresponds to CoreCLR constants
+ /// IMAGE_FILE_MACHINE_NATIVE_OS_OVERRIDE.
+ ///
+ internal enum MachineOSOverride : ushort
+ {
+ Windows = 0,
+ Linux = 0x7B79,
+ Apple = 0x4644,
+ FreeBSD = 0xADC4,
+ NetBSD = 0x1993,
+ SunOS = 0x1992,
+ }
+
+ ///
+ /// Constants for emission of Windows PE file mostly copied from CoreCLR pewriter.cpp.
+ ///
+ internal static class PEHeaderConstants
+ {
+ public const uint SectionAlignment = 0x1000;
+
+ public const byte MajorLinkerVersion = 11;
+ public const byte MinorLinkerVersion = 0;
+
+ public const byte MajorOperatingSystemVersion = 4;
+ public const byte MinorOperatingSystemVersion = 0;
+
+ public const ushort MajorImageVersion = 0;
+ public const ushort MinorImageVersion = 0;
+
+ public const ushort MajorSubsystemVersion = 4;
+ public const ushort MinorSubsystemVersion = 0;
+ }
+
+ internal static class PE32HeaderConstants
+ {
+ public const uint ImageBase = 0x0040_0000;
+
+ public const uint SizeOfStackReserve = 0x100000;
+ public const uint SizeOfStackCommit = 0x1000;
+ public const uint SizeOfHeapReserve = 0x100000;
+ public const uint SizeOfHeapCommit = 0x1000;
+ }
+
+ internal static class PE64HeaderConstants
+ {
+ // Default base addresses used by Roslyn
+ public const ulong ExeImageBase = 0x1_4000_0000;
+ public const ulong DllImageBase = 0x1_8000_0000;
+
+ public const ulong SizeOfStackReserve = 0x400000;
+ public const ulong SizeOfStackCommit = 0x4000;
+ public const ulong SizeOfHeapReserve = 0x100000;
+ public const ulong SizeOfHeapCommit = 0x2000;
+ }
+
+ internal static class TargetExtensions
+ {
+ ///
+ /// Calculate machine ID based on compilation target architecture.
+ ///
+ /// Compilation target environment specification
+ ///
+ public static Machine MachineFromTarget(this TargetDetails target)
+ {
+ switch (target.Architecture)
+ {
+ case Internal.TypeSystem.TargetArchitecture.X64:
+ return Machine.Amd64;
+
+ case Internal.TypeSystem.TargetArchitecture.X86:
+ return Machine.I386;
+
+ case Internal.TypeSystem.TargetArchitecture.ARM64:
+ return Machine.Arm64;
+
+ case Internal.TypeSystem.TargetArchitecture.ARM:
+ return Machine.ArmThumb2;
+
+ case Internal.TypeSystem.TargetArchitecture.LoongArch64:
+ return Machine.LoongArch64;
+
+ case Internal.TypeSystem.TargetArchitecture.RiscV64:
+ return Machine.RiscV64;
+
+ default:
+ throw new NotImplementedException(target.Architecture.ToString());
+ }
+ }
+
+ ///
+ /// Determine OS machine override for the target operating system.
+ ///
+ public static MachineOSOverride MachineOSOverrideFromTarget(this TargetDetails target)
+ {
+ switch (target.OperatingSystem)
+ {
+ case TargetOS.Windows:
+ return MachineOSOverride.Windows;
+
+ case TargetOS.Linux:
+ return MachineOSOverride.Linux;
+
+ case TargetOS.OSX:
+ case TargetOS.MacCatalyst:
+ case TargetOS.iOS:
+ case TargetOS.iOSSimulator:
+ case TargetOS.tvOS:
+ case TargetOS.tvOSSimulator:
+ return MachineOSOverride.Apple;
+
+ case TargetOS.FreeBSD:
+ return MachineOSOverride.FreeBSD;
+
+ case TargetOS.NetBSD:
+ return MachineOSOverride.NetBSD;
+
+ default:
+ throw new NotImplementedException(target.OperatingSystem.ToString());
+ }
+ }
+ }
+}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/SectionData.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/SectionData.cs
similarity index 100%
rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/SectionData.cs
rename to src/coreclr/tools/Common/Compiler/ObjectWriter/SectionData.cs
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/SectionWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/SectionWriter.cs
similarity index 100%
rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/SectionWriter.cs
rename to src/coreclr/tools/Common/Compiler/ObjectWriter/SectionWriter.cs
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/StringTableBuilder.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/StringTableBuilder.cs
similarity index 99%
rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/StringTableBuilder.cs
rename to src/coreclr/tools/Common/Compiler/ObjectWriter/StringTableBuilder.cs
index 81f7315d225e4a..0c6d1831dd4eb8 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/StringTableBuilder.cs
+++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/StringTableBuilder.cs
@@ -17,7 +17,7 @@ internal class StringTableBuilder
private readonly SortedSet _reservedStrings = new(StringComparer.Ordinal);
private Dictionary _stringToOffset = new(StringComparer.Ordinal);
- public void Write(FileStream stream)
+ public void Write(Stream stream)
{
_stream.Position = 0;
_stream.CopyTo(stream);
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/TypeString.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/TypeString.cs
similarity index 98%
rename from src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/TypeString.cs
rename to src/coreclr/tools/Common/Compiler/ObjectWriter/TypeString.cs
index 6d40c859fc0788..ca2c1a43fedf4d 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/TypeString.cs
+++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/TypeString.cs
@@ -5,7 +5,7 @@
using Internal.TypeSystem;
-namespace ILCompiler.PEWriter
+namespace ILCompiler.ObjectWriter
{
// This is a very rough equivalent of typestring.cpp in the CLR with only
// basic formatting options. Only used to format type names for map
diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/UnixObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/UnixObjectWriter.cs
new file mode 100644
index 00000000000000..024ea5c53b34c3
--- /dev/null
+++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/UnixObjectWriter.cs
@@ -0,0 +1,49 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Buffers.Binary;
+using ILCompiler.DependencyAnalysis;
+using Internal.TypeSystem;
+
+using Debug = System.Diagnostics.Debug;
+
+namespace ILCompiler.ObjectWriter
+{
+ ///
+ /// Base implementation for ELF and Mach-O object file format writers. Implements
+ /// the common code for DWARF debugging and exception handling information.
+ ///
+ internal abstract partial class UnixObjectWriter : ObjectWriter
+ {
+ private sealed record UnixSectionDefinition(string SymbolName, Stream SectionStream);
+ private readonly List _sections = new();
+
+ private static readonly ObjectNodeSection LsdaSection = new ObjectNodeSection(".dotnet_eh_table", SectionType.ReadOnly);
+ private static readonly ObjectNodeSection EhFrameSection = new ObjectNodeSection(".eh_frame", SectionType.UnwindData);
+
+ protected UnixObjectWriter(NodeFactory factory, ObjectWritingOptions options)
+ : base(factory, options)
+ {
+ }
+
+ private protected override void CreateSection(ObjectNodeSection section, string comdatName, string symbolName, int sectionIndex, Stream sectionStream)
+ {
+ if (section.Type != SectionType.Debug &&
+ section != LsdaSection &&
+ section != EhFrameSection &&
+ (comdatName is null || Equals(comdatName, symbolName)))
+ {
+ // Record code and data sections that can be referenced from debugging information
+ _sections.Add(new UnixSectionDefinition(symbolName, sectionStream));
+ }
+ else
+ {
+ _sections.Add(null);
+ }
+ }
+ }
+}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModulesSectionNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModulesSectionNode.cs
index 3bc379eea93a22..881b21ea1ff0be 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModulesSectionNode.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModulesSectionNode.cs
@@ -5,12 +5,14 @@
namespace ILCompiler.DependencyAnalysis
{
- public class ModulesSectionNode : ObjectNode, ISymbolDefinitionNode
+ public class ModulesSectionNode : ObjectNode, ISymbolDefinitionNode, IUniqueSymbolNode
{
// Each compilation unit produces one module. When all compilation units are linked
// together in multifile mode, the runtime needs to get list of modules present
// in the final binary. This list is created via a special .modules section that
// contains list of pointers to all module headers.
+ //
+ // These intentionally clash with one another, but are merged with linker directives so should not be COMDAT folded
public override ObjectNodeSection GetSection(NodeFactory factory)
{
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
new file mode 100644
index 00000000000000..78f0dad8ed5284
--- /dev/null
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CoffObjectWriter.Aot.cs
@@ -0,0 +1,259 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Buffers;
+using System.Buffers.Binary;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Numerics;
+using System.Reflection.PortableExecutable;
+using System.Runtime.InteropServices;
+using System.Text;
+using ILCompiler.DependencyAnalysis;
+using ILCompiler.DependencyAnalysisFramework;
+using Internal.TypeSystem;
+using Internal.TypeSystem.TypesDebugInfo;
+using static ILCompiler.DependencyAnalysis.RelocType;
+using static ILCompiler.ObjectWriter.CoffObjectWriter.CoffRelocationType;
+
+namespace ILCompiler.ObjectWriter
+{
+ ///
+ /// COFF object file format writer for Windows targets.
+ ///
+ ///
+ /// The PE/COFF object format is described in the official specifciation at
+ /// https://learn.microsoft.com/windows/win32/debug/pe-format. However,
+ /// numerous extensions are missing in the specification. The most notable
+ /// ones are listed below.
+ ///
+ /// Object files with more than 65279 sections use an extended big object
+ /// file format that is recognized by the Microsoft linker. Many of the
+ /// internal file structures are different. The code below denotes it by
+ /// "BigObj" in parameters and variables.
+ ///
+ /// Section names longer than 8 bytes need to be written indirectly in the
+ /// string table. The PE/COFF specification describes the /NNNNNNN syntax
+ /// for referencing them. However, if the string table gets big enough the
+ /// syntax no longer works. There's an undocumented //BBBBBB syntax where
+ /// base64 offset is used instead.
+ ///
+ /// CodeView debugging format uses 16-bit section index relocations. Once
+ /// the number of sections exceeds 2^16 the same file format is still used.
+ /// The linker treats the CodeView relocations symbolically.
+ ///
+ internal partial class CoffObjectWriter : ObjectWriter
+ {
+ // Debugging
+ private SectionWriter _debugTypesSectionWriter;
+ private SectionWriter _debugSymbolSectionWriter;
+ private CodeViewFileTableBuilder _debugFileTableBuilder;
+ private CodeViewSymbolsBuilder _debugSymbolsBuilder;
+ private CodeViewTypesBuilder _debugTypesBuilder;
+
+ // Exception handling
+ private SectionWriter _pdataSectionWriter;
+
+ private protected override void CreateEhSections()
+ {
+ // Create .pdata
+ _pdataSectionWriter = GetOrCreateSection(ObjectNodeSection.PDataSection);
+ }
+
+ private protected override void EmitUnwindInfo(
+ SectionWriter sectionWriter,
+ INodeWithCodeInfo nodeWithCodeInfo,
+ string currentSymbolName)
+ {
+ if (nodeWithCodeInfo.FrameInfos is FrameInfo[] frameInfos &&
+ nodeWithCodeInfo is ISymbolDefinitionNode)
+ {
+ SectionWriter xdataSectionWriter;
+ SectionWriter pdataSectionWriter;
+ bool shareSymbol = ShouldShareSymbol((ObjectNode)nodeWithCodeInfo);
+
+ for (int i = 0; i < frameInfos.Length; i++)
+ {
+ FrameInfo frameInfo = frameInfos[i];
+
+ int start = frameInfo.StartOffset;
+ int end = frameInfo.EndOffset;
+ byte[] blob = frameInfo.BlobData;
+
+ string unwindSymbolName = $"_unwind{i}{currentSymbolName}";
+
+ if (shareSymbol)
+ {
+ // Produce an associative COMDAT symbol.
+ xdataSectionWriter = GetOrCreateSection(ObjectNodeSection.XDataSection, currentSymbolName, unwindSymbolName);
+ pdataSectionWriter = GetOrCreateSection(ObjectNodeSection.PDataSection, currentSymbolName, null);
+ }
+ 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);
+ pdataSectionWriter = _pdataSectionWriter;
+ }
+
+ // Need to emit the UNWIND_INFO at 4-byte alignment to ensure that the
+ // pointer has the lower two bits in .pdata section set to zero. On ARM64
+ // non-zero bits would mean a compact encoding.
+ xdataSectionWriter.EmitAlignment(4);
+
+ xdataSectionWriter.EmitSymbolDefinition(unwindSymbolName);
+
+ // Emit UNWIND_INFO
+ xdataSectionWriter.Write(blob);
+
+ FrameInfoFlags flags = frameInfo.Flags;
+
+ if (i != 0)
+ {
+ xdataSectionWriter.WriteByte((byte)flags);
+ }
+ else
+ {
+ MethodExceptionHandlingInfoNode ehInfo = nodeWithCodeInfo.EHInfo;
+ ISymbolNode associatedDataNode = nodeWithCodeInfo.GetAssociatedDataNode(_nodeFactory) as ISymbolNode;
+
+ flags |= ehInfo is not null ? FrameInfoFlags.HasEHInfo : 0;
+ flags |= associatedDataNode is not null ? FrameInfoFlags.HasAssociatedData : 0;
+
+ xdataSectionWriter.WriteByte((byte)flags);
+
+ if (associatedDataNode is not null)
+ {
+ xdataSectionWriter.EmitSymbolReference(
+ IMAGE_REL_BASED_ADDR32NB,
+ GetMangledName(associatedDataNode));
+ }
+
+ if (ehInfo is not null)
+ {
+ xdataSectionWriter.EmitSymbolReference(
+ IMAGE_REL_BASED_ADDR32NB,
+ GetMangledName(ehInfo));
+ }
+
+ if (nodeWithCodeInfo.GCInfo is not null)
+ {
+ xdataSectionWriter.Write(nodeWithCodeInfo.GCInfo);
+ }
+ }
+
+ // Emit RUNTIME_FUNCTION
+ pdataSectionWriter.EmitAlignment(4);
+ pdataSectionWriter.EmitSymbolReference(IMAGE_REL_BASED_ADDR32NB, currentSymbolName, start);
+ // Only x86/x64 has the End symbol
+ if (_machine is Machine.I386 or Machine.Amd64)
+ {
+ pdataSectionWriter.EmitSymbolReference(IMAGE_REL_BASED_ADDR32NB, currentSymbolName, end);
+ }
+ // Unwind info pointer
+ pdataSectionWriter.EmitSymbolReference(IMAGE_REL_BASED_ADDR32NB, unwindSymbolName, 0);
+ }
+ }
+ }
+
+ private protected override ITypesDebugInfoWriter CreateDebugInfoBuilder()
+ {
+ _debugFileTableBuilder = new CodeViewFileTableBuilder();
+
+ _debugSymbolSectionWriter = GetOrCreateSection(DebugSymbolSection);
+ _debugSymbolSectionWriter.EmitAlignment(4);
+ _debugSymbolsBuilder = new CodeViewSymbolsBuilder(
+ _nodeFactory.Target.Architecture,
+ _debugSymbolSectionWriter);
+
+ _debugTypesSectionWriter = GetOrCreateSection(DebugTypesSection);
+ _debugTypesSectionWriter.EmitAlignment(4);
+ _debugTypesBuilder = new CodeViewTypesBuilder(
+ _nodeFactory.NameMangler, _nodeFactory.Target.PointerSize,
+ _debugTypesSectionWriter);
+ return _debugTypesBuilder;
+ }
+
+ private protected override void EmitDebugFunctionInfo(
+ uint methodTypeIndex,
+ string methodName,
+ SymbolDefinition methodSymbol,
+ INodeWithDebugInfo debugNode,
+ bool hasSequencePoints)
+ {
+ DebugEHClauseInfo[] clauses = null;
+ CodeViewSymbolsBuilder debugSymbolsBuilder;
+
+ if (debugNode is INodeWithCodeInfo nodeWithCodeInfo)
+ {
+ clauses = nodeWithCodeInfo.DebugEHClauseInfos;
+ }
+
+ if (ShouldShareSymbol((ObjectNode)debugNode))
+ {
+ // If the method is emitted in COMDAT section then we need to create an
+ // associated COMDAT section for the debugging symbols.
+ var sectionWriter = GetOrCreateSection(DebugSymbolSection, methodName, null);
+ debugSymbolsBuilder = new CodeViewSymbolsBuilder(_nodeFactory.Target.Architecture, sectionWriter);
+ }
+ else
+ {
+ debugSymbolsBuilder = _debugSymbolsBuilder;
+ }
+
+ debugSymbolsBuilder.EmitSubprogramInfo(
+ methodName,
+ methodSymbol.Size,
+ methodTypeIndex,
+ debugNode.GetDebugVars().Select(debugVar => (debugVar, GetVarTypeIndex(debugNode.IsStateMachineMoveNextMethod, debugVar))),
+ clauses ?? Array.Empty());
+
+ if (hasSequencePoints)
+ {
+ debugSymbolsBuilder.EmitLineInfo(
+ _debugFileTableBuilder,
+ methodName,
+ methodSymbol.Size,
+ debugNode.GetNativeSequencePoints());
+ }
+ }
+
+ private protected override void EmitDebugThunkInfo(
+ string methodName,
+ SymbolDefinition methodSymbol,
+ INodeWithDebugInfo debugNode)
+ {
+ if (!debugNode.GetNativeSequencePoints().Any())
+ return;
+
+ CodeViewSymbolsBuilder debugSymbolsBuilder;
+
+ if (ShouldShareSymbol((ObjectNode)debugNode))
+ {
+ // If the method is emitted in COMDAT section then we need to create an
+ // associated COMDAT section for the debugging symbols.
+ var sectionWriter = GetOrCreateSection(DebugSymbolSection, methodName, null);
+ debugSymbolsBuilder = new CodeViewSymbolsBuilder(_nodeFactory.Target.Architecture, sectionWriter);
+ }
+ else
+ {
+ debugSymbolsBuilder = _debugSymbolsBuilder;
+ }
+
+ debugSymbolsBuilder.EmitLineInfo(
+ _debugFileTableBuilder,
+ methodName,
+ methodSymbol.Size,
+ debugNode.GetNativeSequencePoints());
+ }
+
+ private protected override void EmitDebugSections(IDictionary definedSymbols)
+ {
+ _debugSymbolsBuilder.WriteUserDefinedTypes(_debugTypesBuilder.UserDefinedTypes);
+ _debugFileTableBuilder.Write(_debugSymbolSectionWriter);
+ }
+ }
+}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfObjectWriter.Aot.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfObjectWriter.Aot.cs
new file mode 100644
index 00000000000000..9652211fa0ddc2
--- /dev/null
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfObjectWriter.Aot.cs
@@ -0,0 +1,160 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Diagnostics;
+using System.Buffers.Binary;
+using System.Numerics;
+using System.Reflection;
+using ILCompiler.DependencyAnalysis;
+using ILCompiler.DependencyAnalysisFramework;
+using Internal.TypeSystem;
+using static ILCompiler.DependencyAnalysis.RelocType;
+using static ILCompiler.ObjectWriter.EabiNative;
+using static ILCompiler.ObjectWriter.ElfNative;
+
+namespace ILCompiler.ObjectWriter
+{
+ ///
+ /// ELF object file format writer for Linux/Unix targets.
+ ///
+ ///
+ /// ELF object format is described by the official specification hosted
+ /// at https://refspecs.linuxfoundation.org/elf/elf.pdf. Different
+ /// architectures specify the details in the ABI specification.
+ ///
+ /// Like COFF there are several quirks related to large number of sections
+ /// (> 65279). Some of the fields in the ELF file header are moved to the
+ /// first (NULL) section header. The symbol table that is normally a single
+ /// section in the file is extended with a second .symtab_shndx section
+ /// to accomodate the section indexes that don't fit within the regular
+ /// section number field.
+ ///
+ internal sealed partial class ElfObjectWriter : UnixObjectWriter
+ {
+ private Dictionary _armUnwindSections;
+ private static readonly ObjectNodeSection ArmUnwindIndexSection = new ObjectNodeSection(".ARM.exidx", SectionType.UnwindData);
+ private static readonly ObjectNodeSection ArmUnwindTableSection = new ObjectNodeSection(".ARM.extab", SectionType.ReadOnly);
+
+ private protected override void CreateEhSections()
+ {
+ // ARM creates the EHABI sections lazily in EmitUnwindInfo
+ if (_machine is not EM_ARM)
+ {
+ base.CreateEhSections();
+ }
+ }
+
+ private protected override void EmitUnwindInfo(
+ SectionWriter sectionWriter,
+ INodeWithCodeInfo nodeWithCodeInfo,
+ string currentSymbolName)
+ {
+ if (_machine is not EM_ARM)
+ {
+ base.EmitUnwindInfo(sectionWriter, nodeWithCodeInfo, currentSymbolName);
+ return;
+ }
+
+ if (nodeWithCodeInfo.FrameInfos is FrameInfo[] frameInfos &&
+ nodeWithCodeInfo is ISymbolDefinitionNode)
+ {
+ SectionWriter exidxSectionWriter;
+ SectionWriter extabSectionWriter;
+
+ if (ShouldShareSymbol((ObjectNode)nodeWithCodeInfo))
+ {
+ exidxSectionWriter = GetOrCreateSection(ArmUnwindIndexSection, currentSymbolName, $"_unwind0{currentSymbolName}");
+ extabSectionWriter = GetOrCreateSection(ArmUnwindTableSection, currentSymbolName, $"_extab0{currentSymbolName}");
+ _sections[exidxSectionWriter.SectionIndex].LinkSection = _sections[sectionWriter.SectionIndex];
+ }
+ else
+ {
+ _armUnwindSections ??= new();
+ if (_armUnwindSections.TryGetValue(sectionWriter.SectionIndex, out var unwindSections))
+ {
+ exidxSectionWriter = unwindSections.ExidxSectionWriter;
+ extabSectionWriter = unwindSections.ExtabSectionWriter;
+ }
+ else
+ {
+ string sectionName = _sections[sectionWriter.SectionIndex].Name;
+ exidxSectionWriter = GetOrCreateSection(new ObjectNodeSection($"{ArmUnwindIndexSection.Name}{sectionName}", ArmUnwindIndexSection.Type));
+ extabSectionWriter = GetOrCreateSection(new ObjectNodeSection($"{ArmUnwindTableSection.Name}{sectionName}", ArmUnwindTableSection.Type));
+ _sections[exidxSectionWriter.SectionIndex].LinkSection = _sections[sectionWriter.SectionIndex];
+ _armUnwindSections.Add(sectionWriter.SectionIndex, (exidxSectionWriter, extabSectionWriter));
+ }
+ }
+
+ long mainLsdaOffset = 0;
+ Span unwindWord = stackalloc byte[4];
+ for (int i = 0; i < frameInfos.Length; i++)
+ {
+ FrameInfo frameInfo = frameInfos[i];
+ int start = frameInfo.StartOffset;
+ int end = frameInfo.EndOffset;
+ byte[] blob = frameInfo.BlobData;
+
+ string framSymbolName = $"_fram{i}{currentSymbolName}";
+ string extabSymbolName = $"_extab{i}{currentSymbolName}";
+
+ sectionWriter.EmitSymbolDefinition(framSymbolName, start);
+
+ // Emit the index info
+ exidxSectionWriter.EmitSymbolReference(IMAGE_REL_ARM_PREL31, framSymbolName);
+ exidxSectionWriter.EmitSymbolReference(IMAGE_REL_ARM_PREL31, extabSymbolName);
+
+ Span armUnwindInfo = EabiUnwindConverter.ConvertCFIToEabi(blob);
+ string personalitySymbolName;
+
+ if (armUnwindInfo.Length <= 3)
+ {
+ personalitySymbolName = "__aeabi_unwind_cpp_pr0";
+ unwindWord[3] = 0x80;
+ unwindWord[2] = (byte)(armUnwindInfo.Length > 0 ? armUnwindInfo[0] : 0xB0);
+ unwindWord[1] = (byte)(armUnwindInfo.Length > 1 ? armUnwindInfo[1] : 0xB0);
+ unwindWord[0] = (byte)(armUnwindInfo.Length > 2 ? armUnwindInfo[2] : 0xB0);
+ armUnwindInfo = Span.Empty;
+ }
+ else
+ {
+ Debug.Assert(armUnwindInfo.Length <= 1024);
+ personalitySymbolName = "__aeabi_unwind_cpp_pr1";
+ unwindWord[3] = 0x81;
+ unwindWord[2] = (byte)(((armUnwindInfo.Length - 2) + 3) / 4);
+ unwindWord[1] = armUnwindInfo[0];
+ unwindWord[0] = armUnwindInfo[1];
+ armUnwindInfo = armUnwindInfo.Slice(2);
+ }
+
+ extabSectionWriter.EmitAlignment(4);
+ extabSectionWriter.EmitSymbolDefinition(extabSymbolName);
+
+ // ARM EHABI requires emitting a dummy relocation to the personality routine
+ // to tell the linker to preserve it.
+ extabSectionWriter.EmitRelocation(0, unwindWord, IMAGE_REL_BASED_ABSOLUTE, personalitySymbolName, 0);
+
+ // Emit the unwinding code. First word specifies the personality routine,
+ // format and first few bytes of the unwind code. For longer unwind codes
+ // the other words follow. They are padded with the "finish" instruction
+ // (0xB0).
+ extabSectionWriter.Write(unwindWord);
+ while (armUnwindInfo.Length > 0)
+ {
+ unwindWord[3] = (byte)(armUnwindInfo.Length > 0 ? armUnwindInfo[0] : 0xB0);
+ unwindWord[2] = (byte)(armUnwindInfo.Length > 1 ? armUnwindInfo[1] : 0xB0);
+ unwindWord[1] = (byte)(armUnwindInfo.Length > 2 ? armUnwindInfo[2] : 0xB0);
+ unwindWord[0] = (byte)(armUnwindInfo.Length > 3 ? armUnwindInfo[3] : 0xB0);
+ extabSectionWriter.Write(unwindWord);
+ armUnwindInfo = armUnwindInfo.Length > 3 ? armUnwindInfo.Slice(4) : Span.Empty;
+ }
+
+ // Emit our LSDA info directly into the exception table
+ EmitLsda(nodeWithCodeInfo, frameInfos, i, extabSectionWriter, ref mainLsdaOffset);
+ }
+ }
+ }
+ }
+}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.Aot.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.Aot.cs
new file mode 100644
index 00000000000000..76cdb395b8228f
--- /dev/null
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.Aot.cs
@@ -0,0 +1,297 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Buffers.Binary;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Numerics;
+using System.Text;
+using ILCompiler.DependencyAnalysis;
+using ILCompiler.DependencyAnalysisFramework;
+using Internal.TypeSystem;
+using static ILCompiler.DependencyAnalysis.RelocType;
+using static ILCompiler.ObjectWriter.MachNative;
+
+namespace ILCompiler.ObjectWriter
+{
+ ///
+ /// Mach-O object file format writer for Apple macOS and iOS-like targets.
+ ///
+ ///
+ /// Old version of the Mach-O file format specification is mirrored at
+ /// https://github.com/aidansteele/osx-abi-macho-file-format-reference.
+ ///
+ /// There are some notable differences when compared to ELF or COFF:
+ /// - The maximum number of sections in object file is limited to 255.
+ /// - Sections are subdivided by their symbols and treated by the
+ /// linker as subsections (often referred to as atoms by the linker).
+ ///
+ /// The consequences of these design decisions is the COMDAT sections are
+ /// modeled in entirely different way. Dead code elimination works on the
+ /// atom level, so relative relocations within the same section have to be
+ /// preserved.
+ ///
+ /// Debug information uses the standard DWARF format. It is, however, not
+ /// linked into the intermediate executable files. Instead the linker creates
+ /// a map between the final executable and the object files. Debuggers like
+ /// lldb then use this map to read the debug information from the object
+ /// file directly. As a consequence the DWARF information is not generated
+ /// with relocations for the DWARF sections themselves since it's never
+ /// needed.
+ ///
+ /// While Mach-O uses the DWARF exception handling information for unwind
+ /// tables it also supports a compact representation for common prolog types.
+ /// Unofficial reference of the format can be found at
+ /// https://faultlore.com/blah/compact-unwinding/. It's necessary to emit
+ /// at least the stub entries pointing to the DWARF information but due
+ /// to limits in the linked file format it's advisable to use the compact
+ /// encoding whenever possible.
+ ///
+ /// The Apple linker is extremely picky in which relocation types are allowed
+ /// inside the DWARF sections, both for debugging and exception handling.
+ ///
+ internal sealed partial class MachObjectWriter : UnixObjectWriter
+ {
+ private sealed record CompactUnwindCode(string PcStartSymbolName, uint PcLength, uint Code, string LsdaSymbolName = null, string PersonalitySymbolName = null);
+
+ // Exception handling sections
+ private MachSection _compactUnwindSection;
+ private MemoryStream _compactUnwindStream;
+ private readonly List _compactUnwindCodes = new();
+ private readonly uint _compactUnwindDwarfCode;
+
+ private bool IsEhFrameSection(int sectionIndex) => sectionIndex == EhFrameSectionIndex;
+
+ partial void EmitCompactUnwindTable(IDictionary definedSymbols)
+ {
+ _compactUnwindStream = new MemoryStream(32 * _compactUnwindCodes.Count);
+ // Preset the size of the compact unwind section which is not generated yet
+ _compactUnwindStream.SetLength(32 * _compactUnwindCodes.Count);
+
+ _compactUnwindSection = new MachSection("__LD", "__compact_unwind", _compactUnwindStream)
+ {
+ Log2Alignment = 3,
+ Flags = S_REGULAR | S_ATTR_DEBUG,
+ };
+
+ IList symbols = _symbolTable;
+ Span tempBuffer = stackalloc byte[8];
+ foreach (var cu in _compactUnwindCodes)
+ {
+ EmitCompactUnwindSymbol(cu.PcStartSymbolName);
+ BinaryPrimitives.WriteUInt32LittleEndian(tempBuffer, cu.PcLength);
+ BinaryPrimitives.WriteUInt32LittleEndian(tempBuffer.Slice(4), cu.Code);
+ _compactUnwindStream.Write(tempBuffer);
+ EmitCompactUnwindSymbol(cu.PersonalitySymbolName);
+ EmitCompactUnwindSymbol(cu.LsdaSymbolName);
+ }
+
+ void EmitCompactUnwindSymbol(string symbolName)
+ {
+ Span tempBuffer = stackalloc byte[8];
+ if (symbolName is not null)
+ {
+ SymbolDefinition symbol = definedSymbols[symbolName];
+ MachSection section = _sections[symbol.SectionIndex];
+ BinaryPrimitives.WriteUInt64LittleEndian(tempBuffer, section.VirtualAddress + (ulong)symbol.Value);
+ _compactUnwindSection.Relocations.Add(
+ new MachRelocation
+ {
+ Address = (int)_compactUnwindStream.Position,
+ SymbolOrSectionIndex = (byte)(1 + symbol.SectionIndex), // 1-based
+ Length = 8,
+ RelocationType = ARM64_RELOC_UNSIGNED,
+ IsExternal = false,
+ IsPCRelative = false,
+ }
+ );
+ }
+ _compactUnwindStream.Write(tempBuffer);
+ }
+ }
+
+ private static uint GetArm64CompactUnwindCode(byte[] blobData)
+ {
+ if (blobData == null || blobData.Length == 0)
+ {
+ return UNWIND_ARM64_MODE_FRAMELESS;
+ }
+
+ Debug.Assert(blobData.Length % 8 == 0);
+
+ short spReg = -1;
+
+ int codeOffset = 0;
+ short cfaRegister = spReg;
+ int cfaOffset = 0;
+ int spOffset = 0;
+
+ const int REG_DWARF_X19 = 19;
+ const int REG_DWARF_X30 = 30;
+ const int REG_DWARF_FP = 29;
+ const int REG_DWARF_D8 = 72;
+ const int REG_DWARF_D15 = 79;
+ const int REG_IDX_X19 = 0;
+ const int REG_IDX_X28 = 9;
+ const int REG_IDX_FP = 10;
+ const int REG_IDX_LR = 11;
+ const int REG_IDX_D8 = 12;
+ const int REG_IDX_D15 = 19;
+ Span registerOffset = stackalloc int[20];
+
+ registerOffset.Fill(int.MinValue);
+
+ // First process all the CFI codes to figure out the layout of X19-X28, FP, LR, and
+ // D8-D15 on the stack.
+ int offset = 0;
+ while (offset < blobData.Length)
+ {
+ codeOffset = Math.Max(codeOffset, blobData[offset++]);
+ CFI_OPCODE opcode = (CFI_OPCODE)blobData[offset++];
+ short dwarfReg = BinaryPrimitives.ReadInt16LittleEndian(blobData.AsSpan(offset));
+ offset += sizeof(short);
+ int cfiOffset = BinaryPrimitives.ReadInt32LittleEndian(blobData.AsSpan(offset));
+ offset += sizeof(int);
+
+ switch (opcode)
+ {
+ case CFI_OPCODE.CFI_DEF_CFA_REGISTER:
+ cfaRegister = dwarfReg;
+
+ if (spOffset != 0)
+ {
+ for (int i = 0; i < registerOffset.Length; i++)
+ if (registerOffset[i] != int.MinValue)
+ registerOffset[i] -= spOffset;
+
+ cfaOffset += spOffset;
+ spOffset = 0;
+ }
+
+ break;
+
+ case CFI_OPCODE.CFI_REL_OFFSET:
+ Debug.Assert(cfaRegister == spReg);
+ if (dwarfReg >= REG_DWARF_X19 && dwarfReg <= REG_DWARF_X30) // X19 - X28, FP, LR
+ {
+ registerOffset[dwarfReg - REG_DWARF_X19 + REG_IDX_X19] = cfiOffset;
+ }
+ else if (dwarfReg >= REG_DWARF_D8 && dwarfReg <= REG_DWARF_D15) // D8 - D15
+ {
+ registerOffset[dwarfReg - REG_DWARF_D8 + REG_IDX_D8] = cfiOffset;
+ }
+ else
+ {
+ // We cannot represent this register in the compact unwinding format,
+ // fallback to DWARF immediately.
+ return UNWIND_ARM64_MODE_DWARF;
+ }
+ break;
+
+ case CFI_OPCODE.CFI_ADJUST_CFA_OFFSET:
+ if (cfaRegister != spReg)
+ {
+ cfaOffset += cfiOffset;
+ }
+ else
+ {
+ spOffset += cfiOffset;
+
+ for (int i = 0; i < registerOffset.Length; i++)
+ if (registerOffset[i] != int.MinValue)
+ registerOffset[i] += cfiOffset;
+ }
+ break;
+ }
+ }
+
+ uint unwindCode;
+ int nextOffset;
+
+ if (cfaRegister == REG_DWARF_FP &&
+ cfaOffset == 16 &&
+ registerOffset[REG_IDX_FP] == -16 &&
+ registerOffset[REG_IDX_LR] == -8)
+ {
+ // Frame format - FP/LR are saved on the top. SP is restored to FP+16
+ unwindCode = UNWIND_ARM64_MODE_FRAME;
+ nextOffset = -24;
+ }
+ else if (cfaRegister == -1 && spOffset <= 65520 &&
+ registerOffset[REG_IDX_FP] == int.MinValue && registerOffset[REG_IDX_LR] == int.MinValue)
+ {
+ // Frameless format - FP/LR are not saved, SP must fit within the representable range
+ uint encodedSpOffset = (uint)(spOffset / 16) << 12;
+ unwindCode = UNWIND_ARM64_MODE_FRAMELESS | encodedSpOffset;
+ nextOffset = spOffset - 8;
+ }
+ else
+ {
+ return UNWIND_ARM64_MODE_DWARF;
+ }
+
+ // Check that the integer register pairs are in the right order and mark
+ // a flag for each successive pair that is present.
+ for (int i = REG_IDX_X19; i < REG_IDX_X28; i += 2)
+ {
+ if (registerOffset[i] == int.MinValue)
+ {
+ if (registerOffset[i + 1] != int.MinValue)
+ return UNWIND_ARM64_MODE_DWARF;
+ }
+ else if (registerOffset[i] == nextOffset)
+ {
+ if (registerOffset[i + 1] != nextOffset - 8)
+ return UNWIND_ARM64_MODE_DWARF;
+ nextOffset -= 16;
+ unwindCode |= UNWIND_ARM64_FRAME_X19_X20_PAIR << (i >> 1);
+ }
+ }
+
+ // Check that the floating point register pairs are in the right order and mark
+ // a flag for each successive pair that is present.
+ for (int i = REG_IDX_D8; i < REG_IDX_D15; i += 2)
+ {
+ if (registerOffset[i] == int.MinValue)
+ {
+ if (registerOffset[i + 1] != int.MinValue)
+ return UNWIND_ARM64_MODE_DWARF;
+ }
+ else if (registerOffset[i] == nextOffset)
+ {
+ if (registerOffset[i + 1] != nextOffset - 8)
+ return UNWIND_ARM64_MODE_DWARF;
+ nextOffset -= 16;
+ unwindCode |= UNWIND_ARM64_FRAME_D8_D9_PAIR << (i >> 1);
+ }
+ }
+
+ return unwindCode;
+ }
+
+ private protected override bool EmitCompactUnwinding(string startSymbolName, ulong length, string lsdaSymbolName, byte[] blob)
+ {
+ uint encoding = _compactUnwindDwarfCode;
+
+ if (_cpuType == CPU_TYPE_ARM64)
+ {
+ encoding = GetArm64CompactUnwindCode(blob);
+ }
+
+ _compactUnwindCodes.Add(new CompactUnwindCode(
+ PcStartSymbolName: startSymbolName,
+ PcLength: (uint)length,
+ Code: encoding | (encoding != _compactUnwindDwarfCode && lsdaSymbolName is not null ? 0x40000000u : 0), // UNWIND_HAS_LSDA
+ LsdaSymbolName: encoding != _compactUnwindDwarfCode ? lsdaSymbolName : null
+ ));
+
+ return encoding != _compactUnwindDwarfCode;
+ }
+
+ private protected override bool UseFrameNames => true;
+ }
+}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.Aot.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.Aot.cs
new file mode 100644
index 00000000000000..03717aa881eef5
--- /dev/null
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.Aot.cs
@@ -0,0 +1,151 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Buffers.Binary;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using ILCompiler.DependencyAnalysis;
+using ILCompiler.DependencyAnalysisFramework;
+using Internal.TypeSystem;
+using Internal.TypeSystem.TypesDebugInfo;
+using ObjectData = ILCompiler.DependencyAnalysis.ObjectNode.ObjectData;
+using static ILCompiler.DependencyAnalysis.RelocType;
+
+namespace ILCompiler.ObjectWriter
+{
+ public abstract partial class ObjectWriter
+ {
+ // Debugging
+ private UserDefinedTypeDescriptor _userDefinedTypeDescriptor;
+
+ private protected abstract void EmitUnwindInfo(
+ SectionWriter sectionWriter,
+ INodeWithCodeInfo nodeWithCodeInfo,
+ string currentSymbolName);
+
+ private protected uint GetVarTypeIndex(bool isStateMachineMoveNextMethod, DebugVarInfoMetadata debugVar)
+ {
+ uint typeIndex;
+ try
+ {
+ if (isStateMachineMoveNextMethod && debugVar.DebugVarInfo.VarNumber == 0)
+ {
+ typeIndex = _userDefinedTypeDescriptor.GetStateMachineThisVariableTypeIndex(debugVar.Type);
+ // FIXME
+ // varName = "locals";
+ }
+ else
+ {
+ typeIndex = _userDefinedTypeDescriptor.GetVariableTypeIndex(debugVar.Type);
+ }
+ }
+ catch (TypeSystemException)
+ {
+ typeIndex = 0; // T_NOTYPE
+ // FIXME
+ // Debug.Fail();
+ }
+ return typeIndex;
+ }
+
+ private protected abstract ITypesDebugInfoWriter CreateDebugInfoBuilder();
+
+ private protected abstract void EmitDebugFunctionInfo(
+ uint methodTypeIndex,
+ string methodName,
+ SymbolDefinition methodSymbol,
+ INodeWithDebugInfo debugNode,
+ bool hasSequencePoints);
+
+ private protected virtual void EmitDebugThunkInfo(
+ string methodName,
+ SymbolDefinition methodSymbol,
+ INodeWithDebugInfo debugNode)
+ {
+ }
+
+ private protected abstract void EmitDebugSections(IDictionary definedSymbols);
+
+ partial void EmitDebugInfo(IReadOnlyCollection nodes, Logger logger)
+ {
+ if (logger.IsVerbose)
+ logger.LogMessage($"Emitting debug information");
+
+ _userDefinedTypeDescriptor = new UserDefinedTypeDescriptor(CreateDebugInfoBuilder(), _nodeFactory);
+
+ foreach (DependencyNode depNode in nodes)
+ {
+ ObjectNode node = depNode as ObjectNode;
+ if (node is null || node.ShouldSkipEmittingObjectNode(_nodeFactory))
+ {
+ continue;
+ }
+
+ ISymbolNode symbolNode = node as ISymbolNode;
+ ISymbolNode deduplicatedSymbolNode = _nodeFactory.ObjectInterner.GetDeduplicatedSymbol(_nodeFactory, symbolNode);
+ if (deduplicatedSymbolNode != symbolNode)
+ {
+ continue;
+ }
+
+ // Ensure any allocated MethodTables have debug info
+ if (node is ConstructedEETypeNode methodTable)
+ {
+ _userDefinedTypeDescriptor.GetTypeIndex(methodTable.Type, needsCompleteType: true);
+ }
+
+ if (node is INodeWithDebugInfo debugNode and ISymbolDefinitionNode symbolDefinitionNode)
+ {
+ string methodName = GetMangledName(symbolDefinitionNode);
+ if (_definedSymbols.TryGetValue(methodName, out var methodSymbol))
+ {
+ if (node is IMethodNode methodNode)
+ {
+ bool hasSequencePoints = debugNode.GetNativeSequencePoints().Any();
+ uint methodTypeIndex = hasSequencePoints ? _userDefinedTypeDescriptor.GetMethodFunctionIdTypeIndex(methodNode.Method) : 0;
+ EmitDebugFunctionInfo(methodTypeIndex, methodName, methodSymbol, debugNode, hasSequencePoints);
+ }
+ else
+ {
+ EmitDebugThunkInfo(methodName, methodSymbol, debugNode);
+ }
+ }
+ }
+ }
+
+ // Ensure all fields associated with generated static bases have debug info
+ foreach (MetadataType typeWithStaticBase in _nodeFactory.MetadataManager.GetTypesWithStaticBases())
+ {
+ _userDefinedTypeDescriptor.GetTypeIndex(typeWithStaticBase, needsCompleteType: true);
+ }
+
+ EmitDebugSections(_definedSymbols);
+ }
+
+ private protected abstract void CreateEhSections();
+
+ partial void PrepareForUnwindInfo() => CreateEhSections();
+
+ partial void EmitUnwindInfoForNode(ObjectNode node, string currentSymbolName, SectionWriter sectionWriter)
+ {
+ if (node is INodeWithCodeInfo nodeWithCodeInfo)
+ {
+ EmitUnwindInfo(sectionWriter, nodeWithCodeInfo, currentSymbolName);
+ }
+ }
+
+ partial void HandleControlFlowForRelocation(ISymbolNode relocTarget, string relocSymbolName)
+ {
+ if (relocTarget is IMethodNode or AssemblyStubNode or AddressTakenExternFunctionSymbolNode)
+ {
+ // For now consider all method symbols address taken.
+ // We could restrict this in the future to those that are referenced from
+ // reflection tables, EH tables, were actually address taken in code, or are referenced from vtables.
+ EmitReferencedMethod(relocSymbolName);
+ }
+ }
+ }
+}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/UnixObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/UnixObjectWriter.Aot.cs
similarity index 91%
rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/UnixObjectWriter.cs
rename to src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/UnixObjectWriter.Aot.cs
index 0ae31be325e186..70923f97089317 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/UnixObjectWriter.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/UnixObjectWriter.Aot.cs
@@ -18,14 +18,8 @@ namespace ILCompiler.ObjectWriter
/// Base implementation for ELF and Mach-O object file format writers. Implements
/// the common code for DWARF debugging and exception handling information.
///
- internal abstract class UnixObjectWriter : ObjectWriter
+ internal abstract partial class UnixObjectWriter : ObjectWriter
{
- private sealed record UnixSectionDefinition(string SymbolName, Stream SectionStream);
-
- // Debugging
- private DwarfBuilder _dwarfBuilder;
- private readonly List _sections = new();
-
// Exception handling sections
private SectionWriter _lsdaSectionWriter;
private int _ehFrameSectionIndex;
@@ -34,8 +28,9 @@ private sealed record UnixSectionDefinition(string SymbolName, Stream SectionStr
protected int EhFrameSectionIndex => _ehFrameSectionIndex;
- private static readonly ObjectNodeSection LsdaSection = new ObjectNodeSection(".dotnet_eh_table", SectionType.ReadOnly);
- private static readonly ObjectNodeSection EhFrameSection = new ObjectNodeSection(".eh_frame", SectionType.UnwindData);
+ // Debugging
+ private DwarfBuilder _dwarfBuilder;
+
private static readonly ObjectNodeSection DebugInfoSection = new ObjectNodeSection(".debug_info", SectionType.Debug);
private static readonly ObjectNodeSection DebugStringSection = new ObjectNodeSection(".debug_str", SectionType.Debug);
private static readonly ObjectNodeSection DebugAbbrevSection = new ObjectNodeSection(".debug_abbrev", SectionType.Debug);
@@ -44,30 +39,32 @@ private sealed record UnixSectionDefinition(string SymbolName, Stream SectionStr
private static readonly ObjectNodeSection DebugLineSection = new ObjectNodeSection(".debug_line", SectionType.Debug);
private static readonly ObjectNodeSection DebugARangesSection = new ObjectNodeSection(".debug_aranges", SectionType.Debug);
- protected UnixObjectWriter(NodeFactory factory, ObjectWritingOptions options)
- : base(factory, options)
- {
- }
+ private protected virtual bool UseFrameNames => false;
+
+ private protected virtual bool EmitCompactUnwinding(string startSymbolName, ulong length, string lsdaSymbolName, byte[] blob) => false;
- private protected override void CreateSection(ObjectNodeSection section, string comdatName, string symbolName, Stream sectionStream)
+ private protected override void CreateEhSections()
{
- if (section.Type != SectionType.Debug &&
- section != LsdaSection &&
- section != EhFrameSection &&
- (comdatName is null || Equals(comdatName, symbolName)))
- {
- // Record code and data sections that can be referenced from debugging information
- _sections.Add(new UnixSectionDefinition(symbolName, sectionStream));
- }
- else
- {
- _sections.Add(null);
- }
- }
+ SectionWriter ehFrameSectionWriter;
- private protected virtual bool EmitCompactUnwinding(string startSymbolName, ulong length, string lsdaSymbolName, byte[] blob) => false;
+ // Create sections for exception handling
+ _lsdaSectionWriter = GetOrCreateSection(LsdaSection);
+ ehFrameSectionWriter = GetOrCreateSection(EhFrameSection);
+ _lsdaSectionWriter.EmitAlignment(8);
+ ehFrameSectionWriter.EmitAlignment(8);
+ _ehFrameSectionIndex = ehFrameSectionWriter.SectionIndex;
- private protected virtual bool UseFrameNames => false;
+ // We always use the same CIE in DWARF EH frames, so create and emit it now
+ bool is64Bit = _nodeFactory.Target.Architecture switch
+ {
+ TargetArchitecture.X86 => false,
+ TargetArchitecture.ARM => false,
+ _ => true
+ };
+ _dwarfCie = new DwarfCie(_nodeFactory.Target.Architecture);
+ _dwarfEhFrame = new DwarfEhFrame(ehFrameSectionWriter, is64Bit);
+ _dwarfEhFrame.AddCie(_dwarfCie);
+ }
private protected void EmitLsda(
INodeWithCodeInfo nodeWithCodeInfo,
@@ -318,29 +315,6 @@ private protected override void EmitDebugSections(IDictionary false,
- TargetArchitecture.ARM => false,
- _ => true
- };
- _dwarfCie = new DwarfCie(_nodeFactory.Target.Architecture);
- _dwarfEhFrame = new DwarfEhFrame(ehFrameSectionWriter, is64Bit);
- _dwarfEhFrame.AddCie(_dwarfCie);
- }
-
private protected override ITypesDebugInfoWriter CreateDebugInfoBuilder()
{
return _dwarfBuilder = new DwarfBuilder(
@@ -348,5 +322,6 @@ private protected override ITypesDebugInfoWriter CreateDebugInfoBuilder()
_nodeFactory.Target,
_options.HasFlag(ObjectWritingOptions.UseDwarf5));
}
+
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj
index 86886d93c2baed..c1a26ce1951dc7 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj
@@ -22,6 +22,7 @@
+
@@ -293,6 +294,7 @@
+
@@ -325,6 +327,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -506,7 +526,6 @@
-
@@ -602,6 +621,15 @@
+
+
+
+
+
+
+
+
+
@@ -610,35 +638,17 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunContainerFormat.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunContainerFormat.cs
new file mode 100644
index 00000000000000..403fc97e4c1cd6
--- /dev/null
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunContainerFormat.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ILCompiler.DependencyAnalysis
+{
+ public enum ReadyToRunContainerFormat
+ {
+ PE
+ }
+}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs
index 9c579901fdb0d7..1b475b1cff7d62 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs
@@ -5,19 +5,19 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
+using System.IO.Pipes;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
-
+using System.Security.Cryptography;
using ILCompiler.DependencyAnalysis.ReadyToRun;
using ILCompiler.DependencyAnalysisFramework;
using ILCompiler.Diagnostics;
+using ILCompiler.ObjectWriter;
using ILCompiler.PEWriter;
-using ObjectData = ILCompiler.DependencyAnalysis.ObjectNode.ObjectData;
-
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;
-using System.Security.Cryptography;
+using ObjectData = ILCompiler.DependencyAnalysis.ObjectNode.ObjectData;
namespace ILCompiler.DependencyAnalysis
{
@@ -54,10 +54,10 @@ internal class ReadyToRunObjectWriter
///
/// Nodes to emit into the output executable as collected by the dependency analysis.
///
- private readonly IEnumerable _nodes;
+ private readonly IReadOnlyCollection _nodes;
///
- /// Set to non-null when the executable generator should output a map or symbol file.
+ /// Set to non-null when generating symbol info or profile info.
///
private readonly OutputInfoBuilder _outputInfoBuilder;
@@ -117,29 +117,11 @@ internal class ReadyToRunObjectWriter
///
private readonly int _customPESectionAlignment;
-#if DEBUG
- private struct NodeInfo
- {
- public readonly ISymbolNode Node;
- public readonly int NodeIndex;
- public readonly int SymbolIndex;
-
- public NodeInfo(ISymbolNode node, int nodeIndex, int symbolIndex)
- {
- Node = node;
- NodeIndex = nodeIndex;
- SymbolIndex = symbolIndex;
- }
- }
-
- Dictionary _previouslyWrittenNodeNames = new Dictionary();
-#endif
-
public ReadyToRunObjectWriter(
string objectFilePath,
EcmaModule componentModule,
IEnumerable inputFiles,
- IEnumerable nodes,
+ IReadOnlyCollection nodes,
NodeFactory factory,
bool generateMapFile,
bool generateMapCsvFile,
@@ -172,7 +154,6 @@ public ReadyToRunObjectWriter(
if (generateMap || generateSymbols || generateProfileFile)
{
_outputInfoBuilder = new OutputInfoBuilder();
-
if (generateMap)
{
_mapFileBuilder = new MapFileBuilder(_outputInfoBuilder);
@@ -190,238 +171,99 @@ public ReadyToRunObjectWriter(
}
}
- public void EmitPortableExecutable()
+ public void EmitReadyToRunObjects(ReadyToRunContainerFormat format, Logger logger)
{
bool succeeded = false;
try
{
- Stopwatch stopwatch = new Stopwatch();
- stopwatch.Start();
+ var stopwatch = Stopwatch.StartNew();
+
+ Debug.Assert(format == ReadyToRunContainerFormat.PE);
- PEHeaderBuilder headerBuilder;
int? timeDateStamp;
- ISymbolNode r2rHeaderExportSymbol;
- Func, BlobContentId> peIdProvider = null;
if (_nodeFactory.CompilationModuleGroup.IsCompositeBuildMode && _componentModule == null)
{
- headerBuilder = PEHeaderProvider.Create(Subsystem.Unknown, _nodeFactory.Target, _nodeFactory.ImageBase);
- peIdProvider = new Func, BlobContentId>(content => BlobContentId.FromHash(CryptographicHashProvider.ComputeSourceHash(content)));
timeDateStamp = null;
- r2rHeaderExportSymbol = _nodeFactory.Header;
}
else
{
PEReader inputPeReader = (_componentModule != null ? _componentModule.PEReader : _nodeFactory.CompilationModuleGroup.CompilationModuleSet.First().PEReader);
- headerBuilder = PEHeaderProvider.Create(inputPeReader.PEHeaders.PEHeader.Subsystem, _nodeFactory.Target, _nodeFactory.ImageBase);
timeDateStamp = inputPeReader.PEHeaders.CoffHeader.TimeDateStamp;
- r2rHeaderExportSymbol = null;
}
- Func getRuntimeFunctionsTable = null;
- if (_componentModule == null)
+ PEObjectWriter objectWriter = new(_nodeFactory, ObjectWritingOptions.None, _outputInfoBuilder, _objectFilePath, _customPESectionAlignment, timeDateStamp);
+
+ if (_nodeFactory.CompilationModuleGroup.IsCompositeBuildMode && _componentModule == null)
{
- getRuntimeFunctionsTable = GetRuntimeFunctionsTable;
+ objectWriter.AddExportedSymbol("RTR_HEADER");
}
- R2RPEBuilder r2rPeBuilder = new R2RPEBuilder(
- _nodeFactory.Target,
- headerBuilder,
- r2rHeaderExportSymbol,
- Path.GetFileName(_objectFilePath),
- getRuntimeFunctionsTable,
- _customPESectionAlignment,
- peIdProvider);
-
- NativeDebugDirectoryEntryNode nativeDebugDirectoryEntryNode = null;
- PerfMapDebugDirectoryEntryNode perfMapDebugDirectoryEntryNode = null;
- ISymbolDefinitionNode firstImportThunk = null;
- ISymbolDefinitionNode lastImportThunk = null;
- ObjectNode lastWrittenObjectNode = null;
-
- // Save cold method nodes here, and emit them to execution section last.
- List methodColdCodeNodes = new List();
-
- int nodeIndex = -1;
- foreach (var depNode in _nodes)
- {
- ObjectNode node = depNode as ObjectNode;
-
- if (node is MethodColdCodeNode)
- {
- methodColdCodeNodes.Add(node);
- continue;
- }
-
- ++nodeIndex;
-
- if (node == null)
- {
- continue;
- }
-
- if (node.ShouldSkipEmittingObjectNode(_nodeFactory))
- continue;
- ObjectData nodeContents = node.GetData(_nodeFactory);
+ using FileStream stream = new FileStream(_objectFilePath, FileMode.Create);
+ objectWriter.EmitObject(stream, _nodes, dumper: null, logger);
- if (node is NativeDebugDirectoryEntryNode nddeNode)
- {
- // There should be only one NativeDebugDirectoryEntry.
- Debug.Assert(nativeDebugDirectoryEntryNode == null);
- nativeDebugDirectoryEntryNode = nddeNode;
- }
-
- if (node is PerfMapDebugDirectoryEntryNode pmdeNode)
- {
- // There should be only one PerfMapDebugDirectoryEntryNode.
- Debug.Assert(perfMapDebugDirectoryEntryNode is null);
- perfMapDebugDirectoryEntryNode = pmdeNode;
- }
-
- if (node is ImportThunk importThunkNode)
- {
- Debug.Assert(firstImportThunk == null || lastWrittenObjectNode is ImportThunk,
- "All the import thunks must be in single contiguous run");
-
- if (firstImportThunk == null)
- {
- firstImportThunk = importThunkNode;
- }
- lastImportThunk = importThunkNode;
- }
-
- string name = GetDependencyNodeName(depNode);
-
- EmitObjectData(r2rPeBuilder, nodeContents, nodeIndex, name, node.GetSection(_nodeFactory));
- lastWrittenObjectNode = node;
- }
-
- if (_outputInfoBuilder != null)
+ if (_outputInfoBuilder is not null)
{
foreach (MethodWithGCInfo methodNode in _nodeFactory.EnumerateCompiledMethods())
_outputInfoBuilder.AddMethod(methodNode, methodNode);
}
- // Emit cold method nodes to end of execution section.
- foreach (ObjectNode node in methodColdCodeNodes)
+ if (_mapFileBuilder != null)
{
- ++nodeIndex;
+ _mapFileBuilder.SetFileSize(stream.Length);
+ }
- if (node == null)
+ if (_outputInfoBuilder is not null)
+ {
+ foreach (string inputFile in _inputFiles)
{
- continue;
+ _outputInfoBuilder.AddInputModule(_nodeFactory.TypeSystemContext.GetModuleFromPath(inputFile));
}
-
- ObjectData nodeContents = node.GetData(_nodeFactory);
- string name = GetDependencyNodeName(node);
-
- EmitObjectData(r2rPeBuilder, nodeContents, nodeIndex, name, node.GetSection(_nodeFactory));
}
- r2rPeBuilder.SetCorHeader(_nodeFactory.CopiedCorHeaderNode, _nodeFactory.CopiedCorHeaderNode.Size);
- r2rPeBuilder.SetDebugDirectory(_nodeFactory.DebugDirectoryNode, _nodeFactory.DebugDirectoryNode.Size);
- if (firstImportThunk != null)
+ if (_generateMapFile)
{
- r2rPeBuilder.AddSymbolForRange(_nodeFactory.DelayLoadMethodCallThunks, firstImportThunk, lastImportThunk);
+ string mapFileName = Path.ChangeExtension(_objectFilePath, ".map");
+ _mapFileBuilder.SaveMap(mapFileName);
}
-
- if (_nodeFactory.Win32ResourcesNode != null)
+ if (_generateMapCsvFile)
{
- Debug.Assert(_nodeFactory.Win32ResourcesNode.Size != 0);
- r2rPeBuilder.SetWin32Resources(_nodeFactory.Win32ResourcesNode, _nodeFactory.Win32ResourcesNode.Size);
+ string nodeStatsCsvFileName = Path.ChangeExtension(_objectFilePath, ".nodestats.csv");
+ string mapCsvFileName = Path.ChangeExtension(_objectFilePath, ".map.csv");
+ _mapFileBuilder.SaveCsv(nodeStatsCsvFileName, mapCsvFileName);
}
- if (_outputInfoBuilder != null)
+ if (_generatePdbFile)
{
- foreach (string inputFile in _inputFiles)
+ string path = _pdbPath;
+ if (string.IsNullOrEmpty(path))
{
- _outputInfoBuilder.AddInputModule(_nodeFactory.TypeSystemContext.GetModuleFromPath(inputFile));
+ path = Path.GetDirectoryName(_objectFilePath);
}
+ _symbolFileBuilder.SavePdb(path, _objectFilePath);
}
- using (var peStream = File.Create(_objectFilePath))
+ if (_generatePerfMapFile)
{
- r2rPeBuilder.Write(peStream, timeDateStamp);
-
- if (_mapFileBuilder != null)
- {
- _mapFileBuilder.SetFileSize(peStream.Length);
- }
-
- if (nativeDebugDirectoryEntryNode is not null)
+ string path = _perfMapPath;
+ if (string.IsNullOrEmpty(path))
{
- Debug.Assert(_generatePdbFile);
- // Compute hash of the output image and store that in the native DebugDirectory entry
- using (var hashAlgorithm = SHA256.Create())
- {
- peStream.Seek(0, SeekOrigin.Begin);
- byte[] hash = hashAlgorithm.ComputeHash(peStream);
- byte[] rsdsEntry = nativeDebugDirectoryEntryNode.GenerateRSDSEntryData(hash);
-
- int offsetToUpdate = r2rPeBuilder.GetSymbolFilePosition(nativeDebugDirectoryEntryNode);
- peStream.Seek(offsetToUpdate, SeekOrigin.Begin);
- peStream.Write(rsdsEntry);
- }
- }
-
- if (perfMapDebugDirectoryEntryNode is not null)
- {
- Debug.Assert(_generatePerfMapFile && _outputInfoBuilder is not null && _outputInfoBuilder.EnumerateInputAssemblies().Any());
- byte[] perfmapSig = PerfMapWriter.PerfMapV1SignatureHelper(_outputInfoBuilder.EnumerateInputAssemblies(), _nodeFactory.Target);
- byte[] perfMapEntry = perfMapDebugDirectoryEntryNode.GeneratePerfMapEntryData(perfmapSig, _perfMapFormatVersion);
-
- int offsetToUpdate = r2rPeBuilder.GetSymbolFilePosition(perfMapDebugDirectoryEntryNode);
- peStream.Seek(offsetToUpdate, SeekOrigin.Begin);
- peStream.Write(perfMapEntry);
+ path = Path.GetDirectoryName(_objectFilePath);
}
+ _symbolFileBuilder.SavePerfMap(path, _perfMapFormatVersion, _objectFilePath);
}
- if (_outputInfoBuilder != null)
+ if (_profileFileBuilder != null)
{
- r2rPeBuilder.AddSections(_outputInfoBuilder);
-
- if (_generateMapFile)
- {
- string mapFileName = Path.ChangeExtension(_objectFilePath, ".map");
- _mapFileBuilder.SaveMap(mapFileName);
- }
-
- if (_generateMapCsvFile)
- {
- string nodeStatsCsvFileName = Path.ChangeExtension(_objectFilePath, ".nodestats.csv");
- string mapCsvFileName = Path.ChangeExtension(_objectFilePath, ".map.csv");
- _mapFileBuilder.SaveCsv(nodeStatsCsvFileName, mapCsvFileName);
- }
-
- if (_generatePdbFile)
- {
- string path = _pdbPath;
- if (string.IsNullOrEmpty(path))
- {
- path = Path.GetDirectoryName(_objectFilePath);
- }
- _symbolFileBuilder.SavePdb(path, _objectFilePath);
- }
-
- if (_generatePerfMapFile)
- {
- string path = _perfMapPath;
- if (string.IsNullOrEmpty(path))
- {
- path = Path.GetDirectoryName(_objectFilePath);
- }
- _symbolFileBuilder.SavePerfMap(path, _perfMapFormatVersion, _objectFilePath);
- }
-
- if (_profileFileBuilder != null)
- {
- string path = Path.ChangeExtension(_objectFilePath, ".profile");
- _profileFileBuilder.SaveProfile(path);
- }
+ string path = Path.ChangeExtension(_objectFilePath, ".profile");
+ _profileFileBuilder.SaveProfile(path);
}
+ stopwatch.Stop();
+ if (logger.IsVerbose)
+ logger.LogMessage($"Done writing object file in {stopwatch.Elapsed}");
succeeded = true;
}
finally
@@ -441,81 +283,11 @@ public void EmitPortableExecutable()
}
}
- ///
- /// Helper method to generate the name of a given DependencyNode. Returns null if a name is not needed.
- /// A name is needed only when the executable generator should output a map file.
- ///
- /// The DependencyNode to return a name for, if one is needed.
- private string GetDependencyNodeName(DependencyNode depNode)
- {
- if (_mapFileBuilder == null)
- {
- return null;
- }
-
- string name = depNode.GetType().ToString();
- int firstGeneric = name.IndexOf('[');
-
- if (firstGeneric < 0)
- {
- firstGeneric = name.Length;
- }
-
- int lastDot = name.LastIndexOf('.', firstGeneric - 1, firstGeneric);
-
- if (lastDot > 0)
- {
- name = name.Substring(lastDot + 1);
- }
-
- return name;
- }
-
- ///
- /// Update the PE header directories by setting up the exception directory to point to the runtime functions table.
- /// This is needed for RtlLookupFunctionEntry / RtlLookupFunctionTable to work.
- ///
- /// PE header directory builder can be used to override RVA's / sizes of any of the directories
- private RuntimeFunctionsTableNode GetRuntimeFunctionsTable() => _nodeFactory.RuntimeFunctionsTable;
-
- ///
- /// Emit a single ObjectData into the proper section of the output R2R PE executable.
- ///
- /// R2R PE builder to output object data to
- /// ObjectData blob to emit
- /// Logical index of the emitted node for diagnostic purposes
- /// Textual representation of the ObjecData blob in the map file
- /// Section to emit the blob into
- private void EmitObjectData(R2RPEBuilder r2rPeBuilder, ObjectData data, int nodeIndex, string name, ObjectNodeSection section)
- {
-#if DEBUG
- for (int symbolIndex = 0; symbolIndex < data.DefinedSymbols.Length; symbolIndex++)
- {
- ISymbolNode definedSymbol = data.DefinedSymbols[symbolIndex];
- NodeInfo alreadyWrittenSymbol;
- string symbolName = definedSymbol.GetMangledName(_nodeFactory.NameMangler);
- if (_previouslyWrittenNodeNames.TryGetValue(symbolName, out alreadyWrittenSymbol))
- {
- Console.WriteLine($@"Duplicate symbol - 1st occurrence: [{alreadyWrittenSymbol.NodeIndex}:{alreadyWrittenSymbol.SymbolIndex}], {alreadyWrittenSymbol.Node.GetMangledName(_nodeFactory.NameMangler)}");
- Console.WriteLine($@"Duplicate symbol - 2nd occurrence: [{nodeIndex}:{symbolIndex}], {definedSymbol.GetMangledName(_nodeFactory.NameMangler)}");
- Debug.Fail("Duplicate node name emitted to file",
- $"Symbol {definedSymbol.GetMangledName(_nodeFactory.NameMangler)} has already been written to the output object file {_objectFilePath} with symbol {alreadyWrittenSymbol}");
- }
- else
- {
- _previouslyWrittenNodeNames.Add(symbolName, new NodeInfo(definedSymbol, nodeIndex, symbolIndex));
- }
- }
-#endif
-
- r2rPeBuilder.AddObjectData(data, section, name, _outputInfoBuilder);
- }
-
public static void EmitObject(
string objectFilePath,
EcmaModule componentModule,
IEnumerable inputFiles,
- IEnumerable nodes,
+ IReadOnlyCollection nodes,
NodeFactory factory,
bool generateMapFile,
bool generateMapCsvFile,
@@ -526,7 +298,9 @@ public static void EmitObject(
int perfMapFormatVersion,
bool generateProfileFile,
CallChainProfile callChainProfile,
- int customPESectionAlignment)
+ ReadyToRunContainerFormat format,
+ int customPESectionAlignment,
+ Logger logger)
{
Console.WriteLine($@"Emitting R2R PE file: {objectFilePath}");
ReadyToRunObjectWriter objectWriter = new ReadyToRunObjectWriter(
@@ -545,7 +319,8 @@ public static void EmitObject(
generateProfileFile: generateProfileFile,
callChainProfile,
customPESectionAlignment);
- objectWriter.EmitPortableExecutable();
+
+ objectWriter.EmitReadyToRunObjects(format, logger);
}
}
}
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 38e12cbc072ebd..aa31162f89cd63 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
@@ -22,7 +22,10 @@ public CopiedCorHeaderNode(EcmaModule sourceModule)
_module = sourceModule;
}
- public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.TextSection;
+ public override ObjectNodeSection GetSection(NodeFactory factory)
+ {
+ return ObjectNodeSection.CorMetaSection;
+ }
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 e73c321649509f..533b4fb293fe37 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
@@ -24,7 +24,10 @@ public CopiedFieldRvaNode(EcmaModule module, int rva)
_module = module;
}
- public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.TextSection;
+ public override ObjectNodeSection GetSection(NodeFactory factory)
+ {
+ return ObjectNodeSection.CorMetaSection;
+ }
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 8e015d97178e37..0756da31a76cb1 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
@@ -18,7 +18,10 @@ public CopiedManagedResourcesNode(EcmaModule module)
_module = module;
}
- public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.TextSection;
+ public override ObjectNodeSection GetSection(NodeFactory factory)
+ {
+ return ObjectNodeSection.CorMetaSection;
+ }
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 913ed4ab9e3408..d296b15d8762fb 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
@@ -25,7 +25,10 @@ public CopiedMetadataBlobNode(EcmaModule sourceModule)
_sourceModule = sourceModule;
}
- public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.TextSection;
+ public override ObjectNodeSection GetSection(NodeFactory factory)
+ {
+ return ObjectNodeSection.CorMetaSection;
+ }
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 2b1ad266ae8762..7c16ce08710a68 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
@@ -22,7 +22,10 @@ public CopiedMethodILNode(EcmaMethod method)
_method = (EcmaMethod)method.GetTypicalMethodDefinition();
}
- public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.TextSection;
+ public override ObjectNodeSection GetSection(NodeFactory factory)
+ {
+ return ObjectNodeSection.CorMetaSection;
+ }
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 dd5b03b87dba5d..8398909174018a 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
@@ -21,7 +21,10 @@ public CopiedStrongNameSignatureNode(EcmaModule module)
_module = module;
}
- public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.TextSection;
+ public override ObjectNodeSection GetSection(NodeFactory factory)
+ {
+ return ObjectNodeSection.CorMetaSection;
+ }
public override bool IsShareable => false;
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugDirectoryEntryNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugDirectoryEntryNode.cs
index e665d3c0cc74c3..4d0f410033d574 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugDirectoryEntryNode.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugDirectoryEntryNode.cs
@@ -12,6 +12,10 @@
using Internal.TypeSystem.Ecma;
using System.IO;
using System.Collections.Immutable;
+using System.Collections.Generic;
+using ILCompiler.Diagnostics;
+using ILCompiler.DependencyAnalysisFramework;
+using System.Security.Cryptography;
namespace ILCompiler.DependencyAnalysis.ReadyToRun
{
@@ -77,13 +81,15 @@ public class PerfMapDebugDirectoryEntryNode : DebugDirectoryEntryNode
public unsafe int Size => PerfMapEntrySize;
- public PerfMapDebugDirectoryEntryNode(string entryName)
+ public PerfMapDebugDirectoryEntryNode(string entryName, int perfMapFormatVersion)
: base(null)
{
_entryName = entryName;
+ _perfMapFormatVersion = perfMapFormatVersion;
}
- private string _entryName;
+ private readonly string _entryName;
+ private readonly int _perfMapFormatVersion;
public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
{
@@ -97,30 +103,25 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
builder.RequireInitialPointerAlignment();
builder.AddSymbol(this);
- // Emit empty entry. This will be filled with data after the output image is emitted
- builder.EmitZeros(PerfMapEntrySize);
+ List assemblies = [];
+ foreach (string inputPath in factory.TypeSystemContext.InputFilePaths.Values)
+ {
+ EcmaModule module = factory.TypeSystemContext.GetModuleFromPath(inputPath);
+ assemblies.Add(new AssemblyInfo(module.Assembly.GetName().Name, module.MetadataReader.GetGuid(module.MetadataReader.GetModuleDefinition().Mvid)));
+ }
- return builder.ToObjectData();
- }
+ byte[] signature = PerfMapWriter.PerfMapV1SignatureHelper(assemblies, factory.Target);
- public byte[] GeneratePerfMapEntryData(byte[] signature, int version)
- {
- Debug.Assert(SignatureSize == signature.Length);
- MemoryStream perfmapEntry = new MemoryStream(PerfMapEntrySize);
+ builder.EmitUInt(PerfMapMagic);
+ builder.EmitBytes(signature);
+ builder.EmitInt(_perfMapFormatVersion);
- using (BinaryWriter writer = new BinaryWriter(perfmapEntry))
- {
- writer.Write(PerfMapMagic);
- writer.Write(signature);
- writer.Write(version);
+ builder.EmitBytes(Encoding.UTF8.GetBytes(_entryName));
+ builder.EmitByte(0);
- byte[] perfmapNameBytes = Encoding.UTF8.GetBytes(_entryName);
- writer.Write(perfmapNameBytes);
- writer.Write(0); // Null terminator
+ Debug.Assert(builder.CountBytes <= PerfMapEntrySize);
- Debug.Assert(perfmapEntry.Length <= PerfMapEntrySize);
- return perfmapEntry.ToArray();
- }
+ return builder.ToObjectData();
}
internal void EmitHeader(ref ObjectDataBuilder builder)
@@ -161,7 +162,8 @@ public NativeDebugDirectoryEntryNode(string pdbName)
_pdbName = pdbName;
}
- private string _pdbName;
+ private readonly string _pdbName;
+ private readonly RSDSChecksumNode _checksumNode = new();
public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
{
@@ -175,8 +177,19 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
builder.RequireInitialPointerAlignment();
builder.AddSymbol(this);
- // Emit empty entry. This will be filled with data after the output image is emitted
- builder.EmitZeros(RSDSSize);
+ builder.EmitUInt(RsdsMagic);
+
+ builder.EmitChecksumReloc(_checksumNode);
+
+ // Age
+ builder.EmitInt(1);
+
+ string pdbFileName = _pdbName;
+ byte[] pdbFileNameBytes = Encoding.UTF8.GetBytes(pdbFileName);
+ builder.EmitBytes(pdbFileNameBytes);
+ builder.EmitByte(0); // Null terminator
+
+ Debug.Assert(builder.CountBytes <= RSDSSize);
return builder.ToObjectData();
}
@@ -223,6 +236,44 @@ internal void EmitHeader(ref ObjectDataBuilder builder, uint stamp, ushort major
builder.EmitReloc(this, RelocType.IMAGE_REL_BASED_ADDR32NB);
builder.EmitReloc(this, RelocType.IMAGE_REL_FILE_ABSOLUTE);
}
+
+ private class RSDSChecksumNode : DependencyNodeCore, IChecksumNode
+ {
+ public int ChecksumSize => 16;
+
+ public void EmitChecksum(ReadOnlySpan outputBlob, Span checksumLocation)
+ {
+ Debug.Assert(checksumLocation.Length == ChecksumSize);
+ // Take the first 16 bytes of the SHA256 hash as the RSDS checksum.
+ SHA256.HashData(outputBlob)[0..ChecksumSize].CopyTo(checksumLocation);
+ }
+
+ 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 void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
+ {
+ sb.Append(nameMangler.CompilationUnitPrefix);
+ sb.Append($"__RSDSChecksum");
+ }
+
+ 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)
+ {
+ return "RSDSChecksum";
+ }
+ }
}
public class CopiedDebugDirectoryEntryNode : DebugDirectoryEntryNode
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 d903db2524c0bf..7c304148cfee81 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
@@ -31,7 +31,7 @@ public class DebugDirectoryNode : ObjectNode, ISymbolDefinitionNode
private bool _insertDeterministicEntry;
- public DebugDirectoryNode(EcmaModule sourceModule, string outputFileName, bool shouldAddNiPdb, bool shouldGeneratePerfmap)
+ public DebugDirectoryNode(EcmaModule sourceModule, string outputFileName, bool shouldAddNiPdb, bool shouldGeneratePerfmap, int perfMapFormatVersion)
{
_module = sourceModule;
_insertDeterministicEntry = sourceModule == null; // Mark module as deterministic if generating composite image
@@ -48,11 +48,14 @@ public DebugDirectoryNode(EcmaModule sourceModule, string outputFileName, bool s
if (shouldGeneratePerfmap)
{
- _perfMapEntry = new PerfMapDebugDirectoryEntryNode(pdbNameRoot + ".ni.r2rmap");
+ _perfMapEntry = new PerfMapDebugDirectoryEntryNode(pdbNameRoot + ".ni.r2rmap", perfMapFormatVersion);
}
}
- public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.TextSection;
+ public override ObjectNodeSection GetSection(NodeFactory factory)
+ {
+ return ObjectNodeSection.DebugDirectorySection;
+ }
public override bool IsShareable => false;
@@ -69,6 +72,10 @@ public DebugDirectoryNode(EcmaModule sourceModule, string outputFileName, bool s
+ (_perfMapEntry is not null ? 1 : 0)
+ (_insertDeterministicEntry ? 1 : 0)) * ImageDebugDirectorySize;
+ public NativeDebugDirectoryEntryNode PdbEntry => _nativeEntry;
+
+ public PerfMapDebugDirectoryEntryNode PerfMapEntry => _perfMapEntry;
+
public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
{
sb.Append(nameMangler.CompilationUnitPrefix);
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodCallThunkNodeRange.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodCallThunkNodeRange.cs
index e450f816b666d8..dc2108d714d25d 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodCallThunkNodeRange.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodCallThunkNodeRange.cs
@@ -14,22 +14,47 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
/// 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.
///
- public class DelayLoadMethodCallThunkNodeRange : DependencyNodeCore, ISymbolDefinitionNode
+ public class DelayLoadMethodCallThunkNodeRange : DependencyNodeCore, ISymbolRangeNode
{
+ private const string NodeName = "DelayLoadMethodCallThunkNodeRange";
+ private ImportThunk _startNode;
+ private ImportThunk _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) => null;
- public override IEnumerable GetStaticDependencies(NodeFactory context) => null;
- public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => null;
- protected override string GetName(NodeFactory context) => "DelayLoadMethodCallThunkNodeRange";
+ 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;
public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
{
- sb.Append(GetName(null));
+ sb.Append($"__{NodeName}");
+ }
+
+ public void OnNodeMarked(DependencyNodeCore node)
+ {
+ if (node is ImportThunk thunk)
+ {
+ if (_startNode is null
+ || CompilerComparer.Instance.Compare(thunk, _startNode) <= 0)
+ {
+ _startNode = thunk;
+ }
+
+ if (_endNode is null
+ || CompilerComparer.Instance.Compare(thunk, _endNode) > 0)
+ {
+ _endNode = thunk;
+ }
+ }
}
+
+ public ISymbolNode StartNode(NodeFactory factory) => _startNode;
+ public ISymbolNode EndNode(NodeFactory factory) => _endNode;
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelegateCtorSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelegateCtorSignature.cs
index 8771ce9809056d..4fce8fa99bb579 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelegateCtorSignature.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelegateCtorSignature.cs
@@ -35,7 +35,7 @@ public DelegateCtorSignature(
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
{
- ObjectDataSignatureBuilder builder = new ObjectDataSignatureBuilder();
+ ObjectDataSignatureBuilder builder = new ObjectDataSignatureBuilder(factory, relocsOnly);
builder.AddSymbol(this);
if (!relocsOnly)
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/FieldFixupSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/FieldFixupSignature.cs
index 93c5569c5b01ea..654267876d9006 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/FieldFixupSignature.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/FieldFixupSignature.cs
@@ -32,7 +32,7 @@ public FieldFixupSignature(ReadyToRunFixupKind fixupKind, FieldWithToken fieldWi
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
{
- ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder();
+ ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder(factory, relocsOnly);
if (!relocsOnly)
{
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/GenericLookupSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/GenericLookupSignature.cs
index a50daa232ff56f..e97d8cd835b2f2 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/GenericLookupSignature.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/GenericLookupSignature.cs
@@ -93,7 +93,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
throw new NotImplementedException();
}
- ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder();
+ ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder(factory, relocsOnly);
dataBuilder.AddSymbol(this);
SignatureContext innerContext = dataBuilder.EmitFixup(factory, fixupToEmit, targetModule, factory.SignatureContext);
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/HeaderNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/HeaderNode.cs
index f010b4fd0934f0..702e69f6192c42 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/HeaderNode.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/HeaderNode.cs
@@ -178,13 +178,6 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
if (!relocsOnly && item.Node is ObjectNode on && on.ShouldSkipEmittingObjectNode(factory))
continue;
- // Unmarked nodes are not part of the graph
- if (!item.Node.Marked && !(item.Node is ObjectNode))
- {
- Debug.Assert(item.Node is DelayLoadMethodCallThunkNodeRange);
- continue;
- }
-
builder.EmitInt((int)item.Id);
builder.EmitReloc(item.StartSymbol, RelocType.IMAGE_REL_BASED_ADDR32NB);
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ILBodyFixupSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ILBodyFixupSignature.cs
index 3bca9f037e3daf..45b39e8de77d72 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ILBodyFixupSignature.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ILBodyFixupSignature.cs
@@ -52,7 +52,7 @@ private ModuleToken GetModuleToken(NodeFactory factory)
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
{
- ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder();
+ ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder(factory, relocsOnly);
if (!relocsOnly)
{
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 25a381cf0b80c9..27390868bed540 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
@@ -28,6 +28,8 @@ enum Kind
private readonly ImportSectionNode _containingImportSection;
+ private readonly int _symbolOffset = 0;
+
///
/// Import thunks are used to call a runtime-provided helper which fixes up an indirection cell in a particular
/// import section. Optionally they may also contain a relocation for a specific indirection cell to fix up.
@@ -60,8 +62,22 @@ public ImportThunk(NodeFactory factory, ReadyToRunHelper helperId, ImportSection
{
_thunkKind = Kind.Eager;
}
+
+ if (_thunkKind != Kind.Eager
+ && factory.Target.Architecture is Internal.TypeSystem.TargetArchitecture.ARM64
+ or Internal.TypeSystem.TargetArchitecture.LoongArch64
+ or Internal.TypeSystem.TargetArchitecture.RiscV64)
+ {
+ // We stuff the reloc to the module import pointer before the start of the thunk
+ // to ensure alignment.
+ // The thunk itself starts immediately after the reloc.
+ // We don't need this for an Eager thunk.
+ _symbolOffset = 8;
+ }
}
+ int ISymbolNode.Offset => base.Offset + _symbolOffset;
+
public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
{
sb.Append("DelayLoadHelper->"u8);
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 cfed82c3126870..20240ee78f3ee2 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
@@ -56,7 +56,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
}
byte[] manifestAssemblyMvidTable = _manifestNode.GetManifestAssemblyMvidTableData();
- return new ObjectData(manifestAssemblyMvidTable, Array.Empty(), alignment: 0, new ISymbolDefinitionNode[] { this });
+ return new ObjectData(manifestAssemblyMvidTable, Array.Empty(), alignment: 1, new ISymbolDefinitionNode[] { 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 5e8bffc5e6e211..d9d593d176a363 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
@@ -18,11 +18,13 @@ public MethodColdCodeNode(MethodDesc owningMethod)
_owningMethod = owningMethod;
}
+ protected internal override int Phase => (int)ObjectNodePhase.Late;
+
public int Offset => 0;
public override ObjectNodeSection GetSection(NodeFactory factory)
{
- return factory.Target.IsWindows ? ObjectNodeSection.ManagedCodeWindowsContentSection : ObjectNodeSection.ManagedCodeUnixContentSection;
+ return ObjectNodeSection.TextSection;
}
public override bool IsShareable => false;
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodFixupSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodFixupSignature.cs
index e6a761d9dd7e17..c3f3324e0c6ba4 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodFixupSignature.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodFixupSignature.cs
@@ -83,7 +83,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
return new ObjectData(data: Array.Empty(), relocs: null, alignment: 0, definedSymbols: null);
}
- ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder();
+ ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder(factory, relocsOnly);
dataBuilder.AddSymbol(this);
// Optimize some of the fixups into a more compact form
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 2d13944555485c..4b5f00c661c678 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
@@ -302,7 +302,7 @@ protected override string GetName(NodeFactory factory)
public override ObjectNodeSection GetSection(NodeFactory factory)
{
- return factory.Target.IsWindows ? ObjectNodeSection.ManagedCodeWindowsContentSection : ObjectNodeSection.ManagedCodeUnixContentSection;
+ return ObjectNodeSection.TextSection;
}
public FrameInfo[] FrameInfos => _frameInfos;
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/NewArrayFixupSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/NewArrayFixupSignature.cs
index d6bbd506a9bbd3..1846f5f0a6259d 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/NewArrayFixupSignature.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/NewArrayFixupSignature.cs
@@ -24,7 +24,7 @@ public NewArrayFixupSignature(ArrayType arrayType)
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
{
- ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder();
+ ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder(factory, relocsOnly);
if (!relocsOnly)
{
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/NewObjectFixupSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/NewObjectFixupSignature.cs
index 7ca4bd3d8b552f..da94117cb46234 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/NewObjectFixupSignature.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/NewObjectFixupSignature.cs
@@ -24,7 +24,7 @@ public NewObjectFixupSignature(TypeDesc typeDesc)
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
{
- ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder();
+ ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder(factory, relocsOnly);
if (!relocsOnly)
{
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunHelperSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunHelperSignature.cs
index e42a112ee2015d..fa4b6a7e217f2d 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunHelperSignature.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunHelperSignature.cs
@@ -23,7 +23,7 @@ public ReadyToRunHelperSignature(ReadyToRunHelper helper)
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
{
- ObjectDataSignatureBuilder builder = new ObjectDataSignatureBuilder();
+ ObjectDataSignatureBuilder builder = new ObjectDataSignatureBuilder(factory, relocsOnly);
builder.AddSymbol(this);
builder.EmitByte((byte)ReadyToRunFixupKind.Helper);
builder.EmitUInt((uint)_helperID);
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunInstructionSetSupportSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunInstructionSetSupportSignature.cs
index 44b0000d91df3e..633ac9585b34d7 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunInstructionSetSupportSignature.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunInstructionSetSupportSignature.cs
@@ -74,7 +74,7 @@ private ReadyToRunInstructionSet InstructionSetFromString(string instructionSetS
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
{
- ObjectDataSignatureBuilder builder = new ObjectDataSignatureBuilder();
+ ObjectDataSignatureBuilder builder = new ObjectDataSignatureBuilder(factory, relocsOnly);
builder.AddSymbol(this);
string[] supportedAndUnsupportedSplit = _instructionSetsSupport.Split(',');
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 d65a01c827a303..bf2aa3bd0622e7 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
@@ -16,7 +16,10 @@ public RuntimeFunctionsGCInfoNode()
public override int ClassCode => 316678892;
- public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.ReadOnlyDataSection;
+ public override ObjectNodeSection GetSection(NodeFactory factory)
+ {
+ return ObjectNodeSection.XDataSection;
+ }
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 7c2b07e338f94c..66aaa88514b476 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
@@ -23,6 +23,11 @@ public RuntimeFunctionsTableNode(NodeFactory nodeFactory)
_nodeFactory = nodeFactory;
}
+ public override ObjectNodeSection GetSection(NodeFactory factory)
+ {
+ return ObjectNodeSection.PDataSection;
+ }
+
public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
{
sb.Append(nameMangler.CompilationUnitPrefix);
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs
index 1fb549a6f1afa0..2c4ca7cd004702 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs
@@ -567,9 +567,9 @@ public class ObjectDataSignatureBuilder : SignatureBuilder
{
private ObjectDataBuilder _builder;
- public ObjectDataSignatureBuilder()
+ public ObjectDataSignatureBuilder(NodeFactory factory, bool relocsOnly)
{
- _builder = new ObjectDataBuilder();
+ _builder = new ObjectDataBuilder(factory, relocsOnly);
}
public void AddSymbol(ISymbolDefinitionNode symbol)
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/StringImportSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/StringImportSignature.cs
index 76e1e864d74d9b..91f212eb08271b 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/StringImportSignature.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/StringImportSignature.cs
@@ -19,7 +19,7 @@ public StringImportSignature(ModuleToken token)
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
{
- ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder();
+ ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder(factory, relocsOnly);
if (!relocsOnly)
{
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_ARM64/ImportThunk.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_ARM64/ImportThunk.cs
index ba619129a989bb..2e3827d7d0b765 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_ARM64/ImportThunk.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_ARM64/ImportThunk.cs
@@ -2,7 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
-
+using System.Diagnostics;
using ILCompiler.DependencyAnalysis.ARM64;
namespace ILCompiler.DependencyAnalysis.ReadyToRun
@@ -11,41 +11,57 @@ 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
+ public partial class ImportThunk : ISymbolNode
{
protected override void EmitCode(NodeFactory factory, ref ARM64Emitter instructionEncoder, bool relocsOnly)
{
+ if (_thunkKind == Kind.Eager)
+ {
+ instructionEncoder.EmitJMP(_helperCell);
+ return;
+ }
- switch (_thunkKind)
+ instructionEncoder.Builder.RequireInitialPointerAlignment();
+ Debug.Assert(instructionEncoder.Builder.CountBytes == 0);
+
+ instructionEncoder.Builder.EmitReloc(factory.ModuleImport, RelocType.IMAGE_REL_BASED_DIR64);
+
+ Debug.Assert(instructionEncoder.Builder.CountBytes == ((ISymbolNode)this).Offset);
+
+ if (relocsOnly)
{
- case Kind.Eager:
- break;
+ // When doing relocs only, we don't need to generate the actual instructions
+ // as they will be ignored. Just emit the jump so we record the dependency.
+ instructionEncoder.EmitJMP(_helperCell);
+ return;
+ }
+ switch (_thunkKind)
+ {
case Kind.DelayLoadHelper:
case Kind.DelayLoadHelperWithExistingIndirectionCell:
case Kind.VirtualStubDispatch:
+
// x11 contains indirection cell
// Do nothing x11 contains our first param
- if (!relocsOnly)
- {
- // movz x9, #index
- int index = _containingImportSection.IndexFromBeginningOfArray;
- instructionEncoder.EmitMOV(Register.X9, checked((ushort)index));
- }
+ // movz x9, #index
+ int index = _containingImportSection.IndexFromBeginningOfArray;
+ instructionEncoder.EmitMOV(Register.X9, checked((ushort)index));
// Move Module* -> x10
- // ldr x10, [PC+0x1c]
- instructionEncoder.EmitLDR(Register.X10, 0x1c);
+ // ldr x10, [PC-0xc]
+ instructionEncoder.EmitLDR(Register.X10, -0xc);
// ldr x10, [x10]
instructionEncoder.EmitLDR(Register.X10, Register.X10);
break;
case Kind.Lazy:
+
// Move Module* -> x1
- // ldr x1, [PC+0x1c]
- instructionEncoder.EmitLDR(Register.X1, 0x1c);
+ // ldr x1, [PC-0x8]
+ instructionEncoder.EmitLDR(Register.X1, -0x8);
// ldr x1, [x1]
instructionEncoder.EmitLDR(Register.X1, Register.X1);
@@ -57,10 +73,6 @@ protected override void EmitCode(NodeFactory factory, ref ARM64Emitter instructi
// branch to helper
instructionEncoder.EmitJMP(_helperCell);
-
- // Emit relocation for the Module* load above
- if (_thunkKind != Kind.Eager)
- instructionEncoder.Builder.EmitReloc(factory.ModuleImport, RelocType.IMAGE_REL_BASED_DIR64);
}
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_LoongArch64/ImportThunk.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_LoongArch64/ImportThunk.cs
index 4a3d47000a1bba..0efca97f939375 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_LoongArch64/ImportThunk.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_LoongArch64/ImportThunk.cs
@@ -2,7 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
-
+using System.Diagnostics;
using ILCompiler.DependencyAnalysis.LoongArch64;
namespace ILCompiler.DependencyAnalysis.ReadyToRun
@@ -15,12 +15,30 @@ public partial class ImportThunk
{
protected override void EmitCode(NodeFactory factory, ref LoongArch64Emitter instructionEncoder, bool relocsOnly)
{
+ if (_thunkKind == Kind.Eager)
+ {
+ // branch to helper
+ instructionEncoder.EmitJMP(_helperCell);
+ return;
+ }
- switch (_thunkKind)
+ instructionEncoder.Builder.RequireInitialPointerAlignment();
+ Debug.Assert(instructionEncoder.Builder.CountBytes == 0);
+
+ instructionEncoder.Builder.EmitReloc(factory.ModuleImport, RelocType.IMAGE_REL_BASED_DIR64);
+
+ Debug.Assert(instructionEncoder.Builder.CountBytes == ((ISymbolNode)this).Offset);
+
+ if (relocsOnly)
{
- case Kind.Eager:
- break;
+ // When doing relocs only, we don't need to generate the actual instructions
+ // as they will be ignored. Just emit the jump so we record the dependency.
+ instructionEncoder.EmitJMP(_helperCell);
+ return;
+ }
+ switch (_thunkKind)
+ {
case Kind.DelayLoadHelper:
case Kind.VirtualStubDispatch:
case Kind.DelayLoadHelperWithExistingIndirectionCell:
@@ -28,18 +46,16 @@ protected override void EmitCode(NodeFactory factory, ref LoongArch64Emitter ins
// T8 contains indirection cell
// Do nothing T8=R20 contains our first param
- if (!relocsOnly)
- {
- // ori T0=R12, R0, #index
- int index = _containingImportSection.IndexFromBeginningOfArray;
- instructionEncoder.EmitMOV(Register.R12, checked((ushort)index));
- }
+ // ori T0=R12, R0, #index
+ int index = _containingImportSection.IndexFromBeginningOfArray;
+ instructionEncoder.EmitMOV(Register.R12, checked((ushort)index));
+
+ int offset = -instructionEncoder.Builder.CountBytes;
// get pc
// pcaddi T1=R13, 0
instructionEncoder.EmitPCADDI(Register.R13);
- int offset = _helperCell.RepresentsIndirectionCell ? 0x24 : 0x14;
// load Module* -> T1
instructionEncoder.EmitLD(Register.R13, Register.R13, offset);
@@ -50,11 +66,11 @@ protected override void EmitCode(NodeFactory factory, ref LoongArch64Emitter ins
case Kind.Lazy:
{
+ int offset = -instructionEncoder.Builder.CountBytes;
// get pc
// pcaddi R5, 0
instructionEncoder.EmitPCADDI(Register.R5);
- int offset = _helperCell.RepresentsIndirectionCell ? 0x24 : 0x14;
// load Module* -> R5=A1
instructionEncoder.EmitLD(Register.R5, Register.R5, offset);
@@ -69,10 +85,6 @@ protected override void EmitCode(NodeFactory factory, ref LoongArch64Emitter ins
// branch to helper
instructionEncoder.EmitJMP(_helperCell);
-
- // Emit relocation for the Module* load above
- if (_thunkKind != Kind.Eager)
- instructionEncoder.Builder.EmitReloc(factory.ModuleImport, RelocType.IMAGE_REL_BASED_DIR64);
}
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_RiscV64/ImportThunk.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_RiscV64/ImportThunk.cs
index 9db82c334f1d6c..d4342dd4b3f42a 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_RiscV64/ImportThunk.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_RiscV64/ImportThunk.cs
@@ -2,7 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
-
+using System.Diagnostics;
using ILCompiler.DependencyAnalysis.RiscV64;
namespace ILCompiler.DependencyAnalysis.ReadyToRun
@@ -15,6 +15,26 @@ public partial class ImportThunk
{
protected override void EmitCode(NodeFactory factory, ref RiscV64Emitter instructionEncoder, bool relocsOnly)
{
+ if (_thunkKind == Kind.Eager)
+ {
+ instructionEncoder.EmitJMP(_helperCell);
+ return;
+ }
+
+ instructionEncoder.Builder.RequireInitialPointerAlignment();
+ Debug.Assert(instructionEncoder.Builder.CountBytes == 0);
+
+ instructionEncoder.Builder.EmitReloc(factory.ModuleImport, RelocType.IMAGE_REL_BASED_DIR64);
+
+ Debug.Assert(instructionEncoder.Builder.CountBytes == ((ISymbolNode)this).Offset);
+
+ if (relocsOnly)
+ {
+ // When doing relocs only, we don't need to generate the actual instructions
+ // as they will be ignored. Just emit the jump so we record the dependency.
+ instructionEncoder.EmitJMP(_helperCell);
+ return;
+ }
switch (_thunkKind)
{
@@ -23,46 +43,47 @@ protected override void EmitCode(NodeFactory factory, ref RiscV64Emitter instruc
case Kind.DelayLoadHelper:
case Kind.VirtualStubDispatch:
+ {
// t5 contains indirection cell
// Do nothing t5 contains our first param
- if (!relocsOnly)
- {
- // li t0, #index
- int index = _containingImportSection.IndexFromBeginningOfArray;
- instructionEncoder.EmitLI(Register.X5, index);
- }
+
+ // li t0, #index
+ int index = _containingImportSection.IndexFromBeginningOfArray;
+ instructionEncoder.EmitLI(Register.X5, index);
+
+ int offset = -instructionEncoder.Builder.CountBytes;
+
// get pc
// auipc t1, 0
instructionEncoder.EmitPC(Register.X6);
// load Module* -> t1
- instructionEncoder.EmitLD(Register.X6, Register.X6, 0x24);
+ instructionEncoder.EmitLD(Register.X6, Register.X6, offset);
// ld t1, t1, 0
instructionEncoder.EmitLD(Register.X6, Register.X6, 0);
break;
-
+ }
case Kind.Lazy:
+ {
+ int offset = -instructionEncoder.Builder.CountBytes;
+
// get pc
instructionEncoder.EmitPC(Register.X11);
// load Module* -> a1
- instructionEncoder.EmitLD(Register.X11, Register.X11, 0x24);
+ instructionEncoder.EmitLD(Register.X11, Register.X11, offset);
// ld a1, a1, 0
instructionEncoder.EmitLD(Register.X11, Register.X11, 0);
break;
-
+ }
default:
throw new NotImplementedException();
}
// branch to helper
instructionEncoder.EmitJMP(_helperCell);
-
- // Emit relocation for the Module* load above
- if (_thunkKind != Kind.Eager)
- instructionEncoder.Builder.EmitReloc(factory.ModuleImport, RelocType.IMAGE_REL_BASED_DIR64);
}
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs
index afd5b384d912d4..4a45d024afcf89 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs
@@ -32,7 +32,7 @@ public TypeFixupSignature(ReadyToRunFixupKind fixupKind, TypeDesc typeDesc)
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
{
- ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder();
+ ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder(factory, relocsOnly);
if (!relocsOnly)
{
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/VirtualResolutionFixupSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/VirtualResolutionFixupSignature.cs
index e3f1cef834f64f..4d946fce7a2e51 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/VirtualResolutionFixupSignature.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/VirtualResolutionFixupSignature.cs
@@ -41,7 +41,7 @@ public VirtualResolutionFixupSignature(ReadyToRunFixupKind fixupKind, MethodWith
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
{
- ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder();
+ ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder(factory, relocsOnly);
if (!relocsOnly)
{
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 b062478a786035..343a860dddd9f4 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
@@ -19,7 +19,10 @@ public Win32ResourcesNode(ResourceData resourceData)
_size = -1;
}
- public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.TextSection;
+ public override ObjectNodeSection GetSection(NodeFactory factory)
+ {
+ return ObjectNodeSection.Win32ResourcesSection;
+ }
public override bool IsShareable => false;
@@ -44,6 +47,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
private ObjectData GetDataInternal()
{
ObjectDataBuilder builder = new ObjectDataBuilder();
+ builder.RequireInitialAlignment(1);
builder.AddSymbol(this);
_resourceData.WriteResources(this, ref builder);
_size = builder.CountBytes;
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 e644439b2bec67..6f55a9e35a14e7 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs
@@ -52,13 +52,14 @@ public enum TypeValidationRule
SkipTypeValidation
}
- public sealed class NodeFactoryOptimizationFlags
+ public struct NodeFactoryOptimizationFlags
{
public bool OptimizeAsyncMethods;
public TypeValidationRule TypeValidation;
public int DeterminismStress;
public bool PrintReproArgs;
public bool EnableCachedInterfaceDispatchSupport;
+ public bool IsComponentModule;
}
// To make the code future compatible to the composite R2R story
@@ -699,6 +700,8 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase graph, I
graph.AddRoot(RuntimeFunctionsGCInfo, "GC info is always generated");
DelayLoadMethodCallThunks = new DelayLoadMethodCallThunkNodeRange();
+ graph.AddRoot(DelayLoadMethodCallThunks, "DelayLoadMethodCallThunks header entry is always generated");
+ graph.NewMarkedNode += DelayLoadMethodCallThunks.OnNodeMarked;
Header.Add(Internal.Runtime.ReadyToRunSectionType.DelayLoadMethodCallThunks, DelayLoadMethodCallThunks, DelayLoadMethodCallThunks);
ExceptionInfoLookupTableNode exceptionInfoLookupTableNode = new ExceptionInfoLookupTableNode(this);
@@ -1056,5 +1059,15 @@ public void DetectGenericCycles(TypeSystemEntity caller, TypeSystemEntity callee
{
_genericCycleDetector?.DetectCycle(caller, callee);
}
+
+ public string GetSymbolAlternateName(ISymbolNode node, out bool isHidden)
+ {
+ isHidden = false;
+ if (node == Header)
+ {
+ return "RTR_HEADER";
+ }
+ return null;
+ }
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs
index ab820af948a71c..e7e4039f95071f 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs
@@ -305,6 +305,8 @@ public sealed class ReadyToRunCodegenCompilation : Compilation
public ReadyToRunSymbolNodeFactory SymbolNodeFactory { get; }
public ReadyToRunCompilationModuleGroupBase CompilationModuleGroup { get; }
private readonly int _customPESectionAlignment;
+ private readonly ReadyToRunContainerFormat _format;
+
///
/// Determining whether a type's layout is fixed is a little expensive and the question can be asked many times
/// for the same type during compilation so preserve the computed value.
@@ -337,7 +339,8 @@ internal ReadyToRunCodegenCompilation(
MethodLayoutAlgorithm methodLayoutAlgorithm,
FileLayoutAlgorithm fileLayoutAlgorithm,
int customPESectionAlignment,
- bool verifyTypeAndFieldLayout)
+ bool verifyTypeAndFieldLayout,
+ ReadyToRunContainerFormat format)
: base(
dependencyGraph,
nodeFactory,
@@ -361,6 +364,7 @@ internal ReadyToRunCodegenCompilation(
_perfMapFormatVersion = perfMapFormatVersion;
_generateProfileFile = generateProfileFile;
_customPESectionAlignment = customPESectionAlignment;
+ _format = format;
SymbolNodeFactory = new ReadyToRunSymbolNodeFactory(nodeFactory, verifyTypeAndFieldLayout);
if (nodeFactory.InstrumentationDataTable != null)
nodeFactory.InstrumentationDataTable.Initialize(SymbolNodeFactory);
@@ -414,7 +418,9 @@ public override void Compile(string outputFile)
perfMapFormatVersion: _perfMapFormatVersion,
generateProfileFile: _generateProfileFile,
callChainProfile: _profileData.CallChainProfile,
- _customPESectionAlignment);
+ _format,
+ _customPESectionAlignment,
+ _logger);
CompilationModuleGroup moduleGroup = _nodeFactory.CompilationModuleGroup;
if (moduleGroup.IsCompositeBuildMode)
@@ -459,12 +465,14 @@ private void RewriteComponentFile(string inputFile, string outputFile, string ow
flags |= ReadyToRunFlags.READYTORUN_FLAG_SkipTypeValidation;
}
+ NodeFactoryOptimizationFlags optimizationFlags = _nodeFactory.OptimizationFlags with { IsComponentModule = true };
+
flags |= _nodeFactory.CompilationModuleGroup.GetReadyToRunFlags() & ReadyToRunFlags.READYTORUN_FLAG_MultiModuleVersionBubble;
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.
- DebugDirectoryNode debugDirectory = new DebugDirectoryNode(inputModule, outputFile, shouldAddNiPdb: false, shouldGeneratePerfmap: false);
+ DebugDirectoryNode debugDirectory = new DebugDirectoryNode(inputModule, outputFile, shouldAddNiPdb: false, shouldGeneratePerfmap: false, perfMapFormatVersion: 0);
NodeFactory componentFactory = new NodeFactory(
_nodeFactory.TypeSystemContext,
_nodeFactory.CompilationModuleGroup,
@@ -474,7 +482,7 @@ private void RewriteComponentFile(string inputFile, string outputFile, string ow
debugDirectory,
win32Resources: new Win32Resources.ResourceData(inputModule),
flags,
- _nodeFactory.OptimizationFlags,
+ optimizationFlags,
_nodeFactory.ImageBase,
automaticTypeValidation ? inputModule : null,
genericCycleDepthCutoff: -1, // We don't need generic cycle detection when rewriting component assemblies
@@ -509,7 +517,9 @@ private void RewriteComponentFile(string inputFile, string outputFile, string ow
perfMapFormatVersion: _perfMapFormatVersion,
generateProfileFile: false,
_profileData.CallChainProfile,
- customPESectionAlignment: 0);
+ ReadyToRunContainerFormat.PE,
+ customPESectionAlignment: 0,
+ _logger);
}
public override void WriteDependencyLog(string outputFileName)
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs
index 3d937f94df1994..17dede9e9adebe 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs
@@ -45,6 +45,7 @@ public sealed class ReadyToRunCodegenCompilationBuilder : CompilationBuilder
private NodeFactoryOptimizationFlags _nodeFactoryOptimizationFlags = new NodeFactoryOptimizationFlags();
private int _genericCycleDetectionDepthCutoff = -1;
private int _genericCycleDetectionBreadthCutoff = -1;
+ private ReadyToRunContainerFormat _format = ReadyToRunContainerFormat.PE;
private string _jitPath;
private string _outputFile;
@@ -220,6 +221,12 @@ public ReadyToRunCodegenCompilationBuilder UseGenericCycleDetection(int depthCut
return this;
}
+ public ReadyToRunCodegenCompilationBuilder UseContainerFormat(ReadyToRunContainerFormat format)
+ {
+ _format = format;
+ return this;
+ }
+
public override ICompilation ToCompilation()
{
// TODO: only copy COR headers for single-assembly build and for composite build with embedded MSIL
@@ -227,7 +234,7 @@ public override ICompilation ToCompilation()
EcmaModule singleModule = _compilationGroup.IsCompositeBuildMode ? null : inputModules.First();
CopiedCorHeaderNode corHeaderNode = new CopiedCorHeaderNode(singleModule);
// TODO: proper support for multiple input files
- DebugDirectoryNode debugDirectoryNode = new DebugDirectoryNode(singleModule, _outputFile, _generatePdbFile, _generatePerfMapFile);
+ DebugDirectoryNode debugDirectoryNode = new DebugDirectoryNode(singleModule, _outputFile, _generatePdbFile, _generatePerfMapFile, _perfMapFormatVersion);
// Produce a ResourceData where the IBC PROFILE_DATA entry has been filtered out
// TODO: proper support for multiple input files
@@ -344,7 +351,8 @@ public override ICompilation ToCompilation()
_r2rMethodLayoutAlgorithm,
_r2rFileLayoutAlgorithm,
_customPESectionAlignment,
- _verifyTypeAndFieldLayout);
+ _verifyTypeAndFieldLayout,
+ _format);
}
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs
index a2ea17ccc111b8..5e86bc9a6eed1d 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs
@@ -931,5 +931,7 @@ public virtual void ApplyProfileGuidedOptimizationData(ProfileDataManager profil
{
_profileData = profileGuidedCompileRestriction;
}
+
+ public bool IsSingleFileCompilation => true;
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj
index 3251ff2660ebe3..bf9887611b02fc 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj
@@ -75,6 +75,7 @@
+
@@ -137,6 +138,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -149,6 +168,7 @@
+
@@ -274,10 +294,8 @@
-
-
@@ -285,9 +303,6 @@
-
-
-
@@ -338,4 +353,8 @@
JitInterface\UnboxingMethodDesc.cs
+
+
+
+
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/MapFileBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/MapFileBuilder.cs
index 726ae5fe079e6d..9d49bb86680145 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/MapFileBuilder.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/MapFileBuilder.cs
@@ -18,6 +18,7 @@
using ILCompiler.DependencyAnalysis;
using ILCompiler.DependencyAnalysis.ReadyToRun;
using ILCompiler.Diagnostics;
+using ILCompiler.ObjectWriter;
namespace ILCompiler.PEWriter
{
@@ -197,12 +198,12 @@ private void WriteSections(StreamWriter writer)
WriteTitle(writer, "INDEX | FILEOFFSET | RVA | END_RVA | LENGTH | NAME");
for (int sectionIndex = 0; sectionIndex < _outputInfoBuilder.Sections.Count; sectionIndex++)
{
- Section section = _outputInfoBuilder.Sections[sectionIndex];
+ OutputSection section = _outputInfoBuilder.Sections[sectionIndex];
writer.Write($"{sectionIndex,5} | ");
- writer.Write($"0x{section.FilePosWhenPlaced:X8} | ");
- writer.Write($"0x{section.RVAWhenPlaced:X8} | ");
- writer.Write($"0x{(section.RVAWhenPlaced + section.Content.Count):X8} | ");
- writer.Write($"0x{section.Content.Count:X8} | ");
+ writer.Write($"0x{section.FilePosition:X8} | ");
+ writer.Write($"0x{section.VirtualAddress:X8} | ");
+ writer.Write($"0x{(section.VirtualAddress + section.Length):X8} | ");
+ writer.Write($"0x{section.Length:X8} | ");
writer.WriteLine(section.Name);
}
}
@@ -223,8 +224,8 @@ private void WriteMap(StreamWriter writer)
{
// No more nodes or next symbol is below next node - emit symbol
OutputSymbol symbol = _outputInfoBuilder.Symbols[symbolIndex++];
- Section section = _outputInfoBuilder.Sections[symbol.SectionIndex];
- writer.Write($"0x{symbol.Offset + section.RVAWhenPlaced:X8} | ");
+ OutputSection section = _outputInfoBuilder.Sections[symbol.SectionIndex];
+ writer.Write($"0x{symbol.Offset + section.VirtualAddress:X8} | ");
writer.Write(" | ");
writer.Write(" | ");
writer.Write($"{GetNameHead(section),-SectionNameHeadLength} | ");
@@ -234,9 +235,9 @@ private void WriteMap(StreamWriter writer)
{
// Emit node and optionally symbol
OutputNode node = _outputInfoBuilder.Nodes[nodeIndex++];
- Section section = _outputInfoBuilder.Sections[node.SectionIndex];
+ OutputSection section = _outputInfoBuilder.Sections[node.SectionIndex];
- writer.Write($"0x{node.Offset + section.RVAWhenPlaced:X8} | ");
+ writer.Write($"0x{node.Offset + section.VirtualAddress:X8} | ");
writer.Write($"0x{node.Length:X6} | ");
writer.Write($"{node.Relocations,6} | ");
writer.Write($"{GetNameHead(section),-SectionNameHeadLength} | ");
@@ -265,8 +266,8 @@ private void WriteMapCsv(StreamWriter writer)
{
// No more nodes or next symbol is below next node - emit symbol
OutputSymbol symbol = _outputInfoBuilder.Symbols[symbolIndex++];
- Section section = _outputInfoBuilder.Sections[symbol.SectionIndex];
- writer.Write($"0x{symbol.Offset + section.RVAWhenPlaced:X8},");
+ OutputSection section = _outputInfoBuilder.Sections[symbol.SectionIndex];
+ writer.Write($"0x{symbol.Offset + section.VirtualAddress:X8},");
writer.Write(",");
writer.Write(",");
writer.Write($"{section.Name},");
@@ -277,9 +278,9 @@ private void WriteMapCsv(StreamWriter writer)
{
// Emit node and optionally symbol
OutputNode node = _outputInfoBuilder.Nodes[nodeIndex++];
- Section section = _outputInfoBuilder.Sections[node.SectionIndex];
+ OutputSection section = _outputInfoBuilder.Sections[node.SectionIndex];
- writer.Write($"0x{node.Offset + section.RVAWhenPlaced:X8},");
+ writer.Write($"0x{node.Offset + section.VirtualAddress:X8},");
writer.Write($"{node.Length},");
writer.Write($"{node.Relocations},");
writer.Write($"{section.Name},");
@@ -294,7 +295,7 @@ private void WriteMapCsv(StreamWriter writer)
}
}
- private static string GetNameHead(Section section)
+ private static string GetNameHead(OutputSection section)
{
string sectionNameHead = section.Name;
if (sectionNameHead.Length > SectionNameHeadLength)
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/ProfileFileBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/ProfileFileBuilder.cs
index c35aac16c0c785..ce1194efcbae4e 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/ProfileFileBuilder.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/ProfileFileBuilder.cs
@@ -12,6 +12,7 @@
using ILCompiler.DependencyAnalysis;
using ILCompiler.DependencyAnalysis.ReadyToRun;
+using ILCompiler.ObjectWriter;
namespace ILCompiler.PEWriter
{
@@ -132,7 +133,7 @@ private void CalculateSymbolMethodMap()
return;
}
_symbolMethodMap = new Dictionary();
- foreach (KeyValuePair kvpSymbolMethod in _outputInfoBuilder.MethodSymbolMap)
+ foreach (KeyValuePair kvpSymbolMethod in _outputInfoBuilder.MethodSymbolMap)
{
_symbolMethodMap.Add(kvpSymbolMethod.Value.Method, kvpSymbolMethod.Key);
}
@@ -152,32 +153,35 @@ private void CalculateCallInfo()
foreach (KeyValuePair> kvpCallerCalleeCount in _callChainProfile.ResolvedProfileData)
{
OutputNode callerNode = null;
- int callerRVA = 0;
+ ulong callerRVA = 0;
if (_symbolMethodMap.TryGetValue(kvpCallerCalleeCount.Key, out ISymbolDefinitionNode callerSymbol) &&
_outputInfoBuilder.NodeSymbolMap.TryGetValue(callerSymbol, out callerNode))
{
- callerRVA = _outputInfoBuilder.Sections[callerNode.SectionIndex].RVAWhenPlaced + callerNode.Offset;
+ callerRVA = _outputInfoBuilder.Sections[callerNode.SectionIndex].VirtualAddress + callerNode.Offset;
}
foreach (KeyValuePair kvpCalleeCount in kvpCallerCalleeCount.Value)
{
OutputNode calleeNode = null;
- int calleeRVA = 0;
+ ulong calleeRVA = 0;
if (_symbolMethodMap.TryGetValue(kvpCalleeCount.Key, out ISymbolDefinitionNode calleeSymbol) &&
_outputInfoBuilder.NodeSymbolMap.TryGetValue(calleeSymbol, out calleeNode))
{
- calleeRVA = _outputInfoBuilder.Sections[calleeNode.SectionIndex].RVAWhenPlaced + calleeNode.Offset;
+ calleeRVA = _outputInfoBuilder.Sections[calleeNode.SectionIndex].VirtualAddress + calleeNode.Offset;
}
+ int callerRVA32Bit = checked((int)callerRVA);
+ int calleeRVA32Bit = checked((int)calleeRVA);
+
_callInfo.Add(new CallInfo(
caller: kvpCallerCalleeCount.Key,
callerNode: callerNode,
- callerRVA: callerRVA,
+ callerRVA: callerRVA32Bit,
callee: kvpCalleeCount.Key,
calleeNode: calleeNode,
- calleeRVA: calleeRVA,
+ calleeRVA: calleeRVA32Bit,
callCount: kvpCalleeCount.Value,
- callType: GetCallType(callerNode, callerRVA, calleeNode, calleeRVA)));
+ callType: GetCallType(callerNode, callerRVA32Bit, calleeNode, calleeRVA32Bit)));
}
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs
deleted file mode 100644
index 738910d98e92e5..00000000000000
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs
+++ /dev/null
@@ -1,698 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Collections.Generic;
-using System.Collections.Immutable;
-using System.Diagnostics;
-using System.IO;
-using System.Reflection.Metadata;
-using System.Reflection.PortableExecutable;
-
-using ILCompiler.DependencyAnalysis;
-using ILCompiler.DependencyAnalysis.ReadyToRun;
-
-using Internal.TypeSystem;
-
-namespace ILCompiler.PEWriter
-{
- ///
- /// Ready-to-run PE builder combines copying the input MSIL PE executable with managed
- /// metadata and IL and adding new code and data representing the R2R JITted code and
- /// additional runtime structures (R2R header and tables).
- ///
- public sealed class R2RPEBuilder : PEBuilder
- {
- ///
- /// Number of low-order RVA bits that must match file position on Linux.
- ///
- const int RVABitsToMatchFilePos = 16;
-
- ///
- /// Name of the text section.
- ///
- public const string TextSectionName = ".text";
-
- ///
- /// Name of the relocation section.
- ///
- public const string RelocSectionName = ".reloc";
-
- ///
- /// Name of the writeable data section.
- ///
- public const string DataSectionName = ".data";
-
- ///
- /// Name of the export data section.
- ///
- public const string ExportDataSectionName = ".edata";
-
- ///
- /// Compilation target OS and architecture specification.
- ///
- private TargetDetails _target;
-
- ///
- /// Callback to retrieve the runtime function table which needs setting to the
- /// ExceptionTable PE directory entry.
- ///
- private Func _getRuntimeFunctionsTable;
-
- private class SerializedSectionData
- {
- ///
- /// Name of the section
- ///
- public string Name;
-
- ///
- /// Logical section start RVAs. When emitting R2R PE executables for Linux, we must
- /// align RVA's so that their 'RVABitsToMatchFilePos' lowest-order bits match the
- /// file position (otherwise memory mapping of the file fails and CoreCLR silently
- /// switches over to runtime JIT). PEBuilder doesn't support this today so that we
- /// must store the RVA's and post-process the produced PE by patching the section
- /// headers in the PE header.
- ///
- public int RVA;
-
- ///
- /// Pointers to the location of the raw data. Needed to allow phyical file alignment
- /// beyond 4KB. PEBuilder doesn't support this today so that we
- /// must store the RVA's and post-process the produced PE by patching the section
- /// headers in the PE header.
- ///
- public int PointerToRawData;
-
- ///
- /// Maximum of virtual and physical size for each section.
- ///
- public int RawSize;
-
- ///
- /// Whether or not the section has been serialized - if the RVA, pointer to raw data,
- /// and size have been set.
- ///
- public bool IsSerialized;
- }
-
- ///
- /// List of possible sections to emit into the output R2R executable in the order in which
- /// they are expected to be serialized. Data (aside from name) is set during serialization.
- ///
- private readonly SerializedSectionData[] _sectionData;
-
- ///
- /// R2R PE section builder & relocator.
- ///
- private readonly SectionBuilder _sectionBuilder;
-
- ///
- /// Zero-based index of the CPAOT-generated text section
- ///
- private readonly int _textSectionIndex;
-
- ///
- /// Zero-based index of the CPAOT-generated read-write data section
- ///
- private readonly int _dataSectionIndex;
-
- ///
- /// True after Write has been called; it's not possible to add further object data items past that point.
- ///
- private bool _written;
-
- ///
- /// If non-null, the PE file will be laid out such that it can naturally be mapped with a higher alignment than 4KB
- /// This is used to support loading via large pages on Linux
- ///
- private readonly int _customPESectionAlignment;
-
- ///
- /// Constructor initializes the various control structures and combines the section list.
- ///
- /// Target environment specifier
- /// PE file header builder
- /// Callback to retrieve the runtime functions table
- public R2RPEBuilder(
- TargetDetails target,
- PEHeaderBuilder peHeaderBuilder,
- ISymbolNode r2rHeaderExportSymbol,
- string outputFileSimpleName,
- Func getRuntimeFunctionsTable,
- int customPESectionAlignment,
- Func, BlobContentId> deterministicIdProvider)
- : base(peHeaderBuilder, deterministicIdProvider: deterministicIdProvider)
- {
- _target = target;
- _getRuntimeFunctionsTable = getRuntimeFunctionsTable;
-
- _sectionBuilder = new SectionBuilder(target);
-
- _textSectionIndex = _sectionBuilder.AddSection(TextSectionName, SectionCharacteristics.ContainsCode | SectionCharacteristics.MemExecute | SectionCharacteristics.MemRead, 512);
- _dataSectionIndex = _sectionBuilder.AddSection(DataSectionName, SectionCharacteristics.ContainsInitializedData | SectionCharacteristics.MemWrite | SectionCharacteristics.MemRead, 512);
-
- _customPESectionAlignment = customPESectionAlignment;
-
- if (r2rHeaderExportSymbol != null)
- {
- _sectionBuilder.AddSection(R2RPEBuilder.ExportDataSectionName, SectionCharacteristics.ContainsInitializedData | SectionCharacteristics.MemRead, 512);
- _sectionBuilder.AddExportSymbol("RTR_HEADER", 1, r2rHeaderExportSymbol);
- _sectionBuilder.SetDllNameForExportDirectoryTable(outputFileSimpleName);
- }
-
- // Always inject the relocation section to the end of section list
- _sectionBuilder.AddSection(
- R2RPEBuilder.RelocSectionName,
- SectionCharacteristics.ContainsInitializedData |
- SectionCharacteristics.MemRead |
- SectionCharacteristics.MemDiscardable,
- PEHeaderConstants.SectionAlignment);
-
- List sectionData = new List();
- foreach (SectionInfo sectionInfo in _sectionBuilder.GetSections())
- {
- sectionData.Add(new SerializedSectionData() { Name = sectionInfo.SectionName });
- }
-
- _sectionData = sectionData.ToArray();
- }
-
- public void SetCorHeader(ISymbolNode symbol, int headerSize)
- {
- _sectionBuilder.SetCorHeader(symbol, headerSize);
- }
-
- public void SetDebugDirectory(ISymbolNode symbol, int size)
- {
- _sectionBuilder.SetDebugDirectory(symbol, size);
- }
-
- public void SetWin32Resources(ISymbolNode symbol, int resourcesSize)
- {
- _sectionBuilder.SetWin32Resources(symbol, resourcesSize);
- }
-
- ///
- /// Emit a single object data item into the output R2R PE file using the section builder.
- ///
- /// Object data to emit
- /// Target section
- /// Textual name of the object data for diagnostic purposese
- /// Optional output info builder to output the data item to
- public void AddObjectData(DependencyAnalysis.ObjectNode.ObjectData objectData, ObjectNodeSection section, string name, OutputInfoBuilder outputInfoBuilder)
- {
- if (_written)
- {
- throw new InternalCompilerErrorException("Inconsistent upstream behavior - AddObjectData mustn't be called after Write");
- }
-
- int targetSectionIndex;
- switch (section.Type)
- {
- case SectionType.ReadOnly:
- // We put ReadOnly data into the text section to limit the number of sections.
- case SectionType.Executable:
- targetSectionIndex = _textSectionIndex;
- break;
-
- case SectionType.Writeable:
- targetSectionIndex = _dataSectionIndex;
- break;
-
- default:
- throw new NotImplementedException();
- }
-
- _sectionBuilder.AddObjectData(objectData, targetSectionIndex, name, outputInfoBuilder);
- }
-
- ///
- /// Add a symbol to the symbol map which defines the area of the binary between the two emitted symbols.
- /// This allows relocations (both position and size) to regions of the image. Both nodes must be in the
- /// same section and firstNode must be emitted before secondNode.
- ///
- public void AddSymbolForRange(ISymbolNode symbol, ISymbolNode firstNode, ISymbolNode secondNode)
- {
- _sectionBuilder.AddSymbolForRange(symbol, firstNode, secondNode);
- }
-
- public int GetSymbolFilePosition(ISymbolNode symbol)
- {
- return _sectionBuilder.GetSymbolFilePosition(symbol);
- }
-
- ///
- /// Emit built sections into the R2R PE file.
- ///
- /// Output stream for the final R2R PE file
- /// Timestamp to set in the PE header of the output R2R executable
- public void Write(Stream outputStream, int? timeDateStamp)
- {
- BlobBuilder outputPeFile = new BlobBuilder();
- Serialize(outputPeFile);
-
- _sectionBuilder.RelocateOutputFile(outputPeFile, Header.ImageBase, outputStream);
-
- UpdateSectionRVAs(outputStream);
-
- if (_customPESectionAlignment != 0)
- SetPEHeaderSectionAlignment(outputStream, _customPESectionAlignment);
-
- ApplyMachineOSOverride(outputStream);
-
- if (timeDateStamp.HasValue)
- SetPEHeaderTimeStamp(outputStream, timeDateStamp.Value);
-
- _written = true;
- }
-
- ///
- /// Fill in map builder section table.
- ///
- /// Object info builder to set up
- public void AddSections(OutputInfoBuilder outputInfoBuilder)
- {
- _sectionBuilder.AddSections(outputInfoBuilder);
- }
-
- ///
- /// PE header constants copied from System.Reflection.Metadata where they are
- /// sadly mostly internal or private.
- ///
- const int DosHeaderSize = 0x80;
- const int PESignatureSize = sizeof(uint);
-
- const int COFFHeaderSize =
- sizeof(short) + // Machine
- sizeof(short) + // NumberOfSections
- sizeof(int) + // TimeDateStamp:
- sizeof(int) + // PointerToSymbolTable
- sizeof(int) + // NumberOfSymbols
- sizeof(short) + // SizeOfOptionalHeader:
- sizeof(ushort); // Characteristics
-
- const int OffsetOfSectionAlign =
- sizeof(short) + // Magic
- sizeof(byte) + // MajorLinkerVersion
- sizeof(byte) + // MinorLinkerVersion
- sizeof(int) + // SizeOfCode
- sizeof(int) + // SizeOfInitializedData
- sizeof(int) + // SizeOfUninitializedData
- sizeof(int) + // AddressOfEntryPoint
- sizeof(int) + // BaseOfCode
- sizeof(long); // PE32: BaseOfData (int), ImageBase (int)
- // PE32+: ImageBase (long)
- const int OffsetOfChecksum = OffsetOfSectionAlign +
- sizeof(int) + // SectionAlignment
- sizeof(int) + // FileAlignment
- sizeof(short) + // MajorOperatingSystemVersion
- sizeof(short) + // MinorOperatingSystemVersion
- sizeof(short) + // MajorImageVersion
- sizeof(short) + // MinorImageVersion
- sizeof(short) + // MajorSubsystemVersion
- sizeof(short) + // MinorSubsystemVersion
- sizeof(int) + // Win32VersionValue
- sizeof(int) + // SizeOfImage
- sizeof(int); // SizeOfHeaders
-
- const int OffsetOfSizeOfImage = OffsetOfChecksum - 2 * sizeof(int); // SizeOfHeaders, SizeOfImage
-
- const int SectionHeaderNameSize = 8;
- const int SectionHeaderVirtualSize = SectionHeaderNameSize; // VirtualSize follows
- const int SectionHeaderRVAOffset = SectionHeaderVirtualSize + sizeof(int); // RVA Offset follows VirtualSize + 4 bytes VirtualSize
- const int SectionHeaderSizeOfRawData = SectionHeaderRVAOffset + sizeof(int); // SizeOfRawData follows RVA
- const int SectionHeaderPointerToRawDataOffset = SectionHeaderSizeOfRawData + sizeof(int); // PointerToRawData immediately follows the SizeOfRawData
-
- const int SectionHeaderSize =
- SectionHeaderNameSize +
- sizeof(int) + // VirtualSize
- sizeof(int) + // VirtualAddress
- sizeof(int) + // SizeOfRawData
- sizeof(int) + // PointerToRawData
- sizeof(int) + // PointerToRelocations
- sizeof(int) + // PointerToLineNumbers
- sizeof(short) + // NumberOfRelocations
- sizeof(short) + // NumberOfLineNumbers
- sizeof(int); // SectionCharacteristics
-
- ///
- /// On Linux, we must patch the section headers. This is because the CoreCLR runtime on Linux
- /// requires the 12-16 low-order bits of section RVAs (the number of bits corresponds to the page
- /// size) to be identical to the file offset, otherwise memory mapping of the file fails.
- /// Sadly PEBuilder in System.Reflection.Metadata doesn't support this so we must post-process
- /// the EXE by patching section headers with the correct RVA's. To reduce code variations
- /// we're performing the same transformation on Windows where it is a no-op.
- ///
- ///
- private void UpdateSectionRVAs(Stream outputStream)
- {
- int peHeaderSize =
- OffsetOfChecksum +
- sizeof(int) + // Checksum
- sizeof(short) + // Subsystem
- sizeof(short) + // DllCharacteristics
- 4 * _target.PointerSize + // SizeOfStackReserve, SizeOfStackCommit, SizeOfHeapReserve, SizeOfHeapCommit
- sizeof(int) + // LoaderFlags
- sizeof(int) + // NumberOfRvaAndSizes
- 16 * sizeof(long); // directory entries
-
- int sectionHeaderOffset = DosHeaderSize + PESignatureSize + COFFHeaderSize + peHeaderSize;
- int sectionCount = _sectionData.Length;
- for (int sectionIndex = 0; sectionIndex < sectionCount; sectionIndex++)
- {
- SerializedSectionData section = _sectionData[sectionIndex];
- if (!section.IsSerialized)
- continue;
-
- if (_customPESectionAlignment != 0)
- {
- // When _customPESectionAlignment is set, the physical and virtual sizes are the same
- byte[] sizeBytes = BitConverter.GetBytes(section.RawSize);
- Debug.Assert(sizeBytes.Length == sizeof(int));
-
- // Update VirtualSize
- {
- outputStream.Seek(sectionHeaderOffset + SectionHeaderSize * sectionIndex + SectionHeaderVirtualSize, SeekOrigin.Begin);
- outputStream.Write(sizeBytes, 0, sizeBytes.Length);
- }
- // Update SizeOfRawData
- {
- outputStream.Seek(sectionHeaderOffset + SectionHeaderSize * sectionIndex + SectionHeaderSizeOfRawData, SeekOrigin.Begin);
- outputStream.Write(sizeBytes, 0, sizeBytes.Length);
- }
- }
-
- // Update RVAs
- {
- outputStream.Seek(sectionHeaderOffset + SectionHeaderSize * sectionIndex + SectionHeaderRVAOffset, SeekOrigin.Begin);
- byte[] rvaBytes = BitConverter.GetBytes(section.RVA);
- Debug.Assert(rvaBytes.Length == sizeof(int));
- outputStream.Write(rvaBytes, 0, rvaBytes.Length);
- }
-
- // Update pointer to raw data
- {
- outputStream.Seek(sectionHeaderOffset + SectionHeaderSize * sectionIndex + SectionHeaderPointerToRawDataOffset, SeekOrigin.Begin);
- byte[] rawDataBytesBytes = BitConverter.GetBytes(section.PointerToRawData);
- Debug.Assert(rawDataBytesBytes.Length == sizeof(int));
- outputStream.Write(rawDataBytesBytes, 0, rawDataBytesBytes.Length);
- }
- }
-
- // Patch SizeOfImage to point past the end of the last section
- SerializedSectionData lastSection = null;
- for (int i = sectionCount - 1; i >= 0; i--)
- {
- if (_sectionData[i].IsSerialized)
- {
- lastSection = _sectionData[i];
- break;
- }
- }
- Debug.Assert(lastSection != null);
- outputStream.Seek(DosHeaderSize + PESignatureSize + COFFHeaderSize + OffsetOfSizeOfImage, SeekOrigin.Begin);
- int sizeOfImage = AlignmentHelper.AlignUp(lastSection.RVA + lastSection.RawSize, Header.SectionAlignment);
- byte[] sizeOfImageBytes = BitConverter.GetBytes(sizeOfImage);
- Debug.Assert(sizeOfImageBytes.Length == sizeof(int));
- outputStream.Write(sizeOfImageBytes, 0, sizeOfImageBytes.Length);
- }
-
- ///
- /// Set PE header section alignment, for alignments not supported by the System.Reflection.Metadata
- ///
- /// Output stream representing the R2R PE executable
- /// Timestamp to set in the R2R PE header
- private void SetPEHeaderSectionAlignment(Stream outputStream, int customAlignment)
- {
- outputStream.Seek(DosHeaderSize + PESignatureSize + COFFHeaderSize + OffsetOfSectionAlign, SeekOrigin.Begin);
- byte[] alignBytes = BitConverter.GetBytes(customAlignment);
- Debug.Assert(alignBytes.Length == sizeof(int));
- outputStream.Write(alignBytes, 0, alignBytes.Length);
- }
-
- ///
- /// TODO: System.Reflection.Metadata doesn't currently support OS machine overrides.
- /// We cannot directly pass the xor-ed target machine to PEHeaderBuilder because it
- /// may incorrectly detect 32-bitness and emit wrong OptionalHeader.Magic. Therefore
- /// we create the executable using the raw Machine ID and apply the override as the
- /// last operation before closing the file.
- ///
- /// Output stream representing the R2R PE executable
- private void ApplyMachineOSOverride(Stream outputStream)
- {
- byte[] patchedTargetMachine = BitConverter.GetBytes(
- (ushort)unchecked((ushort)Header.Machine ^ (ushort)_target.MachineOSOverrideFromTarget()));
- Debug.Assert(patchedTargetMachine.Length == sizeof(ushort));
-
- outputStream.Seek(DosHeaderSize + PESignatureSize, SeekOrigin.Begin);
- outputStream.Write(patchedTargetMachine, 0, patchedTargetMachine.Length);
- }
-
- ///
- /// Set PE header timestamp in the output R2R image to a given value.
- ///
- /// Output stream representing the R2R PE executable
- /// Timestamp to set in the R2R PE header
- private void SetPEHeaderTimeStamp(Stream outputStream, int timeDateStamp)
- {
- byte[] patchedTimestamp = BitConverter.GetBytes(timeDateStamp);
- int seekSize =
- DosHeaderSize +
- PESignatureSize +
- sizeof(short) + // Machine
- sizeof(short); // NumberOfSections
-
- outputStream.Seek(seekSize, SeekOrigin.Begin);
- outputStream.Write(patchedTimestamp, 0, patchedTimestamp.Length);
- }
-
- ///
- /// Copy all directory entries and the address of entry point, relocating them along the way.
- ///
- protected override PEDirectoriesBuilder GetDirectories()
- {
- PEDirectoriesBuilder builder = new PEDirectoriesBuilder();
-
- _sectionBuilder.UpdateDirectories(builder);
-
- if (_getRuntimeFunctionsTable != null)
- {
- RuntimeFunctionsTableNode runtimeFunctionsTable = _getRuntimeFunctionsTable();
- if (runtimeFunctionsTable.TableSizeExcludingSentinel != 0)
- {
- builder.ExceptionTable = new DirectoryEntry(
- relativeVirtualAddress: _sectionBuilder.GetSymbolRVA(runtimeFunctionsTable),
- size: runtimeFunctionsTable.TableSizeExcludingSentinel);
- }
- }
-
- return builder;
- }
-
- ///
- /// Provide an array of sections for the PEBuilder to use.
- ///
- protected override ImmutableArray CreateSections()
- {
- ImmutableArray.Builder sectionListBuilder = ImmutableArray.CreateBuilder();
- foreach (SectionInfo sectionInfo in _sectionBuilder.GetSections())
- {
- // Only include sections that have content.
- if (!_sectionBuilder.HasContent(sectionInfo.SectionName))
- continue;
-
- sectionListBuilder.Add(new Section(sectionInfo.SectionName, sectionInfo.Characteristics));
- }
-
- return sectionListBuilder.ToImmutable();
- }
-
- ///
- /// Output the section with a given name.
- ///
- /// Section name
- /// RVA and file location where the section will be put
- /// Blob builder representing the section data
- protected override BlobBuilder SerializeSection(string name, SectionLocation location)
- {
- BlobBuilder sectionDataBuilder = null;
- int sectionStartRva = location.RelativeVirtualAddress;
-
- int outputSectionIndex = _sectionData.Length - 1;
- while (outputSectionIndex >= 0 && _sectionData[outputSectionIndex].Name != name)
- {
- outputSectionIndex--;
- }
-
- if (outputSectionIndex < 0)
- throw new ArgumentException($"Unknown section name: '{name}'", nameof(name));
-
- Debug.Assert(_sectionBuilder.HasContent(name));
- SerializedSectionData outputSection = _sectionData[outputSectionIndex];
- SerializedSectionData previousSection = null;
- for (int i = outputSectionIndex - 1; i >= 0; i--)
- {
- if (_sectionData[i].IsSerialized)
- {
- previousSection = _sectionData[i];
- break;
- }
- }
-
- int injectedPadding = 0;
- if (_customPESectionAlignment != 0)
- {
- if (previousSection is not null)
- {
- sectionStartRva = Math.Max(sectionStartRva, previousSection.RVA + previousSection.RawSize);
- }
-
- int newSectionStartRva = AlignmentHelper.AlignUp(sectionStartRva, _customPESectionAlignment);
- int newSectionPointerToRawData = AlignmentHelper.AlignUp(location.PointerToRawData, _customPESectionAlignment);
- if (newSectionPointerToRawData > location.PointerToRawData)
- {
- sectionDataBuilder = new BlobBuilder();
- injectedPadding = newSectionPointerToRawData - location.PointerToRawData;
- sectionDataBuilder.WriteBytes(1, injectedPadding);
- }
- sectionStartRva = newSectionStartRva;
- location = new SectionLocation(sectionStartRva, newSectionPointerToRawData);
- }
-
- if (!_target.IsWindows)
- {
- const int RVAAlign = 1 << RVABitsToMatchFilePos;
- if (previousSection is not null)
- {
- sectionStartRva = Math.Max(sectionStartRva, previousSection.RVA + previousSection.RawSize);
-
- // when assembly is stored in a singlefile bundle, an additional skew is introduced
- // as the streams inside the bundle are not necessarily page aligned as we do not
- // know the actual page size on the target system.
- // We may need one page gap of unused VA space before the next section starts.
- // We will assume the page size is <= RVAAlign
- sectionStartRva += RVAAlign;
- }
-
- sectionStartRva = AlignmentHelper.AlignUp(sectionStartRva, RVAAlign);
-
- int rvaAdjust = (location.PointerToRawData - sectionStartRva) & (RVAAlign - 1);
- sectionStartRva += rvaAdjust;
- location = new SectionLocation(sectionStartRva, location.PointerToRawData);
- }
-
- outputSection.RVA = sectionStartRva;
- outputSection.PointerToRawData = location.PointerToRawData;
-
- BlobBuilder extraData = _sectionBuilder.SerializeSection(name, location);
- Debug.Assert(extraData != null);
- if (sectionDataBuilder == null)
- {
- // See above - there's a bug due to which LinkSuffix to an empty BlobBuilder screws up the blob content.
- sectionDataBuilder = extraData;
- }
- else
- {
- sectionDataBuilder.LinkSuffix(extraData);
- }
-
- int sectionRawSize = sectionDataBuilder.Count - injectedPadding;
-
- if (_customPESectionAlignment != 0)
- {
- // Align the end of the section to the padding offset
- int count = AlignmentHelper.AlignUp(sectionRawSize, _customPESectionAlignment);
- sectionDataBuilder.WriteBytes(0, count - sectionRawSize);
- sectionRawSize = count;
- }
-
- outputSection.RawSize = sectionRawSize;
- outputSection.IsSerialized = true;
-
- return sectionDataBuilder;
- }
- }
-
- ///
- /// Simple helper for filling in PE header information.
- ///
- static class PEHeaderProvider
- {
- ///
- /// Fill in PE header information into a PEHeaderBuilder used by PEBuilder.
- ///
- /// Targeting subsystem
- /// Target architecture to set in the header
- public static PEHeaderBuilder Create(Subsystem subsystem, TargetDetails target, ulong imageBase)
- {
- bool is64BitTarget = target.PointerSize == sizeof(long);
-
- Characteristics imageCharacteristics = Characteristics.ExecutableImage | Characteristics.Dll;
- imageCharacteristics |= is64BitTarget ? Characteristics.LargeAddressAware : Characteristics.Bit32Machine;
-
- int fileAlignment = 0x200;
- bool isWindowsOr32bit = target.IsWindows || !is64BitTarget;
- if (isWindowsOr32bit)
- {
- // To minimize wasted VA space on 32-bit systems (regardless of OS),
- // align file to page boundaries (presumed to be 4K)
- //
- // On Windows we use 4K file alignment (regardless of ptr size),
- // per requirements of memory mapping API (MapViewOfFile3, et al).
- // The alternative could be using the same approach as on Unix, but that would result in PEs
- // incompatible with OS loader. While that is not a problem on Unix, we do not want that on Windows.
- fileAlignment = 0x1000;
- }
-
- int sectionAlignment = 0x1000;
- if (!isWindowsOr32bit)
- {
- // On 64bit Linux, we must match the bottom 12 bits of section RVA's to their file offsets. For this reason
- // we need the same alignment for both.
- //
- // In addition to that we specify section RVAs to be at least 64K apart, which is > page on most systems.
- // It ensures that the sections will not overlap when mapped from a singlefile bundle, which introduces a sub-page skew.
- //
- // Such format would not be accepted by OS loader on Windows, but it is not a problem on Unix.
- sectionAlignment = fileAlignment;
- }
-
- // Without NxCompatible the PE executable cannot execute on Windows ARM64
- DllCharacteristics dllCharacteristics =
- DllCharacteristics.DynamicBase |
- DllCharacteristics.NxCompatible |
- DllCharacteristics.TerminalServerAware;
-
- if (is64BitTarget)
- {
- dllCharacteristics |= DllCharacteristics.HighEntropyVirtualAddressSpace;
- }
- else
- {
- dllCharacteristics |= DllCharacteristics.NoSeh;
- }
-
- return new PEHeaderBuilder(
- machine: target.MachineFromTarget(),
- sectionAlignment: sectionAlignment,
- fileAlignment: fileAlignment,
- imageBase: imageBase,
- majorLinkerVersion: PEHeaderConstants.MajorLinkerVersion,
- minorLinkerVersion: PEHeaderConstants.MinorLinkerVersion,
- majorOperatingSystemVersion: PEHeaderConstants.MajorOperatingSystemVersion,
- minorOperatingSystemVersion: PEHeaderConstants.MinorOperatingSystemVersion,
- majorImageVersion: PEHeaderConstants.MajorImageVersion,
- minorImageVersion: PEHeaderConstants.MinorImageVersion,
- majorSubsystemVersion: PEHeaderConstants.MajorSubsystemVersion,
- minorSubsystemVersion: PEHeaderConstants.MinorSubsystemVersion,
- subsystem: subsystem,
- dllCharacteristics: dllCharacteristics,
- imageCharacteristics: imageCharacteristics,
- sizeOfStackReserve: (is64BitTarget ? PE64HeaderConstants.SizeOfStackReserve : PE32HeaderConstants.SizeOfStackReserve),
- sizeOfStackCommit: (is64BitTarget ? PE64HeaderConstants.SizeOfStackCommit : PE32HeaderConstants.SizeOfStackCommit),
- sizeOfHeapReserve: (is64BitTarget ? PE64HeaderConstants.SizeOfHeapReserve : PE32HeaderConstants.SizeOfHeapReserve),
- sizeOfHeapCommit: (is64BitTarget ? PE64HeaderConstants.SizeOfHeapCommit : PE32HeaderConstants.SizeOfHeapCommit));
- }
- }
-}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/RelocationHelper.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/RelocationHelper.cs
deleted file mode 100644
index 6a2501482ab786..00000000000000
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/RelocationHelper.cs
+++ /dev/null
@@ -1,549 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Collections.Generic;
-using System.Collections.Immutable;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Reflection;
-using System.Reflection.Metadata;
-using System.Reflection.Metadata.Ecma335;
-using System.Reflection.PortableExecutable;
-using System.Runtime.CompilerServices;
-
-using ILCompiler.DependencyAnalysis;
-
-namespace ILCompiler.PEWriter
-{
- ///
- /// Helper used to copy the produced PE file from a BlobBuilder to an output stream,
- /// applying relocations along the way. It's mostly a linear copier that occasionally stops,
- /// patches a few bytes and then continues.
- ///
- class RelocationHelper
- {
- ///
- /// Maximum number of bytes to process for any relocation type.
- ///
- const int LongestRelocationBytes = 8;
-
- ///
- /// Enumerator of blobs within the blob builder.
- ///
- private BlobBuilder.Blobs _peFileBlobs;
-
- ///
- /// Blob length is used at the end to verify that the relocated file hasn't changed length.
- ///
- private int _peFileLength;
-
- ///
- /// Backing array for the ArraySegment of the current blob.
- ///
- private byte[] _currentBlob;
-
- ///
- /// Current offset within the active blob.
- ///
- private int _blobOffset;
-
- ///
- /// Remaining number of bytes unprocessed in the active blob.
- ///
- private int _remainingLength;
-
- ///
- /// Preferred image load address is needed to properly fix up absolute relocation types.
- ///
- private ulong _defaultImageBase;
-
- ///
- /// Output stream to receive the relocated file
- ///
- private Stream _outputStream;
-
- ///
- /// Current position in the output file
- ///
- private int _outputFilePos;
-
- ///
- /// Buffer to hold data for the currently processed relocation.
- ///
- private byte[] _relocationBuffer = new byte[LongestRelocationBytes];
-
- ///
- /// Relocation helper stores the output stream and initializes the PE blob builder enumerator.
- ///
- /// Output stream for the relocated PE file
- /// PE file blob builder
- public RelocationHelper(Stream outputStream, ulong defaultImageBase, BlobBuilder peFileBuilder)
- {
- _outputStream = outputStream;
- _outputFilePos = 0;
-
- _defaultImageBase = defaultImageBase;
-
- _peFileLength = peFileBuilder.Count;
- _peFileBlobs = peFileBuilder.GetBlobs();
- FetchNextBlob();
- }
-
- ///
- /// Copy data from the PE file builder to the output stream, stopping at given file position.
- ///
- /// Output PE file position to stop at
- public void CopyToFilePosition(int filePos)
- {
- CopyBytesToOutput(filePos - _outputFilePos);
- }
-
- ///
- /// Advance output position in case of external writes to the output stream.
- ///
- /// Number of bytes advance output by
- public void AdvanceOutputPos(int delta)
- {
- _outputFilePos += delta;
- }
-
- ///
- /// Copy all unprocessed data (after the last relocation) into the output file
- /// without any further modifications.
- ///
- public void CopyRestOfFile()
- {
- do
- {
- CopyBytesToOutput(_remainingLength);
- }
- while (TryFetchNextBlob());
-
- if (_outputFilePos != _peFileLength)
- {
- // Input / output PE file length mismatch - internal error in the relocator
- throw new BadImageFormatException();
- }
- }
-
- ///
- /// Process a single relocation by copying the required number of bytes into a
- /// buffer, applying the relocation and writing it to the output file.
- ///
- /// Relocation type to process
- /// RVA representing the address to relocate
- /// RVA representing the relocation target
- public void ProcessRelocation(RelocType relocationType, int sourceRVA, int targetRVA, int filePosWhenPlaced)
- {
- int relocationLength = 0;
- long delta = 0;
-
- switch (relocationType)
- {
- case RelocType.IMAGE_REL_BASED_ABSOLUTE:
- // No relocation
- return;
-
- case RelocType.IMAGE_REL_BASED_HIGHLOW:
- {
- relocationLength = 4;
- delta = unchecked(targetRVA + (int)_defaultImageBase);
- break;
- }
-
- case RelocType.IMAGE_REL_BASED_ADDR32NB:
- case RelocType.IMAGE_REL_SYMBOL_SIZE:
- {
- relocationLength = 4;
- delta = targetRVA;
- break;
- }
-
- case RelocType.IMAGE_REL_BASED_REL32:
- {
- relocationLength = 4;
- delta = targetRVA - sourceRVA - 4;
- break;
- }
-
- case RelocType.IMAGE_REL_BASED_DIR64:
- {
- relocationLength = 8;
- delta = unchecked(targetRVA + (long)_defaultImageBase);
- break;
- }
-
- case RelocType.IMAGE_REL_BASED_THUMB_MOV32:
- {
- relocationLength = 8;
- delta = unchecked(targetRVA + (int)_defaultImageBase);
- break;
- }
-
- case RelocType.IMAGE_REL_BASED_THUMB_MOV32_PCREL:
- {
- relocationLength = 8;
- const uint offsetCorrection = 12;
- delta = unchecked(targetRVA - (sourceRVA + offsetCorrection));
- break;
- }
-
- case RelocType.IMAGE_REL_BASED_THUMB_BRANCH24:
- {
- relocationLength = 4;
- delta = targetRVA - sourceRVA - 4;
- break;
- }
-
- case RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21:
- {
- relocationLength = 4;
- int sourcePageRVA = sourceRVA & ~0xfff;
- // Page delta always fits in 21 bits as long as we use 4-byte RVAs
- delta = ((targetRVA - sourcePageRVA) >> 12) & 0x1f_ffff;
- break;
- }
-
- case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A:
- {
- relocationLength = 4;
- delta = targetRVA & 0xfff;
- break;
- }
-
- case RelocType.IMAGE_REL_FILE_ABSOLUTE:
- {
- relocationLength = 4;
- delta = filePosWhenPlaced;
- break;
- }
-
- case RelocType.IMAGE_REL_BASED_LOONGARCH64_PC:
- {
- relocationLength = 8;
- delta = (int)(targetRVA - (sourceRVA & ~0xfff) + ((targetRVA & 0x800) << 1));
- break;
- }
- case RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR:
- {
- relocationLength = 8;
- delta = targetRVA - sourceRVA;
- break;
- }
-
- case RelocType.IMAGE_REL_BASED_RISCV64_PC:
- {
- relocationLength = 8;
- delta = targetRVA - sourceRVA;
- break;
- }
-
- default:
- throw new NotSupportedException();
- }
-
- if (relocationLength > 0)
- {
- CopyBytesToBuffer(_relocationBuffer, relocationLength);
- unsafe
- {
- fixed (byte *bufferContent = _relocationBuffer)
- {
- long value = Relocation.ReadValue(relocationType, bufferContent);
- // Supporting non-zero values for ARM64 would require refactoring this function
- if (((relocationType == RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21) ||
- (relocationType == RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A) ||
- (relocationType == RelocType.IMAGE_REL_BASED_LOONGARCH64_PC) ||
- (relocationType == RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR) ||
- (relocationType == RelocType.IMAGE_REL_BASED_RISCV64_PC)
- ) && (value != 0))
- {
- throw new NotSupportedException();
- }
-
- Relocation.WriteValue(relocationType, bufferContent, unchecked(value + delta));
- }
- }
-
- // Write the relocated bytes to the output file
- _outputStream.Write(_relocationBuffer, 0, relocationLength);
- _outputFilePos += relocationLength;
- }
- }
-
- ///
- /// Read next blob from the PE blob builder. Throw exception of no more data is available
- /// (indicates an inconsistent PE file).
- ///
- private void FetchNextBlob()
- {
- if (!TryFetchNextBlob())
- {
- throw new BadImageFormatException();
- }
- }
-
- ///
- /// Try to fetch next blob from the PE blob builder, return false on EOF.
- ///
- /// True when another blob was successfully fetched, false on EOF
- private bool TryFetchNextBlob()
- {
- if (!_peFileBlobs.MoveNext())
- {
- return false;
- }
-
- ArraySegment blobContent = _peFileBlobs.Current.GetBytes();
- _currentBlob = blobContent.Array;
- _blobOffset = blobContent.Offset;
- _remainingLength = blobContent.Count;
- return true;
- }
-
- ///
- /// Copy a given number of bytes from the PE blob builder to the output stream.
- ///
- /// Number of bytes to copy
- private void CopyBytesToOutput(int length)
- {
- Debug.Assert(length >= 0);
-
- while (length > 0)
- {
- if (_remainingLength == 0)
- {
- FetchNextBlob();
- }
-
- int part = Math.Min(length, _remainingLength);
- _outputStream.Write(_currentBlob, _blobOffset, part);
- _outputFilePos += part;
- _blobOffset += part;
- _remainingLength -= part;
- length -= part;
- }
- }
-
- ///
- /// Copy bytes from the PE blob builder to the given byte buffer.
- ///
- /// Buffer to fill in from the blob builder
- /// Number of bytes to copy to the buffer
- public void CopyBytesToBuffer(byte[] buffer, int count)
- {
- int offset = 0;
- while (offset < count)
- {
- if (_remainingLength == 0)
- {
- FetchNextBlob();
- }
-
- int part = Math.Min(count - offset, _remainingLength);
- Array.Copy(
- sourceArray: _currentBlob,
- sourceIndex: _blobOffset,
- destinationArray: buffer,
- destinationIndex: offset,
- length: part);
-
- _blobOffset += part;
- _remainingLength -= part;
- offset += part;
- }
- }
-
- ///
- /// Extract the 24-bit rel offset from bl instruction
- ///
- /// Byte buffer containing the instruction to analyze
- /// Offset of the instruction within the buffer
- private static unsafe int GetThumb2BlRel24(byte[] bytes, int offset)
- {
- uint opcode0 = BitConverter.ToUInt16(bytes, offset + 0);
- uint opcode1 = BitConverter.ToUInt16(bytes, offset + 2);
-
- uint s = opcode0 >> 10;
- uint j2 = opcode1 >> 11;
- uint j1 = opcode1 >> 13;
-
- uint ret =
- ((s << 24) & 0x1000000) |
- (((j1 ^ s ^ 1) << 23) & 0x0800000) |
- (((j2 ^ s ^ 1) << 22) & 0x0400000) |
- ((opcode0 << 12) & 0x03FF000) |
- ((opcode1 << 1) & 0x0000FFE);
-
- // Sign-extend and return
- return (int)((ret << 7) >> 7);
- }
-
- ///
- /// Patch a MOVW / MOVT Thumb2 instruction by updating its 16-bit immediate operand to imm16.
- ///
- /// Immediate 16-bit operand to inject into the instruction
- /// Byte array containing the instruction to patch
- /// Offset of the MOVW / MOVT instruction
- private static void PutThumb2Imm16(ushort imm16, byte[] bytes, int offset)
- {
- const ushort Mask1 = 0xf000;
- const ushort Val1 = (Mask1 >> 12);
- const ushort Mask2 = 0x0800;
- const ushort Val2 = (Mask2 >> 1);
- const ushort Mask3 = 0x0700;
- const ushort Val3 = (Mask3 << 4);
- const ushort Mask4 = 0x00ff;
- const ushort Val4 = (Mask4 << 0);
- const ushort Val = Val1 | Val2 | Val3 | Val4;
-
- ushort opcode0 = BitConverter.ToUInt16(bytes, offset);
- ushort opcode1 = BitConverter.ToUInt16(bytes, offset + 2);
-
- opcode0 &= unchecked((ushort)~Val);
- opcode0 |= unchecked((ushort)(((imm16 & Mask1) >> 12) | ((imm16 & Mask2) >> 1) | ((imm16 & Mask3) << 4) | ((imm16 & Mask4) << 0)));
-
- WriteUInt16(opcode0, bytes, offset);
- WriteUInt16(opcode1, bytes, offset + 2);
- }
-
- ///
- /// Decode the 32-bit immediate operand from a MOVW / MOVT instruction pair (8 bytes total).
- ///
- /// Byte array containing the 8-byte sequence MOVW - MOVT
- private static int GetThumb2Mov32(byte[] bytes)
- {
- Debug.Assert(((uint)BitConverter.ToUInt16(bytes, 0) & 0xFBF0) == 0xF240);
- Debug.Assert(((uint)BitConverter.ToUInt16(bytes, 4) & 0xFBF0) == 0xF2C0);
-
- return (int)GetThumb2Imm16(bytes, 0) + ((int)(GetThumb2Imm16(bytes, 4) << 16));
- }
-
- ///
- /// Decode the 16-bit immediate operand from a MOVW / MOVT instruction.
- ///
- private static ushort GetThumb2Imm16(byte[] bytes, int offset)
- {
- uint opcode0 = BitConverter.ToUInt16(bytes, offset);
- uint opcode1 = BitConverter.ToUInt16(bytes, offset + 2);
- uint result =
- ((opcode0 << 12) & 0xf000) |
- ((opcode0 << 1) & 0x0800) |
- ((opcode1 >> 4) & 0x0700) |
- ((opcode1 >> 0) & 0x00ff);
- return (ushort)result;
- }
-
- ///
- /// Returns whether the offset fits into bl instruction
- ///
- /// Immediate operand to check.
- private static bool FitsInThumb2BlRel24(int imm24)
- {
- return ((imm24 << 7) >> 7) == imm24;
- }
-
- ///
- /// Deposit the 24-bit rel offset into bl instruction
- ///
- /// Immediate operand to inject into the instruction
- /// Byte buffer containing the BL instruction to patch
- /// Offset of the instruction within the buffer
- private static void PutThumb2BlRel24(int imm24, byte[] bytes, int offset)
- {
- // Verify that we got a valid offset
- Debug.Assert(FitsInThumb2BlRel24(imm24));
-
- // Ensure that the ThumbBit is not set on the offset
- // as it cannot be encoded.
- Debug.Assert((imm24 & 1/*THUMB_CODE*/) == 0);
-
- ushort opcode0 = BitConverter.ToUInt16(bytes, 0);
- ushort opcode1 = BitConverter.ToUInt16(bytes, 2);
- opcode0 &= 0xF800;
- opcode1 &= 0xD000;
-
- uint s = (unchecked((uint)imm24) & 0x1000000) >> 24;
- uint j1 = ((unchecked((uint)imm24) & 0x0800000) >> 23) ^ s ^ 1;
- uint j2 = ((unchecked((uint)imm24) & 0x0400000) >> 22) ^ s ^ 1;
-
- opcode0 |= (ushort)(((unchecked((uint)imm24) & 0x03FF000) >> 12) | (s << 10));
- opcode1 |= (ushort)(((unchecked((uint)imm24) & 0x0000FFE) >> 1) | (j1 << 13) | (j2 << 11));
-
- WriteUInt16(opcode0, bytes, offset + 0);
- WriteUInt16(opcode1, bytes, offset + 2);
-
- Debug.Assert(GetThumb2BlRel24(bytes, 0) == imm24);
- }
-
- ///
- /// Helper to write 16-bit value to a byte array.
- ///
- /// Value to write to the byte array
- /// Target byte array
- /// Offset in the array
- static void WriteUInt16(ushort value, byte[] bytes, int offset)
- {
- bytes[offset + 0] = unchecked((byte)value);
- bytes[offset + 1] = (byte)(value >> 8);
- }
-
- ///
- /// Helper to write 32-bit value to a byte array.
- ///
- /// Value to write to the byte array
- /// Target byte array
- /// Offset in the array
- static void WriteUInt32(uint value, byte[] bytes, int offset)
- {
- bytes[offset + 0] = unchecked((byte)(value >> 0));
- bytes[offset + 1] = unchecked((byte)(value >> 8));
- bytes[offset + 2] = unchecked((byte)(value >> 16));
- bytes[offset + 3] = unchecked((byte)(value >> 24));
- }
-
- ///
- /// We use the same byte encoding for signed and unsigned 32-bit values
- /// so this method just forwards to WriteUInt32.
- ///
- /// Value to write to the byte array
- /// Target byte array
- /// Offset in the array
- static void WriteInt32(int value, byte[] bytes, int offset)
- {
- WriteUInt32(unchecked((uint)value), bytes, offset);
- }
-
- ///
- /// Helper to write 64-bit value to a byte array.
- ///
- /// Value to write to the byte array
- /// Target byte array
- /// Offset in the array
- static void WriteUInt64(ulong value, byte[] bytes, int offset)
- {
- bytes[offset + 0] = unchecked((byte)(value >> 0));
- bytes[offset + 1] = unchecked((byte)(value >> 8));
- bytes[offset + 2] = unchecked((byte)(value >> 16));
- bytes[offset + 3] = unchecked((byte)(value >> 24));
- bytes[offset + 4] = unchecked((byte)(value >> 32));
- bytes[offset + 5] = unchecked((byte)(value >> 40));
- bytes[offset + 6] = unchecked((byte)(value >> 48));
- bytes[offset + 7] = unchecked((byte)(value >> 56));
- }
-
- ///
- /// We use the same byte encoding for signed and unsigned 64-bit values
- /// so this method just forwards to WriteUInt64.
- ///
- /// Value to write to the byte array
- /// Target byte array
- /// Offset in the array
- static void WriteInt64(long value, byte[] bytes, int offset)
- {
- WriteUInt64(unchecked((ulong)value), bytes, offset);
- }
- }
-}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs
deleted file mode 100644
index f3ebd11aae868c..00000000000000
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs
+++ /dev/null
@@ -1,965 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Reflection.Metadata;
-using System.Reflection.PortableExecutable;
-
-using ILCompiler.DependencyAnalysis;
-
-using Internal.Text;
-using Internal.TypeSystem;
-
-namespace ILCompiler.PEWriter
-{
- ///
- /// For a given symbol, this structure represents its target section and offset
- /// within the containing section.
- ///
- public struct SymbolTarget
- {
- ///
- /// Index of the section holding the symbol target.
- ///
- public readonly int SectionIndex;
-
- ///
- /// Offset of the symbol within the section.
- ///
- public readonly int Offset;
-
- public readonly int Size;
-
- ///
- /// Initialize symbol target with section and offset.
- ///
- /// Section index where the symbol target resides
- /// Offset of the target within the section
- public SymbolTarget(int sectionIndex, int offset, int size)
- {
- SectionIndex = sectionIndex;
- Offset = offset;
- Size = size;
- }
- }
-
- ///
- /// After placing an ObjectData within a section, we use this helper structure to record
- /// its relocation information for the final relocation pass.
- ///
- public struct PlacedObjectData
- {
- ///
- /// Offset of the ObjectData block within the section
- ///
- public readonly int Offset;
-
- ///
- /// Object data representing an array of relocations to fix up.
- ///
- public readonly ObjectNode.ObjectData Data;
-
- ///
- /// Array of relocations that need fixing up within the block.
- ///
- public Relocation[] Relocs => Data.Relocs;
-
- ///
- /// Initialize the list of relocations for a given object data item within the section.
- ///
- /// Offset within the section
- /// Object data block containing the list of relocations to fix up
- public PlacedObjectData(int offset, ObjectNode.ObjectData data)
- {
- Offset = offset;
- Data = data;
- }
- }
-
- public struct SectionInfo
- {
- public readonly string SectionName;
- public readonly SectionCharacteristics Characteristics;
-
- public SectionInfo(string sectionName, SectionCharacteristics characteristics)
- {
- SectionName = sectionName;
- Characteristics = characteristics;
- }
- }
-
- ///
- /// Section represents a contiguous area of code or data with the same characteristics.
- ///
- public class Section
- {
- ///
- /// Index within the internal section table used by the section builder
- ///
- public readonly int Index;
-
- ///
- /// Section name
- ///
- public readonly string Name;
-
- ///
- /// Section characteristics
- ///
- public readonly SectionCharacteristics Characteristics;
-
- ///
- /// Alignment to apply when combining multiple builder sections into a single
- /// physical output section (typically when combining hot and cold code into
- /// the output code section).
- ///
- public readonly int Alignment;
-
- ///
- /// Blob builder representing the section content.
- ///
- public readonly BlobBuilder Content;
-
- ///
- /// All blocks requiring relocation resolution within the section
- ///
- public readonly List PlacedObjectDataToRelocate;
-
- ///
- /// RVA gets filled in during section serialization.
- ///
- public int RVAWhenPlaced;
-
- ///
- /// Output file position gets filled in during section serialization.
- ///
- public int FilePosWhenPlaced;
-
- ///
- /// Construct a new session object.
- ///
- /// Zero-based section index
- /// Section name
- /// Section characteristics
- /// Alignment for combining multiple logical sections
- public Section(int index, string name, SectionCharacteristics characteristics, int alignment)
- {
- Index = index;
- Name = name;
- Characteristics = characteristics;
- Alignment = alignment;
- Content = new BlobBuilder();
- PlacedObjectDataToRelocate = new List();
- RVAWhenPlaced = 0;
- FilePosWhenPlaced = 0;
- }
- }
-
- ///
- /// This class represents a single export symbol in the PE file.
- ///
- public class ExportSymbol
- {
- ///
- /// Symbol identifier
- ///
- public readonly string Name;
-
- ///
- /// When placed into the export section, RVA of the symbol name gets updated.
- ///
- public int NameRVAWhenPlaced;
-
- ///
- /// Export symbol ordinal
- ///
- public readonly int Ordinal;
-
- ///
- /// Symbol to export
- ///
- public readonly ISymbolNode Symbol;
-
- ///
- /// Construct the export symbol instance filling in its arguments
- ///
- /// Export symbol identifier
- /// Ordinal ID of the export symbol
- /// Symbol to export
- public ExportSymbol(string name, int ordinal, ISymbolNode symbol)
- {
- Name = name;
- Ordinal = ordinal;
- Symbol = symbol;
- }
- }
-
- ///
- /// Section builder is capable of accumulating blocks, using them to lay out sections
- /// and relocate the produced executable according to the block relocation information.
- ///
- public class SectionBuilder
- {
- ///
- /// Target OS / architecture.
- ///
- TargetDetails _target;
-
- ///
- /// Map from symbols to their target sections and offsets.
- ///
- Dictionary _symbolMap;
-
- ///
- /// List of sections defined in the builder
- ///
- List _sections;
-
- ///
- /// Symbols to export from the PE file.
- ///
- List _exportSymbols;
-
- ///
- /// Optional symbol representing an entrypoint override.
- ///
- ISymbolNode _entryPointSymbol;
-
- ///
- /// Export directory entry when available.
- ///
- DirectoryEntry _exportDirectoryEntry;
-
- ///
- /// Directory entry representing the extra relocation records.
- ///
- DirectoryEntry _relocationDirectoryEntry;
-
- ///
- /// Symbol representing the ready-to-run COR (MSIL) header table.
- /// Only present in single-file R2R executables. Composite R2R
- /// executables don't have a COR header and locate the ReadyToRun
- /// header directly using the well-known export symbol RTR_HEADER.
- ///
- ISymbolNode _corHeaderSymbol;
-
- ///
- /// Size of the ready-to-run header table in bytes.
- ///
- int _corHeaderSize;
-
- ///
- /// Symbol representing the debug directory.
- ///
- ISymbolNode _debugDirectorySymbol;
-
- ///
- /// Size of the debug directory in bytes.
- ///
- int _debugDirectorySize;
-
- ///
- /// Symbol representing the start of the win32 resources
- ///
- ISymbolNode _win32ResourcesSymbol;
-
- ///
- /// Size of the win32 resources
- ///
- int _win32ResourcesSize;
-
- ///
- /// Padding 4-byte sequence to use in code section. Typically corresponds
- /// to some interrupt to be thrown at "invalid" IP addresses.
- ///
- uint _codePadding;
-
- ///
- /// For PE files with exports, this is the "DLL name" string to store in the export directory table.
- ///
- string _dllNameForExportDirectoryTable;
-
- ///
- /// Construct an empty section builder without any sections or blocks.
- ///
- public SectionBuilder(TargetDetails target)
- {
- _target = target;
- _symbolMap = new Dictionary();
- _sections = new List();
- _exportSymbols = new List();
- _entryPointSymbol = null;
- _exportDirectoryEntry = default(DirectoryEntry);
- _relocationDirectoryEntry = default(DirectoryEntry);
-
- switch (_target.Architecture)
- {
- case TargetArchitecture.X86:
- case TargetArchitecture.X64:
- // 4 times INT 3 (or debugger break)
- _codePadding = 0xCCCCCCCCu;
- break;
-
- case TargetArchitecture.ARM:
- // 2 times undefined instruction used as debugger break
- _codePadding = (_target.IsWindows ? 0xDEFEDEFEu : 0xDE01DE01u);
- break;
-
- case TargetArchitecture.ARM64:
- _codePadding = 0xD43E0000u;
- break;
-
- case TargetArchitecture.LoongArch64:
- _codePadding = 0x002A0005u;
- break;
-
- case TargetArchitecture.RiscV64:
- _codePadding = 0x00100073u;
- break;
-
- default:
- throw new NotImplementedException();
- }
- }
-
- ///
- /// Add a new section. Section names must be unique.
- ///
- /// Section name
- /// Section characteristics
- ///
- /// Alignment for composing multiple builder sections into one physical output section
- ///
- /// Zero-based index of the added section
- public int AddSection(string name, SectionCharacteristics characteristics, int alignment)
- {
- int sectionIndex = _sections.Count;
- _sections.Add(new Section(sectionIndex, name, characteristics, alignment));
- return sectionIndex;
- }
-
- ///
- /// Try to look up a pre-existing section in the builder; returns null if not found.
- ///
- public Section FindSection(string name)
- {
- return _sections.FirstOrDefault((sec) => sec.Name == name);
- }
-
- ///
- /// Look up RVA for a given symbol. This assumes the section have already been placed.
- ///
- /// Symbol to look up
- /// RVA of the symbol
- public int GetSymbolRVA(ISymbolNode symbol)
- {
- SymbolTarget symbolTarget = _symbolMap[symbol];
- Section section = _sections[symbolTarget.SectionIndex];
- Debug.Assert(section.RVAWhenPlaced != 0);
- return section.RVAWhenPlaced + symbolTarget.Offset;
- }
-
- ///
- /// Look up final file position for a given symbol. This assumes the section have already been placed.
- ///
- /// Symbol to look up
- /// File position of the symbol, from the beginning of the emitted image
- public int GetSymbolFilePosition(ISymbolNode symbol)
- {
- SymbolTarget symbolTarget = _symbolMap[symbol];
- Section section = _sections[symbolTarget.SectionIndex];
- Debug.Assert(section.RVAWhenPlaced != 0);
- return section.FilePosWhenPlaced + symbolTarget.Offset;
- }
-
- ///
- /// Attach an export symbol to the output PE file.
- ///
- /// Export symbol identifier
- /// Ordinal ID of the export symbol
- /// Symbol to export
- public void AddExportSymbol(string name, int ordinal, ISymbolNode symbol)
- {
- _exportSymbols.Add(new ExportSymbol(
- name: name,
- ordinal: ordinal,
- symbol: symbol));
- }
-
- ///
- /// Record DLL name to emit in the export directory table.
- ///
- /// DLL name to emit
- public void SetDllNameForExportDirectoryTable(string dllName)
- {
- _dllNameForExportDirectoryTable = dllName;
- }
-
- ///
- /// Override entry point for the app.
- ///
- /// Symbol representing the new entry point
- public void SetEntryPoint(ISymbolNode symbol)
- {
- _entryPointSymbol = symbol;
- }
-
- public void SetCorHeader(ISymbolNode symbol, int headerSize)
- {
- _corHeaderSymbol = symbol;
- _corHeaderSize = headerSize;
- }
-
- public void SetDebugDirectory(ISymbolNode symbol, int size)
- {
- _debugDirectorySymbol = symbol;
- _debugDirectorySize = size;
- }
-
- public void SetWin32Resources(ISymbolNode symbol, int resourcesSize)
- {
- _win32ResourcesSymbol = symbol;
- _win32ResourcesSize = resourcesSize;
- }
-
- private NativeAotNameMangler _nameMangler;
-
- private NameMangler GetNameMangler()
- {
- if (_nameMangler == null)
- {
- // TODO-REFACTOR: why do we have two name manglers?
- _nameMangler = new NativeAotNameMangler();
- _nameMangler.CompilationUnitPrefix = "";
- }
- return _nameMangler;
- }
-
- ///
- /// Add an ObjectData block to a given section.
- ///
- /// Block to add
- /// Section index
- /// Node name to emit in the map file
- /// Optional output info to collect (used for creating maps and symbols)
- public void AddObjectData(ObjectNode.ObjectData objectData, int sectionIndex, string name, OutputInfoBuilder outputInfoBuilder)
- {
- Section section = _sections[sectionIndex];
-
- // Calculate alignment padding - apparently ObjectDataBuilder can produce an alignment of 0
- int alignedOffset = section.Content.Count;
- if (objectData.Alignment > 1)
- {
- alignedOffset = (section.Content.Count + objectData.Alignment - 1) & -objectData.Alignment;
- int padding = alignedOffset - section.Content.Count;
- if (padding > 0)
- {
- if ((section.Characteristics & SectionCharacteristics.ContainsCode) != 0)
- {
- uint cp = _codePadding;
- while (padding >= sizeof(uint))
- {
- section.Content.WriteUInt32(cp);
- padding -= sizeof(uint);
- }
- if (padding >= 2)
- {
- section.Content.WriteUInt16(unchecked((ushort)cp));
- cp >>= 16;
- }
- if ((padding & 1) != 0)
- {
- section.Content.WriteByte(unchecked((byte)cp));
- }
- }
- else
- {
- section.Content.WriteBytes(0, padding);
- }
- }
- }
-
- if (outputInfoBuilder != null)
- {
- var node = new OutputNode(sectionIndex, alignedOffset, objectData.Data.Length, name);
- outputInfoBuilder.AddNode(node, objectData.DefinedSymbols[0]);
- if (objectData.Relocs != null)
- {
- foreach (Relocation reloc in objectData.Relocs)
- {
- RelocType fileReloc = Relocation.GetFileRelocationType(reloc.RelocType);
- if (fileReloc != RelocType.IMAGE_REL_BASED_ABSOLUTE)
- {
- outputInfoBuilder.AddRelocation(node, fileReloc);
- }
- }
- }
- }
-
- section.Content.WriteBytes(objectData.Data);
-
- if (objectData.DefinedSymbols != null)
- {
- foreach (ISymbolDefinitionNode symbol in objectData.DefinedSymbols)
- {
- if (outputInfoBuilder != null)
- {
- Utf8StringBuilder sb = new Utf8StringBuilder();
- symbol.AppendMangledName(GetNameMangler(), sb);
- int sectionRelativeOffset = alignedOffset + symbol.Offset;
- outputInfoBuilder.AddSymbol(new OutputSymbol(sectionIndex, sectionRelativeOffset, sb.ToString()));
- }
- _symbolMap.Add(symbol, new SymbolTarget(
- sectionIndex: sectionIndex,
- offset: alignedOffset + symbol.Offset,
- size: objectData.Data.Length));
- }
- }
-
- if (objectData.Relocs != null && objectData.Relocs.Length != 0)
- {
- section.PlacedObjectDataToRelocate.Add(new PlacedObjectData(alignedOffset, objectData));
- }
- }
-
- public void AddSymbolForRange(ISymbolNode symbol, ISymbolNode firstNode, ISymbolNode secondNode)
- {
- SymbolTarget firstSymbolTarget = _symbolMap[firstNode];
- SymbolTarget secondSymbolTarget = _symbolMap[secondNode];
- Debug.Assert(firstSymbolTarget.SectionIndex == secondSymbolTarget.SectionIndex);
- Debug.Assert(firstSymbolTarget.Offset <= secondSymbolTarget.Offset);
-
- _symbolMap.Add(symbol, new SymbolTarget(
- sectionIndex: firstSymbolTarget.SectionIndex,
- offset: firstSymbolTarget.Offset,
- size: secondSymbolTarget.Offset - firstSymbolTarget.Offset + secondSymbolTarget.Size
- ));
- }
-
- ///
- /// Get the list of sections that need to be emitted to the output PE file.
- /// We filter out name duplicates as we'll end up merging builder sections with the same name
- /// into a single output physical section.
- ///
- public IEnumerable GetSections()
- {
- List sectionList = new List();
- foreach (Section section in _sections)
- {
- if (!sectionList.Any((sc) => sc.SectionName == section.Name))
- {
- sectionList.Add(new SectionInfo(section.Name, section.Characteristics));
- }
- }
-
- return sectionList;
- }
-
- public void AddSections(OutputInfoBuilder outputInfoBuilder)
- {
- foreach (Section section in _sections)
- {
- outputInfoBuilder.AddSection(section);
- }
- }
-
- ///
- /// Traverse blocks within a single section and use them to calculate final layout
- /// of the given section.
- ///
- /// Section to serialize
- /// Logical section address within the output PE file
- ///
- public BlobBuilder SerializeSection(string name, SectionLocation sectionLocation)
- {
- if (name == R2RPEBuilder.RelocSectionName)
- {
- return SerializeRelocationSection(sectionLocation);
- }
-
- if (name == R2RPEBuilder.ExportDataSectionName)
- {
- return SerializeExportSection(sectionLocation);
- }
-
- BlobBuilder serializedSection = null;
-
- // Locate logical section index by name
- foreach (Section section in _sections.Where((sec) => sec.Name == name))
- {
- // Calculate alignment padding
- int alignedRVA = (sectionLocation.RelativeVirtualAddress + section.Alignment - 1) & -section.Alignment;
- int padding = alignedRVA - sectionLocation.RelativeVirtualAddress;
- if (padding > 0)
- {
- if (serializedSection == null)
- {
- serializedSection = new BlobBuilder();
- }
- serializedSection.WriteBytes(0, padding);
- sectionLocation = new SectionLocation(
- sectionLocation.RelativeVirtualAddress + padding,
- sectionLocation.PointerToRawData + padding);
- }
-
- // Place the section
- section.RVAWhenPlaced = sectionLocation.RelativeVirtualAddress;
- section.FilePosWhenPlaced = sectionLocation.PointerToRawData;
-
- if (section.Content.Count != 0)
- {
- sectionLocation = new SectionLocation(
- sectionLocation.RelativeVirtualAddress + section.Content.Count,
- sectionLocation.PointerToRawData + section.Content.Count);
-
- if (serializedSection == null)
- {
- serializedSection = section.Content;
- }
- else
- {
- serializedSection.LinkSuffix(section.Content);
- }
- }
- }
-
- return serializedSection;
- }
-
- ///
- /// Emit the .reloc section based on file relocation information in the individual blocks.
- /// We rely on the fact that the .reloc section is emitted last so that, by the time
- /// it's getting serialized, all other sections that may contain relocations have already
- /// been laid out.
- ///
- private BlobBuilder SerializeRelocationSection(SectionLocation sectionLocation)
- {
- // There are 12 bits for the relative offset
- const int RelocationTypeShift = 12;
- const int MaxRelativeOffsetInBlock = (1 << RelocationTypeShift) - 1;
-
- // Even though the format doesn't dictate it, it seems customary
- // to align the base RVA's on 4K boundaries.
- const int BaseRVAAlignment = 1 << RelocationTypeShift;
-
- BlobBuilder builder = new BlobBuilder();
- int baseRVA = 0;
- List offsetsAndTypes = null;
-
- Section relocSection = FindSection(R2RPEBuilder.RelocSectionName);
- if (relocSection != null)
- {
- relocSection.FilePosWhenPlaced = sectionLocation.PointerToRawData;
- relocSection.RVAWhenPlaced = sectionLocation.RelativeVirtualAddress;
- builder = relocSection.Content;
- }
-
- // Traverse relocations in all sections in their RVA order
- // By now, all "normal" sections with relocations should already have been laid out
- foreach (Section section in _sections.OrderBy((sec) => sec.RVAWhenPlaced))
- {
- foreach (PlacedObjectData placedObjectData in section.PlacedObjectDataToRelocate)
- {
- for (int relocIndex = 0; relocIndex < placedObjectData.Relocs.Length; relocIndex++)
- {
- RelocType relocType = placedObjectData.Relocs[relocIndex].RelocType;
- RelocType fileRelocType = Relocation.GetFileRelocationType(relocType);
- if (fileRelocType != RelocType.IMAGE_REL_BASED_ABSOLUTE)
- {
- int relocationRVA = section.RVAWhenPlaced + placedObjectData.Offset + placedObjectData.Relocs[relocIndex].Offset;
- if (offsetsAndTypes != null && relocationRVA - baseRVA > MaxRelativeOffsetInBlock)
- {
- // Need to flush relocation block as the current RVA is too far from base RVA
- FlushRelocationBlock(builder, baseRVA, offsetsAndTypes);
- offsetsAndTypes = null;
- }
- if (offsetsAndTypes == null)
- {
- // Create new relocation block
- baseRVA = relocationRVA & -BaseRVAAlignment;
- offsetsAndTypes = new List();
- }
- ushort offsetAndType = (ushort)(((ushort)fileRelocType << RelocationTypeShift) | (relocationRVA - baseRVA));
- offsetsAndTypes.Add(offsetAndType);
- }
- }
- }
- }
-
- if (offsetsAndTypes != null)
- {
- FlushRelocationBlock(builder, baseRVA, offsetsAndTypes);
- }
-
- if (builder.Count != 0)
- {
- _relocationDirectoryEntry = new DirectoryEntry(sectionLocation.RelativeVirtualAddress, builder.Count);
- }
-
- return builder;
- }
-
- ///
- /// Serialize a block of relocations into the .reloc section.
- ///
- /// Output blob builder to receive the serialized relocation block
- /// Base RVA of the relocation block
- /// 16-bit entries encoding offset relative to the base RVA (low 12 bits) and relocation type (top 4 bite)
- private static void FlushRelocationBlock(BlobBuilder builder, int baseRVA, List offsetsAndTypes)
- {
- // Ensure blocks are 4-byte aligned. This is required by kernel memory manager
- // on Windows 8.1 and earlier.
- if ((offsetsAndTypes.Count & 1) == 1)
- {
- offsetsAndTypes.Add(0);
- }
-
- // First, emit the block header: 4 bytes starting RVA,
- builder.WriteInt32(baseRVA);
- // followed by the total block size comprising this header
- // and following 16-bit entries.
- builder.WriteInt32(4 + 4 + 2 * offsetsAndTypes.Count);
- // Now serialize out the entries
- foreach (ushort offsetAndType in offsetsAndTypes)
- {
- builder.WriteUInt16(offsetAndType);
- }
- }
-
- ///
- /// Serialize the export symbol table into the export section.
- ///
- /// RVA and file location of the .edata section
- private BlobBuilder SerializeExportSection(SectionLocation sectionLocation)
- {
- _exportSymbols.MergeSort((es1, es2) => StringComparer.Ordinal.Compare(es1.Name, es2.Name));
-
- BlobBuilder builder = new BlobBuilder();
-
- int minOrdinal = int.MaxValue;
- int maxOrdinal = int.MinValue;
-
- // First, emit the name table and store the name RVA's for the individual export symbols
- // Also, record the ordinal range.
- foreach (ExportSymbol symbol in _exportSymbols)
- {
- symbol.NameRVAWhenPlaced = sectionLocation.RelativeVirtualAddress + builder.Count;
- builder.WriteUTF8(symbol.Name);
- builder.WriteByte(0);
-
- if (symbol.Ordinal < minOrdinal)
- {
- minOrdinal = symbol.Ordinal;
- }
- if (symbol.Ordinal > maxOrdinal)
- {
- maxOrdinal = symbol.Ordinal;
- }
- }
-
- // Emit the DLL name
- int dllNameRVA = sectionLocation.RelativeVirtualAddress + builder.Count;
- builder.WriteUTF8(_dllNameForExportDirectoryTable);
- builder.WriteByte(0);
-
- int[] addressTable = new int[maxOrdinal - minOrdinal + 1];
-
- // Emit the name pointer table; it should be alphabetically sorted.
- // Also, we can now fill in the export address table as we've detected its size
- // in the previous pass.
- builder.Align(4);
- int namePointerTableRVA = sectionLocation.RelativeVirtualAddress + builder.Count;
- foreach (ExportSymbol symbol in _exportSymbols)
- {
- builder.WriteInt32(symbol.NameRVAWhenPlaced);
- SymbolTarget symbolTarget = _symbolMap[symbol.Symbol];
- Section symbolSection = _sections[symbolTarget.SectionIndex];
- Debug.Assert(symbolSection.RVAWhenPlaced != 0);
- addressTable[symbol.Ordinal - minOrdinal] = symbolSection.RVAWhenPlaced + symbolTarget.Offset;
- }
-
- // Emit the ordinal table
- int ordinalTableRVA = sectionLocation.RelativeVirtualAddress + builder.Count;
- foreach (ExportSymbol symbol in _exportSymbols)
- {
- builder.WriteUInt16((ushort)(symbol.Ordinal - minOrdinal));
- }
-
- // Emit the address table
- builder.Align(4);
- int addressTableRVA = sectionLocation.RelativeVirtualAddress + builder.Count;
- foreach (int addressTableEntry in addressTable)
- {
- builder.WriteInt32(addressTableEntry);
- }
-
- // Emit the export directory table
- builder.Align(4);
- int exportDirectoryTableRVA = sectionLocation.RelativeVirtualAddress + builder.Count;
- // +0x00: reserved
- builder.WriteInt32(0);
- // +0x04: TODO: time/date stamp
- builder.WriteInt32(0);
- // +0x08: major version
- builder.WriteInt16(0);
- // +0x0A: minor version
- builder.WriteInt16(0);
- // +0x0C: DLL name RVA
- builder.WriteInt32(dllNameRVA);
- // +0x10: ordinal base
- builder.WriteInt32(minOrdinal);
- // +0x14: number of entries in the address table
- builder.WriteInt32(addressTable.Length);
- // +0x18: number of name pointers
- builder.WriteInt32(_exportSymbols.Count);
- // +0x1C: export address table RVA
- builder.WriteInt32(addressTableRVA);
- // +0x20: name pointer RVV
- builder.WriteInt32(namePointerTableRVA);
- // +0x24: ordinal table RVA
- builder.WriteInt32(ordinalTableRVA);
- int exportDirectorySize = sectionLocation.RelativeVirtualAddress + builder.Count - exportDirectoryTableRVA;
-
- _exportDirectoryEntry = new DirectoryEntry(relativeVirtualAddress: exportDirectoryTableRVA, size: exportDirectorySize);
-
- return builder;
- }
-
- ///
- /// Update the PE file directories. Currently this is used to update the export symbol table
- /// when export symbols have been added to the section builder.
- ///
- /// PE directory builder to update
- public void UpdateDirectories(PEDirectoriesBuilder directoriesBuilder)
- {
- if (_corHeaderSymbol != null)
- {
- SymbolTarget symbolTarget = _symbolMap[_corHeaderSymbol];
- Section section = _sections[symbolTarget.SectionIndex];
- Debug.Assert(section.RVAWhenPlaced != 0);
- directoriesBuilder.CorHeaderTable = new DirectoryEntry(section.RVAWhenPlaced + symbolTarget.Offset, _corHeaderSize);
- }
-
- if (_win32ResourcesSymbol != null)
- {
- SymbolTarget symbolTarget = _symbolMap[_win32ResourcesSymbol];
- Section section = _sections[symbolTarget.SectionIndex];
- Debug.Assert(section.RVAWhenPlaced != 0);
-
- // Windows has a bug in its resource processing logic that occurs when
- // 1. A PE file is loaded as a data file
- // 2. The resource data found in the resources has an RVA which has a magnitude greater than the size of the section which holds the resources
- // 3. The offset of the start of the resource data from the start of the section is not zero.
- //
- // As it is impossible to effect condition 1 in the compiler, and changing condition 2 would require bloating the virtual size of the sections,
- // instead require that the resource data is located at offset 0 within the section.
- // We achieve that by sorting the Win32ResourcesNode as the first node.
- Debug.Assert(symbolTarget.Offset == 0);
- directoriesBuilder.ResourceTable = new DirectoryEntry(section.RVAWhenPlaced + symbolTarget.Offset, _win32ResourcesSize);
- }
-
- if (_exportDirectoryEntry.Size != 0)
- {
- directoriesBuilder.ExportTable = _exportDirectoryEntry;
- }
-
- int relocationTableRVA = directoriesBuilder.BaseRelocationTable.RelativeVirtualAddress;
- if (relocationTableRVA == 0)
- {
- relocationTableRVA = _relocationDirectoryEntry.RelativeVirtualAddress;
- }
- directoriesBuilder.BaseRelocationTable = new DirectoryEntry(
- relocationTableRVA,
- directoriesBuilder.BaseRelocationTable.Size + _relocationDirectoryEntry.Size);
-
- if (_entryPointSymbol != null)
- {
- SymbolTarget symbolTarget = _symbolMap[_entryPointSymbol];
- Section section = _sections[symbolTarget.SectionIndex];
- Debug.Assert(section.RVAWhenPlaced != 0);
- directoriesBuilder.AddressOfEntryPoint = section.RVAWhenPlaced + symbolTarget.Offset;
- }
-
- if (_debugDirectorySymbol != null)
- {
- SymbolTarget symbolTarget = _symbolMap[_debugDirectorySymbol];
- Section section = _sections[symbolTarget.SectionIndex];
- Debug.Assert(section.RVAWhenPlaced != 0);
- directoriesBuilder.DebugTable = new DirectoryEntry(section.RVAWhenPlaced + symbolTarget.Offset, _debugDirectorySize);
- }
- }
-
- ///
- /// Relocate the produced PE file and output the result into a given stream.
- ///
- /// Blob builder representing the complete PE file
- /// Default load address for the image
- /// COR header
- /// File position of the COR header
- /// Stream to receive the relocated PE file
- public void RelocateOutputFile(
- BlobBuilder peFile,
- ulong defaultImageBase,
- Stream outputStream)
- {
- RelocationHelper relocationHelper = new RelocationHelper(outputStream, defaultImageBase, peFile);
-
- // Traverse relocations in all sections in their RVA order
- foreach (Section section in _sections.OrderBy((sec) => sec.RVAWhenPlaced))
- {
- int rvaToFilePosDelta = section.FilePosWhenPlaced - section.RVAWhenPlaced;
- foreach (PlacedObjectData placedObjectData in section.PlacedObjectDataToRelocate)
- {
- foreach (Relocation relocation in placedObjectData.Relocs)
- {
- // Process a single relocation
- int relocationRVA = section.RVAWhenPlaced + placedObjectData.Offset + relocation.Offset;
- int relocationFilePos = relocationRVA + rvaToFilePosDelta;
-
- // Flush parts of PE file before the relocation to the output stream
- relocationHelper.CopyToFilePosition(relocationFilePos);
-
- // Look up relocation target
- SymbolTarget relocationTarget = _symbolMap[relocation.Target];
- Section targetSection = _sections[relocationTarget.SectionIndex];
- int targetRVA = targetSection.RVAWhenPlaced + relocationTarget.Offset;
- int filePosWhenPlaced = targetSection.FilePosWhenPlaced + relocationTarget.Offset;
-
- // If relocating to a node's size, switch out the target RVA with data length
- if (relocation.RelocType == RelocType.IMAGE_REL_SYMBOL_SIZE)
- {
- targetRVA = relocationTarget.Size;
- }
-
- // Apply the relocation
- relocationHelper.ProcessRelocation(relocation.RelocType, relocationRVA, targetRVA, filePosWhenPlaced);
- }
- }
- }
-
- // Flush remaining PE file blocks after the last relocation
- relocationHelper.CopyRestOfFile();
- }
-
- internal bool HasContent(string sectionName)
- {
- if (sectionName == R2RPEBuilder.ExportDataSectionName)
- return _exportSymbols.Count > 0 && _dllNameForExportDirectoryTable != null;
-
- if (sectionName == R2RPEBuilder.RelocSectionName)
- {
- return _sections.Any(
- s => s.PlacedObjectDataToRelocate.Any(
- d => d.Relocs.Any(
- r => Relocation.GetFileRelocationType(r.RelocType) != RelocType.IMAGE_REL_BASED_ABSOLUTE)));
- }
-
- Section section = FindSection(sectionName);
- return section != null && section.Content.Count > 0;
- }
- }
-}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SymbolFileBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SymbolFileBuilder.cs
index 3065ae37e16896..80feb9812767b6 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SymbolFileBuilder.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SymbolFileBuilder.cs
@@ -10,6 +10,7 @@
using Internal.TypeSystem;
using ILCompiler.Diagnostics;
+using ILCompiler.ObjectWriter;
namespace ILCompiler.PEWriter
{
diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs
index 98b214e2b4785a..039b50d699aede 100644
--- a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs
+++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs
@@ -106,7 +106,7 @@ protected override void CompileInternal(string outputFile, ObjectDumper dumper)
NodeFactory.SetMarkingComplete();
- ObjectWritingOptions options = default;
+ ObjectWritingOptions options = ObjectWritingOptions.GenerateUnwindInfo;
if ((_compilationOptions & RyuJitCompilationOptions.UseDwarf5) != 0)
options |= ObjectWritingOptions.UseDwarf5;
diff --git a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs
index bcb792d2eb7eaa..dc07e5abc4c43f 100644
--- a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs
+++ b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs
@@ -30,6 +30,8 @@ internal class Crossgen2RootCommand : RootCommand
new("--mibc", "-m") { DefaultValueFactory = _ => Array.Empty(), Description = SR.MibcFiles };
public Option OutputFilePath { get; } =
new("--out", "-o") { Description = SR.OutputFilePath };
+ public Option OutputFormat { get; } =
+ new("--obj-format", "-f") { CustomParser = MakeOutputFormat, DefaultValueFactory = MakeOutputFormat, Description = SR.OutputFormat, HelpName = "arg" };
public Option CompositeRootPath { get; } =
new("--compositerootpath", "--crp") { Description = SR.CompositeRootPath };
public Option Optimize { get; } =
@@ -161,6 +163,7 @@ public Crossgen2RootCommand(string[] args) : base(SR.Crossgen2BannerText)
Options.Add(MaxVectorTBitWidth);
Options.Add(MibcFilePaths);
Options.Add(OutputFilePath);
+ Options.Add(OutputFormat);
Options.Add(CompositeRootPath);
Options.Add(Optimize);
Options.Add(OptimizeDisabled);
@@ -404,6 +407,18 @@ private static FileLayoutAlgorithm MakeFileLayoutAlgorithm(ArgumentResult result
};
}
+ private static ReadyToRunContainerFormat MakeOutputFormat(ArgumentResult result)
+ {
+ if (result.Tokens.Count == 0)
+ return ReadyToRunContainerFormat.PE;
+
+ return result.Tokens[0].Value.ToLowerInvariant() switch
+ {
+ "pe" => ReadyToRunContainerFormat.PE,
+ _ => throw new CommandLineException(SR.InvalidOutputFormat)
+ };
+ }
+
#if DEBUG
private static bool DumpReproArguments(CodeGenerationFailedException ex)
{
diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs
index 90aabdbc349c31..231f2f637ee50e 100644
--- a/src/coreclr/tools/aot/crossgen2/Program.cs
+++ b/src/coreclr/tools/aot/crossgen2/Program.cs
@@ -616,6 +616,7 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru
.UseHotColdSplitting(Get(_command.HotColdSplitting))
.GenerateOutputFile(outFile)
.UseImageBase(_imageBase)
+ .UseContainerFormat(Get(_command.OutputFormat))
.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 0803acdcfe812d..faaa727911aac1 100644
--- a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx
+++ b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx
@@ -426,4 +426,10 @@
Enable support for cached interface dispatch
+
+ Target output format for the ReadyToRun image
+
+
+ Format must be PE
+
\ No newline at end of file
diff --git a/src/coreclr/tools/aot/ilc.slnx b/src/coreclr/tools/aot/ilc.slnx
index 49b7198de62088..ba4d00a6f349f9 100644
--- a/src/coreclr/tools/aot/ilc.slnx
+++ b/src/coreclr/tools/aot/ilc.slnx
@@ -34,6 +34,12 @@
+
+
+
+
+
+