Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ public ObjectNodeSection(string name, SectionType type, string comdatName)
public ObjectNodeSection(string name, SectionType type) : this(name, type, null)
{ }

public static readonly ObjectNodeSection XDataSection = new ObjectNodeSection("xdata", SectionType.ReadOnly);
public static readonly ObjectNodeSection DataSection = new ObjectNodeSection("data", SectionType.Writeable);
public static readonly ObjectNodeSection ReadOnlyDataSection = new ObjectNodeSection("rdata", SectionType.ReadOnly);
public static readonly ObjectNodeSection FoldableReadOnlyDataSection = new ObjectNodeSection("rdata", SectionType.ReadOnly);
Expand All @@ -51,10 +50,5 @@ public ObjectNodeSection(string name, SectionType type) : this(name, type, null)

public static readonly ObjectNodeSection ModulesWindowsContentSection = new ObjectNodeSection(".modules$I", SectionType.ReadOnly);
public static readonly ObjectNodeSection ModulesUnixContentSection = new ObjectNodeSection("__modules", SectionType.Writeable);

public static readonly ObjectNodeSection DebugDirectorySection = new ObjectNodeSection("debug", SectionType.Debug);
public static readonly ObjectNodeSection CorMetaSection = new ObjectNodeSection("cormeta", SectionType.ReadOnly);
public static readonly ObjectNodeSection Win32ResourcesSection = new ObjectNodeSection("rsrc", SectionType.ReadOnly);
public static readonly ObjectNodeSection PDataSection = new ObjectNodeSection("pdata", SectionType.ReadOnly);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public virtual int CompareToImpl(ISortableNode other, CompilerComparer comparer)
throw new NotImplementedException("Multiple nodes of this type are not supported");
}

protected enum ObjectNodePhase
protected internal enum ObjectNodePhase
{
/// <summary>
/// Nodes should only be placed in this phase if they have strict output ordering requirements that
Expand All @@ -49,7 +49,7 @@ protected enum ObjectNodePhase
Late,
}

