Skip to content

Commit

Permalink
[cdac] Start Loader contract and implement ISOSDacInterface::GetModul…
Browse files Browse the repository at this point in the history
…eData in cDAC (#104257)

- Start a `Loader` contract - currently contains what is needed for GetModuleData
- Implement `ISOSDacInterface::GetModuleData` in cDAC
- Store base address and is reflection emit bit on `Module` for easier diagnostics access
elinor-fung authored Jul 9, 2024
1 parent eae1542 commit 04a40c1
Showing 16 changed files with 523 additions and 15 deletions.
2 changes: 1 addition & 1 deletion docs/design/datacontracts/Exception.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Contract Thread
# Contract Exception

This contract is for getting information about exceptions in the process.

107 changes: 107 additions & 0 deletions docs/design/datacontracts/Loader.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Contract Loader

This contract is for getting information about loaded modules and assemblies

## APIs of contract

``` csharp
readonly struct ModuleHandle
{
// Opaque handle - no public members
internal TargetPointer Address;
}

[Flags]
enum ModuleFlags
{
EditAndContinue = 0x00000008, // Edit and Continue is enabled for this module
ReflectionEmit = 0x00000040, // Reflection.Emit was used to create this module
}

record struct ModuleLookupTables(
TargetPointer FieldDefToDesc,
TargetPointer ManifestModuleReferences,
TargetPointer MemberRefToDesc,
TargetPointer MethodDefToDesc,
TargetPointer TypeDefToMethodTable,
TargetPointer TypeRefToMethodTable);
```

``` csharp
ModuleHandle GetModuleHandle(TargetPointer);
TargetPointer GetAssembly(ModuleHandle handle);
ModuleFlags GetFlags(ModuleHandle handle);
TargetPointer GetLoaderAllocator(ModuleHandle handle);
TargetPointer GetThunkHeap(ModuleHandle handle);
TargetPointer GetILBase(ModuleHandle handle);
TargetPointer GetMetadataAddress(ModuleHandle handle, out ulong size);
ModuleLookupTables GetLookupTables(ModuleHandle handle);
```

## Version 1

Data descriptors used:
- `Module`

``` csharp
ModuleHandle GetModuleHandle(TargetPointer modulePointer)
{
return new ModuleHandle(modulePointer);
}

TargetPointer GetAssembly(ModuleHandle handle)
{
return target.ReadPointer(handle.Address + /* Module::Assrembly offset */);
}

ModuleFlags GetFlags(ModuleHandle handle)
{
return target.Read<uint>(handle.Address + /* Module::Flags offset */);
}

TargetPointer GetLoaderAllocator(ModuleHandle handle)
{
return target.ReadPointer(handle.Address + /* Module::LoaderAllocator offset */);
}

TargetPointer GetThunkHeap(ModuleHandle handle)
{
return target.ReadPointer(handle.Address + /* Module::ThunkHeap offset */);
}

TargetPointer GetILBase(ModuleHandle handle)
{
return target.ReadPointer(handle.Address + /* Module::Base offset */);
}

TargetPointer GetMetadataAddress(ModuleHandle handle, out ulong size)
{
TargetPointer baseAddress = GetILBase(handle);
if (baseAddress == TargetPointer.Null)
{
size = 0;
return TargetPointer.Null;
}

// Read CLR header per https://learn.microsoft.com/windows/win32/debug/pe-format
ulong clrHeaderRVA = ...

// Read Metadata per ECMA-335 II.25.3.3 CLI Header
ulong metadataDirectoryAddress = baseAddress + clrHeaderRva + /* offset to Metadata */
int rva = target.Read<int>(metadataDirectoryAddress);
size = target.Read<int>(metadataDirectoryAddress + sizeof(int));
return baseAddress + rva;
}

ModuleLookupTables GetLookupTables(ModuleHandle handle)
{
return new ModuleLookupTables(
FieldDefToDescMap: target.ReadPointer(handle.Address + /* Module::FieldDefToDescMap */),
ManifestModuleReferencesMap: target.ReadPointer(handle.Address + /* Module::ManifestModuleReferencesMap */),
MemberRefToDescMap: target.ReadPointer(handle.Address + /* Module::MemberRefToDescMap */),
MethodDefToDescMap: target.ReadPointer(handle.Address + /* Module::MethodDefToDescMap */),
TypeDefToMethodTableMap: target.ReadPointer(handle.Address + /* Module::TypeDefToMethodTableMap */),
TypeRefToMethodTableMap: target.ReadPointer(handle.Address + /* Module::TypeRefToMethodTableMap */));
}
```
1 change: 1 addition & 0 deletions src/coreclr/debug/daccess/dacimpl.h
Original file line number Diff line number Diff line change
@@ -1231,6 +1231,7 @@ class ClrDataAccess

HRESULT GetThreadDataImpl(CLRDATA_ADDRESS threadAddr, struct DacpThreadData *threadData);
HRESULT GetThreadStoreDataImpl(struct DacpThreadStoreData *data);
HRESULT GetModuleDataImpl(CLRDATA_ADDRESS addr, struct DacpModuleData *moduleData);
HRESULT GetNestedExceptionDataImpl(CLRDATA_ADDRESS exception, CLRDATA_ADDRESS *exceptionObject, CLRDATA_ADDRESS *nextNestedException);
HRESULT GetMethodTableDataImpl(CLRDATA_ADDRESS mt, struct DacpMethodTableData *data);
HRESULT GetMethodTableForEEClassImpl (CLRDATA_ADDRESS eeClassReallyMT, CLRDATA_ADDRESS *value);
63 changes: 55 additions & 8 deletions src/coreclr/debug/daccess/request.cpp
Original file line number Diff line number Diff line change
@@ -1706,13 +1706,62 @@ ClrDataAccess::GetModule(CLRDATA_ADDRESS addr, IXCLRDataModule **mod)
}

