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
34 changes: 23 additions & 11 deletions docs/design/datacontracts/ExecutionManager.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ struct CodeBlockHandle
TargetCodePointer GetFuncletStartAddress(CodeBlockHandle codeInfoHandle);
// Get the method region info (hot and cold code size, and cold code start address)
void GetMethodRegionInfo(CodeBlockHandle codeInfoHandle, out uint hotSize, out TargetPointer coldStart, out uint coldSize);
// Get the JIT type
JitType GetJITType(CodeBlockHandle codeInfoHandle);
// Attempt to get the method desc of an entrypoint
TargetPointer NonVirtualEntry2MethodDesc(TargetCodePointer entrypoint);

Expand Down Expand Up @@ -58,6 +56,9 @@ struct CodeBlockHandle
// Returns true if the code block is specifically a filter funclet
bool IsFilterFunclet(CodeBlockHandle codeInfoHandle);

// Classify a code address as a known code kind (jitted, ReadyToRun, stub, etc.).
// Returns Unknown if the address is not recognized.
CodeKind GetCodeKind(TargetCodePointer codeAddress);
// Finds the ReadyToRun module that contains the given address.
TargetPointer FindReadyToRunModule(TargetPointer address);
```
Expand Down Expand Up @@ -122,6 +123,23 @@ public struct ExceptionClauseInfo
public TargetNUInt? TypeHandle;
public TargetPointer? ModuleAddr;
}

public enum CodeKind : uint
{
Unknown = 0,
JumpStub = 1,
DynamicHelper = 2,
StubPrecode = 3,
FixupPrecode = 4,
VSD_DispatchStub = 5,
VSD_ResolveStub = 6,
VSD_LookupStub = 7,
VSD_VTableStub = 8,
CallCountingStub = 9,
MethodCallThunk = 10,
Jitted = 11,
ReadyToRun = 12
}
Comment thread
rcj1 marked this conversation as resolved.
Comment thread
rcj1 marked this conversation as resolved.
```

## Version 1
Expand All @@ -146,6 +164,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 |
Expand Down Expand Up @@ -387,15 +407,6 @@ public override void GetMethodRegionInfo(RangeSection rangeSection, TargetCodePo

```

`GetJITType` returns the JIT type by finding the JIT manager for the data range containing the relevant code block. We return `Jit` for the `EEJitManager`, `R2R` for the `R2RJitManager`, and `Unknown` for any other value.
```csharp
public enum JitType : uint
{
Unknown = 0,
Jit = 1,
R2R = 2
};
```
`NonVirtualEntry2MethodDesc` attempts to find a method desc from an entrypoint. If portable entrypoints are enabled, we attempt to read the entrypoint data structure to find the method table. We also attempt to find the method desc from a precode stub. Finally, we attempt to find the method desc using `GetMethodInfo` as described above.
```csharp
TargetPointer IExecutionManager.NonVirtualEntry2MethodDesc(TargetCodePointer entrypoint)
Expand Down Expand Up @@ -504,6 +515,7 @@ 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.

`GetCodeKind` classifies a code address by finding its owning range section and determining the code kind. It distinguishes between jitted code, stub code blocks (jump stubs, precode stubs, VSD stubs, etc.), and ReadyToRun code. Returns `Unknown` if the address cannot be classified.
### FindReadyToRunModule

`FindReadyToRunModule` locates the ReadyToRun module whose PE image contains the given address. Unlike `GetCodeBlockHandle` (which only matches code regions), this API matches against the full PE image range - including data sections such as import tables. This is used in GCRefMap resolution as it requires finding the module that owns an import section indirection address, which is in the data section rather than the code section.
Expand Down
33 changes: 33 additions & 0 deletions docs/design/datacontracts/PrecodeStubs.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ 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);

