Skip to content

Commit

Permalink
[cdac] Implement `ExecutionManager.ReadyToRunJitManager.GetMethodInfo…
Browse files Browse the repository at this point in the history
…` (minus handling of hot/cold lookup) (#109766)

Implement `ExecutionManager.ReadyToRunJitManager.GetMethodInfo` without handling of hot/cold lookup. This maps to `ReadyToRunJitManager::JitCodeToMethodInfo` in the runtime. Basic logic is:
- Check if the address is in a thunk for `READYTORUN_HELPER_DelayLoad_MethodCall`
- Find the runtime function entry corresponding to the address
- Look up the `MethodDesc` for the entry point using the `ReadyToRunInfo`'s hash map

Add tests for `ExecutionManager` for getting code blocks and method desc in R2R and for `HashMap` lookup functionality
- Start using `Moq` - this change only uses it to mock `IPlatformMetadata`, but I think we should be able to use this instead some of the explicit subclasses we have for testing.
- Simplify usage of `TestPlaceholderTarget` such that setting the reader delegate and data cache are not explicit operations - make its constructor take a reader delegate and always create a default data cache
- Slight clean up of `ExecutionManagerTestBuilder` - make it more consistent with `MockDescriptors.*`
  - I think this should probably also be moved under `MockDescriptors`, but I didn't want to do that in this change (same with some helpers in other test classes, like PrecodeStubsTests)

Manually validated with `!clrstack` and `!ip2md` in windbg that R2R functions now show up correctly (were `<unknown>` before this change).
  • Loading branch information
elinor-fung authored Nov 15, 2024
1 parent 0cecd7d commit 22dc50d
Show file tree
Hide file tree
Showing 38 changed files with 1,405 additions and 395 deletions.
150 changes: 112 additions & 38 deletions docs/design/datacontracts/ExecutionManager.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,30 +28,57 @@ struct CodeBlockHandle
## Version 1

The execution manager uses two data structures to map the entire target address space to native executable code.
The range section map is used to partition the address space into large chunks which point to range section fragments. Each chunk is relatively large. If there is any executable code in the chunk, the chunk will contain one or more range section fragments that cover subsets of the chunk. Conversely if a massive method is JITed a single range section fragment may span multiple adjacent chunks.
The [range section map](#rangesectionmap) is used to partition the address space into large chunks which point to range section fragments. Each chunk is relatively large. If there is any executable code in the chunk, the chunk will contain one or more range section fragments that cover subsets of the chunk. Conversely if a massive method is JITed a single range section fragment may span multiple adjacent chunks.

Within a range section fragment, a nibble map structure is used to map arbitrary IP addresses back to the start of the method (and to the code header which immediately preceeeds the entrypoint to the code).
Within a range section fragment, a [nibble map](#nibblemap) structure is used to map arbitrary IP addresses back to the start of the method (and to the code header which immediately preceeeds the entrypoint to the code).

Data descriptors used:
| Data Descriptor Name | Field | Meaning |
| --- | --- | --- |
| RangeSectionMap | TopLevelData | pointer to the outermost RangeSection |
| RangeSectionFragment| ? | ? |
| RangeSection | ? | ? |
| RealCodeHeader | ? | ? |
| HeapList | ? | ? |


| `RangeSectionMap` | `TopLevelData` | Pointer to the outermost RangeSection |
| `RangeSectionFragment`| `RangeBegin` | Begin address of the fragment |
| `RangeSectionFragment`| `RangeEndOpen` | End address of the fragment |
| `RangeSectionFragment`| `RangeSection` | Pointer to the corresponding `RangeSection` |
| `RangeSectionFragment`| `Next` | Pointer to the next fragment |
| `RangeSection` | `RangeBegin` | Begin address of the range section |
| `RangeSection` | `RangeEndOpen` | End address of the range section |
| `RangeSection` | `NextForDelete` | Pointer to next range section for deletion |
| `RangeSection` | `JitManager` | Pointer to the JIT manager |
| `RangeSection` | `Flags` | Flags for the range section |
| `RangeSection` | `HeapList` | Pointer to the heap list |
| `RangeSection` | `R2RModule` | ReadyToRun module |
| `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 |
| `CodeHeapListNode` | `MapBase` | Start of the map - start address rounded down based on OS page size |
| `CodeHeapListNode` | `HeaderMap` | Bit array used to find the start of methods - relative to `MapBase` |
| `RealCodeHeader` | `MethodDesc` | Pointer to the corresponding `MethodDesc` |
| `Module` | `ReadyToRunInfo` | Pointer to the `ReadyToRunInfo` for the module |
| `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` |
| `ReadyToRunInfo` | `DelayLoadMethodCallThunks` | Pointer to an `ImageDataDirectory` for the delay load method call thunks |
| `ReadyToRunInfo` | `EntryPointToMethodDescMap` | `HashMap` of entry point addresses to `MethodDesc` pointers |
| `ImageDataDirectory` | `VirtualAddress` | Virtual address of the image data directory |
| `ImageDataDirectory` | `Size` | Size of the data |
| `RuntimeFunction` | `BeginAddress` | Begin address of the function |
| `HashMap` | `Buckets` | Pointer to the buckets of a `HashMap` |
| `Bucket` | `Keys` | Array of keys of `HashMapSlotsPerBucket` length |
| `Bucket` | `Values` | Array of values of `HashMapSlotsPerBucket` length |

Global variables used:
| Global Name | Type | Purpose |
| --- | --- | --- |
| ExecutionManagerCodeRangeMapAddress | TargetPointer | Pointer to the global RangeSectionMap
| StubCodeBlockLast | uint8 | Maximum sentinel code header value indentifying a stub code block
| `ExecutionManagerCodeRangeMapAddress` | TargetPointer | Pointer to the global RangeSectionMap |
| `StubCodeBlockLast` | uint8 | Maximum sentinel code header value indentifying a stub code block |
| `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 |

Contracts used:
| Contract Name |
| --- |
| `PlatformMetadata` |

The bulk of the work is done by the `GetCodeBlockHandle` API that maps a code pointer to information about the containing jitted method.

Expand Down Expand Up @@ -97,46 +124,93 @@ There are two `JitManager`s: the "EE JitManager" for jitted code and "R2R JitMan
The EE JitManager `GetMethodInfo` implements the nibble map lookup, summarized below, followed by returning the `RealCodeHeader` data:

```csharp
bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, [NotNullWhen(true)] out CodeBlock? info)
bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, [NotNullWhen(true)] out CodeBlock? info)
{
TargetPointer start = FindMethodCode(rangeSection, jittedCodeAddress); // nibble map lookup
if (start == TargetPointer.Null)
{
TargetPointer start = FindMethodCode(rangeSection, jittedCodeAddress); // nibble map lookup
if (start == TargetPointer.Null)
{
return false;
}
TargetNUInt relativeOffset = jittedCodeAddress - start;
int codeHeaderOffset = Target.PointerSize;
TargetPointer codeHeaderIndirect = start - codeHeaderOffset;
if (RangeSection.IsStubCodeBlock(Target, codeHeaderIndirect))
{
return false;
}
TargetPointer codeHeaderAddress = Target.ReadPointer(codeHeaderIndirect);
Data.RealCodeHeader realCodeHeader = Target.ProcessedData.GetOrAdd<Data.RealCodeHeader>(codeHeaderAddress);
info = new CodeBlock(jittedCodeAddress, codeHeaderOffset, relativeOffset, realCodeHeader, rangeSection.Data!.JitManager);
return true;
return false;
}
TargetNUInt relativeOffset = jittedCodeAddress - start;
int codeHeaderOffset = Target.PointerSize;
TargetPointer codeHeaderIndirect = start - codeHeaderOffset;
if (RangeSection.IsStubCodeBlock(Target, codeHeaderIndirect))
{
return false;
}
TargetPointer codeHeaderAddress = Target.ReadPointer(codeHeaderIndirect);
Data.RealCodeHeader realCodeHeader = Target.ProcessedData.GetOrAdd<Data.RealCodeHeader>(codeHeaderAddress);
info = new CodeBlock(jittedCodeAddress, realCodeHeader.MethodDesc, relativeOffset, rangeSection.Data!.JitManager);
return true;
}
```

The R2R JitManager `GetMethodInfo` finds the runtime function corresponding to an address and maps its entry point pack to a method:

```csharp
bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, [NotNullWhen(true)] out CodeBlock? info)
{
if (rangeSection.Data == null)
throw new ArgumentException(nameof(rangeSection));

info = default;

TargetPointer r2rModule = Target.ReadPointer(/* range section address + RangeSection::R2RModule offset */);
TargetPointer r2rInfo = Target.ReadPointer(r2rModule + /* Module::ReadyToRunInfo offset */);

// Check if address is in a thunk
if (IsStubCodeBlockThunk(rangeSection.Data, r2rInfo, jittedCodeAddress))
return false;

// Find the relative address that we are looking for
TargetCodePointer code = /* code pointer from jittedCodeAddress using PlatformMetadata.GetCodePointerFlags */
TargetPointer imageBase = Target.ReadPointer(/* range section address + RangeSection::RangeBegin offset */);
TargetPointer relativeAddr = code - imageBase;

TargetPointer runtimeFunctions = Target.ReadPointer(r2rInfo + /* ReadyToRunInfo::RuntimeFunctions offset */);
int index = // Iterate through runtimeFunctions and find index of function with relativeAddress
if (index < 0)
return false;

bool featureEHFunclets = Target.ReadGlobal<byte>(Constants.Globals.FeatureEHFunclets) != 0;
if (featureEHFunclets)
{
// TODO: [cdac] Look up in hot/cold mapping lookup table and if the method is in the cold block,
// get the index of the associated hot block.
}

TargetPointer function = runtimeFunctions + (ulong)(index * /* size of RuntimeFunction */);

TargetPointer startAddress = imageBase + Target.Read<uint>(function + /* RuntimeFunction::BeginAddress offset */);
TargetPointer entryPoint = /* code pointer from startAddress using PlatformMetadata.GetCodePointerFlags */

TargetPointer mapAddress = r2rInfo + /* ReadyToRunInfo::EntryPointToMethodDescMap offset */;
TargetPointer methodDesc = /* look up entryPoint in HashMap at mapAddress */;

// TODO: [cdac] Handle method with cold code when computing relative offset
TargetNUInt relativeOffset = new TargetNUInt(code - startAddress);

info = new CodeBlock(startAddress.Value, methodDesc, relativeOffset, rangeSection.Data!.JitManager);
return true;
}
```

The `CodeBlock` encapsulates the `RealCodeHeader` data from the target runtime together with the start of the jitted method
The `CodeBlock` encapsulates the `MethodDesc` data from the target runtime together with the start of the jitted method

```csharp
class CodeBlock
{
private readonly int _codeHeaderOffset;

public TargetCodePointer StartAddress { get; }
// note: this is the address of the pointer to the "real code header", you need to
// dereference it to get the address of _codeHeaderData
public TargetPointer CodeHeaderAddress => StartAddress - _codeHeaderOffset;
private Data.RealCodeHeader _codeHeaderData;
public TargetPointer MethodDesc { get; }
public TargetPointer JitManagerAddress { get; }
public TargetNUInt RelativeOffset { get; }
public CodeBlock(TargetCodePointer startAddress, int codeHeaderOffset, TargetNUInt relativeOffset, Data.RealCodeHeader codeHeaderData, TargetPointer jitManagerAddress)

public CodeBlock(TargetCodePointer startAddress, TargetPointer methodDesc, TargetNUInt relativeOffset, TargetPointer jitManagerAddress)
{
_codeHeaderOffset = codeHeaderOffset;
StartAddress = startAddress;
_codeHeaderData = codeHeaderData;
MethodDesc = methodDesc;
RelativeOffset = relativeOffset;
JitManagerAddress = jitManagerAddress;
}
Expand All @@ -151,13 +225,13 @@ The remaining contract APIs extract fields of the `CodeBlock`:
```csharp
TargetPointer IExecutionManager.GetMethodDesc(CodeBlockHandle codeInfoHandle)
{
/* find EECodeBlock info for codeInfoHandle.Address*/
/* find CodeBlock info for codeInfoHandle.Address*/
return info.MethodDescAddress;
}

TargetCodePointer IExecutionManager.GetStartAddress(CodeBlockHandle codeInfoHandle)
{
/* find EECodeBlock info for codeInfoHandle.Address*/
/* find CodeBlock info for codeInfoHandle.Address*/
return info.StartAddress;
}
```
Expand Down
34 changes: 34 additions & 0 deletions src/coreclr/debug/runtimeinfo/datadescriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ CDAC_TYPE_FIELD(Module, /*pointer*/, ThunkHeap, cdac_data<Module>::ThunkHeap)
CDAC_TYPE_FIELD(Module, /*pointer*/, DynamicMetadata, cdac_data<Module>::DynamicMetadata)
CDAC_TYPE_FIELD(Module, /*pointer*/, Path, cdac_data<Module>::Path)
CDAC_TYPE_FIELD(Module, /*pointer*/, FileName, cdac_data<Module>::FileName)
CDAC_TYPE_FIELD(Module, /*pointer*/, ReadyToRunInfo, cdac_data<Module>::ReadyToRunInfo)

CDAC_TYPE_FIELD(Module, /*pointer*/, FieldDefToDescMap, cdac_data<Module>::FieldDefToDescMap)
CDAC_TYPE_FIELD(Module, /*pointer*/, ManifestModuleReferencesMap, cdac_data<Module>::ManifestModuleReferencesMap)
Expand Down Expand Up @@ -412,6 +413,37 @@ CDAC_TYPE_INDETERMINATE(FixupPrecodeData)
CDAC_TYPE_FIELD(FixupPrecodeData, /*pointer*/, MethodDesc, offsetof(FixupPrecodeData, MethodDesc))
CDAC_TYPE_END(FixupPrecodeData)

CDAC_TYPE_BEGIN(ReadyToRunInfo)
CDAC_TYPE_INDETERMINATE(ReadyToRunInfo)
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)
CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, DelayLoadMethodCallThunks, cdac_data<ReadyToRunInfo>::DelayLoadMethodCallThunks)
CDAC_TYPE_FIELD(ReadyToRunInfo, /*HashMap*/, EntryPointToMethodDescMap, cdac_data<ReadyToRunInfo>::EntryPointToMethodDescMap)
CDAC_TYPE_END(ReadyToRunInfo)

CDAC_TYPE_BEGIN(ImageDataDirectory)
CDAC_TYPE_SIZE(sizeof(IMAGE_DATA_DIRECTORY))
CDAC_TYPE_FIELD(ImageDataDirectory, /*uint32*/, VirtualAddress, offsetof(IMAGE_DATA_DIRECTORY, VirtualAddress))
CDAC_TYPE_FIELD(ImageDataDirectory, /*uint32*/, Size, offsetof(IMAGE_DATA_DIRECTORY, Size))
CDAC_TYPE_END(ImageDataDirectory)

CDAC_TYPE_BEGIN(RuntimeFunction)
CDAC_TYPE_SIZE(sizeof(RUNTIME_FUNCTION))
CDAC_TYPE_FIELD(RuntimeFunction, /*uint32*/, BeginAddress, offsetof(RUNTIME_FUNCTION, BeginAddress))
CDAC_TYPE_END(RuntimeFunction)

CDAC_TYPE_BEGIN(HashMap)
CDAC_TYPE_INDETERMINATE(HashMap)
CDAC_TYPE_FIELD(HashMap, /*pointer*/, Buckets, cdac_data<HashMap>::Buckets)
CDAC_TYPE_END(HashMap)

CDAC_TYPE_BEGIN(Bucket)
CDAC_TYPE_SIZE(sizeof(Bucket))
CDAC_TYPE_FIELD(Bucket, /*pointer*/, Keys, offsetof(Bucket, m_rgKeys))
CDAC_TYPE_FIELD(Bucket, /*pointer*/, Values, offsetof(Bucket, m_rgValues))
CDAC_TYPE_END(Bucket)

CDAC_TYPE_BEGIN(RangeSectionMap)
CDAC_TYPE_INDETERMINATE(RangeSectionMap)
CDAC_TYPE_FIELD(RangeSectionMap, /*pointer*/, TopLevelData, cdac_data<RangeSectionMap>::TopLevelData)
Expand Down Expand Up @@ -501,6 +533,8 @@ CDAC_GLOBAL(ObjectToMethodTableUnmask, uint8, 1 | 1 << 1)
#endif //TARGET_64BIT
CDAC_GLOBAL(SOSBreakingChangeVersion, uint8, SOS_BREAKING_CHANGE_VERSION)
CDAC_GLOBAL(DirectorySeparator, uint8, (uint8_t)DIRECTORY_SEPARATOR_CHAR_A)
CDAC_GLOBAL(HashMapSlotsPerBucket, uint32, SLOTS_PER_BUCKET)
CDAC_GLOBAL(HashMapValueMask, uint64, VALUE_MASK)
CDAC_GLOBAL(MethodDescAlignment, uint64, MethodDesc::ALIGNMENT)
CDAC_GLOBAL(ObjectHeaderSize, uint64, OBJHEADER_SIZE)
CDAC_GLOBAL(SyncBlockValueToObjectOffset, uint16, OBJHEADER_SIZE - cdac_data<ObjHeader>::SyncBlockValue)
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/ceeload.h
Original file line number Diff line number Diff line change
Expand Up @@ -1649,6 +1649,7 @@ struct cdac_data<Module>
static constexpr size_t DynamicMetadata = offsetof(Module, m_pDynamicMetadata);
static constexpr size_t Path = offsetof(Module, m_path);
static constexpr size_t FileName = offsetof(Module, m_fileName);
static constexpr size_t ReadyToRunInfo = offsetof(Module, m_pReadyToRunInfo);

// Lookup map pointers
static constexpr size_t FieldDefToDescMap = offsetof(Module, m_FieldDefToDescMap);
Expand Down
2 changes: 0 additions & 2 deletions src/coreclr/vm/codeman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5888,8 +5888,6 @@ BOOL ReadyToRunJitManager::JitCodeToMethodInfo(RangeSection * pRangeSection,
// Save the raw entry
PTR_RUNTIME_FUNCTION RawFunctionEntry = pRuntimeFunctions + MethodIndex;

ULONG UMethodIndex = (ULONG)MethodIndex;

const int lookupIndex = HotColdMappingLookupTable::LookupMappingForMethod(pInfo, (ULONG)MethodIndex);
if ((lookupIndex != -1) && ((lookupIndex & 1) == 1))
{
Expand Down
7 changes: 2 additions & 5 deletions src/coreclr/vm/codeman.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,9 @@ Module Name:
An IJitManager knows about which method bodies live in each RangeSection.
It can handle methods of one given CodeType. It can map a method body to
a MethodDesc. It knows where the GCInfo about the method lives.
Today, we have three IJitManagers viz.
Today, we have two IJitManagers:
1. EEJitManager for JITcompiled code generated by clrjit.dll
2. NativeImageJitManager for ngenned code.
3. ReadyToRunJitManager for version resiliant ReadyToRun code
2. ReadyToRunJitManager for version resiliant ReadyToRun code
An ICodeManager knows how to crack a specific format of GCInfo. There is
a default format (handled by ExecutionManager::GetDefaultCodeManager())
Expand Down Expand Up @@ -73,7 +72,6 @@ class MethodDesc;
class ICorJitCompiler;
class IJitManager;
class EEJitManager;
class NativeImageJitManager;
class ReadyToRunJitManager;
class ExecutionManager;
class Thread;
Expand Down Expand Up @@ -1657,7 +1655,6 @@ class HostCodeHeap;
typedef VPTR(class HostCodeHeap) PTR_HostCodeHeap;

typedef VPTR(class EEJitManager) PTR_EEJitManager;
typedef VPTR(class NativeImageJitManager) PTR_NativeImageJitManager;
typedef VPTR(class ReadyToRunJitManager) PTR_ReadyToRunJitManager;

struct JumpStubBlockHeader
Expand Down
2 changes: 0 additions & 2 deletions src/coreclr/vm/hash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ const DWORD g_rgPrimes[] = {
};
const SIZE_T g_rgNumPrimes = sizeof(g_rgPrimes) / sizeof(*g_rgPrimes);

const unsigned int SLOTS_PER_BUCKET = 4;

#ifndef DACCESS_COMPILE

void *PtrHashMap::operator new(size_t size, LoaderHeap *pHeap)
Expand Down
14 changes: 12 additions & 2 deletions src/coreclr/vm/hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ typedef ULONG_PTR UPTR;
class Bucket;
class HashMap;

const unsigned int SLOTS_PER_BUCKET = 4;

//-------------------------------------------------------
// class Bucket
// used by hash table implementation
Expand All @@ -54,8 +56,8 @@ typedef DPTR(class Bucket) PTR_Bucket;
class Bucket
{
public:
UPTR m_rgKeys[4];
UPTR m_rgValues[4];
UPTR m_rgKeys[SLOTS_PER_BUCKET];
UPTR m_rgValues[SLOTS_PER_BUCKET];

#define VALUE_MASK (sizeof(LPVOID) == 4 ? 0x7FFFFFFF : I64(0x7FFFFFFFFFFFFFFF))

Expand Down Expand Up @@ -532,6 +534,14 @@ class HashMap

return m_cbInserts-m_cbDeletes;
}

friend struct ::cdac_data<HashMap>;
};

template<>
struct cdac_data<HashMap>
{
static constexpr size_t Buckets = offsetof(HashMap, m_rgBuckets);
};

//---------------------------------------------------------------------------------------
Expand Down
18 changes: 15 additions & 3 deletions src/coreclr/vm/readytoruninfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class ReadyToRunCoreInfo
PTR_PEImageLayout m_pLayout;
PTR_READYTORUN_CORE_HEADER m_pCoreHeader;
Volatile<bool> m_fForbidLoadILBodyFixups;

public:
ReadyToRunCoreInfo();
ReadyToRunCoreInfo(PEImageLayout * pLayout, READYTORUN_CORE_HEADER * pCoreHeader);
Expand Down Expand Up @@ -123,7 +123,7 @@ class ReadyToRunInfo
PTR_READYTORUN_IMPORT_SECTION m_pImportSections;
DWORD m_nImportSections;

bool m_readyToRunCodeDisabled; // Is
bool m_readyToRunCodeDisabled;

NativeFormat::NativeReader m_nativeReader;
NativeFormat::NativeArray m_methodDefEntryPoints;
Expand Down Expand Up @@ -335,8 +335,20 @@ class ReadyToRunInfo

PTR_MethodDesc GetMethodDescForEntryPointInNativeImage(PCODE entryPoint);
void SetMethodDescForEntryPointInNativeImage(PCODE entryPoint, PTR_MethodDesc methodDesc);

PTR_ReadyToRunCoreInfo GetComponentInfo() { return dac_cast<PTR_ReadyToRunCoreInfo>(&m_component); }

friend struct ::cdac_data<ReadyToRunInfo>;
};

template<>
struct cdac_data<ReadyToRunInfo>
{
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);
static constexpr size_t DelayLoadMethodCallThunks = offsetof(ReadyToRunInfo, m_pSectionDelayLoadMethodCallThunks);
static constexpr size_t EntryPointToMethodDescMap = offsetof(ReadyToRunInfo, m_entryPointToMethodDescMap);
};

class DynamicHelpers
Expand Down
Loading

0 comments on commit 22dc50d

Please sign in to comment.