HRESULT
ClrDataAccess::GetModuleData(CLRDATA_ADDRESS addr, struct DacpModuleData *ModuleData)
ClrDataAccess::GetModuleData(CLRDATA_ADDRESS addr, struct DacpModuleData* moduleData)
{
if (addr == 0 || ModuleData == NULL)
if (addr == 0 || moduleData == NULL)
return E_INVALIDARG;

SOSDacEnter();

if (m_cdacSos != NULL)
{
hr = m_cdacSos->GetModuleData(addr, moduleData);
if (FAILED(hr))
{
hr = GetModuleDataImpl(addr, moduleData);
}
#ifdef _DEBUG
else
{
DacpModuleData moduleDataLocal;
HRESULT hrLocal = GetModuleDataImpl(addr, &moduleDataLocal);
_ASSERTE(hr == hrLocal);
_ASSERTE(moduleData->Address == moduleDataLocal.Address);
_ASSERTE(moduleData->PEAssembly == moduleDataLocal.PEAssembly);
_ASSERTE(moduleData->ilBase == moduleDataLocal.ilBase);
_ASSERTE(moduleData->metadataStart == moduleDataLocal.metadataStart);
_ASSERTE(moduleData->metadataSize == moduleDataLocal.metadataSize);
_ASSERTE(moduleData->Assembly == moduleDataLocal.Assembly);
_ASSERTE(moduleData->bIsReflection == moduleDataLocal.bIsReflection);
_ASSERTE(moduleData->bIsPEFile == moduleDataLocal.bIsPEFile);
_ASSERTE(moduleData->dwBaseClassIndex == moduleDataLocal.dwBaseClassIndex);
_ASSERTE(moduleData->dwModuleID == moduleDataLocal.dwModuleID);
_ASSERTE(moduleData->dwTransientFlags == moduleDataLocal.dwTransientFlags);
_ASSERTE(moduleData->TypeDefToMethodTableMap == moduleDataLocal.TypeDefToMethodTableMap);
_ASSERTE(moduleData->TypeRefToMethodTableMap == moduleDataLocal.TypeRefToMethodTableMap);
_ASSERTE(moduleData->MethodDefToDescMap == moduleDataLocal.MethodDefToDescMap);
_ASSERTE(moduleData->FieldDefToDescMap == moduleDataLocal.FieldDefToDescMap);
_ASSERTE(moduleData->MemberRefToDescMap == moduleDataLocal.MemberRefToDescMap);
_ASSERTE(moduleData->FileReferencesMap == moduleDataLocal.FileReferencesMap);
_ASSERTE(moduleData->ManifestModuleReferencesMap == moduleDataLocal.ManifestModuleReferencesMap);
_ASSERTE(moduleData->LoaderAllocator == moduleDataLocal.LoaderAllocator);
_ASSERTE(moduleData->ThunkHeap == moduleDataLocal.ThunkHeap);
_ASSERTE(moduleData->dwModuleIndex == moduleDataLocal.dwModuleIndex);
}
#endif
}
else
{
hr = GetModuleDataImpl(addr, moduleData);
}

SOSDacLeave();
return hr;
}

