-
Notifications
You must be signed in to change notification settings - Fork 5.4k
[cDAC] implement GetRuntimeNameByAddress #127134
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -52,6 +52,10 @@ struct CodeBlockHandle | |
| // Get the exception clause info for the code block | ||
| List<ExceptionClauseInfo> GetExceptionClauses(CodeBlockHandle codeInfoHandle); | ||
|
|
||
| // Classify a code address as a known stub kind (precode, jump stub, VSD stub, etc.) | ||
| // or as managed code. Returns CodeBlockUnknown if the address is not recognized. | ||
| 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); | ||
|
|
@@ -143,6 +147,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 | | ||
|
|
@@ -219,6 +225,11 @@ Global variables used: | |
| | `GCInfoVersion` | uint32 | JITted code GCInfo version | | ||
| | `FeatureOnStackReplacement` | uint8 | 1 if FEATURE_ON_STACK_REPLACEMENT is enabled, 0 otherwise | | ||
| | `FeaturePortableEntrypoints` | uint8 | 1 if FEATURE_PORTABLE_ENTRYPOINTS is enabled, 0 otherwise | | ||
| | `ThePreStub` | TargetPointer | Address of the pre-stub entry point (only present when `FeaturePortableEntrypoints` is disabled) | | ||
| | `GenericPInvokeCalliHelper` | TargetPointer | Address of the generic PInvoke CALLI helper stub | | ||
| | `VarargPInvokeStub` | TargetPointer | Address of the vararg PInvoke stub | | ||
| | `VarargPInvokeStub_RetBuffArg` | TargetPointer | Address of the vararg PInvoke stub with return buffer argument (not present on x86, ARM64, LoongArch64, RISC-V) | | ||
| | `TailCallJitHelper` | TargetPointer | Address of the JIT tail call helper (only present on x86 Windows) | | ||
| | `ObjectMethodTable` | TargetPointer | Pointer to the `System.Object` MethodTable, used for catch-all handler detection | | ||
|
|
||
| Contract constants used: | ||
|
|
@@ -501,6 +512,36 @@ 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 first checks the address against well-known global stub pointers (`ThePreStub`, `VarargPInvokeStub`, `VarargPInvokeStub_RetBuffArg`, `GenericPInvokeCalliHelper`, `TailCallJitHelper`). If the address matches one of these, it returns the corresponding `StubKind` immediately. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we doing this classification just to match the name returned by the legacy DAC? I think we should depend on coreclr.dll symbols for the names of these assembly helpers (similar to what we have done for JIT helpers).
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, we are matching the DAC. The DAC uses
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assume that this is only about symbols displayed by disassembly windbg. We do not need to match 1:1 what windbg displays in disassembly. We just need to make sure that it displays it is reasonable. If we do nothing (ie stop recognizing these addresses in
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. BTW: windbg disassembly experience that involves this API is very poor today. When I run I assume that I would like to see something like |
||
|
|
||
| If no global match is found, 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); | ||
|
|
||
| // Check well-known global stubs | ||
| if (address == ThePreStub) return StubKind.PreStub; | ||
| if (address == VarargPInvokeStub || address == VarargPInvokeStub_RetBuffArg | ||
| || address == GenericPInvokeCalliHelper) | ||
| return StubKind.InteropDispatchStub; | ||
| if (address == TailCallJitHelper) return StubKind.TailCallStub; | ||
|
|
||
| // Look up in range section map | ||
| RangeSection range = FindRangeSection(jittedCodeAddress); | ||
| if (range == null) return StubKind.CodeBlockUnknown; | ||
|
|
||
| JitManager jitManager = GetJitManager(range); | ||
| return jitManager.GetStubCodeBlockKind(range, jittedCodeAddress); | ||
| } | ||
| ``` | ||
|
|
||
| ### EE JIT Manager and Code Heap Info | ||
|
|
||
| ```csharp | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,11 @@ This contract provides support for examining [precode](../coreclr/botr/method-de | |
| ```csharp | ||
| // Gets a pointer to the MethodDesc for a given stub entrypoint | ||
| TargetPointer GetMethodDescFromStubAddress(TargetCodePointer entryPoint); | ||
|
|
||
| // Enumerates candidate precode entry points near a given code address. | ||
| // This is used to resolve a code address that falls within a | ||
| // precode stub back to its entry point. | ||
| IEnumerable<TargetCodePointer> GetCandidateEntryPoints(TargetCodePointer address); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should be able to find the precode start address using some address math rather than having to do guess-and-check repeatedly. I think the algorithm would look something like this: |
||
| ``` | ||
|
|
||
| ## Version 1, 2, and 3 | ||
|
|
@@ -295,4 +300,17 @@ After the initial precode type is determined, for stub precodes a refined precod | |
|
|
||
| return precode.GetMethodDesc(_target, MachineDescriptor); | ||
| } | ||
|
|
||
| IEnumerable<TargetCodePointer> IPrecodeStubs.GetCandidateEntryPoints(TargetCodePointer address) | ||
| { | ||
| TargetPointer instrPointer = CodePointerReadableInstrPointer(address); | ||
| ulong aligned = instrPointer.Value & ~(ulong)(PointerSize - 1); | ||
| uint count = StubPrecodeSize / PointerSize; | ||
|
|
||
| for (uint i = 0; i < count; i++) | ||
| { | ||
| TargetPointer candidateAddr = new TargetPointer(aligned - (i * PointerSize)); | ||
| yield return CodePointerFromAddress(candidateAddr); | ||
| } | ||
| } | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -5327,7 +5327,7 @@ static int FormatCLRStubName( | |||||||||||||
|
|
||||||||||||||
| // Compute the address as a string safely. | ||||||||||||||
| WCHAR addrString[Max64BitHexString + 1]; | ||||||||||||||
| FormatInteger(addrString, ARRAY_SIZE(addrString), "%p", stubAddr); | ||||||||||||||
| FormatInteger(addrString, ARRAY_SIZE(addrString), sizeof(void*) == 8 ? "%016llX" : "%08X", stubAddr); | ||||||||||||||
|
||||||||||||||
| FormatInteger(addrString, ARRAY_SIZE(addrString), sizeof(void*) == 8 ? "%016llX" : "%08X", stubAddr); | |
| #ifdef HOST_64BIT | |
| FormatInteger(addrString, ARRAY_SIZE(addrString), "%016llX", static_cast<unsigned long long>(stubAddr)); | |
| #else | |
| FormatInteger(addrString, ARRAY_SIZE(addrString), "%08X", static_cast<unsigned int>(stubAddr)); | |
| #endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm worried that this classification would make the interface easily broken if we added/removed/merged/split/redefined stubs in the future. How about instead of returning an explicit enumeration value we return a string with a looser guarantee? Something like this:
Hopefully this together with GetRelativeOffset() could implement the GetRuntimeNameByAddress API. I am using a CodeBlockHandle here thinking we might benefit from caching some CodeBlock data about stubs too (RangeSection, StartAddress, CodeBlockStubKind) even though it currently appears we don't. Even if we continue to not cache anything it still makes the API a little more consistent.