Adding GetAssemblyLoadContext cDAC API (#117939)

* adding GetAssemblyLoadContext cDAC API

* simplifying field access

* restructuring etc

* comments
This commit is contained in:
Rachel 2025-07-28 18:39:50 -07:00 committed by GitHub
parent 2d3bf386a5
commit 2db9fdf5b4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 115 additions and 56 deletions

View File

@ -68,6 +68,7 @@ string GetPath(ModuleHandle handle);
string GetFileName(ModuleHandle handle);
TargetPointer GetLoaderAllocator(ModuleHandle handle);
TargetPointer GetILBase(ModuleHandle handle);
TargetPointer GetAssemblyLoadContext(ModuleHandle handle);
ModuleLookupTables GetLookupTables(ModuleHandle handle);
TargetPointer GetModuleLookupMapElement(TargetPointer table, uint token, out TargetNUInt flags);
bool IsCollectible(ModuleHandle handle);
@ -107,6 +108,8 @@ TargetPointer GetStubHeap(TargetPointer loaderAllocatorPointer);
| `Assembly` | `NotifyFlags` | Flags relating to the debugger/profiler notification state of the assembly |
| `Assembly` | `Level` | File load level of the assembly |
| `PEAssembly` | `PEImage` | Pointer to the PEAssembly's PEImage |
| `PEAssembly` | `AssemblyBinder` | Pointer to the PEAssembly's binder |
| `AssemblyBinder` | `ManagedAssemblyLoadContext` | Pointer to the AssemblyBinder's ManagedAssemblyLoadContext |
| `PEImage` | `LoadedImageLayout` | Pointer to the PEImage's loaded PEImageLayout |
| `PEImage` | `ProbeExtensionResult` | PEImage's ProbeExtensionResult |
| `ProbeExtensionResult` | `Type` | Type of ProbeExtensionResult |
@ -384,6 +387,14 @@ TargetPointer GetILBase(ModuleHandle handle)
return target.ReadPointer(handle.Address + /* Module::Base offset */);
}
TargetPointer ILoader.GetAssemblyLoadContext(ModuleHandle handle)
{
PEAssembly peAssembly = target.ReadPointer(handle.Address + /* Module::PEAssembly offset */);
AssemblyBinder binder = target.ReadPointer(peAssembly + /* PEAssembly::AssemblyBinder offset */);
ObjectHandle objectHandle = new ObjectHandle(binder);
return objectHandle.Object;
}
ModuleLookupTables GetLookupTables(ModuleHandle handle)
{
return new ModuleLookupTables(

View File

@ -46,7 +46,6 @@ record struct ThreadData (
ThreadStoreData GetThreadStoreData();
ThreadStoreCounts GetThreadCounts();
ThreadData GetThreadData(TargetPointer threadPointer);
TargetPointer GetManagedThreadObject(TargetPointer threadPointer);
```
## Version 1
@ -128,10 +127,4 @@ ThreadData GetThreadData(TargetPointer address)
NextThread: target.ReadPointer(address + /* Thread::LinkNext offset */) - threadLinkOffset;
);
}
TargetPointer GetManagedThreadObject(TargetPointer threadPointer)
{
var runtimeThread = new Thread(Target, threadPointer);
return Contracts.GCHandle.GetObject(new DacGCHandle(runtimeThread.m_ExposedObject));
}
```

View File

@ -266,8 +266,14 @@ CDAC_TYPE_END(LoaderAllocator)
CDAC_TYPE_BEGIN(PEAssembly)
CDAC_TYPE_INDETERMINATE(PEAssembly)
CDAC_TYPE_FIELD(PEAssembly, /*pointer*/, PEImage, cdac_data<PEAssembly>::PEImage)
CDAC_TYPE_FIELD(PEAssembly, /*pointer*/, AssemblyBinder, cdac_data<PEAssembly>::AssemblyBinder)
CDAC_TYPE_END(PEAssembly)
CDAC_TYPE_BEGIN(AssemblyBinder)
CDAC_TYPE_INDETERMINATE(AssemblyBinder)
CDAC_TYPE_FIELD(AssemblyBinder, /*pointer*/, ManagedAssemblyLoadContext, cdac_data<AssemblyBinder>::ManagedAssemblyLoadContext)
CDAC_TYPE_END(AssemblyBinder)
CDAC_TYPE_BEGIN(PEImage)
CDAC_TYPE_INDETERMINATE(PEImage)
CDAC_TYPE_FIELD(PEImage, /*pointer*/, LoadedImageLayout, cdac_data<PEImage>::LoadedImageLayout)

View File

@ -431,10 +431,7 @@ Assembly *Assembly::CreateDynamic(AssemblyBinder* pBinder, NativeAssemblyNamePar
IfFailThrow(pAssemblyEmit->DefineAssembly(pAssemblyNameParts->_pPublicKeyOrToken, pAssemblyNameParts->_cbPublicKeyOrToken, hashAlgorithm,
pAssemblyNameParts->_pName, &assemData, pAssemblyNameParts->_flags,
&ma));
pPEAssembly = PEAssembly::Create(pAssemblyEmit);
// Set it as the fallback load context binder for the dynamic assembly being created
pPEAssembly->SetFallbackBinder(pBinder);
pPEAssembly = PEAssembly::Create(pAssemblyEmit, pBinder);
}
AppDomain* pDomain = ::GetAppDomain();

View File

@ -128,6 +128,12 @@ private:
INT_PTR m_ptrManagedAssemblyLoadContext;
SArray<Assembly*> m_loadedAssemblies;
friend struct cdac_data<AssemblyBinder>;
};
template<>
struct cdac_data<AssemblyBinder>
{
static constexpr size_t ManagedAssemblyLoadContext = offsetof(AssemblyBinder, m_ptrManagedAssemblyLoadContext);
};
#endif

View File

@ -647,6 +647,7 @@ PEAssembly::PEAssembly(
BINDER_SPACE::Assembly* pBindResultInfo,
IMetaDataEmit* pEmit,
BOOL isSystem,
AssemblyBinder* pFallbackBinder /*= NULL*/,
PEImage * pPEImage /*= NULL*/,
BINDER_SPACE::Assembly * pHostAssembly /*= NULL*/)
{
@ -670,7 +671,7 @@ PEAssembly::PEAssembly(
m_refCount = 1;
m_isSystem = isSystem;
m_pHostAssembly = nullptr;
m_pFallbackBinder = nullptr;
m_pAssemblyBinder = nullptr;
pPEImage = pBindResultInfo ? pBindResultInfo->GetPEImage() : pPEImage;
if (pPEImage)
@ -722,6 +723,15 @@ PEAssembly::PEAssembly(
m_pHostAssembly = pBindResultInfo;
}
if (m_pHostAssembly != nullptr)
{
m_pAssemblyBinder = m_pHostAssembly->GetBinder();
}
else
{
m_pAssemblyBinder = pFallbackBinder;
}
#ifdef LOGGING
GetPathOrCodeBase(m_debugName);
m_pDebugName = m_debugName.GetUTF8();
@ -740,6 +750,7 @@ PEAssembly *PEAssembly::Open(
nullptr, // BindResult
nullptr, // IMetaDataEmit
FALSE, // isSystem
nullptr, // FallbackBinder
pPEImageIL,
pHostAssembly);
@ -833,7 +844,7 @@ PEAssembly* PEAssembly::Open(BINDER_SPACE::Assembly* pBindResult)
};
/* static */
PEAssembly *PEAssembly::Create(IMetaDataAssemblyEmit *pAssemblyEmit)
PEAssembly *PEAssembly::Create(IMetaDataAssemblyEmit *pAssemblyEmit, AssemblyBinder *pFallbackBinder)
{
CONTRACT(PEAssembly *)
{
@ -847,7 +858,7 @@ PEAssembly *PEAssembly::Create(IMetaDataAssemblyEmit *pAssemblyEmit)
// we have.)
SafeComHolder<IMetaDataEmit> pEmit;
pAssemblyEmit->QueryInterface(IID_IMetaDataEmit, (void **)&pEmit);
RETURN new PEAssembly(NULL, pEmit, FALSE);
RETURN new PEAssembly(NULL, pEmit, FALSE, pFallbackBinder);
}
#endif // #ifndef DACCESS_COMPILE
@ -1083,25 +1094,5 @@ TADDR PEAssembly::GetMDInternalRWAddress()
// Returns the AssemblyBinder* instance associated with the PEAssembly
PTR_AssemblyBinder PEAssembly::GetAssemblyBinder()
{
LIMITED_METHOD_CONTRACT;
PTR_AssemblyBinder pBinder = NULL;
PTR_BINDER_SPACE_Assembly pHostAssembly = GetHostAssembly();
if (pHostAssembly)
{
pBinder = pHostAssembly->GetBinder();
}
else
{
// If we do not have a host assembly, check if we are dealing with
// a dynamically emitted assembly and if so, use its fallback load context
// binder reference.
if (IsReflectionEmit())
{
pBinder = GetFallbackBinder();
}
}
return pBinder;
return m_pAssemblyBinder;
}

View File

@ -312,20 +312,18 @@ public:
// For Dynamic assemblies this is the fallback binder.
PTR_AssemblyBinder GetAssemblyBinder();
#ifndef DACCESS_COMPILE
void SetFallbackBinder(PTR_AssemblyBinder pFallbackBinder)
{
LIMITED_METHOD_CONTRACT;
m_pFallbackBinder = pFallbackBinder;
}
#endif //!DACCESS_COMPILE
// For certain assemblies, we do not have m_pHostAssembly since they are not bound using an actual binder.
// An example is Ref-Emitted assemblies. Thus, when such assemblies trigger load of their dependencies,
// we need to ensure they are loaded in appropriate load context.
//
// To enable this, we maintain a concept of "FallbackBinder", which will be set to the Binder of the
// 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 GetFallbackBinder()
{
LIMITED_METHOD_CONTRACT;
return m_pFallbackBinder;
return (m_pHostAssembly != NULL) ? NULL : m_pAssemblyBinder;
}
// ------------------------------------------------------------
@ -341,7 +339,7 @@ public:
static PEAssembly* Open(BINDER_SPACE::Assembly* pBindResult);
static PEAssembly* Create(IMetaDataAssemblyEmit* pEmit);
static PEAssembly* Create(IMetaDataAssemblyEmit* pEmit, AssemblyBinder* pFallbackBinder);
// ------------------------------------------------------------
// Utility functions
@ -372,6 +370,7 @@ private:
BINDER_SPACE::Assembly* pBindResultInfo,
IMetaDataEmit* pEmit,
BOOL isSystem,
AssemblyBinder* pFallbackBinder = NULL,
PEImage* pPEImageIL = NULL,
BINDER_SPACE::Assembly* pHostAssembly = NULL
);
@ -425,15 +424,7 @@ private:
bool m_isSystem;
PTR_BINDER_SPACE_Assembly m_pHostAssembly;
// For certain assemblies, we do not have m_pHostAssembly since they are not bound using an actual binder.
// An example is Ref-Emitted assemblies. Thus, when such assemblies trigger load of their dependencies,
// we need to ensure they are loaded in appropriate load context.
//
// To enable this, we maintain a concept of "FallbackBinder", which will be set to the Binder of the
// 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;
PTR_AssemblyBinder m_pAssemblyBinder;
friend struct cdac_data<PEAssembly>;
}; // class PEAssembly
@ -442,6 +433,7 @@ template<>
struct cdac_data<PEAssembly>
{
static constexpr size_t PEImage = offsetof(PEAssembly, m_PEImage);
static constexpr size_t AssemblyBinder = offsetof(PEAssembly, m_pAssemblyBinder);
};
typedef ReleaseHolder<PEAssembly> PEAssemblyHolder;

View File

@ -91,6 +91,7 @@ public interface ILoader : IContract
string GetFileName(ModuleHandle handle) => throw new NotImplementedException();
TargetPointer GetLoaderAllocator(ModuleHandle handle) => throw new NotImplementedException();
TargetPointer GetILBase(ModuleHandle handle) => throw new NotImplementedException();
TargetPointer GetAssemblyLoadContext(ModuleHandle handle) => throw new NotImplementedException();
ModuleLookupTables GetLookupTables(ModuleHandle handle) => throw new NotImplementedException();
TargetPointer GetModuleLookupMapElement(TargetPointer table, uint token, out TargetNUInt flags) => throw new NotImplementedException();
bool IsCollectible(ModuleHandle handle) => throw new NotImplementedException();

View File

@ -35,6 +35,7 @@ public enum DataType
Assembly,
LoaderAllocator,
PEAssembly,
AssemblyBinder,
PEImage,
PEImageLayout,
CGrowableSymbolStream,

View File

@ -306,6 +306,15 @@ internal readonly struct Loader_1 : ILoader
return module.Base;
}
TargetPointer ILoader.GetAssemblyLoadContext(ModuleHandle handle)
{
Data.Module module = _target.ProcessedData.GetOrAdd<Data.Module>(handle.Address);
Data.PEAssembly peAssembly = _target.ProcessedData.GetOrAdd<Data.PEAssembly>(module.PEAssembly);
Data.AssemblyBinder binder = _target.ProcessedData.GetOrAdd<Data.AssemblyBinder>(peAssembly.AssemblyBinder);
Data.ObjectHandle objectHandle = _target.ProcessedData.GetOrAdd<Data.ObjectHandle>(binder.ManagedAssemblyLoadContext);
return objectHandle.Object;
}
ModuleLookupTables ILoader.GetLookupTables(ModuleHandle handle)
{
Data.Module module = _target.ProcessedData.GetOrAdd<Data.Module>(handle.Address);

View File

@ -0,0 +1,17 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
namespace Microsoft.Diagnostics.DataContractReader.Data;
internal sealed class AssemblyBinder : IData<AssemblyBinder>
{
static AssemblyBinder IData<AssemblyBinder>.Create(Target target, TargetPointer address) => new AssemblyBinder(target, address);
public AssemblyBinder(Target target, TargetPointer address)
{
Target.TypeInfo type = target.GetTypeInfo(DataType.AssemblyBinder);
ManagedAssemblyLoadContext = target.ReadPointer(address + (ulong)type.Fields[nameof(ManagedAssemblyLoadContext)].Offset);
}
public TargetPointer ManagedAssemblyLoadContext { get; init; }
}

View File

@ -13,7 +13,9 @@ internal sealed class PEAssembly : IData<PEAssembly>
Target.TypeInfo type = target.GetTypeInfo(DataType.PEAssembly);
PEImage = target.ReadPointer(address + (ulong)type.Fields[nameof(PEImage)].Offset);
AssemblyBinder = target.ReadPointer(address + (ulong)type.Fields[nameof(AssemblyBinder)].Offset);
}
public TargetPointer PEImage { get; init; }
public TargetPointer AssemblyBinder { get; init; }
}

