Skip to content
Merged
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
4 changes: 2 additions & 2 deletions docs/design/datacontracts/ExecutionManager.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ virtual-IP ranges bypass native thunk classification.
| `ReadyToRunInfo` | `DelayLoadMethodCallThunks` | `pointer` | Pointer to an `ImageDataDirectory` for the delay load method call thunks; absent on WebAssembly |
| `ReadyToRunInfo` | `EntryPointToMethodDescMap` | `HashMap` | `HashMap` of entry point addresses to `MethodDesc` pointers |
| `ReadyToRunInfo` | `HotColdMap` | `pointer` | Pointer to an array of 32-bit integers; present only with `FEATURE_COLD_R2R_CODE` - [see R2R format](../coreclr/botr/readytorun-format.md#readytorunsectiontypehotcoldmap-v80) |
| `ReadyToRunInfo` | `LoadedImageBase` | `pointer` | Base address of the loaded R2R image |
Comment thread
lewing marked this conversation as resolved.
Resolved
| `ReadyToRunInfo` | `LoadedImageBase` | `pointer` | Base address of the loaded R2R image; unset for component assemblies of a composite image, so it is read from the `CompositeInfo` |
| `ReadyToRunInfo` | `MinVirtualIP` | `pointer` | Base virtual IP assigned to the ReadyToRun module on WebAssembly |
| `ReadyToRunInfo` | `NumHotColdMap` | `uint32` | Number of entries in the `HotColdMap`; absent without `FEATURE_COLD_R2R_CODE`, meaning no hot/cold mapping |
| `ReadyToRunInfo` | `NumRuntimeFunctions` | `uint32` | Number of `RuntimeFunctions` |
Expand Down Expand Up @@ -398,7 +398,7 @@ bool GetMethodInfo(TargetPointer rangeSection, TargetCodePointer jittedCodeAddre
// Find the relative address that we are looking for
TargetCodePointer addr = /* code pointer from jittedCodeAddress using PlatformMetadata.GetCodePointerFlags */
TargetPointer codeBase = /* RangeSection.RangeBegin, or ReadyToRunInfo.MinVirtualIP on WebAssembly */;
TargetPointer loadedImageBase = /* ReadyToRunInfo.LoadedImageBase on WebAssembly, otherwise codeBase */;
TargetPointer loadedImageBase = /* LoadedImageBase of r2rInfo.CompositeInfo on WebAssembly, otherwise codeBase */;
TargetPointer relativeAddr = addr - codeBase;

TargetPointer runtimeFunctions = Target.ReadPointer(r2rInfo + /* ReadyToRunInfo::RuntimeFunctions offset */);
Expand Down
5 changes: 3 additions & 2 deletions docs/design/datacontracts/StackWalk.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,9 @@ On ARM64, the `pac` unwind opcode strips pointer authentication bits from `Lr` u
| `Object` | `m_pMethTab` | `pointer` | Method table for the object |
| `PInvokeCalliFrame` | `VASigCookiePtr` | `pointer` | Pointer to the varargs signature cookie for the unmanaged call |
| `PortableEntryPoint` | `ActualCode` | `pointer` | Native code for the entrypoint; on WebAssembly R2R code this is a function-table index (only defined if `FeaturePortableEntrypoints` is enabled) |
| `ReadyToRunInfo` | `CompositeInfo` | `pointer` | Pointer to composite R2R info - or itself for non-composite |
| `ReadyToRunInfo` | `ImportSections` | `pointer` | Pointer to the array of ReadyToRun import sections |
| `ReadyToRunInfo` | `LoadedImageBase` | `pointer` | Base address of the loaded R2R image |
| `ReadyToRunInfo` | `LoadedImageBase` | `pointer` | Base address of the loaded R2R image; unset for component assemblies of a composite image, so it is read from the `CompositeInfo` |
| `ReadyToRunInfo` | `MinVirtualIP` | `pointer` | Base virtual IP assigned to the ReadyToRun module on WebAssembly |
| `ReadyToRunInfo` | `NumImportSections` | `uint32` | Number of ReadyToRun import sections |
| `ReadyToRunInfo` | `NumRuntimeFunctions` | `uint32` | Number of `RuntimeFunctions` |
Expand Down Expand Up @@ -873,7 +874,7 @@ GCRefMap resolution from a frame's `Indirection` pointer proceeds as follows:

1. Call `FindReadyToRunModule(indirection)` (see [ExecutionManager contract](./ExecutionManager.md)) to find the ReadyToRun module containing the import slot.
2. Load the module's `ReadyToRunInfo` to access the import section array.
3. Compute the RVA of the indirection address: `rva = indirection - imageBase`.
3. Compute the RVA of the indirection address: `rva = indirection - imageBase`, where `imageBase` is the `LoadedImageBase` of the `ReadyToRunInfo`'s `CompositeInfo`.
4. Search through `READYTORUN_IMPORT_SECTION` entries to find the section containing the RVA.
5. Compute the slot index within the section: `index = (rva - sectionVA) / entrySize`.
6. If the section has the `GCRefMap` flag (`0x0008`), read the DWORD at `imageBase + Signatures + index * 4`. This RVA points directly to the GCRefMap prefix of the import signature.
Expand Down
2 changes: 1 addition & 1 deletion docs/design/datacontracts/data-descriptor-meanings.json
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@
"ReadyToRunInfo.EntryPointToMethodDescMap": "`HashMap` of entry point addresses to `MethodDesc` pointers",
"ReadyToRunInfo.HotColdMap": "Pointer to an array of 32-bit integers; present only with `FEATURE_COLD_R2R_CODE` - [see R2R format](../coreclr/botr/readytorun-format.md#readytorunsectiontypehotcoldmap-v80)",
"ReadyToRunInfo.ImportSections": "Pointer to the array of ReadyToRun import sections",
"ReadyToRunInfo.LoadedImageBase": "Base address of the loaded R2R image",
"ReadyToRunInfo.LoadedImageBase": "Base address of the loaded R2R image; unset for component assemblies of a composite image, so it is read from the `CompositeInfo`",
"ReadyToRunInfo.MinVirtualIP": "Base virtual IP assigned to the ReadyToRun module on WebAssembly",
"ReadyToRunInfo.NumHotColdMap": "Number of entries in the `HotColdMap`; absent without `FEATURE_COLD_R2R_CODE`, meaning no hot/cold mapping",
"ReadyToRunInfo.NumImportSections": "Number of ReadyToRun import sections",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ internal sealed partial class ReadyToRunInfo : IData<ReadyToRunInfo>
[Field] public partial TargetPointer? DelayLoadMethodCallThunks { get; }
[Field] public partial TargetPointer DebugInfoSection { get; }
[Field] public partial TargetPointer ExceptionInfoSection { get; }
[Field] public partial TargetPointer LoadedImageBase { get; }
[Field] public partial TargetPointer Composite { get; }
[Field] public partial uint NumImportSections { get; }

Expand All @@ -27,6 +26,7 @@ internal sealed partial class ReadyToRunInfo : IData<ReadyToRunInfo>
[CustomInit(nameof(InitHotColdMap))] public partial TargetPointer HotColdMap { get; }
[CustomInit(nameof(InitImportSections))] public partial TargetPointer ImportSections { get; }
[CustomInit(nameof(InitEntryPointToMethodDescMap))] public partial TargetPointer EntryPointToMethodDescMap { get; }
[CustomInit(nameof(InitLoadedImageBase))] public partial TargetPointer LoadedImageBase { get; }

[DataDescriptorDependency(nameof(NumRuntimeFunctions), "uint32")]
[DataDescriptorDependency(nameof(RuntimeFunctions), "pointer")]
Expand Down Expand Up @@ -68,4 +68,14 @@ private partial TargetPointer InitEntryPointToMethodDescMap(Target target, Targe
// Map is from the composite info pointer (set to itself for non-multi-assembly composite images)
return CompositeInfo + (ulong)type.Fields[nameof(EntryPointToMethodDescMap)].Offset;
}

[DataDescriptorDependency(nameof(CompositeInfo), "pointer")]
[DataDescriptorDependency(nameof(LoadedImageBase), "pointer")]
private partial TargetPointer InitLoadedImageBase(Target target, TargetPointer address)
{
// Component assemblies of a multi-assembly composite image do not record the image base;
// read it from the composite info (set to itself for non-component images).
Target.TypeInfo type = target.GetTypeInfo(DataType.ReadyToRunInfo);
return target.ReadPointerField(CompositeInfo, type, nameof(LoadedImageBase));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -441,8 +441,10 @@ int ReadWithReadableZeroPage(ulong address, Span<byte> buffer)
}
}

[Fact]
public void GetMethodDesc_R2R_WasmVirtualIPRangeList_ResolvesCapturedShape()
[Theory]
[InlineData(false)]
[InlineData(true)]
public void GetMethodDesc_R2R_WasmVirtualIPRangeList_ResolvesCapturedShape(bool compositeComponent)
{
MockTarget.Architecture wasmArch = new() { IsLittleEndian = true, Is64Bit = false };

Expand All @@ -466,7 +468,16 @@ public void GetMethodDesc_R2R_WasmVirtualIPRangeList_ResolvesCapturedShape()
emBuilder.AllocateJittedCodeRange(VirtualIPRangeStart, VirtualIPRangeSize);
MockReadyToRunInfo r2rInfo = emBuilder.AddReadyToRunInfo([0x80, FunctionBeginAddress, 0x200], []);
r2rInfo.MinVirtualIP = VirtualIPRangeStart;
r2rInfo.LoadedImageBase = LoadedImageBase;

// A component assembly of a composite image leaves LoadedImageBase unset; the image base
// and entry point map belong to the composite image's ReadyToRunInfo.
MockReadyToRunInfo compositeInfo = r2rInfo;
if (compositeComponent)
{
compositeInfo = emBuilder.AddReadyToRunInfo([0x80, FunctionBeginAddress, 0x200], []);
r2rInfo.CompositeInfo = compositeInfo.Address;
}
compositeInfo.LoadedImageBase = LoadedImageBase;
runtimeFunctionsAddress = r2rInfo.RuntimeFunctions;
runtimeFunctionSize = emBuilder.RuntimeFunctionLayout.Size;
Assert.Equal(8, runtimeFunctionSize);
Expand All @@ -476,7 +487,7 @@ public void GetMethodDesc_R2R_WasmVirtualIPRangeList_ResolvesCapturedShape()

MockHashMapBuilder hashMapBuilder = new(emBuilder.Builder);
hashMapBuilder.PopulatePtrMap(
r2rInfo.EntryPointToMethodDescMapAddress,
compositeInfo.EntryPointToMethodDescMapAddress,
[(CapturedVirtualIP, ExpectedMethodDescAddress)]);

MockLoaderModule r2rModule = emBuilder.AddReadyToRunModule(r2rInfo.Address);
Expand Down
28 changes: 20 additions & 8 deletions src/native/managed/cdac/tests/UnitTests/StackWalkTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@ public unsafe class StackWalkTests
private const uint X86TransitionBlockSize = 7 * sizeof(uint);

[Theory]
[InlineData(false)]
[InlineData(true)]
public void FindGCRefMap_ResolvesImportRecord(bool inlineGCRefMap)
[InlineData(false, false)]
[InlineData(true, false)]
[InlineData(true, true)]
public void FindGCRefMap_ResolvesImportRecord(bool inlineGCRefMap, bool compositeComponent)
{
(TestPlaceholderTarget target, _) = CreateX86TransitionFrameTarget(
nameof(Data.ExternalMethodFrame), gcRefMap: [0x83, 0x05], hasMethodDesc: false,
inlineGCRefMap: inlineGCRefMap);
inlineGCRefMap: inlineGCRefMap, compositeComponent: compositeComponent);

TargetPointer map = new FrameHelpers(target).FindGCRefMap(new TargetPointer(0x100108));

Expand Down Expand Up @@ -1208,7 +1209,8 @@ private static (TestPlaceholderTarget Target, ContextHolder<X86Context> Context)
string frameType, byte[]? gcRefMap, bool hasMethodDesc, byte[]? signatureBlob = null,
uint? representativeMethodDescAddress = null, bool hasUnresolvedIndirection = false,
RuntimeInfoArchitecture architecture = RuntimeInfoArchitecture.X86, bool hasCodeData = true,
bool hasGCRefMapField = true, bool? valueType = null, bool unboxing = false, bool inlineGCRefMap = true)
bool hasGCRefMapField = true, bool? valueType = null, bool unboxing = false, bool inlineGCRefMap = true,
bool compositeComponent = false)
{
MockTarget.Architecture arch = new() { IsLittleEndian = true, Is64Bit = false };
TestPlaceholderTarget.Builder builder = new(arch);
Expand Down Expand Up @@ -1260,11 +1262,20 @@ private static (TestPlaceholderTarget Target, ContextHolder<X86Context> Context)
byte[] module = new byte[4];
helpers.Write(module, 0x4000u);
builder.MemoryBuilder.AddHeapFragment(new() { Address = 0x3000, Data = module, Name = "Module" });
byte[] r2rInfo = new byte[12];
helpers.Write(r2rInfo.AsSpan(0), ImageBase);
byte[] r2rInfo = new byte[16];
// A component assembly of a composite image leaves LoadedImageBase unset; the composite's info holds it.
helpers.Write(r2rInfo.AsSpan(0), compositeComponent ? 0u : ImageBase);
helpers.Write(r2rInfo.AsSpan(4), 1u);
helpers.Write(r2rInfo.AsSpan(8), 0x5000u);
helpers.Write(r2rInfo.AsSpan(12), compositeComponent ? 0x4100u : 0x4000u);
builder.MemoryBuilder.AddHeapFragment(new() { Address = 0x4000, Data = r2rInfo, Name = "ReadyToRun info" });
if (compositeComponent)
{
byte[] compositeInfo = new byte[16];
helpers.Write(compositeInfo.AsSpan(0), ImageBase);
helpers.Write(compositeInfo.AsSpan(12), 0x4100u);
builder.MemoryBuilder.AddHeapFragment(new() { Address = 0x4100, Data = compositeInfo, Name = "Composite ReadyToRun info" });
}
byte[] importSection = new byte[20];
helpers.Write(importSection.AsSpan(0), ImportSectionRva);
helpers.Write(importSection.AsSpan(4), 3u * sizeof(uint));
Expand Down Expand Up @@ -1340,7 +1351,8 @@ private static (TestPlaceholderTarget Target, ContextHolder<X86Context> Context)
(nameof(X86Context.Ebp), 0), (nameof(X86Context.Ebx), 4), (nameof(X86Context.Edi), 8), (nameof(X86Context.Esi), 12)),
[DataType.Module] = CreateTypeInfo((nameof(Data.Module.ReadyToRunInfo), 0)),
[DataType.ReadyToRunInfo] = CreateTypeInfo(
(nameof(Data.ReadyToRunInfo.LoadedImageBase), 0), (nameof(Data.ReadyToRunInfo.NumImportSections), 4), (nameof(Data.ReadyToRunInfo.ImportSections), 8)),
(nameof(Data.ReadyToRunInfo.LoadedImageBase), 0), (nameof(Data.ReadyToRunInfo.NumImportSections), 4), (nameof(Data.ReadyToRunInfo.ImportSections), 8),
(nameof(Data.ReadyToRunInfo.CompositeInfo), 12)),
};
if (methodDescMap)
{
Expand Down
Loading