diff --git a/docs/design/datacontracts/ExecutionManager.md b/docs/design/datacontracts/ExecutionManager.md index cf7163569789f9..61e7b2a8b93bfc 100644 --- a/docs/design/datacontracts/ExecutionManager.md +++ b/docs/design/datacontracts/ExecutionManager.md @@ -52,6 +52,10 @@ struct CodeBlockHandle // Get the exception clause info for the code block List GetExceptionClauses(CodeBlockHandle codeInfoHandle); + // Classify a code address as a known stub kind (precode, jump stub, VSD stub, etc.). + // Returns Unknown if the address is not a recognized stub. + StubKind GetStubKind(TargetCodePointer jittedCodeAddress); + // Extension Methods (implemented in terms of other APIs) // Returns true if the code block is a funclet (exception handler, filter, or finally) bool IsFunclet(CodeBlockHandle codeInfoHandle); @@ -119,6 +123,21 @@ public struct ExceptionClauseInfo public TargetNUInt? TypeHandle; public TargetPointer? ModuleAddr; } + +public enum StubKind : uint +{ + Unknown = 0, + JumpStub = 1, + DynamicHelper = 3, + Prestub = 4, + VSD_DispatchStub = 5, + VSD_ResolveStub = 6, + VSD_LookupStub = 7, + VSD_VTableStub = 8, + CallCountingStub = 9, + StubLinkStub = 10, + MethodCallThunk = 11, +} ``` ## Version 1 @@ -143,6 +162,8 @@ Data descriptors used: | `RangeSection` | `Flags` | Flags for the range section | | `RangeSection` | `HeapList` | Pointer to the heap list | | `RangeSection` | `R2RModule` | ReadyToRun module | +| `RangeSection` | `RangeList` | Pointer to the `CodeRangeMapRangeList` associated with this range section | +| `CodeRangeMapRangeList` | `RangeListType` | Integer identifying the stub code block kind for this range list | | `CodeHeapListNode` | `Next` | Next node | | `CodeHeapListNode` | `StartAddress` | Start address of the used portion of the code heap | | `CodeHeapListNode` | `EndAddress` | End address of the used portion of the code heap | @@ -501,6 +522,29 @@ After obtaining the clause array bounds, the common iteration logic classifies e `IsFilterFunclet` first checks `IsFunclet`. If the code block is a funclet, it retrieves the EH clauses for the method and checks whether any filter clause's handler offset matches the funclet's relative offset. If a match is found, the funclet is a filter funclet. +### Stub Kind Classification + +`GetStubKind` classifies a code address as a known stub type or managed code. It returns `Unknown` if the address is not recognized. + +The method looks up the address in the `RangeSectionMap`. If a `RangeSection` is found, the JIT manager for that section classifies the code: + +- **EEJitManager**: If the range section is a range list, reads the `CodeRangeMapRangeList.RangeListType` to determine the stub code block kind. Otherwise, it uses the nibble map to find the method code start, reads the code header indirect pointer, and checks whether it is a stub code block (value ≤ `StubCodeBlockLast`). If so, the value identifies the specific stub kind. +- **ReadyToRunJitManager**: Checks whether the address falls within a delay-load method call thunk region. + +```csharp +StubKind GetStubKind(TargetCodePointer jittedCodeAddress) +{ + TargetPointer address = CodePointerUtils.AddressFromCodePointer(jittedCodeAddress); + + // Look up in range section map + RangeSection range = FindRangeSection(jittedCodeAddress); + if (range == null) return StubKind.Unknown; + + JitManager jitManager = GetJitManager(range); + return jitManager.GetStubCodeBlockKind(range, jittedCodeAddress); +} +``` + ### EE JIT Manager and Code Heap Info ```csharp diff --git a/src/coreclr/debug/daccess/daccess.cpp b/src/coreclr/debug/daccess/daccess.cpp index 0c8801da35fe27..1a2dc72a430021 100644 --- a/src/coreclr/debug/daccess/daccess.cpp +++ b/src/coreclr/debug/daccess/daccess.cpp @@ -5309,86 +5309,6 @@ ClrDataAccess::GetJitHelperName(IN TADDR address) return NULL; } -// This function expects more memory than maybe needed. -static int FormatCLRStubName( - _In_opt_z_ LPCWSTR stubNameMaybe, - _In_ TADDR stubAddr, - _In_ ULONG32 bufLen, - _Out_ ULONG32 *symbolLen, - _Out_writes_bytes_opt_(bufLen) WCHAR* symbolBuf) -{ - // Parts needed to construct a name: - // With stub manager name: "CLRStub[%s]@%p" - // No stub manager name: "CLRStub@%p" - const WCHAR formatName_Prefix[] = W("CLRStub"); - const WCHAR formatName_OpenBracket[] = W("["); - const WCHAR formatName_CloseBracket[] = W("]"); - const WCHAR formatName_PrefixEnd[] = W("@"); - - // Compute the address as a string safely. - WCHAR addrString[Max64BitHexString + 1]; - FormatInteger(addrString, ARRAY_SIZE(addrString), "%p", stubAddr); - size_t addStringLen = u16_strlen(addrString); - - // Compute maximum length, include the null terminator. - size_t formatName_MaxLen = ARRAY_SIZE(formatName_Prefix) // Include trailing null - + ARRAY_SIZE(formatName_PrefixEnd) - 1 - + addStringLen; - - // Consider stub manager name - size_t stubManagedNameLen = 0; - if (stubNameMaybe != NULL) - { - stubManagedNameLen = u16_strlen(stubNameMaybe); - formatName_MaxLen += ARRAY_SIZE(formatName_OpenBracket) - 1; - formatName_MaxLen += ARRAY_SIZE(formatName_CloseBracket) - 1; - } - - HRESULT hr = S_FALSE; - - // Compute the exact length needed. - const size_t lenNeeded = formatName_MaxLen + stubManagedNameLen; - if (lenNeeded <= bufLen) - { - size_t written = 0; - - // Set the prefix - wcscpy_s(symbolBuf, bufLen - written, formatName_Prefix); - written += ARRAY_SIZE(formatName_Prefix) - 1; - - // Add the name - if (stubManagedNameLen > 0) - { - wcscat_s(symbolBuf, bufLen - written, formatName_OpenBracket); - written += ARRAY_SIZE(formatName_OpenBracket) - 1; - wcscat_s(symbolBuf, bufLen - written, stubNameMaybe); - written += stubManagedNameLen; - wcscat_s(symbolBuf, bufLen - written, formatName_CloseBracket); - written += ARRAY_SIZE(formatName_CloseBracket) - 1; - } - - // Append the prefix end - wcscat_s(symbolBuf, bufLen - written, formatName_PrefixEnd); - written += ARRAY_SIZE(formatName_PrefixEnd) - 1; - - // Append the address - wcscat_s(symbolBuf, bufLen - written, addrString); - written += addStringLen; - - hr = S_OK; - } - - if (symbolLen) - { - if (!FitsIn(lenNeeded)) - return COR_E_OVERFLOW; - - *symbolLen = (ULONG32)lenNeeded; - } - - return hr; -} - HRESULT ClrDataAccess::RawGetMethodName( /* [in] */ CLRDATA_ADDRESS address, @@ -5422,86 +5342,22 @@ ClrDataAccess::RawGetMethodName( } PTR_StubManager pStubManager; - MethodDesc* methodDesc = NULL; - - { - EECodeInfo codeInfo(GetInterpreterCodeFromInterpreterPrecodeIfPresent(TO_TADDR(address))); - if (codeInfo.IsValid()) - { - if (displacement) - { - *displacement = codeInfo.GetRelOffset(); - } - - methodDesc = codeInfo.GetMethodDesc(); - goto NameFromMethodDesc; - } - } pStubManager = StubManager::FindStubManager(TO_TADDR(address)); if (pStubManager != NULL) { - if (displacement) - { - *displacement = 0; - } - - // - // Special-cased stub managers - // - if (pStubManager == PrecodeStubManager::g_pManager) + LPCWSTR wszStubManagerName = pStubManager->GetStubManagerName(TO_TADDR(address)); + _ASSERTE(wszStubManagerName != NULL); + if (u16_strcmp(wszStubManagerName, W("ThePreStub")) != 0 && u16_strcmp(wszStubManagerName, W("InteropDispatchStub")) != 0 && u16_strcmp(wszStubManagerName, W("TailCallStub")) != 0) { - PCODE alignedAddress = AlignDown(TO_TADDR(address), PRECODE_ALIGNMENT); - -#ifdef TARGET_ARM - alignedAddress += THUMB_CODE; -#endif - - SIZE_T maxPrecodeSize = sizeof(StubPrecode); - -#ifdef HAS_THISPTR_RETBUF_PRECODE - maxPrecodeSize = max((size_t)maxPrecodeSize, sizeof(ThisPtrRetBufPrecode)); -#endif - - for (SIZE_T i = 0; i < maxPrecodeSize / PRECODE_ALIGNMENT; i++) + // Skip the stubs that are just assembly helpers. + wcscpy_s(symbolBuf, bufLen, wszStubManagerName); + if (displacement) { - EX_TRY - { - // Try to find matching precode entrypoint - Precode* pPrecode = Precode::GetPrecodeFromEntryPoint(alignedAddress, TRUE); - if (pPrecode != NULL && pPrecode->GetType() != PRECODE_UMENTRY_THUNK) - { - methodDesc = pPrecode->GetMethodDesc(); - if (methodDesc != NULL) - { - if (DacValidateMD(methodDesc)) - { - if (displacement) - { - *displacement = TO_TADDR(address) - PCODEToPINSTR(alignedAddress); - } - goto NameFromMethodDesc; - } - } - } - alignedAddress -= PRECODE_ALIGNMENT; - } - EX_CATCH - { - } - EX_END_CATCH + *displacement = 0; } + return S_OK; } - - LPCWSTR wszStubManagerName = pStubManager->GetStubManagerName(TO_TADDR(address)); - _ASSERTE(wszStubManagerName != NULL); - - return FormatCLRStubName( - wszStubManagerName, - TO_TADDR(address), - bufLen, - symbolLen, - symbolBuf); } // Do not waste time looking up name for static helper. Debugger can get the actual name from .pdb. @@ -5509,33 +5365,18 @@ ClrDataAccess::RawGetMethodName( pHelperName = GetJitHelperName(TO_TADDR(address)); if (pHelperName != NULL) { - if (displacement) - { - *displacement = 0; - } - HRESULT hr = ConvertUtf8(pHelperName, bufLen, symbolLen, symbolBuf); if (FAILED(hr)) return S_FALSE; + if (displacement) + { + *displacement = 0; + } return S_OK; } return E_NOINTERFACE; - -NameFromMethodDesc: - if (methodDesc->GetClassification() == mcDynamic - && methodDesc->GetSigParser().IsNull()) - { - return FormatCLRStubName( - NULL, - TO_TADDR(address), - bufLen, - symbolLen, - symbolBuf); - } - - return GetFullMethodName(methodDesc, bufLen, symbolLen, symbolBuf); } HRESULT diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index 5ba8d32028e0ef..033a2d62645e3e 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -4728,9 +4728,9 @@ StubCodeBlockKind EEJitManager::GetStubCodeBlockKind(RangeSection * pRangeSectio TADDR start = dac_cast(pRangeSection->_pjit)->FindMethodCode(pRangeSection, currentPC); if (start == (TADDR)0) - return STUB_CODE_BLOCK_NOCODE; + return STUB_CODE_BLOCK_UNKNOWN; CodeHeader * pCHdr = PTR_CodeHeader(start - sizeof(CodeHeader)); - return pCHdr->IsStubCodeBlock() ? pCHdr->GetStubCodeBlockKind() : STUB_CODE_BLOCK_MANAGED; + return pCHdr->IsStubCodeBlock() ? pCHdr->GetStubCodeBlockKind() : STUB_CODE_BLOCK_UNKNOWN; } @@ -4744,7 +4744,7 @@ TADDR EECodeGenManager::FindMethodCode(PCODE currentPC) RangeSection * pRS = ExecutionManager::FindCodeRange(currentPC, ExecutionManager::GetScanFlags()); if (pRS == NULL || (pRS->_flags & RangeSection::RANGE_SECTION_CODEHEAP) == 0) - return STUB_CODE_BLOCK_NOCODE; + return STUB_CODE_BLOCK_UNKNOWN; return dac_cast(pRS->_pjit)->FindMethodCode(pRS, currentPC); } diff --git a/src/coreclr/vm/codeman.h b/src/coreclr/vm/codeman.h index 668b95beb9ddef..c6636548a6d9e1 100644 --- a/src/coreclr/vm/codeman.h +++ b/src/coreclr/vm/codeman.h @@ -113,8 +113,6 @@ enum StubCodeBlockKind : int // Last valid value. Note that the definition is duplicated in debug\daccess\fntableaccess.cpp STUB_CODE_BLOCK_LAST = 0xF, // Placeholders returned by code:GetStubCodeBlockKind - STUB_CODE_BLOCK_NOCODE = 0x10, - STUB_CODE_BLOCK_MANAGED = 0x11, STUB_CODE_BLOCK_STUBLINK = 0x12, // Placeholder used by ReadyToRun images STUB_CODE_BLOCK_METHOD_CALL_THUNK = 0x13, @@ -128,8 +126,6 @@ inline const char *GetStubCodeBlockKindString(StubCodeBlockKind kind) return "JumpStub"; case STUB_CODE_BLOCK_STUBLINK: return "StubLinkStub"; - case STUB_CODE_BLOCK_MANAGED: - return "Managed"; case STUB_CODE_BLOCK_METHOD_CALL_THUNK: return "MethodCallThunk"; #ifdef FEATURE_TIERED_COMPILATION @@ -793,6 +789,7 @@ template<> struct cdac_data static constexpr size_t Flags = offsetof(RangeSection, _flags); static constexpr size_t HeapList = offsetof(RangeSection, _pHeapList); static constexpr size_t R2RModule = offsetof(RangeSection, _pR2RModule); + static constexpr size_t RangeList = offsetof(RangeSection, _pRangeList); }; enum class RangeSectionLockState diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index a13b9b275ce31c..baa439d0c0b49f 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -805,8 +805,14 @@ CDAC_TYPE_FIELD(RangeSection, T_POINTER, JitManager, cdac_data::Ji CDAC_TYPE_FIELD(RangeSection, T_INT32, Flags, cdac_data::Flags) CDAC_TYPE_FIELD(RangeSection, T_POINTER, HeapList, cdac_data::HeapList) CDAC_TYPE_FIELD(RangeSection, T_POINTER, R2RModule, cdac_data::R2RModule) +CDAC_TYPE_FIELD(RangeSection, T_POINTER, RangeList, cdac_data::RangeList) CDAC_TYPE_END(RangeSection) +CDAC_TYPE_BEGIN(CodeRangeMapRangeList) +CDAC_TYPE_INDETERMINATE(CodeRangeMapRangeList) +CDAC_TYPE_FIELD(CodeRangeMapRangeList, T_INT32, RangeListType, cdac_data::RangeListType) +CDAC_TYPE_END(CodeRangeMapRangeList) + CDAC_TYPE_BEGIN(EEJitManager) CDAC_TYPE_INDETERMINATE(EEJitManager) CDAC_TYPE_FIELD(EEJitManager, T_BOOL, StoreRichDebugInfo, cdac_data::StoreRichDebugInfo) diff --git a/src/coreclr/vm/loaderallocator.hpp b/src/coreclr/vm/loaderallocator.hpp index 68c372185b4723..23b2da9157727f 100644 --- a/src/coreclr/vm/loaderallocator.hpp +++ b/src/coreclr/vm/loaderallocator.hpp @@ -182,6 +182,13 @@ class CodeRangeMapRangeList : public RangeList SArray _starts; void* _id; bool _collectible; + friend struct ::cdac_data; +}; + +template<> +struct cdac_data +{ + static constexpr size_t RangeListType = offsetof(CodeRangeMapRangeList, _rangeListType); }; // Iterator over Assemblies in the same ALC diff --git a/src/coreclr/vm/stubmgr.h b/src/coreclr/vm/stubmgr.h index bbce9d33591dd2..da04e1f11dbe63 100644 --- a/src/coreclr/vm/stubmgr.h +++ b/src/coreclr/vm/stubmgr.h @@ -432,7 +432,7 @@ class PrecodeStubManager : public StubManager protected: virtual LPCWSTR GetStubManagerName(PCODE addr) - { LIMITED_METHOD_CONTRACT; return W("MethodDescPrestub"); } + { LIMITED_METHOD_CONTRACT; return W("Prestub"); } #endif }; #endif // !FEATURE_PORTABLE_ENTRYPOINTS diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IExecutionManager.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IExecutionManager.cs index 0dddd31417e5ad..3c1630b3442f35 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IExecutionManager.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IExecutionManager.cs @@ -42,6 +42,21 @@ public struct JitManagerInfo public TargetPointer HeapListAddress; } +public enum StubKind : uint +{ + Unknown = 0, + JumpStub = 1, + DynamicHelper = 3, + Prestub = 4, + VSD_DispatchStub = 5, + VSD_ResolveStub = 6, + VSD_LookupStub = 7, + VSD_VTableStub = 8, + CallCountingStub = 9, + StubLinkStub = 10, + MethodCallThunk = 11, +} + public interface ICodeHeapInfo { } @@ -101,6 +116,9 @@ public interface IExecutionManager : IContract List GetExceptionClauses(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException(); JitManagerInfo GetEEJitManagerInfo() => throw new NotImplementedException(); IEnumerable GetCodeHeapInfos() => throw new NotImplementedException(); + // Classify a code address as a known stub kind (precode, jump stub, VSD stub, etc.) + // or as managed code. Returns Unknown if the address is not recognized. + StubKind GetStubKind(TargetCodePointer jittedCodeAddress) => throw new NotImplementedException(); } public readonly struct ExecutionManager : IExecutionManager diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs index 521d3b56783f2d..190e85f07043fe 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs @@ -168,6 +168,7 @@ public enum DataType ComInterfaceEntry, InternalComInterfaceDispatch, AuxiliarySymbolInfo, + CodeRangeMapRangeList, /* GC Data Types */ diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs index 41eccea4c65d96..07dd7b7972a4c6 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs @@ -82,6 +82,7 @@ public static class Globals public const string ExecutionManagerCodeRangeMapAddress = nameof(ExecutionManagerCodeRangeMapAddress); public const string EEJitManagerAddress = nameof(EEJitManagerAddress); public const string StubCodeBlockLast = nameof(StubCodeBlockLast); + public const string DefaultADID = nameof(DefaultADID); public const string StaticsPointerMask = nameof(StaticsPointerMask); public const string PtrArrayOffsetToDataArray = nameof(PtrArrayOffsetToDataArray); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.EEJitManager.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.EEJitManager.cs index a3e73ed437a098..06f6476ea68f5e 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.EEJitManager.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.EEJitManager.cs @@ -20,6 +20,20 @@ public EEJitManager(Target target, INibbleMap nibbleMap) : base(target) _runtimeFunctions = RuntimeFunctionLookup.Create(target); } + private enum StubCodeBlockKind : int + { + Unknown = 0, + JumpStub = 1, + DynamicHelper = 3, + StubPrecode = 4, + FixupPrecode = 5, + VSDDispatchStub = 6, + VSDResolveStub = 7, + VSDLookupStub = 8, + VSDVTableStub = 9, + CallCountingStub = 10, + } + public override bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, [NotNullWhen(true)] out CodeBlock? info) { info = null; @@ -117,6 +131,19 @@ public override TargetPointer GetDebugInfo(RangeSection rangeSection, TargetCode return realCodeHeader.DebugInfo; } + public override StubKind GetStubCodeBlockKind(RangeSection rangeSection, TargetCodePointer jittedCodeAddress) + { + if (rangeSection.IsRangeList) + { + Data.CodeRangeMapRangeList rangeList = Target.ProcessedData.GetOrAdd(rangeSection.Data!.RangeList); + return GetStubKind((StubCodeBlockKind)rangeList.RangeListType); + } + TargetPointer startAddr = FindMethodCode(rangeSection, jittedCodeAddress); // validate that the code address is within the method's code range + if (startAddr == TargetPointer.Null) + return StubKind.Unknown; + return GetCodeHeaderStubKind(rangeSection, startAddr); + } + public override void GetGCInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, out TargetPointer gcInfo, out uint gcVersion) { gcInfo = TargetPointer.Null; @@ -154,9 +181,9 @@ private TargetPointer FindMethodCode(RangeSection rangeSection, TargetCodePointe return _nibbleMap.FindMethodCode(heapListNode, jittedCodeAddress); } - private bool GetRealCodeHeader(RangeSection rangeSection, TargetPointer codeStart, [NotNullWhen(true)] out Data.RealCodeHeader? realCodeHeader) + private bool GetCodeHeaderAddress(RangeSection rangeSection, TargetPointer codeStart, out TargetPointer codeHeaderAddress) { - realCodeHeader = null; + codeHeaderAddress = TargetPointer.Null; // EEJitManager::JitCodeToMethodInfo if (rangeSection.IsRangeList) return false; @@ -170,15 +197,53 @@ private bool GetRealCodeHeader(RangeSection rangeSection, TargetPointer codeStar // See EEJitManager::GetCodeHeaderFromStartAddress in vm/codeman.h int codeHeaderOffset = Target.PointerSize; TargetPointer codeHeaderIndirect = new TargetPointer(codeStart - (ulong)codeHeaderOffset); - if (RangeSection.IsStubCodeBlock(Target, codeHeaderIndirect)) + codeHeaderAddress = Target.ReadPointer(codeHeaderIndirect); + return true; + } + + private bool GetRealCodeHeader(RangeSection rangeSection, TargetPointer codeStart, [NotNullWhen(true)] out Data.RealCodeHeader? realCodeHeader) + { + realCodeHeader = null; + if (!GetCodeHeaderAddress(rangeSection, codeStart, out TargetPointer codeHeaderAddress)) + { + return false; + } + if (RangeSection.IsStubCodeBlock(Target, codeHeaderAddress)) { return false; } - TargetPointer codeHeaderAddress = Target.ReadPointer(codeHeaderIndirect); realCodeHeader = Target.ProcessedData.GetOrAdd(codeHeaderAddress); return true; } + private static StubKind GetStubKind(StubCodeBlockKind stubCodeBlockKind) + { + return stubCodeBlockKind switch + { + StubCodeBlockKind.JumpStub => StubKind.JumpStub, + StubCodeBlockKind.DynamicHelper => StubKind.DynamicHelper, + StubCodeBlockKind.StubPrecode or StubCodeBlockKind.FixupPrecode => StubKind.Prestub, + StubCodeBlockKind.VSDDispatchStub => StubKind.VSD_DispatchStub, + StubCodeBlockKind.VSDResolveStub => StubKind.VSD_ResolveStub, + StubCodeBlockKind.VSDLookupStub => StubKind.VSD_LookupStub, + StubCodeBlockKind.VSDVTableStub => StubKind.VSD_VTableStub, + StubCodeBlockKind.CallCountingStub => StubKind.CallCountingStub, + _ => StubKind.Unknown, + }; + } + + private StubKind GetCodeHeaderStubKind(RangeSection rangeSection, TargetPointer codeStart) + { + if (GetCodeHeaderAddress(rangeSection, codeStart, out TargetPointer codeHeaderAddress)) + { + if (RangeSection.IsStubCodeBlock(Target, codeHeaderAddress)) + { + return GetStubKind((StubCodeBlockKind)codeHeaderAddress.Value); + } + } + return StubKind.Unknown; + } + public override void GetExceptionClauses(RangeSection rangeSection, CodeBlockHandle codeInfoHandle, out TargetPointer startAddr, out TargetPointer endAddr) { startAddr = TargetPointer.Null; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs index ff05f37b6c6628..c60ea39819678b 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs @@ -142,6 +142,13 @@ public override TargetPointer GetDebugInfo(RangeSection rangeSection, TargetCode return imageBase + debugInfoOffset; } + public override StubKind GetStubCodeBlockKind(RangeSection rangeSection, TargetCodePointer jittedCodeAddress) + { + if (rangeSection.Data == null) + return StubKind.Unknown; + return IsStubCodeBlockThunk(rangeSection.Data, GetReadyToRunInfo(rangeSection), jittedCodeAddress) ? StubKind.MethodCallThunk : StubKind.Unknown; + } + public override void GetGCInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, out TargetPointer gcInfo, out uint gcVersion) { gcInfo = TargetPointer.Null; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.cs index 43e89fbe2237e7..5db7e99481a447 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.cs @@ -99,6 +99,7 @@ public abstract void GetMethodRegionInfo( public abstract TargetPointer GetDebugInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, out bool hasFlagByte); public abstract void GetGCInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, out TargetPointer gcInfo, out uint gcVersion); public abstract void GetExceptionClauses(RangeSection rangeSection, CodeBlockHandle codeInfoHandle, out TargetPointer startAddr, out TargetPointer endAddr); + public abstract StubKind GetStubCodeBlockKind(RangeSection rangeSection, TargetCodePointer jittedCodeAddress); } private sealed class RangeSection @@ -539,4 +540,14 @@ List IExecutionManager.GetExceptionClauses(CodeBlockHandle } return exceptionClauses; } + + public StubKind GetStubKind(TargetCodePointer jittedCodeAddress) + { + RangeSection range = RangeSection.Find(_target, _topRangeSectionMap, _rangeSectionMapLookup, jittedCodeAddress); + if (range.Data == null) + return StubKind.Unknown; + + JitManager jitManager = GetJitManager(range.Data); + return jitManager.GetStubCodeBlockKind(range, jittedCodeAddress); + } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_1.cs index b636a2914c36ec..c23094f8c88c93 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_1.cs @@ -34,5 +34,6 @@ internal ExecutionManager_1(Target target) public List GetExceptionClauses(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetExceptionClauses(codeInfoHandle); public JitManagerInfo GetEEJitManagerInfo() => _executionManagerCore.GetEEJitManagerInfo(); public IEnumerable GetCodeHeapInfos() => _executionManagerCore.GetCodeHeapInfos(); + public StubKind GetStubKind(TargetCodePointer entryPoint) => _executionManagerCore.GetStubKind(entryPoint); public void Flush() => _executionManagerCore.Flush(); } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_2.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_2.cs index 6b84fda982ab5e..ee17e586860b31 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_2.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_2.cs @@ -34,5 +34,6 @@ internal ExecutionManager_2(Target target) public List GetExceptionClauses(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetExceptionClauses(codeInfoHandle); public JitManagerInfo GetEEJitManagerInfo() => _executionManagerCore.GetEEJitManagerInfo(); public IEnumerable GetCodeHeapInfos() => _executionManagerCore.GetCodeHeapInfos(); + public StubKind GetStubKind(TargetCodePointer entryPoint) => _executionManagerCore.GetStubKind(entryPoint); public void Flush() => _executionManagerCore.Flush(); } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/CodeRangeMapRangeList.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/CodeRangeMapRangeList.cs new file mode 100644 index 00000000000000..edcefc8f8d5114 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/CodeRangeMapRangeList.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class CodeRangeMapRangeList : IData +{ + static CodeRangeMapRangeList IData.Create(Target target, TargetPointer address) + => new CodeRangeMapRangeList(target, address); + + public CodeRangeMapRangeList(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.CodeRangeMapRangeList); + RangeListType = target.ReadField(address, type, nameof(RangeListType)); + } + + public int RangeListType { get; init; } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RangeSection.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RangeSection.cs index cf54d9ddcef9d6..f104ca8df6bae1 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RangeSection.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RangeSection.cs @@ -18,6 +18,7 @@ public RangeSection(Target target, TargetPointer address) Flags = target.ReadField(address, type, nameof(Flags)); HeapList = target.ReadPointerField(address, type, nameof(HeapList)); R2RModule = target.ReadPointerField(address, type, nameof(R2RModule)); + RangeList = target.ReadPointerField(address, type, nameof(RangeList)); } public TargetPointer RangeBegin { get; init; } @@ -27,4 +28,5 @@ public RangeSection(Target target, TargetPointer address) public TargetPointer HeapList { get; init; } public int Flags { get; init; } public TargetPointer R2RModule { get; init; } + public TargetPointer RangeList { get; init; } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.IXCLRDataProcess.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.IXCLRDataProcess.cs index 590e529ffc480f..6b3f70dfe0ed74 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.IXCLRDataProcess.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.IXCLRDataProcess.cs @@ -101,7 +101,93 @@ int IXCLRDataProcess.GetRuntimeNameByAddress( uint* nameLen, char* nameBuf, ClrDataAddress* displacement) - => LegacyFallbackHelper.CanFallback() && _legacyProcess is not null ? _legacyProcess.GetRuntimeNameByAddress(address, flags, bufLen, nameLen, nameBuf, displacement) : HResults.E_NOTIMPL; + { + int hr = HResults.S_OK; + try + { + if (flags != 0) + throw new ArgumentException("Flags are not supported", nameof(flags)); + + TargetCodePointer codeAddr = address.ToTargetCodePointer(_target); + + // IsPossibleCodeAddress - validate the address is readable + if (!_target.TryRead(codeAddr, out byte _)) + throw new ArgumentException("Address is not readable", nameof(address)); + + IExecutionManager eman = _target.Contracts.ExecutionManager; + string? resultName = null; + + // Try stub classification + StubKind stubKind = eman.GetStubKind(codeAddr); + resultName = GetStubName(stubKind); + + // try aux symbols + if (resultName is null && _target.Contracts.AuxiliarySymbols.TryGetAuxiliarySymbolName(address.ToTargetPointer(_target), out string? auxSymbolName)) + { + resultName = auxSymbolName; + } + + if (resultName is null) + { + throw new InvalidCastException(); + } + else if (displacement is not null) + { + *displacement = 0; + } + + OutputBufferHelpers.CopyStringToBuffer(nameBuf, bufLen, nameLen, resultName); + + if (nameBuf is not null && bufLen < resultName.Length + 1) + hr = HResults.S_FALSE; + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyProcess is not null) + { + uint nameLenLocal = 0; + char[] nameBufLocal = new char[bufLen > 0 ? bufLen : 1]; + ClrDataAddress displacementLocal = default; + int hrLocal; + fixed (char* pNameBufLocal = nameBufLocal) + { + hrLocal = _legacyProcess.GetRuntimeNameByAddress( + address, flags, bufLen, + &nameLenLocal, + nameBuf is null ? null : pNameBufLocal, + displacement is null ? null : &displacementLocal); + } + + Debug.ValidateHResult(hr, hrLocal); + if (hr == HResults.S_OK || hr == HResults.S_FALSE) + { + Debug.Assert(nameLen is null || *nameLen == nameLenLocal); + if (nameBuf is not null) + { + Debug.Assert(new ReadOnlySpan(nameBuf, (int)nameLenLocal) + .SequenceEqual(nameBufLocal.AsSpan(0, (int)nameLenLocal))); + } + if (displacement is not null) + { + Debug.Assert(*displacement == displacementLocal); + } + } + } +#endif + + return hr; + } + + private static string? GetStubName(Contracts.StubKind stubKind) + { + if (stubKind == Contracts.StubKind.Unknown) + return null; + return stubKind.ToString(); + } int IXCLRDataProcess.StartEnumAppDomains(ulong* handle) => LegacyFallbackHelper.CanFallback() && _legacyProcess is not null ? _legacyProcess.StartEnumAppDomains(handle) : HResults.E_NOTIMPL; diff --git a/src/native/managed/cdac/tests/ExecutionManager/ExecutionManagerTests.cs b/src/native/managed/cdac/tests/ExecutionManager/ExecutionManagerTests.cs index 7f8b5cff5b5603..cc3ab112551453 100644 --- a/src/native/managed/cdac/tests/ExecutionManager/ExecutionManagerTests.cs +++ b/src/native/managed/cdac/tests/ExecutionManager/ExecutionManagerTests.cs @@ -29,6 +29,8 @@ public class ExecutionManagerTests [DataType.ReadyToRunInfo] = TargetTestHelpers.CreateTypeInfo(emBuilder.ReadyToRunInfoLayout), [DataType.EEJitManager] = TargetTestHelpers.CreateTypeInfo(emBuilder.EEJitManagerLayout), [DataType.Module] = TargetTestHelpers.CreateTypeInfo(emBuilder.ModuleLayout), + [DataType.CodeRangeMapRangeList] = TargetTestHelpers.CreateTypeInfo(emBuilder.CodeRangeMapRangeListLayout), + [DataType.ImageDataDirectory] = TargetTestHelpers.CreateTypeInfo(emBuilder.ImageDataDirectoryLayout), [DataType.HashMap] = TargetTestHelpers.CreateTypeInfo(MockHashMap.CreateLayout(helpers.Arch)), [DataType.Bucket] = TargetTestHelpers.CreateTypeInfo(MockHashMapBucket.CreateLayout(helpers.Arch)), }; @@ -655,6 +657,160 @@ public void GetCodeHeapList_LinkedList_TwoNodes(string version, MockTarget.Archi Assert.Equal(currentAddr, hostInfo.CurrentAddress); } + [Theory] + [MemberData(nameof(StdArchAllVersions))] + public void GetStubKind_NoRangeSection(string version, MockTarget.Architecture arch) + { + IExecutionManager em = CreateExecutionManagerContract(version, arch); + + Assert.Equal(StubKind.Unknown, em.GetStubKind(new TargetCodePointer(0x00aa_9000))); + } + + [Theory] + [MemberData(nameof(StdArchAllVersions))] + public void GetStubKind_RangeListStubs(string version, MockTarget.Architecture arch) + { + const ulong codeRangeStart = 0x0a0a_0000u; + const uint codeRangeSize = 0x4000u; + const ulong jitManagerAddress = 0x000b_ff00; + const int stubCodeBlockKindPrecode = 4; // STUB_CODE_BLOCK_STUBPRECODE + + IExecutionManager em = CreateExecutionManagerContract( + version, + arch, + emBuilder => + { + var jittedCode = emBuilder.AllocateJittedCodeRange(codeRangeStart, codeRangeSize); + MockRangeSection rangeSection = emBuilder.AddRangeListRangeSection(jittedCode, jitManagerAddress, stubCodeBlockKindPrecode); + _ = emBuilder.AddRangeSectionFragment(jittedCode, rangeSection.Address); + }); + + StubKind kind = em.GetStubKind(new TargetCodePointer(codeRangeStart + 0x100)); + Assert.Equal(StubKind.Prestub, kind); + } + + [Theory] + [MemberData(nameof(StdArchAllVersions))] + public void GetStubKind_CodeHeapStubs(string version, MockTarget.Architecture arch) + { + const ulong codeRangeStart = 0x0a0a_0000u; + const uint codeRangeSize = 0xc000u; + const uint stubSize = 0x20; + const ulong jitManagerAddress = 0x000b_ff00; + const int stubCodeBlockKindJumpStub = 1; // STUB_CODE_BLOCK_JUMPSTUB + ulong stubCodeAddress = 0; + + IExecutionManager em = CreateExecutionManagerContract( + version, + arch, + emBuilder => + { + var jittedCode = emBuilder.AllocateJittedCodeRange(codeRangeStart, codeRangeSize); + stubCodeAddress = emBuilder.AddStubCodeBlock(jittedCode, stubSize, stubCodeBlockKindJumpStub).CodeAddress; + + NibbleMapTestBuilderBase nibBuilder = emBuilder.CreateNibbleMap(codeRangeStart, codeRangeSize); + nibBuilder.AllocateCodeChunk(new TargetCodePointer(stubCodeAddress), stubSize); + + MockCodeHeapListNode codeHeapListNode = emBuilder.AddCodeHeapListNode(0, codeRangeStart, codeRangeStart + codeRangeSize, codeRangeStart, nibBuilder.NibbleMapFragment.Address); + MockRangeSection rangeSection = emBuilder.AddRangeSection(jittedCode, jitManagerAddress, codeHeapListNode.Address); + _ = emBuilder.AddRangeSectionFragment(jittedCode, rangeSection.Address); + }); + + StubKind kind = em.GetStubKind(new TargetCodePointer(stubCodeAddress)); + Assert.Equal(StubKind.JumpStub, kind); + } + + [Theory] + [MemberData(nameof(StdArchAllVersions))] + public void GetStubKind_ManagedCode(string version, MockTarget.Architecture arch) + { + const ulong codeRangeStart = 0x0a0a_0000u; + const uint codeRangeSize = 0xc000u; + const uint methodSize = 0x450; + const ulong jitManagerAddress = 0x000b_ff00; + const ulong expectedMethodDescAddress = 0x0101_aaa0; + ulong methodStart = 0; + + IExecutionManager em = CreateExecutionManagerContract( + version, + arch, + emBuilder => + { + var jittedCode = emBuilder.AllocateJittedCodeRange(codeRangeStart, codeRangeSize); + methodStart = emBuilder.AddJittedMethod(jittedCode, methodSize, expectedMethodDescAddress).CodeAddress; + + NibbleMapTestBuilderBase nibBuilder = emBuilder.CreateNibbleMap(codeRangeStart, codeRangeSize); + nibBuilder.AllocateCodeChunk(new TargetCodePointer(methodStart), methodSize); + + MockCodeHeapListNode codeHeapListNode = emBuilder.AddCodeHeapListNode(0, codeRangeStart, codeRangeStart + codeRangeSize, codeRangeStart, nibBuilder.NibbleMapFragment.Address); + MockRangeSection rangeSection = emBuilder.AddRangeSection(jittedCode, jitManagerAddress, codeHeapListNode.Address); + _ = emBuilder.AddRangeSectionFragment(jittedCode, rangeSection.Address); + }); + + StubKind kind = em.GetStubKind(new TargetCodePointer(methodStart)); + Assert.Equal(StubKind.Unknown, kind); + } + + [Theory] + [MemberData(nameof(StdArchAllVersions))] + public void GetStubKind_R2R_MethodCallThunk(string version, MockTarget.Architecture arch) + { + const ulong codeRangeStart = 0x0a0a_0000u; + const uint codeRangeSize = 0xc000u; + const ulong jitManagerAddress = 0x000b_ff00; + const uint thunkRva = 0x2000; + const uint thunkSize = 0x100; + + IExecutionManager em = CreateExecutionManagerContract( + version, + arch, + emBuilder => + { + var jittedCode = emBuilder.AllocateJittedCodeRange(codeRangeStart, codeRangeSize); + MockReadyToRunInfo r2rInfo = emBuilder.AddReadyToRunInfo([], []); + emBuilder.SetDelayLoadMethodCallThunks(r2rInfo, thunkRva, thunkSize); + MockHashMapBuilder hashMapBuilder = new(emBuilder.Builder); + hashMapBuilder.PopulatePtrMap(r2rInfo.EntryPointToMethodDescMapAddress, []); + + MockLoaderModule r2rModule = emBuilder.AddReadyToRunModule(r2rInfo.Address); + MockRangeSection rangeSection = emBuilder.AddReadyToRunRangeSection(jittedCode, jitManagerAddress, r2rModule.Address); + _ = emBuilder.AddRangeSectionFragment(jittedCode, rangeSection.Address); + }); + + StubKind kind = em.GetStubKind(new TargetCodePointer(codeRangeStart + thunkRva + 0x10)); + Assert.Equal(StubKind.MethodCallThunk, kind); + } + + [Theory] + [MemberData(nameof(StdArchAllVersions))] + public void GetStubKind_R2R_OutsideThunkRange(string version, MockTarget.Architecture arch) + { + const ulong codeRangeStart = 0x0a0a_0000u; + const uint codeRangeSize = 0xc000u; + const ulong jitManagerAddress = 0x000b_ff00; + const uint thunkRva = 0x2000; + const uint thunkSize = 0x100; + + IExecutionManager em = CreateExecutionManagerContract( + version, + arch, + emBuilder => + { + var jittedCode = emBuilder.AllocateJittedCodeRange(codeRangeStart, codeRangeSize); + MockReadyToRunInfo r2rInfo = emBuilder.AddReadyToRunInfo([], []); + emBuilder.SetDelayLoadMethodCallThunks(r2rInfo, thunkRva, thunkSize); + MockHashMapBuilder hashMapBuilder = new(emBuilder.Builder); + hashMapBuilder.PopulatePtrMap(r2rInfo.EntryPointToMethodDescMapAddress, []); + + MockLoaderModule r2rModule = emBuilder.AddReadyToRunModule(r2rInfo.Address); + MockRangeSection rangeSection = emBuilder.AddReadyToRunRangeSection(jittedCode, jitManagerAddress, r2rModule.Address); + _ = emBuilder.AddRangeSectionFragment(jittedCode, rangeSection.Address); + }); + + StubKind kind = em.GetStubKind(new TargetCodePointer(codeRangeStart + thunkRva + thunkSize + 0x10)); + Assert.Equal(StubKind.Unknown, kind); + } + public static IEnumerable StdArchAllVersions() { const int highestVersion = 2; diff --git a/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.ExecutionManager.cs b/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.ExecutionManager.cs index c72126d962f939..cfe1d4fcb77753 100644 --- a/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.ExecutionManager.cs +++ b/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.ExecutionManager.cs @@ -99,6 +99,7 @@ internal sealed class MockRangeSection : TypedView private const string FlagsFieldName = "Flags"; private const string HeapListFieldName = "HeapList"; private const string R2RModuleFieldName = "R2RModule"; + private const string RangeListFieldName = "RangeList"; public static Layout CreateLayout(MockTarget.Architecture architecture) => new SequentialLayoutBuilder("RangeSection", architecture) @@ -109,6 +110,7 @@ public static Layout CreateLayout(MockTarget.Architecture arch .AddUInt32Field(FlagsFieldName) .AddPointerField(HeapListFieldName) .AddPointerField(R2RModuleFieldName) + .AddPointerField(RangeListFieldName) .Build(); public ulong RangeBegin @@ -146,6 +148,28 @@ public ulong R2RModule get => ReadPointerField(R2RModuleFieldName); set => WritePointerField(R2RModuleFieldName, value); } + + public ulong RangeList + { + get => ReadPointerField(RangeListFieldName); + set => WritePointerField(RangeListFieldName, value); + } +} + +internal sealed class MockCodeRangeMapRangeList : TypedView +{ + private const string RangeListTypeFieldName = "RangeListType"; + + public static Layout CreateLayout(MockTarget.Architecture architecture) + => new SequentialLayoutBuilder("CodeRangeMapRangeList", architecture) + .AddUInt32Field(RangeListTypeFieldName) + .Build(); + + public int RangeListType + { + get => (int)ReadUInt32Field(RangeListTypeFieldName); + set => WriteUInt32Field(RangeListTypeFieldName, (uint)value); + } } internal sealed class MockCodeHeapListNode : TypedView @@ -383,9 +407,39 @@ public ulong ExceptionInfoSection set => WritePointerField(ExceptionInfoSectionFieldName, value); } + public ulong DelayLoadMethodCallThunks + { + get => ReadPointerField(DelayLoadMethodCallThunksFieldName); + set => WritePointerField(DelayLoadMethodCallThunksFieldName, value); + } + public ulong EntryPointToMethodDescMapAddress => GetFieldAddress(EntryPointToMethodDescMapFieldName); } +internal sealed class MockImageDataDirectory : TypedView +{ + private const string VirtualAddressFieldName = "VirtualAddress"; + private const string SizeFieldName = "Size"; + + public static Layout CreateLayout(MockTarget.Architecture architecture) + => new SequentialLayoutBuilder("ImageDataDirectory", architecture) + .AddUInt32Field(VirtualAddressFieldName) + .AddUInt32Field(SizeFieldName) + .Build(); + + public uint VirtualAddress + { + get => ReadUInt32Field(VirtualAddressFieldName); + set => WriteUInt32Field(VirtualAddressFieldName, value); + } + + public uint Size + { + get => ReadUInt32Field(SizeFieldName); + set => WriteUInt32Field(SizeFieldName, value); + } +} + internal sealed class MockEEJitManager : TypedView { private const string StoreRichDebugInfoFieldName = "StoreRichDebugInfo"; @@ -442,6 +496,7 @@ public Memory CodeBytes internal sealed class MockExecutionManagerBuilder { private const uint CodeHeapRangeSectionFlag = 0x02; + private const uint RangeListRangeSectionFlag = 0x04; private const string EEJitManagerGlobalName = "EEJitManagerGlobalPointer"; private const int RangeSectionMapBitsPerLevel = 8; @@ -501,6 +556,8 @@ internal readonly struct JittedCodeRange internal Layout ReadyToRunInfoLayout { get; } internal Layout EEJitManagerLayout { get; } internal Layout ModuleLayout { get; } + internal Layout CodeRangeMapRangeListLayout { get; } + internal Layout ImageDataDirectoryLayout { get; } internal Layout RuntimeFunctionLayout => _runtimeFunctions.RuntimeFunctionLayout; internal Layout UnwindInfoLayout => _runtimeFunctions.UnwindInfoLayout; internal (string Name, ulong Value)[] Globals { get; } @@ -555,6 +612,8 @@ internal MockExecutionManagerBuilder(string version, MockMemorySpace.Builder bui ReadyToRunInfoLayout = MockReadyToRunInfo.CreateLayout(architecture, hashMapStride); EEJitManagerLayout = MockEEJitManager.CreateLayout(architecture); ModuleLayout = MockLoaderModule.CreateLayout(architecture); + CodeRangeMapRangeListLayout = MockCodeRangeMapRangeList.CreateLayout(architecture); + ImageDataDirectoryLayout = MockImageDataDirectory.CreateLayout(architecture); _eeJitManager = AllocateAndCreate(EEJitManagerLayout, "EEJitManager"); _eeJitManager.AllCodeHeaps = allCodeHeaps; @@ -614,6 +673,27 @@ public MockRangeSection AddReadyToRunRangeSection(JittedCodeRange jittedCodeRang return rangeSection; } + public MockRangeSection AddRangeListRangeSection(JittedCodeRange jittedCodeRange, ulong jitManagerAddress, int rangeListType) + { + MockCodeRangeMapRangeList rangeList = AllocateAndCreate(CodeRangeMapRangeListLayout, "CodeRangeMapRangeList"); + rangeList.RangeListType = rangeListType; + + MockRangeSection rangeSection = AllocateAndCreate(RangeSectionLayout, "RangeSection (RangeList)", _rangeSectionMapAllocator); + rangeSection.RangeBegin = jittedCodeRange.RangeStart; + rangeSection.RangeEndOpen = jittedCodeRange.RangeEnd; + rangeSection.Flags = RangeListRangeSectionFlag; + rangeSection.RangeList = rangeList.Address; + rangeSection.JitManager = jitManagerAddress; + return rangeSection; + } + + public MockJittedMethod AddStubCodeBlock(JittedCodeRange jittedCodeRange, uint codeSize, int stubCodeBlockKind) + { + MockJittedMethod stub = AllocateJittedMethod(jittedCodeRange, codeSize, "Stub Code Block"); + stub.CodeHeader = (ulong)stubCodeBlockKind; + return stub; + } + public MockRangeSectionFragment AddRangeSectionFragment(JittedCodeRange jittedCodeRange, ulong rangeSectionAddress) => AddRangeSectionFragment(jittedCodeRange, rangeSectionAddress, insertIntoMap: true); @@ -710,6 +790,14 @@ public MockReadyToRunInfo AddReadyToRunInfo(uint[] runtimeFunctions, uint[] hotC return readyToRunInfo; } + public void SetDelayLoadMethodCallThunks(MockReadyToRunInfo readyToRunInfo, uint thunkRva, uint thunkSize) + { + MockImageDataDirectory imageDataDir = AllocateAndCreate(ImageDataDirectoryLayout, "DelayLoadMethodCallThunks"); + imageDataDir.VirtualAddress = thunkRva; + imageDataDir.Size = thunkSize; + readyToRunInfo.DelayLoadMethodCallThunks = imageDataDir.Address; + } + public MockLoaderModule AddReadyToRunModule(ulong readyToRunInfoAddress) { MockLoaderModule module = AllocateAndCreate(ModuleLayout, "R2R Module");