Skip to content

[cDAC] Support x86 stackwalking #116072

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

Merged
merged 61 commits into from
Jun 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
b5140fe
Implement partial ExecutionManager GetFuncletStartAddress
May 6, 2025
89e9505
allow nativeAOT compilation on x86
May 16, 2025
02f14ef
implement GCInfo and RelativeAddress lookup
May 19, 2025
99a7f56
wip
May 21, 2025
6829dda
wip working PushedArgDepth
May 22, 2025
f71b823
finish the bones of x86 unwinder
May 22, 2025
3fe4a8a
continued testing and bug fixes
May 27, 2025
6b58012
fix issues with ICFs
May 28, 2025
fba0091
add docs for ExecutionManager changes
May 28, 2025
7f6b388
add doc for IsFunclet
May 28, 2025
2917ef9
fix some build issues
May 28, 2025
34a7169
move gcinfo decoding into folder
May 28, 2025
1b19fb2
move x86 context next to other contexts
May 28, 2025
3092546
add some doc comments
May 28, 2025
3b02419
remove debug print helpers
May 28, 2025
05e5909
use constant
May 29, 2025
5e813b6
add x86 stackwalk docs
May 29, 2025
1505bbb
lazily read ArgTable
Jun 2, 2025
e648dda
add datadescriptors for CalleeSavedRegisters on x86
Jun 2, 2025
2335f6d
remove unused GCInfo decoders
Jun 2, 2025
7f06960
fix stackwalk logic for x86
Jun 2, 2025
570659d
fix UnwindEspFrame
Jun 2, 2025
7e75287
R2R find GCInfo correctly
Jun 2, 2025
d7a99f0
Handle parsing ArgTab correctly when ArgTabOffset is not present
Jun 2, 2025
d6ecb88
loosen GetUsefulGlobals assertion
Jun 2, 2025
e1feb95
remove prints
Jun 2, 2025
aeb8dc4
refactor ReadyToRunJitManager to de-duplicate RuntimeFunction lookup …
Jun 2, 2025
61fbed5
comments/cleanup InfoHdr.cs
Jun 3, 2025
af6a9b1
add rts docs
Jun 3, 2025
82eb5aa
remove r2r GCDecoder change
Jun 3, 2025
ebf14c3
clean up GCInfo.cs
Jun 3, 2025
350b993
move x86 specific stackwalking helpers into namespace
Jun 3, 2025
233615e
import nit
Jun 3, 2025
dce3e02
fix
Jun 3, 2025
dff89fd
overriding increment/decrement is not required
Jun 3, 2025
2170a9f
fix some TODOs in the X86 Unwinder
Jun 3, 2025
9710bde
use xor for InfoHdr REV_PINVOKE_OFFSET
Jun 3, 2025
43eed4f
convert to use ClrDataAddress
Jun 4, 2025
7909b7d
add readytorunheader datadescriptors
Jun 5, 2025
408c566
clean up R2RJitManager
Jun 5, 2025
55e26f9
improve GCInfoVersion handling
max-charlamb Jun 5, 2025
675673b
always check for JitHalt
max-charlamb Jun 5, 2025
0bcaaf0
remove magic numbers in RTS contract
max-charlamb Jun 5, 2025
26d4b7d
remove todos
max-charlamb Jun 5, 2025
581ea4a
Merge remote-tracking branch 'origin/main' into cdac-funclets-x86-wip-2
max-charlamb Jun 5, 2025
7e4d18f
remove unused datadescriptor
max-charlamb Jun 5, 2025
905b3dc
fix managed tests
max-charlamb Jun 6, 2025
116f178
Merge remote-tracking branch 'origin/main' into cdac-funclets-x86-wip-2
max-charlamb Jun 9, 2025
38c5048
Merge branch 'cdac-funclets-x86-wip-2' of github.com:max-charlamb/run…
max-charlamb Jun 9, 2025
4b2df0f
update to match #116388
max-charlamb Jun 9, 2025
c8bba50
clean up docs
max-charlamb Jun 9, 2025
9160287
use span for const data
max-charlamb Jun 13, 2025
0a5b3d5
fix unwinder issue
max-charlamb Jun 13, 2025
ef1281e
add check for GCInfo version
max-charlamb Jun 13, 2025
423ff27
comments
max-charlamb Jun 16, 2025
fe4ead1
remove FeatureEHFunclets check in modified code
max-charlamb Jun 16, 2025
4c96d48
add comment explaining loosened GetUsefulGlobals assertions
max-charlamb Jun 16, 2025
cf8dd64
word
max-charlamb Jun 16, 2025
49d1631
Merge branch 'main' into cdac-funclets-x86-wip-2
max-charlamb Jun 16, 2025
7b18c84
Merge branch 'main' into cdac-funclets-x86-wip-2
max-charlamb Jun 17, 2025
8b7f03f
fix merge conflict
max-charlamb Jun 17, 2025
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
42 changes: 38 additions & 4 deletions docs/design/datacontracts/ExecutionManager.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,20 @@ struct CodeBlockHandle
TargetPointer GetMethodDesc(CodeBlockHandle codeInfoHandle);
// Get the instruction pointer address of the start of the code block
TargetCodePointer GetStartAddress(CodeBlockHandle codeInfoHandle);
// Get the instruction pointer address of the start of the funclet containing the code block
TargetCodePointer GetFuncletStartAddress(CodeBlockHandle codeInfoHandle);
// Gets the unwind info of the code block at the specified code pointer
TargetPointer GetUnwindInfo(CodeBlockHandle codeInfoHandle, TargetCodePointer ip);
// Gets the base address the UnwindInfo of codeInfoHandle is relative to.
TargetPointer GetUnwindInfo(CodeBlockHandle codeInfoHandle);
// Gets the base address the UnwindInfo of codeInfoHandle is relative to
TargetPointer GetUnwindInfoBaseAddress(CodeBlockHandle codeInfoHandle);
// Gets the GCInfo associated with the code block and its version
// **Currently GetGCInfo only supports X86**
void GetGCInfo(CodeBlockHandle codeInfoHandle, out TargetPointer gcInfo, out uint gcVersion);
// Gets the offset of the codeInfoHandle inside of the code block
TargetNUInt GetRelativeOffset(CodeBlockHandle codeInfoHandle);