HRESULT
ClrDataAccess::GetModuleDataImpl(CLRDATA_ADDRESS addr, struct DacpModuleData *ModuleData)
{
Module* pModule = PTR_Module(TO_TADDR(addr));

ZeroMemory(ModuleData,sizeof(DacpModuleData));
@@ -1721,7 +1770,7 @@ ClrDataAccess::GetModuleData(CLRDATA_ADDRESS addr, struct DacpModuleData *Module
COUNT_T metadataSize = 0;
if (!pModule->IsReflectionEmit())
{
ModuleData->ilBase = TO_CDADDR(dac_cast<TADDR>(pModule->GetPEAssembly()->GetLoadedLayout()->GetBase()));
ModuleData->ilBase = TO_CDADDR(dac_cast<TADDR>(pModule->m_baseAddress));
}

ModuleData->metadataStart = (CLRDATA_ADDRESS)dac_cast<TADDR>(pModule->GetPEAssembly()->GetLoadedMetadata(&metadataSize));
@@ -1754,8 +1803,7 @@ ClrDataAccess::GetModuleData(CLRDATA_ADDRESS addr, struct DacpModuleData *Module
}
EX_END_CATCH(SwallowAllExceptions)

SOSDacLeave();
return hr;
return S_OK;
}

HRESULT
@@ -2232,12 +2280,11 @@ ClrDataAccess::GetPEFileBase(CLRDATA_ADDRESS moduleAddr, CLRDATA_ADDRESS *base)
SOSDacEnter();

PTR_Module pModule = PTR_Module(TO_TADDR(moduleAddr));
PEAssembly* pPEAssembly = pModule->GetPEAssembly();

// More fields later?
if (!pPEAssembly->IsReflectionEmit())
if (!pModule->IsReflectionEmit())
{
*base = TO_CDADDR(dac_cast<TADDR>(pPEAssembly->GetLoadedLayout()->GetBase()));
*base = TO_CDADDR(dac_cast<TADDR>(pModule->m_baseAddress));
}
else
{
1 change: 1 addition & 0 deletions src/coreclr/debug/runtimeinfo/contracts.jsonc
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@
// so to conditionally include contracts, put additional contracts in a separate file
{
"Exception": 1,
"Loader": 1,
"RuntimeTypeSystem": 1,
"Thread": 1
}
18 changes: 18 additions & 0 deletions src/coreclr/debug/runtimeinfo/datadescriptor.h
Original file line number Diff line number Diff line change
@@ -156,6 +156,24 @@ CDAC_TYPE_BEGIN(GCHandle)
CDAC_TYPE_SIZE(sizeof(OBJECTHANDLE))
CDAC_TYPE_END(GCHandle)

// Loader

CDAC_TYPE_BEGIN(Module)
CDAC_TYPE_INDETERMINATE(Module)
CDAC_TYPE_FIELD(Module, /*pointer*/, Assembly, cdac_offsets<Module>::Assembly)
CDAC_TYPE_FIELD(Module, /*pointer*/, Base, cdac_offsets<Module>::Base)
CDAC_TYPE_FIELD(Module, /*pointer*/, Flags, cdac_offsets<Module>::Flags)
CDAC_TYPE_FIELD(Module, /*pointer*/, LoaderAllocator, cdac_offsets<Module>::LoaderAllocator)
CDAC_TYPE_FIELD(Module, /*pointer*/, ThunkHeap, cdac_offsets<Module>::ThunkHeap)

CDAC_TYPE_FIELD(Module, /*pointer*/, FieldDefToDescMap, cdac_offsets<Module>::FieldDefToDescMap)
CDAC_TYPE_FIELD(Module, /*pointer*/, ManifestModuleReferencesMap, cdac_offsets<Module>::ManifestModuleReferencesMap)
CDAC_TYPE_FIELD(Module, /*pointer*/, MemberRefToDescMap, cdac_offsets<Module>::MemberRefToDescMap)
CDAC_TYPE_FIELD(Module, /*pointer*/, MethodDefToDescMap, cdac_offsets<Module>::MethodDefToDescMap)
CDAC_TYPE_FIELD(Module, /*pointer*/, TypeDefToMethodTableMap, cdac_offsets<Module>::TypeDefToMethodTableMap)
CDAC_TYPE_FIELD(Module, /*pointer*/, TypeRefToMethodTableMap, cdac_offsets<Module>::TypeRefToMethodTableMap)
CDAC_TYPE_END(Module)

// Metadata

CDAC_TYPE_BEGIN(MethodTable)
4 changes: 4 additions & 0 deletions src/coreclr/vm/ceeload.cpp
Original file line number Diff line number Diff line change
@@ -406,11 +406,15 @@ void Module::Initialize(AllocMemTracker *pamTracker, LPCWSTR szName)
INSTANCE_CHECK;
STANDARD_VM_CHECK;
PRECONDITION(szName == NULL);
PRECONDITION(m_pPEAssembly->IsLoaded());
}
CONTRACTL_END;

m_loaderAllocator = GetAssembly()->GetLoaderAllocator();
m_pSimpleName = m_pPEAssembly->GetSimpleName();
m_baseAddress = m_pPEAssembly->HasLoadedPEImage() ? m_pPEAssembly->GetLoadedLayout()->GetBase() : NULL;
if (m_pPEAssembly->IsReflectionEmit())
m_dwTransientFlags |= IS_REFLECTION_EMIT;

m_Crst.Init(CrstModule);
m_LookupTableCrst.Init(CrstModuleLookupTable, CrstFlags(CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD));
25 changes: 24 additions & 1 deletion src/coreclr/vm/ceeload.h
Original file line number Diff line number Diff line change
@@ -603,6 +603,7 @@ class Module : public ModuleBase
PTR_CUTF8 m_pSimpleName; // Cached simple name for better performance and easier diagnostics

PTR_PEAssembly m_pPEAssembly;
PTR_VOID m_baseAddress; // Cached base address for easier diagnostics

enum {
// These are the values set in m_dwTransientFlags.
@@ -615,6 +616,8 @@ class Module : public ModuleBase
IS_PROFILER_NOTIFIED = 0x00000010,
IS_ETW_NOTIFIED = 0x00000020,

IS_REFLECTION_EMIT = 0x00000040,

//
// Note: The values below must match the ones defined in
// cordbpriv.h for DebuggerAssemblyControlFlags when shifted
@@ -886,7 +889,7 @@ class Module : public ModuleBase
CodeVersionManager * GetCodeVersionManager();
#endif

BOOL IsReflectionEmit() const { WRAPPER_NO_CONTRACT; SUPPORTS_DAC; return GetPEAssembly()->IsReflectionEmit(); }
BOOL IsReflectionEmit() const { WRAPPER_NO_CONTRACT; SUPPORTS_DAC; return (m_dwTransientFlags & IS_REFLECTION_EMIT) != 0; }
BOOL IsSystem() { WRAPPER_NO_CONTRACT; SUPPORTS_DAC; return m_pPEAssembly->IsSystem(); }
// Returns true iff the debugger can see this module.
BOOL IsVisibleToDebugger();
@@ -1602,6 +1605,26 @@ class Module : public ModuleBase

uint32_t GetNativeMetadataAssemblyCount();
#endif // !defined(DACCESS_COMPILE)

template<typename T> friend struct ::cdac_offsets;
};

template<>
struct cdac_offsets<Module>
{
static constexpr size_t Assembly = offsetof(Module, m_pAssembly);
static constexpr size_t Base = offsetof(Module, m_baseAddress);
static constexpr size_t Flags = offsetof(Module, m_dwTransientFlags);
static constexpr size_t LoaderAllocator = offsetof(Module, m_loaderAllocator);
static constexpr size_t ThunkHeap = offsetof(Module, m_pThunkHeap);

// Lookup map pointers
static constexpr size_t FieldDefToDescMap = offsetof(Module, m_FieldDefToDescMap) + offsetof(LookupMap<PTR_FieldDesc>, pTable);
static constexpr size_t ManifestModuleReferencesMap = offsetof(Module, m_ManifestModuleReferencesMap) + offsetof(LookupMap<PTR_Module>, pTable);
static constexpr size_t MemberRefToDescMap = offsetof(Module, m_MemberRefMap) + offsetof(LookupMap<TADDR>, pTable);
static constexpr size_t MethodDefToDescMap = offsetof(Module, m_MethodDefToDescMap) + offsetof(LookupMap<PTR_MethodDesc>, pTable);
static constexpr size_t TypeDefToMethodTableMap = offsetof(Module, m_TypeDefToMethodTableMap) + offsetof(LookupMap<PTR_MethodTable>, pTable);
static constexpr size_t TypeRefToMethodTableMap = offsetof(Module, m_TypeRefToMethodTableMap) + offsetof(LookupMap<PTR_TypeRef>, pTable);
};

//
1 change: 0 additions & 1 deletion src/coreclr/vm/peassembly.h
Original file line number Diff line number Diff line change
@@ -433,7 +433,6 @@ class PEAssembly final
// assembly that created the dynamic assembly. If the creator assembly is dynamic itself, then its fallback
// load context would be propagated to the assembly being dynamically generated.
PTR_AssemblyBinder m_pFallbackBinder;

}; // class PEAssembly

