Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,17 @@ private BadImageFormatException(string? fileName, int hResult)
_fileName = fileName;
SetMessageField();
}

// Do not delete: this is invoked from native code.
// Used when the requesting assembly chain is known, to provide assembly load dependency context.
private BadImageFormatException(string? fileName, string? requestingAssemblyChain, int hResult)
: base(null)
{
HResult = hResult;
_fileName = fileName;
SetMessageField();
if (requestingAssemblyChain is not null)
_message += Environment.NewLine + IO.FileLoadException.FormatRequestingAssemblyChain(requestingAssemblyChain);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,45 @@ private FileLoadException(string? fileName, int hResult)
_message = FormatFileLoadExceptionMessage(FileName, HResult);
}

// Do not delete: this is invoked from native code.
// Used when the requesting assembly chain is known, to provide assembly load dependency context.
Copy link
Member

Choose a reason for hiding this comment

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

This talks about chains, but the implementation seems to be only providing the immediate requesting assembly (no chain).

// The requestingAssemblyChain parameter is a newline-separated list of assembly display names,
// from immediate parent to root ancestor.
private FileLoadException(string? fileName, string? requestingAssemblyChain, int hResult)
: base(null)
{
HResult = hResult;
FileName = fileName;
_message = FormatFileLoadExceptionMessage(FileName, HResult);
if (requestingAssemblyChain is not null)
_message += Environment.NewLine + FormatRequestingAssemblyChain(requestingAssemblyChain);
}

internal static string FormatRequestingAssemblyChain(string requestingAssemblyChain)
{
int newlineIndex = requestingAssemblyChain.IndexOf('\n');
if (newlineIndex < 0)
return SR.Format(SR.IO_FileLoad_RequestingAssembly, requestingAssemblyChain);

var result = new System.Text.StringBuilder();
int start = 0;
while (start < requestingAssemblyChain.Length)
{
int end = requestingAssemblyChain.IndexOf('\n', start);
string name = end >= 0
? requestingAssemblyChain.Substring(start, end - start)
: requestingAssemblyChain.Substring(start);

if (result.Length > 0)
result.Append(Environment.NewLine);
result.Append(SR.Format(SR.IO_FileLoad_RequestingAssembly, name));

start = end >= 0 ? end + 1 : requestingAssemblyChain.Length;
}

return result.ToString();
}

internal static string FormatFileLoadExceptionMessage(string? fileName, int hResult)
{
string? format = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,17 @@ private FileNotFoundException(string? fileName, int hResult)
FileName = fileName;
SetMessageField();
}

// Do not delete: this is invoked from native code.
// Used when the requesting assembly chain is known, to provide assembly load dependency context.
private FileNotFoundException(string? fileName, string? requestingAssemblyChain, int hResult)
: base(null)
{
HResult = hResult;
FileName = fileName;
SetMessageField();
if (requestingAssemblyChain is not null)
_message += Environment.NewLine + FileLoadException.FormatRequestingAssemblyChain(requestingAssemblyChain);
}
}
}
6 changes: 6 additions & 0 deletions src/coreclr/vm/appdomain.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1115,6 +1115,12 @@ class AppDomain final
return m_AssemblyCache.LookupAssembly(pSpec, fThrow);
}

Assembly* FindCachedParentAssembly(Assembly* pAssembly)
{
WRAPPER_NO_CONTRACT;
return m_AssemblyCache.LookupParentAssemblyForAssembly(pAssembly);
}

private:
PEAssembly* FindCachedFile(AssemblySpec* pSpec, BOOL fThrow = TRUE);
BOOL IsCached(AssemblySpec *pSpec);
Expand Down
28 changes: 28 additions & 0 deletions src/coreclr/vm/assemblyspec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,34 @@ PEAssembly *AssemblySpecBindingCache::LookupFile(AssemblySpec *pSpec, BOOL fThro
}
}

Assembly *AssemblySpecBindingCache::LookupParentAssemblyForAssembly(Assembly *pAssembly)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
FORBID_FAULT;
MODE_ANY;
PRECONDITION(pAssembly != NULL);
}
CONTRACTL_END;

PtrHashMap::PtrIterator i = m_map.begin();
while (!i.end())
{
AssemblyBinding *b = (AssemblyBinding*) i.GetValue();
if (!b->IsError() && b->GetAssembly() == pAssembly)
{
Assembly *pParent = b->GetParentAssembly();
if (pParent != NULL)
return pParent;
}
++i;
}

return NULL;
}