// Extension Methods (implemented in terms of other APIs)
bool IsFunclet(CodeBlockHandle codeInfoHandle);
```

## Version 1
Expand Down Expand Up @@ -59,14 +69,18 @@ Data descriptors used:
| `RealCodeHeader` | `MethodDesc` | Pointer to the corresponding `MethodDesc` |
| `RealCodeHeader` | `NumUnwindInfos` | Number of Unwind Infos |
| `RealCodeHeader` | `UnwindInfos` | Start address of Unwind Infos |
| `RealCodeHeader` | `GCInfo` | Pointer to the GCInfo encoding |
| `Module` | `ReadyToRunInfo` | Pointer to the `ReadyToRunInfo` for the module |
| `ReadyToRunInfo` | `ReadyToRunHeader` | Pointer to the ReadyToRunHeader |
| `ReadyToRunInfo` | `CompositeInfo` | Pointer to composite R2R info - or itself for non-composite |
| `ReadyToRunInfo` | `NumRuntimeFunctions` | Number of `RuntimeFunctions` |
| `ReadyToRunInfo` | `RuntimeFunctions` | Pointer to an array of `RuntimeFunctions` - [see R2R format](../coreclr/botr/readytorun-format.md#readytorunsectiontyperuntimefunctions)|
| `ReadyToRunInfo` | `NumHotColdMap` | Number of entries in the `HotColdMap` |
| `ReadyToRunInfo` | `HotColdMap` | Pointer to an array of 32-bit integers - [see R2R format](../coreclr/botr/readytorun-format.md#readytorunsectiontypehotcoldmap-v80) |
| `ReadyToRunInfo` | `DelayLoadMethodCallThunks` | Pointer to an `ImageDataDirectory` for the delay load method call thunks |
| `ReadyToRunInfo` | `EntryPointToMethodDescMap` | `HashMap` of entry point addresses to `MethodDesc` pointers |
| `ReadyToRunHeader` | `MajorVersion` | ReadyToRun major version |
| `ReadyToRunHeader` | `MinorVersion` | ReadyToRun minor version |
| `ImageDataDirectory` | `VirtualAddress` | Virtual address of the image data directory |
| `ImageDataDirectory` | `Size` | Size of the data |
| `RuntimeFunction` | `BeginAddress` | Begin address of the function |
Expand All @@ -85,6 +99,7 @@ Global variables used:
| `HashMapSlotsPerBucket` | uint32 | Number of slots in each bucket of a `HashMap` |
| `HashMapValueMask` | uint64 | Bitmask used when storing values in a `HashMap` |
| `FeatureEHFunclets` | uint8 | 1 if EH funclets are enabled, 0 otherwise |
Copy link
Member

Choose a reason for hiding this comment

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

I think you can delete this constant too

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There are a couple other uses of the FeatureEHFunclets flag, I was going to open a different PR to totally remove the datadescriptor if that is okay.

Copy link
Member

Choose a reason for hiding this comment

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

Ok

Copy link
Contributor Author

Choose a reason for hiding this comment

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

#116743 created, to avoid merge conflicts it will fail to build until this PR is merged

| `GCInfoVersion` | uint32 | JITted code GCInfo version |

Contracts used:
| Contract Name |
Expand Down Expand Up @@ -220,7 +235,7 @@ class CodeBlock
}
```

