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/*============================================================
6**
7** Header: AssemblySpec.cpp
8**
9** Purpose: Implements Assembly binding class
10**
11**
12
13
14**
15===========================================================*/
16
17#include "common.h"
18
19#include <stdlib.h>
20
21#include "assemblyspec.hpp"
22#include "eeconfig.h"
23#include "strongname.h"
24#include "strongnameholders.h"
25#include "mdaassistants.h"
26#include "eventtrace.h"
27
28#ifdef FEATURE_COMINTEROP
29#include "clrprivbinderutil.h"
30#include "winrthelpers.h"
31#endif
32
33#ifdef _DEBUG
34// This debug-only wrapper for LookupAssembly is solely for the use of postconditions and
35// assertions. The problem is that the real LookupAssembly can throw an OOM
36// simply because it can't allocate scratch space. For the sake of asserting,
37// we can treat those as successful lookups.
38BOOL UnsafeVerifyLookupAssembly(AssemblySpecBindingCache *pCache, AssemblySpec *pSpec, DomainAssembly *pComparator)
39{
40 STATIC_CONTRACT_NOTHROW;
41 STATIC_CONTRACT_GC_TRIGGERS;
42 STATIC_CONTRACT_FORBID_FAULT;
43
44 BOOL result = FALSE;
45
46 EX_TRY
47 {
48 SCAN_IGNORE_FAULT; // Won't go away: This wrapper exists precisely to turn an OOM here into something our postconditions can deal with.
49 result = (pComparator == pCache->LookupAssembly(pSpec));
50 }
51 EX_CATCH
52 {
53 Exception *ex = GET_EXCEPTION();
54
55 result = ex->IsTransient();
56 }
57 EX_END_CATCH(SwallowAllExceptions)
58
59 return result;
60
61}
62#endif
63
64#ifdef _DEBUG
65// This debug-only wrapper for LookupFile is solely for the use of postconditions and
66// assertions. The problem is that the real LookupFile can throw an OOM
67// simply because it can't allocate scratch space. For the sake of asserting,
68// we can treat those as successful lookups.
69BOOL UnsafeVerifyLookupFile(AssemblySpecBindingCache *pCache, AssemblySpec *pSpec, PEAssembly *pComparator)
70{
71 STATIC_CONTRACT_NOTHROW;
72 STATIC_CONTRACT_GC_TRIGGERS;
73 STATIC_CONTRACT_FORBID_FAULT;
74
75 BOOL result = FALSE;
76
77 EX_TRY
78 {
79 SCAN_IGNORE_FAULT; // Won't go away: This wrapper exists precisely to turn an OOM here into something our postconditions can deal with.
80 result = pCache->LookupFile(pSpec)->Equals(pComparator);
81 }
82 EX_CATCH
83 {
84 Exception *ex = GET_EXCEPTION();
85
86 result = ex->IsTransient();
87 }
88 EX_END_CATCH(SwallowAllExceptions)
89
90 return result;
91
92}
93
94#endif
95
96#ifdef _DEBUG
97
98// This debug-only wrapper for Contains is solely for the use of postconditions and
99// assertions. The problem is that the real Contains can throw an OOM
100// simply because it can't allocate scratch space. For the sake of asserting,
101// we can treat those as successful lookups.
102BOOL UnsafeContains(AssemblySpecBindingCache *pCache, AssemblySpec *pSpec)
103{
104 STATIC_CONTRACT_NOTHROW;
105 STATIC_CONTRACT_GC_TRIGGERS;
106 STATIC_CONTRACT_FORBID_FAULT;
107
108 BOOL result = FALSE;
109
110 EX_TRY
111 {
112 SCAN_IGNORE_FAULT; // Won't go away: This wrapper exists precisely to turn an OOM here into something our postconditions can deal with.
113 result = pCache->Contains(pSpec);
114 }
115 EX_CATCH
116 {
117 Exception *ex = GET_EXCEPTION();
118
119 result = ex->IsTransient();
120 }
121 EX_END_CATCH(SwallowAllExceptions)
122
123 return result;
124
125}
126#endif
127
128
129
130AssemblySpecHash::~AssemblySpecHash()
131{
132 CONTRACTL
133 {
134 DESTRUCTOR_CHECK;
135 NOTHROW;
136 GC_TRIGGERS;
137 MODE_ANY;
138 }
139 CONTRACTL_END;
140
141 PtrHashMap::PtrIterator i = m_map.begin();
142 while (!i.end())
143 {
144 AssemblySpec *s = (AssemblySpec*) i.GetValue();
145 if (m_pHeap != NULL)
146 s->~AssemblySpec();
147 else
148 delete s;
149
150 ++i;
151 }
152}
153
154// Check assembly name for invalid characters
155// Return value:
156// TRUE: If no invalid characters were found, or if the assembly name isn't set
157// FALSE: If invalid characters were found
158// This is needed to prevent security loopholes with ':', '/' and '\' in the assembly name
159BOOL AssemblySpec::IsValidAssemblyName()
160{
161 CONTRACTL
162 {
163 THROWS;
164 GC_TRIGGERS;
165 }
166 CONTRACTL_END;
167
168 if (GetName())
169 {
170 SString ssAssemblyName(SString::Utf8, GetName());
171 for (SString::Iterator i = ssAssemblyName.Begin(); i[0] != W('\0'); i++) {
172 switch (i[0]) {
173 case W(':'):
174 case W('\\'):
175 case W('/'):
176 return FALSE;
177
178 default:
179 break;
180 }
181 }
182 }
183 return TRUE;
184}
185
186HRESULT AssemblySpec::InitializeSpecInternal(mdToken kAssemblyToken,
187 IMDInternalImport *pImport,
188 DomainAssembly *pStaticParent,
189 BOOL fAllowAllocation)
190{
191 CONTRACTL
192 {
193 INSTANCE_CHECK;
194 if (fAllowAllocation) {GC_TRIGGERS;} else {GC_NOTRIGGER;};
195 if (fAllowAllocation) {INJECT_FAULT(COMPlusThrowOM());} else {FORBID_FAULT;};
196 NOTHROW;
197 MODE_ANY;
198 PRECONDITION(pImport->IsValidToken(kAssemblyToken));
199 PRECONDITION(TypeFromToken(kAssemblyToken) == mdtAssembly
200 || TypeFromToken(kAssemblyToken) == mdtAssemblyRef);
201 }
202 CONTRACTL_END;
203
204 HRESULT hr = S_OK;
205
206 EX_TRY
207 {
208 IfFailThrow(BaseAssemblySpec::Init(kAssemblyToken,pImport));
209
210 if (IsContentType_WindowsRuntime())
211 {
212 if (!fAllowAllocation)
213 { // We don't support this because we must be able to allocate in order to
214 // extract embedded type names for the native image scenario. Currently,
215 // the only caller of this method with fAllowAllocation == FALSE is
216 // Module::GetAssemblyIfLoaded, and since this method will only check the
217 // assembly spec cache, and since we can't cache WinRT assemblies, this
218 // limitation should have no negative impact.
219 IfFailThrow(E_FAIL);
220 }
221
222 // Extract embedded content, if present (currently used for embedded WinRT type names).
223 ParseEncodedName();
224 }
225
226 // For static binds, we cannot reference a weakly named assembly from a strong named one.
227 // (Note that this constraint doesn't apply to dynamic binds which is why this check is
228 // not farther down the stack.)
229 if (pStaticParent != NULL)
230 {
231 // We dont validate this for CoreCLR as there is no good use-case for this scenario.
232
233 SetParentAssembly(pStaticParent);
234 }
235 }
236 EX_CATCH_HRESULT(hr);
237
238 return hr;
239} // AssemblySpec::InitializeSpecInternal
240
241
242
243void AssemblySpec::InitializeSpec(PEAssembly * pFile)
244{
245 CONTRACTL
246 {
247 INSTANCE_CHECK;
248 THROWS;
249 GC_TRIGGERS;
250 MODE_ANY;
251 PRECONDITION(CheckPointer(pFile));
252 INJECT_FAULT(COMPlusThrowOM(););
253 }
254 CONTRACTL_END;
255 ReleaseHolder<IMDInternalImport> pImport(pFile->GetMDImportWithRef());
256 mdAssembly a;
257 IfFailThrow(pImport->GetAssemblyFromScope(&a));
258
259 InitializeSpec(a, pImport, NULL);
260
261#ifdef FEATURE_COMINTEROP
262 if (IsContentType_WindowsRuntime())
263 {
264 LPCSTR szNamespace;
265 LPCSTR szTypeName;
266 SString ssFakeNameSpaceAllocationBuffer;
267 IfFailThrow(::GetFirstWinRTTypeDef(pImport, &szNamespace, &szTypeName, pFile->GetPath(), &ssFakeNameSpaceAllocationBuffer));
268
269 SetWindowsRuntimeType(szNamespace, szTypeName);
270
271 // pFile is not guaranteed to stay around (it might be unloaded with the AppDomain), we have to copy the type name
272 CloneFields(WINRT_TYPE_NAME_OWNED);
273 }
274#endif //FEATURE_COMINTEROP
275
276 // Set the binding context for the AssemblySpec
277 ICLRPrivBinder* pCurrentBinder = GetBindingContext();
278 ICLRPrivBinder* pExpectedBinder = pFile->GetBindingContext();
279 if (pCurrentBinder == NULL)
280 {
281 // We should aways having the binding context in the PEAssembly. The only exception to this are the following:
282 //
283 // 1) when we are here during EEStartup and loading mscorlib.dll.
284 // 2) We are dealing with dynamic assemblies
285 _ASSERTE((pExpectedBinder != NULL) || pFile->IsSystem() || pFile->IsDynamic());
286 SetBindingContext(pExpectedBinder);
287 }
288}
289
290#ifndef CROSSGEN_COMPILE
291
292// This uses thread storage to allocate space. Please use Checkpoint and release it.
293HRESULT AssemblySpec::InitializeSpec(StackingAllocator* alloc, ASSEMBLYNAMEREF* pName,
294 BOOL fParse /*=TRUE*/)
295{
296 CONTRACTL
297 {
298 INSTANCE_CHECK;
299 THROWS;
300 MODE_COOPERATIVE;
301 GC_TRIGGERS;
302 PRECONDITION(CheckPointer(alloc));
303 PRECONDITION(CheckPointer(pName));
304 PRECONDITION(IsProtectedByGCFrame(pName));
305 INJECT_FAULT(COMPlusThrowOM(););
306 }
307 CONTRACTL_END;
308
309 // Simple name
310 if ((*pName)->GetSimpleName() != NULL) {
311 WCHAR* pString;
312 int iString;
313 ((STRINGREF) (*pName)->GetSimpleName())->RefInterpretGetStringValuesDangerousForGC(&pString, &iString);
314 DWORD lgth = WszWideCharToMultiByte(CP_UTF8, 0, pString, iString, NULL, 0, NULL, NULL);
315 if (lgth + 1 < lgth)
316 ThrowHR(E_INVALIDARG);
317 LPSTR lpName = (LPSTR) alloc->Alloc(S_UINT32(lgth) + S_UINT32(1));
318 WszWideCharToMultiByte(CP_UTF8, 0, pString, iString,
319 lpName, lgth+1, NULL, NULL);
320 lpName[lgth] = '\0';
321 // Calling Init here will trash the cached lpName in AssemblySpec, but lpName is still needed by ParseName
322 // call below.
323 SetName(lpName);
324 }
325 else
326 {
327 // Ensure we always have an assembly simple name.
328 LPSTR lpName = (LPSTR) alloc->Alloc(S_UINT32(1));
329 lpName[0] = '\0';
330 SetName(lpName);
331 }
332
333 if (fParse) {
334 HRESULT hr = ParseName();
335 // Sometimes Fusion flags invalid characters in the name, sometimes it doesn't
336 // depending on where the invalid characters are
337 // We want to Raise the assembly resolve event on all invalid characters
338 // but calling ParseName before checking for invalid characters gives Fusion a chance to
339 // parse the rest of the name (to get a public key token, etc.)
340 if ((hr == FUSION_E_INVALID_NAME) || (!IsValidAssemblyName())) {
341 // This is the only case where we do not throw on an error
342 // We don't want to throw so as to give the caller a chance to call RaiseAssemblyResolveEvent
343 // The only caller that cares is System.Reflection.Assembly.InternalLoad which calls us through
344 // AssemblyNameNative::Init
345 return FUSION_E_INVALID_NAME;
346 }
347 else
348 IfFailThrow(hr);
349 }
350 else {
351 AssemblyMetaDataInternal asmInfo;
352 // Flags
353 DWORD dwFlags = (*pName)->GetFlags();
354
355 // Version
356 VERSIONREF version = (VERSIONREF) (*pName)->GetVersion();
357 if(version == NULL) {
358 asmInfo.usMajorVersion = (USHORT)-1;
359 asmInfo.usMinorVersion = (USHORT)-1;
360 asmInfo.usBuildNumber = (USHORT)-1;
361 asmInfo.usRevisionNumber = (USHORT)-1;
362 }
363 else {
364 asmInfo.usMajorVersion = (USHORT)version->GetMajor();
365 asmInfo.usMinorVersion = (USHORT)version->GetMinor();
366 asmInfo.usBuildNumber = (USHORT)version->GetBuild();
367 asmInfo.usRevisionNumber = (USHORT)version->GetRevision();
368 }
369
370 asmInfo.szLocale = 0;
371 asmInfo.ulOS = 0;
372 asmInfo.rOS = 0;
373 asmInfo.ulProcessor = 0;
374 asmInfo.rProcessor = 0;
375
376 if ((*pName)->GetCultureInfo() != NULL)
377 {
378 struct _gc {
379 OBJECTREF cultureinfo;
380 STRINGREF pString;
381 } gc;
382
383 gc.cultureinfo = (*pName)->GetCultureInfo();
384 gc.pString = NULL;
385
386 GCPROTECT_BEGIN(gc);
387
388 MethodDescCallSite getName(METHOD__CULTURE_INFO__GET_NAME, &gc.cultureinfo);
389
390 ARG_SLOT args[] = {
391 ObjToArgSlot(gc.cultureinfo)
392 };
393 gc.pString = getName.Call_RetSTRINGREF(args);
394 if (gc.pString != NULL) {
395 WCHAR* pString;
396 int iString;
397 gc.pString->RefInterpretGetStringValuesDangerousForGC(&pString, &iString);
398 DWORD lgth = WszWideCharToMultiByte(CP_UTF8, 0, pString, iString, NULL, 0, NULL, NULL);
399 S_UINT32 lengthWillNull = S_UINT32(lgth) + S_UINT32(1);
400 LPSTR lpLocale = (LPSTR) alloc->Alloc(lengthWillNull);
401 if (lengthWillNull.IsOverflow())
402 {
403 COMPlusThrowHR(COR_E_OVERFLOW);
404 }
405 WszWideCharToMultiByte(CP_UTF8, 0, pString, iString,
406 lpLocale, lengthWillNull.Value(), NULL, NULL);
407 lpLocale[lgth] = '\0';
408 asmInfo.szLocale = lpLocale;
409 }
410 GCPROTECT_END();
411 }
412
413 // Strong name
414 DWORD cbPublicKeyOrToken=0;
415 BYTE* pbPublicKeyOrToken=NULL;
416 // Note that we prefer to take a public key token if present,
417 // even if flags indicate a full public key
418 if ((*pName)->GetPublicKeyToken() != NULL) {
419 dwFlags &= ~afPublicKey;
420 PBYTE pArray = NULL;
421 pArray = (*pName)->GetPublicKeyToken()->GetDirectPointerToNonObjectElements();
422 cbPublicKeyOrToken = (*pName)->GetPublicKeyToken()->GetNumComponents();
423 pbPublicKeyOrToken = pArray;
424 }
425 else if ((*pName)->GetPublicKey() != NULL) {
426 dwFlags |= afPublicKey;
427 PBYTE pArray = NULL;
428 pArray = (*pName)->GetPublicKey()->GetDirectPointerToNonObjectElements();
429 cbPublicKeyOrToken = (*pName)->GetPublicKey()->GetNumComponents();
430 pbPublicKeyOrToken = pArray;
431 }
432 BaseAssemblySpec::Init(GetName(),&asmInfo,pbPublicKeyOrToken,cbPublicKeyOrToken,dwFlags);
433 }
434
435 CloneFieldsToStackingAllocator(alloc);
436
437 // Hash for control
438 // <TODO>@TODO cts, can we use unsafe in this case!!!</TODO>
439 if ((*pName)->GetHashForControl() != NULL)
440 SetHashForControl((*pName)->GetHashForControl()->GetDataPtr(),
441 (*pName)->GetHashForControl()->GetNumComponents(),
442 (*pName)->GetHashAlgorithmForControl());
443
444 // Extract embedded WinRT name, if present.
445 ParseEncodedName();
446
447 return S_OK;
448}
449
450void AssemblySpec::AssemblyNameInit(ASSEMBLYNAMEREF* pAsmName, PEImage* pImageInfo)
451{
452 CONTRACTL
453 {
454 THROWS;
455 MODE_COOPERATIVE;
456 GC_TRIGGERS;
457 SO_INTOLERANT;
458 PRECONDITION(IsProtectedByGCFrame (pAsmName));
459 }
460 CONTRACTL_END;
461
462 struct _gc {
463 OBJECTREF CultureInfo;
464 STRINGREF Locale;
465 OBJECTREF Version;
466 U1ARRAYREF PublicKeyOrToken;
467 STRINGREF Name;
468 STRINGREF CodeBase;
469 } gc;
470 ZeroMemory(&gc, sizeof(gc));
471
472 GCPROTECT_BEGIN(gc);
473
474 if ((m_context.usMajorVersion != (USHORT) -1) &&
475 (m_context.usMinorVersion != (USHORT) -1)) {
476
477 MethodTable* pVersion = MscorlibBinder::GetClass(CLASS__VERSION);
478
479 // version
480 gc.Version = AllocateObject(pVersion);
481
482 // BaseAssemblySpec and AssemblyName properties store uint16 components for the version. Version and AssemblyVersion
483 // store int32 or uint32. When the former are initialized from the latter, the components are truncated to uint16 size.
484 // When the latter are initialized from the former, they are zero-extended to int32 size. For uint16 components, the max
485 // value is used to indicate an unspecified component. For int32 components, -1 is used. Since we're initializing a
486 // Version from an assembly version, map the uint16 unspecified value to the int32 size.
487 int componentCount = 2;
488 if (m_context.usBuildNumber != (USHORT)-1)
489 {
490 ++componentCount;
491 if (m_context.usRevisionNumber != (USHORT)-1)
492 {
493 ++componentCount;
494 }
495 }
496 switch (componentCount)
497 {
498 case 2:
499 {
500 // Call Version(int, int) because Version(int, int, int, int) does not allow passing the unspecified value -1
501 MethodDescCallSite ctorMethod(METHOD__VERSION__CTOR_Ix2);
502 ARG_SLOT VersionArgs[] =
503 {
504 ObjToArgSlot(gc.Version),
505 (ARG_SLOT) m_context.usMajorVersion,
506 (ARG_SLOT) m_context.usMinorVersion
507 };
508 ctorMethod.Call(VersionArgs);
509 break;
510 }
511
512 case 3:
513 {
514 // Call Version(int, int, int) because Version(int, int, int, int) does not allow passing the unspecified value -1
515 MethodDescCallSite ctorMethod(METHOD__VERSION__CTOR_Ix3);
516 ARG_SLOT VersionArgs[] =
517 {
518 ObjToArgSlot(gc.Version),
519 (ARG_SLOT) m_context.usMajorVersion,
520 (ARG_SLOT) m_context.usMinorVersion,
521 (ARG_SLOT) m_context.usBuildNumber
522 };
523 ctorMethod.Call(VersionArgs);
524 break;
525 }
526
527 default:
528 {
529 // Call Version(int, int, int, int)
530 _ASSERTE(componentCount == 4);
531 MethodDescCallSite ctorMethod(METHOD__VERSION__CTOR_Ix4);
532 ARG_SLOT VersionArgs[] =
533 {
534 ObjToArgSlot(gc.Version),
535 (ARG_SLOT) m_context.usMajorVersion,
536 (ARG_SLOT) m_context.usMinorVersion,
537 (ARG_SLOT) m_context.usBuildNumber,
538 (ARG_SLOT) m_context.usRevisionNumber
539 };
540 ctorMethod.Call(VersionArgs);
541 break;
542 }
543 }
544 }
545
546 // cultureinfo
547 if (m_context.szLocale) {
548
549 MethodTable* pCI = MscorlibBinder::GetClass(CLASS__CULTURE_INFO);
550 gc.CultureInfo = AllocateObject(pCI);
551
552 gc.Locale = StringObject::NewString(m_context.szLocale);
553
554 MethodDescCallSite strCtor(METHOD__CULTURE_INFO__STR_CTOR);
555
556 ARG_SLOT args[2] =
557 {
558 ObjToArgSlot(gc.CultureInfo),
559 ObjToArgSlot(gc.Locale)
560 };
561
562 strCtor.Call(args);
563 }
564
565 // public key or token byte array
566 if (m_pbPublicKeyOrToken)
567 {
568 gc.PublicKeyOrToken = (U1ARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_U1, m_cbPublicKeyOrToken);
569 memcpyNoGCRefs(gc.PublicKeyOrToken->m_Array, m_pbPublicKeyOrToken, m_cbPublicKeyOrToken);
570 }
571
572 // simple name
573 if(GetName())
574 gc.Name = StringObject::NewString(GetName());
575
576 if (GetCodeBase())
577 gc.CodeBase = StringObject::NewString(GetCodeBase());
578
579 BOOL fPublicKey = m_dwFlags & afPublicKey;
580
581 ULONG hashAlgId=0;
582 if (pImageInfo != NULL)
583 {
584 if(!pImageInfo->GetMDImport()->IsValidToken(TokenFromRid(1, mdtAssembly)))
585 {
586 ThrowHR(COR_E_BADIMAGEFORMAT);
587 }
588 IfFailThrow(pImageInfo->GetMDImport()->GetAssemblyProps(TokenFromRid(1, mdtAssembly), NULL, NULL, &hashAlgId, NULL, NULL, NULL));
589 }
590
591 MethodDescCallSite init(METHOD__ASSEMBLY_NAME__INIT);
592
593 ARG_SLOT MethodArgs[] =
594 {
595 ObjToArgSlot(*pAsmName),
596 ObjToArgSlot(gc.Name),
597 fPublicKey ? ObjToArgSlot(gc.PublicKeyOrToken) :
598 (ARG_SLOT) NULL, // public key
599 fPublicKey ? (ARG_SLOT) NULL :
600 ObjToArgSlot(gc.PublicKeyOrToken), // public key token
601 ObjToArgSlot(gc.Version),
602 ObjToArgSlot(gc.CultureInfo),
603 (ARG_SLOT) hashAlgId,
604 (ARG_SLOT) 1, // AssemblyVersionCompatibility.SameMachine
605 ObjToArgSlot(gc.CodeBase),
606 (ARG_SLOT) m_dwFlags,
607 (ARG_SLOT) NULL // key pair
608 };
609
610 init.Call(MethodArgs);
611
612 // Only set the processor architecture if we're looking at a newer binary that has
613 // that information in the PE, and we're not looking at a reference assembly.
614 if(pImageInfo && !pImageInfo->HasV1Metadata() && !pImageInfo->IsReferenceAssembly())
615 {
616 DWORD dwMachine, dwKind;
617
618 pImageInfo->GetPEKindAndMachine(&dwMachine,&dwKind);
619
620 MethodDescCallSite setPA(METHOD__ASSEMBLY_NAME__SET_PROC_ARCH_INDEX);
621
622 ARG_SLOT PAMethodArgs[] = {
623 ObjToArgSlot(*pAsmName),
624 (ARG_SLOT)dwMachine,
625 (ARG_SLOT)dwKind
626 };
627
628 setPA.Call(PAMethodArgs);
629 }
630
631 GCPROTECT_END();
632}
633
634// This uses thread storage to allocate space. Please use Checkpoint and release it.
635void AssemblySpec::SetCodeBase(StackingAllocator* alloc, STRINGREF *pCodeBase)
636{
637 CONTRACTL
638 {
639 INSTANCE_CHECK;
640 THROWS;
641 GC_TRIGGERS;
642 MODE_COOPERATIVE;
643 PRECONDITION(CheckPointer(pCodeBase));
644 INJECT_FAULT(COMPlusThrowOM(););
645 }
646 CONTRACTL_END;
647
648 // Codebase
649 if (pCodeBase != NULL && *pCodeBase != NULL) {
650 WCHAR* pString;
651 int iString;
652 (*pCodeBase)->RefInterpretGetStringValuesDangerousForGC(&pString, &iString);
653
654 DWORD dwCodeBase = (DWORD) iString+1;
655 m_wszCodeBase = new (alloc) WCHAR[dwCodeBase];
656 memcpy((void*)m_wszCodeBase, pString, dwCodeBase * sizeof(WCHAR));
657 }
658}
659
660#endif // CROSSGEN_COMPILE
661
662
663void AssemblySpec::MatchRetargetedPublicKeys(Assembly *pAssembly)
664{
665 CONTRACTL
666 {
667 INSTANCE_CHECK;
668 THROWS;
669 GC_TRIGGERS;
670 MODE_ANY;
671 PRECONDITION(CheckPointer(pAssembly));
672 }
673 CONTRACTL_END;
674 ThrowHR(FUSION_E_REF_DEF_MISMATCH);
675}
676
677
678// Check if the supplied assembly's public key matches up with the one in the Spec, if any
679// Throws an appropriate exception in case of a mismatch
680void AssemblySpec::MatchPublicKeys(Assembly *pAssembly)
681{
682 CONTRACTL
683 {
684 INSTANCE_CHECK;
685 THROWS;
686 GC_TRIGGERS;
687 MODE_ANY;
688 }
689 CONTRACTL_END;
690 // Check that the public keys are the same as in the AR.
691 if (IsStrongNamed()) {
692
693 const void *pbPublicKey;
694 DWORD cbPublicKey;
695 pbPublicKey = pAssembly->GetPublicKey(&cbPublicKey);
696 if (cbPublicKey == 0)
697 ThrowHR(FUSION_E_PRIVATE_ASM_DISALLOWED);
698
699 if (m_dwFlags & afPublicKey) {
700 if ((m_cbPublicKeyOrToken != cbPublicKey) ||
701 memcmp(m_pbPublicKeyOrToken, pbPublicKey, m_cbPublicKeyOrToken))
702 return MatchRetargetedPublicKeys(pAssembly);
703 }
704
705 // Ref has a token
706 else {
707 StrongNameBufferHolder<BYTE> pbStrongNameToken;
708 DWORD cbStrongNameToken;
709
710 if (!StrongNameTokenFromPublicKey((BYTE*) pbPublicKey,
711 cbPublicKey,
712 &pbStrongNameToken,
713 &cbStrongNameToken))
714 ThrowHR(StrongNameErrorInfo());
715 if ((m_cbPublicKeyOrToken != cbStrongNameToken) ||
716 memcmp(m_pbPublicKeyOrToken,
717 pbStrongNameToken,
718 cbStrongNameToken)) {
719 return MatchRetargetedPublicKeys(pAssembly);
720 }
721 }
722 }
723}
724
725
726PEAssembly *AssemblySpec::ResolveAssemblyFile(AppDomain *pDomain)
727{
728 CONTRACT(PEAssembly *)
729 {
730 INSTANCE_CHECK;
731 THROWS;
732 GC_TRIGGERS;
733 MODE_ANY;
734 POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
735 INJECT_FAULT(COMPlusThrowOM(););
736 }
737 CONTRACT_END;
738
739 // No assembly resolve on codebase binds
740 if (GetName() == NULL)
741 RETURN NULL;
742
743 Assembly *pAssembly = pDomain->RaiseAssemblyResolveEvent(this);
744
745 if (pAssembly != NULL) {
746 PEAssembly *pFile = pAssembly->GetManifestFile();
747 pFile->AddRef();
748
749 RETURN pFile;
750 }
751
752 RETURN NULL;
753}
754
755
756Assembly *AssemblySpec::LoadAssembly(FileLoadLevel targetLevel, BOOL fThrowOnFileNotFound)
757{
758 CONTRACTL
759 {
760 THROWS;
761 GC_TRIGGERS;
762 MODE_ANY;
763 }
764 CONTRACTL_END;
765
766 DomainAssembly * pDomainAssembly = LoadDomainAssembly(targetLevel, fThrowOnFileNotFound);
767 if (pDomainAssembly == NULL) {
768 _ASSERTE(!fThrowOnFileNotFound);
769 return NULL;
770 }
771 return pDomainAssembly->GetAssembly();
772}
773
774// Returns a BOOL indicating if the two Binder references point to the same
775// binder instance.
776BOOL AreSameBinderInstance(ICLRPrivBinder *pBinderA, ICLRPrivBinder *pBinderB)
777{
778 LIMITED_METHOD_CONTRACT;
779
780 BOOL fIsSameInstance = (pBinderA == pBinderB);
781
782 if (!fIsSameInstance && (pBinderA != NULL) && (pBinderB != NULL))
783 {
784 // Get the ID for the first binder
785 UINT_PTR binderIDA = 0, binderIDB = 0;
786 HRESULT hr = pBinderA->GetBinderID(&binderIDA);
787 if (SUCCEEDED(hr))
788 {
789 // Get the ID for the second binder
790 hr = pBinderB->GetBinderID(&binderIDB);
791 if (SUCCEEDED(hr))
792 {
793 fIsSameInstance = (binderIDA == binderIDB);
794 }
795 }
796 }
797
798 return fIsSameInstance;
799}
800
801ICLRPrivBinder* AssemblySpec::GetBindingContextFromParentAssembly(AppDomain *pDomain)
802{
803 CONTRACTL
804 {
805 NOTHROW;
806 GC_NOTRIGGER;
807 MODE_ANY;
808 PRECONDITION(pDomain != NULL);
809 }
810 CONTRACTL_END;
811
812 ICLRPrivBinder *pParentAssemblyBinder = NULL;
813 DomainAssembly *pParentDomainAssembly = GetParentAssembly();
814
815 if(pParentDomainAssembly != NULL)
816 {
817 // Get the PEAssembly associated with the parent's domain assembly
818 PEAssembly *pParentPEAssembly = pParentDomainAssembly->GetFile();
819
820 // ICLRPrivAssembly implements ICLRPrivBinder and thus, "is a" binder in a manner of semantics.
821 pParentAssemblyBinder = pParentPEAssembly->GetBindingContext();
822 }
823
824 if (GetPreferFallbackLoadContextBinder())
825 {
826 // If we have been asked to use the fallback load context binder (currently only supported for AssemblyLoadContext.LoadFromAssemblyName),
827 // then pretend we do not have any binder yet available.
828 _ASSERTE(GetFallbackLoadContextBinderForRequestingAssembly() != NULL);
829 pParentAssemblyBinder = NULL;
830 }
831
832 if (pParentAssemblyBinder == NULL)
833 {
834 // If the parent assembly binder is not available, then we maybe dealing with one of the following
835 // assembly scenarios:
836 //
837 // 1) Domain Neutral assembly
838 // 2) Entrypoint assembly
839 // 3) AssemblyLoadContext.LoadFromAssemblyName
840 //
841 // For (1) and (2), we will need to bind against the DefaultContext binder (aka TPA Binder). This happens
842 // below if we do not find the parent assembly binder.
843 //
844 // For (3), fetch the fallback load context binder reference.
845
846 pParentAssemblyBinder = GetFallbackLoadContextBinderForRequestingAssembly();
847 }
848
849 if (pParentAssemblyBinder != NULL)
850 {
851 CLRPrivBinderCoreCLR *pTPABinder = pDomain->GetTPABinderContext();
852 if (AreSameBinderInstance(pTPABinder, pParentAssemblyBinder))
853 {
854 // If the parent assembly is a platform (TPA) assembly, then its binding context will always be the TPABinder context. In
855 // such case, we will return the default context for binding to allow the bind to go
856 // via the custom binder context, if it was overridden. If it was not overridden, then we will get the expected
857 // TPABinder context anyways.
858 //
859 // Get the reference to the default binding context (this could be the TPABinder context or custom AssemblyLoadContext)
860 pParentAssemblyBinder = static_cast<ICLRPrivBinder*>(pDomain->GetFusionContext());
861 }
862 }
863
864#if defined(FEATURE_COMINTEROP)
865 if (!IsContentType_WindowsRuntime() && (pParentAssemblyBinder != NULL))
866 {
867 CLRPrivBinderWinRT *pWinRTBinder = pDomain->GetWinRtBinder();
868 if (AreSameBinderInstance(pWinRTBinder, pParentAssemblyBinder))
869 {
870 // We could be here when a non-WinRT assembly load is triggerred by a winmd (e.g. System.Runtime being loaded due to
871 // types being referenced from Windows.Foundation.Winmd).
872 //
873 // If the AssemblySpec does not correspond to WinRT type but our parent assembly binder is a WinRT binder,
874 // then such an assembly will not be found by the binder. In such a case, we reset our binder reference.
875 pParentAssemblyBinder = NULL;
876 }
877 }
878#endif // defined(FEATURE_COMINTEROP)
879
880 if (!pParentAssemblyBinder)
881 {
882 // We can be here when loading assemblies via the host (e.g. ICLRRuntimeHost2::ExecuteAssembly) or dealing with assemblies
883 // whose parent is a domain neutral assembly (see comment above for details).
884 //
885 // In such a case, the parent assembly (semantically) is CoreLibrary and thus, the default binding context should be
886 // used as the parent assembly binder.
887 pParentAssemblyBinder = static_cast<ICLRPrivBinder*>(pDomain->GetFusionContext());
888 }
889
890 return pParentAssemblyBinder;
891}
892
893DomainAssembly *AssemblySpec::LoadDomainAssembly(FileLoadLevel targetLevel,
894 BOOL fThrowOnFileNotFound)
895{
896 CONTRACT(DomainAssembly *)
897 {
898 INSTANCE_CHECK;
899 THROWS;
900 GC_TRIGGERS;
901 MODE_ANY;
902 POSTCONDITION((!fThrowOnFileNotFound && CheckPointer(RETVAL, NULL_OK))
903 || CheckPointer(RETVAL));
904 INJECT_FAULT(COMPlusThrowOM(););
905 }
906 CONTRACT_END;
907
908 ETWOnStartup (LoaderCatchCall_V1, LoaderCatchCallEnd_V1);
909 AppDomain* pDomain = GetAppDomain();
910
911
912 DomainAssembly *pAssembly = nullptr;
913
914 ICLRPrivBinder * pBinder = GetHostBinder();
915
916 // If no binder was explicitly set, check if parent assembly has a binder.
917 if (pBinder == nullptr)
918 {
919 pBinder = GetBindingContextFromParentAssembly(pDomain);
920 }
921
922 if ((pAssembly == nullptr) && CanUseWithBindingCache())
923 {
924 pAssembly = pDomain->FindCachedAssembly(this);
925 }
926
927 if (pAssembly)
928 {
929 pDomain->LoadDomainFile(pAssembly, targetLevel);
930 RETURN pAssembly;
931 }
932
933
934 PEAssemblyHolder pFile(pDomain->BindAssemblySpec(this, fThrowOnFileNotFound));
935 if (pFile == NULL)
936 RETURN NULL;
937
938 pAssembly = pDomain->LoadDomainAssembly(this, pFile, targetLevel);
939
940 RETURN pAssembly;
941}
942
943/* static */
944Assembly *AssemblySpec::LoadAssembly(LPCSTR pSimpleName,
945 AssemblyMetaDataInternal* pContext,
946 const BYTE * pbPublicKeyOrToken,
947 DWORD cbPublicKeyOrToken,
948 DWORD dwFlags)
949{
950 CONTRACT(Assembly *)
951 {
952 THROWS;
953 GC_TRIGGERS;
954 MODE_ANY;
955 PRECONDITION(CheckPointer(pSimpleName));
956 POSTCONDITION(CheckPointer(RETVAL));
957 INJECT_FAULT(COMPlusThrowOM(););
958 }
959 CONTRACT_END;
960
961 AssemblySpec spec;
962 IfFailThrow(spec.Init(pSimpleName, pContext,
963 pbPublicKeyOrToken, cbPublicKeyOrToken, dwFlags));
964
965 RETURN spec.LoadAssembly(FILE_LOADED);
966}
967
968/* static */
969Assembly *AssemblySpec::LoadAssembly(LPCWSTR pFilePath)
970{
971 CONTRACT(Assembly *)
972 {
973 THROWS;
974 GC_TRIGGERS;
975 MODE_ANY;
976 PRECONDITION(CheckPointer(pFilePath));
977 POSTCONDITION(CheckPointer(RETVAL));
978 INJECT_FAULT(COMPlusThrowOM(););
979 }
980 CONTRACT_END;
981
982 AssemblySpec spec;
983 spec.SetCodeBase(pFilePath);
984 RETURN spec.LoadAssembly(FILE_LOADED);
985}
986
987HRESULT AssemblySpec::CheckFriendAssemblyName()
988{
989 WRAPPER_NO_CONTRACT;
990
991 // Version, Culture, Architecture, and publickeytoken are not permitted
992 if ((m_context.usMajorVersion != (USHORT) -1) ||
993 (m_context.szLocale != NULL) ||
994 (IsAfPA_Specified(m_dwFlags)) ||
995 (IsStrongNamed() && !HasPublicKey()))
996 {
997 return META_E_CA_BAD_FRIENDS_ARGS;
998 }
999 else
1000 {
1001 return S_OK;
1002 }
1003}
1004
1005HRESULT AssemblySpec::EmitToken(
1006 IMetaDataAssemblyEmit *pEmit,
1007 mdAssemblyRef *pToken,
1008 BOOL fUsePublicKeyToken, /*=TRUE*/
1009 BOOL fMustBeBindable /*=FALSE*/)
1010{
1011 CONTRACTL
1012 {
1013 INSTANCE_CHECK;
1014 MODE_ANY;
1015 NOTHROW;
1016 GC_NOTRIGGER;
1017 INJECT_FAULT(return E_OUTOFMEMORY;);
1018 }
1019 CONTRACTL_END;
1020
1021 HRESULT hr = S_OK;
1022
1023 EX_TRY
1024 {
1025 SmallStackSString ssName;
1026 fMustBeBindable ? GetEncodedName(ssName) : GetName(ssName);
1027
1028 ASSEMBLYMETADATA AMD;
1029
1030 AMD.usMajorVersion = m_context.usMajorVersion;
1031 AMD.usMinorVersion = m_context.usMinorVersion;
1032 AMD.usBuildNumber = m_context.usBuildNumber;
1033 AMD.usRevisionNumber = m_context.usRevisionNumber;
1034
1035 if (m_context.szLocale) {
1036 AMD.cbLocale = MultiByteToWideChar(CP_UTF8, 0, m_context.szLocale, -1, NULL, 0);
1037 if(AMD.cbLocale==0)
1038 IfFailGo(HRESULT_FROM_GetLastError());
1039 AMD.szLocale = (LPWSTR) alloca(AMD.cbLocale * sizeof(WCHAR) );
1040 if(MultiByteToWideChar(CP_UTF8, 0, m_context.szLocale, -1, AMD.szLocale, AMD.cbLocale)==0)
1041 IfFailGo(HRESULT_FROM_GetLastError());
1042 }
1043 else {
1044 AMD.cbLocale = 0;
1045 AMD.szLocale = NULL;
1046 }
1047
1048 // If we've been asked to emit a public key token in the reference but we've
1049 // been given a public key then we need to generate the token now.
1050 if (m_cbPublicKeyOrToken && fUsePublicKeyToken && IsAfPublicKey(m_dwFlags)) {
1051 StrongNameBufferHolder<BYTE> pbPublicKeyToken;
1052 DWORD cbPublicKeyToken;
1053 if (!StrongNameTokenFromPublicKey(m_pbPublicKeyOrToken,
1054 m_cbPublicKeyOrToken,
1055 &pbPublicKeyToken,
1056 &cbPublicKeyToken)) {
1057 IfFailGo(StrongNameErrorInfo());
1058 }
1059
1060 hr = pEmit->DefineAssemblyRef(pbPublicKeyToken,
1061 cbPublicKeyToken,
1062 ssName.GetUnicode(),
1063 &AMD,
1064 NULL,
1065 0,
1066 m_dwFlags & ~afPublicKey,
1067 pToken);
1068 }
1069 else {
1070 hr = pEmit->DefineAssemblyRef(m_pbPublicKeyOrToken,
1071 m_cbPublicKeyOrToken,
1072 ssName.GetUnicode(),
1073 &AMD,
1074 NULL,
1075 0,
1076 m_dwFlags,
1077 pToken);
1078 }
1079
1080 hr = S_OK;
1081 ErrExit:
1082 ;
1083 }
1084 EX_CATCH_HRESULT(hr);
1085
1086 return hr;
1087}
1088
1089//===========================================================================================
1090// Constructs an AssemblySpec for the given IAssemblyName. Recognizes IAssemblyName objects
1091// that were built from WinRT AssemblySpec objects, extracts the encoded type name, and sets
1092// the type namespace and class name properties appropriately.
1093
1094void AssemblySpec::ParseEncodedName()
1095{
1096 CONTRACTL {
1097 THROWS;
1098 GC_NOTRIGGER;
1099 MODE_ANY;
1100 } CONTRACTL_END
1101
1102#ifdef FEATURE_COMINTEROP
1103 if (IsContentType_WindowsRuntime())
1104 {
1105 StackSString ssEncodedName(SString::Utf8, m_pAssemblyName);
1106 ssEncodedName.Normalize();
1107
1108 SString::Iterator itBang = ssEncodedName.Begin();
1109 if (ssEncodedName.Find(itBang, SL(W("!"))))
1110 {
1111 StackSString ssAssemblyName(ssEncodedName, ssEncodedName.Begin(), itBang - ssEncodedName.Begin());
1112 StackSString ssTypeName(ssEncodedName, ++itBang, ssEncodedName.End() - itBang);
1113 SetName(ssAssemblyName);
1114 SetWindowsRuntimeType(ssTypeName);
1115 }
1116 }
1117#endif
1118}
1119
1120void AssemblySpec::SetWindowsRuntimeType(
1121 LPCUTF8 szNamespace,
1122 LPCUTF8 szClassName)
1123{
1124 CONTRACTL
1125 {
1126 THROWS;
1127 GC_NOTRIGGER;
1128 MODE_ANY;
1129 }
1130 CONTRACTL_END;
1131#ifdef FEATURE_COMINTEROP
1132 // Release already allocated string
1133 if (m_ownedFlags & WINRT_TYPE_NAME_OWNED)
1134 {
1135 if (m_szWinRtTypeNamespace != nullptr)
1136 delete [] m_szWinRtTypeNamespace;
1137 if (m_szWinRtTypeClassName != nullptr)
1138 delete [] m_szWinRtTypeClassName;
1139 }
1140 m_szWinRtTypeNamespace = szNamespace;
1141 m_szWinRtTypeClassName = szClassName;
1142
1143 m_ownedFlags &= ~WINRT_TYPE_NAME_OWNED;
1144#else
1145 // Classic (non-phone) CoreCLR does not support WinRT interop; this should never be called with a non-empty type name
1146 _ASSERTE((szNamespace == NULL) && (szClassName == NULL));
1147#endif
1148}
1149
1150void AssemblySpec::SetWindowsRuntimeType(
1151 SString const & _ssTypeName)
1152{
1153 CONTRACTL
1154 {
1155 THROWS;
1156 GC_NOTRIGGER;
1157 MODE_ANY;
1158 }
1159 CONTRACTL_END;
1160
1161 // Release already allocated string
1162 if (m_ownedFlags & WINRT_TYPE_NAME_OWNED)
1163 {
1164 if (m_szWinRtTypeNamespace != nullptr)
1165 delete[] m_szWinRtTypeNamespace;
1166 if (m_szWinRtTypeClassName != nullptr)
1167 delete[] m_szWinRtTypeClassName;
1168 m_ownedFlags &= ~WINRT_TYPE_NAME_OWNED;
1169 }
1170
1171 SString ssTypeName;
1172 _ssTypeName.ConvertToUTF8(ssTypeName);
1173
1174 LPUTF8 szTypeName = (LPUTF8)ssTypeName.GetUTF8NoConvert();
1175 ns::SplitInline(szTypeName, m_szWinRtTypeNamespace, m_szWinRtTypeClassName);
1176 m_ownedFlags &= ~WINRT_TYPE_NAME_OWNED;
1177 // Make a copy of the type name strings
1178 CloneFields(WINRT_TYPE_NAME_OWNED);
1179}
1180
1181
1182AssemblySpecBindingCache::AssemblySpecBindingCache()
1183{
1184 LIMITED_METHOD_CONTRACT;
1185}
1186
1187AssemblySpecBindingCache::~AssemblySpecBindingCache()
1188{
1189 CONTRACTL
1190 {
1191 DESTRUCTOR_CHECK;
1192 NOTHROW;
1193 GC_TRIGGERS;
1194 MODE_ANY;
1195 }
1196 CONTRACTL_END;
1197
1198 Clear();
1199}
1200
1201void AssemblySpecBindingCache::Clear()
1202{
1203 CONTRACTL
1204 {
1205 DESTRUCTOR_CHECK;
1206 NOTHROW;
1207 GC_TRIGGERS;
1208 MODE_ANY;
1209 }
1210 CONTRACTL_END;
1211
1212 PtrHashMap::PtrIterator i = m_map.begin();
1213 while (!i.end())
1214 {
1215 AssemblyBinding *b = (AssemblyBinding*) i.GetValue();
1216 if (m_pHeap == NULL)
1217 delete b;
1218 else
1219 b->~AssemblyBinding();
1220
1221 ++i;
1222 }
1223
1224 m_map.Clear();
1225}
1226
1227void AssemblySpecBindingCache::OnAppDomainUnload()
1228{
1229 CONTRACTL
1230 {
1231 DESTRUCTOR_CHECK;
1232 NOTHROW;
1233 GC_TRIGGERS;
1234 MODE_ANY;
1235 }
1236 CONTRACTL_END;
1237
1238 PtrHashMap::PtrIterator i = m_map.begin();
1239 while (!i.end())
1240 {
1241 AssemblyBinding *b = (AssemblyBinding*) i.GetValue();
1242 b->OnAppDomainUnload();
1243
1244 ++i;
1245 }
1246}
1247
1248void AssemblySpecBindingCache::Init(CrstBase *pCrst, LoaderHeap *pHeap)
1249{
1250 WRAPPER_NO_CONTRACT;
1251
1252 LockOwner lock = {pCrst, IsOwnerOfCrst};
1253 m_map.Init(INITIAL_ASM_SPEC_HASH_SIZE, CompareSpecs, TRUE, &lock);
1254 m_pHeap = pHeap;
1255}
1256
1257AssemblySpecBindingCache::AssemblyBinding* AssemblySpecBindingCache::LookupInternal(AssemblySpec* pSpec, BOOL fThrow)
1258{
1259 CONTRACTL
1260 {
1261 if (fThrow)
1262 {
1263 THROWS;
1264 GC_TRIGGERS;
1265 INJECT_FAULT(COMPlusThrowOM(););
1266 }
1267 else
1268 {
1269 GC_NOTRIGGER;
1270 NOTHROW;
1271 FORBID_FAULT;
1272 }
1273 MODE_ANY;
1274 PRECONDITION(pSpec != NULL);
1275 }
1276 CONTRACTL_END;
1277
1278 UPTR key = (UPTR)pSpec->Hash();
1279 UPTR lookupKey = key;
1280
1281 // On CoreCLR, we will use the BinderID as the key
1282 ICLRPrivBinder *pBinderContextForLookup = NULL;
1283 AppDomain *pSpecDomain = pSpec->GetAppDomain();
1284 bool fGetBindingContextFromParent = true;
1285
1286 // Check if the AssemblySpec already has specified its binding context. This will be set for assemblies that are
1287 // attempted to be explicitly bound using AssemblyLoadContext LoadFrom* methods.
1288 if(!pSpec->IsAssemblySpecForMscorlib())
1289 pBinderContextForLookup = pSpec->GetBindingContext();
1290 else
1291 {
1292 // For System.Private.Corelib Binding context is either not set or if set then it should be TPA
1293 _ASSERTE(pSpec->GetBindingContext() == NULL || pSpec->GetBindingContext() == pSpecDomain->GetFusionContext());
1294 }
1295
1296 if (pBinderContextForLookup != NULL)
1297 {
1298 // We are working with the actual binding context in which the assembly was expected to be loaded.
1299 // Thus, we don't need to get it from the parent assembly.
1300 fGetBindingContextFromParent = false;
1301 }
1302
1303 if (fGetBindingContextFromParent)
1304 {
1305 // MScorlib does not have a binding context associated with it and its lookup will only be done
1306 // using its AssemblySpec hash.
1307 if (!pSpec->IsAssemblySpecForMscorlib())
1308 {
1309 pBinderContextForLookup = pSpec->GetBindingContextFromParentAssembly(pSpecDomain);
1310 pSpec->SetBindingContext(pBinderContextForLookup);
1311 }
1312 }
1313
1314 if (pBinderContextForLookup)
1315 {
1316 UINT_PTR binderID = 0;
1317 HRESULT hr = pBinderContextForLookup->GetBinderID(&binderID);
1318 _ASSERTE(SUCCEEDED(hr));
1319 lookupKey = key^binderID;
1320 }
1321
1322 AssemblyBinding* pEntry = (AssemblyBinding *)m_map.LookupValue(lookupKey, pSpec);
1323
1324 // Reset the binding context if one was originally never present in the AssemblySpec and we didnt find any entry
1325 // in the cache.
1326 if (fGetBindingContextFromParent)
1327 {
1328 if (pEntry == (AssemblyBinding *) INVALIDENTRY)
1329 {
1330 pSpec->SetBindingContext(NULL);
1331 }
1332 }
1333
1334 return pEntry;
1335}
1336
1337BOOL AssemblySpecBindingCache::Contains(AssemblySpec *pSpec)
1338{
1339 WRAPPER_NO_CONTRACT;
1340 return (LookupInternal(pSpec, TRUE) != (AssemblyBinding *) INVALIDENTRY);
1341}
1342
1343DomainAssembly *AssemblySpecBindingCache::LookupAssembly(AssemblySpec *pSpec,
1344 BOOL fThrow /*=TRUE*/)
1345{
1346 CONTRACT(DomainAssembly *)
1347 {
1348 INSTANCE_CHECK;
1349 if (fThrow) {
1350 GC_TRIGGERS;
1351 THROWS;
1352 INJECT_FAULT(COMPlusThrowOM(););
1353 }
1354 else {
1355 GC_NOTRIGGER;
1356 NOTHROW;
1357 FORBID_FAULT;
1358 }
1359 MODE_ANY;
1360 POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
1361 }
1362 CONTRACT_END;
1363
1364 AssemblyBinding *entry = (AssemblyBinding *) INVALIDENTRY;
1365
1366 entry = LookupInternal(pSpec, fThrow);
1367
1368 if (entry == (AssemblyBinding *) INVALIDENTRY)
1369 RETURN NULL;
1370 else
1371 {
1372 if ((entry->GetAssembly() == NULL) && fThrow)
1373 {
1374 // May be either unloaded, or an exception occurred.
1375 entry->ThrowIfError();
1376 }
1377
1378 RETURN entry->GetAssembly();
1379 }
1380}
1381
1382PEAssembly *AssemblySpecBindingCache::LookupFile(AssemblySpec *pSpec, BOOL fThrow /*=TRUE*/)
1383{
1384 CONTRACT(PEAssembly *)
1385 {
1386 INSTANCE_CHECK;
1387 if (fThrow) {
1388 GC_TRIGGERS;
1389 THROWS;
1390 INJECT_FAULT(COMPlusThrowOM(););
1391 }
1392 else {
1393 GC_NOTRIGGER;
1394 NOTHROW;
1395 FORBID_FAULT;
1396 }
1397 MODE_ANY;
1398 POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
1399 }
1400 CONTRACT_END;
1401
1402 AssemblyBinding *entry = (AssemblyBinding *) INVALIDENTRY;
1403 entry = LookupInternal(pSpec, fThrow);
1404
1405 if (entry == (AssemblyBinding *) INVALIDENTRY)
1406 RETURN NULL;
1407 else
1408 {
1409 if (fThrow && (entry->GetFile() == NULL))
1410 {
1411 CONSISTENCY_CHECK(entry->IsError());
1412 entry->ThrowIfError();
1413 }
1414
1415 RETURN entry->GetFile();
1416 }
1417}
1418
1419
1420class AssemblyBindingHolder
1421{
1422public:
1423 AssemblyBindingHolder()
1424 {
1425 LIMITED_METHOD_CONTRACT;
1426 m_entry = NULL;
1427 m_pHeap = NULL;
1428 }
1429
1430 AssemblySpecBindingCache::AssemblyBinding *CreateAssemblyBinding(LoaderHeap *pHeap)
1431 {
1432 CONTRACTL
1433 {
1434 THROWS;
1435 GC_TRIGGERS;
1436 INJECT_FAULT(COMPlusThrowOM(););
1437 }
1438 CONTRACTL_END
1439
1440 m_pHeap = pHeap;
1441 if (pHeap)
1442 {
1443 m_entry = new (m_amTracker.Track(pHeap->AllocMem(S_SIZE_T(sizeof(AssemblySpecBindingCache::AssemblyBinding))))) AssemblySpecBindingCache::AssemblyBinding;
1444 }
1445 else
1446 {
1447 m_entry = new AssemblySpecBindingCache::AssemblyBinding;
1448 }
1449 return m_entry;
1450 }
1451
1452 ~AssemblyBindingHolder()
1453 {
1454 CONTRACTL
1455 {
1456 NOTHROW;
1457 GC_TRIGGERS;
1458 FORBID_FAULT;
1459 }
1460 CONTRACTL_END
1461
1462 if (m_entry)
1463 {
1464 if (m_pHeap)
1465 {
1466 // just call destructor - m_amTracker will delete the memory for m_entry itself.
1467 m_entry->~AssemblyBinding();
1468 }
1469 else
1470 {
1471 delete m_entry;
1472 }
1473 }
1474 }
1475
1476 void SuppressRelease()
1477 {
1478 LIMITED_METHOD_CONTRACT;
1479 m_entry = NULL;
1480 m_pHeap = NULL;
1481 m_amTracker.SuppressRelease();
1482 }
1483
1484 AllocMemTracker *GetPamTracker()
1485 {
1486 LIMITED_METHOD_CONTRACT;
1487 return &m_amTracker;
1488 }
1489
1490
1491
1492private:
1493 AssemblySpecBindingCache::AssemblyBinding *m_entry;
1494 LoaderHeap *m_pHeap;
1495 AllocMemTracker m_amTracker;
1496};
1497
1498// NOTE ABOUT STATE OF CACHE ENTRIES:
1499//
1500// A cache entry can be in one of 4 states:
1501// 1. Empty (no entry)
1502// 2. File (a PEAssembly has been bound, but not yet an Assembly)
1503// 3. Assembly (Both a PEAssembly & Assembly are available.)
1504// 4. Error (an error has occurred)
1505//
1506// The legal state transitions are:
1507// 1 -> any
1508// 2 -> 3
1509// 2 -> 4
1510
1511
1512BOOL AssemblySpecBindingCache::StoreAssembly(AssemblySpec *pSpec, DomainAssembly *pAssembly)
1513{
1514 CONTRACT(BOOL)
1515 {
1516 INSTANCE_CHECK;
1517 THROWS;
1518 GC_TRIGGERS;
1519 MODE_ANY;
1520 // Host binder based assembly spec's cannot currently be safely inserted into caches.
1521 PRECONDITION(pSpec->GetHostBinder() == nullptr);
1522 POSTCONDITION(UnsafeContains(this, pSpec));
1523 POSTCONDITION(UnsafeVerifyLookupAssembly(this, pSpec, pAssembly));
1524 INJECT_FAULT(COMPlusThrowOM(););
1525 }
1526 CONTRACT_END;
1527
1528 UPTR key = (UPTR)pSpec->Hash();
1529
1530 // On CoreCLR, we will use the BinderID as the key
1531 ICLRPrivBinder* pBinderContextForLookup = pAssembly->GetFile()->GetBindingContext();
1532
1533 _ASSERTE(pBinderContextForLookup || pAssembly->GetFile()->IsSystem());
1534 if (pBinderContextForLookup)
1535 {
1536 UINT_PTR binderID = 0;
1537 HRESULT hr = pBinderContextForLookup->GetBinderID(&binderID);
1538 _ASSERTE(SUCCEEDED(hr));
1539 key = key^binderID;
1540
1541 if (!pSpec->GetBindingContext())
1542 {
1543 pSpec->SetBindingContext(pBinderContextForLookup);
1544 }
1545 }
1546
1547 AssemblyBinding *entry = (AssemblyBinding *) m_map.LookupValue(key, pSpec);
1548
1549 if (entry == (AssemblyBinding *) INVALIDENTRY)
1550 {
1551 AssemblyBindingHolder abHolder;
1552
1553 LoaderHeap* pHeap = m_pHeap;
1554 if (pAssembly->IsCollectible())
1555 {
1556 pHeap = pAssembly->GetLoaderAllocator()->GetHighFrequencyHeap();
1557 }
1558
1559 entry = abHolder.CreateAssemblyBinding(pHeap);
1560 entry->Init(pSpec,pAssembly->GetFile(),pAssembly,NULL,pHeap, abHolder.GetPamTracker());
1561
1562 m_map.InsertValue(key, entry);
1563
1564 abHolder.SuppressRelease();
1565
1566 STRESS_LOG2(LF_CLASSLOADER,LL_INFO10,"StoreFile (StoreAssembly): Add cached entry (%p) with PEFile %p",entry,pAssembly->GetFile());
1567 RETURN TRUE;
1568 }
1569 else
1570 {
1571 if (!entry->IsError())
1572 {
1573 if (entry->GetAssembly() != NULL)
1574 {
1575 // OK if this is a duplicate
1576 if (entry->GetAssembly() == pAssembly)
1577 RETURN TRUE;
1578 }
1579 else
1580 {
1581 // OK if we have have a matching PEAssembly
1582 if (entry->GetFile() != NULL
1583 && pAssembly->GetFile()->Equals(entry->GetFile()))
1584 {
1585 entry->SetAssembly(pAssembly);
1586 RETURN TRUE;
1587 }
1588 }
1589 }
1590
1591 // Invalid cache transition (see above note about state transitions)
1592 RETURN FALSE;
1593 }
1594}
1595
1596// Note that this routine may be called outside a lock, so may be racing with another thread.
1597// Returns TRUE if add was successful - if FALSE is returned, caller should honor current
1598// cached value to ensure consistency.
1599
1600BOOL AssemblySpecBindingCache::StoreFile(AssemblySpec *pSpec, PEAssembly *pFile)
1601{
1602 CONTRACT(BOOL)
1603 {
1604 INSTANCE_CHECK;
1605 THROWS;
1606 GC_TRIGGERS;
1607 MODE_ANY;
1608 // Host binder based assembly spec's cannot currently be safely inserted into caches.
1609 PRECONDITION(pSpec->GetHostBinder() == nullptr);
1610 POSTCONDITION((!RETVAL) || (UnsafeContains(this, pSpec) && UnsafeVerifyLookupFile(this, pSpec, pFile)));
1611 INJECT_FAULT(COMPlusThrowOM(););
1612 }
1613 CONTRACT_END;
1614
1615 UPTR key = (UPTR)pSpec->Hash();
1616
1617 // On CoreCLR, we will use the BinderID as the key
1618 ICLRPrivBinder* pBinderContextForLookup = pFile->GetBindingContext();
1619
1620 _ASSERTE(pBinderContextForLookup || pFile->IsSystem());
1621 if (pBinderContextForLookup)
1622 {
1623 UINT_PTR binderID = 0;
1624 HRESULT hr = pBinderContextForLookup->GetBinderID(&binderID);
1625 _ASSERTE(SUCCEEDED(hr));
1626 key = key^binderID;
1627
1628 if (!pSpec->GetBindingContext())
1629 {
1630 pSpec->SetBindingContext(pBinderContextForLookup);
1631 }
1632 }
1633
1634 AssemblyBinding *entry = (AssemblyBinding *) m_map.LookupValue(key, pSpec);
1635
1636 if (entry == (AssemblyBinding *) INVALIDENTRY)
1637 {
1638 AssemblyBindingHolder abHolder;
1639
1640 LoaderHeap* pHeap = m_pHeap;
1641
1642#ifndef CROSSGEN_COMPILE
1643 if (pBinderContextForLookup != NULL)
1644 {
1645 LoaderAllocator* pLoaderAllocator = NULL;
1646
1647 // Assemblies loaded with AssemblyLoadContext need to use a different heap if
1648 // marked as collectible
1649 if (SUCCEEDED(pBinderContextForLookup->GetLoaderAllocator((LPVOID*)&pLoaderAllocator)))
1650 {
1651 _ASSERTE(pLoaderAllocator != NULL);
1652 pHeap = pLoaderAllocator->GetHighFrequencyHeap();
1653 }
1654 }
1655#endif // !CROSSGEN_COMPILE
1656
1657 entry = abHolder.CreateAssemblyBinding(pHeap);
1658
1659 entry->Init(pSpec,pFile,NULL,NULL,pHeap, abHolder.GetPamTracker());
1660
1661 m_map.InsertValue(key, entry);
1662 abHolder.SuppressRelease();
1663
1664 STRESS_LOG2(LF_CLASSLOADER,LL_INFO10,"StoreFile: Add cached entry (%p) with PEFile %p\n", entry, pFile);
1665
1666 RETURN TRUE;
1667 }
1668 else
1669 {
1670 if (!entry->IsError())
1671 {
1672 // OK if this is a duplicate
1673 if (entry->GetFile() != NULL
1674 && pFile->Equals(entry->GetFile()))
1675 RETURN TRUE;
1676 }
1677 else
1678 if (entry->IsPostBindError())
1679 {
1680 // Another thread has reported what's going to happen later.
1681 entry->ThrowIfError();
1682
1683 }
1684 STRESS_LOG2(LF_CLASSLOADER,LL_INFO10,"Incompatible cached entry found (%p) when adding PEFile %p\n", entry, pFile);
1685 // Invalid cache transition (see above note about state transitions)
1686 RETURN FALSE;
1687 }
1688}
1689
1690BOOL AssemblySpecBindingCache::StoreException(AssemblySpec *pSpec, Exception* pEx)
1691{
1692 CONTRACT(BOOL)
1693 {
1694 INSTANCE_CHECK;
1695 THROWS;
1696 GC_TRIGGERS;
1697 MODE_ANY;
1698 // Host binder based assembly spec's cannot currently be safely inserted into caches.
1699 PRECONDITION(pSpec->GetHostBinder() == nullptr);
1700 DISABLED(POSTCONDITION(UnsafeContains(this, pSpec))); //<TODO>@todo: Getting violations here - StoreExceptions could happen anywhere so this is possibly too aggressive.</TODO>
1701 INJECT_FAULT(COMPlusThrowOM(););
1702 }
1703 CONTRACT_END;
1704
1705 UPTR key = (UPTR)pSpec->Hash();
1706
1707 AssemblyBinding *entry = LookupInternal(pSpec, TRUE);
1708 if (entry == (AssemblyBinding *) INVALIDENTRY)
1709 {
1710 // TODO: Merge this with the failure lookup in the binder
1711 //
1712 // Since no entry was found for this assembly in any binding context, save the failure
1713 // in the TPABinder context
1714 ICLRPrivBinder* pBinderToSaveException = NULL;
1715 pBinderToSaveException = pSpec->GetBindingContext();
1716 if (pBinderToSaveException == NULL)
1717 {
1718 if (!pSpec->IsAssemblySpecForMscorlib())
1719 {
1720 pBinderToSaveException = pSpec->GetBindingContextFromParentAssembly(pSpec->GetAppDomain());
1721 UINT_PTR binderID = 0;
1722 HRESULT hr = pBinderToSaveException->GetBinderID(&binderID);
1723 _ASSERTE(SUCCEEDED(hr));
1724 key = key^binderID;
1725 }
1726 }
1727 }
1728
1729 if (entry == (AssemblyBinding *) INVALIDENTRY) {
1730 AssemblyBindingHolder abHolder;
1731 entry = abHolder.CreateAssemblyBinding(m_pHeap);
1732
1733 entry->Init(pSpec,NULL,NULL,pEx,m_pHeap, abHolder.GetPamTracker());
1734
1735 m_map.InsertValue(key, entry);
1736 abHolder.SuppressRelease();
1737
1738 STRESS_LOG2(LF_CLASSLOADER,LL_INFO10,"StoreFile (StoreException): Add cached entry (%p) with exception %p",entry,pEx);
1739 RETURN TRUE;
1740 }
1741 else
1742 {
1743 // OK if this is a duplicate
1744 if (entry->IsError())
1745 {
1746 if (entry->GetHR() == pEx->GetHR())
1747 RETURN TRUE;
1748 }
1749 else
1750 {
1751 // OK to transition to error if we don't have an Assembly yet
1752 if (entry->GetAssembly() == NULL)
1753 {
1754 entry->InitException(pEx);
1755 RETURN TRUE;
1756 }
1757 }
1758
1759 // Invalid cache transition (see above note about state transitions)
1760 RETURN FALSE;
1761 }
1762}
1763
1764BOOL AssemblySpecBindingCache::RemoveAssembly(DomainAssembly* pAssembly)
1765{
1766 CONTRACT(BOOL)
1767 {
1768 INSTANCE_CHECK;
1769 NOTHROW;
1770 GC_TRIGGERS;
1771 MODE_ANY;
1772 PRECONDITION(pAssembly != NULL);
1773 }
1774 CONTRACT_END;
1775 BOOL result = FALSE;
1776 PtrHashMap::PtrIterator i = m_map.begin();
1777 while (!i.end())
1778 {
1779 AssemblyBinding* entry = (AssemblyBinding*)i.GetValue();
1780 if (entry->GetAssembly() == pAssembly)
1781 {
1782 UPTR key = i.GetKey();
1783 m_map.DeleteValue(key, entry);
1784
1785 if (m_pHeap == NULL)
1786 delete entry;
1787 else
1788 entry->~AssemblyBinding();
1789
1790 result = TRUE;
1791 }
1792 ++i;
1793 }
1794
1795 RETURN result;
1796}
1797
1798/* static */
1799BOOL AssemblySpecHash::CompareSpecs(UPTR u1, UPTR u2)
1800{
1801 // the same...
1802 WRAPPER_NO_CONTRACT;
1803 return AssemblySpecBindingCache::CompareSpecs(u1,u2);
1804}
1805
1806/* static */
1807BOOL AssemblySpecBindingCache::CompareSpecs(UPTR u1, UPTR u2)
1808{
1809 WRAPPER_NO_CONTRACT;
1810 AssemblySpec *a1 = (AssemblySpec *) (u1 << 1);
1811 AssemblySpec *a2 = (AssemblySpec *) u2;
1812
1813 return a1->CompareEx(a2);
1814}
1815
1816/* static */
1817BOOL DomainAssemblyCache::CompareBindingSpec(UPTR spec1, UPTR spec2)
1818{
1819 WRAPPER_NO_CONTRACT;
1820
1821 AssemblySpec* pSpec1 = (AssemblySpec*) (spec1 << 1);
1822 AssemblyEntry* pEntry2 = (AssemblyEntry*) spec2;
1823
1824 return pSpec1->CompareEx(&pEntry2->spec);
1825}
1826
1827DomainAssemblyCache::AssemblyEntry* DomainAssemblyCache::LookupEntry(AssemblySpec* pSpec)
1828{
1829 CONTRACT (DomainAssemblyCache::AssemblyEntry*)
1830 {
1831 INSTANCE_CHECK;
1832 THROWS;
1833 GC_TRIGGERS;
1834 MODE_ANY;
1835 POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
1836 INJECT_FAULT(COMPlusThrowOM(););
1837 }
1838 CONTRACT_END
1839
1840 DWORD hashValue = pSpec->Hash();
1841
1842 LPVOID pResult = m_Table.LookupValue(hashValue, pSpec);
1843 if(pResult == (LPVOID) INVALIDENTRY)
1844 RETURN NULL;
1845 else
1846 RETURN (AssemblyEntry*) pResult;
1847}
1848
1849VOID DomainAssemblyCache::InsertEntry(AssemblySpec* pSpec, LPVOID pData1, LPVOID pData2/*=NULL*/)
1850{
1851 CONTRACTL
1852 {
1853 INSTANCE_CHECK;
1854 THROWS;
1855 GC_TRIGGERS;
1856 MODE_ANY;
1857 INJECT_FAULT(COMPlusThrowOM(););
1858 }
1859 CONTRACTL_END
1860
1861 LPVOID ptr = LookupEntry(pSpec);
1862 if(ptr == NULL) {
1863
1864 BaseDomain::CacheLockHolder lh(m_pDomain);
1865
1866 ptr = LookupEntry(pSpec);
1867 if(ptr == NULL) {
1868 AllocMemTracker amTracker;
1869 AllocMemTracker *pamTracker = &amTracker;
1870
1871 AssemblyEntry* pEntry = (AssemblyEntry*) pamTracker->Track( m_pDomain->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(AssemblyEntry))) );
1872 new (&pEntry->spec) AssemblySpec ();
1873
1874 pEntry->spec.CopyFrom(pSpec);
1875 pEntry->spec.CloneFieldsToLoaderHeap(AssemblySpec::ALL_OWNED, m_pDomain->GetLowFrequencyHeap(), pamTracker);
1876 pEntry->pData[0] = pData1;
1877 pEntry->pData[1] = pData2;
1878 DWORD hashValue = pEntry->Hash();
1879 m_Table.InsertValue(hashValue, pEntry);
1880
1881 pamTracker->SuppressRelease();
1882 }
1883 // lh goes out of scope here
1884 }
1885#ifdef _DEBUG
1886 else {
1887 _ASSERTE(pData1 == ((AssemblyEntry*) ptr)->pData[0]);
1888 _ASSERTE(pData2 == ((AssemblyEntry*) ptr)->pData[1]);
1889 }
1890#endif
1891
1892}
1893
1894
1895
1896
1897DomainAssembly * AssemblySpec::GetParentAssembly()
1898{
1899 LIMITED_METHOD_CONTRACT;
1900 return m_pParentAssembly;
1901}
1902