protected enum ObjectNodeOrder
protected internal enum ObjectNodeOrder
{
//
// The ordering of this sequence of nodes is deliberate and currently required for
Expand All @@ -66,7 +66,9 @@ protected enum ObjectNodeOrder
ImportSectionsTableNode,
ImportSectionNode,
MethodEntrypointTableNode,

DebugDirectoryNode,
RuntimeFunctionsGCInfoNode,
RuntimeFunctionsTableNode,

//
// NativeAOT Nodes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ private protected override void CreateSection(ObjectNodeSection section, Utf8Str
{
SectionType.ReadOnly =>
SectionCharacteristics.MemRead | SectionCharacteristics.ContainsInitializedData,
SectionType.UnwindData =>
SectionCharacteristics.MemRead | SectionCharacteristics.ContainsInitializedData,
SectionType.Writeable =>
SectionCharacteristics.MemRead | SectionCharacteristics.MemWrite |
SectionCharacteristics.ContainsInitializedData,
Expand All @@ -102,7 +104,7 @@ private protected override void CreateSection(ObjectNodeSection section, Utf8Str
}
};

if (section == DebugTypesSection || section == ObjectNodeSection.DebugDirectorySection)
if (section == DebugTypesSection)
{
sectionHeader.SectionCharacteristics =
SectionCharacteristics.MemRead | SectionCharacteristics.ContainsInitializedData |
Expand Down
23 changes: 23 additions & 0 deletions src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,18 @@ private protected ObjectWriter(NodeFactory factory, ObjectWritingOptions options

protected internal abstract void UpdateSectionAlignment(int sectionIndex, int alignment);

/// <summary>
/// Get the section in the image where nodes in the passed in section should actually be emitted.
/// </summary>
/// <param name="section">A node's requested section.</param>
/// <returns>The section to actually emit the node into.</returns>
/// <remarks>
/// Sections in an image can be very expensive, and unlike linkable formats,
/// sections cannot be merged after the fact.
/// This method allows formats that want to merge sections during emit to do so.
/// </remarks>
private protected virtual ObjectNodeSection GetEmitSection(ObjectNodeSection section) => section;

private protected SectionWriter GetOrCreateSection(ObjectNodeSection section)
=> GetOrCreateSection(section, default, default);

Expand All @@ -87,6 +99,8 @@ private protected SectionWriter GetOrCreateSection(ObjectNodeSection section, Ut
int sectionIndex;
SectionData sectionData;

section = GetEmitSection(section);

if (!comdatName.IsNull || !_sectionNameToSectionIndex.TryGetValue(section.Name, out sectionIndex))
{
sectionData = new SectionData(section.Type == SectionType.Executable ? _insPaddingByte : (byte)0);
Expand Down Expand Up @@ -426,6 +440,11 @@ public virtual void EmitObject(Stream outputFileStream, IReadOnlyCollection<Depe

_outputInfoBuilder?.AddSymbol(new OutputSymbol(sectionWriter.SectionIndex, (ulong)(sectionWriter.Position + n.Offset), alternateCName));
}

if (node.Phase == (int)SortableDependencyNode.ObjectNodePhase.Ordered)
{
RecordWellKnownSymbol(currentSymbolName, (SortableDependencyNode.ObjectNodeOrder)node.ClassCode);
}
}

if (nodeContents.Relocs is not null)
Expand Down Expand Up @@ -590,6 +609,10 @@ public virtual void EmitObject(Stream outputFileStream, IReadOnlyCollection<Depe
}
}

private protected virtual void RecordWellKnownSymbol(Utf8String currentSymbolName, SortableDependencyNode.ObjectNodeOrder classCode)
{
}

private protected virtual void EmitSymbolRangeDefinition(Utf8String rangeNodeName, Utf8String startNodeName, Utf8String endNodeName, SymbolDefinition endSymbol)
{
if (!_definedSymbols.TryGetValue(startNodeName, out var startSymbol))
Expand Down
121 changes: 78 additions & 43 deletions src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ internal sealed class PEObjectWriter : CoffObjectWriter
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;
Expand All @@ -72,18 +71,14 @@ internal sealed class PEObjectWriter : CoffObjectWriter
// Relocations that we can resolve at emit time (ie not file-based relocations).
private Dictionary<int, List<SymbolicRelocation>> _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<uint, List<ushort>> _baseRelocMap = new();
private Dictionary<Utf8String, SymbolDefinition> _definedSymbols = [];

private HashSet<string> _exportedSymbolNames = new();
private Dictionary<SortableDependencyNode.ObjectNodeOrder, Utf8String> _wellKnownSymbols = new();
private long _coffHeaderOffset;

public PEObjectWriter(NodeFactory factory, ObjectWritingOptions options, OutputInfoBuilder outputInfoBuilder, string outputPath, int sectionAlignment, int? coffTimestamp)
Expand All @@ -102,6 +97,33 @@ public void AddExportedSymbol(string symbol)
}
}

private protected override ObjectNodeSection GetEmitSection(ObjectNodeSection section)
{
// Put executable code into .text for PE files as AV software really
// doesn't like executable code in non-standard sections.
if (section == ObjectNodeSection.ManagedCodeWindowsContentSection)
{
return ObjectNodeSection.TextSection;
}

// RISC-V has a limited addressing range (±2GB) for relocs.
// Don't merge rdata into text to avoid relocation overflow.
if (_nodeFactory.Target.Architecture == TargetArchitecture.RiscV64)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This is order-dependent. If the code is moved or reordered, it may change the emitting strategy. Consider adding explicit conditions.

{
return section;
}

// We want to reduce the number of sections in the PE files we emit,
// so merge the read-only data section into the .text section.
if (section == ObjectNodeSection.ReadOnlyDataSection)
{
return ObjectNodeSection.TextSection;
}

// Otherwise, use the requested section.
return section;
}

private protected override void CreateSection(ObjectNodeSection section, Utf8String comdatName, Utf8String symbolName, int sectionIndex, Stream sectionStream)
{
// COMDAT sections are not supported in PE files
Expand Down Expand Up @@ -340,6 +362,19 @@ private enum ImageDirectoryEntry
Reserved = 15,
}

private protected override void RecordWellKnownSymbol(Utf8String currentSymbolName, SortableDependencyNode.ObjectNodeOrder classCode)
{
if (classCode is SortableDependencyNode.ObjectNodeOrder.Win32ResourcesNode
or SortableDependencyNode.ObjectNodeOrder.CorHeaderNode
or SortableDependencyNode.ObjectNodeOrder.DebugDirectoryNode
or SortableDependencyNode.ObjectNodeOrder.RuntimeFunctionsTableNode)
{
// These nodes represent directories in the PE header.
// We need to know what symbol name they have so we know where they are located during emit.
_wellKnownSymbols.Add(classCode, currentSymbolName);
}
}

private protected override void EmitSymbolTable(IDictionary<Utf8String, SymbolDefinition> definedSymbols, SortedSet<Utf8String> undefinedSymbols)
{
if (undefinedSymbols.Count > 0)
Expand All @@ -353,16 +388,19 @@ private protected override void EmitSymbolTable(IDictionary<Utf8String, SymbolDe

private protected override void EmitSectionsAndLayout()
{
SectionWriter exportDirectory = GetOrCreateSection(ExportDirectorySection);
if (_exportedSymbolNames.Count != 0)
{
// Emit the export directory into the text section to reduce the number of sections we emit.
SectionWriter textSection = GetOrCreateSection(ObjectNodeSection.TextSection);

EmitExportDirectory(exportDirectory);
textSection.EmitAlignment(_nodeFactory.Target.PointerSize);

// 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;
long startOffset = textSection.Position;

EmitExportDirectory(textSection);

EmitSymbolDefinition(textSection.SectionIndex, ExportDirectorySymbol, startOffset, (int)(textSection.Position - startOffset));
}

// 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,
Expand Down Expand Up @@ -407,6 +445,8 @@ private protected override void EmitSectionsAndLayout()
LayoutSections(recordFinalLayout: false, out _, out _, out _, out _, out _);
}

private string ExportDirectorySymbol => $"{_nodeFactory.NameMangler.CompilationUnitPrefix}__ExportDirectory";

private void LayoutSections(bool recordFinalLayout, out ushort numberOfSections, out uint sizeOfHeaders, out uint sizeOfImage, out uint sizeOfInitializedData, out uint sizeOfCode)
{
bool isPE32Plus = _nodeFactory.Target.PointerSize == 8;
Expand Down Expand Up @@ -542,12 +582,6 @@ private protected override unsafe void EmitRelocations(int sectionIndex, List<Sy

private void EmitExportDirectory(SectionWriter sectionWriter)
{
if (_exportedSymbolNames.Count == 0)
{
// No exports to emit.
return;
}

List<string> exports = [.._exportedSymbolNames];

exports.Sort(StringComparer.Ordinal);
Expand All @@ -567,8 +601,6 @@ private void EmitExportDirectory(SectionWriter sectionWriter)
string namePointerTableSymbol = GenerateSymbolNameForReloc("namePointerTable");
string ordinalPointerTableSymbol = GenerateSymbolNameForReloc("ordinalPointerTable");

Debug.Assert(sectionWriter.Position == 0);

// +0x00: reserved
sectionWriter.WriteLittleEndian(0);
// +0x04: time/date stamp
Expand Down Expand Up @@ -683,7 +715,7 @@ private protected override void EmitObjectFile(Stream outputFileStream)
#if READYTORUN
// On R2R, we encode the target OS into the machine bits to ensure we don't try running
// linux or mac R2R code on Windows, or vice versa.
machine = (Machine) ((ushort)machine ^ (ushort)_nodeFactory.Target.MachineOSOverrideFromTarget());
machine = (Machine)((ushort)machine ^ (ushort)_nodeFactory.Target.MachineOSOverrideFromTarget());
#endif

// COFF File Header
Expand Down Expand Up @@ -737,30 +769,17 @@ private protected override void EmitObjectFile(Stream outputFileStream)
// before writing if needed.
var dataDirs = new OptionalHeaderDataDirectories();
// Populate data directories if present.
if (_rsrcSectionIndex != NoSectionIndex)
{
dataDirs.SetIfNonEmpty((int)ImageDirectoryEntry.Resource, (uint)_outputSectionLayout[_rsrcSectionIndex].VirtualAddress, (uint)_outputSectionLayout[_rsrcSectionIndex].Length);
}
if (_pdataSectionIndex != NoSectionIndex)
{
dataDirs.SetIfNonEmpty((int)ImageDirectoryEntry.Exception, (uint)_outputSectionLayout[_pdataSectionIndex].VirtualAddress, (uint)_outputSectionLayout[_pdataSectionIndex].Length);
}
if (_exportSectionIndex != NoSectionIndex)
{
dataDirs.SetIfNonEmpty((int)ImageDirectoryEntry.Export, (uint)_outputSectionLayout[_exportSectionIndex].VirtualAddress, (uint)_outputSectionLayout[_exportSectionIndex].Length);
}
PopulateDataDirectoryForWellKnownSymbolIfPresent(dataDirs, ImageDirectoryEntry.Resource, SortableDependencyNode.ObjectNodeOrder.Win32ResourcesNode);
PopulateDataDirectoryForWellKnownSymbolIfPresent(dataDirs, ImageDirectoryEntry.Debug, SortableDependencyNode.ObjectNodeOrder.DebugDirectoryNode);
PopulateDataDirectoryForWellKnownSymbolIfPresent(dataDirs, ImageDirectoryEntry.CLRRuntimeHeader, SortableDependencyNode.ObjectNodeOrder.CorHeaderNode);
PopulateDataDirectoryForWellKnownSymbolIfPresent(dataDirs, ImageDirectoryEntry.Exception, SortableDependencyNode.ObjectNodeOrder.RuntimeFunctionsTableNode);
PopulateDataDirectoryForWellKnownSymbolIfPresent(dataDirs, ImageDirectoryEntry.Export, ExportDirectorySymbol);

if (_baseRelocSectionIndex != NoSectionIndex)
{
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();
Expand Down Expand Up @@ -811,6 +830,22 @@ private protected override void EmitObjectFile(Stream outputFileStream)
outputFileStream.SetLength(sizeOfImage);
}

private void PopulateDataDirectoryForWellKnownSymbolIfPresent(OptionalHeaderDataDirectories dataDirs, ImageDirectoryEntry directory, SortableDependencyNode.ObjectNodeOrder wellKnownSymbol)
{
if (_wellKnownSymbols.TryGetValue(wellKnownSymbol, out Utf8String symbolName))
{
PopulateDataDirectoryForWellKnownSymbolIfPresent(dataDirs, directory, symbolName);
}
}

private void PopulateDataDirectoryForWellKnownSymbolIfPresent(OptionalHeaderDataDirectories dataDirs, ImageDirectoryEntry directory, Utf8String symbolName)
{
if (_definedSymbols.TryGetValue(symbolName, out SymbolDefinition symbol))
{
dataDirs.SetIfNonEmpty((int)directory, checked((uint)(_outputSectionLayout[symbol.SectionIndex].VirtualAddress + (ulong)symbol.Value)), (uint)symbol.Size);
}
}

private protected override void EmitChecksumsForObject(Stream outputFileStream, List<ChecksumsToCalculate> checksumRelocations, ReadOnlySpan<byte> originalOutput)
{
base.EmitChecksumsForObject(outputFileStream, checksumRelocations, originalOutput);
Expand Down
Loading
Loading