The `GetMethodDesc` and `GetStartAddress` APIs extract fields of the `CodeBlock`:
The `GetMethodDesc`, `GetStartAddress`, and `GetRelativeOffset` APIs extract fields of the `CodeBlock`:

```csharp
TargetPointer IExecutionManager.GetMethodDesc(CodeBlockHandle codeInfoHandle)
Expand All @@ -234,9 +249,15 @@ The `GetMethodDesc` and `GetStartAddress` APIs extract fields of the `CodeBlock`
/* find CodeBlock info for codeInfoHandle.Address*/
return info.StartAddress;
}

TargetNUInt IExecutionManager.GetRelativeOffset(CodeBlockHandle codeInfoHandle)
{
/* find CodeBlock info for codeInfoHandle.Address*/
return info.RelativeOffset;
}
```

`GetUnwindInfo` gets the Windows style unwind data in the form of `RUNTIME_FUNCTION` which has a platform dependent implementation. The ExecutionManager delegates to the JitManager implementations as the unwind infos (`RUNTIME_FUNCTION`) are stored differently on jitted and R2R code.
`IExecutionManager.GetUnwindInfo` gets the Windows style unwind data in the form of `RUNTIME_FUNCTION` which has a platform dependent implementation. The ExecutionManager delegates to the JitManager implementations as the unwind infos (`RUNTIME_FUNCTION`) are stored differently on jitted and R2R code.

* For jitted code (`EEJitManager`) a list of sorted `RUNTIME_FUNCTION` are stored on the `RealCodeHeader` which is accessed in the same was as `GetMethodInfo` described above. The correct `RUNTIME_FUNCTION` is found by binary searching the list based on IP.

Expand All @@ -245,6 +266,19 @@ The `GetMethodDesc` and `GetStartAddress` APIs extract fields of the `CodeBlock`
Unwind info (`RUNTIME_FUNCTION`) use relative addressing. For managed code, these values are relative to the start of the code's containing range in the RangeSectionMap (described below). This could be the beginning of a `CodeHeap` for jitted code or the base address of the loaded image for ReadyToRun code.
`GetUnwindInfoBaseAddress` finds this base address for a given `CodeBlockHandle`.

`IExecutionManager.GetGCInfo` gets a pointer to the relevant GCInfo for a `CodeBlockHandle`. The ExecutionManager delegates to the JitManager implementations as the GCInfo is stored differently on jitted and R2R code.

* For jitted code (`EEJitManager`) a pointer to the `GCInfo` is stored on the `RealCodeHeader` which is accessed in the same was as `GetMethodInfo` described above. This can simply be returned as is. The `GCInfoVersion` is defined by the runtime global `GCInfoVersion`.

