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 | // DomainFile.cpp |
6 | // |
7 | |
8 | // -------------------------------------------------------------------------------- |
9 | |
10 | |
11 | #include "common.h" |
12 | |
13 | // -------------------------------------------------------------------------------- |
14 | // Headers |
15 | // -------------------------------------------------------------------------------- |
16 | |
17 | #include <shlwapi.h> |
18 | |
19 | #include "invokeutil.h" |
20 | #include "eeconfig.h" |
21 | #include "dynamicmethod.h" |
22 | #include "field.h" |
23 | #include "dbginterface.h" |
24 | #include "eventtrace.h" |
25 | |
26 | #ifdef FEATURE_PREJIT |
27 | #include <corcompile.h> |
28 | #include "compile.h" |
29 | #endif // FEATURE_PREJIT |
30 | |
31 | #include "dllimportcallback.h" |
32 | #include "peimagelayout.inl" |
33 | |
34 | #include "winrthelpers.h" |
35 | |
36 | #ifdef FEATURE_PERFMAP |
37 | #include "perfmap.h" |
38 | #endif // FEATURE_PERFMAP |
39 | |
40 | #ifndef DACCESS_COMPILE |
41 | DomainFile::DomainFile(AppDomain *pDomain, PEFile *pFile) |
42 | : m_pDomain(pDomain), |
43 | m_pFile(pFile), |
44 | m_pOriginalFile(NULL), |
45 | m_pModule(NULL), |
46 | m_level(FILE_LOAD_CREATE), |
47 | m_pError(NULL), |
48 | m_notifyflags(NOT_NOTIFIED), |
49 | m_loading(TRUE), |
50 | m_pDynamicMethodTable(NULL), |
51 | m_pUMThunkHash(NULL), |
52 | m_bDisableActivationCheck(FALSE), |
53 | m_dwReasonForRejectingNativeImage(0) |
54 | { |
55 | CONTRACTL |
56 | { |
57 | CONSTRUCTOR_CHECK; |
58 | THROWS; // From CreateHandle |
59 | GC_NOTRIGGER; |
60 | MODE_ANY; |
61 | FORBID_FAULT; |
62 | } |
63 | CONTRACTL_END; |
64 | |
65 | m_hExposedModuleObject = NULL; |
66 | pFile->AddRef(); |
67 | } |
68 | |
69 | DomainFile::~DomainFile() |
70 | { |
71 | CONTRACTL |
72 | { |
73 | DESTRUCTOR_CHECK; |
74 | NOTHROW; |
75 | GC_TRIGGERS; |
76 | MODE_ANY; |
77 | } |
78 | CONTRACTL_END; |
79 | |
80 | m_pFile->Release(); |
81 | if(m_pOriginalFile) |
82 | m_pOriginalFile->Release(); |
83 | if (m_pDynamicMethodTable) |
84 | m_pDynamicMethodTable->Destroy(); |
85 | delete m_pError; |
86 | } |
87 | |
88 | #endif //!DACCESS_COMPILE |
89 | |
90 | LoaderAllocator * DomainFile::GetLoaderAllocator() |
91 | { |
92 | CONTRACTL |
93 | { |
94 | NOTHROW; |
95 | GC_NOTRIGGER; |
96 | SO_TOLERANT; |
97 | MODE_ANY; |
98 | } |
99 | CONTRACTL_END; |
100 | Assembly *pAssembly = GetDomainAssembly()->GetAssembly(); |
101 | if ((pAssembly != NULL) && (pAssembly->IsCollectible())) |
102 | { |
103 | return pAssembly->GetLoaderAllocator(); |
104 | } |
105 | else |
106 | { |
107 | return this->GetAppDomain()->GetLoaderAllocator(); |
108 | } |
109 | } |
110 | |
111 | #ifndef DACCESS_COMPILE |
112 | |
113 | void DomainFile::ReleaseFiles() |
114 | { |
115 | WRAPPER_NO_CONTRACT; |
116 | Module* pModule=GetCurrentModule(); |
117 | if(pModule) |
118 | pModule->StartUnload(); |
119 | |
120 | if (m_pFile) |
121 | m_pFile->ReleaseIL(); |
122 | if(m_pOriginalFile) |
123 | m_pOriginalFile->ReleaseIL(); |
124 | |
125 | if(pModule) |
126 | pModule->ReleaseILData(); |
127 | } |
128 | |
129 | BOOL DomainFile::TryEnsureActive() |
130 | { |
131 | CONTRACT(BOOL) |
132 | { |
133 | INSTANCE_CHECK; |
134 | THROWS; |
135 | GC_TRIGGERS; |
136 | } |
137 | CONTRACT_END; |
138 | |
139 | BOOL success = TRUE; |
140 | |
141 | EX_TRY |
142 | { |
143 | EnsureActive(); |
144 | } |
145 | EX_CATCH |
146 | { |
147 | success = FALSE; |
148 | } |
149 | EX_END_CATCH(RethrowTransientExceptions); |
150 | |
151 | RETURN success; |
152 | } |
153 | |
154 | // Optimization intended for EnsureLoadLevel only |
155 | #include <optsmallperfcritical.h> |
156 | void DomainFile::EnsureLoadLevel(FileLoadLevel targetLevel) |
157 | { |
158 | CONTRACT_VOID |
159 | { |
160 | INSTANCE_CHECK; |
161 | THROWS; |
162 | GC_TRIGGERS; |
163 | } |
164 | CONTRACT_END; |
165 | |
166 | TRIGGERSGC (); |
167 | if (IsLoading()) |
168 | { |
169 | this->GetAppDomain()->LoadDomainFile(this, targetLevel); |
170 | |
171 | // Enforce the loading requirement. Note that we may have a deadlock in which case we |
172 | // may be off by one which is OK. (At this point if we are short of targetLevel we know |
173 | // we have done so because of reentrancy contraints.) |
174 | |
175 | RequireLoadLevel((FileLoadLevel)(targetLevel-1)); |
176 | } |
177 | else |
178 | ThrowIfError(targetLevel); |
179 | |
180 | RETURN; |
181 | } |
182 | #include <optdefault.h> |
183 | |
184 | void DomainFile::AttemptLoadLevel(FileLoadLevel targetLevel) |
185 | { |
186 | CONTRACT_VOID |
187 | { |
188 | INSTANCE_CHECK; |
189 | THROWS; |
190 | GC_TRIGGERS; |
191 | } |
192 | CONTRACT_END; |
193 | |
194 | if (IsLoading()) |
195 | this->GetAppDomain()->LoadDomainFile(this, targetLevel); |
196 | else |
197 | ThrowIfError(targetLevel); |
198 | |
199 | RETURN; |
200 | } |
201 | |
202 | |
203 | CHECK DomainFile::CheckLoadLevel(FileLoadLevel requiredLevel, BOOL deadlockOK) |
204 | { |
205 | CONTRACTL |
206 | { |
207 | INSTANCE_CHECK; |
208 | NOTHROW; |
209 | GC_NOTRIGGER; |
210 | } |
211 | CONTRACTL_END; |
212 | |
213 | if (deadlockOK) |
214 | { |
215 | #ifndef CROSSGEN_COMPILE |
216 | // CheckLoading requires waiting on a host-breakable lock. |
217 | // Since this is only a checked-build assert and we've been |
218 | // living with it for a while, I'll leave it as is. |
219 | //@TODO: CHECK statements are *NOT* debug-only!!! |
220 | CONTRACT_VIOLATION(ThrowsViolation|GCViolation|TakesLockViolation); |
221 | CHECK(this->GetAppDomain()->CheckLoading(this, requiredLevel)); |
222 | #endif |
223 | } |
224 | else |
225 | { |
226 | CHECK_MSG(m_level >= requiredLevel, |
227 | "File not sufficiently loaded" ); |
228 | } |
229 | |
230 | CHECK_OK; |
231 | } |
232 | |
233 | |
234 | |
235 | void DomainFile::RequireLoadLevel(FileLoadLevel targetLevel) |
236 | { |
237 | CONTRACT_VOID |
238 | { |
239 | INSTANCE_CHECK; |
240 | THROWS; |
241 | GC_TRIGGERS; |
242 | } |
243 | CONTRACT_END; |
244 | |
245 | if (GetLoadLevel() < targetLevel) |
246 | { |
247 | ThrowIfError(targetLevel); |
248 | ThrowHR(MSEE_E_ASSEMBLYLOADINPROGRESS); // @todo: better exception |
249 | } |
250 | |
251 | RETURN; |
252 | } |
253 | |
254 | |
255 | void DomainFile::SetError(Exception *ex) |
256 | { |
257 | CONTRACT_VOID |
258 | { |
259 | PRECONDITION(!IsError()); |
260 | PRECONDITION(ex != NULL); |
261 | INSTANCE_CHECK; |
262 | THROWS; |
263 | GC_TRIGGERS; |
264 | POSTCONDITION(IsError()); |
265 | } |
266 | CONTRACT_END; |
267 | |
268 | m_pError = new ExInfo(ex->DomainBoundClone()); |
269 | |
270 | GetCurrentModule()->NotifyEtwLoadFinished(ex->GetHR()); |
271 | |
272 | if (!IsProfilerNotified()) |
273 | { |
274 | SetProfilerNotified(); |
275 | |
276 | #ifdef PROFILING_SUPPORTED |
277 | if (GetCurrentModule() != NULL) |
278 | { |
279 | // Only send errors for non-shared assemblies; other assemblies might be successfully completed |
280 | // in another app domain later. |
281 | GetCurrentModule()->NotifyProfilerLoadFinished(ex->GetHR()); |
282 | } |
283 | #endif |
284 | } |
285 | |
286 | RETURN; |
287 | } |
288 | |
289 | void DomainFile::ThrowIfError(FileLoadLevel targetLevel) |
290 | { |
291 | CONTRACT_VOID |
292 | { |
293 | INSTANCE_CHECK; |
294 | MODE_ANY; |
295 | THROWS; |
296 | GC_TRIGGERS; |
297 | } |
298 | CONTRACT_END; |
299 | |
300 | if (m_level < targetLevel) |
301 | { |
302 | if (m_pError) |
303 | m_pError->Throw(); |
304 | } |
305 | |
306 | RETURN; |
307 | } |
308 | |
309 | CHECK DomainFile::CheckNoError(FileLoadLevel targetLevel) |
310 | { |
311 | LIMITED_METHOD_CONTRACT; |
312 | CHECK(m_level >= targetLevel |
313 | || !IsError()); |
314 | |
315 | CHECK_OK; |
316 | } |
317 | |
318 | CHECK DomainFile::CheckLoaded() |
319 | { |
320 | CONTRACTL |
321 | { |
322 | INSTANCE_CHECK; |
323 | NOTHROW; |
324 | GC_NOTRIGGER; |
325 | MODE_ANY; |
326 | } |
327 | CONTRACTL_END; |
328 | |
329 | CHECK_MSG(CheckNoError(FILE_LOADED), "DomainFile load resulted in an error" ); |
330 | |
331 | if (IsLoaded()) |
332 | CHECK_OK; |
333 | |
334 | // Mscorlib is allowed to run managed code much earlier than other |
335 | // assemblies for bootstrapping purposes. This is because it has no |
336 | // dependencies, security checks, and doesn't rely on loader notifications. |
337 | |
338 | if (GetFile()->IsSystem()) |
339 | CHECK_OK; |
340 | |
341 | CHECK_MSG(GetFile()->CheckLoaded(), "PEFile has not been loaded" ); |
342 | |
343 | CHECK_OK; |
344 | } |
345 | |
346 | CHECK DomainFile::CheckActivated() |
347 | { |
348 | CONTRACTL |
349 | { |
350 | INSTANCE_CHECK; |
351 | NOTHROW; |
352 | GC_NOTRIGGER; |
353 | MODE_ANY; |
354 | } |
355 | CONTRACTL_END; |
356 | |
357 | CHECK_MSG(CheckNoError(FILE_ACTIVE), "DomainFile load resulted in an error" ); |
358 | |
359 | if (IsActive()) |
360 | CHECK_OK; |
361 | |
362 | // Mscorlib is allowed to run managed code much earlier than other |
363 | // assemblies for bootstrapping purposes. This is because it has no |
364 | // dependencies, security checks, and doesn't rely on loader notifications. |
365 | |
366 | if (GetFile()->IsSystem()) |
367 | CHECK_OK; |
368 | |
369 | CHECK_MSG(GetFile()->CheckLoaded(), "PEFile has not been loaded" ); |
370 | CHECK_MSG(IsLoaded(), "DomainFile has not been fully loaded" ); |
371 | CHECK_MSG(m_bDisableActivationCheck || CheckLoadLevel(FILE_ACTIVE), "File has not had execution verified" ); |
372 | |
373 | CHECK_OK; |
374 | } |
375 | |
376 | #endif //!DACCESS_COMPILE |
377 | |
378 | DomainAssembly *DomainFile::GetDomainAssembly() |
379 | { |
380 | CONTRACTL |
381 | { |
382 | SUPPORTS_DAC; |
383 | NOTHROW; |
384 | GC_NOTRIGGER; |
385 | SO_TOLERANT; |
386 | } |
387 | CONTRACTL_END; |
388 | |
389 | _ASSERTE(IsAssembly()); |
390 | return (DomainAssembly *) this; |
391 | } |
392 | |
393 | // Return true iff the debugger should get notifications about this assembly. |
394 | // |
395 | // Notes: |
396 | // The debuggee may be stopped while a DomainAssmebly is being initialized. In this time window, |
397 | // GetAssembly() may be NULL. If that's the case, this function has to return FALSE. Later on, when |
398 | // the DomainAssembly is fully initialized, this function will return TRUE. This is the only scenario |
399 | // where this function is mutable. In other words, a DomainAssembly can only change from being invisible |
400 | // to visible, but NOT vice versa. Once a DomainAssmebly is fully initialized, this function should be |
401 | // immutable for an instance of a module. That ensures that the debugger gets consistent |
402 | // notifications about it. It this value mutates, than the debugger may miss relevant notifications. |
403 | BOOL DomainAssembly::IsVisibleToDebugger() |
404 | { |
405 | WRAPPER_NO_CONTRACT; |
406 | SUPPORTS_DAC; |
407 | |
408 | return (GetAssembly() != NULL); |
409 | } |
410 | |
411 | #ifndef DACCESS_COMPILE |
412 | #ifdef FEATURE_PREJIT |
413 | void DomainFile::ExternalLog(DWORD level, const WCHAR *fmt, ...) |
414 | { |
415 | WRAPPER_NO_CONTRACT; |
416 | |
417 | va_list args; |
418 | va_start(args, fmt); |
419 | |
420 | GetOriginalFile()->ExternalVLog(LF_ZAP, level, fmt, args); |
421 | |
422 | va_end(args); |
423 | } |
424 | |
425 | void DomainFile::ExternalLog(DWORD level, const char *msg) |
426 | { |
427 | WRAPPER_NO_CONTRACT; |
428 | |
429 | GetOriginalFile()->ExternalLog(level, msg); |
430 | } |
431 | #endif |
432 | |
433 | #ifndef CROSSGEN_COMPILE |
434 | //--------------------------------------------------------------------------------------- |
435 | // |
436 | // Returns managed representation of the module (Module or ModuleBuilder). |
437 | // Returns NULL if the managed scout was already collected (see code:LoaderAllocator#AssemblyPhases). |
438 | // |
439 | OBJECTREF DomainFile::GetExposedModuleObject() |
440 | { |
441 | CONTRACTL |
442 | { |
443 | INSTANCE_CHECK; |
444 | THROWS; |
445 | MODE_COOPERATIVE; |
446 | GC_TRIGGERS; |
447 | } |
448 | CONTRACTL_END; |
449 | |
450 | LoaderAllocator * pLoaderAllocator = GetLoaderAllocator(); |
451 | |
452 | if (m_hExposedModuleObject == NULL) |
453 | { |
454 | // Atomically create a handle |
455 | LOADERHANDLE handle = pLoaderAllocator->AllocateHandle(NULL); |
456 | |
457 | FastInterlockCompareExchangePointer(&m_hExposedModuleObject, handle, static_cast<LOADERHANDLE>(NULL)); |
458 | } |
459 | |
460 | if (pLoaderAllocator->GetHandleValue(m_hExposedModuleObject) == NULL) |
461 | { |
462 | REFLECTMODULEBASEREF refClass = NULL; |
463 | |
464 | // Will be TRUE only if LoaderAllocator managed object was already collected and therefore we should |
465 | // return NULL |
466 | BOOL fIsLoaderAllocatorCollected = FALSE; |
467 | |
468 | GCPROTECT_BEGIN(refClass); |
469 | |
470 | if (GetFile()->IsDynamic()) |
471 | { |
472 | refClass = (REFLECTMODULEBASEREF) AllocateObject(MscorlibBinder::GetClass(CLASS__MODULE_BUILDER)); |
473 | } |
474 | else |
475 | { |
476 | refClass = (REFLECTMODULEBASEREF) AllocateObject(MscorlibBinder::GetClass(CLASS__MODULE)); |
477 | } |
478 | refClass->SetModule(m_pModule); |
479 | |
480 | // Attach the reference to the assembly to keep the LoaderAllocator for this collectible type |
481 | // alive as long as a reference to the module is kept alive. |
482 | if (GetModule()->GetAssembly() != NULL) |
483 | { |
484 | OBJECTREF refAssembly = GetModule()->GetAssembly()->GetExposedObject(); |
485 | if ((refAssembly == NULL) && GetModule()->GetAssembly()->IsCollectible()) |
486 | { |
487 | fIsLoaderAllocatorCollected = TRUE; |
488 | } |
489 | refClass->SetAssembly(refAssembly); |
490 | } |
491 | |
492 | pLoaderAllocator->CompareExchangeValueInHandle(m_hExposedModuleObject, (OBJECTREF)refClass, NULL); |
493 | GCPROTECT_END(); |
494 | |
495 | if (fIsLoaderAllocatorCollected) |
496 | { // The LoaderAllocator managed object was already collected, we cannot re-create it |
497 | // Note: We did not publish the allocated Module/ModuleBuilder object, it will get collected |
498 | // by GC |
499 | return NULL; |
500 | } |
501 | } |
502 | |
503 | return pLoaderAllocator->GetHandleValue(m_hExposedModuleObject); |
504 | } // DomainFile::GetExposedModuleObject |
505 | #endif // CROSSGEN_COMPILE |
506 | |
507 | BOOL DomainFile::DoIncrementalLoad(FileLoadLevel level) |
508 | { |
509 | STANDARD_VM_CONTRACT; |
510 | |
511 | if (IsError()) |
512 | return FALSE; |
513 | |
514 | Thread *pThread; |
515 | pThread = GetThread(); |
516 | _ASSERTE(pThread); |
517 | INTERIOR_STACK_PROBE_FOR(pThread, 8); |
518 | |
519 | switch (level) |
520 | { |
521 | case FILE_LOAD_BEGIN: |
522 | Begin(); |
523 | break; |
524 | |
525 | case FILE_LOAD_FIND_NATIVE_IMAGE: |
526 | #ifdef FEATURE_PREJIT |
527 | FindNativeImage(); |
528 | #endif |
529 | break; |
530 | |
531 | case FILE_LOAD_VERIFY_NATIVE_IMAGE_DEPENDENCIES: |
532 | #ifdef FEATURE_PREJIT |
533 | VerifyNativeImageDependencies(); |
534 | #endif |
535 | break; |
536 | |
537 | case FILE_LOAD_ALLOCATE: |
538 | Allocate(); |
539 | break; |
540 | |
541 | case FILE_LOAD_ADD_DEPENDENCIES: |
542 | AddDependencies(); |
543 | break; |
544 | |
545 | case FILE_LOAD_PRE_LOADLIBRARY: |
546 | PreLoadLibrary(); |
547 | break; |
548 | |
549 | case FILE_LOAD_LOADLIBRARY: |
550 | LoadLibrary(); |
551 | break; |
552 | |
553 | case FILE_LOAD_POST_LOADLIBRARY: |
554 | PostLoadLibrary(); |
555 | break; |
556 | |
557 | case FILE_LOAD_EAGER_FIXUPS: |
558 | EagerFixups(); |
559 | break; |
560 | |
561 | case FILE_LOAD_VTABLE_FIXUPS: |
562 | VtableFixups(); |
563 | break; |
564 | |
565 | case FILE_LOAD_DELIVER_EVENTS: |
566 | DeliverSyncEvents(); |
567 | break; |
568 | |
569 | case FILE_LOADED: |
570 | FinishLoad(); |
571 | break; |
572 | |
573 | case FILE_LOAD_VERIFY_EXECUTION: |
574 | VerifyExecution(); |
575 | break; |
576 | |
577 | case FILE_ACTIVE: |
578 | Activate(); |
579 | break; |
580 | |
581 | default: |
582 | UNREACHABLE(); |
583 | } |
584 | |
585 | END_INTERIOR_STACK_PROBE; |
586 | |
587 | #ifdef FEATURE_MULTICOREJIT |
588 | { |
589 | Module * pModule = GetModule(); |
590 | |
591 | if (pModule != NULL) // Should not triggle assert when module is NULL |
592 | { |
593 | this->GetAppDomain()->GetMulticoreJitManager().RecordModuleLoad(pModule, level); |
594 | } |
595 | } |
596 | #endif |
597 | |
598 | return TRUE; |
599 | } |
600 | |
601 | #ifdef FEATURE_PREJIT |
602 | |
603 | void DomainFile::VerifyNativeImageDependencies(bool verifyOnly) |
604 | { |
605 | CONTRACTL |
606 | { |
607 | INSTANCE_CHECK; |
608 | STANDARD_VM_CHECK; |
609 | PRECONDITION(verifyOnly || (m_pDomain->GetDomainFileLoadLevel(this) == |
610 | FILE_LOAD_FIND_NATIVE_IMAGE)); |
611 | } |
612 | CONTRACTL_END; |
613 | |
614 | // This function gets called multiple times. The first call is the real work. |
615 | // Subsequent calls are only to verify that everything still looks OK. |
616 | if (!verifyOnly) |
617 | ClearNativeImageStress(); |
618 | |
619 | if (!m_pFile->HasNativeImage()) |
620 | { |
621 | CheckZapRequired(); |
622 | return; |
623 | } |
624 | |
625 | { |
626 | // Go through native dependencies & make sure they still have their prejit images after |
627 | // the security check. |
628 | // NOTE: we could theoretically do this without loading the dependencies, if we cache the |
629 | // COR_TRUST structures from the dependencies in the version information. |
630 | // |
631 | // Verify that all of our hard dependencies are loaded at the right base address. |
632 | // If not, abandon prejit image (or fix ours up) |
633 | // Also, if there are any hard dependencies, then our native image also needs to be |
634 | // loaded at the right base address |
635 | |
636 | // Note: we will go through all of our dependencies, call Load on them, and check the base |
637 | // addresses & identity. |
638 | // It is important to note that all of those dependencies are also going to do the |
639 | // same thing, so we might conceivably check a base address as OK, and then have that image |
640 | // abandoned by that assembly during its VerifyNativeImageDependencies phase. |
641 | // However, we avoid this problem since the hard depedencies stored are a closure of the |
642 | // hard dependencies of an image. This effectively means that our check here is a superset |
643 | // of the check that the dependencies will perform. Even if we hit a dependency loop, we |
644 | // will still guarantee that we've examined all of our dependencies. |
645 | |
646 | ReleaseHolder<PEImage> pNativeImage = m_pFile->GetNativeImageWithRef(); |
647 | if(pNativeImage==NULL) |
648 | { |
649 | CheckZapRequired(); |
650 | return; |
651 | } |
652 | |
653 | PEImageLayout* pNativeLayout = pNativeImage->GetLoadedLayout(); |
654 | |
655 | // reuse same codepath for both manifest and non-manifest modules |
656 | ReleaseHolder<PEImage> pManifestNativeImage(NULL); |
657 | |
658 | PEFile* pManifestFile = m_pFile; |
659 | PEImageLayout* pManifestNativeLayout = pNativeLayout; |
660 | |
661 | if (!IsAssembly()) |
662 | { |
663 | pManifestFile = GetDomainAssembly()->GetCurrentAssembly() |
664 | ->GetManifestModule()->GetFile(); |
665 | |
666 | pManifestNativeImage = pManifestFile->GetNativeImageWithRef(); |
667 | |
668 | if (pManifestNativeImage == NULL) |
669 | { |
670 | ExternalLog(LL_ERROR, "Rejecting native image because there is no " |
671 | "ngen image for manifest module. Check why the manifest module " |
672 | "does not have an ngen image" ); |
673 | m_dwReasonForRejectingNativeImage = ReasonForRejectingNativeImage_NoNiForManifestModule; |
674 | STRESS_LOG3(LF_ZAP,LL_INFO100,"Rejecting native file %p, because its manifest module %p has no NI - reason 0x%x\n" ,pNativeImage.GetValue(),pManifestFile,m_dwReasonForRejectingNativeImage); |
675 | goto NativeImageRejected; |
676 | } |
677 | |
678 | return; |
679 | } |
680 | |
681 | COUNT_T cDependencies; |
682 | CORCOMPILE_DEPENDENCY *pDependencies = pManifestNativeLayout->GetNativeDependencies(&cDependencies); |
683 | |
684 | LOG((LF_ZAP, LL_INFO100, "ZAP: Checking native image dependencies for %S.\n" , |
685 | pNativeImage->GetPath().GetUnicode())); |
686 | |
687 | for (COUNT_T iDependency = 0; iDependency < cDependencies; iDependency++) |
688 | { |
689 | CORCOMPILE_DEPENDENCY *pDependency = &(pDependencies[iDependency]); |
690 | |
691 | // Later, for domain neutral assemblies, we will also want to verify security policy |
692 | // in such cases, the prejit image should store the publisher info for the dependencies |
693 | // for us. |
694 | |
695 | // If this is not a hard-bound dependency, then skip to the next dependency |
696 | if (pDependency->signNativeImage == INVALID_NGEN_SIGNATURE) |
697 | continue; |
698 | |
699 | |
700 | // |
701 | // CoreCLR hard binds to mscorlib.dll only. Avoid going through the full load. |
702 | // |
703 | |
704 | #ifdef _DEBUG |
705 | AssemblySpec name; |
706 | name.InitializeSpec(pDependency->dwAssemblyRef, |
707 | ((pManifestNativeImage != NULL) ? pManifestNativeImage : pNativeImage)->GetNativeMDImport(), |
708 | GetDomainAssembly()); |
709 | _ASSERTE(name.IsMscorlib()); |
710 | #endif |
711 | |
712 | PEAssembly * pDependencyFile = SystemDomain::SystemFile(); |
713 | |
714 | |
715 | ReleaseHolder<PEImage> pDependencyNativeImage = pDependencyFile->GetNativeImageWithRef(); |
716 | if (pDependencyNativeImage == NULL) |
717 | { |
718 | ExternalLog(LL_ERROR, W("Rejecting native image because dependency %s is not native" ), |
719 | pDependencyFile->GetPath().GetUnicode()); |
720 | m_dwReasonForRejectingNativeImage = ReasonForRejectingNativeImage_DependencyNotNative; |
721 | STRESS_LOG3(LF_ZAP,LL_INFO100,"Rejecting native file %p, because dependency %p is not NI - reason 0x%x\n" ,pNativeImage.GetValue(),pDependencyFile,m_dwReasonForRejectingNativeImage); |
722 | goto NativeImageRejected; |
723 | } |
724 | |
725 | PTR_PEImageLayout pDependencyNativeLayout = pDependencyNativeImage->GetLoadedLayout(); |
726 | // Assert that the native image signature is as expected |
727 | // Fusion will ensure this |
728 | CORCOMPILE_VERSION_INFO * pDependencyNativeVersion = |
729 | pDependencyNativeLayout->GetNativeVersionInfo(); |
730 | |
731 | if (!RuntimeVerifyNativeImageDependency(pDependency, pDependencyNativeVersion, pDependencyFile)) |
732 | goto NativeImageRejected; |
733 | } |
734 | LOG((LF_ZAP, LL_INFO100, "ZAP: Native image dependencies for %S OK.\n" , |
735 | pNativeImage->GetPath().GetUnicode())); |
736 | |
737 | return; |
738 | } |
739 | |
740 | NativeImageRejected: |
741 | m_pFile->ClearNativeImage(); |
742 | m_pFile->SetCannotUseNativeImage(); |
743 | |
744 | CheckZapRequired(); |
745 | |
746 | return; |
747 | } |
748 | |
749 | BOOL DomainFile::IsZapRequired() |
750 | { |
751 | CONTRACTL |
752 | { |
753 | INSTANCE_CHECK; |
754 | THROWS; |
755 | GC_TRIGGERS; |
756 | MODE_ANY; |
757 | INJECT_FAULT(COMPlusThrowOM();); |
758 | } |
759 | CONTRACTL_END; |
760 | |
761 | if (!m_pFile->HasMetadata() || !g_pConfig->RequireZap(GetSimpleName())) |
762 | return FALSE; |
763 | |
764 | #if defined(_DEBUG) |
765 | // If we're intentionally treating NIs as if they were MSIL assemblies, and the test |
766 | // is flexible enough to accept that (e.g., complus_zaprequired=2), then zaps are not |
767 | // required (i.e., it's ok for m_pFile->m_nativeImage to be NULL), but only if we |
768 | // loaded an actual NI to be treated as an IL assembly |
769 | if (PEFile::ShouldTreatNIAsMSIL()) |
770 | { |
771 | // Since the RequireZap() call above returned true, we know that some level of |
772 | // zap requiredness was configured |
773 | _ASSERTE(g_pConfig->RequireZaps() != EEConfig::REQUIRE_ZAPS_NONE); |
774 | |
775 | // If config uses this special value (2), zaps are not required, so long as |
776 | // we're using an actual NI as IL |
777 | if ((g_pConfig->RequireZaps() == EEConfig::REQUIRE_ZAPS_ALL_JIT_OK) && |
778 | m_pFile->HasOpenedILimage() && |
779 | m_pFile->GetOpenedILimage()->HasNativeHeader()) |
780 | { |
781 | return FALSE; |
782 | } |
783 | } |
784 | #endif // defined(_DEBUG) |
785 | |
786 | // Does this look like a resource-only assembly? We assume an assembly is resource-only |
787 | // if it contains no TypeDef (other than the <Module> TypeDef) and no MethodDef. |
788 | // Note that pMD->GetCountWithTokenKind(mdtTypeDef) doesn't count the <Module> type. |
789 | IMDInternalImportHolder pMD = m_pFile->GetMDImport(); |
790 | if (pMD->GetCountWithTokenKind(mdtTypeDef) == 0 && pMD->GetCountWithTokenKind(mdtMethodDef) == 0) |
791 | return FALSE; |
792 | |
793 | DomainAssembly * pDomainAssembly = GetDomainAssembly(); |
794 | |
795 | // If the manifest module does not have an ngen image, the non-manifest |
796 | // modules cannot either |
797 | if (m_pFile->IsModule() && !pDomainAssembly->GetFile()->CanUseNativeImage()) |
798 | m_pFile->SetCannotUseNativeImage(); |
799 | |
800 | // Some cases are not supported by design. They can never have a native image. |
801 | // So ignore such cases |
802 | |
803 | if (!m_pFile->CanUseNativeImage() && |
804 | g_pConfig->RequireZaps() == EEConfig::REQUIRE_ZAPS_SUPPORTED) |
805 | return FALSE; |
806 | |
807 | #ifdef FEATURE_NATIVE_IMAGE_GENERATION |
808 | if (IsCompilationProcess()) |
809 | { |
810 | // Ignore the assembly being ngened. |
811 | |
812 | bool fileIsBeingNGened = false; |
813 | |
814 | if (this->GetAppDomain()->IsCompilationDomain()) |
815 | { |
816 | Assembly * assemblyBeingNGened = this->GetAppDomain()->ToCompilationDomain()->GetTargetAssembly(); |
817 | if (assemblyBeingNGened == NULL || assemblyBeingNGened == pDomainAssembly->GetCurrentAssembly()) |
818 | fileIsBeingNGened = true; |
819 | } |
820 | else if (IsSystem()) |
821 | { |
822 | // mscorlib gets loaded before the CompilationDomain gets created. |
823 | // However, we may be ngening mscorlib itself |
824 | fileIsBeingNGened = true; |
825 | } |
826 | |
827 | if (fileIsBeingNGened) |
828 | return FALSE; |
829 | } |
830 | #endif |
831 | |
832 | return TRUE; |
833 | } |
834 | |
835 | void DomainFile::CheckZapRequired() |
836 | { |
837 | CONTRACTL |
838 | { |
839 | INSTANCE_CHECK; |
840 | THROWS; |
841 | GC_TRIGGERS; |
842 | MODE_ANY; |
843 | INJECT_FAULT(COMPlusThrowOM();); |
844 | } |
845 | CONTRACTL_END; |
846 | |
847 | if (m_pFile->HasNativeImage() || !IsZapRequired()) |
848 | return; |
849 | |
850 | #ifdef FEATURE_READYTORUN |
851 | if(m_pFile->GetLoaded()->HasReadyToRunHeader()) |
852 | return; |
853 | #endif |
854 | |
855 | // Flush any log messages |
856 | GetFile()->FlushExternalLog(); |
857 | |
858 | StackSString ss; |
859 | ss.Printf("ZapRequire: Could not get native image for %s.\n" |
860 | "Use FusLogVw.exe to check the reason." , |
861 | GetSimpleName()); |
862 | |
863 | #if defined(_DEBUG) |
864 | // Assert as some test may not check their error codes well. So throwing an |
865 | // exception may not cause a test failure (as it should). |
866 | StackScratchBuffer scratch; |
867 | DbgAssertDialog(__FILE__, __LINE__, (char*)ss.GetUTF8(scratch)); |
868 | #endif // defined(_DEBUG) |
869 | |
870 | COMPlusThrowNonLocalized(kFileNotFoundException, ss.GetUnicode()); |
871 | } |
872 | |
873 | // Discarding an ngen image can cause problems. For more coverage, |
874 | // this stress-mode discards ngen images even if not needed. |
875 | |
876 | void DomainFile::ClearNativeImageStress() |
877 | { |
878 | WRAPPER_NO_CONTRACT; |
879 | |
880 | #ifdef _DEBUG |
881 | static ConfigDWORD clearNativeImageStress; |
882 | DWORD stressPercentage = clearNativeImageStress.val(CLRConfig::INTERNAL_clearNativeImageStress); |
883 | _ASSERTE(stressPercentage <= 100); |
884 | if (stressPercentage == 0 || !GetFile()->HasNativeImage()) |
885 | return; |
886 | |
887 | // Note that discarding a native image can affect dependencies. So its not enough |
888 | // to only check DomainFile::IsZapRequired() here. |
889 | if (g_pConfig->RequireZaps() != EEConfig::REQUIRE_ZAPS_NONE) |
890 | return; |
891 | |
892 | // Its OK to ClearNativeImage even for a shared assembly, as the current PEFile will |
893 | // be discarded if we decide to share the assembly. However, we always use the same |
894 | // PEFile for the system assembly. So discarding the native image in the current |
895 | // AppDomain will actually affect the system assembly in the shared domain, and other |
896 | // appdomains may have already committed to using its ngen image. |
897 | if (GetFile()->IsSystem() && !this->GetAppDomain()->IsDefaultDomain()) |
898 | return; |
899 | |
900 | if (g_IBCLogger.InstrEnabled()) |
901 | return; |
902 | |
903 | ULONG hash = HashStringA(GetSimpleName()); |
904 | |
905 | // Hash in the FileLoadLevel so that we make a different decision for every level. |
906 | FileLoadLevel fileLoadLevel = m_pDomain->GetDomainFileLoadLevel(this); |
907 | hash ^= ULONG(fileLoadLevel); |
908 | // We do not discard native images after this level |
909 | _ASSERTE(fileLoadLevel < FILE_LOAD_VERIFY_NATIVE_IMAGE_DEPENDENCIES); |
910 | |
911 | // Different app-domains should make different decisions |
912 | hash ^= HashString(this->GetAppDomain()->GetFriendlyName()); |
913 | |
914 | #ifdef FEATURE_NATIVE_IMAGE_GENERATION |
915 | // Since DbgRandomOnHashAndExe() is not so random under ngen.exe, also |
916 | // factor in the module being compiled |
917 | if (this->GetAppDomain()->IsCompilationDomain()) |
918 | { |
919 | Module * module = this->GetAppDomain()->ToCompilationDomain()->GetTargetModule(); |
920 | // Has the target module been set yet? |
921 | if (module) |
922 | hash ^= HashStringA(module->GetSimpleName()); |
923 | } |
924 | #endif |
925 | |
926 | if (DbgRandomOnHashAndExe(hash, float(stressPercentage)/100)) |
927 | { |
928 | GetFile()->SetCannotUseNativeImage(); |
929 | GetFile()->ClearNativeImage(); |
930 | ExternalLog(LL_ERROR, "Rejecting native image for **clearNativeImageStress**" ); |
931 | } |
932 | #endif |
933 | } |
934 | |
935 | #endif // FEATURE_PREJIT |
936 | |
937 | void DomainFile::PreLoadLibrary() |
938 | { |
939 | CONTRACTL |
940 | { |
941 | INSTANCE_CHECK; |
942 | STANDARD_VM_CHECK; |
943 | } |
944 | CONTRACTL_END; |
945 | |
946 | } // DomainFile::PreLoadLibrary |
947 | |
948 | // Note that this is the sole loading function which must be called OUTSIDE THE LOCK, since |
949 | // it will potentially involve the OS loader lock. |
950 | void DomainFile::LoadLibrary() |
951 | { |
952 | CONTRACTL |
953 | { |
954 | INSTANCE_CHECK; |
955 | STANDARD_VM_CHECK; |
956 | } |
957 | CONTRACTL_END; |
958 | |
959 | Thread::LoadingFileHolder holder(GetThread()); |
960 | GetThread()->SetLoadingFile(this); |
961 | GetFile()->LoadLibrary(); |
962 | } |
963 | |
964 | void DomainFile::PostLoadLibrary() |
965 | { |
966 | CONTRACTL |
967 | { |
968 | INSTANCE_CHECK; |
969 | // Note that GetFile()->LoadLibrary must be called before this OUTSIDE OF THE LOCKS |
970 | PRECONDITION(GetFile()->CheckLoaded()); |
971 | STANDARD_VM_CHECK; |
972 | } |
973 | CONTRACTL_END; |
974 | |
975 | #ifdef FEATURE_PREJIT |
976 | if (GetFile()->HasNativeImage()) |
977 | { |
978 | InsertIntoDomainFileWithNativeImageList(); |
979 | } |
980 | #endif |
981 | #ifdef PROFILING_SUPPORTED |
982 | // After this point, it is possible to load types. |
983 | // We need to notify the profiler now because the profiler may need to inject methods into |
984 | // the module, and to do so reliably, it must have the chance to do so before |
985 | // any types are loaded from the module. |
986 | // |
987 | // In the past we only allowed injecting types/methods on non-NGEN images so notifying here |
988 | // worked ok, but for NGEN images this is pretty ugly. Rejitting often occurs in this callback, |
989 | // but then during fixup the results of LoadedMethodDesc iterator would change and we would |
990 | // need to re-iterate everything. Aside from Rejit other code often wasn't designed to handle |
991 | // running before Fixup. A concrete example VS recently hit, calling GetClassLayout using |
992 | // a MethodTable which doesn't need restore but its parent pointer isn't fixed up yet. |
993 | // We've already set the rules so that profilers can't modify the member list of types in NGEN images |
994 | // so it doesn't matter if types are pre-loaded. We only need the guarantee that code for the |
995 | // loaded types won't execute yet. For NGEN images we deliver the load notification in |
996 | // FILE_LOAD_DELIVER_EVENTS. |
997 | if (!GetFile()->HasNativeImage()) |
998 | { |
999 | if (!IsProfilerNotified()) |
1000 | { |
1001 | SetProfilerNotified(); |
1002 | GetCurrentModule()->NotifyProfilerLoadFinished(S_OK); |
1003 | } |
1004 | } |
1005 | |
1006 | #endif |
1007 | } |
1008 | |
1009 | void DomainFile::AddDependencies() |
1010 | { |
1011 | STANDARD_VM_CONTRACT; |
1012 | |
1013 | #ifdef FEATURE_PREJIT |
1014 | |
1015 | // |
1016 | // CoreCLR hard binds to mscorlib.dll only. No need to track hardbound dependencies. |
1017 | // |
1018 | |
1019 | #endif // FEATURE_PREJIT |
1020 | } |
1021 | |
1022 | void DomainFile::EagerFixups() |
1023 | { |
1024 | WRAPPER_NO_CONTRACT; |
1025 | |
1026 | #ifdef FEATURE_PREJIT |
1027 | if (GetCurrentModule()->HasNativeImage()) |
1028 | { |
1029 | GetCurrentModule()->RunEagerFixups(); |
1030 | } |
1031 | #ifdef FEATURE_READYTORUN |
1032 | else |
1033 | if (GetCurrentModule()->IsReadyToRun()) |
1034 | { |
1035 | #ifndef CROSSGEN_COMPILE |
1036 | GetCurrentModule()->RunEagerFixups(); |
1037 | #endif |
1038 | |
1039 | PEImageLayout * pLayout = GetCurrentModule()->GetReadyToRunInfo()->GetImage(); |
1040 | |
1041 | TADDR base = dac_cast<TADDR>(pLayout->GetBase()); |
1042 | |
1043 | ExecutionManager::AddCodeRange(base, base + (TADDR)pLayout->GetVirtualSize(), |
1044 | ExecutionManager::GetReadyToRunJitManager(), |
1045 | RangeSection::RANGE_SECTION_READYTORUN, |
1046 | GetCurrentModule() /* (void *)pLayout */); |
1047 | } |
1048 | #endif // FEATURE_READYTORUN |
1049 | |
1050 | #endif // FEATURE_PREJIT |
1051 | } |
1052 | |
1053 | void DomainFile::VtableFixups() |
1054 | { |
1055 | WRAPPER_NO_CONTRACT; |
1056 | |
1057 | #if !defined(CROSSGEN_COMPILE) |
1058 | if (!GetCurrentModule()->IsResource()) |
1059 | GetCurrentModule()->FixupVTables(); |
1060 | #endif // !CROSSGEN_COMPILE |
1061 | } |
1062 | |
1063 | void DomainFile::FinishLoad() |
1064 | { |
1065 | CONTRACTL |
1066 | { |
1067 | INSTANCE_CHECK; |
1068 | STANDARD_VM_CHECK; |
1069 | } |
1070 | CONTRACTL_END; |
1071 | |
1072 | #ifdef FEATURE_PREJIT |
1073 | |
1074 | if (m_pFile->HasNativeImage()) |
1075 | { |
1076 | |
1077 | LOG((LF_ZAP, LL_INFO10, "Using native image %S.\n" , m_pFile->GetPersistentNativeImage()->GetPath().GetUnicode())); |
1078 | ExternalLog(LL_INFO10, "Native image successfully used." ); |
1079 | |
1080 | // Inform metadata that it has been loaded from a native image |
1081 | // (and so there was an opportunity to check for or fix inconsistencies in the original IL metadata) |
1082 | m_pFile->GetMDImport()->SetVerifiedByTrustedSource(TRUE); |
1083 | } |
1084 | |
1085 | // Are we absolutely required to use a native image? |
1086 | CheckZapRequired(); |
1087 | |
1088 | #if defined(FEATURE_COMINTEROP) |
1089 | // If this is a winmd file, ensure that the ngen reference namespace is loadable. |
1090 | // This is necessary as on the phone we don't check ngen image dependencies, and thus we can get in a situation |
1091 | // where a winmd is loaded as a dependency of an ngen image, but the type used to build cross module references |
1092 | // in winmd files isn't loaded. |
1093 | if (GetFile()->AsAssembly()->IsWindowsRuntime() && GetFile()->HasHostAssembly()) |
1094 | { |
1095 | IMDInternalImport *pImport = GetFile()->GetPersistentMDImport(); |
1096 | LPCSTR szNameSpace; |
1097 | LPCSTR szTypeName; |
1098 | // It does not make sense to pass the file name to recieve fake type name for empty WinMDs, because we would use the name |
1099 | // for binding in next call to BindAssemblySpec which would fail for fake WinRT type name |
1100 | // We will throw/return the error instead and the caller will recognize it and react to it by not creating the ngen image - |
1101 | // see code:Zapper::ComputeDependenciesInCurrentDomain |
1102 | if (SUCCEEDED(::GetFirstWinRTTypeDef(pImport, &szNameSpace, &szTypeName, NULL, NULL))) |
1103 | { |
1104 | // Build assembly spec to describe binding to that WinRT type. |
1105 | AssemblySpec spec; |
1106 | IfFailThrow(spec.Init("WindowsRuntimeAssemblyName, ContentType=WindowsRuntime" )); |
1107 | spec.SetWindowsRuntimeType(szNameSpace, szTypeName); |
1108 | |
1109 | // Bind to assembly using the CLRPriv binder infrastructure. (All WinRT loads are done through CLRPriv binders |
1110 | ReleaseHolder<IAssemblyName> pAssemblyName; |
1111 | IfFailThrow(spec.CreateFusionName(&pAssemblyName, FALSE, TRUE)); |
1112 | ReleaseHolder<ICLRPrivAssembly> pPrivAssembly; |
1113 | IfFailThrow(GetFile()->GetHostAssembly()->BindAssemblyByName(pAssemblyName, &pPrivAssembly)); |
1114 | |
1115 | // Verify that we found this. If this invariant doesn't hold, then the ngen images that reference this winmd are be invalid. |
1116 | // ALSO, this winmd file is invalid as it doesn't follow spec about how it is distributed. |
1117 | if (GetAppDomain()->FindAssembly(pPrivAssembly) != this) |
1118 | { |
1119 | ThrowHR(COR_E_BADIMAGEFORMAT); |
1120 | } |
1121 | } |
1122 | } |
1123 | #endif //defined(FEATURE_COMINTEROP) |
1124 | #endif // FEATURE_PREJIT |
1125 | |
1126 | // Flush any log messages |
1127 | #ifdef FEATURE_PREJIT |
1128 | GetFile()->FlushExternalLog(); |
1129 | #endif |
1130 | // Must set this a bit prematurely for the DAC stuff to work |
1131 | m_level = FILE_LOADED; |
1132 | |
1133 | // Now the DAC can find this module by enumerating assemblies in a domain. |
1134 | DACNotify::DoModuleLoadNotification(m_pModule); |
1135 | |
1136 | #if defined(DEBUGGING_SUPPORTED) && !defined(DACCESS_COMPILE) |
1137 | if (IsDebuggerNotified() && (g_pDebugInterface != NULL)) |
1138 | { |
1139 | // We already notified dbgapi that this module was loading (via LoadModule()). |
1140 | // Now let the dbgapi know the module has reached FILE_LOADED, so it can do any |
1141 | // processing that needs to wait until this stage (e.g., binding breakpoints in |
1142 | // NGENd generics). |
1143 | g_pDebugInterface->LoadModuleFinished(m_pModule, m_pDomain); |
1144 | } |
1145 | #endif // defined(DEBUGGING_SUPPORTED) && !defined(DACCESS_COMPILE) |
1146 | |
1147 | // Set a bit to indicate that the module has been loaded in some domain, and therefore |
1148 | // typeloads can involve types from this module. (Used for candidate instantiations.) |
1149 | GetModule()->SetIsReadyForTypeLoad(); |
1150 | |
1151 | #ifdef FEATURE_PERFMAP |
1152 | // Notify the perfmap of the IL image load. |
1153 | PerfMap::LogImageLoad(m_pFile); |
1154 | #endif |
1155 | } |
1156 | |
1157 | void DomainFile::VerifyExecution() |
1158 | { |
1159 | CONTRACT_VOID |
1160 | { |
1161 | INSTANCE_CHECK; |
1162 | PRECONDITION(IsLoaded()); |
1163 | STANDARD_VM_CHECK; |
1164 | } |
1165 | CONTRACT_END; |
1166 | |
1167 | if(GetFile()->PassiveDomainOnly()) |
1168 | { |
1169 | // Remove path - location must be hidden for security purposes |
1170 | LPCWSTR path=GetFile()->GetPath(); |
1171 | LPCWSTR pStart = wcsrchr(path, '\\'); |
1172 | if (pStart != NULL) |
1173 | pStart++; |
1174 | else |
1175 | pStart = path; |
1176 | COMPlusThrow(kInvalidOperationException, IDS_EE_CODEEXECUTION_ASSEMBLY_FOR_PASSIVE_DOMAIN_ONLY,pStart); |
1177 | } |
1178 | |
1179 | RETURN; |
1180 | } |
1181 | |
1182 | void DomainFile::Activate() |
1183 | { |
1184 | CONTRACT_VOID |
1185 | { |
1186 | INSTANCE_CHECK; |
1187 | PRECONDITION(IsLoaded()); |
1188 | STANDARD_VM_CHECK; |
1189 | } |
1190 | CONTRACT_END; |
1191 | |
1192 | // If we are a module, ensure we've activated the assembly first. |
1193 | |
1194 | if (!IsAssembly()) |
1195 | { |
1196 | GetDomainAssembly()->EnsureActive(); |
1197 | } |
1198 | else |
1199 | { |
1200 | // We cannot execute any code in this assembly until we know what exception plan it is on. |
1201 | // At the point of an exception's stack-crawl it is too late because we cannot tolerate a GC. |
1202 | // See PossiblyUnwrapThrowable and its callers. |
1203 | _ASSERTE(GetLoadedModule() == GetDomainAssembly()->GetLoadedAssembly()->GetManifestModule()); |
1204 | GetLoadedModule()->IsRuntimeWrapExceptions(); |
1205 | } |
1206 | |
1207 | // Now activate any dependencies. |
1208 | // This will typically cause reentrancy of course. |
1209 | |
1210 | #ifndef CROSSGEN_COMPILE |
1211 | |
1212 | // |
1213 | // Now call the module constructor. Note that this might cause reentrancy; |
1214 | // this is fine and will be handled by the class cctor mechanism. |
1215 | // |
1216 | |
1217 | MethodTable *pMT = m_pModule->GetGlobalMethodTable(); |
1218 | if (pMT != NULL) |
1219 | { |
1220 | pMT->CheckRestore(); |
1221 | m_bDisableActivationCheck=TRUE; |
1222 | pMT->CheckRunClassInitThrowing(); |
1223 | } |
1224 | #ifdef _DEBUG |
1225 | if (g_pConfig->ExpandModulesOnLoad()) |
1226 | { |
1227 | m_pModule->ExpandAll(); |
1228 | } |
1229 | #endif //_DEBUG |
1230 | |
1231 | #endif // CROSSGEN_COMPILE |
1232 | |
1233 | RETURN; |
1234 | } |
1235 | |
1236 | #ifdef FEATURE_PREJIT |
1237 | DomainFile *DomainFile::FindNextDomainFileWithNativeImage() |
1238 | { |
1239 | LIMITED_METHOD_CONTRACT; |
1240 | return m_pNextDomainFileWithNativeImage; |
1241 | } |
1242 | |
1243 | void DomainFile::InsertIntoDomainFileWithNativeImageList() |
1244 | { |
1245 | LIMITED_METHOD_CONTRACT; |
1246 | |
1247 | while (true) |
1248 | { |
1249 | DomainFile *pLastDomainFileFoundWithNativeImage = m_pDomain->m_pDomainFileWithNativeImageList; |
1250 | m_pNextDomainFileWithNativeImage = pLastDomainFileFoundWithNativeImage; |
1251 | if (pLastDomainFileFoundWithNativeImage == InterlockedCompareExchangeT(&m_pDomain->m_pDomainFileWithNativeImageList, this, pLastDomainFileFoundWithNativeImage)) |
1252 | break; |
1253 | } |
1254 | } |
1255 | #endif |
1256 | |
1257 | //-------------------------------------------------------------------------------- |
1258 | // DomainAssembly |
1259 | //-------------------------------------------------------------------------------- |
1260 | |
1261 | DomainAssembly::DomainAssembly(AppDomain *pDomain, PEFile *pFile, LoaderAllocator *pLoaderAllocator) |
1262 | : DomainFile(pDomain, pFile), |
1263 | m_pAssembly(NULL), |
1264 | m_debuggerFlags(DACF_NONE), |
1265 | m_fDebuggerUnloadStarted(FALSE), |
1266 | m_fCollectible(pLoaderAllocator->IsCollectible()), |
1267 | m_fHostAssemblyPublished(false), |
1268 | m_pLoaderAllocator(pLoaderAllocator), |
1269 | m_NextDomainAssemblyInSameALC(NULL) |
1270 | { |
1271 | CONTRACTL |
1272 | { |
1273 | CONSTRUCTOR_CHECK; |
1274 | STANDARD_VM_CHECK; |
1275 | INJECT_FAULT(COMPlusThrowOM();); |
1276 | } |
1277 | CONTRACTL_END; |
1278 | |
1279 | pFile->ValidateForExecution(); |
1280 | |
1281 | // !!! backout |
1282 | |
1283 | m_hExposedAssemblyObject = NULL; |
1284 | |
1285 | SetupDebuggingConfig(); |
1286 | |
1287 | // Add a Module iterator entry for this assembly. |
1288 | IfFailThrow(m_Modules.Append(this)); |
1289 | } |
1290 | |
1291 | DomainAssembly::~DomainAssembly() |
1292 | { |
1293 | CONTRACTL |
1294 | { |
1295 | DESTRUCTOR_CHECK; |
1296 | NOTHROW; |
1297 | GC_TRIGGERS; |
1298 | MODE_ANY; |
1299 | INJECT_FAULT(COMPlusThrowOM();); |
1300 | } |
1301 | CONTRACTL_END; |
1302 | |
1303 | if (m_fHostAssemblyPublished) |
1304 | { |
1305 | // Remove association first. |
1306 | GetAppDomain()->UnPublishHostedAssembly(this); |
1307 | } |
1308 | |
1309 | ModuleIterator i = IterateModules(kModIterIncludeLoading); |
1310 | while (i.Next()) |
1311 | { |
1312 | if (i.GetDomainFile() != this) |
1313 | delete i.GetDomainFile(); |
1314 | } |
1315 | |
1316 | if (m_pAssembly != NULL) |
1317 | { |
1318 | delete m_pAssembly; |
1319 | } |
1320 | } |
1321 | |
1322 | void DomainAssembly::ReleaseFiles() |
1323 | { |
1324 | STANDARD_VM_CONTRACT; |
1325 | |
1326 | if(m_pAssembly) |
1327 | m_pAssembly->StartUnload(); |
1328 | ModuleIterator i = IterateModules(kModIterIncludeLoading); |
1329 | while (i.Next()) |
1330 | { |
1331 | if (i.GetDomainFile() != this) |
1332 | i.GetDomainFile()->ReleaseFiles(); |
1333 | } |
1334 | |
1335 | DomainFile::ReleaseFiles(); |
1336 | } |
1337 | |
1338 | void DomainAssembly::SetAssembly(Assembly* pAssembly) |
1339 | { |
1340 | STANDARD_VM_CONTRACT; |
1341 | |
1342 | UpdatePEFile(pAssembly->GetManifestFile()); |
1343 | _ASSERTE(pAssembly->GetManifestModule()->GetFile()==m_pFile); |
1344 | m_pAssembly = pAssembly; |
1345 | m_pModule = pAssembly->GetManifestModule(); |
1346 | |
1347 | pAssembly->SetDomainAssembly(this); |
1348 | } |
1349 | |
1350 | |
1351 | #ifndef CROSSGEN_COMPILE |
1352 | //--------------------------------------------------------------------------------------- |
1353 | // |
1354 | // Returns managed representation of the assembly (Assembly or AssemblyBuilder). |
1355 | // Returns NULL if the managed scout was already collected (see code:LoaderAllocator#AssemblyPhases). |
1356 | // |
1357 | OBJECTREF DomainAssembly::GetExposedAssemblyObject() |
1358 | { |
1359 | CONTRACTL |
1360 | { |
1361 | INSTANCE_CHECK; |
1362 | THROWS; |
1363 | MODE_COOPERATIVE; |
1364 | GC_TRIGGERS; |
1365 | } |
1366 | CONTRACTL_END; |
1367 | |
1368 | LoaderAllocator * pLoaderAllocator = GetLoaderAllocator(); |
1369 | |
1370 | if (!pLoaderAllocator->IsManagedScoutAlive()) |
1371 | { // We already collected the managed scout, so we cannot re-create any managed objects |
1372 | // Note: This is an optimization, as the managed scout can be collected right after this check |
1373 | return NULL; |
1374 | } |
1375 | |
1376 | if (m_hExposedAssemblyObject == NULL) |
1377 | { |
1378 | // Atomically create a handle |
1379 | |
1380 | LOADERHANDLE handle = pLoaderAllocator->AllocateHandle(NULL); |
1381 | |
1382 | FastInterlockCompareExchangePointer(&m_hExposedAssemblyObject, handle, static_cast<LOADERHANDLE>(NULL)); |
1383 | } |
1384 | |
1385 | if (pLoaderAllocator->GetHandleValue(m_hExposedAssemblyObject) == NULL) |
1386 | { |
1387 | ASSEMBLYREF assemblyObj = NULL; |
1388 | MethodTable * pMT; |
1389 | if (GetFile()->IsDynamic()) |
1390 | { |
1391 | // This is unnecessary because the managed InternalAssemblyBuilder object |
1392 | // should have already been created at the time of DefineDynamicAssembly |
1393 | OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED); |
1394 | pMT = MscorlibBinder::GetClass(CLASS__INTERNAL_ASSEMBLY_BUILDER); |
1395 | } |
1396 | else |
1397 | { |
1398 | OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED); |
1399 | pMT = MscorlibBinder::GetClass(CLASS__ASSEMBLY); |
1400 | } |
1401 | |
1402 | // Will be TRUE only if LoaderAllocator managed object was already collected and therefore we should |
1403 | // return NULL |
1404 | BOOL fIsLoaderAllocatorCollected = FALSE; |
1405 | |
1406 | // Create the assembly object |
1407 | GCPROTECT_BEGIN(assemblyObj); |
1408 | assemblyObj = (ASSEMBLYREF)AllocateObject(pMT); |
1409 | |
1410 | assemblyObj->SetAssembly(this); |
1411 | |
1412 | // Attach the reference to the assembly to keep the LoaderAllocator for this collectible type |
1413 | // alive as long as a reference to the assembly is kept alive. |
1414 | // Currently we overload the sync root field of the assembly to do so, but the overload is not necessary. |
1415 | if (GetAssembly() != NULL) |
1416 | { |
1417 | OBJECTREF refLA = GetAssembly()->GetLoaderAllocator()->GetExposedObject(); |
1418 | if ((refLA == NULL) && GetAssembly()->GetLoaderAllocator()->IsCollectible()) |
1419 | { // The managed LoaderAllocator object was collected |
1420 | fIsLoaderAllocatorCollected = TRUE; |
1421 | } |
1422 | assemblyObj->SetSyncRoot(refLA); |
1423 | } |
1424 | |
1425 | if (!fIsLoaderAllocatorCollected) |
1426 | { // We should not expose this value in case the LoaderAllocator managed object was already |
1427 | // collected |
1428 | pLoaderAllocator->CompareExchangeValueInHandle(m_hExposedAssemblyObject, (OBJECTREF)assemblyObj, NULL); |
1429 | } |
1430 | GCPROTECT_END(); |
1431 | |
1432 | if (fIsLoaderAllocatorCollected) |
1433 | { // The LoaderAllocator managed object was already collected, we cannot re-create it |
1434 | // Note: We did not publish the allocated Assembly/AssmeblyBuilder object, it will get collected |
1435 | // by GC |
1436 | return NULL; |
1437 | } |
1438 | } |
1439 | |
1440 | return pLoaderAllocator->GetHandleValue(m_hExposedAssemblyObject); |
1441 | } // DomainAssembly::GetExposedAssemblyObject |
1442 | #endif // CROSSGEN_COMPILE |
1443 | |
1444 | DomainFile* DomainAssembly::FindIJWModule(HMODULE hMod) |
1445 | { |
1446 | CONTRACT (DomainFile*) |
1447 | { |
1448 | INSTANCE_CHECK; |
1449 | THROWS; |
1450 | GC_NOTRIGGER; |
1451 | MODE_ANY; |
1452 | POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); |
1453 | } |
1454 | CONTRACT_END; |
1455 | |
1456 | ModuleIterator i = IterateModules(kModIterIncludeLoaded); |
1457 | while (i.Next()) |
1458 | { |
1459 | PEFile *pFile = i.GetDomainFile()->GetFile(); |
1460 | |
1461 | if ( !pFile->IsResource() |
1462 | && !pFile->IsDynamic() |
1463 | && !pFile->IsILOnly() |
1464 | && pFile->GetIJWBase() == hMod) |
1465 | { |
1466 | RETURN i.GetDomainFile(); |
1467 | } |
1468 | } |
1469 | RETURN NULL; |
1470 | } |
1471 | |
1472 | |
1473 | void DomainAssembly::Begin() |
1474 | { |
1475 | STANDARD_VM_CONTRACT; |
1476 | |
1477 | { |
1478 | AppDomain::LoadLockHolder lock(m_pDomain); |
1479 | m_pDomain->AddAssembly(this); |
1480 | } |
1481 | // Make it possible to find this DomainAssembly object from associated ICLRPrivAssembly. |
1482 | GetAppDomain()->PublishHostedAssembly(this); |
1483 | m_fHostAssemblyPublished = true; |
1484 | } |
1485 | |
1486 | #ifdef FEATURE_PREJIT |
1487 | void DomainAssembly::FindNativeImage() |
1488 | { |
1489 | CONTRACTL |
1490 | { |
1491 | INSTANCE_CHECK; |
1492 | STANDARD_VM_CHECK; |
1493 | } |
1494 | CONTRACTL_END; |
1495 | |
1496 | ClearNativeImageStress(); |
1497 | |
1498 | // We already have an image - we just need to do a few more checks |
1499 | |
1500 | if (GetFile()->HasNativeImage()) |
1501 | { |
1502 | #if defined(_DEBUG) |
1503 | if (g_pConfig->ForbidZap(GetSimpleName())) |
1504 | { |
1505 | SString sbuf; |
1506 | StackScratchBuffer scratch; |
1507 | sbuf.Printf("COMPlus_NgenBind_ZapForbid violation: %s." , GetSimpleName()); |
1508 | DbgAssertDialog(__FILE__, __LINE__, sbuf.GetUTF8(scratch)); |
1509 | } |
1510 | #endif |
1511 | |
1512 | ReleaseHolder<PEImage> pNativeImage = GetFile()->GetNativeImageWithRef(); |
1513 | |
1514 | if (!CheckZapDependencyIdentities(pNativeImage)) |
1515 | { |
1516 | m_dwReasonForRejectingNativeImage = ReasonForRejectingNativeImage_DependencyIdentityMismatch; |
1517 | STRESS_LOG2(LF_ZAP,LL_INFO100,"Rejecting native file %p, because dependency identity mismatch - reason 0x%x\n" ,pNativeImage.GetValue(),m_dwReasonForRejectingNativeImage); |
1518 | ExternalLog(LL_ERROR, "Rejecting native image because of identity mismatch " |
1519 | "with one or more of its assembly dependencies. The assembly needs " |
1520 | "to be ngenned again" ); |
1521 | |
1522 | GetFile()->ClearNativeImage(); |
1523 | |
1524 | // Always throw exceptions when we throw the NI out |
1525 | ThrowHR(CLR_E_BIND_NI_DEP_IDENTITY_MISMATCH); |
1526 | } |
1527 | else |
1528 | { |
1529 | Module * pNativeModule = pNativeImage->GetLoadedLayout()->GetPersistedModuleImage(); |
1530 | EnsureWritablePages(pNativeModule); |
1531 | PEFile ** ppNativeFile = (PEFile **) (PBYTE(pNativeModule) + Module::GetFileOffset()); |
1532 | |
1533 | PEAssembly * pFile = (PEAssembly *)FastInterlockCompareExchangePointer((void **)ppNativeFile, (void *)GetFile(), (void *)NULL); |
1534 | STRESS_LOG3(LF_ZAP,LL_INFO100,"Attempted to set new native file %p, old file was %p, location in the image=%p\n" ,GetFile(),pFile,ppNativeFile); |
1535 | if (pFile!=NULL) |
1536 | { |
1537 | // The non-shareable native image has already been used in this process by another Module. |
1538 | // We have to abandon the native image. (Note that it isn't enough to |
1539 | // just abandon the preload image, since the code in the file will |
1540 | // reference the image directly). |
1541 | m_dwReasonForRejectingNativeImage = ReasonForRejectingNativeImage_CannotShareNiAssemblyNotDomainNeutral; |
1542 | STRESS_LOG3(LF_ZAP,LL_INFO100,"Rejecting native file %p, because it is already used by file %p - reason 0x%x\n" ,GetFile(),pFile,m_dwReasonForRejectingNativeImage); |
1543 | |
1544 | ExternalLog(LL_WARNING, "ZAP: An ngen image of an assembly which " |
1545 | "is not loaded as domain-neutral cannot be used in multiple appdomains " |
1546 | "- abandoning ngen image. The assembly will be JIT-compiled in " |
1547 | "the second appdomain. See System.LoaderOptimization.MultiDomain " |
1548 | "for information about domain-neutral loading." ); |
1549 | GetFile()->ClearNativeImage(); |
1550 | |
1551 | // We only support a (non-shared) native image to be used from a single |
1552 | // AppDomain. Its not obvious if this is an implementation restriction, |
1553 | // or if this should fail DomainFile::CheckZapRequired(). |
1554 | // We err on the side of conservativeness, so that multi-domain tests |
1555 | // do not blow up in CheckZapRequired() |
1556 | GetFile()->SetCannotUseNativeImage(); |
1557 | } |
1558 | else |
1559 | { |
1560 | GetFile()->AddRef(); |
1561 | |
1562 | LOG((LF_ZAP, LL_INFO100, "ZAP: Found a candidate native image for %s\n" , GetSimpleName())); |
1563 | } |
1564 | } |
1565 | } |
1566 | |
1567 | if (!GetFile()->HasNativeImage()) |
1568 | { |
1569 | // |
1570 | // Verify that the IL image is consistent with the NGen images loaded into appdomain |
1571 | // |
1572 | |
1573 | AssemblySpec spec; |
1574 | spec.InitializeSpec(GetFile()); |
1575 | |
1576 | GUID mvid; |
1577 | GetFile()->GetMVID(&mvid); |
1578 | |
1579 | GetAppDomain()->CheckForMismatchedNativeImages(&spec, &mvid); |
1580 | } |
1581 | |
1582 | CheckZapRequired(); |
1583 | } |
1584 | #endif // FEATURE_PREJIT |
1585 | |
1586 | void DomainAssembly::Allocate() |
1587 | { |
1588 | CONTRACTL |
1589 | { |
1590 | INSTANCE_CHECK; |
1591 | STANDARD_VM_CHECK; |
1592 | INJECT_FAULT(COMPlusThrowOM();); |
1593 | } |
1594 | CONTRACTL_END; |
1595 | |
1596 | AllocMemTracker amTracker; |
1597 | AllocMemTracker * pamTracker = &amTracker; |
1598 | |
1599 | Assembly * pAssembly = m_pAssembly; |
1600 | |
1601 | if (pAssembly==NULL) |
1602 | { |
1603 | //! If you decide to remove "if" do not remove this brace: order is important here - in the case of an exception, |
1604 | //! the Assembly holder must destruct before the AllocMemTracker declared above. |
1605 | |
1606 | // We can now rely on the fact that our MDImport will not change so we can stop refcounting it. |
1607 | GetFile()->MakeMDImportPersistent(); |
1608 | |
1609 | NewHolder<Assembly> assemblyHolder(NULL); |
1610 | |
1611 | assemblyHolder = pAssembly = Assembly::Create(m_pDomain, GetFile(), GetDebuggerInfoBits(), this->IsCollectible(), pamTracker, this->IsCollectible() ? this->GetLoaderAllocator() : NULL); |
1612 | assemblyHolder->SetIsTenured(); |
1613 | |
1614 | //@todo! This is too early to be calling SuppressRelease. The right place to call it is below after |
1615 | // the CANNOTTHROWCOMPLUSEXCEPTION. Right now, we have to do this to unblock OOM injection testing quickly |
1616 | // as doing the right thing is nontrivial. |
1617 | pamTracker->SuppressRelease(); |
1618 | assemblyHolder.SuppressRelease(); |
1619 | } |
1620 | |
1621 | #ifdef FEATURE_COMINTEROP |
1622 | // If we are in an AppX process we should prevent loading of PIA in the AppDomain. |
1623 | // This will ensure that we do not run into any compatibility issues in case a type has both a co-Class and a Winrt Class |
1624 | if (AppX::IsAppXProcess() && pAssembly->IsPIA()) |
1625 | { |
1626 | COMPlusThrow(kNotSupportedException, W("NotSupported_PIAInAppxProcess" )); |
1627 | } |
1628 | #endif |
1629 | |
1630 | SetAssembly(pAssembly); |
1631 | |
1632 | #ifdef FEATURE_PREJIT |
1633 | BOOL fInsertIntoAssemblySpecBindingCache = TRUE; |
1634 | |
1635 | // Insert AssemblyDef details into AssemblySpecBindingCache if appropriate |
1636 | |
1637 | |
1638 | fInsertIntoAssemblySpecBindingCache = fInsertIntoAssemblySpecBindingCache && GetFile()->CanUseWithBindingCache(); |
1639 | |
1640 | if (fInsertIntoAssemblySpecBindingCache) |
1641 | { |
1642 | AssemblySpec specAssemblyDef; |
1643 | specAssemblyDef.InitializeSpec(GetFile()); |
1644 | if (specAssemblyDef.IsStrongNamed() && specAssemblyDef.HasPublicKey()) |
1645 | { |
1646 | specAssemblyDef.ConvertPublicKeyToToken(); |
1647 | } |
1648 | m_pDomain->AddAssemblyToCache(&specAssemblyDef, this); |
1649 | } |
1650 | #endif |
1651 | } // DomainAssembly::Allocate |
1652 | |
1653 | void DomainAssembly::DeliverAsyncEvents() |
1654 | { |
1655 | CONTRACTL |
1656 | { |
1657 | INSTANCE_CHECK; |
1658 | NOTHROW; |
1659 | GC_TRIGGERS; |
1660 | MODE_ANY; |
1661 | SO_INTOLERANT; |
1662 | } |
1663 | CONTRACTL_END; |
1664 | |
1665 | OVERRIDE_LOAD_LEVEL_LIMIT(FILE_ACTIVE); |
1666 | m_pDomain->RaiseLoadingAssemblyEvent(this); |
1667 | |
1668 | } |
1669 | |
1670 | |
1671 | void DomainAssembly::DeliverSyncEvents() |
1672 | { |
1673 | CONTRACTL |
1674 | { |
1675 | INSTANCE_CHECK; |
1676 | STANDARD_VM_CHECK; |
1677 | } |
1678 | CONTRACTL_END; |
1679 | |
1680 | GetCurrentModule()->NotifyEtwLoadFinished(S_OK); |
1681 | |
1682 | // We may be notified from inside the loader lock if we are delivering IJW events, so keep track. |
1683 | #ifdef PROFILING_SUPPORTED |
1684 | if (!IsProfilerNotified()) |
1685 | { |
1686 | SetProfilerNotified(); |
1687 | GetCurrentModule()->NotifyProfilerLoadFinished(S_OK); |
1688 | } |
1689 | |
1690 | #endif |
1691 | #ifdef DEBUGGING_SUPPORTED |
1692 | GCX_COOP(); |
1693 | if (!IsDebuggerNotified()) |
1694 | { |
1695 | SetShouldNotifyDebugger(); |
1696 | |
1697 | // Still work to do even if no debugger is attached. |
1698 | NotifyDebuggerLoad(ATTACH_ASSEMBLY_LOAD, FALSE); |
1699 | |
1700 | } |
1701 | #endif // DEBUGGING_SUPPORTED |
1702 | } // DomainAssembly::DeliverSyncEvents |
1703 | |
1704 | /* |
1705 | // The enum for dwLocation from managed code: |
1706 | public enum ResourceLocation |
1707 | { |
1708 | Embedded = 1, |
1709 | ContainedInAnotherAssembly = 2, |
1710 | ContainedInManifestFile = 4 |
1711 | } |
1712 | */ |
1713 | |
1714 | BOOL DomainAssembly::GetResource(LPCSTR szName, DWORD *cbResource, |
1715 | PBYTE *pbInMemoryResource, DomainAssembly** pAssemblyRef, |
1716 | LPCSTR *szFileName, DWORD *dwLocation, |
1717 | BOOL fSkipRaiseResolveEvent) |
1718 | { |
1719 | CONTRACTL |
1720 | { |
1721 | INSTANCE_CHECK; |
1722 | THROWS; |
1723 | MODE_ANY; |
1724 | INJECT_FAULT(COMPlusThrowOM();); |
1725 | } |
1726 | CONTRACTL_END; |
1727 | |
1728 | return GetFile()->GetResource( szName, |
1729 | cbResource, |
1730 | pbInMemoryResource, |
1731 | pAssemblyRef, |
1732 | szFileName, |
1733 | dwLocation, |
1734 | fSkipRaiseResolveEvent, |
1735 | this, |
1736 | this->m_pDomain ); |
1737 | } |
1738 | |
1739 | |
1740 | #ifdef FEATURE_PREJIT |
1741 | |
1742 | // -------------------------------------------------------------------------------- |
1743 | // Remember the timestamp of the CLR DLLs used to compile the ngen image. |
1744 | // These will be checked at runtime by PEFile::CheckNativeImageTimeStamp(). |
1745 | // |
1746 | |
1747 | void GetTimeStampsForNativeImage(CORCOMPILE_VERSION_INFO * pNativeVersionInfo) |
1748 | { |
1749 | CONTRACTL |
1750 | { |
1751 | STANDARD_VM_CHECK; |
1752 | PRECONDITION(::GetAppDomain()->IsCompilationDomain()); |
1753 | } |
1754 | CONTRACTL_END; |
1755 | |
1756 | // Do not store runtime timestamps into NGen image for cross-platform NGen determinism |
1757 | } |
1758 | |
1759 | // |
1760 | // Which processor should ngen target? |
1761 | // This is needed when ngen wants to target for "reach" if the ngen images will be |
1762 | // used on other machines (the Operating System or the OEM build lab can do this). |
1763 | // It can also be used to reduce the testing matrix |
1764 | // |
1765 | void GetNGenCpuInfo(CORINFO_CPU * cpuInfo) |
1766 | { |
1767 | LIMITED_METHOD_CONTRACT; |
1768 | |
1769 | #ifdef _TARGET_X86_ |
1770 | |
1771 | static CORINFO_CPU ngenCpuInfo = |
1772 | { |
1773 | (CPU_X86_PENTIUM_PRO << 8), // dwCPUType |
1774 | 0x00000000, // dwFeatures |
1775 | 0 // dwExtendedFeatures |
1776 | }; |
1777 | |
1778 | // We always generate P3-compatible code on CoreCLR |
1779 | *cpuInfo = ngenCpuInfo; |
1780 | |
1781 | #else // _TARGET_X86_ |
1782 | cpuInfo->dwCPUType = 0; |
1783 | cpuInfo->dwFeatures = 0; |
1784 | cpuInfo->dwExtendedFeatures = 0; |
1785 | #endif // _TARGET_X86_ |
1786 | } |
1787 | |
1788 | // -------------------------------------------------------------------------------- |
1789 | |
1790 | void DomainAssembly::GetCurrentVersionInfo(CORCOMPILE_VERSION_INFO *pNativeVersionInfo) |
1791 | { |
1792 | CONTRACTL |
1793 | { |
1794 | INSTANCE_CHECK; |
1795 | STANDARD_VM_CHECK; |
1796 | } |
1797 | CONTRACTL_END; |
1798 | |
1799 | // Clear memory so that we won't write random data into the zapped file |
1800 | ZeroMemory(pNativeVersionInfo, sizeof(CORCOMPILE_VERSION_INFO)); |
1801 | |
1802 | // Pick up any compilation directives for code flavor |
1803 | |
1804 | BOOL fForceDebug, fForceProfiling, fForceInstrument; |
1805 | SystemDomain::GetCompilationOverrides(&fForceDebug, |
1806 | &fForceProfiling, |
1807 | &fForceInstrument); |
1808 | |
1809 | #ifndef FEATURE_PAL |
1810 | pNativeVersionInfo->wOSPlatformID = VER_PLATFORM_WIN32_NT; |
1811 | #else |
1812 | pNativeVersionInfo->wOSPlatformID = VER_PLATFORM_UNIX; |
1813 | #endif |
1814 | |
1815 | // The native images should be OS-version agnostic. Do not store the actual OS version for determinism. |
1816 | // pNativeVersionInfo->wOSMajorVersion = (WORD) osInfo.dwMajorVersion; |
1817 | pNativeVersionInfo->wOSMajorVersion = 4; |
1818 | |
1819 | pNativeVersionInfo->wMachine = IMAGE_FILE_MACHINE_NATIVE_NI; |
1820 | |
1821 | pNativeVersionInfo->wVersionMajor = CLR_MAJOR_VERSION; |
1822 | pNativeVersionInfo->wVersionMinor = CLR_MINOR_VERSION; |
1823 | pNativeVersionInfo->wVersionBuildNumber = CLR_BUILD_VERSION; |
1824 | pNativeVersionInfo->wVersionPrivateBuildNumber = CLR_BUILD_VERSION_QFE; |
1825 | |
1826 | GetNGenCpuInfo(&pNativeVersionInfo->cpuInfo); |
1827 | |
1828 | #if _DEBUG |
1829 | pNativeVersionInfo->wBuild = CORCOMPILE_BUILD_CHECKED; |
1830 | #else |
1831 | pNativeVersionInfo->wBuild = CORCOMPILE_BUILD_FREE; |
1832 | #endif |
1833 | |
1834 | #ifdef DEBUGGING_SUPPORTED |
1835 | if (fForceDebug || !CORDebuggerAllowJITOpts(GetDebuggerInfoBits())) |
1836 | { |
1837 | pNativeVersionInfo->wCodegenFlags |= CORCOMPILE_CODEGEN_DEBUGGING; |
1838 | pNativeVersionInfo->wConfigFlags |= CORCOMPILE_CONFIG_DEBUG; |
1839 | } |
1840 | else |
1841 | #endif // DEBUGGING_SUPPORTED |
1842 | { |
1843 | pNativeVersionInfo->wConfigFlags |= CORCOMPILE_CONFIG_DEBUG_NONE; |
1844 | } |
1845 | |
1846 | #if defined (PROFILING_SUPPORTED_DATA) || defined(PROFILING_SUPPORTED) |
1847 | if (fForceProfiling || CORProfilerUseProfileImages()) |
1848 | { |
1849 | pNativeVersionInfo->wCodegenFlags |= CORCOMPILE_CODEGEN_PROFILING; |
1850 | pNativeVersionInfo->wConfigFlags |= CORCOMPILE_CONFIG_PROFILING; |
1851 | #ifdef DEBUGGING_SUPPORTED |
1852 | // Note that we have hardwired profiling to also imply optimized debugging |
1853 | // info. This cuts down on one permutation of prejit files. |
1854 | pNativeVersionInfo->wCodegenFlags &= ~CORCOMPILE_CODEGEN_DEBUGGING; |
1855 | pNativeVersionInfo->wConfigFlags &= ~(CORCOMPILE_CONFIG_DEBUG| |
1856 | CORCOMPILE_CONFIG_DEBUG_DEFAULT); |
1857 | pNativeVersionInfo->wConfigFlags |= CORCOMPILE_CONFIG_DEBUG_NONE; |
1858 | #endif // DEBUGGING_SUPPORTED |
1859 | } |
1860 | else |
1861 | #endif // PROFILING_SUPPORTED_DATA || PROFILING_SUPPORTED |
1862 | { |
1863 | pNativeVersionInfo->wConfigFlags |= CORCOMPILE_CONFIG_PROFILING_NONE; |
1864 | } |
1865 | |
1866 | #ifdef DEBUGGING_SUPPORTED |
1867 | |
1868 | // Note the default assembly flags (from the custom attributes & INI file) , so we can |
1869 | // set determine whether or not the current settings |
1870 | // match the "default" setting or not. |
1871 | |
1872 | // Note that the INI file settings are considered a part of the |
1873 | // assembly, even though they could theoretically change between |
1874 | // ngen time and runtime. It is just too expensive and awkward to |
1875 | // look up the INI file before binding to the native image at |
1876 | // runtime, so we effectively snapshot it at ngen time. |
1877 | |
1878 | DWORD defaultFlags = ComputeDebuggingConfig(); |
1879 | |
1880 | if (CORDebuggerAllowJITOpts(defaultFlags)) |
1881 | { |
1882 | // Default is optimized code |
1883 | if ((pNativeVersionInfo->wCodegenFlags & CORCOMPILE_CODEGEN_DEBUGGING) == 0) |
1884 | pNativeVersionInfo->wConfigFlags |= CORCOMPILE_CONFIG_DEBUG_DEFAULT; |
1885 | } |
1886 | else |
1887 | { |
1888 | // Default is non-optimized debuggable code |
1889 | if ((pNativeVersionInfo->wCodegenFlags & CORCOMPILE_CODEGEN_DEBUGGING) != 0) |
1890 | pNativeVersionInfo->wConfigFlags |= CORCOMPILE_CONFIG_DEBUG_DEFAULT; |
1891 | } |
1892 | |
1893 | #endif // DEBUGGING_SUPPORTED |
1894 | |
1895 | if (fForceInstrument || GetAssembly()->IsInstrumented()) |
1896 | { |
1897 | pNativeVersionInfo->wCodegenFlags |= CORCOMPILE_CODEGEN_PROF_INSTRUMENTING; |
1898 | pNativeVersionInfo->wConfigFlags |= CORCOMPILE_CONFIG_INSTRUMENTATION; |
1899 | } |
1900 | else |
1901 | { |
1902 | pNativeVersionInfo->wConfigFlags |= CORCOMPILE_CONFIG_INSTRUMENTATION_NONE; |
1903 | } |
1904 | |
1905 | |
1906 | GetTimeStampsForNativeImage(pNativeVersionInfo); |
1907 | |
1908 | // Store signature of source assembly. |
1909 | GetOptimizedIdentitySignature(&pNativeVersionInfo->sourceAssembly); |
1910 | |
1911 | // signature will is hash of the whole file. It is written by zapper. |
1912 | // IfFailThrow(CoCreateGuid(&pNativeVersionInfo->signature)); |
1913 | } |
1914 | |
1915 | void DomainAssembly::GetOptimizedIdentitySignature(CORCOMPILE_ASSEMBLY_SIGNATURE *pSignature) |
1916 | { |
1917 | CONTRACTL |
1918 | { |
1919 | INSTANCE_CHECK; |
1920 | THROWS; |
1921 | GC_TRIGGERS; |
1922 | MODE_ANY; |
1923 | INJECT_FAULT(COMPlusThrowOM();); |
1924 | } |
1925 | CONTRACTL_END; |
1926 | |
1927 | // |
1928 | // Write the MVID into the version header. |
1929 | // |
1930 | |
1931 | // |
1932 | // If this assembly has skip verification permission, then we store its |
1933 | // mvid. If at load time the assembly still has skip verification |
1934 | // permission, then we can base the matches purely on mvid values and |
1935 | // skip the perf-heavy hashing of the file. |
1936 | // |
1937 | |
1938 | // |
1939 | // The reason that we tell IsFullyTrusted to do a quick check |
1940 | // only is because that allows us make a determination for the most |
1941 | // common full trust scenarios (local machine) without actually |
1942 | // resolving policy and bringing in a whole list of assembly |
1943 | // dependencies. |
1944 | // |
1945 | ReleaseHolder<IMDInternalImport> scope (GetFile()->GetMDImportWithRef()); |
1946 | IfFailThrow(scope->GetScopeProps(NULL, &pSignature->mvid)); |
1947 | |
1948 | // Use the NGen image if posssible. IL image does not even have to be present on CoreCLR. |
1949 | if (GetFile()->HasNativeImage()) |
1950 | { |
1951 | PEImageHolder pNativeImage(GetFile()->GetNativeImageWithRef()); |
1952 | |
1953 | CORCOMPILE_VERSION_INFO* pVersionInfo = pNativeImage->GetLoadedLayout()->GetNativeVersionInfo(); |
1954 | pSignature->timeStamp = pVersionInfo->sourceAssembly.timeStamp; |
1955 | pSignature->ilImageSize = pVersionInfo->sourceAssembly.ilImageSize; |
1956 | |
1957 | return; |
1958 | } |
1959 | |
1960 | // Write the time stamp |
1961 | PEImageLayoutHolder ilLayout(GetFile()->GetAnyILWithRef()); |
1962 | pSignature->timeStamp = ilLayout->GetTimeDateStamp(); |
1963 | pSignature->ilImageSize = ilLayout->GetVirtualSize(); |
1964 | } |
1965 | |
1966 | BOOL DomainAssembly::CheckZapDependencyIdentities(PEImage *pNativeImage) |
1967 | { |
1968 | CONTRACTL |
1969 | { |
1970 | INSTANCE_CHECK; |
1971 | STANDARD_VM_CHECK; |
1972 | } |
1973 | CONTRACTL_END; |
1974 | |
1975 | AssemblySpec spec; |
1976 | spec.InitializeSpec(this->GetFile()); |
1977 | |
1978 | // The assembly spec should have the binding context associated with it |
1979 | _ASSERTE(spec.GetBindingContext() || spec.IsAssemblySpecForMscorlib()); |
1980 | |
1981 | CORCOMPILE_VERSION_INFO *pVersionInfo = pNativeImage->GetLoadedLayout()->GetNativeVersionInfo(); |
1982 | |
1983 | // Check our own assembly first |
1984 | GetAppDomain()->CheckForMismatchedNativeImages(&spec, &pVersionInfo->sourceAssembly.mvid); |
1985 | |
1986 | // Check MVID in metadata against MVID in CORCOMPILE_VERSION_INFO - important when metadata is loaded from IL instead of NI |
1987 | ReleaseHolder<IMDInternalImport> pImport(this->GetFile()->GetMDImportWithRef()); |
1988 | GUID mvid; |
1989 | IfFailThrow(pImport->GetScopeProps(NULL, &mvid)); |
1990 | GetAppDomain()->CheckForMismatchedNativeImages(&spec, &mvid); |
1991 | |
1992 | // Now Check dependencies |
1993 | COUNT_T cDependencies; |
1994 | CORCOMPILE_DEPENDENCY *pDependencies = pNativeImage->GetLoadedLayout()->GetNativeDependencies(&cDependencies); |
1995 | CORCOMPILE_DEPENDENCY *pDependenciesEnd = pDependencies + cDependencies; |
1996 | |
1997 | while (pDependencies < pDependenciesEnd) |
1998 | { |
1999 | if (pDependencies->dwAssemblyDef != mdAssemblyRefNil) |
2000 | { |
2001 | AssemblySpec name; |
2002 | name.InitializeSpec(pDependencies->dwAssemblyDef, pNativeImage->GetNativeMDImport(), this); |
2003 | |
2004 | if (!name.IsAssemblySpecForMscorlib()) |
2005 | { |
2006 | // We just initialized the assembly spec for the NI dependency. This will not have binding context |
2007 | // associated with it, so set it from that of the parent. |
2008 | _ASSERTE(!name.GetBindingContext()); |
2009 | ICLRPrivBinder *pParentAssemblyBindingContext = name.GetBindingContextFromParentAssembly(name.GetAppDomain()); |
2010 | _ASSERTE(pParentAssemblyBindingContext); |
2011 | name.SetBindingContext(pParentAssemblyBindingContext); |
2012 | } |
2013 | |
2014 | GetAppDomain()->CheckForMismatchedNativeImages(&name, &pDependencies->signAssemblyDef.mvid); |
2015 | } |
2016 | |
2017 | pDependencies++; |
2018 | } |
2019 | |
2020 | return TRUE; |
2021 | } |
2022 | #endif // FEATURE_PREJIT |
2023 | |
2024 | |
2025 | |
2026 | |
2027 | |
2028 | // <TODO>@todo Find a better place for these</TODO> |
2029 | #define DE_CUSTOM_VALUE_NAMESPACE "System.Diagnostics" |
2030 | #define DE_DEBUGGABLE_ATTRIBUTE_NAME "DebuggableAttribute" |
2031 | |
2032 | // <TODO>@todo .INI file is a temporary workaround for Beta 1</TODO> |
2033 | #define DE_INI_FILE_SECTION_NAME W(".NET Framework Debugging Control") |
2034 | #define DE_INI_FILE_KEY_TRACK_INFO W("GenerateTrackingInfo") |
2035 | #define DE_INI_FILE_KEY_ALLOW_JIT_OPTS W("AllowOptimize") |
2036 | |
2037 | DWORD DomainAssembly::ComputeDebuggingConfig() |
2038 | { |
2039 | CONTRACTL |
2040 | { |
2041 | INSTANCE_CHECK; |
2042 | THROWS; |
2043 | WRAPPER(GC_TRIGGERS); |
2044 | MODE_ANY; |
2045 | INJECT_FAULT(COMPlusThrowOM();); |
2046 | } |
2047 | CONTRACTL_END; |
2048 | |
2049 | #ifdef DEBUGGING_SUPPORTED |
2050 | DWORD dacfFlags = DACF_ALLOW_JIT_OPTS; |
2051 | |
2052 | if (GetDebuggingOverrides(&dacfFlags)) |
2053 | { |
2054 | dacfFlags |= DACF_USER_OVERRIDE; |
2055 | } |
2056 | else |
2057 | { |
2058 | IfFailThrow(GetDebuggingCustomAttributes(&dacfFlags)); |
2059 | } |
2060 | |
2061 | return dacfFlags; |
2062 | #else // !DEBUGGING_SUPPORTED |
2063 | return 0; |
2064 | #endif // DEBUGGING_SUPPORTED |
2065 | } |
2066 | |
2067 | void DomainAssembly::SetupDebuggingConfig(void) |
2068 | { |
2069 | CONTRACTL |
2070 | { |
2071 | INSTANCE_CHECK; |
2072 | THROWS; |
2073 | WRAPPER(GC_TRIGGERS); |
2074 | MODE_ANY; |
2075 | INJECT_FAULT(COMPlusThrowOM();); |
2076 | } |
2077 | CONTRACTL_END; |
2078 | |
2079 | #ifdef DEBUGGING_SUPPORTED |
2080 | DWORD dacfFlags = ComputeDebuggingConfig(); |
2081 | |
2082 | SetDebuggerInfoBits((DebuggerAssemblyControlFlags)dacfFlags); |
2083 | |
2084 | LOG((LF_CORDB, LL_INFO10, "Assembly %S: bits=0x%x\n" , GetDebugName(), GetDebuggerInfoBits())); |
2085 | #endif // DEBUGGING_SUPPORTED |
2086 | } |
2087 | |
2088 | // The format for the (temporary) .INI file is: |
2089 | |
2090 | // [.NET Framework Debugging Control] |
2091 | // GenerateTrackingInfo=<n> where n is 0 or 1 |
2092 | // AllowOptimize=<n> where n is 0 or 1 |
2093 | |
2094 | // Where neither x nor y equal INVALID_INI_INT: |
2095 | #define INVALID_INI_INT (0xFFFF) |
2096 | |
2097 | bool DomainAssembly::GetDebuggingOverrides(DWORD *pdwFlags) |
2098 | { |
2099 | CONTRACTL |
2100 | { |
2101 | INSTANCE_CHECK; |
2102 | THROWS; |
2103 | GC_NOTRIGGER; |
2104 | MODE_ANY; |
2105 | INJECT_FAULT(COMPlusThrowOM();); |
2106 | } |
2107 | CONTRACTL_END; |
2108 | |
2109 | #if defined(DEBUGGING_SUPPORTED) && !defined(FEATURE_CORESYSTEM) |
2110 | // TODO FIX in V5.0 |
2111 | // Any touch of the file system is relatively expensive even in the warm case. |
2112 | // |
2113 | // Ideally we remove the .INI feature completely (if we need something put it in the .exe.config file) |
2114 | // |
2115 | // However because of compatibility concerns, we won't do this until the next side-by-side release |
2116 | // In the mean time don't check in the case where we have already loaded the NGEN image, as the |
2117 | // JIT overrides don't mean anything in that case as we won't be jitting anyway. |
2118 | // This avoids doing these probes for framework DLLs right away. |
2119 | if (GetFile()->HasNativeImage()) |
2120 | return false; |
2121 | |
2122 | _ASSERTE(pdwFlags); |
2123 | |
2124 | bool fHasBits = false; |
2125 | WCHAR *pFileName = NULL; |
2126 | HRESULT hr = S_OK; |
2127 | UINT cbExtOrValue = 4; |
2128 | WCHAR *pTail = NULL; |
2129 | size_t len = 0; |
2130 | WCHAR *lpFileName = NULL; |
2131 | |
2132 | const WCHAR *wszFileName = GetFile()->GetPath(); |
2133 | |
2134 | if (wszFileName == NULL) |
2135 | { |
2136 | return false; |
2137 | } |
2138 | |
2139 | // lpFileName is a copy of the original, and will be edited. |
2140 | CQuickBytes qb; |
2141 | len = wcslen(wszFileName); |
2142 | size_t cchlpFileName = (len + 1); |
2143 | lpFileName = (WCHAR*)qb.AllocThrows(cchlpFileName * sizeof(WCHAR)); |
2144 | wcscpy_s(lpFileName, cchlpFileName, wszFileName); |
2145 | |
2146 | pFileName = wcsrchr(lpFileName, W('\\')); |
2147 | |
2148 | if (pFileName == NULL) |
2149 | { |
2150 | pFileName = lpFileName; |
2151 | } |
2152 | |
2153 | if (*pFileName == W('\\')) |
2154 | { |
2155 | pFileName++; //move the pointer past the last '\' |
2156 | } |
2157 | |
2158 | _ASSERTE(wcslen(W(".INI" )) == cbExtOrValue); |
2159 | |
2160 | if (pFileName == NULL || (pTail=wcsrchr(pFileName, W('.'))) == NULL || (wcslen(pTail)<cbExtOrValue)) |
2161 | { |
2162 | return false; |
2163 | } |
2164 | |
2165 | wcscpy_s(pTail, cchlpFileName - (pTail - lpFileName), W(".INI" )); |
2166 | |
2167 | // Win2K has a problem if multiple processes call GetPrivateProfile* on the same |
2168 | // non-existent .INI file simultaneously. The OS livelocks in the kernel (i.e. |
2169 | // outside of user space) and remains there at full CPU for several minutes. Then |
2170 | // it breaks out. Here is our work-around, while we pursue a fix in a future |
2171 | // version of the OS. |
2172 | if (WszGetFileAttributes(lpFileName) == INVALID_FILE_ATTRIBUTES) |
2173 | return false; |
2174 | |
2175 | // Having modified the filename, we use the full path |
2176 | // to actually get the file. |
2177 | if ((cbExtOrValue=WszGetPrivateProfileInt(DE_INI_FILE_SECTION_NAME, |
2178 | DE_INI_FILE_KEY_TRACK_INFO, |
2179 | INVALID_INI_INT, |
2180 | lpFileName)) != INVALID_INI_INT) |
2181 | { |
2182 | if (cbExtOrValue != 0) |
2183 | { |
2184 | *pdwFlags |= DACF_OBSOLETE_TRACK_JIT_INFO; |
2185 | } |
2186 | else |
2187 | { |
2188 | *pdwFlags &= (~DACF_OBSOLETE_TRACK_JIT_INFO); |
2189 | } |
2190 | |
2191 | fHasBits = true; |
2192 | } |
2193 | |
2194 | if ((cbExtOrValue=WszGetPrivateProfileInt(DE_INI_FILE_SECTION_NAME, |
2195 | DE_INI_FILE_KEY_ALLOW_JIT_OPTS, |
2196 | INVALID_INI_INT, |
2197 | lpFileName)) != INVALID_INI_INT) |
2198 | { |
2199 | if (cbExtOrValue != 0) |
2200 | { |
2201 | *pdwFlags |= DACF_ALLOW_JIT_OPTS; |
2202 | } |
2203 | else |
2204 | { |
2205 | *pdwFlags &= (~DACF_ALLOW_JIT_OPTS); |
2206 | } |
2207 | |
2208 | fHasBits = true; |
2209 | } |
2210 | |
2211 | return fHasBits; |
2212 | |
2213 | #else // DEBUGGING_SUPPORTED && !FEATURE_CORESYSTEM |
2214 | return false; |
2215 | #endif // DEBUGGING_SUPPORTED && !FEATURE_CORESYSTEM |
2216 | } |
2217 | |
2218 | |
2219 | // For right now, we only check to see if the DebuggableAttribute is present - later may add fields/properties to the |
2220 | // attributes. |
2221 | HRESULT DomainAssembly::GetDebuggingCustomAttributes(DWORD *pdwFlags) |
2222 | { |
2223 | CONTRACTL |
2224 | { |
2225 | INSTANCE_CHECK; |
2226 | NOTHROW; |
2227 | WRAPPER(GC_TRIGGERS); |
2228 | MODE_ANY; |
2229 | FORBID_FAULT; |
2230 | PRECONDITION(CheckPointer(pdwFlags)); |
2231 | } |
2232 | CONTRACTL_END; |
2233 | |
2234 | HRESULT hr = S_OK; |
2235 | |
2236 | #ifdef FEATURE_PREJIT |
2237 | ReleaseHolder<PEImage> pNativeImage=GetFile()->GetNativeImageWithRef(); |
2238 | if (pNativeImage) |
2239 | { |
2240 | CORCOMPILE_VERSION_INFO * pVersion = pNativeImage->GetLoadedLayout()->GetNativeVersionInfo(); |
2241 | PREFIX_ASSUME(pVersion != NULL); |
2242 | |
2243 | WORD codegen = pVersion->wCodegenFlags; |
2244 | |
2245 | if (codegen & CORCOMPILE_CODEGEN_DEBUGGING) |
2246 | { |
2247 | *pdwFlags &= (~DACF_ALLOW_JIT_OPTS); |
2248 | } |
2249 | else |
2250 | { |
2251 | *pdwFlags |= DACF_ALLOW_JIT_OPTS; |
2252 | } |
2253 | |
2254 | } |
2255 | else |
2256 | #endif // FEATURE_PREJIT |
2257 | { |
2258 | ULONG size; |
2259 | BYTE *blob; |
2260 | mdModule mdMod; |
2261 | ReleaseHolder<IMDInternalImport> mdImport(GetFile()->GetMDImportWithRef()); |
2262 | mdMod = mdImport->GetModuleFromScope(); |
2263 | mdAssembly asTK = TokenFromRid(mdtAssembly, 1); |
2264 | |
2265 | hr = mdImport->GetCustomAttributeByName(asTK, |
2266 | DE_CUSTOM_VALUE_NAMESPACE |
2267 | NAMESPACE_SEPARATOR_STR |
2268 | DE_DEBUGGABLE_ATTRIBUTE_NAME, |
2269 | (const void**)&blob, |
2270 | &size); |
2271 | |
2272 | // If there is no custom value, then there is no entrypoint defined. |
2273 | if (!(FAILED(hr) || hr == S_FALSE)) |
2274 | { |
2275 | // We're expecting a 6 or 8 byte blob: |
2276 | // |
2277 | // 1, 0, enable tracking, disable opts, 0, 0 |
2278 | if ((size == 6) || (size == 8)) |
2279 | { |
2280 | if (!((blob[0] == 1) && (blob[1] == 0))) |
2281 | { |
2282 | BAD_FORMAT_NOTHROW_ASSERT(!"Invalid blob format for custom attribute" ); |
2283 | return COR_E_BADIMAGEFORMAT; |
2284 | } |
2285 | |
2286 | if (blob[2] & 0x1) |
2287 | { |
2288 | *pdwFlags |= DACF_OBSOLETE_TRACK_JIT_INFO; |
2289 | } |
2290 | else |
2291 | { |
2292 | *pdwFlags &= (~DACF_OBSOLETE_TRACK_JIT_INFO); |
2293 | } |
2294 | |
2295 | if (blob[2] & 0x2) |
2296 | { |
2297 | *pdwFlags |= DACF_IGNORE_PDBS; |
2298 | } |
2299 | else |
2300 | { |
2301 | *pdwFlags &= (~DACF_IGNORE_PDBS); |
2302 | } |
2303 | |
2304 | |
2305 | // For compatibility, we enable optimizations if the tracking byte is zero, |
2306 | // even if disable opts is nonzero |
2307 | if (((blob[2] & 0x1) == 0) || (blob[3] == 0)) |
2308 | { |
2309 | *pdwFlags |= DACF_ALLOW_JIT_OPTS; |
2310 | } |
2311 | else |
2312 | { |
2313 | *pdwFlags &= (~DACF_ALLOW_JIT_OPTS); |
2314 | } |
2315 | |
2316 | LOG((LF_CORDB, LL_INFO10, "Assembly %S: has %s=%d,%d bits = 0x%x\n" , GetDebugName(), |
2317 | DE_DEBUGGABLE_ATTRIBUTE_NAME, |
2318 | blob[2], blob[3], *pdwFlags)); |
2319 | } |
2320 | } |
2321 | } |
2322 | |
2323 | return hr; |
2324 | } |
2325 | |
2326 | BOOL DomainAssembly::NotifyDebuggerLoad(int flags, BOOL attaching) |
2327 | { |
2328 | WRAPPER_NO_CONTRACT; |
2329 | |
2330 | BOOL result = FALSE; |
2331 | |
2332 | if (!IsVisibleToDebugger()) |
2333 | return FALSE; |
2334 | |
2335 | // Debugger Attach is done totally out-of-process. Does not call code in-proc. |
2336 | _ASSERTE(!attaching); |
2337 | |
2338 | // Make sure the debugger has been initialized. See code:Debugger::Startup. |
2339 | if (g_pDebugInterface == NULL) |
2340 | { |
2341 | _ASSERTE(!CORDebuggerAttached()); |
2342 | return FALSE; |
2343 | } |
2344 | |
2345 | // There is still work we need to do even when no debugger is attached. |
2346 | |
2347 | if (flags & ATTACH_ASSEMBLY_LOAD) |
2348 | { |
2349 | if (ShouldNotifyDebugger()) |
2350 | { |
2351 | g_pDebugInterface->LoadAssembly(this); |
2352 | } |
2353 | result = TRUE; |
2354 | } |
2355 | |
2356 | DomainModuleIterator i = IterateModules(kModIterIncludeLoading); |
2357 | while (i.Next()) |
2358 | { |
2359 | DomainFile * pDomainFile = i.GetDomainFile(); |
2360 | if(pDomainFile->ShouldNotifyDebugger()) |
2361 | { |
2362 | result = result || |
2363 | pDomainFile->GetModule()->NotifyDebuggerLoad(this->GetAppDomain(), pDomainFile, flags, attaching); |
2364 | } |
2365 | } |
2366 | if( ShouldNotifyDebugger()) |
2367 | { |
2368 | result|=m_pModule->NotifyDebuggerLoad(m_pDomain, this, ATTACH_MODULE_LOAD, attaching); |
2369 | SetDebuggerNotified(); |
2370 | } |
2371 | |
2372 | |
2373 | |
2374 | return result; |
2375 | } |
2376 | |
2377 | void DomainAssembly::NotifyDebuggerUnload() |
2378 | { |
2379 | LIMITED_METHOD_CONTRACT; |
2380 | |
2381 | if (!IsVisibleToDebugger()) |
2382 | return; |
2383 | |
2384 | if (!this->GetAppDomain()->IsDebuggerAttached()) |
2385 | return; |
2386 | |
2387 | m_fDebuggerUnloadStarted = TRUE; |
2388 | |
2389 | // Dispatch module unloads for all modules. Debugger is resilient in case we haven't dispatched |
2390 | // a previous load event (such as if debugger attached after the modules was loaded). |
2391 | DomainModuleIterator i = IterateModules(kModIterIncludeLoading); |
2392 | while (i.Next()) |
2393 | { |
2394 | i.GetDomainFile()->GetModule()->NotifyDebuggerUnload(this->GetAppDomain()); |
2395 | } |
2396 | |
2397 | g_pDebugInterface->UnloadAssembly(this); |
2398 | |
2399 | } |
2400 | |
2401 | // This will enumerate for static GC refs (but not thread static GC refs) |
2402 | |
2403 | void DomainAssembly::EnumStaticGCRefs(promote_func* fn, ScanContext* sc) |
2404 | { |
2405 | CONTRACT_VOID |
2406 | { |
2407 | NOTHROW; |
2408 | GC_NOTRIGGER; |
2409 | } |
2410 | CONTRACT_END; |
2411 | |
2412 | _ASSERTE(GCHeapUtilities::IsGCInProgress() && |
2413 | GCHeapUtilities::IsServerHeap() && |
2414 | IsGCSpecialThread()); |
2415 | |
2416 | DomainModuleIterator i = IterateModules(kModIterIncludeLoaded); |
2417 | while (i.Next()) |
2418 | { |
2419 | DomainFile* pDomainFile = i.GetDomainFile(); |
2420 | |
2421 | if (pDomainFile->IsActive()) |
2422 | { |
2423 | // We guarantee that at this point the module has it's DomainLocalModule set up |
2424 | // , as we create it while we load the module |
2425 | _ASSERTE(pDomainFile->GetLoadedModule()->GetDomainLocalModule(this->GetAppDomain())); |
2426 | pDomainFile->GetLoadedModule()->EnumRegularStaticGCRefs(this->GetAppDomain(), fn, sc); |
2427 | |
2428 | // We current to do not iterate over the ThreadLocalModules that correspond |
2429 | // to this Module. The GC discovers thread statics through the handle table. |
2430 | } |
2431 | } |
2432 | |
2433 | RETURN; |
2434 | } |
2435 | |
2436 | |
2437 | |
2438 | |
2439 | #endif // #ifndef DACCESS_COMPILE |
2440 | |
2441 | #ifdef DACCESS_COMPILE |
2442 | |
2443 | void |
2444 | DomainFile::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
2445 | { |
2446 | SUPPORTS_DAC; |
2447 | |
2448 | //sizeof(DomainFile) == 0x60 |
2449 | DAC_ENUM_VTHIS(); |
2450 | |
2451 | // Modules are needed for all minidumps, but they are enumerated elsewhere |
2452 | // so we don't need to duplicate effort; thus we do noting with m_pModule. |
2453 | |
2454 | // For MiniDumpNormal, we only want the file name. |
2455 | if (m_pFile.IsValid()) |
2456 | { |
2457 | m_pFile->EnumMemoryRegions(flags); |
2458 | } |
2459 | |
2460 | if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE |
2461 | && m_pDomain.IsValid()) |
2462 | { |
2463 | m_pDomain->EnumMemoryRegions(flags, true); |
2464 | } |
2465 | } |
2466 | |
2467 | void |
2468 | DomainAssembly::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
2469 | { |
2470 | SUPPORTS_DAC; |
2471 | |
2472 | //sizeof(DomainAssembly) == 0xe0 |
2473 | DAC_ENUM_VTHIS(); |
2474 | DomainFile::EnumMemoryRegions(flags); |
2475 | |
2476 | // For minidumps without full memory, we need to always be able to iterate over m_Modules. |
2477 | m_Modules.EnumMemoryRegions(flags); |
2478 | |
2479 | if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE) |
2480 | { |
2481 | if (m_pAssembly.IsValid()) |
2482 | { |
2483 | m_pAssembly->EnumMemoryRegions(flags); |
2484 | } |
2485 | } |
2486 | } |
2487 | |
2488 | |
2489 | #endif // #ifdef DACCESS_COMPILE |
2490 | |
2491 | |