class AssemblyBindingHolder
{
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/vm/assemblyspec.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ class AssemblySpecBindingCache
inline Assembly* GetAssembly(){ LIMITED_METHOD_CONTRACT; return m_pAssembly; };
inline void SetAssembly(Assembly* pAssembly){ LIMITED_METHOD_CONTRACT; m_pAssembly = pAssembly; };
inline PEAssembly* GetFile(){ LIMITED_METHOD_CONTRACT; return m_pPEAssembly;};
inline Assembly* GetParentAssembly(){ WRAPPER_NO_CONTRACT; return m_spec.GetParentAssembly(); };
inline BOOL IsError(){ LIMITED_METHOD_CONTRACT; return (m_exceptionType!=EXTYPE_NONE);};

// bound to the file, but failed later
Expand Down Expand Up @@ -492,6 +493,7 @@ class AssemblySpecBindingCache

Assembly *LookupAssembly(AssemblySpec *pSpec, BOOL fThrow=TRUE);
PEAssembly *LookupFile(AssemblySpec *pSpec, BOOL fThrow = TRUE);
Assembly *LookupParentAssemblyForAssembly(Assembly *pAssembly);

BOOL StoreAssembly(AssemblySpec *pSpec, Assembly *pAssembly);
BOOL StorePEAssembly(AssemblySpec *pSpec, PEAssembly *pPEAssembly);
Expand Down
151 changes: 136 additions & 15 deletions src/coreclr/vm/clrex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1606,32 +1606,62 @@ OBJECTREF EEFileLoadException::CreateThrowable()
struct {
OBJECTREF pNewException;
STRINGREF pNewFileString;
STRINGREF pNewFusionLogString;
} gc;
gc.pNewException = NULL;
gc.pNewFileString = NULL;
gc.pNewFusionLogString = NULL;
GCPROTECT_BEGIN(gc);

gc.pNewFileString = StringObject::NewString(m_name);
gc.pNewException = AllocateObject(CoreLibBinder::GetException(m_kind));

MethodDesc* pMD = MemberLoader::FindMethod(gc.pNewException->GetMethodTable(),
COR_CTOR_METHOD_NAME, &gsig_IM_Str_Int_RetVoid);
bool usedThreeArgCtor = false;

if (!pMD)
if (!m_requestingAssemblyName.IsEmpty())
{
MAKE_WIDEPTR_FROMUTF8(wzMethodName, COR_CTOR_METHOD_NAME);
COMPlusThrowNonLocalized(kMissingMethodException, wzMethodName);
gc.pNewFusionLogString = StringObject::NewString(m_requestingAssemblyName);

MethodDesc* pMD = MemberLoader::FindMethod(gc.pNewException->GetMethodTable(),
COR_CTOR_METHOD_NAME, &gsig_IM_Str_Str_Int_RetVoid);

if (pMD)
{
MethodDescCallSite exceptionCtor(pMD);

ARG_SLOT args[] = {
ObjToArgSlot(gc.pNewException),
ObjToArgSlot(gc.pNewFileString),
ObjToArgSlot(gc.pNewFusionLogString),
(ARG_SLOT) m_hr
};

exceptionCtor.Call(args);
usedThreeArgCtor = true;
}
}

MethodDescCallSite exceptionCtor(pMD);
if (!usedThreeArgCtor)
{
MethodDesc* pMD = MemberLoader::FindMethod(gc.pNewException->GetMethodTable(),
COR_CTOR_METHOD_NAME, &gsig_IM_Str_Int_RetVoid);

ARG_SLOT args[] = {
ObjToArgSlot(gc.pNewException),
ObjToArgSlot(gc.pNewFileString),
(ARG_SLOT) m_hr
};
if (!pMD)
{
MAKE_WIDEPTR_FROMUTF8(wzMethodName, COR_CTOR_METHOD_NAME);
COMPlusThrowNonLocalized(kMissingMethodException, wzMethodName);
}

exceptionCtor.Call(args);
MethodDescCallSite exceptionCtor(pMD);

ARG_SLOT args[] = {
ObjToArgSlot(gc.pNewException),
ObjToArgSlot(gc.pNewFileString),
(ARG_SLOT) m_hr
};

exceptionCtor.Call(args);
}

GCPROTECT_END();

Expand Down Expand Up @@ -1664,8 +1694,6 @@ BOOL EEFileLoadException::CheckType(Exception* ex)

// <TODO>@todo: ideally we would use inner exceptions with these routines</TODO>

/* static */

/* static */
void DECLSPEC_NORETURN EEFileLoadException::Throw(AssemblySpec *pSpec, HRESULT hr, Exception *pInnerException/* = NULL*/)
{
Expand All @@ -1684,7 +1712,100 @@ void DECLSPEC_NORETURN EEFileLoadException::Throw(AssemblySpec *pSpec, HRESULT

StackSString name;
pSpec->GetDisplayName(0, name);
EX_THROW_WITH_INNER(EEFileLoadException, (name, hr), pInnerException);

// Extract the requesting assembly chain for diagnostic purposes
{
FAULT_NOT_FATAL();

Exception *inner2 = ExThrowWithInnerHelper(pInnerException);
EEFileLoadException *pException = new EEFileLoadException(name, hr);
pException->SetInnerException(inner2);

Assembly *pParentAssembly = pSpec->GetParentAssembly();
if (pParentAssembly != NULL)
{
StackSString parentName;
pParentAssembly->GetDisplayName(parentName);

// Build the requesting assembly chain: start with the immediate parent,
// then walk up the binding cache to find transitive requesting assemblies.
StackSString requestingChain;
requestingChain.Set(parentName);

EX_TRY
{
AppDomain *pDomain = pSpec->GetAppDomain();
if (pDomain != NULL)
{
Assembly *pWalkAssembly = pParentAssembly;
int depth = 0;
const int MaxChainDepth = 10;

while (pWalkAssembly != NULL && depth < MaxChainDepth)
{
// Look up the current assembly in the binding cache to find
// which assembly requested its load (its parent).
Assembly *pGrandParent = pDomain->FindCachedParentAssembly(pWalkAssembly);
if (pGrandParent == NULL || pGrandParent == pWalkAssembly)
break;

// Skip system assemblies (System.Private.CoreLib etc.)
if (!pGrandParent->IsSystem())
{
StackSString grandParentName;
pGrandParent->GetDisplayName(grandParentName);
requestingChain.Append(W("\n"));
requestingChain.Append(grandParentName);
}

pWalkAssembly = pGrandParent;
depth++;
}
}
}
EX_CATCH
{
// If the chain walk fails for any reason, just use what we have so far
}
EX_END_CATCH

pException->SetRequestingAssembly(requestingChain);

// Walk the inner exception chain and append the full chain to each
// inner exception, so that every exception in the chain
// carries the full dependency path from its perspective
EEFileLoadException *pInnerFLE = NULL;
Exception *pWalk = inner2;
int depth = 0;
while (pWalk != NULL && depth < 20)
{
if (EEFileLoadException::CheckType(pWalk))
{
pInnerFLE = (EEFileLoadException*)pWalk;
if (!pInnerFLE->m_requestingAssemblyName.IsEmpty())
{
StackSString updatedChain;
updatedChain.Set(pInnerFLE->m_requestingAssemblyName);
updatedChain.Append(W("\n"));
updatedChain.Append(requestingChain);
pInnerFLE->SetRequestingAssembly(updatedChain);
}
pWalk = pInnerFLE->m_innerException;
}
else
{
break;
}
depth++;
}
}

STRESS_LOG3(LF_EH, LL_INFO100, "EX_THROW_WITH_INNER Type = 0x%x HR = 0x%x, "
INDEBUG(__FILE__) " line %d\n", EEFileLoadException::GetType(),
pException->GetHR(), __LINE__);
EX_THROW_DEBUG_TRAP(__FUNCTION__, __FILE__, __LINE__, "EEFileLoadException", pException->GetHR(), "(name, hr)");
PAL_CPP_THROW(EEFileLoadException *, pException);
}
}

/* static */
Expand Down
11 changes: 10 additions & 1 deletion src/coreclr/vm/clrex.h
Original file line number Diff line number Diff line change
Expand Up @@ -665,12 +665,19 @@ class EEFileLoadException : public EEException
private:
SString m_name;
HRESULT m_hr;
SString m_requestingAssemblyName;

public:

EEFileLoadException(const SString &name, HRESULT hr, Exception *pInnerException = NULL);
~EEFileLoadException();

void SetRequestingAssembly(const SString &name)
{
WRAPPER_NO_CONTRACT;
m_requestingAssemblyName = name;
}

// virtual overrides
HRESULT GetHR()
{
Expand All @@ -692,7 +699,9 @@ class EEFileLoadException : public EEException
virtual Exception *CloneHelper()
{
WRAPPER_NO_CONTRACT;
return new EEFileLoadException(m_name, m_hr);
EEFileLoadException *pClone = new EEFileLoadException(m_name, m_hr);
pClone->SetRequestingAssembly(m_requestingAssemblyName);
return pClone;
}

private:
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/metasig.h
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ DEFINE_METASIG(SM(PtrChar_Int_PtrPtrChar_RetArrStr, P(u) i P(P(u)), a(s)))
DEFINE_METASIG_T(IM(Str_Exception_RetVoid, s C(EXCEPTION), v))
DEFINE_METASIG(IM(Str_Str_RetVoid, s s, v))
DEFINE_METASIG(IM(Str_Int_RetVoid, s i, v))
DEFINE_METASIG(IM(Str_Str_Int_RetVoid, s s i, v))
DEFINE_METASIG(IM(Str_Str_Str_Int_RetVoid, s s s i, v))
DEFINE_METASIG_T(IM(Str_BindingFlags_Binder_Obj_ArrObj_ArrParameterModifier_CultureInfo_ArrStr_RetObj, \
s g(BINDING_FLAGS) C(BINDER) j a(j) a(g(PARAMETER_MODIFIER)) C(CULTURE_INFO) a(s), j))
Expand Down
Loading
Loading