* For R2R code (`ReadyToRunJitManager`), the `GCInfo` is stored directly after the `UnwindData`. This in turn is found by looking up the `UnwindInfo` (`RUNTIME_FUNCTION`) and reading the `UnwindData` offset. We find the `UnwindInfo` as described above in `IExecutionManager.GetUnwindInfo`. Once we have the relevant unwind data, we calculate the size of the unwind data and return a pointer to the following byte (first byte of the GCInfo). The size of the unwind data is a platform specific. Currently only X86 is supported with a constant unwind data size of 32-bits.
* The `GCInfoVersion` of R2R code is mapped from the R2R MajorVersion and MinorVersion which is read from the ReadyToRunHeader which itself is read from the ReadyToRunInfo (can be found as in GetMethodInfo). The current GCInfoVersion mapping is:
* MajorVersion >= 11 and MajorVersion < 15 => 4


`IExecutionManager.GetFuncletStartAddress` finds the start of the code blocks funclet. This will be different than the methods start address `GetStartAddress` if the current code block is inside of a funclet. To find the funclet start address, we get the unwind info corresponding to the code block using `IExecutionManager.GetUnwindInfo`. We then parse the unwind info to find the begin address (relative to the unwind info base address) and return the unwind info base address + unwind info begin address.

`IsFunclet` is implemented in terms of `IExecutionManager.GetStartAddress` and `IExecutionManager.GetFuncletStartAddress`. If the values are the same, the code block handle is not a funclet. If they are different, it is a funclet.

### RangeSectionMap

The range section map logically partitions the entire 32-bit or 64-bit addressable space into chunks.
Expand Down
13 changes: 13 additions & 0 deletions docs/design/datacontracts/StackWalk.md
Original file line number Diff line number Diff line change
Expand Up @@ -327,3 +327,16 @@ TargetPointer GetFrameAddress(IStackDataFrameHandle stackDataFrameHandle);
```csharp
string GetFrameName(TargetPointer frameIdentifier);
```

### x86 Specifics

The x86 platform has some major differences to other platforms. In general this stems from the platform being older and not having a defined unwinding codes. Instead, to unwind managed frames, we rely on GCInfo associated with JITted code. For the unwind, we do not defer to a 'Windows like' native unwinder, instead the custom unwinder implementation was ported to managed code.

#### GCInfo Parsing
The GCInfo structure is encoded using a variety of formats to optimize for speed of decoding and size on disk. For information on decoding and parsing refer to [GC Information Encoding for x86](../coreclr/jit/jit-gc-info-x86.md).

#### Unwinding Algorithm

The x86 architecture uses a custom unwinding algorithm defined in `gc_unwind_x86.inl`. The cDAC uses a copy of this algorithm ported to managed code in `X86Unwinder.cs`.

Currently there isn't great documentation on the algorithm, beyond inspecting the implementations.
12 changes: 11 additions & 1 deletion src/coreclr/debug/runtimeinfo/datadescriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,7 @@ CDAC_TYPE_END(FixupPrecodeData)

CDAC_TYPE_BEGIN(ReadyToRunInfo)
CDAC_TYPE_INDETERMINATE(ReadyToRunInfo)
CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, ReadyToRunHeader, cdac_data<ReadyToRunInfo>::ReadyToRunHeader)
CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, CompositeInfo, cdac_data<ReadyToRunInfo>::CompositeInfo)
CDAC_TYPE_FIELD(ReadyToRunInfo, /*uint32*/, NumRuntimeFunctions, cdac_data<ReadyToRunInfo>::NumRuntimeFunctions)
CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, RuntimeFunctions, cdac_data<ReadyToRunInfo>::RuntimeFunctions)
Expand All @@ -575,6 +576,12 @@ CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, DelayLoadMethodCallThunks, cdac_dat
CDAC_TYPE_FIELD(ReadyToRunInfo, /*HashMap*/, EntryPointToMethodDescMap, cdac_data<ReadyToRunInfo>::EntryPointToMethodDescMap)
CDAC_TYPE_END(ReadyToRunInfo)

