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 | |
13 | using namespace BINDER_SPACE; |
14 | |
15 | // ============================================================================ |
16 | // CLRPrivBinderAssemblyLoadContext implementation |
17 | // ============================================================================ |
18 | HRESULT 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 | |
47 | HRESULT 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(); |
113 | Exit:; |
114 | } |
115 | // EX_CATCH_HRESULT(hr); |
116 | |
117 | return hr; |
118 | } |
119 | |
120 | HRESULT 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 | } |
165 | Exit:; |
166 | } |
167 | EX_CATCH_HRESULT(hr); |
168 | |
169 | return hr; |
170 | } |
171 | |
172 | HRESULT CLRPrivBinderAssemblyLoadContext::GetBinderID( |
173 | UINT_PTR *pBinderId) |
174 | { |
175 | *pBinderId = reinterpret_cast<UINT_PTR>(this); |
176 | return S_OK; |
177 | } |
178 | |
179 | HRESULT 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 */ |
198 | HRESULT 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 | |
251 | Exit: |
252 | return hr; |
253 | } |
254 | |
255 | void 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 | |
287 | CLRPrivBinderAssemblyLoadContext::CLRPrivBinderAssemblyLoadContext() |
288 | { |
289 | m_pTPABinder = NULL; |
290 | } |
291 | |
292 | void 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 | |