Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[cdac] Start Loader contract and implement ISOSDacInterface::GetModuleData in cDAC #104257

Merged
merged 16 commits into from
Jul 9, 2024
Merged
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Add functions to Loader contract to handle GetModuleData - still need…
… MetadataAddress
elinor-fung committed Jul 1, 2024
commit 1191ae818c9aaf2c8d30ef9776ae368a1d234390
32 changes: 32 additions & 0 deletions src/coreclr/debug/runtimeinfo/datadescriptor.h
Original file line number Diff line number Diff line change
@@ -156,6 +156,38 @@ CDAC_TYPE_BEGIN(GCHandle)
CDAC_TYPE_SIZE(sizeof(OBJECTHANDLE))
CDAC_TYPE_END(GCHandle)

CDAC_TYPE_BEGIN(Module)
CDAC_TYPE_INDETERMINATE(Module)
CDAC_TYPE_FIELD(Module, /*pointer*/, Assembly, cdac_offsets<Module>::Assembly)
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*/, PEAssembly, cdac_offsets<Module>::PEAssembly)
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)

CDAC_TYPE_BEGIN(PEAssembly)
CDAC_TYPE_INDETERMINATE(PEAssembly)
CDAC_TYPE_FIELD(PEAssembly, /*pointer*/, PEImage, cdac_offsets<PEAssembly>::PEImage)
CDAC_TYPE_END(PEAssembly)

CDAC_TYPE_BEGIN(PEImage)
CDAC_TYPE_INDETERMINATE(PEImage)
CDAC_TYPE_FIELD(PEImage, /*pointer*/, LoadedLayout, cdac_offsets<PEImage>::LoadedLayout)
CDAC_TYPE_END(PEImage)

CDAC_TYPE_BEGIN(PEImageLayout)
CDAC_TYPE_INDETERMINATE(PEImageLayout)
CDAC_TYPE_FIELD(PEImageLayout, /*pointer*/, Base, cdac_offsets<PEImageLayout>::Base)
CDAC_TYPE_FIELD(PEImageLayout, /*uint32*/, Size, cdac_offsets<PEImageLayout>::Size)
CDAC_TYPE_END(PEImageLayout)

CDAC_TYPES_END()

CDAC_GLOBALS_BEGIN()
3 changes: 3 additions & 0 deletions src/coreclr/inc/pedecoder.h
Original file line number Diff line number Diff line change
@@ -396,8 +396,11 @@ class PEDecoder
FLAG_HAS_NO_READYTORUN_HEADER = 0x100,
};

protected:
TADDR m_base;
COUNT_T m_size; // size of file on disk, as opposed to OptionalHeaders.SizeOfImage

private:
ULONG m_flags;

PTR_IMAGE_NT_HEADERS m_pNTHeaders;
20 changes: 20 additions & 0 deletions src/coreclr/vm/ceeload.h
Original file line number Diff line number Diff line change
@@ -1602,6 +1602,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 Flags = offsetof(Module, m_dwTransientFlags);
static constexpr size_t LoaderAllocator = offsetof(Module, m_loaderAllocator);
static constexpr size_t PEAssembly = offsetof(Module, m_pPEAssembly);
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);
};

//
7 changes: 7 additions & 0 deletions src/coreclr/vm/peassembly.h
Original file line number Diff line number Diff line change
@@ -435,8 +435,15 @@ class PEAssembly final
// load context would be propagated to the assembly being dynamically generated.
PTR_AssemblyBinder m_pFallbackBinder;

template<typename T> friend struct ::cdac_offsets;
}; // class PEAssembly

template<>
struct cdac_offsets<PEAssembly>
{
static constexpr size_t PEImage = offsetof(PEAssembly, m_PEImage);
};

typedef ReleaseHolder<PEAssembly> PEAssemblyHolder;

#endif // PEASSEMBLY_H_
10 changes: 10 additions & 0 deletions src/coreclr/vm/peimage.h
Original file line number Diff line number Diff line change
@@ -319,6 +319,16 @@ class PEImage final
SimpleRWLock *m_pLayoutLock;
PTR_PEImageLayout m_pLayouts[IMAGE_COUNT];
IMDInternalImport* m_pMDImport;

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