typedef ReleaseHolder<PEAssembly> PEAssemblyHolder;
62 changes: 62 additions & 0 deletions src/native/managed/cdacreader/src/Contracts/Loader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;

namespace Microsoft.Diagnostics.DataContractReader.Contracts;

internal readonly struct ModuleHandle
{
internal ModuleHandle(TargetPointer address)
{
Address = address;
}

internal TargetPointer Address { get; }
}

[Flags]
internal enum ModuleFlags
{
EditAndContinue = 0x00000008, // Edit and Continue is enabled for this module
ReflectionEmit = 0x00000040, // Reflection.Emit was used to create this module
}

internal record struct ModuleLookupTables(
TargetPointer FieldDefToDesc,
TargetPointer ManifestModuleReferences,
TargetPointer MemberRefToDesc,
TargetPointer MethodDefToDesc,
TargetPointer TypeDefToMethodTable,
TargetPointer TypeRefToMethodTable);

internal interface ILoader : IContract
{
static string IContract.Name => nameof(Loader);
static IContract IContract.Create(Target target, int version)
{
return version switch
{
1 => new Loader_1(target),
_ => default(Loader),
};
}

public virtual ModuleHandle GetModuleHandle(TargetPointer modulePointer) => throw new NotImplementedException();

public virtual TargetPointer GetAssembly(ModuleHandle handle) => throw new NotImplementedException();
public virtual ModuleFlags GetFlags(ModuleHandle handle) => throw new NotImplementedException();
public virtual TargetPointer GetLoaderAllocator(ModuleHandle handle) => throw new NotImplementedException();
public virtual TargetPointer GetThunkHeap(ModuleHandle handle) => throw new NotImplementedException();

public virtual TargetPointer GetILBase(ModuleHandle handle) => throw new NotImplementedException();
public virtual TargetPointer GetMetadataAddress(ModuleHandle handle, out ulong size) => throw new NotImplementedException();

public virtual ModuleLookupTables GetLookupTables(ModuleHandle handle) => throw new NotImplementedException();
}

