diff --git a/docs/design/datacontracts/Exception.md b/docs/design/datacontracts/Exception.md index e507c23f00c4f..0c37889355455 100644 --- a/docs/design/datacontracts/Exception.md +++ b/docs/design/datacontracts/Exception.md @@ -1,4 +1,4 @@ -# Contract Thread +# Contract Exception This contract is for getting information about exceptions in the process. diff --git a/docs/design/datacontracts/Loader.md b/docs/design/datacontracts/Loader.md new file mode 100644 index 0000000000000..4291c96569b27 --- /dev/null +++ b/docs/design/datacontracts/Loader.md @@ -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(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(metadataDirectoryAddress); + size = target.Read(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 */)); +} +``` diff --git a/src/coreclr/debug/daccess/dacimpl.h b/src/coreclr/debug/daccess/dacimpl.h index 57be1538e2bb7..2d2aad5bd1f96 100644 --- a/src/coreclr/debug/daccess/dacimpl.h +++ b/src/coreclr/debug/daccess/dacimpl.h @@ -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); diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index 1021fc3db128f..291d048eed7ed 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -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(pModule->GetPEAssembly()->GetLoadedLayout()->GetBase())); + ModuleData->ilBase = TO_CDADDR(dac_cast(pModule->m_baseAddress)); } ModuleData->metadataStart = (CLRDATA_ADDRESS)dac_cast(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(pPEAssembly->GetLoadedLayout()->GetBase())); + *base = TO_CDADDR(dac_cast(pModule->m_baseAddress)); } else { diff --git a/src/coreclr/debug/runtimeinfo/contracts.jsonc b/src/coreclr/debug/runtimeinfo/contracts.jsonc index 44e7f914f9b07..f40991da45b67 100644 --- a/src/coreclr/debug/runtimeinfo/contracts.jsonc +++ b/src/coreclr/debug/runtimeinfo/contracts.jsonc @@ -10,6 +10,7 @@ // so to conditionally include contracts, put additional contracts in a separate file { "Exception": 1, + "Loader": 1, "RuntimeTypeSystem": 1, "Thread": 1 } diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index 0e0aa0c8a819d..d1b5443e497b5 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -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::Assembly) +CDAC_TYPE_FIELD(Module, /*pointer*/, Base, cdac_offsets::Base) +CDAC_TYPE_FIELD(Module, /*pointer*/, Flags, cdac_offsets::Flags) +CDAC_TYPE_FIELD(Module, /*pointer*/, LoaderAllocator, cdac_offsets::LoaderAllocator) +CDAC_TYPE_FIELD(Module, /*pointer*/, ThunkHeap, cdac_offsets::ThunkHeap) + +CDAC_TYPE_FIELD(Module, /*pointer*/, FieldDefToDescMap, cdac_offsets::FieldDefToDescMap) +CDAC_TYPE_FIELD(Module, /*pointer*/, ManifestModuleReferencesMap, cdac_offsets::ManifestModuleReferencesMap) +CDAC_TYPE_FIELD(Module, /*pointer*/, MemberRefToDescMap, cdac_offsets::MemberRefToDescMap) +CDAC_TYPE_FIELD(Module, /*pointer*/, MethodDefToDescMap, cdac_offsets::MethodDefToDescMap) +CDAC_TYPE_FIELD(Module, /*pointer*/, TypeDefToMethodTableMap, cdac_offsets::TypeDefToMethodTableMap) +CDAC_TYPE_FIELD(Module, /*pointer*/, TypeRefToMethodTableMap, cdac_offsets::TypeRefToMethodTableMap) +CDAC_TYPE_END(Module) + // Metadata CDAC_TYPE_BEGIN(MethodTable) diff --git a/src/coreclr/vm/ceeload.cpp b/src/coreclr/vm/ceeload.cpp index 81d6f2347cd1b..c7a6ba8fc13b5 100644 --- a/src/coreclr/vm/ceeload.cpp +++ b/src/coreclr/vm/ceeload.cpp @@ -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)); diff --git a/src/coreclr/vm/ceeload.h b/src/coreclr/vm/ceeload.h index 2523ebf45c1a8..cb370bae893e0 100644 --- a/src/coreclr/vm/ceeload.h +++ b/src/coreclr/vm/ceeload.h @@ -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 friend struct ::cdac_offsets; +}; + +template<> +struct cdac_offsets +{ + 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, pTable); + static constexpr size_t ManifestModuleReferencesMap = offsetof(Module, m_ManifestModuleReferencesMap) + offsetof(LookupMap, pTable); + static constexpr size_t MemberRefToDescMap = offsetof(Module, m_MemberRefMap) + offsetof(LookupMap, pTable); + static constexpr size_t MethodDefToDescMap = offsetof(Module, m_MethodDefToDescMap) + offsetof(LookupMap, pTable); + static constexpr size_t TypeDefToMethodTableMap = offsetof(Module, m_TypeDefToMethodTableMap) + offsetof(LookupMap, pTable); + static constexpr size_t TypeRefToMethodTableMap = offsetof(Module, m_TypeRefToMethodTableMap) + offsetof(LookupMap, pTable); }; // diff --git a/src/coreclr/vm/peassembly.h b/src/coreclr/vm/peassembly.h index f30ce986cb372..7d552a94cc075 100644 --- a/src/coreclr/vm/peassembly.h +++ b/src/coreclr/vm/peassembly.h @@ -434,7 +434,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 PEAssemblyHolder; diff --git a/src/native/managed/cdacreader/src/Contracts/Loader.cs b/src/native/managed/cdacreader/src/Contracts/Loader.cs new file mode 100644 index 0000000000000..e30633fdf2a25 --- /dev/null +++ b/src/native/managed/cdacreader/src/Contracts/Loader.cs @@ -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 +} diff --git a/src/native/managed/cdacreader/src/Contracts/Loader_1.cs b/src/native/managed/cdacreader/src/Contracts/Loader_1.cs new file mode 100644 index 0000000000000..533dbc467d34e --- /dev/null +++ b/src/native/managed/cdacreader/src/Contracts/Loader_1.cs @@ -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(handle.Address); + return module.Assembly; + } + + ModuleFlags ILoader.GetFlags(ModuleHandle handle) + { + Data.Module module = _target.ProcessedData.GetOrAdd(handle.Address); + return (ModuleFlags)module.Flags; + } + + TargetPointer ILoader.GetLoaderAllocator(ModuleHandle handle) + { + Data.Module module = _target.ProcessedData.GetOrAdd(handle.Address); + return module.LoaderAllocator; + } + + TargetPointer ILoader.GetThunkHeap(ModuleHandle handle) + { + Data.Module module = _target.ProcessedData.GetOrAdd(handle.Address); + return module.ThunkHeap; + } + + TargetPointer ILoader.GetILBase(ModuleHandle handle) + { + Data.Module module = _target.ProcessedData.GetOrAdd(handle.Address); + return module.Base; + } + + TargetPointer ILoader.GetMetadataAddress(ModuleHandle handle, out ulong size) + { + Data.Module module = _target.ProcessedData.GetOrAdd(handle.Address); + return module.GetLoadedMetadata(out size); + } + + ModuleLookupTables ILoader.GetLookupTables(ModuleHandle handle) + { + Data.Module module = _target.ProcessedData.GetOrAdd(handle.Address); + return new ModuleLookupTables( + module.FieldDefToDescMap, + module.ManifestModuleReferencesMap, + module.MemberRefToDescMap, + module.MethodDefToDescMap, + module.TypeDefToMethodTableMap, + module.TypeRefToMethodTableMap); + } +} diff --git a/src/native/managed/cdacreader/src/Contracts/Registry.cs b/src/native/managed/cdacreader/src/Contracts/Registry.cs index 9f7151adec432..21f46e481ecd9 100644 --- a/src/native/managed/cdacreader/src/Contracts/Registry.cs +++ b/src/native/managed/cdacreader/src/Contracts/Registry.cs @@ -19,6 +19,7 @@ public Registry(Target target) } public IException Exception => GetContract(); + public ILoader Loader => GetContract(); public IThread Thread => GetContract(); public IRuntimeTypeSystem RuntimeTypeSystem => GetContract(); diff --git a/src/native/managed/cdacreader/src/Data/Module.cs b/src/native/managed/cdacreader/src/Data/Module.cs new file mode 100644 index 0000000000000..e462a201241ce --- /dev/null +++ b/src/native/managed/cdacreader/src/Data/Module.cs @@ -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 +{ + static Module IData.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(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(Base + PEFormat.DosStub.PESignatureOffset); + ulong headerOffset = Base + (ulong)peSignatureOffset; + ushort magic = _target.Read(headerOffset + PEFormat.PEHeader.MagicOffset); + ulong clrHeaderOffset = magic == (ushort)PEMagic.PE32 + ? PEFormat.PEHeader.CLRRuntimeHeader32Offset + : PEFormat.PEHeader.CLRRuntimeHeader32PlusOffset; + int corHeaderRva = _target.Read(headerOffset + clrHeaderOffset); + + // Read RVA and size of the metadata + ulong metadataDirectoryAddress = Base + (ulong)corHeaderRva + PEFormat.CorHeader.MetadataOffset; + _metadataStart = Base + (ulong)_target.Read(metadataDirectoryAddress); + _metadataSize = (ulong)_target.Read(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; + } + } +} diff --git a/src/native/managed/cdacreader/src/DataType.cs b/src/native/managed/cdacreader/src/DataType.cs index 338a544ab488c..88e973c82ee14 100644 --- a/src/native/managed/cdacreader/src/DataType.cs +++ b/src/native/managed/cdacreader/src/DataType.cs @@ -25,7 +25,7 @@ public enum DataType GCAllocContext, ExceptionInfo, RuntimeThreadLocals, - + Module, MethodTable, EEClass, MethodTableAuxiliaryData, diff --git a/src/native/managed/cdacreader/src/Legacy/ISOSDacInterface.cs b/src/native/managed/cdacreader/src/Legacy/ISOSDacInterface.cs index 6a806e631731c..02b77265554ba 100644 --- a/src/native/managed/cdacreader/src/Legacy/ISOSDacInterface.cs +++ b/src/native/managed/cdacreader/src/Legacy/ISOSDacInterface.cs @@ -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] diff --git a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs index 240d98bf7d295..3567f3b8571f3 100644 --- a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs @@ -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) {