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(); }