internal readonly struct Loader : ILoader
{
// Everything throws NotImplementedException
}
73 changes: 73 additions & 0 deletions src/native/managed/cdacreader/src/Contracts/Loader_1.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;

namespace Microsoft.Diagnostics.DataContractReader.Contracts;

internal readonly struct Loader_1 : ILoader
{
private readonly Target _target;

internal Loader_1(Target target)
{
_target = target;
}

ModuleHandle ILoader.GetModuleHandle(TargetPointer modulePointer)
{
if (modulePointer == TargetPointer.Null)
throw new ArgumentNullException(nameof(modulePointer));

return new ModuleHandle(modulePointer);
}

TargetPointer ILoader.GetAssembly(ModuleHandle handle)
{
Data.Module module = _target.ProcessedData.GetOrAdd<Data.Module>(handle.Address);
return module.Assembly;
}

ModuleFlags ILoader.GetFlags(ModuleHandle handle)
{
Data.Module module = _target.ProcessedData.GetOrAdd<Data.Module>(handle.Address);
return (ModuleFlags)module.Flags;
}

TargetPointer ILoader.GetLoaderAllocator(ModuleHandle handle)
{
Data.Module module = _target.ProcessedData.GetOrAdd<Data.Module>(handle.Address);
return module.LoaderAllocator;
}

TargetPointer ILoader.GetThunkHeap(ModuleHandle handle)
{
Data.Module module = _target.ProcessedData.GetOrAdd<Data.Module>(handle.Address);
return module.ThunkHeap;
}

TargetPointer ILoader.GetILBase(ModuleHandle handle)
{
Data.Module module = _target.ProcessedData.GetOrAdd<Data.Module>(handle.Address);
return module.Base;
}

TargetPointer ILoader.GetMetadataAddress(ModuleHandle handle, out ulong size)
{
Data.Module module = _target.ProcessedData.GetOrAdd<Data.Module>(handle.Address);
return module.GetLoadedMetadata(out size);
}

ModuleLookupTables ILoader.GetLookupTables(ModuleHandle handle)
{
Data.Module module = _target.ProcessedData.GetOrAdd<Data.Module>(handle.Address);
return new ModuleLookupTables(
module.FieldDefToDescMap,
module.ManifestModuleReferencesMap,
module.MemberRefToDescMap,
module.MethodDefToDescMap,
module.TypeDefToMethodTableMap,
module.TypeRefToMethodTableMap);
}
}
1 change: 1 addition & 0 deletions src/native/managed/cdacreader/src/Contracts/Registry.cs
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ public Registry(Target target)
}