CDAC_TYPE_BEGIN(ReadyToRunHeader)
CDAC_TYPE_INDETERMINATE(READYTORUN_HEADER)
CDAC_TYPE_FIELD(ReadyToRunHeader, /*uint16*/, MajorVersion, offsetof(READYTORUN_HEADER, MajorVersion))
CDAC_TYPE_FIELD(ReadyToRunHeader, /*uint16*/, MinorVersion, offsetof(READYTORUN_HEADER, MinorVersion))
CDAC_TYPE_END(ReadyToRunHeader)

CDAC_TYPE_BEGIN(ImageDataDirectory)
CDAC_TYPE_SIZE(sizeof(IMAGE_DATA_DIRECTORY))
CDAC_TYPE_FIELD(ImageDataDirectory, /*uint32*/, VirtualAddress, offsetof(IMAGE_DATA_DIRECTORY, VirtualAddress))
Expand Down Expand Up @@ -635,6 +642,7 @@ CDAC_TYPE_END(RangeSection)
CDAC_TYPE_BEGIN(RealCodeHeader)
CDAC_TYPE_INDETERMINATE(RealCodeHeader)
CDAC_TYPE_FIELD(RealCodeHeader, /*pointer*/, MethodDesc, offsetof(RealCodeHeader, phdrMDesc))
CDAC_TYPE_FIELD(RealCodeHeader, /*pointer*/, GCInfo, offsetof(RealCodeHeader, phdrJitGCInfo))
#ifdef FEATURE_EH_FUNCLETS
CDAC_TYPE_FIELD(RealCodeHeader, /*uint32*/, NumUnwindInfos, offsetof(RealCodeHeader, nUnwindInfos))
CDAC_TYPE_FIELD(RealCodeHeader, /* T_RUNTIME_FUNCTION */, UnwindInfos, offsetof(RealCodeHeader, unwindInfos))
Expand Down Expand Up @@ -783,7 +791,7 @@ CDAC_TYPE_END(FaultingExceptionFrame)
// CalleeSavedRegisters struct is different on each platform
CDAC_TYPE_BEGIN(CalleeSavedRegisters)
CDAC_TYPE_SIZE(sizeof(CalleeSavedRegisters))
#if defined(TARGET_AMD64)
#if defined(TARGET_AMD64) || defined(TARGET_X86)

#define CALLEE_SAVED_REGISTER(regname) \
CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, regname, offsetof(CalleeSavedRegisters, regname))
Expand Down Expand Up @@ -838,6 +846,8 @@ CDAC_GLOBAL_STRING(Architecture, riscv64)

CDAC_GLOBAL_STRING(RID, RID_STRING)

CDAC_GLOBAL(GCInfoVersion, uint32, GCINFO_VERSION)

CDAC_GLOBAL_POINTER(AppDomain, &AppDomain::m_pTheAppDomain)
CDAC_GLOBAL_POINTER(SystemDomain, cdac_data<SystemDomain>::SystemDomain)
CDAC_GLOBAL_POINTER(ThreadStore, &ThreadStore::s_pThreadStore)
Expand Down
12 changes: 7 additions & 5 deletions src/coreclr/inc/gcinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,15 @@ struct GCInfoToken
}
#endif

// Keep this in sync with GetR2RGCInfoVersion in cDac (ExecutionManagerCore.ReadyToRunJitManager.cs)
static uint32_t ReadyToRunVersionToGcInfoVersion(uint32_t readyToRunMajorVersion, uint32_t readyToRunMinorVersion)
{
#ifdef SOS_INCLUDE
return GCInfoVersion();
#else
return GCINFO_VERSION;
#endif
if (readyToRunMajorVersion >= 11)
return 4;

// Since v2 and v3 had the same file format and v1 is no longer supported,
// we can assume GCInfo v3.
return 3;
}
};