template<>
struct cdac_offsets<PEImage>
{
static constexpr size_t LoadedLayout = offsetof(PEImage, m_pLayouts) + PEImage::IMAGE_LOADED * sizeof(PTR_PEImageLayout);
static_assert(std::is_same<decltype(std::declval<PEImage>().m_pLayouts), PTR_PEImageLayout[PEImage::IMAGE_COUNT]>::value,
"PEImage::m_pLayouts is of type PTR_PEImageLayout[]");
};

FORCEINLINE void PEImageRelease(PEImage *i)
9 changes: 9 additions & 0 deletions src/coreclr/vm/peimagelayout.h
Original file line number Diff line number Diff line change
@@ -72,6 +72,15 @@ class PEImageLayout : public PEDecoder
Volatile<LONG> m_refCount;
public:
PEImage* m_pOwner;

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

template<>
struct cdac_offsets<PEImageLayout>
{
static constexpr size_t Base = offsetof(PEImageLayout, m_base);
static constexpr size_t Size = offsetof(PEImageLayout, m_size);
};

typedef ReleaseHolder<PEImageLayout> PEImageLayoutHolder;
30 changes: 29 additions & 1 deletion src/native/managed/cdacreader/src/Contracts/Loader.cs
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;

namespace Microsoft.Diagnostics.DataContractReader.Contracts;

@@ -15,6 +16,22 @@ internal ModuleHandle(TargetPointer address)
internal TargetPointer Address { get; }
}

[Flags]
internal enum ModuleFlags
{
EditAndContinue = 0x00000008, // Edit and Continue is enabled for this module
}

internal enum ModuleLookupTable
{
FieldDefToDesc,
ManifestModuleReferences,
MemberRefToDesc,
MethodDefToDesc,
TypeDefToMethodTable,
TypeRefToMethodTable,
}

internal interface ILoader : IContract
{
static string IContract.Name => nameof(Loader);
@@ -27,7 +44,18 @@ static IContract IContract.Create(Target target, int version)
};
}

public virtual ModuleHandle GetModuleHandle(TargetPointer targetPointer) => throw new NotImplementedException();
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 bool IsReflectionEmit(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 IDictionary<ModuleLookupTable, TargetPointer> GetLookupTables(ModuleHandle handle) => throw new NotImplementedException();
}

internal readonly struct Loader : ILoader
70 changes: 70 additions & 0 deletions src/native/managed/cdacreader/src/Contracts/Loader_1.cs
Original file line number Diff line number Diff line change
@@ -2,6 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;

namespace Microsoft.Diagnostics.DataContractReader.Contracts;

@@ -18,4 +22,70 @@ ModuleHandle ILoader.GetModuleHandle(TargetPointer 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;
}

bool ILoader.IsReflectionEmit(ModuleHandle handle)
{
Data.Module module = _target.ProcessedData.GetOrAdd<Data.Module>(handle.Address);
return module.PEAssembly.PEImage == null;
}

TargetPointer ILoader.GetILBase(ModuleHandle handle)
{
Data.Module module = _target.ProcessedData.GetOrAdd<Data.Module>(handle.Address);
if (module.PEAssembly.PEImage == null)
return TargetPointer.Null;

return module.PEAssembly.PEImage.Base;
}

TargetPointer ILoader.GetMetadataAddress(ModuleHandle handle, out ulong size)
{
Data.Module module = _target.ProcessedData.GetOrAdd<Data.Module>(handle.Address);
if (module.PEAssembly.PEImage == null)
{
size = 0;
return TargetPointer.Null;
}

// TODO: [cdac]
size = 0;
return TargetPointer.Null;
}