public IException Exception => GetContract<IException>();
public ILoader Loader => GetContract<ILoader>();
public IThread Thread => GetContract<IThread>();
public IRuntimeTypeSystem RuntimeTypeSystem => GetContract<IRuntimeTypeSystem>();

96 changes: 96 additions & 0 deletions src/native/managed/cdacreader/src/Data/Module.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Reflection.PortableExecutable;

namespace Microsoft.Diagnostics.DataContractReader.Data;

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

private readonly Target _target;

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

Flags = target.Read<uint>(address + (ulong)type.Fields[nameof(Flags)].Offset);
Assembly = target.ReadPointer(address + (ulong)type.Fields[nameof(Assembly)].Offset);
Base = target.ReadPointer(address + (ulong)type.Fields[nameof(Base)].Offset);
LoaderAllocator = target.ReadPointer(address + (ulong)type.Fields[nameof(LoaderAllocator)].Offset);
ThunkHeap = target.ReadPointer(address + (ulong)type.Fields[nameof(ThunkHeap)].Offset);

FieldDefToDescMap = target.ReadPointer(address + (ulong)type.Fields[nameof(FieldDefToDescMap)].Offset);
ManifestModuleReferencesMap = target.ReadPointer(address + (ulong)type.Fields[nameof(ManifestModuleReferencesMap)].Offset);
MemberRefToDescMap = target.ReadPointer(address + (ulong)type.Fields[nameof(MemberRefToDescMap)].Offset);
MethodDefToDescMap = target.ReadPointer(address + (ulong)type.Fields[nameof(MethodDefToDescMap)].Offset);
TypeDefToMethodTableMap = target.ReadPointer(address + (ulong)type.Fields[nameof(TypeDefToMethodTableMap)].Offset);
TypeRefToMethodTableMap = target.ReadPointer(address + (ulong)type.Fields[nameof(TypeRefToMethodTableMap)].Offset);
}

