diff --git a/src/coreclr/binder/assemblybindercommon.cpp b/src/coreclr/binder/assemblybindercommon.cpp index caa3525a029d43..f5613aee5a499d 100644 --- a/src/coreclr/binder/assemblybindercommon.cpp +++ b/src/coreclr/binder/assemblybindercommon.cpp @@ -1155,7 +1155,6 @@ HRESULT AssemblyBinderCommon::BindUsingHostAssemblyResolver(/* in */ INT_PTR pAs HRESULT AssemblyBinderCommon::BindUsingPEImage(/* in */ AssemblyBinder* pBinder, /* in */ BINDER_SPACE::AssemblyName *pAssemblyName, /* in */ PEImage *pPEImage, - /* in */ bool excludeAppPaths, /* [retval] [out] */ Assembly **ppAssembly) { HRESULT hr = E_FAIL; @@ -1177,55 +1176,45 @@ HRESULT AssemblyBinderCommon::BindUsingPEImage(/* in */ AssemblyBinder* pBinder // Lock the application context CRITSEC_Holder contextLock(pApplicationContext->GetCriticalSectionCookie()); - // Attempt uncached bind and register stream if possible - // We skip version compatibility check - so assemblies with same simple name will be reported - // as a successful bind. Below we compare MVIDs in that case instead (which is a more precise equality check). - hr = BindByName(pApplicationContext, - pAssemblyName, - true, // skipFailureCaching - true, // skipVersionCompatibilityCheck - excludeAppPaths, // excludeAppPaths - &bindResult); + // Check if an assembly with the same name was already loaded. + Assembly *pAssembly = NULL; + hr = FindInExecutionContext(pApplicationContext, pAssemblyName, &pAssembly); - if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + if (hr == S_FALSE && pAssembly == NULL) { + // No assembly with the same name was already loaded, so load from the stream IF_FAIL_GO(CreateImageAssembly(pPEImage, &bindResult)); } - else if (hr == S_OK) + else if (hr == S_OK && pAssembly != NULL) { - if (bindResult.HaveResult()) - { - // Attempt was made to load an assembly that has the same name as a previously loaded one. Since same name - // does not imply the same assembly, we will need to check the MVID to confirm it is the same assembly as being - // requested. + // An assembly with the same name was already loaded. We need to check the MVID + // to confirm it is the same assembly as the one being requested. - GUID incomingMVID; - GUID boundMVID; - - // GetMVID can throw exception - EX_TRY - { - pPEImage->GetMVID(&incomingMVID); - bindResult.GetAssembly()->GetPEImage()->GetMVID(&boundMVID); - } - EX_CATCH - { - hr = GET_EXCEPTION()->GetHR(); - goto Exit; - } - EX_END_CATCH + GUID incomingMVID; + GUID boundMVID; + // GetMVID can throw exception + EX_TRY + { + pPEImage->GetMVID(&incomingMVID); + pAssembly->GetPEImage()->GetMVID(&boundMVID); + } + EX_CATCH + { + hr = GET_EXCEPTION()->GetHR(); + goto Exit; + } + EX_END_CATCH - mvidMismatch = incomingMVID != boundMVID; - if (mvidMismatch) - { - // MVIDs do not match, so fail the load. - IF_FAIL_GO(COR_E_FILELOAD); - } - - // MVIDs match - request came in for the same assembly that was previously loaded. - // Let it through... + mvidMismatch = incomingMVID != boundMVID; + if (mvidMismatch) + { + // MVIDs do not match, so fail the load. + IF_FAIL_GO(COR_E_FILELOAD); } + + // MVIDs match - the assembly was previously loaded. + bindResult.SetResult(pAssembly, /*isInContext*/ true); } // Remember the post-bind version of the context diff --git a/src/coreclr/binder/customassemblybinder.cpp b/src/coreclr/binder/customassemblybinder.cpp index 950e462d512ae5..952225bca7acaf 100644 --- a/src/coreclr/binder/customassemblybinder.cpp +++ b/src/coreclr/binder/customassemblybinder.cpp @@ -102,7 +102,6 @@ Exit:; } HRESULT CustomAssemblyBinder::BindUsingPEImage( /* in */ PEImage *pPEImage, - /* in */ bool excludeAppPaths, /* [retval][out] */ BINDER_SPACE::Assembly **ppAssembly) { HRESULT hr = S_OK; @@ -129,7 +128,7 @@ HRESULT CustomAssemblyBinder::BindUsingPEImage( /* in */ PEImage *pPEImage, IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); } - hr = AssemblyBinderCommon::BindUsingPEImage(this, pAssemblyName, pPEImage, excludeAppPaths, &pCoreCLRFoundAssembly); + hr = AssemblyBinderCommon::BindUsingPEImage(this, pAssemblyName, pPEImage, &pCoreCLRFoundAssembly); if (hr == S_OK) { _ASSERTE(pCoreCLRFoundAssembly != NULL); @@ -230,7 +229,7 @@ void CustomAssemblyBinder::PrepareForLoadContextRelease(INT_PTR ptrManagedStrong // native LoaderAllocators of two collectible contexts in case the AssemblyLoadContext.Unload was called on the current // context before returning from its AssemblyLoadContext.Load override or the context's Resolving event. // But we need to release the LoaderAllocator so that it doesn't prevent completion of the final phase of unloading in - // some cases. It is safe to do as the AssemblyLoaderAllocator is guaranteed to be alive at least until the + // some cases. It is safe to do as the AssemblyLoaderAllocator is guaranteed to be alive at least until the // CustomAssemblyBinder::ReleaseLoadContext is called, where we NULL this pointer. m_pAssemblyLoaderAllocator->Release(); @@ -258,7 +257,7 @@ void CustomAssemblyBinder::ReleaseLoadContext() DestroyHandle(handle); SetAssemblyLoadContext((INT_PTR)NULL); - // The AssemblyLoaderAllocator is in a process of shutdown and should not be used + // The AssemblyLoaderAllocator is in a process of shutdown and should not be used // after this point. m_pAssemblyLoaderAllocator = NULL; } diff --git a/src/coreclr/binder/defaultassemblybinder.cpp b/src/coreclr/binder/defaultassemblybinder.cpp index 9900da40a7d263..610052f6719906 100644 --- a/src/coreclr/binder/defaultassemblybinder.cpp +++ b/src/coreclr/binder/defaultassemblybinder.cpp @@ -111,7 +111,6 @@ Exit:; #if !defined(DACCESS_COMPILE) HRESULT DefaultAssemblyBinder::BindUsingPEImage( /* in */ PEImage *pPEImage, - /* in */ bool excludeAppPaths, /* [retval][out] */ BINDER_SPACE::Assembly **ppAssembly) { HRESULT hr = S_OK; @@ -158,7 +157,7 @@ HRESULT DefaultAssemblyBinder::BindUsingPEImage( /* in */ PEImage *pPEImage, } } - hr = AssemblyBinderCommon::BindUsingPEImage(this, pAssemblyName, pPEImage, excludeAppPaths, &pCoreCLRFoundAssembly); + hr = AssemblyBinderCommon::BindUsingPEImage(this, pAssemblyName, pPEImage, &pCoreCLRFoundAssembly); if (hr == S_OK) { _ASSERTE(pCoreCLRFoundAssembly != NULL); diff --git a/src/coreclr/binder/inc/assemblybindercommon.hpp b/src/coreclr/binder/inc/assemblybindercommon.hpp index 55049c894f3ba6..a733d22158e568 100644 --- a/src/coreclr/binder/inc/assemblybindercommon.hpp +++ b/src/coreclr/binder/inc/assemblybindercommon.hpp @@ -56,7 +56,6 @@ namespace BINDER_SPACE static HRESULT BindUsingPEImage(/* in */ AssemblyBinder *pBinder, /* in */ BINDER_SPACE::AssemblyName *pAssemblyName, /* in */ PEImage *pPEImage, - /* in */ bool excludeAppPaths, /* [retval] [out] */ Assembly **ppAssembly); #endif // !defined(DACCESS_COMPILE) diff --git a/src/coreclr/binder/inc/customassemblybinder.h b/src/coreclr/binder/inc/customassemblybinder.h index 7a89c5033b9e50..9d59832f3c9798 100644 --- a/src/coreclr/binder/inc/customassemblybinder.h +++ b/src/coreclr/binder/inc/customassemblybinder.h @@ -18,7 +18,6 @@ class CustomAssemblyBinder final : public AssemblyBinder public: HRESULT BindUsingPEImage(PEImage* pPEImage, - bool excludeAppPaths, BINDER_SPACE::Assembly** ppAssembly) override; HRESULT BindUsingAssemblyName(BINDER_SPACE::AssemblyName* pAssemblyName, diff --git a/src/coreclr/binder/inc/defaultassemblybinder.h b/src/coreclr/binder/inc/defaultassemblybinder.h index 3d35854e09f3ff..398174c65a078b 100644 --- a/src/coreclr/binder/inc/defaultassemblybinder.h +++ b/src/coreclr/binder/inc/defaultassemblybinder.h @@ -16,7 +16,6 @@ class DefaultAssemblyBinder final : public AssemblyBinder public: HRESULT BindUsingPEImage(PEImage* pPEImage, - bool excludeAppPaths, BINDER_SPACE::Assembly** ppAssembly) override; HRESULT BindUsingAssemblyName(BINDER_SPACE::AssemblyName* pAssemblyName, diff --git a/src/coreclr/vm/assemblybinder.h b/src/coreclr/vm/assemblybinder.h index 89b501a6759a23..bc53b5cd80fd81 100644 --- a/src/coreclr/vm/assemblybinder.h +++ b/src/coreclr/vm/assemblybinder.h @@ -19,7 +19,7 @@ class AssemblyBinder public: HRESULT BindAssemblyByName(AssemblyNameData* pAssemblyNameData, BINDER_SPACE::Assembly** ppAssembly); - virtual HRESULT BindUsingPEImage(PEImage* pPEImage, bool excludeAppPaths, BINDER_SPACE::Assembly** ppAssembly) = 0; + virtual HRESULT BindUsingPEImage(PEImage* pPEImage, BINDER_SPACE::Assembly** ppAssembly) = 0; virtual HRESULT BindUsingAssemblyName(BINDER_SPACE::AssemblyName* pAssemblyName, BINDER_SPACE::Assembly** ppAssembly) = 0; /// @@ -89,7 +89,7 @@ class AssemblyBinder { } - SimpleNameToExpectedMVIDAndRequiringAssembly(LPCUTF8 simpleName, GUID mvid, bool compositeComponent, LPCUTF8 AssemblyRequirementName) : + SimpleNameToExpectedMVIDAndRequiringAssembly(LPCUTF8 simpleName, GUID mvid, bool compositeComponent, LPCUTF8 AssemblyRequirementName) : SimpleName(simpleName), Mvid(mvid), AssemblyRequirementName(AssemblyRequirementName), diff --git a/src/coreclr/vm/assemblynative.cpp b/src/coreclr/vm/assemblynative.cpp index a962e22b5fe31c..43cb55c3d4b43e 100644 --- a/src/coreclr/vm/assemblynative.cpp +++ b/src/coreclr/vm/assemblynative.cpp @@ -120,7 +120,7 @@ extern "C" void QCALLTYPE AssemblyNative_InternalLoad(NativeAssemblyNameParts* p } /* static */ -Assembly* AssemblyNative::LoadFromPEImage(AssemblyBinder* pBinder, PEImage *pImage, bool excludeAppPaths) +Assembly* AssemblyNative::LoadFromPEImage(AssemblyBinder* pBinder, PEImage *pImage) { CONTRACT(Assembly*) { @@ -146,7 +146,7 @@ Assembly* AssemblyNative::LoadFromPEImage(AssemblyBinder* pBinder, PEImage *pIma HRESULT hr = S_OK; PTR_AppDomain pCurDomain = GetAppDomain(); - hr = pBinder->BindUsingPEImage(pImage, excludeAppPaths, &pAssembly); + hr = pBinder->BindUsingPEImage(pImage, &pAssembly); if (hr != S_OK) { @@ -250,7 +250,7 @@ extern "C" void QCALLTYPE AssemblyNative_LoadFromStream(INT_PTR ptrNativeAssembl ThrowHR(COR_E_BADIMAGEFORMAT, BFA_IJW_IN_COLLECTIBLE_ALC); } - // Pass the stream based assembly as IL in an attempt to bind and load it + // Pass the stream-based assembly as IL in an attempt to bind and load it Assembly* pLoadedAssembly = AssemblyNative::LoadFromPEImage(pBinder, pILImage); { GCX_COOP(); diff --git a/src/coreclr/vm/assemblynative.hpp b/src/coreclr/vm/assemblynative.hpp index cba6b322391d2a..f6c434dd746b98 100644 --- a/src/coreclr/vm/assemblynative.hpp +++ b/src/coreclr/vm/assemblynative.hpp @@ -21,7 +21,7 @@ class AssemblyNative { public: - static Assembly* LoadFromPEImage(AssemblyBinder* pBinder, PEImage *pImage, bool excludeAppPaths = false); + static Assembly* LoadFromPEImage(AssemblyBinder* pBinder, PEImage *pImage); // static FCALLs static FCDECL0(FC_BOOL_RET, IsTracingEnabled); diff --git a/src/coreclr/vm/assemblyspec.cpp b/src/coreclr/vm/assemblyspec.cpp index 4650b95691e0f2..97b6f7636321b8 100644 --- a/src/coreclr/vm/assemblyspec.cpp +++ b/src/coreclr/vm/assemblyspec.cpp @@ -436,7 +436,7 @@ Assembly *AssemblySpec::LoadAssembly(LPCWSTR pFilePath) if (!pILImage->CheckILFormat()) THROW_BAD_FORMAT(BFA_BAD_IL, pILImage.GetValue()); - RETURN AssemblyNative::LoadFromPEImage(AppDomain::GetCurrentDomain()->GetDefaultBinder(), pILImage, true /* excludeAppPaths */); + RETURN AssemblyNative::LoadFromPEImage(AppDomain::GetCurrentDomain()->GetDefaultBinder(), pILImage); } HRESULT AssemblySpec::CheckFriendAssemblyName() diff --git a/src/tests/Loader/binding/AppPaths/AppPaths.cs b/src/tests/Loader/binding/AppPaths/AppPaths.cs new file mode 100644 index 00000000000000..6073b8719d3305 --- /dev/null +++ b/src/tests/Loader/binding/AppPaths/AppPaths.cs @@ -0,0 +1,40 @@ +// 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.IO; +using System.Reflection; +using System.Runtime.Loader; +using Xunit; + +public class AppPaths +{ + private const string AssemblyToLoad = "AssemblyToLoad"; + + [Fact] + public static void DefaultALC() + { + LoadFromStream(AssemblyLoadContext.Default); + } + + [Fact] + public static void CustomALC() + { + AssemblyLoadContext alc = new("alc"); + LoadFromStream(alc); + } + + private static void LoadFromStream(AssemblyLoadContext alc) + { + // corerun should add the app directory as the APP_PATHS property + Assert.Equal(AppContext.BaseDirectory.TrimEnd(Path.DirectorySeparatorChar), ((string)AppContext.GetData("APP_PATHS")).TrimEnd(Path.DirectorySeparatorChar), StringComparer.OrdinalIgnoreCase); + string assemblyPath = Path.Combine(AppContext.BaseDirectory, $"{AssemblyToLoad}.dll"); + byte[] assemblyBytes = File.ReadAllBytes(assemblyPath); + MemoryStream stream = new(assemblyBytes); + + Assembly assembly = alc.LoadFromStream(stream); + Assert.NotNull(assembly); + Assert.Equal(AssemblyToLoad, assembly.GetName().Name); + Assert.True(string.IsNullOrEmpty(assembly.Location), + $"Assembly should be loaded from stream and have an empty Location. Actual: '{assembly.Location}'"); + } +} diff --git a/src/tests/Loader/binding/AppPaths/AppPaths.csproj b/src/tests/Loader/binding/AppPaths/AppPaths.csproj new file mode 100644 index 00000000000000..8c0c50c3af9bfd --- /dev/null +++ b/src/tests/Loader/binding/AppPaths/AppPaths.csproj @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/tests/Loader/binding/AppPaths/AssemblyToLoad.cs b/src/tests/Loader/binding/AppPaths/AssemblyToLoad.cs new file mode 100644 index 00000000000000..5906dd63abac31 --- /dev/null +++ b/src/tests/Loader/binding/AppPaths/AssemblyToLoad.cs @@ -0,0 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace AssemblyToLoad +{ +} diff --git a/src/tests/Loader/binding/AppPaths/AssemblyToLoad.csproj b/src/tests/Loader/binding/AppPaths/AssemblyToLoad.csproj new file mode 100644 index 00000000000000..5dd8197ec61a27 --- /dev/null +++ b/src/tests/Loader/binding/AppPaths/AssemblyToLoad.csproj @@ -0,0 +1,8 @@ + + + Library + + + + +