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
3 changes: 3 additions & 0 deletions docs/design/datacontracts/GCInfo.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ IGCInfoHandle DecodeInterpreterGCInfo(TargetPointer gcInfoAddress, uint gcVersio

// Fetches length of code as reported in GCInfo
uint GetCodeLength(IGCInfoHandle handle);

// Returns the list of interruptible code offset ranges from the GCInfo
IReadOnlyList<InterruptibleRange> GetInterruptibleRanges(IGCInfoHandle handle);
```

## Version 1
Expand Down
8 changes: 8 additions & 0 deletions docs/design/datacontracts/RuntimeTypeSystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,14 @@ partial interface IRuntimeTypeSystem : IContract
// Return true if a MethodDesc represents an IL stub with a special MethodDesc context arg
public virtual bool HasMDContextArg(MethodDescHandle);

// Return true if the method requires a hidden instantiation argument (generic context parameter).
// Corresponds to native MethodDesc::RequiresInstArg().
public virtual bool RequiresInstArg(MethodDescHandle methodDesc);

// Return true if the method uses the async calling convention.
// Corresponds to native MethodDesc::IsAsyncMethod().
public virtual bool IsAsyncMethod(MethodDescHandle methodDesc);

// Return true if a MethodDesc is in a collectible module
public virtual bool IsCollectibleMethod(MethodDescHandle methodDesc);

Expand Down
14 changes: 14 additions & 0 deletions docs/design/datacontracts/StackWalk.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,21 @@ This contract depends on the following descriptors:
| `StubDispatchFrame` | `MethodDescPtr` | Pointer to Frame's method desc |
| `StubDispatchFrame` | `RepresentativeMTPtr` | Pointer to Frame's method table pointer |
| `StubDispatchFrame` | `RepresentativeSlot` | Frame's method table slot |
| `StubDispatchFrame` | `GCRefMap` | Cached pointer to GC reference map blob for caller stack promotion |
| `StubDispatchFrame` | `ZapModule` | Module pointer for lazy GCRefMap resolution via import sections |
| `StubDispatchFrame` | `Indirection` | Import slot pointer for lazy GCRefMap resolution |
| `ExternalMethodFrame` | `GCRefMap` | Cached pointer to GC reference map blob for caller stack promotion |
| `ExternalMethodFrame` | `Indirection` | Import slot pointer for lazy GCRefMap resolution |
| `ExternalMethodFrame` | `ZapModule` | Module pointer for lazy GCRefMap resolution via import sections |
| `DynamicHelperFrame` | `DynamicHelperFrameFlags` | Flags indicating which argument registers contain GC references |
| `TransitionBlock` | `ReturnAddress` | Return address associated with the TransitionBlock |
| `TransitionBlock` | `CalleeSavedRegisters` | Platform specific CalleeSavedRegisters struct associated with the TransitionBlock |
| `TransitionBlock` (arm) | `ArgumentRegisters` | ARM specific `ArgumentRegisters` struct |
| `TransitionBlock` | `OffsetOfArgs` | Byte offset of stack arguments (first arg after registers) = `sizeof(TransitionBlock)` |
| `TransitionBlock` | `ArgumentRegistersOffset` | Byte offset of the ArgumentRegisters within the TransitionBlock |
| `TransitionBlock` | `FirstGCRefMapSlot` | Byte offset where GCRefMap slot enumeration begins. ARM64: RetBuffArgReg offset; others: ArgumentRegisters offset |
| `ReadyToRunInfo` | `ImportSections` | Pointer to array of `READYTORUN_IMPORT_SECTION` structs for GCRefMap resolution |
| `ReadyToRunInfo` | `NumImportSections` | Count of import sections in the array |
| `FuncEvalFrame` | `DebuggerEvalPtr` | Pointer to the Frame's DebuggerEval object |
| `DebuggerEval` | `TargetContext` | Context saved inside DebuggerEval |
| `DebuggerEval` | `EvalDuringException` | Flag used in processing FuncEvalFrame |
Expand All @@ -85,6 +97,8 @@ This contract depends on the following descriptors:
| `ExceptionInfo` | `CallerOfActualHandlerFrame` | Stack frame of the caller of the catch handler |
| `ExceptionInfo` | `PreviousNestedInfo` | Pointer to previous nested ExInfo |
| `ExceptionInfo` | `PassNumber` | Exception handling pass (1 or 2) |
| `ExceptionInfo` | `ClauseForCatchHandlerStartPC` | Start PC offset of the catch handler clause, used for interruptible offset override |
| `ExceptionInfo` | `ClauseForCatchHandlerEndPC` | End PC offset of the catch handler clause, used for interruptible offset override |

Global variables used:
| Global Name | Type | Purpose |
Expand Down
33 changes: 33 additions & 0 deletions src/coreclr/vm/datadescriptor/datadescriptor.inc
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ CDAC_TYPE_FIELD(ExceptionInfo, T_UINT8, PassNumber, offsetof(ExInfo, m_passNumbe
CDAC_TYPE_FIELD(ExceptionInfo, T_POINTER, CSFEHClause, offsetof(ExInfo, m_csfEHClause))
CDAC_TYPE_FIELD(ExceptionInfo, T_POINTER, CSFEnclosingClause, offsetof(ExInfo, m_csfEnclosingClause))
CDAC_TYPE_FIELD(ExceptionInfo, T_POINTER, CallerOfActualHandlerFrame, offsetof(ExInfo, m_sfCallerOfActualHandlerFrame))
CDAC_TYPE_FIELD(ExceptionInfo, T_UINT32, ClauseForCatchHandlerStartPC, offsetof(ExInfo, m_ClauseForCatch) + offsetof(EE_ILEXCEPTION_CLAUSE, HandlerStartPC))
CDAC_TYPE_FIELD(ExceptionInfo, T_UINT32, ClauseForCatchHandlerEndPC, offsetof(ExInfo, m_ClauseForCatch) + offsetof(EE_ILEXCEPTION_CLAUSE, HandlerEndPC))
CDAC_TYPE_END(ExceptionInfo)

CDAC_TYPE_BEGIN(ObjectHandle)
Expand Down Expand Up @@ -724,6 +726,8 @@ CDAC_TYPE_FIELD(ReadyToRunInfo, T_POINTER, HotColdMap, cdac_data<ReadyToRunInfo>
CDAC_TYPE_FIELD(ReadyToRunInfo, T_POINTER, DelayLoadMethodCallThunks, cdac_data<ReadyToRunInfo>::DelayLoadMethodCallThunks)
CDAC_TYPE_FIELD(ReadyToRunInfo, T_POINTER, DebugInfoSection, cdac_data<ReadyToRunInfo>::DebugInfoSection)
CDAC_TYPE_FIELD(ReadyToRunInfo, T_POINTER, ExceptionInfoSection, cdac_data<ReadyToRunInfo>::ExceptionInfoSection)
CDAC_TYPE_FIELD(ReadyToRunInfo, T_POINTER, ImportSections, cdac_data<ReadyToRunInfo>::ImportSections)
CDAC_TYPE_FIELD(ReadyToRunInfo, T_UINT32, NumImportSections, cdac_data<ReadyToRunInfo>::NumImportSections)
CDAC_TYPE_FIELD(ReadyToRunInfo, TYPE(HashMap), EntryPointToMethodDescMap, cdac_data<ReadyToRunInfo>::EntryPointToMethodDescMap)
CDAC_TYPE_FIELD(ReadyToRunInfo, T_POINTER, LoadedImageBase, cdac_data<ReadyToRunInfo>::LoadedImageBase)
CDAC_TYPE_FIELD(ReadyToRunInfo, T_POINTER, Composite, cdac_data<ReadyToRunInfo>::Composite)
Expand Down Expand Up @@ -969,6 +973,19 @@ CDAC_TYPE_FIELD(TransitionBlock, TYPE(CalleeSavedRegisters), CalleeSavedRegister
#ifdef TARGET_ARM
CDAC_TYPE_FIELD(TransitionBlock, TYPE(ArgumentRegisters), ArgumentRegisters, offsetof(TransitionBlock, m_argumentRegisters))
#endif // TARGET_ARM
// Offset to where stack arguments begin (just past the end of the TransitionBlock)
CDAC_TYPE_FIELD(TransitionBlock, T_UINT32, OffsetOfArgs, sizeof(TransitionBlock))
// Offset to where argument registers are saved in the TransitionBlock
#if (defined(TARGET_AMD64) && !defined(UNIX_AMD64_ABI)) || defined(TARGET_WASM)
CDAC_TYPE_FIELD(TransitionBlock, T_UINT32, ArgumentRegistersOffset, sizeof(TransitionBlock))
CDAC_TYPE_FIELD(TransitionBlock, T_UINT32, FirstGCRefMapSlot, sizeof(TransitionBlock))
#elif defined(TARGET_ARM64)
CDAC_TYPE_FIELD(TransitionBlock, T_UINT32, ArgumentRegistersOffset, offsetof(TransitionBlock, m_argumentRegisters))
CDAC_TYPE_FIELD(TransitionBlock, T_UINT32, FirstGCRefMapSlot, offsetof(TransitionBlock, m_x8RetBuffReg))
#else
CDAC_TYPE_FIELD(TransitionBlock, T_UINT32, ArgumentRegistersOffset, offsetof(TransitionBlock, m_argumentRegisters))
CDAC_TYPE_FIELD(TransitionBlock, T_UINT32, FirstGCRefMapSlot, offsetof(TransitionBlock, m_argumentRegisters))
#endif
CDAC_TYPE_END(TransitionBlock)

#ifdef DEBUGGING_SUPPORTED
Expand All @@ -989,8 +1006,23 @@ CDAC_TYPE_SIZE(sizeof(StubDispatchFrame))
CDAC_TYPE_FIELD(StubDispatchFrame, T_POINTER, RepresentativeMTPtr, cdac_data<StubDispatchFrame>::RepresentativeMTPtr)
CDAC_TYPE_FIELD(StubDispatchFrame, T_POINTER, MethodDescPtr, cdac_data<FramedMethodFrame>::MethodDescPtr)
CDAC_TYPE_FIELD(StubDispatchFrame, T_UINT32, RepresentativeSlot, cdac_data<StubDispatchFrame>::RepresentativeSlot)
CDAC_TYPE_FIELD(StubDispatchFrame, T_POINTER, GCRefMap, cdac_data<StubDispatchFrame>::GCRefMap)
CDAC_TYPE_FIELD(StubDispatchFrame, T_POINTER, ZapModule, cdac_data<StubDispatchFrame>::ZapModule)
CDAC_TYPE_FIELD(StubDispatchFrame, T_POINTER, Indirection, cdac_data<StubDispatchFrame>::Indirection)
CDAC_TYPE_END(StubDispatchFrame)

CDAC_TYPE_BEGIN(ExternalMethodFrame)
CDAC_TYPE_SIZE(sizeof(ExternalMethodFrame))
CDAC_TYPE_FIELD(ExternalMethodFrame, T_POINTER, GCRefMap, cdac_data<ExternalMethodFrame>::GCRefMap)
CDAC_TYPE_FIELD(ExternalMethodFrame, T_POINTER, Indirection, cdac_data<ExternalMethodFrame>::Indirection)
CDAC_TYPE_FIELD(ExternalMethodFrame, T_POINTER, ZapModule, cdac_data<ExternalMethodFrame>::ZapModule)
CDAC_TYPE_END(ExternalMethodFrame)

CDAC_TYPE_BEGIN(DynamicHelperFrame)
CDAC_TYPE_SIZE(sizeof(DynamicHelperFrame))
CDAC_TYPE_FIELD(DynamicHelperFrame, T_INT32, DynamicHelperFrameFlags, cdac_data<DynamicHelperFrame>::DynamicHelperFrameFlags)
CDAC_TYPE_END(DynamicHelperFrame)

#ifdef FEATURE_HIJACK
CDAC_TYPE_BEGIN(ResumableFrame)
CDAC_TYPE_SIZE(sizeof(ResumableFrame))
Expand Down Expand Up @@ -1374,6 +1406,7 @@ CDAC_GLOBAL_POINTER(MetadataUpdatesApplied, &::g_metadataUpdatesApplied)
#undef FRAME_TYPE_NAME

CDAC_GLOBAL(MethodDescTokenRemainderBitCount, T_UINT8, METHOD_TOKEN_REMAINDER_BIT_COUNT)

#if FEATURE_COMINTEROP
CDAC_GLOBAL(FeatureCOMInterop, T_UINT8, 1)
#else
Expand Down
21 changes: 21 additions & 0 deletions src/coreclr/vm/frames.h
Original file line number Diff line number Diff line change
Expand Up @@ -1490,6 +1490,9 @@ struct cdac_data<StubDispatchFrame>
{
static constexpr size_t RepresentativeMTPtr = offsetof(StubDispatchFrame, m_pRepresentativeMT);
static constexpr uint32_t RepresentativeSlot = offsetof(StubDispatchFrame, m_representativeSlot);
static constexpr size_t GCRefMap = offsetof(StubDispatchFrame, m_pGCRefMap);
static constexpr size_t ZapModule = offsetof(StubDispatchFrame, m_pZapModule);
static constexpr size_t Indirection = offsetof(StubDispatchFrame, m_pIndirection);
};

typedef DPTR(class StubDispatchFrame) PTR_StubDispatchFrame;
Expand Down Expand Up @@ -1561,10 +1564,20 @@ class ExternalMethodFrame : public FramedMethodFrame
#ifdef TARGET_X86
void UpdateRegDisplay_Impl(const PREGDISPLAY pRD, bool updateFloats = false);
#endif

friend struct ::cdac_data<ExternalMethodFrame>;
};

typedef DPTR(class ExternalMethodFrame) PTR_ExternalMethodFrame;

template <>
struct cdac_data<ExternalMethodFrame>
{
static constexpr size_t GCRefMap = offsetof(ExternalMethodFrame, m_pGCRefMap);
static constexpr size_t Indirection = offsetof(ExternalMethodFrame, m_pIndirection);
static constexpr size_t ZapModule = offsetof(ExternalMethodFrame, m_pZapModule);
};

class DynamicHelperFrame : public FramedMethodFrame
{
int m_dynamicHelperFrameFlags;
Expand All @@ -1583,10 +1596,18 @@ class DynamicHelperFrame : public FramedMethodFrame
LIMITED_METHOD_DAC_CONTRACT;
return TT_InternalCall;
}

friend struct ::cdac_data<DynamicHelperFrame>;
};

typedef DPTR(class DynamicHelperFrame) PTR_DynamicHelperFrame;

template <>
struct cdac_data<DynamicHelperFrame>
{
static constexpr size_t DynamicHelperFrameFlags = offsetof(DynamicHelperFrame, m_dynamicHelperFrameFlags);
};

//------------------------------------------------------------------------
// This frame protects object references for the EE's convenience.
// This frame type actually is created from C++.
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/vm/readytoruninfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,8 @@ struct cdac_data<ReadyToRunInfo>
static constexpr size_t DelayLoadMethodCallThunks = offsetof(ReadyToRunInfo, m_pSectionDelayLoadMethodCallThunks);
static constexpr size_t DebugInfoSection = offsetof(ReadyToRunInfo, m_pSectionDebugInfo);
static constexpr size_t ExceptionInfoSection = offsetof(ReadyToRunInfo, m_pSectionExceptionInfo);
static constexpr size_t ImportSections = offsetof(ReadyToRunInfo, m_pImportSections);
static constexpr size_t NumImportSections = offsetof(ReadyToRunInfo, m_nImportSections);
static constexpr size_t EntryPointToMethodDescMap = offsetof(ReadyToRunInfo, m_entryPointToMethodDescMap);
static constexpr size_t LoadedImageBase = offsetof(ReadyToRunInfo, m_pLoadedImageBase);
static constexpr size_t Composite = offsetof(ReadyToRunInfo, m_pComposite);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ public interface IExecutionManager : IContract
List<ExceptionClauseInfo> GetExceptionClauses(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException();
JitManagerInfo GetEEJitManagerInfo() => throw new NotImplementedException();
IEnumerable<ICodeHeapInfo> GetCodeHeapInfos() => throw new NotImplementedException();

/// <summary>
/// Finds the R2R module that contains the given address.
/// Used by FindGCRefMap to resolve m_pZapModule when it's null.
Copy link
Copy Markdown
Member

@jkotas jkotas Apr 25, 2026

Choose a reason for hiding this comment

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

Can we do this unconditionally and drop ZapModule from the constract? ZapModule seems to be a nice-to-have cache.

(Also, ZapModule can be renamed to Module or ReadyToRunModule. Zap is a very old codename name for crossgen/ngen that we have almost eradicated from the codebase.)

/// Matches native ExecutionManager::FindReadyToRunModule (codeman.cpp).
/// </summary>
TargetPointer FindReadyToRunModule(TargetPointer address) => throw new NotImplementedException();
}

public readonly struct ExecutionManager : IExecutionManager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;

namespace Microsoft.Diagnostics.DataContractReader.Contracts;

public interface IGCInfoHandle { }
public readonly record struct InterruptibleRange(uint StartOffset, uint EndOffset);

public interface IGCInfo : IContract
{
Expand All @@ -14,6 +16,7 @@ public interface IGCInfo : IContract
IGCInfoHandle DecodePlatformSpecificGCInfo(TargetPointer gcInfoAddress, uint gcVersion) => throw new NotImplementedException();
IGCInfoHandle DecodeInterpreterGCInfo(TargetPointer gcInfoAddress, uint gcVersion) => throw new NotImplementedException();
uint GetCodeLength(IGCInfoHandle handle) => throw new NotImplementedException();
IReadOnlyList<InterruptibleRange> GetInterruptibleRanges(IGCInfoHandle handle) => throw new NotImplementedException();
}

public readonly struct GCInfo : IGCInfo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,14 @@ public interface IRuntimeTypeSystem : IContract
bool IsGenericMethodDefinition(MethodDescHandle methodDesc) => throw new NotImplementedException();
ReadOnlySpan<TypeHandle> GetGenericMethodInstantiation(MethodDescHandle methodDesc) => throw new NotImplementedException();

// Return true if the method requires a hidden instantiation argument (generic context parameter).
// This corresponds to native MethodDesc::RequiresInstArg().
bool RequiresInstArg(MethodDescHandle methodDesc) => throw new NotImplementedException();

// Return true if the method uses the async calling convention (CORINFO_CALLCONV_ASYNCCALL).
// This corresponds to native MethodDesc::IsAsyncMethod().
bool IsAsyncMethod(MethodDescHandle methodDesc) => throw new NotImplementedException();

// Return mdtMethodDef (0x06000000) if the method doesn't have a token, otherwise return the token of the method
uint GetMethodToken(MethodDescHandle methodDesc) => throw new NotImplementedException();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ public enum DataType
HijackFrame,
TailCallFrame,
StubDispatchFrame,
ExternalMethodFrame,
DynamicHelperFrame,

ComCallWrapper,
SimpleComCallWrapper,
ComMethodTable,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,19 @@ TargetNUInt IExecutionManager.GetRelativeOffset(CodeBlockHandle codeInfoHandle)
return info.RelativeOffset;
}

TargetPointer IExecutionManager.FindReadyToRunModule(TargetPointer address)
{
// Use the range section map to find the RangeSection containing the address.
// The R2R range section covers the entire PE image (code + data), so this
// works for import section addresses used by FindGCRefMap.
TargetCodePointer codeAddr = CodePointerUtils.CodePointerFromAddress(address, _target);
RangeSection range = RangeSection.Find(_target, _topRangeSectionMap, _rangeSectionMapLookup, codeAddr);
if (range.Data is null)
return TargetPointer.Null;

return range.Data.R2RModule;
}
Comment thread
max-charlamb marked this conversation as resolved.

JitManagerInfo IExecutionManager.GetEEJitManagerInfo()
{
TargetPointer eeJitManagerPtr = _target.ReadGlobalPointer(Constants.Globals.EEJitManagerAddress);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,6 @@ internal ExecutionManager_1(Target target)
public List<ExceptionClauseInfo> GetExceptionClauses(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetExceptionClauses(codeInfoHandle);
public JitManagerInfo GetEEJitManagerInfo() => _executionManagerCore.GetEEJitManagerInfo();
public IEnumerable<ICodeHeapInfo> GetCodeHeapInfos() => _executionManagerCore.GetCodeHeapInfos();
public TargetPointer FindReadyToRunModule(TargetPointer address) => _executionManagerCore.FindReadyToRunModule(address);
public void Flush() => _executionManagerCore.Flush();
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,6 @@ internal ExecutionManager_2(Target target)
public List<ExceptionClauseInfo> GetExceptionClauses(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetExceptionClauses(codeInfoHandle);
public JitManagerInfo GetEEJitManagerInfo() => _executionManagerCore.GetEEJitManagerInfo();
public IEnumerable<ICodeHeapInfo> GetCodeHeapInfos() => _executionManagerCore.GetCodeHeapInfos();
public TargetPointer FindReadyToRunModule(TargetPointer address) => _executionManagerCore.FindReadyToRunModule(address);
public void Flush() => _executionManagerCore.Flush();
}
Loading
Loading