IDictionary<ModuleLookupTable, TargetPointer> ILoader.GetLookupTables(ModuleHandle handle)
{
Dictionary<ModuleLookupTable, TargetPointer> tables = [];
Data.Module module = _target.ProcessedData.GetOrAdd<Data.Module>(handle.Address);
tables[ModuleLookupTable.FieldDefToDesc] = module.FieldDefToDescMap;
tables[ModuleLookupTable.ManifestModuleReferences] = module.ManifestModuleReferencesMap;
tables[ModuleLookupTable.MemberRefToDesc] = module.MemberRefToDescMap;
tables[ModuleLookupTable.MethodDefToDesc] = module.MethodDefToDescMap;
tables[ModuleLookupTable.TypeDefToMethodTable] = module.TypeDefToMethodTableMap;
tables[ModuleLookupTable.TypeRefToMethodTable] = module.TypeRefToMethodTableMap;
return tables;
}
}
41 changes: 41 additions & 0 deletions src/native/managed/cdacreader/src/Data/Module.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// 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 Module : IData<Module>
{
static Module IData<Module>.Create(Target target, TargetPointer address)
=> new Module(target, address);

public Module(Target target, TargetPointer address)
{
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);
LoaderAllocator = target.ReadPointer(address + (ulong)type.Fields[nameof(LoaderAllocator)].Offset);
PEAssembly = target.ProcessedData.GetOrAdd<PEAssembly>(target.ReadPointer(address + (ulong)type.Fields[nameof(PEAssembly)].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 LoaderAllocator { get; init; }
public PEAssembly PEAssembly { 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; }
}
21 changes: 21 additions & 0 deletions src/native/managed/cdacreader/src/Data/PEAssembly.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// 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 PEAssembly : IData<PEAssembly>
{
static PEAssembly IData<PEAssembly>.Create(Target target, TargetPointer address)
=> new PEAssembly(target, address);

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

TargetPointer peImagePointer = target.ReadPointer(address + (ulong)type.Fields[nameof(PEImage)].Offset);
if (peImagePointer != TargetPointer.Null)
PEImage = target.ProcessedData.GetOrAdd<PEImage>(peImagePointer);
}
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if we're going to have a problem in practice with the cDAC being more eager to read data from the target process up front. I think the brittle DAC is lazier about following pointers in target memory. It might be able to emit more info to the debugger before failing.

Copy link
Member

Choose a reason for hiding this comment

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

This is a concern for mini and triage dumps only. What we need to do for those is make sure that our routines for ensuring that enough data structures are live are updated so that they make sure to cover the important scenarios needed for examining dumps. Also we will likely need to have the ability for some of the apis to produce incomplete results. The only reason this works in the existing scheme is that there are a selected set of places in the DAC where failures are just ignored. We will eventually need to add the same things to the cDAC, but I don't think we should do that yet.


public PEImage? PEImage { get; init; }
}
34 changes: 34 additions & 0 deletions src/native/managed/cdacreader/src/Data/PEImage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// 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.Immutable;
using System.Reflection.PortableExecutable;

namespace Microsoft.Diagnostics.DataContractReader.Data;

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

private readonly Target _target;

public PEImage(Target target, TargetPointer address)
{
_target = target;
Target.TypeInfo type = target.GetTypeInfo(DataType.PEImage);
LoadedLayout = target.ReadPointer(address + (ulong)type.Fields[nameof(LoadedLayout)].Offset);
if (LoadedLayout != TargetPointer.Null)
{
Target.TypeInfo layoutType = target.GetTypeInfo(DataType.PEImageLayout);
Base = target.ReadPointer(LoadedLayout + (ulong)layoutType.Fields[nameof(Base)].Offset);
Size = target.Read<uint>(LoadedLayout + (ulong)layoutType.Fields[nameof(Size)].Offset);
}
}

public TargetPointer Base { get; init; } = TargetPointer.Null;
public uint Size { get; init; }

private TargetPointer LoadedLayout { get; init; }
}
6 changes: 5 additions & 1 deletion src/native/managed/cdacreader/src/DataType.cs
Original file line number Diff line number Diff line change
@@ -24,5 +24,9 @@ public enum DataType
ThreadStore,
GCAllocContext,
ExceptionInfo,
RuntimeThreadLocals
RuntimeThreadLocals,
Module,
PEAssembly,
PEImage,
PEImageLayout,
}
Loading