// Given an interior address within a precode stub and the kind of stub (StubPrecode or FixupPrecode),
// computes the entry point of the precode.
TargetPointer GetPrecodeEntryPointFromInteriorAddress(TargetCodePointer interiorAddress, bool isFixupPrecode);
```

## Version 1, 2, and 3
Expand Down Expand Up @@ -296,3 +300,32 @@ After the initial precode type is determined, for stub precodes a refined precod
return precode.GetMethodDesc(_target, MachineDescriptor);
}
```

### `GetPrecodeEntryPointFromInteriorAddress`

Given an interior address within a precode stub and the kind of stub (StubPrecode or FixupPrecode),
computes the entry point of the precode.

```csharp
TargetPointer IPrecodeStubs.GetPrecodeEntryPointFromInteriorAddress(TargetCodePointer interiorAddress, bool isFixupPrecode)
{
TargetPointer instrPointer = CodePointerReadableInstrPointer(interiorAddress);

uint stubSize;
if (isFixupPrecode)
{
stubSize = MachineDescriptor.FixupStubPrecodeSize;
}
else
{
stubSize = MachineDescriptor.StubPrecodeSize;
}

Comment thread
rcj1 marked this conversation as resolved.
ulong pageMask = MachineDescriptor.StubCodePageSize - 1;
ulong pageBase = instrPointer.Value & ~pageMask;
ulong offset = instrPointer.Value - pageBase;
ulong entryPointAddress = pageBase + (offset / stubSize) * stubSize;

return new TargetPointer(entryPointAddress);
}
```
195 changes: 53 additions & 142 deletions src/coreclr/debug/daccess/daccess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ULONG32>(lenNeeded))
return COR_E_OVERFLOW;

*symbolLen = (ULONG32)lenNeeded;
}

return hr;
}