public TargetPointer Assembly { get; init; }
public uint Flags { get; init; }
public TargetPointer Base { get; init; }
public TargetPointer LoaderAllocator { get; init; }
public TargetPointer ThunkHeap { get; init; }

public TargetPointer FieldDefToDescMap { get; init; }
public TargetPointer ManifestModuleReferencesMap { get; init; }
public TargetPointer MemberRefToDescMap { get; init; }
public TargetPointer MethodDefToDescMap { get; init; }
public TargetPointer TypeDefToMethodTableMap { get; init; }
public TargetPointer TypeRefToMethodTableMap { get; init; }

private TargetPointer _metadataStart = TargetPointer.Null;
private ulong _metadataSize;
public TargetPointer GetLoadedMetadata(out ulong size)
{
if (Base != TargetPointer.Null && _metadataStart == TargetPointer.Null && _metadataSize == 0)
{
int peSignatureOffset = _target.Read<int>(Base + PEFormat.DosStub.PESignatureOffset);
ulong headerOffset = Base + (ulong)peSignatureOffset;
ushort magic = _target.Read<ushort>(headerOffset + PEFormat.PEHeader.MagicOffset);
ulong clrHeaderOffset = magic == (ushort)PEMagic.PE32
? PEFormat.PEHeader.CLRRuntimeHeader32Offset
: PEFormat.PEHeader.CLRRuntimeHeader32PlusOffset;
int corHeaderRva = _target.Read<int>(headerOffset + clrHeaderOffset);

// Read RVA and size of the metadata
ulong metadataDirectoryAddress = Base + (ulong)corHeaderRva + PEFormat.CorHeader.MetadataOffset;
_metadataStart = Base + (ulong)_target.Read<int>(metadataDirectoryAddress);
_metadataSize = (ulong)_target.Read<int>(metadataDirectoryAddress + sizeof(int));
}

size = _metadataSize;
return _metadataStart;
}

// https://learn.microsoft.com/windows/win32/debug/pe-format
private static class PEFormat
{
private const int PESignatureSize = sizeof(int);
private const int CoffHeaderSize = 20;

public static class DosStub
{
public const int PESignatureOffset = 0x3c;
}

public static class PEHeader
{
private const ulong OptionalHeaderOffset = PESignatureSize + CoffHeaderSize;
public const ulong MagicOffset = OptionalHeaderOffset;
public const ulong CLRRuntimeHeader32Offset = OptionalHeaderOffset + 208;
public const ulong CLRRuntimeHeader32PlusOffset = OptionalHeaderOffset + 224;
}

// See ECMA-335 II.25.3.3 CLI Header
public static class CorHeader
{
public const ulong MetadataOffset = 8;
}
}
}
2 changes: 1 addition & 1 deletion src/native/managed/cdacreader/src/DataType.cs
Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@ public enum DataType
GCAllocContext,
ExceptionInfo,
RuntimeThreadLocals,

Module,
MethodTable,
EEClass,
MethodTableAuxiliaryData,
32 changes: 31 additions & 1 deletion src/native/managed/cdacreader/src/Legacy/ISOSDacInterface.cs
Original file line number Diff line number Diff line change
@@ -43,6 +43,36 @@ internal struct DacpThreadData
public ulong nextThread;
}

internal struct DacpModuleData
{
public ulong Address;
public ulong PEAssembly; // Actually the module address in .NET 9+
public ulong ilBase;
public ulong metadataStart;
public ulong metadataSize;
public ulong Assembly; // Assembly pointer
public uint isReflection;
public uint isPEFile;

public ulong dwBaseClassIndex; // Always 0 - .NET no longer has this
public ulong dwModuleID; // Always 0 - .NET no longer has this

public uint dwTransientFlags;

public ulong TypeDefToMethodTableMap;
public ulong TypeRefToMethodTableMap;
public ulong MethodDefToDescMap;
public ulong FieldDefToDescMap;
public ulong MemberRefToDescMap;
public ulong FileReferencesMap;
public ulong ManifestModuleReferencesMap;

public ulong LoaderAllocator;
public ulong ThunkHeap;

public ulong dwModuleIndex; // Always 0 - .NET no longer has this
}