Expand Down
4 changes: 0 additions & 4 deletions src/coreclr/vm/gc_unwind_x86.inl
Original file line number Diff line number Diff line change
Expand Up @@ -2759,14 +2759,12 @@ void UnwindEspFrameProlog(

unsigned offset = 0;

#ifdef _DEBUG
// If the first two instructions are 'nop, int3', then we will
// assume that is from a JitHalt operation and skip past it
if (methodStart[0] == X86_INSTR_NOP && methodStart[1] == X86_INSTR_INT3)
{
offset += 2;
}
#endif

const DWORD curOffs = info->prologOffs;
unsigned ESP = pContext->SP;
Expand Down Expand Up @@ -2922,14 +2920,12 @@ void UnwindEbpDoubleAlignFrameProlog(

DWORD offset = 0;

#ifdef _DEBUG
// If the first two instructions are 'nop, int3', then we will
// assume that is from a JitHalt operation and skip past it
if (methodStart[0] == X86_INSTR_NOP && methodStart[1] == X86_INSTR_INT3)
{
offset += 2;
}
#endif

/* Check for the case where EBP has not been updated yet. */

Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/readytoruninfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ class ReadyToRunInfo
template<>
struct cdac_data<ReadyToRunInfo>
{
static constexpr size_t ReadyToRunHeader = offsetof(ReadyToRunInfo, m_pHeader);
static constexpr size_t CompositeInfo = offsetof(ReadyToRunInfo, m_pCompositeInfo);
static constexpr size_t NumRuntimeFunctions = offsetof(ReadyToRunInfo, m_nRuntimeFunctions);
static constexpr size_t RuntimeFunctions = offsetof(ReadyToRunInfo, m_pRuntimeFunctions);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// 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.Contracts.Extensions;

public static class IExecutionManagerExtensions
{
public static bool IsFunclet(this IExecutionManager eman, CodeBlockHandle codeBlockHandle)
{
return eman.GetStartAddress(codeBlockHandle) != eman.GetFuncletStartAddress(codeBlockHandle);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@ public interface IExecutionManager : IContract
CodeBlockHandle? GetCodeBlockHandle(TargetCodePointer ip) => throw new NotImplementedException();
TargetPointer GetMethodDesc(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException();
TargetCodePointer GetStartAddress(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException();
TargetPointer GetUnwindInfo(CodeBlockHandle codeInfoHandle, TargetCodePointer ip) => throw new NotImplementedException();
TargetCodePointer GetFuncletStartAddress(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException();
TargetPointer GetUnwindInfo(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException();
TargetPointer GetUnwindInfoBaseAddress(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException();
// **Currently GetGCInfo only supports X86**
void GetGCInfo(CodeBlockHandle codeInfoHandle, out TargetPointer gcInfo, out uint gcVersion) => throw new NotImplementedException();
TargetNUInt GetRelativeOffset(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException();
}

public readonly struct ExecutionManager : IExecutionManager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ public enum DataType
ProfControlBlock,
ILCodeVersionNode,
ReadyToRunInfo,
ReadyToRunHeader,
ImageDataDirectory,
RuntimeFunction,
HashMap,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ public static class Globals

public const string Architecture = nameof(Architecture);
public const string OperatingSystem = nameof(OperatingSystem);

public const string GCInfoVersion = nameof(GCInfoVersion);
}
public static class FieldNames
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,30 @@ public override TargetPointer GetUnwindInfo(RangeSection rangeSection, TargetCod
return _runtimeFunctions.GetRuntimeFunctionAddress(unwindInfos, index);
}

public override void GetGCInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, out TargetPointer gcInfo, out uint gcVersion)
{
gcInfo = TargetPointer.Null;
gcVersion = 0;

// EEJitManager::GetGCInfoToken
if (rangeSection.IsRangeList)
return;

if (rangeSection.Data == null)
throw new ArgumentException(nameof(rangeSection));

TargetPointer codeStart = FindMethodCode(rangeSection, jittedCodeAddress);
if (codeStart == TargetPointer.Null)
return;
Debug.Assert(codeStart.Value <= jittedCodeAddress.Value);

if (!GetRealCodeHeader(rangeSection, codeStart, out Data.RealCodeHeader? realCodeHeader))
return;

gcVersion = Target.ReadGlobal<uint>(Constants.Globals.GCInfoVersion);
gcInfo = realCodeHeader.GCInfo;
}

private TargetPointer FindMethodCode(RangeSection rangeSection, TargetCodePointer jittedCodeAddress)
{
// EEJitManager::FindMethodCode
Expand Down
Loading
Loading