From a189446d7877e45e566114ae924ee209a5323fef Mon Sep 17 00:00:00 2001 From: Tom McDonald Date: Mon, 9 Feb 2026 00:55:53 -0500 Subject: [PATCH 1/5] Add support for async thunk methods in GetNativeCodeInfo --- src/coreclr/debug/daccess/dacdbiimpl.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index 9e368c4d99ded0..28f3e79c1d5d9b 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -1279,8 +1279,23 @@ HRESULT DacDbiInterfaceImpl::GetNativeCodeInfo(VMPTR_DomainAssembly vmDomainAsse DomainAssembly * pDomainAssembly = vmDomainAssembly.GetDacPtr(); Module * pModule = pDomainAssembly->GetAssembly()->GetModule(); - MethodDesc* pMethodDesc = FindLoadedMethodRefOrDef(pModule, functionToken); - pCodeInfo->vmNativeCodeMethodDescToken.SetHostPtr(pMethodDesc); + MethodDesc* pMethodDesc = FindLoadedMethodRefOrDef(pModule, functionToken); + if (pMethodDesc->IsAsyncThunkMethod()) + { + MethodTable * pMT = pMethodDesc->GetMethodTable(); + MethodTable::IntroducedMethodIterator it(pMT); + for (; it.IsValid(); it.Next()) + { + MethodDesc * pMD = it.GetMethodDesc(); + CONSISTENCY_CHECK(pMD != NULL && pMD->GetMethodTable() == pMT); + if (!pMD->IsDiagnosticsHidden()) + { + pMethodDesc = pMD; + break; + } + } + } + pCodeInfo->vmNativeCodeMethodDescToken.SetHostPtr(pMethodDesc); // if we are loading a module and trying to bind a previously set breakpoint, we may not have // a method desc yet, so check for that situation From 80429581640ccffc3ea48c4ff20a325f04d7bd70 Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Tue, 17 Feb 2026 16:54:57 -0500 Subject: [PATCH 2/5] dacize and use FindOrCreateAssociatedMethodDesc --- src/coreclr/debug/daccess/dacdbiimpl.cpp | 13 +-------- src/coreclr/vm/CMakeLists.txt | 2 +- src/coreclr/vm/generics.cpp | 9 ++++++ src/coreclr/vm/genmeth.cpp | 36 +++++++++++++++++++++--- src/coreclr/vm/instmethhash.cpp | 7 +++-- src/coreclr/vm/method.hpp | 5 ++++ 6 files changed, 53 insertions(+), 19 deletions(-) diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index 28f3e79c1d5d9b..a961521c491618 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -1282,18 +1282,7 @@ HRESULT DacDbiInterfaceImpl::GetNativeCodeInfo(VMPTR_DomainAssembly vmDomainAsse MethodDesc* pMethodDesc = FindLoadedMethodRefOrDef(pModule, functionToken); if (pMethodDesc->IsAsyncThunkMethod()) { - MethodTable * pMT = pMethodDesc->GetMethodTable(); - MethodTable::IntroducedMethodIterator it(pMT); - for (; it.IsValid(); it.Next()) - { - MethodDesc * pMD = it.GetMethodDesc(); - CONSISTENCY_CHECK(pMD != NULL && pMD->GetMethodTable() == pMT); - if (!pMD->IsDiagnosticsHidden()) - { - pMethodDesc = pMD; - break; - } - } + pMethodDesc = pMethodDesc->GetAsyncOtherVariantNoCreate(); } pCodeInfo->vmNativeCodeMethodDescToken.SetHostPtr(pMethodDesc); diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt index 3a4c0babdab259..77851ac7f97452 100644 --- a/src/coreclr/vm/CMakeLists.txt +++ b/src/coreclr/vm/CMakeLists.txt @@ -88,6 +88,7 @@ set(VM_SOURCES_DAC_AND_WKS_COMMON gchandleutilities.cpp genericdict.cpp generics.cpp + genmeth.cpp hash.cpp ilinstrumentation.cpp ilstubcache.cpp @@ -335,7 +336,6 @@ set(VM_SOURCES_WKS gcenv.ee.common.cpp gchelpers.cpp genanalysis.cpp - genmeth.cpp hosting.cpp hostinformation.cpp ilmarshalers.cpp diff --git a/src/coreclr/vm/generics.cpp b/src/coreclr/vm/generics.cpp index ad76316877fa71..b032f873ec26a4 100644 --- a/src/coreclr/vm/generics.cpp +++ b/src/coreclr/vm/generics.cpp @@ -494,6 +494,8 @@ ClassLoader::CreateTypeHandleForNonCanonicalGenericInstantiation( RETURN(TypeHandle(pMT)); } // ClassLoader::CreateTypeHandleForNonCanonicalGenericInstantiation +#endif // !DACCESS_COMPILE + namespace Generics { @@ -532,6 +534,13 @@ BOOL CheckInstantiation(Instantiation inst) return TRUE; } +} // namespace Generics + +#ifndef DACCESS_COMPILE + +namespace Generics +{ + // Just records the owner and links to the previous graph. RecursionGraph::RecursionGraph(RecursionGraph *pPrev, TypeHandle thOwner) { diff --git a/src/coreclr/vm/genmeth.cpp b/src/coreclr/vm/genmeth.cpp index 492ea84fe7e7f5..6dd9bdda0a1f32 100644 --- a/src/coreclr/vm/genmeth.cpp +++ b/src/coreclr/vm/genmeth.cpp @@ -15,7 +15,9 @@ #include "instmethhash.h" #include "typestring.h" #include "typedesc.h" +#ifndef DACCESS_COMPILE #include "comdelegate.h" +#endif // !DACCESS_COMPILE // Instantiated generic methods // @@ -61,6 +63,8 @@ // +#ifndef DACCESS_COMPILE + // Helper method that creates a method-desc off a template method desc static MethodDesc* CreateMethodDesc(LoaderAllocator *pAllocator, Module* pLoaderModule, @@ -150,6 +154,8 @@ static MethodDesc* CreateMethodDesc(LoaderAllocator *pAllocator, return pMD; } +#endif // !DACCESS_COMPILE + // // The following methods map between tightly bound boxing and unboxing MethodDesc. // We always layout boxing and unboxing MethodDescs next to each other in same @@ -167,6 +173,7 @@ static MethodDesc * FindTightlyBoundWrappedMethodDesc(MethodDesc * pMD) NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(pMD)); + SUPPORTS_DAC; } CONTRACTL_END @@ -196,6 +203,7 @@ static MethodDesc * FindTightlyBoundUnboxingStub(MethodDesc * pMD) NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(pMD)); + SUPPORTS_DAC; } CONTRACTL_END @@ -227,7 +235,7 @@ static MethodDesc * FindTightlyBoundUnboxingStub(MethodDesc * pMD) return pCurMD->IsUnboxingStub() ? pCurMD : NULL; } -#ifdef _DEBUG +#if defined(_DEBUG) && !defined(DACCESS_COMPILE) // // Alternative brute-force implementation of FindTightlyBoundWrappedMethodDesc for debug-only check. // @@ -304,7 +312,9 @@ static MethodDesc * FindTightlyBoundUnboxingStub_DEBUG(MethodDesc * pMD) } return NULL; } -#endif // _DEBUG +#endif // _DEBUG && !DACCESS_COMPILE + +#ifndef DACCESS_COMPILE /* static */ InstantiatedMethodDesc * @@ -574,6 +584,8 @@ InstantiatedMethodDesc::FindOrCreateExactClassMethod(MethodTable *pExactMT, return pInstMD; } +#endif // !DACCESS_COMPILE + // N.B. it is not guarantee that the returned InstantiatedMethodDesc is restored. // It is the caller's responsibility to call CheckRestore on the returned value. /* static */ @@ -590,6 +602,7 @@ InstantiatedMethodDesc::FindLoadedInstantiatedMethodDesc(MethodTable *pExactOrRe GC_NOTRIGGER; FORBID_FAULT; PRECONDITION(CheckPointer(pExactOrRepMT)); + SUPPORTS_DAC; // All wrapped method descriptors (except BoxedEntryPointStubs, which don't use this path) are // canonical and exhibit some kind of code sharing. @@ -744,6 +757,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD, { THROWS; if (allowCreate) { GC_TRIGGERS; } else { GC_NOTRIGGER; } + if (!allowCreate) { SUPPORTS_DAC; } INJECT_FAULT(COMPlusThrowOM();); PRECONDITION(CheckPointer(pDefMD)); @@ -868,8 +882,10 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD, // that there is no associated unboxing stub, and FindTightlyBoundUnboxingStub takes // this into account but the _DEBUG version does not, so only use it if the method // returned is actually different. +#ifndef DACCESS_COMPILE _ASSERTE(pResultMD == pMDescInCanonMT || pResultMD == FindTightlyBoundUnboxingStub_DEBUG(pMDescInCanonMT)); +#endif // !DACCESS_COMPILE if (pResultMD != NULL) { @@ -901,6 +917,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD, RETURN(NULL); } +#ifndef DACCESS_COMPILE CrstHolder ch(&pLoaderModule->m_InstMethodHashTableCrst); // Check whether another thread beat us to it! @@ -939,6 +956,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD, } // CrstHolder goes out of scope here +#endif // !DACCESS_COMPILE } } @@ -966,6 +984,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD, RETURN(NULL); } +#ifndef DACCESS_COMPILE // Recursively get the non-unboxing instantiating stub. Thus we chain an unboxing // stub with an instantiating stub. MethodDesc* pNonUnboxingStub= @@ -1023,6 +1042,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD, } // CrstHolder goes out of scope here +#endif // !DACCESS_COMPILE } } _ASSERTE(pResultMD); @@ -1071,8 +1091,10 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD, // that this is not an unboxing stub, and FindTightlyBoundWrappedMethodDesc takes // this into account but the _DEBUG version does not, so only use it if the method // returned is actually different. +#ifndef DACCESS_COMPILE _ASSERTE(pResultMD == pMDescInCanonMT || pResultMD == FindTightlyBoundWrappedMethodDesc_DEBUG(pMDescInCanonMT)); +#endif // !DACCESS_COMPILE if (pResultMD != NULL) { @@ -1143,11 +1165,13 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD, RETURN(NULL); } +#ifndef DACCESS_COMPILE pInstMD = InstantiatedMethodDesc::NewInstantiatedMethodDesc(pExactMT->GetCanonicalMethodTable(), pMDescInCanonMT, NULL, Instantiation(repInst, methodInst.GetNumArgs()), TRUE); +#endif // !DACCESS_COMPILE } } else if (getWrappedThenStub) @@ -1168,6 +1192,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD, RETURN(NULL); } +#ifndef DACCESS_COMPILE // This always returns the shared code. Repeat the original call except with // approximate params and allowInstParam=true MethodDesc* pWrappedMD = FindOrCreateAssociatedMethodDesc(pDefMD, @@ -1188,6 +1213,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD, pWrappedMD, methodInst, FALSE); +#endif // !DACCESS_COMPILE } } else @@ -1209,11 +1235,13 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD, RETURN(NULL); } +#ifndef DACCESS_COMPILE pInstMD = InstantiatedMethodDesc::NewInstantiatedMethodDesc(pExactMT, pMDescInCanonMT, NULL, methodInst, FALSE); +#endif // !DACCESS_COMPILE } } _ASSERTE(pInstMD); @@ -1229,6 +1257,8 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD, } } +#ifndef DACCESS_COMPILE + // Normalize the methoddesc for reflection /*static*/ MethodDesc* MethodDesc::FindOrCreateAssociatedMethodDescForReflection( MethodDesc *pMethod, @@ -1589,8 +1619,6 @@ void MethodDesc::CheckConstraintMetadataValidity(BOOL *pfHasCircularMethodConstr } -#ifndef DACCESS_COMPILE - BOOL MethodDesc::SatisfiesMethodConstraints(TypeHandle thParent, BOOL fThrowIfNotSatisfied/* = FALSE*/) { CONTRACTL diff --git a/src/coreclr/vm/instmethhash.cpp b/src/coreclr/vm/instmethhash.cpp index 63058191523c52..7b06904897cd28 100644 --- a/src/coreclr/vm/instmethhash.cpp +++ b/src/coreclr/vm/instmethhash.cpp @@ -86,6 +86,7 @@ PTR_LoaderAllocator InstMethodHashTable::GetLoaderAllocator() } } +#endif // #ifndef DACCESS_COMPILE // Calculate a hash value for a method-desc key static DWORD Hash(TypeHandle declaringType, mdMethodDef token, Instantiation inst) @@ -97,9 +98,9 @@ static DWORD Hash(TypeHandle declaringType, mdMethodDef token, Instantiation ins DWORD dwHash = 0x87654321; #define INST_HASH_ADD(_value) dwHash = ((dwHash << 5) + dwHash) ^ (_value) #ifdef TARGET_64BIT -#define INST_HASH_ADDPOINTER(_value) INST_HASH_ADD((uint32_t)(uintptr_t)_value); INST_HASH_ADD((uint32_t)(((uintptr_t)_value) >> 32)) +#define INST_HASH_ADDPOINTER(_value) INST_HASH_ADD((uint32_t)dac_cast(_value)); INST_HASH_ADD((uint32_t)((dac_cast(_value)) >> 32)) #else -#define INST_HASH_ADDPOINTER(_value) INST_HASH_ADD((uint32_t)(uintptr_t)_value); +#define INST_HASH_ADDPOINTER(_value) INST_HASH_ADD((uint32_t)dac_cast(_value)); #endif INST_HASH_ADDPOINTER(declaringType.AsPtr()); @@ -196,6 +197,8 @@ MethodDesc* InstMethodHashTable::FindMethodDesc(TypeHandle declaringType, return pMDResult; } +#ifndef DACCESS_COMPILE + BOOL InstMethodHashTable::ContainsMethodDesc(MethodDesc* pMD) { CONTRACTL diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index 689ea59ad5c336..455439763ccf9a 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -1722,6 +1722,11 @@ class MethodDesc return FindOrCreateAssociatedMethodDesc(this, GetMethodTable(), FALSE, GetMethodInstantiation(), allowInstParam, FALSE, TRUE, AsyncVariantLookup::AsyncOtherVariant); } + MethodDesc* GetAsyncOtherVariantNoCreate(BOOL allowInstParam = TRUE) + { + return FindOrCreateAssociatedMethodDesc(this, GetMethodTable(), FALSE, GetMethodInstantiation(), allowInstParam, FALSE, FALSE, AsyncVariantLookup::AsyncOtherVariant); + } + MethodDesc* GetAsyncVariant(BOOL allowInstParam = TRUE) { _ASSERT(!IsAsyncVariantMethod()); From 60b80499de3f2b3f2723a00b8f370a6f0ba18ec6 Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Tue, 17 Feb 2026 17:19:20 -0500 Subject: [PATCH 3/5] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/coreclr/debug/daccess/dacdbiimpl.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index a961521c491618..446dc9e683cf39 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -1280,9 +1280,13 @@ HRESULT DacDbiInterfaceImpl::GetNativeCodeInfo(VMPTR_DomainAssembly vmDomainAsse Module * pModule = pDomainAssembly->GetAssembly()->GetModule(); MethodDesc* pMethodDesc = FindLoadedMethodRefOrDef(pModule, functionToken); - if (pMethodDesc->IsAsyncThunkMethod()) + if (pMethodDesc != NULL && pMethodDesc->IsAsyncThunkMethod()) { - pMethodDesc = pMethodDesc->GetAsyncOtherVariantNoCreate(); + MethodDesc* pAsyncVariant = pMethodDesc->GetAsyncOtherVariantNoCreate(); + if (pAsyncVariant != NULL) + { + pMethodDesc = pAsyncVariant; + } } pCodeInfo->vmNativeCodeMethodDescToken.SetHostPtr(pMethodDesc); From 348247cdf70d1dcfd2d05c62349aa8d507a523d2 Mon Sep 17 00:00:00 2001 From: Tom McDonald Date: Wed, 18 Mar 2026 18:19:52 -0400 Subject: [PATCH 4/5] Fix formatting --- src/coreclr/debug/daccess/dacdbiimpl.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index 446dc9e683cf39..8630dc5951c624 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -1279,16 +1279,16 @@ HRESULT DacDbiInterfaceImpl::GetNativeCodeInfo(VMPTR_DomainAssembly vmDomainAsse DomainAssembly * pDomainAssembly = vmDomainAssembly.GetDacPtr(); Module * pModule = pDomainAssembly->GetAssembly()->GetModule(); - MethodDesc* pMethodDesc = FindLoadedMethodRefOrDef(pModule, functionToken); - if (pMethodDesc != NULL && pMethodDesc->IsAsyncThunkMethod()) - { - MethodDesc* pAsyncVariant = pMethodDesc->GetAsyncOtherVariantNoCreate(); - if (pAsyncVariant != NULL) + MethodDesc* pMethodDesc = FindLoadedMethodRefOrDef(pModule, functionToken); + if (pMethodDesc != NULL && pMethodDesc->IsAsyncThunkMethod()) { - pMethodDesc = pAsyncVariant; + MethodDesc* pAsyncVariant = pMethodDesc->GetAsyncOtherVariantNoCreate(); + if (pAsyncVariant != NULL) + { + pMethodDesc = pAsyncVariant; + } } - } - pCodeInfo->vmNativeCodeMethodDescToken.SetHostPtr(pMethodDesc); + pCodeInfo->vmNativeCodeMethodDescToken.SetHostPtr(pMethodDesc); // if we are loading a module and trying to bind a previously set breakpoint, we may not have // a method desc yet, so check for that situation From 4331f0d559d8718a2c99e206b715ab51a0e59943 Mon Sep 17 00:00:00 2001 From: Tom McDonald Date: Thu, 19 Mar 2026 12:19:04 -0400 Subject: [PATCH 5/5] Apply suggestion from @noahfalk Co-authored-by: Noah Falk --- src/coreclr/vm/method.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index 455439763ccf9a..e9eb61abaf5348 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -1724,6 +1724,7 @@ class MethodDesc MethodDesc* GetAsyncOtherVariantNoCreate(BOOL allowInstParam = TRUE) { + _ASSERTE(HasAsyncOtherVariant()); return FindOrCreateAssociatedMethodDesc(this, GetMethodTable(), FALSE, GetMethodInstantiation(), allowInstParam, FALSE, FALSE, AsyncVariantLookup::AsyncOtherVariant); }