View File

@ -2284,7 +2284,40 @@ internal sealed unsafe partial class SOSDacImpl
=> _legacyImpl8 is not null ? _legacyImpl8.GetFinalizationFillPointersSvr(heapAddr, cFillPointers, pFinalizationFillPointers, pNeeded) : HResults.E_NOTIMPL;
int ISOSDacInterface8.GetAssemblyLoadContext(ClrDataAddress methodTable, ClrDataAddress* assemblyLoadContext)
=> _legacyImpl8 is not null ? _legacyImpl8.GetAssemblyLoadContext(methodTable, assemblyLoadContext) : HResults.E_NOTIMPL;
{
int hr = HResults.S_OK;
try
{
if (methodTable == 0 || assemblyLoadContext == null)
hr = HResults.E_INVALIDARG;
else
{
Contracts.IRuntimeTypeSystem rtsContract = _target.Contracts.RuntimeTypeSystem;
Contracts.ILoader loaderContract = _target.Contracts.Loader;
Contracts.TypeHandle methodTableHandle = rtsContract.GetTypeHandle(methodTable.ToTargetPointer(_target));
Contracts.ModuleHandle moduleHandle = loaderContract.GetModuleHandleFromModulePtr(rtsContract.GetModule(methodTableHandle));
TargetPointer alc = loaderContract.GetAssemblyLoadContext(moduleHandle);
*assemblyLoadContext = alc.ToClrDataAddress(_target);
}
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
#if DEBUG
if (_legacyImpl8 is not null)
{
ClrDataAddress assemblyLoadContextLocal;
int hrLocal = _legacyImpl8.GetAssemblyLoadContext(methodTable, &assemblyLoadContextLocal);
Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}");
if (hr == HResults.S_OK)
{
Debug.Assert(*assemblyLoadContext == assemblyLoadContextLocal);
}
}
#endif
return hr;
}
#endregion ISOSDacInterface8
#region ISOSDacInterface9