HRESULT
ClrDataAccess::RawGetMethodName(
/* [in] */ CLRDATA_ADDRESS address,
Expand Down Expand Up @@ -5424,51 +5344,42 @@ ClrDataAccess::RawGetMethodName(
PTR_StubManager pStubManager;
MethodDesc* methodDesc = NULL;

{
EECodeInfo codeInfo(GetInterpreterCodeFromEntryPointIfPresent(TO_TADDR(address)));
Comment thread
jkotas marked this conversation as resolved.
if (codeInfo.IsValid())
{
if (displacement)
{
*displacement = codeInfo.GetRelOffset();
}

methodDesc = codeInfo.GetMethodDesc();
goto NameFromMethodDesc;
}
}

pStubManager = StubManager::FindStubManager(TO_TADDR(address));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we want to update fragile DAC to match cDAC implementation and behavior? Or are we intentionally going to leave them out of sync?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The only remaining case of dynamically allocated remaining that are covered by StubManager::FindStubManager here, but not covered by the RangeSection.FInd are instantiating stubs and shuffle thunks (EmitShuffleThunk, EmitComputedInstantiatingMethodStub).

If we want to handle this case in cDAC, we should switch the tracking of these stubs to the RangeSection plan so that are covered by RangeSection.FInd as well. (It can be done in a follow up PR.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Do we want to update fragile DAC

I think so. If we update the DAC to our liking, the SOS tests will hit it repeatedly through !u and other commands and catch any regressions in the cDAC implementation.

if (pStubManager != NULL)
{
if (displacement)
{
*displacement = 0;
}

//
// Special-cased stub managers
//
if (pStubManager == PrecodeStubManager::g_pManager)
{
PCODE alignedAddress = AlignDown(TO_TADDR(address), PRECODE_ALIGNMENT);
StubCodeBlockKind stubKind = RangeSectionStubManager::GetStubKind(TO_TADDR(address));
SIZE_T stubSize = 0;

#ifdef TARGET_ARM
alignedAddress += THUMB_CODE;
switch (stubKind)
{
case STUB_CODE_BLOCK_STUBPRECODE:
stubSize = StubPrecode::CodeSize;
break;
#ifdef HAS_FIXUP_PRECODE
case STUB_CODE_BLOCK_FIXUPPRECODE:
stubSize = FixupPrecode::CodeSize;
break;
#endif
default:
break;
}

SIZE_T maxPrecodeSize = sizeof(StubPrecode);
if (stubSize != 0)
{
const SIZE_T pageMask = GetStubCodePageSize() - 1;
const TADDR pageBase = TO_TADDR(address) & ~static_cast<TADDR>(pageMask);
const SIZE_T offset = static_cast<SIZE_T>(TO_TADDR(address) - pageBase);
PCODE precodeAddress = pageBase + (offset / stubSize) * stubSize;

#ifdef HAS_THISPTR_RETBUF_PRECODE
maxPrecodeSize = max((size_t)maxPrecodeSize, sizeof(ThisPtrRetBufPrecode));
#ifdef TARGET_ARM
precodeAddress += THUMB_CODE;
#endif

for (SIZE_T i = 0; i < maxPrecodeSize / PRECODE_ALIGNMENT; i++)
{
EX_TRY
{
// Try to find matching precode entrypoint
Precode* pPrecode = Precode::GetPrecodeFromEntryPoint(alignedAddress, TRUE);
Precode* pPrecode = Precode::GetPrecodeFromEntryPoint(precodeAddress, TRUE);
if (pPrecode != NULL && pPrecode->GetType() != PRECODE_UMENTRY_THUNK)
{
methodDesc = pPrecode->GetMethodDesc();
Expand All @@ -5478,64 +5389,64 @@ ClrDataAccess::RawGetMethodName(
{
if (displacement)
{
*displacement = TO_TADDR(address) - PCODEToPINSTR(alignedAddress);
*displacement = TO_TADDR(address) - PCODEToPINSTR(precodeAddress);
}
goto NameFromMethodDesc;
return GetFullMethodName(methodDesc, bufLen, symbolLen, symbolBuf);
}
}
}
alignedAddress -= PRECODE_ALIGNMENT;
}
EX_CATCH
{
}
EX_END_CATCH
}
}

LPCWSTR wszStubManagerName = pStubManager->GetStubManagerName(TO_TADDR(address));
_ASSERTE(wszStubManagerName != NULL);

return FormatCLRStubName(
wszStubManagerName,
TO_TADDR(address),
bufLen,
symbolLen,
symbolBuf);
if (u16_strcmp(wszStubManagerName, W("ThePreStub")) != 0 && u16_strcmp(wszStubManagerName, W("InteropDispatchStub")) != 0 && u16_strcmp(wszStubManagerName, W("TailCallStub")) != 0)
{
// Skip the stubs that are just assembly helpers.
Comment thread
rcj1 marked this conversation as resolved.
size_t nameLen = u16_strlen(wszStubManagerName) + 1; // include null terminator
if (symbolLen)
{
if (!FitsIn<ULONG32>(nameLen))
return COR_E_OVERFLOW;
*symbolLen = (ULONG32)nameLen;
}
if (symbolBuf && bufLen >= nameLen)
{
wcscpy_s(symbolBuf, bufLen, wszStubManagerName);
}
else if (symbolBuf)
{
return S_FALSE;
}
if (displacement)
{
*displacement = 0;
}
return S_OK;
}
Comment thread
rcj1 marked this conversation as resolved.
Comment thread
rcj1 marked this conversation as resolved.
Comment thread
rcj1 marked this conversation as resolved.
}

// Do not waste time looking up name for static helper. Debugger can get the actual name from .pdb.
PCSTR pHelperName;
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
Expand Down
6 changes: 3 additions & 3 deletions src/coreclr/vm/codeman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4728,9 +4728,9 @@ StubCodeBlockKind EEJitManager::GetStubCodeBlockKind(RangeSection * pRangeSectio

TADDR start = dac_cast<PTR_EEJitManager>(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;
}


Expand All @@ -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<PTR_EECodeGenManager>(pRS->_pjit)->FindMethodCode(pRS, currentPC);
}

Expand Down
Loading
Loading