internal struct DacpMethodTableData
{
public int bIsFree; // everything else is NULL if this is true.
@@ -98,7 +128,7 @@ internal unsafe partial interface ISOSDacInterface
[PreserveSig]
int GetModule(ulong addr, /*IXCLRDataModule*/ void** mod);
[PreserveSig]
int GetModuleData(ulong moduleAddr, /*struct DacpModuleData*/ void* data);
int GetModuleData(ulong moduleAddr, DacpModuleData* data);
[PreserveSig]
int TraverseModuleMap(/*ModuleMapType*/ int mmt, ulong moduleAddr, /*MODULEMAPTRAVERSE*/ void* pCallback, void* token);
[PreserveSig]
50 changes: 48 additions & 2 deletions src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs
Original file line number Diff line number Diff line change
@@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics.Contracts;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;

@@ -151,7 +150,54 @@ public unsafe int GetMethodTableForEEClass(ulong eeClassReallyCanonMT, ulong* va
public unsafe int GetMethodTableSlot(ulong mt, uint slot, ulong* value) => HResults.E_NOTIMPL;
public unsafe int GetMethodTableTransparencyData(ulong mt, void* data) => HResults.E_NOTIMPL;
public unsafe int GetModule(ulong addr, void** mod) => HResults.E_NOTIMPL;
public unsafe int GetModuleData(ulong moduleAddr, void* data) => HResults.E_NOTIMPL;

public unsafe int GetModuleData(ulong moduleAddr, DacpModuleData* data)
{
if (moduleAddr == 0 || data == null)
return HResults.E_INVALIDARG;

try
{
Contracts.ILoader contract = _target.Contracts.Loader;
Contracts.ModuleHandle handle = contract.GetModuleHandle(moduleAddr);

data->Address = moduleAddr;
data->PEAssembly = moduleAddr; // Module address in .NET 9+ - correspondingly, SOS-DAC APIs for PE assemblies expect a module address
data->Assembly = contract.GetAssembly(handle);

Contracts.ModuleFlags flags = contract.GetFlags(handle);
bool isReflectionEmit = flags.HasFlag(Contracts.ModuleFlags.ReflectionEmit);
data->isReflection = (uint)(isReflectionEmit ? 1 : 0);
data->isPEFile = (uint)(isReflectionEmit ? 0 : 1); // ReflectionEmit module means it is not a PE file
data->dwTransientFlags = (uint)flags;

data->ilBase = contract.GetILBase(handle);
data->metadataStart = contract.GetMetadataAddress(handle, out ulong metadataSize);
data->metadataSize = metadataSize;

data->LoaderAllocator = contract.GetLoaderAllocator(handle);
data->ThunkHeap = contract.GetThunkHeap(handle);

Contracts.ModuleLookupTables tables = contract.GetLookupTables(handle);
data->FieldDefToDescMap = tables.FieldDefToDesc;
data->ManifestModuleReferencesMap = tables.ManifestModuleReferences;
data->MemberRefToDescMap = tables.MemberRefToDesc;
data->MethodDefToDescMap = tables.MethodDefToDesc;
data->TypeDefToMethodTableMap = tables.TypeDefToMethodTable;
data->TypeRefToMethodTableMap = tables.TypeRefToMethodTable;

// Always 0 - .NET no longer has these concepts
data->dwModuleID = 0;
data->dwBaseClassIndex = 0;
data->dwModuleIndex = 0;
}
catch (Exception e)
{
return e.HResult;
}

return HResults.S_OK;
}

public unsafe int GetNestedExceptionData(ulong exception, ulong* exceptionObject, ulong* nextNestedException)
{

0 comments on commit 04a40c1

Please sign in to comment.