diff --git a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs
index 18f7ffbb900357..20d6dac16b8a93 100644
--- a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs
+++ b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs
@@ -138,6 +138,9 @@ public void EmitPortableExecutable()
_customPESectionAlignment);
NativeDebugDirectoryEntryNode nativeDebugDirectoryEntryNode = null;
+ ISymbolDefinitionNode firstImportThunk = null;
+ ISymbolDefinitionNode lastImportThunk = null;
+ ObjectNode lastWrittenObjectNode = null;
int nodeIndex = -1;
foreach (var depNode in _nodes)
@@ -162,6 +165,18 @@ public void EmitPortableExecutable()
nativeDebugDirectoryEntryNode = nddeNode;
}
+ 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 = null;
if (_mapFileBuilder != null)
@@ -180,10 +195,16 @@ public void EmitPortableExecutable()
}
EmitObjectData(r2rPeBuilder, nodeContents, nodeIndex, name, node.Section, _mapFileBuilder);
+ lastWrittenObjectNode = node;
}
r2rPeBuilder.SetCorHeader(_nodeFactory.CopiedCorHeaderNode, _nodeFactory.CopiedCorHeaderNode.Size);
r2rPeBuilder.SetDebugDirectory(_nodeFactory.DebugDirectoryNode, _nodeFactory.DebugDirectoryNode.Size);
+ if (firstImportThunk != null)
+ {
+ r2rPeBuilder.AddSymbolForRange(_nodeFactory.DelayLoadMethodCallThunks, firstImportThunk, lastImportThunk);
+ }
+
if (_nodeFactory.Win32ResourcesNode != null)
{
diff --git a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodCallThunkNodeRange.cs b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodCallThunkNodeRange.cs
new file mode 100644
index 00000000000000..b0463ee3de8bf8
--- /dev/null
+++ b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodCallThunkNodeRange.cs
@@ -0,0 +1,36 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+
+using ILCompiler.DependencyAnalysisFramework;
+
+using Internal.Text;
+
+namespace ILCompiler.DependencyAnalysis.ReadyToRun
+{
+ ///
+ /// Provides an ISymbolNode for the R2R header table to relocate against when looking up the delay load method call thunks.
+ /// They are emitted in a contiguous run of object nodes. This symbol is used in the object writer to represent the range
+ /// of bytes containing all the thunks.
+ ///
+ public class DelayLoadMethodCallThunkNodeRange : DependencyNodeCore, ISymbolDefinitionNode
+ {
+ 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 void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
+ {
+ sb.Append(GetName(null));
+ }
+ }
+}
diff --git a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/HeaderNode.cs b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/HeaderNode.cs
index fe33cebfbe5804..5ff1eeccad72c0 100644
--- a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/HeaderNode.cs
+++ b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/HeaderNode.cs
@@ -7,6 +7,7 @@
using Internal.Text;
using Internal.TypeSystem;
using Internal.ReadyToRunConstants;
+using ILCompiler.DependencyAnalysisFramework;
namespace ILCompiler.DependencyAnalysis.ReadyToRun
{
@@ -45,7 +46,7 @@ public abstract class HeaderNode : ObjectNode, ISymbolDefinitionNode
{
struct HeaderItem
{
- public HeaderItem(ReadyToRunSectionType id, ObjectNode node, ISymbolNode startSymbol)
+ public HeaderItem(ReadyToRunSectionType id, DependencyNodeCore node, ISymbolNode startSymbol)
{
Id = id;
Node = node;
@@ -53,7 +54,7 @@ public HeaderItem(ReadyToRunSectionType id, ObjectNode node, ISymbolNode startSy
}
public readonly ReadyToRunSectionType Id;
- public readonly ObjectNode Node;
+ public readonly DependencyNodeCore Node;
public readonly ISymbolNode StartSymbol;
}
@@ -67,7 +68,7 @@ public HeaderNode(TargetDetails target, ReadyToRunFlags flags)
_flags = flags;
}
- public void Add(ReadyToRunSectionType id, ObjectNode node, ISymbolNode startSymbol)
+ public void Add(ReadyToRunSectionType id, DependencyNodeCore node, ISymbolNode startSymbol)
{
_items.Add(new HeaderItem(id, node, startSymbol));
}
@@ -116,13 +117,17 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
foreach (var item in _items)
{
// Skip empty entries
- if (!relocsOnly && item.Node.ShouldSkipEmittingObjectNode(factory))
+ if (!relocsOnly && item.Node is ObjectNode on && on.ShouldSkipEmittingObjectNode(factory))
continue;
builder.EmitInt((int)item.Id);
builder.EmitReloc(item.StartSymbol, RelocType.IMAGE_REL_BASED_ADDR32NB);
- builder.EmitReloc(item.StartSymbol, RelocType.IMAGE_REL_SYMBOL_SIZE);
+
+ // The header entry for the runtime functions table should not include the 4 byte 0xffffffff sentinel
+ // value in the covered range.
+ int delta = item.Id == ReadyToRunSectionType.RuntimeFunctions ? RuntimeFunctionsTableNode.SentinelSizeAdjustment : 0;
+ builder.EmitReloc(item.StartSymbol, RelocType.IMAGE_REL_SYMBOL_SIZE, delta);
count++;
}
diff --git a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/RuntimeFunctionsTableNode.cs b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/RuntimeFunctionsTableNode.cs
index 24dd8d91c5a17d..c2ab705ef5c2f6 100644
--- a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/RuntimeFunctionsTableNode.cs
+++ b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/RuntimeFunctionsTableNode.cs
@@ -103,15 +103,22 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
return runtimeFunctionsBuilder.ToObjectData();
}
- public int TableSize
+ ///
+ /// Returns the runtime functions table size and excludes the 4 byte sentinel entry at the end (used by
+ /// the runtime in NativeUnwindInfoLookupTable::LookupUnwindInfoForMethod) so that it's not treated as
+ /// part of the table itself.
+ ///
+ public int TableSizeExcludingSentinel
{
get
{
Debug.Assert(_tableSize >= 0);
- return _tableSize;
+ return _tableSize + SentinelSizeAdjustment;
}
}
public override int ClassCode => -855231428;
+
+ internal const int SentinelSizeAdjustment = -4;
}
}
diff --git a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs
index 0a29f3ff689b53..9f9ebf9366280c 100644
--- a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs
+++ b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs
@@ -305,6 +305,7 @@ private void CreateNodeCaches()
public RuntimeFunctionsGCInfoNode RuntimeFunctionsGCInfo;
public ProfileDataSectionNode ProfileDataSection;
+ public DelayLoadMethodCallThunkNodeRange DelayLoadMethodCallThunks;
public InstanceEntryPointTableNode InstanceEntryPointTable;
@@ -502,6 +503,9 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase graph)
ProfileDataSection = new ProfileDataSectionNode();
Header.Add(Internal.Runtime.ReadyToRunSectionType.ProfileDataInfo, ProfileDataSection, ProfileDataSection.StartSymbol);
+ DelayLoadMethodCallThunks = new DelayLoadMethodCallThunkNodeRange();
+ Header.Add(Internal.Runtime.ReadyToRunSectionType.DelayLoadMethodCallThunks, DelayLoadMethodCallThunks, DelayLoadMethodCallThunks);
+
ExceptionInfoLookupTableNode exceptionInfoLookupTableNode = new ExceptionInfoLookupTableNode(this);
Header.Add(Internal.Runtime.ReadyToRunSectionType.ExceptionInfo, exceptionInfoLookupTableNode, exceptionInfoLookupTableNode);
graph.AddRoot(exceptionInfoLookupTableNode, "ExceptionInfoLookupTable is always generated");
diff --git a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj
index 099820c53e54f4..483973871a90b5 100644
--- a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj
+++ b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj
@@ -94,6 +94,7 @@
+
diff --git a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs
index 95b4035178d3da..839b1f21d11067 100644
--- a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs
+++ b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs
@@ -268,6 +268,16 @@ public void AddObjectData(ObjectNode.ObjectData objectData, ObjectNodeSection se
_sectionBuilder.AddObjectData(objectData, targetSectionIndex, name, mapFileBuilder);
}
+ ///
+ /// 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);
@@ -497,7 +507,7 @@ protected override PEDirectoriesBuilder GetDirectories()
RuntimeFunctionsTableNode runtimeFunctionsTable = _getRuntimeFunctionsTable();
builder.ExceptionTable = new DirectoryEntry(
relativeVirtualAddress: _sectionBuilder.GetSymbolRVA(runtimeFunctionsTable),
- size: runtimeFunctionsTable.TableSize);
+ size: runtimeFunctionsTable.TableSizeExcludingSentinel);
}
return builder;
diff --git a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs
index 3d31687cccf7b6..dd82ab79c76e2f 100644
--- a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs
+++ b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs
@@ -518,6 +518,20 @@ public void AddObjectData(ObjectNode.ObjectData objectData, int sectionIndex, st
}
}
+ 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
diff --git a/src/coreclr/src/vm/codeman.cpp b/src/coreclr/src/vm/codeman.cpp
index 7af2850b83d491..a04894fa77edfa 100644
--- a/src/coreclr/src/vm/codeman.cpp
+++ b/src/coreclr/src/vm/codeman.cpp
@@ -6610,14 +6610,15 @@ StubCodeBlockKind ReadyToRunJitManager::GetStubCodeBlockKind(RangeSection * pRan
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
+ SUPPORTS_DAC;
}
CONTRACTL_END;
DWORD rva = (DWORD)(currentPC - pRangeSection->LowAddress);
- ReadyToRunInfo * pReadyToRunInfo = dac_cast(pRangeSection->pHeapListOrZapModule)->GetReadyToRunInfo();
+ PTR_ReadyToRunInfo pReadyToRunInfo = dac_cast(pRangeSection->pHeapListOrZapModule)->GetReadyToRunInfo();
- IMAGE_DATA_DIRECTORY * pDelayLoadMethodCallThunksDir = pReadyToRunInfo->FindSection(ReadyToRunSectionType::DelayLoadMethodCallThunks);
+ PTR_IMAGE_DATA_DIRECTORY pDelayLoadMethodCallThunksDir = pReadyToRunInfo->GetDelayMethodCallThunksSection();
if (pDelayLoadMethodCallThunksDir != NULL)
{
if (pDelayLoadMethodCallThunksDir->VirtualAddress <= rva
@@ -6778,6 +6779,12 @@ BOOL ReadyToRunJitManager::JitCodeToMethodInfo(RangeSection * pRangeSection,
// READYTORUN: FUTURE: Hot-cold spliting
+ // If the address is in a thunk, return NULL.
+ if (GetStubCodeBlockKind(pRangeSection, currentPC) != STUB_CODE_BLOCK_UNKNOWN)
+ {
+ return FALSE;
+ }
+
TADDR currentInstr = PCODEToPINSTR(currentPC);
TADDR ImageBase = pRangeSection->LowAddress;
diff --git a/src/coreclr/src/vm/codeman.h b/src/coreclr/src/vm/codeman.h
index ae76b6939da0c6..d90f54df7cd5ac 100644
--- a/src/coreclr/src/vm/codeman.h
+++ b/src/coreclr/src/vm/codeman.h
@@ -1619,7 +1619,7 @@ class NativeUnwindInfoLookupTable
#ifdef FEATURE_READYTORUN
-class ReadyToRunJitManager : public IJitManager
+class ReadyToRunJitManager final: public IJitManager
{
VPTR_VTABLE_CLASS(ReadyToRunJitManager, IJitManager)
diff --git a/src/coreclr/src/vm/readytoruninfo.cpp b/src/coreclr/src/vm/readytoruninfo.cpp
index b5b69edaac27e5..06d238b44b6c58 100644
--- a/src/coreclr/src/vm/readytoruninfo.cpp
+++ b/src/coreclr/src/vm/readytoruninfo.cpp
@@ -683,6 +683,8 @@ ReadyToRunInfo::ReadyToRunInfo(Module * pModule, PEImageLayout * pLayout, READYT
m_methodDefEntryPoints = NativeArray(&m_nativeReader, pEntryPointsDir->VirtualAddress);
}
+ m_pSectionDelayLoadMethodCallThunks = m_component.FindSection(ReadyToRunSectionType::DelayLoadMethodCallThunks);
+
IMAGE_DATA_DIRECTORY * pinstMethodsDir = m_pComposite->FindSection(ReadyToRunSectionType::InstanceMethodEntryPoints);
if (pinstMethodsDir != NULL)
{
diff --git a/src/coreclr/src/vm/readytoruninfo.h b/src/coreclr/src/vm/readytoruninfo.h
index b7ec74769bd954..7a1919aa04b9c9 100644
--- a/src/coreclr/src/vm/readytoruninfo.h
+++ b/src/coreclr/src/vm/readytoruninfo.h
@@ -61,6 +61,8 @@ class ReadyToRunInfo
PTR_RUNTIME_FUNCTION m_pRuntimeFunctions;
DWORD m_nRuntimeFunctions;
+ PTR_IMAGE_DATA_DIRECTORY m_pSectionDelayLoadMethodCallThunks;
+
PTR_CORCOMPILE_IMPORT_SECTION m_pImportSections;
DWORD m_nImportSections;
@@ -92,6 +94,8 @@ class ReadyToRunInfo
PTR_READYTORUN_HEADER GetReadyToRunHeader() const { return m_pHeader; }
+ PTR_IMAGE_DATA_DIRECTORY GetDelayMethodCallThunksSection() const { return m_pSectionDelayLoadMethodCallThunks; }
+
PTR_NativeImage GetNativeImage() const { return m_pNativeImage; }
PTR_PEImageLayout GetImage() const { return m_pComposite->GetImage(); }