Skip to content
Draft
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
47 changes: 47 additions & 0 deletions docs/design/datacontracts/Loader.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ TargetPointer GetStubHeap(TargetPointer loaderAllocatorPointer);
TargetPointer GetObjectHandle(TargetPointer loaderAllocatorPointer);
TargetPointer GetILHeader(ModuleHandle handle, uint token);
TargetPointer GetDynamicIL(ModuleHandle handle, uint token);
IReadOnlyDictionary<string, TargetPointer> GetLoaderAllocatorHeaps(TargetPointer loaderAllocatorPointer);
```

## Version 1
Expand Down Expand Up @@ -140,7 +141,15 @@ TargetPointer GetDynamicIL(ModuleHandle handle, uint token);
| `LoaderAllocator` | `HighFrequencyHeap` | High-frequency heap of LoaderAllocator |
| `LoaderAllocator` | `LowFrequencyHeap` | Low-frequency heap of LoaderAllocator |
| `LoaderAllocator` | `StubHeap` | Stub heap of LoaderAllocator |
| `LoaderAllocator` | `StaticsHeap` | Statics heap of LoaderAllocator |
| `LoaderAllocator` | `ExecutableHeap` | Executable heap of LoaderAllocator |
| `LoaderAllocator` | `FixupPrecodeHeap` | FixupPrecode heap of LoaderAllocator (optional, present when `HAS_FIXUP_PRECODE`) |
| `LoaderAllocator` | `NewStubPrecodeHeap` | NewStubPrecode heap of LoaderAllocator (optional, present when not `FEATURE_PORTABLE_ENTRYPOINTS`) |
| `LoaderAllocator` | `DynamicHelpersStubHeap` | DynamicHelpers stub heap of LoaderAllocator (optional, present when `FEATURE_READYTORUN && FEATURE_STUBPRECODE_DYNAMIC_HELPERS`) |
| `LoaderAllocator` | `VirtualCallStubManager` | Pointer to the VirtualCallStubManager of LoaderAllocator |
| `LoaderAllocator` | `ObjectHandle` | object handle of LoaderAllocator |
| `VirtualCallStubManager` | `IndcellHeap` | Indirection cell heap of VirtualCallStubManager |
| `VirtualCallStubManager` | `CacheEntryHeap` | Cache entry heap of VirtualCallStubManager (optional, present when `FEATURE_VIRTUAL_STUB_DISPATCH`) |
| `ArrayListBase` | `Count` | Total number of elements in the ArrayListBase |
| `ArrayListBase` | `FirstBlock` | First ArrayListBlock |
| `ArrayListBlock` | `Next` | Next ArrayListBlock in chain |
Expand Down Expand Up @@ -650,6 +659,44 @@ TargetPointer GetObjectHandle(TargetPointer loaderAllocatorPointer)
return target.ReadPointer(loaderAllocatorPointer + /* LoaderAllocator::ObjectHandle offset */);
}

IReadOnlyDictionary<string, TargetPointer> GetLoaderAllocatorHeaps(TargetPointer loaderAllocatorPointer)
{
// Read LoaderAllocator data
LoaderAllocator la = // read LoaderAllocator object at loaderAllocatorPointer

// Always-present heaps
Dictionary<string, TargetPointer> heaps = {
["LowFrequencyHeap"] = la.LowFrequencyHeap,
["HighFrequencyHeap"] = la.HighFrequencyHeap,
["StaticsHeap"] = la.StaticsHeap,
["StubHeap"] = la.StubHeap,
["ExecutableHeap"] = la.ExecutableHeap,
};

// Feature-conditional heaps: only included when the data descriptor field exists
if (LoaderAllocator type has "FixupPrecodeHeap" field)
heaps["FixupPrecodeHeap"] = la.FixupPrecodeHeap;

if (LoaderAllocator type has "NewStubPrecodeHeap" field)
heaps["NewStubPrecodeHeap"] = la.NewStubPrecodeHeap;

if (LoaderAllocator type has "DynamicHelpersStubHeap" field)
heaps["DynamicHelpersStubHeap"] = la.DynamicHelpersStubHeap;

// VirtualCallStubManager heaps: only included when VirtualCallStubManager is non-null
if (la.VirtualCallStubManager != null)
{
VirtualCallStubManager vcsMgr = // read VirtualCallStubManager object at la.VirtualCallStubManager

heaps["IndcellHeap"] = vcsMgr.IndcellHeap;

if (VirtualCallStubManager type has "CacheEntryHeap" field)
heaps["CacheEntryHeap"] = vcsMgr.CacheEntryHeap;
}

return heaps;
}

private sealed class DynamicILBlobTraits : ITraits<uint, DynamicILBlobEntry>
{
public uint GetKey(DynamicILBlobEntry entry) => entry.EntryMethodToken;
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/datadescriptor/datadescriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include "configure.h"

#include "virtualcallstub.h"
#include "../debug/ee/debugger.h"
#include "patchpointinfo.h"

Expand Down
20 changes: 20 additions & 0 deletions src/coreclr/vm/datadescriptor/datadescriptor.inc
Original file line number Diff line number Diff line change
Expand Up @@ -225,10 +225,30 @@ CDAC_TYPE_INDETERMINATE(LoaderAllocator)
CDAC_TYPE_FIELD(LoaderAllocator, /*uint32*/, ReferenceCount, cdac_data<LoaderAllocator>::ReferenceCount)
CDAC_TYPE_FIELD(LoaderAllocator, /*pointer*/, HighFrequencyHeap, cdac_data<LoaderAllocator>::HighFrequencyHeap)
CDAC_TYPE_FIELD(LoaderAllocator, /*pointer*/, LowFrequencyHeap, cdac_data<LoaderAllocator>::LowFrequencyHeap)
CDAC_TYPE_FIELD(LoaderAllocator, /*pointer*/, StaticsHeap, cdac_data<LoaderAllocator>::StaticsHeap)
CDAC_TYPE_FIELD(LoaderAllocator, /*pointer*/, StubHeap, cdac_data<LoaderAllocator>::StubHeap)
CDAC_TYPE_FIELD(LoaderAllocator, /*pointer*/, ExecutableHeap, cdac_data<LoaderAllocator>::ExecutableHeap)
#ifdef HAS_FIXUP_PRECODE
CDAC_TYPE_FIELD(LoaderAllocator, /*pointer*/, FixupPrecodeHeap, cdac_data<LoaderAllocator>::FixupPrecodeHeap)
#endif // HAS_FIXUP_PRECODE
#ifndef FEATURE_PORTABLE_ENTRYPOINTS
CDAC_TYPE_FIELD(LoaderAllocator, /*pointer*/, NewStubPrecodeHeap, cdac_data<LoaderAllocator>::NewStubPrecodeHeap)
#endif // !FEATURE_PORTABLE_ENTRYPOINTS
#if defined(FEATURE_READYTORUN) && defined(FEATURE_STUBPRECODE_DYNAMIC_HELPERS)
CDAC_TYPE_FIELD(LoaderAllocator, /*pointer*/, DynamicHelpersStubHeap, cdac_data<LoaderAllocator>::DynamicHelpersStubHeap)
#endif // defined(FEATURE_READYTORUN) && defined(FEATURE_STUBPRECODE_DYNAMIC_HELPERS)
CDAC_TYPE_FIELD(LoaderAllocator, /*pointer*/, VirtualCallStubManager, cdac_data<LoaderAllocator>::VirtualCallStubManager)
CDAC_TYPE_FIELD(LoaderAllocator, /*pointer*/, ObjectHandle, cdac_data<LoaderAllocator>::ObjectHandle)
CDAC_TYPE_END(LoaderAllocator)

CDAC_TYPE_BEGIN(VirtualCallStubManager)
CDAC_TYPE_INDETERMINATE(VirtualCallStubManager)
CDAC_TYPE_FIELD(VirtualCallStubManager, /*pointer*/, IndcellHeap, cdac_data<VirtualCallStubManager>::IndcellHeap)
#ifdef FEATURE_VIRTUAL_STUB_DISPATCH
CDAC_TYPE_FIELD(VirtualCallStubManager, /*pointer*/, CacheEntryHeap, cdac_data<VirtualCallStubManager>::CacheEntryHeap)
#endif // FEATURE_VIRTUAL_STUB_DISPATCH
CDAC_TYPE_END(VirtualCallStubManager)

CDAC_TYPE_BEGIN(PEAssembly)
CDAC_TYPE_INDETERMINATE(PEAssembly)
CDAC_TYPE_FIELD(PEAssembly, /*pointer*/, PEImage, cdac_data<PEAssembly>::PEImage)
Expand Down
12 changes: 12 additions & 0 deletions src/coreclr/vm/loaderallocator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -917,7 +917,19 @@ struct cdac_data<LoaderAllocator>
static constexpr size_t ReferenceCount = offsetof(LoaderAllocator, m_cReferences);
static constexpr size_t HighFrequencyHeap = offsetof(LoaderAllocator, m_pHighFrequencyHeap);
static constexpr size_t LowFrequencyHeap = offsetof(LoaderAllocator, m_pLowFrequencyHeap);
static constexpr size_t StaticsHeap = offsetof(LoaderAllocator, m_pStaticsHeap);
static constexpr size_t StubHeap = offsetof(LoaderAllocator, m_pStubHeap);
static constexpr size_t ExecutableHeap = offsetof(LoaderAllocator, m_pExecutableHeap);
#ifdef HAS_FIXUP_PRECODE
static constexpr size_t FixupPrecodeHeap = offsetof(LoaderAllocator, m_pFixupPrecodeHeap);
#endif // HAS_FIXUP_PRECODE
#ifndef FEATURE_PORTABLE_ENTRYPOINTS
static constexpr size_t NewStubPrecodeHeap = offsetof(LoaderAllocator, m_pNewStubPrecodeHeap);
#endif // !FEATURE_PORTABLE_ENTRYPOINTS
#if defined(FEATURE_READYTORUN) && defined(FEATURE_STUBPRECODE_DYNAMIC_HELPERS)
static constexpr size_t DynamicHelpersStubHeap = offsetof(LoaderAllocator, m_pDynamicHelpersStubHeap);
#endif // defined(FEATURE_READYTORUN) && defined(FEATURE_STUBPRECODE_DYNAMIC_HELPERS)
static constexpr size_t VirtualCallStubManager = offsetof(LoaderAllocator, m_pVirtualCallStubManager);
static constexpr size_t ObjectHandle = offsetof(LoaderAllocator, m_hLoaderAllocatorObjectHandle);
};

Expand Down
11 changes: 11 additions & 0 deletions src/coreclr/vm/virtualcallstub.h
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,17 @@ class VirtualCallStubManager : public StubManager
return W("Unexpected. RangeSectionStubManager should report the name");
}
#endif

friend struct ::cdac_data<VirtualCallStubManager>;
};

template<>
struct cdac_data<VirtualCallStubManager>
{
static constexpr size_t IndcellHeap = offsetof(VirtualCallStubManager, indcell_heap);
#ifdef FEATURE_VIRTUAL_STUB_DISPATCH
static constexpr size_t CacheEntryHeap = offsetof(VirtualCallStubManager, cache_entry_heap);
#endif // FEATURE_VIRTUAL_STUB_DISPATCH
};

/********************************************************************************************************
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ public interface ILoader : IContract
TargetPointer GetILHeader(ModuleHandle handle, uint token) => throw new NotImplementedException();
TargetPointer GetObjectHandle(TargetPointer loaderAllocatorPointer) => throw new NotImplementedException();
TargetPointer GetDynamicIL(ModuleHandle handle, uint token) => throw new NotImplementedException();
IReadOnlyDictionary<string, TargetPointer> GetLoaderAllocatorHeaps(TargetPointer loaderAllocatorPointer) => throw new NotImplementedException();
Copy link
Member

Choose a reason for hiding this comment

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

I don't see any docs for this in the contract doc. I assume we need to add it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added documentation in ab7f065. The GetLoaderAllocatorHeaps API, new LoaderAllocator heap data descriptor fields, and the VirtualCallStubManager type are now documented in docs/design/datacontracts/Loader.md with pseudocode showing the feature-conditional dictionary building logic.

}

public readonly struct Loader : ILoader
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ public enum DataType
DynamicILBlobTable,
EEJitManager,
PatchpointInfo,
VirtualCallStubManager,
PortableEntryPoint,

TransitionBlock,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -552,4 +552,41 @@ TargetPointer ILoader.GetDynamicIL(ModuleHandle handle, uint token)
ISHash shashContract = _target.Contracts.SHash;
return shashContract.LookupSHash(dynamicILBlobTable.HashTable, token).EntryIL;
}

IReadOnlyDictionary<string, TargetPointer> ILoader.GetLoaderAllocatorHeaps(TargetPointer loaderAllocatorPointer)
{
Data.LoaderAllocator loaderAllocator = _target.ProcessedData.GetOrAdd<Data.LoaderAllocator>(loaderAllocatorPointer);
Target.TypeInfo laType = _target.GetTypeInfo(DataType.LoaderAllocator);

Dictionary<string, TargetPointer> heaps = new()
{
[nameof(Data.LoaderAllocator.LowFrequencyHeap)] = loaderAllocator.LowFrequencyHeap,
[nameof(Data.LoaderAllocator.HighFrequencyHeap)] = loaderAllocator.HighFrequencyHeap,
[nameof(Data.LoaderAllocator.StaticsHeap)] = loaderAllocator.StaticsHeap,
[nameof(Data.LoaderAllocator.StubHeap)] = loaderAllocator.StubHeap,
[nameof(Data.LoaderAllocator.ExecutableHeap)] = loaderAllocator.ExecutableHeap,
};

if (laType.Fields.ContainsKey(nameof(Data.LoaderAllocator.FixupPrecodeHeap)))
heaps[nameof(Data.LoaderAllocator.FixupPrecodeHeap)] = loaderAllocator.FixupPrecodeHeap!.Value;

if (laType.Fields.ContainsKey(nameof(Data.LoaderAllocator.NewStubPrecodeHeap)))
heaps[nameof(Data.LoaderAllocator.NewStubPrecodeHeap)] = loaderAllocator.NewStubPrecodeHeap!.Value;

if (laType.Fields.ContainsKey(nameof(Data.LoaderAllocator.DynamicHelpersStubHeap)))
heaps[nameof(Data.LoaderAllocator.DynamicHelpersStubHeap)] = loaderAllocator.DynamicHelpersStubHeap!.Value;

if (loaderAllocator.VirtualCallStubManager != TargetPointer.Null)
{
Data.VirtualCallStubManager vcsMgr = _target.ProcessedData.GetOrAdd<Data.VirtualCallStubManager>(loaderAllocator.VirtualCallStubManager);
Target.TypeInfo vcsType = _target.GetTypeInfo(DataType.VirtualCallStubManager);

heaps[nameof(Data.VirtualCallStubManager.IndcellHeap)] = vcsMgr.IndcellHeap;

if (vcsType.Fields.ContainsKey(nameof(Data.VirtualCallStubManager.CacheEntryHeap)))
heaps[nameof(Data.VirtualCallStubManager.CacheEntryHeap)] = vcsMgr.CacheEntryHeap!.Value;
}

return heaps;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,33 @@ public LoaderAllocator(Target target, TargetPointer address)
ReferenceCount = target.Read<uint>(address + (ulong)type.Fields[nameof(ReferenceCount)].Offset);
HighFrequencyHeap = target.ReadPointer(address + (ulong)type.Fields[nameof(HighFrequencyHeap)].Offset);
LowFrequencyHeap = target.ReadPointer(address + (ulong)type.Fields[nameof(LowFrequencyHeap)].Offset);
StaticsHeap = target.ReadPointer(address + (ulong)type.Fields[nameof(StaticsHeap)].Offset);
StubHeap = target.ReadPointer(address + (ulong)type.Fields[nameof(StubHeap)].Offset);
ExecutableHeap = target.ReadPointer(address + (ulong)type.Fields[nameof(ExecutableHeap)].Offset);

if (type.Fields.ContainsKey(nameof(FixupPrecodeHeap)))
FixupPrecodeHeap = target.ReadPointer(address + (ulong)type.Fields[nameof(FixupPrecodeHeap)].Offset);
if (type.Fields.ContainsKey(nameof(NewStubPrecodeHeap)))
NewStubPrecodeHeap = target.ReadPointer(address + (ulong)type.Fields[nameof(NewStubPrecodeHeap)].Offset);
if (type.Fields.ContainsKey(nameof(DynamicHelpersStubHeap)))
DynamicHelpersStubHeap = target.ReadPointer(address + (ulong)type.Fields[nameof(DynamicHelpersStubHeap)].Offset);

VirtualCallStubManager = target.ReadPointer(address + (ulong)type.Fields[nameof(VirtualCallStubManager)].Offset);

ObjectHandle = target.ProcessedData.GetOrAdd<ObjectHandle>(
target.ReadPointer(address + (ulong)type.Fields[nameof(ObjectHandle)].Offset));
}

public uint ReferenceCount { get; init; }
public TargetPointer HighFrequencyHeap { get; init; }
public TargetPointer LowFrequencyHeap { get; init; }
public TargetPointer StaticsHeap { get; init; }
public TargetPointer StubHeap { get; init; }
public TargetPointer ExecutableHeap { get; init; }
public TargetPointer? FixupPrecodeHeap { get; init; }
public TargetPointer? NewStubPrecodeHeap { get; init; }
public TargetPointer? DynamicHelpersStubHeap { get; init; }
public TargetPointer VirtualCallStubManager { get; init; }
public ObjectHandle ObjectHandle { get; init; }

public bool IsAlive => ReferenceCount != 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// 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.Data;

internal sealed class VirtualCallStubManager : IData<VirtualCallStubManager>
{
static VirtualCallStubManager IData<VirtualCallStubManager>.Create(Target target, TargetPointer address)
=> new VirtualCallStubManager(target, address);

public VirtualCallStubManager(Target target, TargetPointer address)
{
Target.TypeInfo type = target.GetTypeInfo(DataType.VirtualCallStubManager);

IndcellHeap = target.ReadPointer(address + (ulong)type.Fields[nameof(IndcellHeap)].Offset);

if (type.Fields.ContainsKey(nameof(CacheEntryHeap)))
CacheEntryHeap = target.ReadPointer(address + (ulong)type.Fields[nameof(CacheEntryHeap)].Offset);
}

public TargetPointer IndcellHeap { get; init; }
public TargetPointer? CacheEntryHeap { get; init; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -4459,10 +4459,109 @@ int ISOSDacInterface13.GetDomainLoaderAllocator(ClrDataAddress domainAddress, Cl
#endif
return hr;
}

private nint[]? _loaderAllocatorHeapNamePtrs;

private nint[] GetOrInitializeHeapNamePtrs()
{
if (_loaderAllocatorHeapNamePtrs is not null)
return _loaderAllocatorHeapNamePtrs;

Contracts.ILoader contract = _target.Contracts.Loader;
TargetPointer globalLoaderAllocator = contract.GetGlobalLoaderAllocator();
IReadOnlyDictionary<string, TargetPointer> heaps = contract.GetLoaderAllocatorHeaps(globalLoaderAllocator);
nint[] ptrs = new nint[heaps.Count];
int i = 0;
foreach (string name in heaps.Keys)
{
ptrs[i++] = Marshal.StringToHGlobalAnsi(name);
}
_loaderAllocatorHeapNamePtrs = ptrs;
return ptrs;
}

int ISOSDacInterface13.GetLoaderAllocatorHeapNames(int count, char** ppNames, int* pNeeded)
=> _legacyImpl13 is not null ? _legacyImpl13.GetLoaderAllocatorHeapNames(count, ppNames, pNeeded) : HResults.E_NOTIMPL;
{
int hr = HResults.S_OK;
try
{
nint[] heapNamePtrs = GetOrInitializeHeapNamePtrs();
int loaderHeapCount = heapNamePtrs.Length;
if (pNeeded != null)
*pNeeded = loaderHeapCount;

if (ppNames != null)
{
for (int i = 0; i < Math.Min(count, loaderHeapCount); i++)
ppNames[i] = (char*)heapNamePtrs[i];
}

if (count < loaderHeapCount)
hr = HResults.S_FALSE;
}
catch (System.Exception ex)
{
hr = ex.HResult;
}

#if DEBUG
if (_legacyImpl13 is not null)
{
int pNeededLocal;
_legacyImpl13.GetLoaderAllocatorHeapNames(0, null, &pNeededLocal);
Debug.Assert(pNeeded is null || *pNeeded == pNeededLocal, $"cDAC needed: {(pNeeded != null ? *pNeeded : -1)}, DAC needed: {pNeededLocal}");
}
#endif
return hr;
}
int ISOSDacInterface13.GetLoaderAllocatorHeaps(ClrDataAddress loaderAllocator, int count, ClrDataAddress* pLoaderHeaps, /*LoaderHeapKind*/ int* pKinds, int* pNeeded)
=> _legacyImpl13 is not null ? _legacyImpl13.GetLoaderAllocatorHeaps(loaderAllocator, count, pLoaderHeaps, pKinds, pNeeded) : HResults.E_NOTIMPL;
{
if (loaderAllocator == 0)
return HResults.E_INVALIDARG;

int hr = HResults.S_OK;
try
{
Contracts.ILoader contract = _target.Contracts.Loader;
IReadOnlyDictionary<string, TargetPointer> heaps = contract.GetLoaderAllocatorHeaps(loaderAllocator.ToTargetPointer(_target));
int loaderHeapCount = heaps.Count;

if (pNeeded != null)
*pNeeded = loaderHeapCount;

if (pLoaderHeaps != null)
{
if (count < loaderHeapCount)
{
hr = HResults.E_INVALIDARG;
}
else
{
int i = 0;
foreach (TargetPointer heap in heaps.Values)
{
pLoaderHeaps[i] = heap.ToClrDataAddress(_target);
pKinds[i] = 0; // LoaderHeapKindNormal
i++;
}
}
}
}
catch (System.Exception ex)
{
hr = ex.HResult;
}

#if DEBUG
if (_legacyImpl13 is not null)
{
int pNeededLocal;
int hrLocal = _legacyImpl13.GetLoaderAllocatorHeaps(loaderAllocator, 0, null, null, &pNeededLocal);
Debug.Assert(pNeeded is null || *pNeeded == pNeededLocal, $"cDAC needed: {(pNeeded != null ? *pNeeded : -1)}, DAC needed: {pNeededLocal}");
}
#endif
return hr;
}
int ISOSDacInterface13.GetHandleTableMemoryRegions(/*ISOSMemoryEnum*/ void** ppEnum)
=> _legacyImpl13 is not null ? _legacyImpl13.GetHandleTableMemoryRegions(ppEnum) : HResults.E_NOTIMPL;
int ISOSDacInterface13.GetGCBookkeepingMemoryRegions(/*ISOSMemoryEnum*/ void** ppEnum)
Expand Down
Loading