1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5#include "common.h"
6#include "assemblybinder.hpp"
7#include "clrprivbindercoreclr.h"
8#include "clrprivbinderassemblyloadcontext.h"
9#include "clrprivbinderutil.h"
10
11#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
12
13using namespace BINDER_SPACE;
14
15// ============================================================================
16// CLRPrivBinderAssemblyLoadContext implementation
17// ============================================================================
18HRESULT CLRPrivBinderAssemblyLoadContext::BindAssemblyByNameWorker(BINDER_SPACE::AssemblyName *pAssemblyName,
19 BINDER_SPACE::Assembly **ppCoreCLRFoundAssembly)
20{
21 VALIDATE_ARG_RET(pAssemblyName != nullptr && ppCoreCLRFoundAssembly != nullptr);
22 HRESULT hr = S_OK;
23
24#ifdef _DEBUG
25 // MSCORLIB should be bound using BindToSystem
26 _ASSERTE(!pAssemblyName->IsMscorlib());
27#endif
28
29 // Do we have the assembly already loaded in the context of the current binder?
30 hr = AssemblyBinder::BindAssembly(&m_appContext,
31 pAssemblyName,
32 NULL,
33 NULL,
34 FALSE, //fNgenExplicitBind,
35 FALSE, //fExplicitBindToNativeImage,
36 false, //excludeAppPaths,
37 ppCoreCLRFoundAssembly);
38 if (!FAILED(hr))
39 {
40 _ASSERTE(*ppCoreCLRFoundAssembly != NULL);
41 (*ppCoreCLRFoundAssembly)->SetBinder(this);
42 }
43
44 return hr;
45}
46
47HRESULT CLRPrivBinderAssemblyLoadContext::BindAssemblyByName(IAssemblyName *pIAssemblyName,
48 ICLRPrivAssembly **ppAssembly)
49{
50 HRESULT hr = S_OK;
51 VALIDATE_ARG_RET(pIAssemblyName != nullptr && ppAssembly != nullptr);
52
53 // DevDiv #933506: Exceptions thrown during AssemblyLoadContext.Load should propagate
54 // EX_TRY
55 {
56 _ASSERTE(m_pTPABinder != NULL);
57
58 ReleaseHolder<BINDER_SPACE::Assembly> pCoreCLRFoundAssembly;
59 ReleaseHolder<AssemblyName> pAssemblyName;
60
61 SAFE_NEW(pAssemblyName, AssemblyName);
62 IF_FAIL_GO(pAssemblyName->Init(pIAssemblyName));
63
64 // When LoadContext needs to resolve an assembly reference, it will go through the following lookup order:
65 //
66 // 1) Lookup the assembly within the LoadContext itself. If assembly is found, use it.
67 // 2) Invoke the LoadContext's Load method implementation. If assembly is found, use it.
68 // 3) Lookup the assembly within TPABinder. If assembly is found, use it.
69 // 4) Invoke the LoadContext's Resolving event. If assembly is found, use it.
70 // 5) Raise exception.
71 //
72 // This approach enables a LoadContext to override assemblies that have been loaded in TPA context by loading
73 // a different (or even the same!) version.
74
75 {
76 // Step 1 - Try to find the assembly within the LoadContext.
77 hr = BindAssemblyByNameWorker(pAssemblyName, &pCoreCLRFoundAssembly);
78 if ((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) ||
79 (hr == FUSION_E_APP_DOMAIN_LOCKED) || (hr == FUSION_E_REF_DEF_MISMATCH))
80 {
81 // If we are here, one of the following is possible:
82 //
83 // 1) The assembly has not been found in the current binder's application context (i.e. it has not already been loaded), OR
84 // 2) An assembly with the same simple name was already loaded in the context of the current binder but we ran into a Ref/Def
85 // mismatch (either due to version difference or strong-name difference).
86 //
87 // Thus, if default binder has been overridden, then invoke it in an attempt to perform the binding for it make the call
88 // of what to do next. The host-overridden binder can either fail the bind or return reference to an existing assembly
89 // that has been loaded.
90 //
91 hr = AssemblyBinder::BindUsingHostAssemblyResolver(GetManagedAssemblyLoadContext(), pAssemblyName, pIAssemblyName, m_pTPABinder, &pCoreCLRFoundAssembly);
92 if (SUCCEEDED(hr))
93 {
94 // We maybe returned an assembly that was bound to a different AssemblyLoadContext instance.
95 // In such a case, we will not overwrite the binding context (which would be wrong since it would not
96 // be present in the cache of the current binding context).
97 if (pCoreCLRFoundAssembly->GetBinder() == NULL)
98 {
99 pCoreCLRFoundAssembly->SetBinder(this);
100 }
101 }
102 }
103 }
104
105 IF_FAIL_GO(hr);
106
107 // Extract the assembly reference.
108 //
109 // For TPA assemblies that were bound, TPABinder
110 // would have already set the binder reference for the assembly, so we just need to
111 // extract the reference now.
112 *ppAssembly = pCoreCLRFoundAssembly.Extract();
113Exit:;
114 }
115 // EX_CATCH_HRESULT(hr);
116
117 return hr;
118}
119
120HRESULT CLRPrivBinderAssemblyLoadContext::BindUsingPEImage( /* in */ PEImage *pPEImage,
121 /* in */ BOOL fIsNativeImage,
122 /* [retval][out] */ ICLRPrivAssembly **ppAssembly)
123{
124 HRESULT hr = S_OK;
125
126 EX_TRY
127 {
128 ReleaseHolder<BINDER_SPACE::Assembly> pCoreCLRFoundAssembly;
129 ReleaseHolder<BINDER_SPACE::AssemblyName> pAssemblyName;
130 ReleaseHolder<IMDInternalImport> pIMetaDataAssemblyImport;
131
132 PEKIND PeKind = peNone;
133
134 // Get the Metadata interface
135 DWORD dwPAFlags[2];
136 IF_FAIL_GO(BinderAcquireImport(pPEImage, &pIMetaDataAssemblyImport, dwPAFlags, fIsNativeImage));
137 IF_FAIL_GO(AssemblyBinder::TranslatePEToArchitectureType(dwPAFlags, &PeKind));
138
139 _ASSERTE(pIMetaDataAssemblyImport != NULL);
140
141 // Using the information we just got, initialize the assemblyname
142 SAFE_NEW(pAssemblyName, AssemblyName);
143 IF_FAIL_GO(pAssemblyName->Init(pIMetaDataAssemblyImport, PeKind));
144
145 // Validate architecture
146 if (!BINDER_SPACE::Assembly::IsValidArchitecture(pAssemblyName->GetArchitecture()))
147 {
148 IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_BAD_FORMAT));
149 }
150
151 // Disallow attempt to bind to the core library. Aside from that,
152 // the LoadContext can load any assembly (even if it was in a different LoadContext like TPA).
153 if (pAssemblyName->IsMscorlib())
154 {
155 IF_FAIL_GO(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
156 }
157
158 hr = AssemblyBinder::BindUsingPEImage(&m_appContext, pAssemblyName, pPEImage, PeKind, pIMetaDataAssemblyImport, &pCoreCLRFoundAssembly);
159 if (hr == S_OK)
160 {
161 _ASSERTE(pCoreCLRFoundAssembly != NULL);
162 pCoreCLRFoundAssembly->SetBinder(this);
163 *ppAssembly = pCoreCLRFoundAssembly.Extract();
164 }
165Exit:;
166 }
167 EX_CATCH_HRESULT(hr);
168
169 return hr;
170}
171
172HRESULT CLRPrivBinderAssemblyLoadContext::GetBinderID(
173 UINT_PTR *pBinderId)
174{
175 *pBinderId = reinterpret_cast<UINT_PTR>(this);
176 return S_OK;
177}
178
179HRESULT CLRPrivBinderAssemblyLoadContext::GetLoaderAllocator(LPVOID* pLoaderAllocator)
180{
181 _ASSERTE(pLoaderAllocator != NULL);
182 if (m_pAssemblyLoaderAllocator == NULL)
183 {
184 return E_FAIL;
185 }
186
187 *pLoaderAllocator = m_pAssemblyLoaderAllocator;
188 return S_OK;
189}
190
191//=============================================================================
192// Creates an instance of the AssemblyLoadContext Binder
193//
194// This method does not take a lock since it is invoked from the ctor of the
195// managed AssemblyLoadContext type.
196//=============================================================================
197/* static */
198HRESULT CLRPrivBinderAssemblyLoadContext::SetupContext(DWORD dwAppDomainId,
199 CLRPrivBinderCoreCLR *pTPABinder,
200 LoaderAllocator* pLoaderAllocator,
201 void* loaderAllocatorHandle,
202 UINT_PTR ptrAssemblyLoadContext,
203 CLRPrivBinderAssemblyLoadContext **ppBindContext)
204{
205 HRESULT hr = E_FAIL;
206 EX_TRY
207 {
208 if(ppBindContext != NULL)
209 {
210 ReleaseHolder<CLRPrivBinderAssemblyLoadContext> pBinder;
211
212 SAFE_NEW(pBinder, CLRPrivBinderAssemblyLoadContext);
213 hr = pBinder->m_appContext.Init();
214 if(SUCCEEDED(hr))
215 {
216 // Save the reference to the AppDomain in which the binder lives
217 pBinder->m_appContext.SetAppDomainId(dwAppDomainId);
218
219 // Mark that this binder can explicitly bind to native images
220 pBinder->m_appContext.SetExplicitBindToNativeImages(true);
221
222 // Save reference to the TPABinder that is required to be present.
223 _ASSERTE(pTPABinder != NULL);
224 pBinder->m_pTPABinder = pTPABinder;
225
226 // Save the reference to the IntPtr for GCHandle for the managed
227 // AssemblyLoadContext instance
228 pBinder->m_ptrManagedAssemblyLoadContext = ptrAssemblyLoadContext;
229
230 if (pLoaderAllocator != NULL)
231 {
232 // Link to LoaderAllocator, keep a reference to it
233 VERIFY(pLoaderAllocator->AddReferenceIfAlive());
234 }
235 pBinder->m_pAssemblyLoaderAllocator = pLoaderAllocator;
236 pBinder->m_loaderAllocatorHandle = loaderAllocatorHandle;
237
238#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
239 if (pLoaderAllocator != NULL)
240 {
241 ((AssemblyLoaderAllocator*)pLoaderAllocator)->RegisterBinder(pBinder);
242 }
243#endif
244 // Return reference to the allocated Binder instance
245 *ppBindContext = clr::SafeAddRef(pBinder.Extract());
246 }
247 }
248 }
249 EX_CATCH_HRESULT(hr);
250
251Exit:
252 return hr;
253}
254
255void CLRPrivBinderAssemblyLoadContext::PrepareForLoadContextRelease(INT_PTR ptrManagedStrongAssemblyLoadContext)
256{
257 CONTRACTL
258 {
259 GC_NOTRIGGER;
260 THROWS;
261 MODE_COOPERATIVE;
262 SO_TOLERANT;
263 }
264 CONTRACTL_END;
265
266 // Replace the weak handle with a strong handle so that the managed assembly load context stays alive until the
267 // CLRPrivBinderAssemblyLoadContext::ReleaseLoadContext is called.
268 OBJECTHANDLE handle = reinterpret_cast<OBJECTHANDLE>(m_ptrManagedAssemblyLoadContext);
269 OBJECTHANDLE strongHandle = reinterpret_cast<OBJECTHANDLE>(ptrManagedStrongAssemblyLoadContext);
270 DestroyShortWeakHandle(handle);
271 m_ptrManagedAssemblyLoadContext = reinterpret_cast<INT_PTR>(strongHandle);
272
273 _ASSERTE(m_pAssemblyLoaderAllocator != NULL);
274 _ASSERTE(m_loaderAllocatorHandle != NULL);
275
276 // We cannot delete the binder here as it is used indirectly when comparing assemblies with the same binder
277 // It will be deleted when the LoaderAllocator will be deleted
278 // But we can release the LoaderAllocator as we are no longer using it here
279 m_pAssemblyLoaderAllocator->Release();
280 m_pAssemblyLoaderAllocator = NULL;
281
282 // Destroy the strong handle to the LoaderAllocator in order to let it reach its finalizer
283 DestroyHandle(reinterpret_cast<OBJECTHANDLE>(m_loaderAllocatorHandle));
284 m_loaderAllocatorHandle = NULL;
285}
286
287CLRPrivBinderAssemblyLoadContext::CLRPrivBinderAssemblyLoadContext()
288{
289 m_pTPABinder = NULL;
290}
291
292void CLRPrivBinderAssemblyLoadContext::ReleaseLoadContext()
293{
294 VERIFY(m_ptrManagedAssemblyLoadContext != NULL);
295
296 // This method is called to release the strong handle on the managed AssemblyLoadContext
297 // once the Unloading event has been fired
298 OBJECTHANDLE handle = reinterpret_cast<OBJECTHANDLE>(m_ptrManagedAssemblyLoadContext);
299 DestroyHandle(handle);
300 m_ptrManagedAssemblyLoadContext = NULL;
301}
302
303#endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
304
305