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 // ===========================================================================
9 // Support for zap compiler and zap files
10 // ===========================================================================
18 #include <corcompile.h>
20 #include "assemblyspec.hpp"
31 #include "virtualcallstub.h"
32 #include "typeparse.h"
33 #include "typestring.h"
34 #include "constrainedexecutionregion.h"
35 #include "dllimport.h"
36 #include "comdelegate.h"
37 #include "stringarraylist.h"
39 #ifdef FEATURE_COMINTEROP
40 #include "clrtocomcall.h"
41 #include "comtoclrcall.h"
42 #include "winrttypenameconverter.h"
43 #endif // FEATURE_COMINTEROP
45 #include "dllimportcallback.h"
47 #include "sigbuilder.h"
49 #include "peimagelayout.inl"
51 #if defined(FEATURE_APPX_BINDER)
53 #include "clrprivbinderappx.h"
54 #include "clrprivtypecachewinrt.h"
55 #endif // defined(FEATURE_APPX_BINDER)
57 #ifdef FEATURE_COMINTEROP
58 #include "clrprivbinderwinrt.h"
59 #include "winrthelpers.h"
62 #ifdef CROSSGEN_COMPILE
63 #include "crossgenroresolvenamespace.h"
70 #ifdef FEATURE_PERFMAP
74 #include "argdestination.h"
76 #include "versionresilienthashcode.h"
78 #ifdef CROSSGEN_COMPILE
79 CompilationDomain * theDomain;
82 VerboseLevel g_CorCompileVerboseLevel = CORCOMPILE_NO_LOG;
85 // CEECompileInfo implements most of ICorCompileInfo
88 HRESULT CEECompileInfo::Startup( BOOL fForceDebug,
90 BOOL fForceInstrument)
92 SystemDomain::SetCompilationOverrides(fForceDebug,
98 m_fCachingOfInliningHintsEnabled = TRUE;
99 m_fGeneratingNgenPDB = FALSE;
101 _ASSERTE(!g_fEEStarted && !g_fEEInit && "You cannot run the EE inside an NGEN compilation process");
103 if (!g_fEEStarted && !g_fEEInit)
105 #ifdef CROSSGEN_COMPILE
106 GetSystemInfo(&g_SystemInfo);
108 theDomain = new CompilationDomain(fForceDebug,
113 // When NGEN'ing this call may execute EE code, e.g. the managed code to set up
115 hr = InitializeEE(COINITEE_DEFAULT);
119 // JIT interface expects to be called with
120 // preemptive GC enabled
124 Thread *pThread = GetThread();
128 GCX_PREEMP_NO_DTOR();
134 HRESULT CEECompileInfo::CreateDomain(ICorCompilationDomain **ppDomain,
135 IMetaDataAssemblyEmit *pEmitter,
137 BOOL fForceProfiling,
138 BOOL fForceInstrument,
139 BOOL fForceFulltrustDomain)
141 STANDARD_VM_CONTRACT;
143 COOPERATIVE_TRANSITION_BEGIN();
145 #ifndef CROSSGEN_COMPILE
146 AppDomainCreationHolder<CompilationDomain> pCompilationDomain;
148 pCompilationDomain.Assign(new CompilationDomain(fForceDebug,
152 CompilationDomain * pCompilationDomain = theDomain;
156 SystemDomain::LockHolder lh;
157 pCompilationDomain->Init();
161 pCompilationDomain->SetDependencyEmitter(pEmitter);
163 #if defined(FEATURE_APPX_BINDER)
164 if (AppX::IsAppXProcess())
167 ReleaseHolder<ICLRPrivBinder> pBinderInterface;
168 CLRPrivBinderAppX * pBinder = CLRPrivBinderAppX::GetOrCreateBinder();
170 IfFailThrow(pBinder->QueryInterface(IID_ICLRPrivBinder, &pBinderInterface));
171 pCompilationDomain->SetLoadContextHostBinder(pBinderInterface);
173 #endif // defined(FEATURE_APPX_BINDER)
175 #ifdef DEBUGGING_SUPPORTED
176 // Notify the debugger here, before the thread transitions into the
177 // AD to finish the setup, and before any assemblies are loaded into it.
178 SystemDomain::PublishAppDomainAndInformDebugger(pCompilationDomain);
179 #endif // DEBUGGING_SUPPORTED
181 pCompilationDomain->LoadSystemAssemblies();
183 pCompilationDomain->SetupSharedStatics();
185 *ppDomain = static_cast<ICorCompilationDomain*>(pCompilationDomain);
190 ENTER_DOMAIN_PTR(pCompilationDomain,ADV_COMPILATION)
192 if (fForceFulltrustDomain)
193 ((ApplicationSecurityDescriptor *)pCompilationDomain->GetSecurityDescriptor())->SetGrantedPermissionSet(NULL, NULL, 0xFFFFFFFF);
195 #ifndef CROSSGEN_COMPILE
197 pCompilationDomain->InitializeDomainContext(TRUE, NULL, NULL);
199 #ifndef CROSSGEN_COMPILE
201 if (!NingenEnabled())
203 APPDOMAINREF adRef = (APPDOMAINREF)pCompilationDomain->GetExposedObject();
204 GCPROTECT_BEGIN(adRef);
205 MethodDescCallSite initializeSecurity(METHOD__APP_DOMAIN__INITIALIZE_DOMAIN_SECURITY);
212 static_cast<ARG_SLOT>(FALSE)
214 initializeSecurity.Call(args);
222 // We load assemblies as domain-bound (However, they're compiled as domain neutral)
223 #ifdef FEATURE_LOADER_OPTIMIZATION
224 #ifdef FEATURE_FUSION
227 pCompilationDomain->SetSharePolicy(AppDomain::SHARE_POLICY_NEVER);
231 pCompilationDomain->SetupLoaderOptimization(AppDomain::SHARE_POLICY_NEVER);
233 #else //FEATURE_FUSION
234 pCompilationDomain->SetSharePolicy(AppDomain::SHARE_POLICY_NEVER);
235 #endif //FEATURE_FUSION
236 #endif // FEATURE_LOADER_OPTIMIZATION
238 #ifdef FEATURE_FUSION
239 CorCompileConfigFlags flags = PEFile::GetNativeImageConfigFlags(pCompilationDomain->m_fForceDebug,
240 pCompilationDomain->m_fForceProfiling,
241 pCompilationDomain->m_fForceInstrument);
243 FusionBind::SetApplicationContextDWORDProperty(GetAppDomain()->GetFusionContext(),
244 ACTAG_ZAP_CONFIG_FLAGS, flags);
245 #endif //FEATURE_FUSION
248 pCompilationDomain->SetFriendlyName(W("Compilation Domain"));
249 if (!NingenEnabled())
251 Security::SetDefaultAppDomainProperty(pCompilationDomain->GetSecurityDescriptor());
252 pCompilationDomain->GetSecurityDescriptor()->FinishInitialization();
254 SystemDomain::System()->LoadDomain(pCompilationDomain);
256 #ifndef CROSSGEN_COMPILE
257 pCompilationDomain.DoneCreating();
260 END_DOMAIN_TRANSITION;
263 COOPERATIVE_TRANSITION_END();
269 HRESULT CEECompileInfo::DestroyDomain(ICorCompilationDomain *pDomain)
271 STANDARD_VM_CONTRACT;
273 #ifndef CROSSGEN_COMPILE
274 COOPERATIVE_TRANSITION_BEGIN();
278 CompilationDomain *pCompilationDomain = (CompilationDomain *) pDomain;
280 // DDB 175659: Make sure that canCallNeedsRestore() returns FALSE during compilation
282 pCompilationDomain->setCannotCallNeedsRestore();
284 pCompilationDomain->Unload(TRUE);
286 COOPERATIVE_TRANSITION_END();
292 HRESULT MakeCrossDomainCallbackWorker(
293 CROSS_DOMAIN_CALLBACK pfnCallback,
296 STATIC_CONTRACT_MODE_COOPERATIVE;
297 STATIC_CONTRACT_SO_INTOLERANT;
299 HRESULT hrRetVal = E_UNEXPECTED;
300 BEGIN_SO_TOLERANT_CODE(GetThread());
301 hrRetVal = pfnCallback(pArgs);
302 END_SO_TOLERANT_CODE;
306 HRESULT CEECompileInfo::MakeCrossDomainCallback(
307 ICorCompilationDomain* pDomain,
308 CROSS_DOMAIN_CALLBACK pfnCallback,
311 STANDARD_VM_CONTRACT;
313 HRESULT hrRetVal = E_UNEXPECTED;
315 COOPERATIVE_TRANSITION_BEGIN();
318 // Switch to cooperative mode to switch appdomains
321 ENTER_DOMAIN_PTR((CompilationDomain*)pDomain,ADV_COMPILATION)
324 // Switch to preemptive mode on before calling back into
330 hrRetVal = MakeCrossDomainCallbackWorker(pfnCallback, pArgs);
332 END_DOMAIN_TRANSITION;
335 COOPERATIVE_TRANSITION_END();
340 #ifdef TRITON_STRESS_NEED_IMPL
341 int LogToSvcLogger(LPCWSTR format, ...)
343 STANDARD_VM_CONTRACT;
348 va_start(args, format);
349 s.VPrintf(format, args);
352 GetSvcLogger()->Printf(W("%s"), s.GetUnicode());
358 HRESULT CEECompileInfo::LoadAssemblyByPath(
361 // Normally this is FALSE, but crossgen /CreatePDB sets this to TRUE, so it can
362 // explicitly load an NI by path
363 BOOL fExplicitBindToNativeImage,
365 CORINFO_ASSEMBLY_HANDLE *pHandle)
367 STANDARD_VM_CONTRACT;
371 COOPERATIVE_TRANSITION_BEGIN();
373 Assembly * pAssembly;
374 HRESULT hrProcessLibraryBitnessMismatch = S_OK;
376 // We don't want to do a LoadFrom, since they do not work with ngen. Instead,
377 // read the metadata from the file and do a bind based on that.
381 // Pre-open the image so we can grab some metadata to help initialize the
382 // binder's AssemblySpec, which we'll use later to load the assembly for real.
384 PEImageHolder pImage;
389 pImage = PEImage::OpenImage(
392 // If we're explicitly binding to an NGEN image, we do not want the cache
393 // this PEImage for use later, as pointers that need fixup (e.g.,
394 // Module::m_pModuleSecurityDescriptor) will not be valid for use later.
395 // Normal caching is done when we open it "for real" further down when we
396 // call LoadDomainAssembly().
397 fExplicitBindToNativeImage ? MDInternalImport_NoCache : MDInternalImport_Default);
400 if (fExplicitBindToNativeImage && !pImage->HasReadyToRunHeader())
402 pImage->VerifyIsNIAssembly();
406 pImage->VerifyIsAssembly();
409 // Check to make sure the bitness of the assembly matches the bitness of the process
410 // we will be loading it into and store the result. If a COR_IMAGE_ERROR gets thrown
411 // by LoadAssembly then we can blame it on bitness mismatch. We do the check here
412 // and not in the CATCH to distinguish between the COR_IMAGE_ERROR that can be thrown by
413 // VerifyIsAssembly (not necessarily a bitness mismatch) and that from LoadAssembly
415 if (pImage->Has32BitNTHeaders())
417 hrProcessLibraryBitnessMismatch = PEFMT_E_32BIT;
420 if (!pImage->Has32BitNTHeaders())
422 hrProcessLibraryBitnessMismatch = PEFMT_E_64BIT;
427 spec.InitializeSpec(TokenFromRid(1, mdtAssembly), pImage->GetMDImport(), NULL, FALSE);
429 if (spec.IsMscorlib())
431 pAssembly = SystemDomain::System()->SystemAssembly();
435 AppDomain * pDomain = AppDomain::GetCurrentDomain();
437 PEAssemblyHolder pAssemblyHolder;
438 BOOL isWinRT = FALSE;
440 #ifdef FEATURE_COMINTEROP
441 isWinRT = spec.IsContentType_WindowsRuntime();
446 // It does not make sense to pass the file name to recieve fake type name for empty WinMDs, because we would use the name
447 // for binding in next call to BindAssemblySpec which would fail for fake WinRT type name
448 // We will throw/return the error instead and the caller will recognize it and react to it by not creating the ngen image -
449 // see code:Zapper::ComputeDependenciesInCurrentDomain
450 IfFailThrow(::GetFirstWinRTTypeDef(pImage->GetMDImport(), &szNameSpace, &szTypeName, NULL, NULL));
451 spec.SetWindowsRuntimeType(szNameSpace, szTypeName);
453 #endif //FEATURE_COMINTEROP
455 // If there is a host binder then use it to bind the assembly.
456 if (pDomain->HasLoadContextHostBinder() || isWinRT)
458 pAssemblyHolder = pDomain->BindAssemblySpec(&spec, TRUE, FALSE);
462 #ifdef FEATURE_FUSION
463 SafeComHolder<IBindResult> pNativeFusionAssembly;
464 SafeComHolder<IFusionBindLog> pFusionLog;
465 SafeComHolder<IAssembly> pFusionAssembly;
467 IfFailThrow(ExplicitBind(wzPath, pDomain->GetFusionContext(), EXPLICITBIND_FLAGS_EXE,
468 NULL, &pFusionAssembly, &pNativeFusionAssembly, &pFusionLog));
470 pAssemblyHolder = PEAssembly::Open(pFusionAssembly, pNativeFusionAssembly, pFusionLog, FALSE, FALSE);
471 #else //FEATURE_FUSION
473 CoreBindResult bindResult;
474 spec.SetCodeBase(pImage->GetPath());
477 TRUE, // fThrowOnFileNotFound
480 // fNgenExplicitBind: Generally during NGEN compilation, this is
481 // TRUE, meaning "I am NGEN, and I am doing an explicit bind to the IL
482 // image, so don't infer the NI and try to open it, because I already
483 // have it open". But if we're executing crossgen /CreatePDB, this should
484 // be FALSE so that downstream code doesn't assume we're explicitly
485 // trying to bind to an IL image (we're actually explicitly trying to
487 !fExplicitBindToNativeImage,
489 // fExplicitBindToNativeImage: Most callers want this FALSE; but crossgen
490 // /CreatePDB explicitly specifies NI names to open, and cannot assume
491 // that IL assemblies will be available.
492 fExplicitBindToNativeImage
494 pAssemblyHolder = PEAssembly::Open(&bindResult,FALSE,FALSE);
495 #endif //FEATURE_FUSION
498 // Now load assembly into domain.
499 DomainAssembly * pDomainAssembly = pDomain->LoadDomainAssembly(&spec, pAssemblyHolder, FILE_LOAD_BEGIN);
501 #ifndef FEATURE_APPX_BINDER
502 if (spec.CanUseWithBindingCache() && pDomainAssembly->CanUseWithBindingCache())
503 pDomain->AddAssemblyToCache(&spec, pDomainAssembly);
508 // Mark the assembly before it gets fully loaded and NGen image dependencies are verified. This is necessary
509 // to allow skipping compilation if there is NGen image already.
510 pDomainAssembly->GetFile()->SetSafeToHardBindTo();
513 pAssembly = pDomain->LoadAssembly(&spec, pAssemblyHolder, FILE_LOADED);
515 // Add a dependency to the current assembly. This is done to match the behavior
516 // of LoadAssemblyFusion, so that the same native image is generated whether we
517 // ngen install by file name or by assembly name.
518 pDomain->ToCompilationDomain()->AddDependency(&spec, pAssemblyHolder);
521 // Kind of a workaround - if we could have loaded this assembly via normal load,
523 *pHandle = CORINFO_ASSEMBLY_HANDLE(pAssembly);
525 EX_CATCH_HRESULT(hr);
527 if ( hrProcessLibraryBitnessMismatch != S_OK && ( hr == COR_E_BADIMAGEFORMAT || hr == HRESULT_FROM_WIN32(ERROR_BAD_EXE_FORMAT) ) )
529 hr = hrProcessLibraryBitnessMismatch;
532 COOPERATIVE_TRANSITION_END();
537 #ifdef FEATURE_FUSION
539 // Simple helper that factors out code common to LoadAssemblyByIAssemblyName and
540 // LoadAssemblyByName
541 static HRESULT LoadAssemblyByIAssemblyNameWorker(
542 IAssemblyName *pAssemblyName,
543 CORINFO_ASSEMBLY_HANDLE *pHandle)
551 INJECT_FAULT(COMPlusThrowOM(););
558 spec.InitializeSpec(pAssemblyName, NULL, FALSE);
560 if (spec.IsMscorlib())
562 pAssembly = SystemDomain::System()->SystemAssembly();
567 DomainAssembly * pDomainAssembly = spec.LoadDomainAssembly(FILE_LOAD_BEGIN);
569 // Mark the assembly before it gets fully loaded and NGen image dependencies are verified. This is necessary
570 // to allow skipping compilation if there is NGen image already.
571 pDomainAssembly->GetFile()->SetSafeToHardBindTo();
573 pAssembly = spec.LoadAssembly(FILE_LOADED);
577 // Return the module handle
580 *pHandle = CORINFO_ASSEMBLY_HANDLE(pAssembly);
585 HRESULT CEECompileInfo::LoadAssemblyByName(
587 CORINFO_ASSEMBLY_HANDLE *pHandle)
589 STANDARD_VM_CONTRACT;
593 COOPERATIVE_TRANSITION_BEGIN();
597 ReleaseHolder<IAssemblyName> pAssemblyName;
598 IfFailThrow(CreateAssemblyNameObject(&pAssemblyName, wzName, CANOF_PARSE_DISPLAY_NAME, NULL));
599 IfFailThrow(LoadAssemblyByIAssemblyNameWorker(pAssemblyName, pHandle));
601 EX_CATCH_HRESULT(hr);
603 COOPERATIVE_TRANSITION_END();
608 HRESULT CEECompileInfo::LoadAssemblyRef(
609 IMDInternalImport *pAssemblyImport,
611 CORINFO_ASSEMBLY_HANDLE *pHandle,
612 IAssemblyName **refAssemblyName /*=NULL*/)
614 STANDARD_VM_CONTRACT;
618 ReleaseHolder<IAssemblyName> pAssemblyName;
620 COOPERATIVE_TRANSITION_BEGIN();
627 *refAssemblyName = NULL;
630 spec.InitializeSpec(ref, pAssemblyImport, NULL, FALSE);
632 if (spec.HasBindableIdentity())
636 IfFailThrow(spec.CreateFusionName(&pAssemblyName));
639 pAssembly = spec.LoadAssembly(FILE_LOADED);
642 // Return the module handle
645 *pHandle = CORINFO_ASSEMBLY_HANDLE(pAssembly);
648 { // Cannot load assembly refs with non-unique id.
652 EX_CATCH_HRESULT(hr);
654 COOPERATIVE_TRANSITION_END();
656 if (refAssemblyName != NULL && pAssemblyName != NULL)
658 *refAssemblyName = pAssemblyName.Extract();
664 HRESULT CEECompileInfo::LoadAssemblyByIAssemblyName(
665 IAssemblyName *pAssemblyName,
666 CORINFO_ASSEMBLY_HANDLE *pHandle
669 STANDARD_VM_CONTRACT;
673 COOPERATIVE_TRANSITION_BEGIN();
677 IfFailThrow(LoadAssemblyByIAssemblyNameWorker(pAssemblyName, pHandle));
679 EX_CATCH_HRESULT(hr);
681 COOPERATIVE_TRANSITION_END();
686 #endif //FEATURE_FUSION
688 #ifdef FEATURE_COMINTEROP
689 HRESULT CEECompileInfo::LoadTypeRefWinRT(
690 IMDInternalImport *pAssemblyImport,
692 CORINFO_ASSEMBLY_HANDLE *pHandle)
694 STANDARD_VM_CONTRACT;
698 ReleaseHolder<IAssemblyName> pAssemblyName;
700 COOPERATIVE_TRANSITION_BEGIN();
706 mdToken tkResolutionScope;
707 if(FAILED(pAssemblyImport->GetResolutionScopeOfTypeRef(ref, &tkResolutionScope)))
709 else if(TypeFromToken(tkResolutionScope) == mdtAssemblyRef)
711 DWORD dwAssemblyRefFlags;
712 IfFailThrow(pAssemblyImport->GetAssemblyRefProps(tkResolutionScope, NULL, NULL,
714 NULL, NULL, &dwAssemblyRefFlags));
715 if (IsAfContentType_WindowsRuntime(dwAssemblyRefFlags))
719 pAssemblyImport->GetNameOfTypeRef(ref, &psznamespace, &pszname);
721 spec.InitializeSpec(tkResolutionScope, pAssemblyImport, NULL, FALSE);
722 spec.SetWindowsRuntimeType(psznamespace, pszname);
724 _ASSERTE(spec.HasBindableIdentity());
726 pAssembly = spec.LoadAssembly(FILE_LOADED);
729 // Return the module handle
732 *pHandle = CORINFO_ASSEMBLY_HANDLE(pAssembly);
744 EX_CATCH_HRESULT(hr);
746 COOPERATIVE_TRANSITION_END();
752 BOOL CEECompileInfo::IsInCurrentVersionBubble(CORINFO_MODULE_HANDLE hModule)
756 return ((Module*)hModule)->IsInCurrentVersionBubble();
759 HRESULT CEECompileInfo::LoadAssemblyModule(
760 CORINFO_ASSEMBLY_HANDLE assembly,
762 CORINFO_MODULE_HANDLE *pHandle)
764 STANDARD_VM_CONTRACT;
766 COOPERATIVE_TRANSITION_BEGIN();
768 Assembly *pAssembly = (Assembly*) assembly;
770 Module *pModule = pAssembly->GetManifestModule()->LoadModule(GetAppDomain(), file, TRUE)->GetModule();
773 // Return the module handle
776 *pHandle = CORINFO_MODULE_HANDLE(pModule);
778 COOPERATIVE_TRANSITION_END();
784 BOOL CEECompileInfo::CheckAssemblyZap(
785 CORINFO_ASSEMBLY_HANDLE assembly,
786 __out_ecount_opt(*cAssemblyManifestModulePath)
787 LPWSTR assemblyManifestModulePath,
788 LPDWORD cAssemblyManifestModulePath)
790 STANDARD_VM_CONTRACT;
794 COOPERATIVE_TRANSITION_BEGIN();
796 Assembly *pAssembly = (Assembly*) assembly;
798 if (pAssembly->GetManifestFile()->HasNativeImage())
800 PEImage *pImage = pAssembly->GetManifestFile()->GetPersistentNativeImage();
802 if (assemblyManifestModulePath != NULL)
804 DWORD length = pImage->GetPath().GetCount();
805 if (length > *cAssemblyManifestModulePath)
807 length = *cAssemblyManifestModulePath - 1;
808 wcsncpy_s(assemblyManifestModulePath, *cAssemblyManifestModulePath, pImage->GetPath(), length);
809 assemblyManifestModulePath[length] = 0;
812 wcscpy_s(assemblyManifestModulePath, *cAssemblyManifestModulePath, pImage->GetPath());
818 COOPERATIVE_TRANSITION_END();
823 HRESULT CEECompileInfo::SetCompilationTarget(CORINFO_ASSEMBLY_HANDLE assembly,
824 CORINFO_MODULE_HANDLE module)
826 STANDARD_VM_CONTRACT;
828 Assembly *pAssembly = (Assembly *) assembly;
829 Module *pModule = (Module *) module;
831 CompilationDomain *pDomain = (CompilationDomain *) GetAppDomain();
832 pDomain->SetTarget(pAssembly, pModule);
834 if (!pAssembly->IsSystem())
836 // It is possible to get through a compile without calling BindAssemblySpec on mscorlib. This
837 // is because refs to mscorlib are short circuited in a number of places. So, we will explicitly
838 // add it to our dependencies.
840 AssemblySpec mscorlib;
841 mscorlib.InitializeSpec(SystemDomain::SystemFile());
842 GetAppDomain()->BindAssemblySpec(&mscorlib,TRUE,FALSE);
844 if (!IsReadyToRunCompilation() && !SystemDomain::SystemFile()->HasNativeImage())
846 if (!CLRConfig::GetConfigValue(CLRConfig::INTERNAL_NgenAllowMscorlibSoftbind))
848 return NGEN_E_SYS_ASM_NI_MISSING;
853 #ifdef FEATURE_READYTORUN_COMPILER
854 if (IsReadyToRunCompilation() && !pModule->IsILOnly())
856 GetSvcLogger()->Printf(LogLevel_Error, W("Error: /readytorun not supported for mixed mode assemblies\n"));
861 #ifdef FEATURE_READYTORUN_COMPILER
862 if (IsReadyToRunCompilation() && !pModule->IsILOnly())
864 GetSvcLogger()->Printf(LogLevel_Error, W("Error: /readytorun not supported for mixed mode assemblies\n"));
873 CEECompileInfo::GetAssemblyMetaDataImport(CORINFO_ASSEMBLY_HANDLE assembly)
875 STANDARD_VM_CONTRACT;
877 IMDInternalImport * import;
879 COOPERATIVE_TRANSITION_BEGIN();
881 import = ((Assembly*)assembly)->GetManifestImport();
884 COOPERATIVE_TRANSITION_END();
890 CEECompileInfo::GetModuleMetaDataImport(CORINFO_MODULE_HANDLE scope)
892 STANDARD_VM_CONTRACT;
894 IMDInternalImport * import;
896 COOPERATIVE_TRANSITION_BEGIN();
898 import = ((Module*)scope)->GetMDImport();
901 COOPERATIVE_TRANSITION_END();
906 CORINFO_MODULE_HANDLE
907 CEECompileInfo::GetAssemblyModule(CORINFO_ASSEMBLY_HANDLE assembly)
909 STANDARD_VM_CONTRACT;
911 CANNOTTHROWCOMPLUSEXCEPTION();
913 return (CORINFO_MODULE_HANDLE) ((Assembly*)assembly)->GetManifestModule();
916 PEDecoder * CEECompileInfo::GetModuleDecoder(CORINFO_MODULE_HANDLE scope)
918 STANDARD_VM_CONTRACT;
922 COOPERATIVE_TRANSITION_BEGIN();
925 // Note that we go ahead and return the native image if we are using that.
926 // It contains everything we need to ngen. However, the caller must be
927 // aware and check for the native image case, since some fields will need to come
928 // from the CORCOMPILE_ZAP_HEADER rather than the PE headers.
931 PEFile *pFile = ((Module *) scope)->GetFile();
933 if (pFile->HasNativeImage())
934 result = pFile->GetLoadedNative();
936 result = pFile->GetLoadedIL();
938 COOPERATIVE_TRANSITION_END();
944 void CEECompileInfo::GetModuleFileName(CORINFO_MODULE_HANDLE scope,
947 STANDARD_VM_CONTRACT;
949 COOPERATIVE_TRANSITION_BEGIN();
951 result.Set(((Module*)scope)->GetPath());
953 COOPERATIVE_TRANSITION_END();
956 CORINFO_ASSEMBLY_HANDLE
957 CEECompileInfo::GetModuleAssembly(CORINFO_MODULE_HANDLE module)
959 STANDARD_VM_CONTRACT;
961 CANNOTTHROWCOMPLUSEXCEPTION();
963 return (CORINFO_ASSEMBLY_HANDLE) GetModule(module)->GetAssembly();
966 #ifdef FEATURE_FUSION
967 HRESULT CEECompileInfo::GetAssemblyName(
968 CORINFO_ASSEMBLY_HANDLE hAssembly,
970 __out_z LPWSTR wzAssemblyName,
971 LPDWORD pcchAssemblyName)
973 STANDARD_VM_CONTRACT;
975 _ASSERTE(hAssembly != NULL);
976 if (hAssembly == NULL)
984 Assembly *pAssembly = (Assembly *) hAssembly;
985 IAssemblyName * pAssemblyName = pAssembly->GetFusionAssemblyName();
986 if (dwFlags == GANF_Default)
988 hr = pAssemblyName->GetDisplayName(wzAssemblyName, pcchAssemblyName, 0);
990 else if (dwFlags == GANF_Simple)
992 DWORD cbAssemblyName = *pcchAssemblyName * sizeof(WCHAR);
993 hr = pAssemblyName->GetProperty(ASM_NAME_NAME, (LPVOID)wzAssemblyName, &cbAssemblyName);
994 *pcchAssemblyName = cbAssemblyName / sizeof(WCHAR);
997 EX_CATCH_HRESULT(hr);
1001 #endif //FEATURE_FUSION
1003 #ifdef CROSSGEN_COMPILE
1005 // Small wrapper to avoid having too many crossgen ifdefs
1007 class AssemblyForLoadHint
1009 IMDInternalImport * m_pMDImport;
1011 AssemblyForLoadHint(IMDInternalImport * pMDImport)
1012 : m_pMDImport(pMDImport)
1016 IMDInternalImport * GetManifestImport()
1021 LPCSTR GetSimpleName()
1024 IfFailThrow(m_pMDImport->GetAssemblyProps(TokenFromRid(1, mdtAssembly), NULL, NULL, NULL, &name, NULL, NULL));
1028 void GetDisplayName(SString &result, DWORD flags = 0)
1030 PEAssembly::GetFullyQualifiedAssemblyName(m_pMDImport, TokenFromRid(1, mdtAssembly), result, flags);
1040 //-----------------------------------------------------------------------------
1041 // For an assembly with a full name of "Foo, Version=2.0.0.0, Culture=neutral",
1042 // we want any of these attributes specifications to match:
1043 // DependencyAttribute("Foo", LoadHint.Always)
1044 // DependencyAttribute("Foo,", LoadHint.Always)
1045 // DependencyAttribute("Foo, Version=2.0.0.0, Culture=neutral", LoadHint.Always)
1046 // The second case of "Foo," is needed only for intra-V2 compat as
1047 // it was supported at one point during V2. We may be able to get rid of it.
1048 template <typename ASSEMBLY>
1049 BOOL IsAssemblySpecifiedInCA(ASSEMBLY * pAssembly, SString dependencyNameFromCA)
1051 STANDARD_VM_CONTRACT;
1053 // First, check for this:
1054 // DependencyAttribute("Foo", LoadHint.Always)
1055 StackSString simpleName(SString::Utf8, pAssembly->GetSimpleName());
1056 if (simpleName.EqualsCaseInsensitive(dependencyNameFromCA, PEImage::GetFileSystemLocale()))
1059 // Now, check for this:
1060 // DependencyAttribute("Foo,", LoadHint.Always)
1061 SString comma(W(","));
1062 StackSString simpleNameWithComma(simpleName, comma);
1063 if (simpleNameWithComma.EqualsCaseInsensitive(dependencyNameFromCA, PEImage::GetFileSystemLocale()))
1067 // DependencyAttribute("Foo, Version=2.0.0.0, Culture=neutral", LoadHint.Always)
1068 StackSString fullName;
1069 pAssembly->GetDisplayName(fullName);
1070 if (fullName.EqualsCaseInsensitive(dependencyNameFromCA))
1076 template <typename ASSEMBLY>
1077 void GetLoadHint(ASSEMBLY * pAssembly, ASSEMBLY *pAssemblyDependency,
1078 LoadHintEnum *loadHint, LoadHintEnum *defaultLoadHint = NULL)
1080 STANDARD_VM_CONTRACT;
1082 *loadHint = LoadDefault;
1084 if (g_pConfig->NgenHardBind() == EEConfig::NGEN_HARD_BIND_ALL)
1085 *loadHint = LoadAlways;
1087 const BYTE *pbAttr; // Custom attribute data as a BYTE*.
1088 ULONG cbAttr; // Size of custom attribute data.
1091 // Look for the binding custom attribute
1093 IMDInternalImport *pImport = pAssembly->GetManifestImport();
1095 IfFailThrow(pImport->GetAssemblyFromScope(&mdAssembly));
1097 MDEnumHolder hEnum(pImport); // Enumerator for custom attributes
1098 IfFailThrow(pImport->EnumCustomAttributeByNameInit(mdAssembly, DEPENDENCY_TYPE, &hEnum));
1100 mdCustomAttribute tkAttribute; // A custom attribute on this assembly.
1101 while (pImport->EnumNext(&hEnum, &tkAttribute))
1103 // Get raw custom attribute.
1104 IfFailThrow(pImport->GetCustomAttributeAsBlob(tkAttribute, (const void**)&pbAttr, &cbAttr));
1106 CustomAttributeParser cap(pbAttr, cbAttr);
1108 IfFailThrow(cap.ValidateProlog());
1110 // Extract string from custom attribute
1113 IfFailThrow(cap.GetNonNullString(&szString, &cbString));
1115 // Convert the string to Unicode.
1116 StackSString dependencyNameFromCA(SString::Utf8, szString, cbString);
1118 if (IsAssemblySpecifiedInCA(pAssemblyDependency, dependencyNameFromCA))
1120 // Get dependency setting
1122 IfFailThrow(cap.GetU4(&u4));
1123 *loadHint = (LoadHintEnum)u4;
1129 // If not preference is specified, look for the built-in assembly preference
1130 if (*loadHint == LoadDefault || defaultLoadHint != NULL)
1132 IMDInternalImport *pImportDependency = pAssemblyDependency->GetManifestImport();
1134 IfFailThrow(pImportDependency->GetAssemblyFromScope(&mdAssembly));
1136 HRESULT hr = pImportDependency->GetCustomAttributeByName(mdAssembly,
1137 DEFAULTDEPENDENCY_TYPE,
1138 (const void**)&pbAttr, &cbAttr);
1141 // Parse the attribute
1144 CustomAttributeParser cap(pbAttr, cbAttr);
1145 IfFailThrow(cap.ValidateProlog());
1147 // Get default bind setting
1149 IfFailThrow(cap.GetU4(&u4));
1151 if (pAssemblyDependency->IsSystem() && CLRConfig::GetConfigValue(CLRConfig::INTERNAL_NgenAllowMscorlibSoftbind))
1156 if (defaultLoadHint)
1157 *defaultLoadHint = (LoadHintEnum) u4;
1159 *loadHint = (LoadHintEnum) u4;
1164 HRESULT CEECompileInfo::GetLoadHint(CORINFO_ASSEMBLY_HANDLE hAssembly,
1165 CORINFO_ASSEMBLY_HANDLE hAssemblyDependency,
1166 LoadHintEnum *loadHint,
1167 LoadHintEnum *defaultLoadHint)
1169 STANDARD_VM_CONTRACT;
1175 Assembly *pAssembly = (Assembly *) hAssembly;
1176 Assembly *pAssemblyDependency = (Assembly *) hAssemblyDependency;
1178 ::GetLoadHint(pAssembly, pAssemblyDependency, loadHint, defaultLoadHint);
1180 EX_CATCH_HRESULT(hr);
1185 HRESULT CEECompileInfo::GetAssemblyVersionInfo(CORINFO_ASSEMBLY_HANDLE hAssembly,
1186 CORCOMPILE_VERSION_INFO *pInfo)
1188 STANDARD_VM_CONTRACT;
1190 Assembly *pAssembly = (Assembly *) hAssembly;
1192 pAssembly->GetDomainAssembly()->GetCurrentVersionInfo(pInfo);
1197 void CEECompileInfo::GetAssemblyCodeBase(CORINFO_ASSEMBLY_HANDLE hAssembly, SString &result)
1199 STANDARD_VM_CONTRACT;
1201 COOPERATIVE_TRANSITION_BEGIN();
1203 Assembly *pAssembly = (Assembly *)hAssembly;
1204 _ASSERTE(pAssembly != NULL);
1206 pAssembly->GetCodeBase(result);
1208 COOPERATIVE_TRANSITION_END();
1211 //=================================================================================
1213 void FakePromote(PTR_PTR_Object ppObj, ScanContext *pSC, uint32_t dwFlags)
1221 _ASSERTE(*ppObj == NULL);
1222 *(CORCOMPILE_GCREFMAP_TOKENS *)ppObj = (dwFlags & GC_CALL_INTERIOR) ? GCREFMAP_INTERIOR : GCREFMAP_REF;
1225 //=================================================================================
1227 void FakePromoteCarefully(promote_func *fn, Object **ppObj, ScanContext *pSC, uint32_t dwFlags)
1229 (*fn)(ppObj, pSC, dwFlags);
1232 //=================================================================================
1234 void FakeGcScanRoots(MetaSig& msig, ArgIterator& argit, MethodDesc * pMD, BYTE * pFrame)
1236 STANDARD_VM_CONTRACT;
1240 // Encode generic instantiation arg
1241 if (argit.HasParamType())
1243 // Note that intrinsic array methods have hidden instantiation arg too, but it is not reported to GC
1244 if (pMD->RequiresInstMethodDescArg())
1245 *(CORCOMPILE_GCREFMAP_TOKENS *)(pFrame + argit.GetParamTypeArgOffset()) = GCREFMAP_METHOD_PARAM;
1247 if (pMD->RequiresInstMethodTableArg())
1248 *(CORCOMPILE_GCREFMAP_TOKENS *)(pFrame + argit.GetParamTypeArgOffset()) = GCREFMAP_TYPE_PARAM;
1251 // If the function has a this pointer, add it to the mask
1252 if (argit.HasThis())
1254 BOOL interior = pMD->GetMethodTable()->IsValueType() && !pMD->IsUnboxingStub();
1256 FakePromote((Object **)(pFrame + argit.GetThisOffset()), &sc, interior ? GC_CALL_INTERIOR : 0);
1259 if (argit.IsVarArg())
1261 *(CORCOMPILE_GCREFMAP_TOKENS *)(pFrame + argit.GetVASigCookieOffset()) = GCREFMAP_VASIG_COOKIE;
1263 // We are done for varargs - the remaining arguments are reported via vasig cookie
1267 // Also if the method has a return buffer, then it is the first argument, and could be an interior ref,
1268 // so always promote it.
1269 if (argit.HasRetBuffArg())
1271 FakePromote((Object **)(pFrame + argit.GetRetBuffArgOffset()), &sc, GC_CALL_INTERIOR);
1275 // Now iterate the arguments
1278 // Cycle through the arguments, and call msig.GcScanRoots for each
1280 while ((argOffset = argit.GetNextOffset()) != TransitionBlock::InvalidOffset)
1282 ArgDestination argDest(pFrame, argOffset, argit.GetArgLocDescForStructInRegs());
1283 msig.GcScanRoots(&argDest, &FakePromote, &sc, &FakePromoteCarefully);
1287 void CEECompileInfo::GetCallRefMap(CORINFO_METHOD_HANDLE hMethod, GCRefMapBuilder * pBuilder)
1290 DWORD dwInitialLength = pBuilder->GetBlobLength();
1291 UINT nTokensWritten = 0;
1294 MethodDesc *pMD = (MethodDesc *)hMethod;
1297 ArgIterator argit(&msig);
1299 UINT nStackBytes = argit.SizeOfFrameArgumentArray();
1301 // Allocate a fake stack
1302 CQuickBytes qbFakeStack;
1303 qbFakeStack.AllocThrows(sizeof(TransitionBlock) + nStackBytes);
1304 memset(qbFakeStack.Ptr(), 0, qbFakeStack.Size());
1306 BYTE * pFrame = (BYTE *)qbFakeStack.Ptr();
1309 FakeGcScanRoots(msig, argit, pMD, pFrame);
1312 // Encode the ref map
1318 UINT cbStackPop = argit.CbStackPop();
1319 pBuilder->WriteStackPop(cbStackPop / sizeof(TADDR));
1321 nStackSlots = nStackBytes / sizeof(TADDR) + NUM_ARGUMENT_REGISTERS;
1323 nStackSlots = (sizeof(TransitionBlock) + nStackBytes - TransitionBlock::GetOffsetOfArgumentRegisters()) / sizeof(TADDR);
1326 for (UINT pos = 0; pos < nStackSlots; pos++)
1331 ofs = (pos < NUM_ARGUMENT_REGISTERS) ?
1332 (TransitionBlock::GetOffsetOfArgumentRegisters() + ARGUMENTREGISTERS_SIZE - (pos + 1) * sizeof(TADDR)) :
1333 (TransitionBlock::GetOffsetOfArgs() + (pos - NUM_ARGUMENT_REGISTERS) * sizeof(TADDR));
1335 ofs = TransitionBlock::GetOffsetOfArgumentRegisters() + pos * sizeof(TADDR);
1338 CORCOMPILE_GCREFMAP_TOKENS token = *(CORCOMPILE_GCREFMAP_TOKENS *)(pFrame + ofs);
1342 INDEBUG(nTokensWritten++;)
1343 pBuilder->WriteToken(pos, token);
1352 // Verify that decoder produces what got encoded
1355 DWORD dwFinalLength;
1356 PVOID pBlob = pBuilder->GetBlob(&dwFinalLength);
1358 UINT nTokensDecoded = 0;
1360 GCRefMapDecoder decoder((BYTE *)pBlob + dwInitialLength);
1363 _ASSERTE(decoder.ReadStackPop() * sizeof(TADDR) == cbStackPop);
1366 while (!decoder.AtEnd())
1368 int pos = decoder.CurrentPos();
1369 int token = decoder.ReadToken();
1374 ofs = (pos < NUM_ARGUMENT_REGISTERS) ?
1375 (TransitionBlock::GetOffsetOfArgumentRegisters() + ARGUMENTREGISTERS_SIZE - (pos + 1) * sizeof(TADDR)) :
1376 (TransitionBlock::GetOffsetOfArgs() + (pos - NUM_ARGUMENT_REGISTERS) * sizeof(TADDR));
1378 ofs = TransitionBlock::GetOffsetOfArgumentRegisters() + pos * sizeof(TADDR);
1383 _ASSERTE(*(CORCOMPILE_GCREFMAP_TOKENS *)(pFrame + ofs) == token);
1388 // Verify that all tokens got decoded.
1389 _ASSERTE(nTokensWritten == nTokensDecoded);
1393 void CEECompileInfo::CompressDebugInfo(
1394 IN ICorDebugInfo::OffsetMapping * pOffsetMapping,
1395 IN ULONG iOffsetMapping,
1396 IN ICorDebugInfo::NativeVarInfo * pNativeVarInfo,
1397 IN ULONG iNativeVarInfo,
1398 IN OUT SBuffer * pDebugInfoBuffer
1401 STANDARD_VM_CONTRACT;
1403 CompressDebugInfo::CompressBoundariesAndVars(pOffsetMapping, iOffsetMapping, pNativeVarInfo, iNativeVarInfo, pDebugInfoBuffer, NULL);
1406 HRESULT CEECompileInfo::GetBaseJitFlags(
1407 IN CORINFO_METHOD_HANDLE hMethod,
1408 OUT CORJIT_FLAGS *pFlags)
1410 STANDARD_VM_CONTRACT;
1412 MethodDesc *pMD = (MethodDesc *)hMethod;
1413 *pFlags = CEEInfo::GetBaseCompileFlags(pMD);
1418 //=================================================================================
1433 BOOL CEEPreloader::CanEmbedClassID(CORINFO_CLASS_HANDLE typeHandle)
1435 STANDARD_VM_CONTRACT;
1437 TypeHandle hnd = (TypeHandle) typeHandle;
1438 return m_image->CanEagerBindToTypeHandle(hnd) &&
1439 !hnd.AsMethodTable()->NeedsCrossModuleGenericsStaticsInfo();
1442 BOOL CEEPreloader::CanEmbedModuleID(CORINFO_MODULE_HANDLE moduleHandle)
1444 STANDARD_VM_CONTRACT;
1446 return m_image->CanEagerBindToModule((Module *)moduleHandle);
1449 BOOL CEEPreloader::CanEmbedModuleHandle(CORINFO_MODULE_HANDLE moduleHandle)
1451 STANDARD_VM_CONTRACT;
1453 return m_image->CanEagerBindToModule((Module *)moduleHandle);
1456 BOOL CEEPreloader::CanEmbedClassHandle(CORINFO_CLASS_HANDLE typeHandle)
1458 STANDARD_VM_CONTRACT;
1460 TypeHandle hnd = (TypeHandle) typeHandle;
1462 BOOL decision = m_image->CanEagerBindToTypeHandle(hnd);
1468 embedStats.noEmbed++;
1474 CorElementType arrType = hnd.AsArray()->GetInternalCorElementType();
1475 if (arrType == ELEMENT_TYPE_SZARRAY)
1476 embedStats.szarray++;
1478 CorElementType elemType = hnd.AsArray()->GetArrayElementTypeHandle().GetInternalCorElementType();
1479 if (elemType <= ELEMENT_TYPE_R8)
1480 embedStats.primitives++;
1487 /*static*/ BOOL CanEmbedMethodDescViaContext(MethodDesc * pMethod, MethodDesc * pContext)
1489 STANDARD_VM_CONTRACT;
1491 if (pContext != NULL)
1493 _ASSERTE(pContext->GetLoaderModule() == GetAppDomain()->ToCompilationDomain()->GetTargetModule());
1495 // a method can always embed its own handle
1496 if (pContext == pMethod)
1501 // Methods that are tightly bound to the same method table can
1502 // always refer each other directly. This check allows methods
1503 // within one speculative generic instantiations to call each
1506 if ((pContext->GetMethodTable() == pMethod->GetMethodTable()) &&
1507 pContext->IsTightlyBoundToMethodTable() &&
1508 pMethod->IsTightlyBoundToMethodTable())
1516 BOOL CEEPreloader::CanEmbedMethodHandle(CORINFO_METHOD_HANDLE methodHandle,
1517 CORINFO_METHOD_HANDLE contextHandle)
1519 STANDARD_VM_CONTRACT;
1521 MethodDesc * pContext = GetMethod(contextHandle);
1522 MethodDesc * pMethod = GetMethod(methodHandle);
1524 if (CanEmbedMethodDescViaContext(pMethod, pContext))
1527 return m_image->CanEagerBindToMethodDesc(pMethod);
1530 BOOL CEEPreloader::CanEmbedFieldHandle(CORINFO_FIELD_HANDLE fieldHandle)
1532 STANDARD_VM_CONTRACT;
1534 return m_image->CanEagerBindToFieldDesc((FieldDesc *) fieldHandle);
1538 void* CEECompileInfo::GetStubSize(void *pStubAddress, DWORD *pSizeToCopy)
1543 PRECONDITION(pStubAddress && pSizeToCopy);
1547 Stub *stub = Stub::RecoverStubAndSize((TADDR)pStubAddress, pSizeToCopy);
1548 _ASSERTE(*pSizeToCopy > sizeof(Stub));
1552 HRESULT CEECompileInfo::GetStubClone(void *pStub, BYTE *pBuffer, DWORD dwBufferSize)
1554 STANDARD_VM_CONTRACT;
1558 return E_INVALIDARG;
1561 return (reinterpret_cast<Stub *>(pStub)->CloneStub(pBuffer, dwBufferSize));
1564 HRESULT CEECompileInfo::GetTypeDef(CORINFO_CLASS_HANDLE classHandle,
1567 STANDARD_VM_CONTRACT;
1569 CANNOTTHROWCOMPLUSEXCEPTION();
1571 TypeHandle hClass(classHandle);
1573 *token = hClass.GetCl();
1578 HRESULT CEECompileInfo::GetMethodDef(CORINFO_METHOD_HANDLE methodHandle,
1581 STANDARD_VM_CONTRACT;
1583 CANNOTTHROWCOMPLUSEXCEPTION();
1585 *token = ((MethodDesc*)methodHandle)->GetMemberDef();
1590 /*********************************************************************/
1591 // Used to determine if a methodHandle can be embedded in an ngen image.
1592 // Depends on what things are persisted by CEEPreloader
1594 BOOL CEEPreloader::CanEmbedFunctionEntryPoint(
1595 CORINFO_METHOD_HANDLE methodHandle,
1596 CORINFO_METHOD_HANDLE contextHandle, /* = NULL */
1597 CORINFO_ACCESS_FLAGS accessFlags /*=CORINFO_ACCESS_ANY*/)
1599 STANDARD_VM_CONTRACT;
1601 MethodDesc * pMethod = GetMethod(methodHandle);
1602 MethodDesc * pContext = GetMethod(contextHandle);
1604 // IsRemotingInterceptedViaVirtualDispatch is a rather special case.
1606 // Other remoting intercepts are implemented by one of:
1607 // (1) in DoPrestub (for non-virtual calls)
1608 // (2) by transparent proxy vtables, where all the entries in the vtable
1609 // go to the same code.
1611 // However when calling virtual functions non-virtually the JIT interface
1612 // pointer to the code for the function in a stub
1613 // (see GetNonVirtualEntryPointForVirtualMethod).
1614 // Thus we cannot embed non-virtual calls to these functions because we
1615 // don't save these stubs. Unlike most other remoting stubs these ones
1616 // are NOT inserted by DoPrestub.
1618 if (((accessFlags & CORINFO_ACCESS_THIS) == 0) &&
1619 (pMethod->IsRemotingInterceptedViaVirtualDispatch()))
1624 // Methods with native callable attribute are special , since
1625 // they are used as LDFTN targets.Native Callable methods
1626 // uses the same code path as reverse pinvoke and embedding them
1627 // in an ngen image require saving the reverse pinvoke stubs.
1628 if (pMethod->HasNativeCallableAttribute())
1634 BOOL CEEPreloader::DoesMethodNeedRestoringBeforePrestubIsRun(
1635 CORINFO_METHOD_HANDLE methodHandle)
1637 STANDARD_VM_CONTRACT;
1639 MethodDesc * ftn = GetMethod(methodHandle);
1641 // The restore mechanism for InstantiatedMethodDescs (IMDs) is complicated, and causes
1642 // circular dependency complications with the GC if we hardbind to the prestub/precode
1643 // of an unrestored IMD. As such, we're eliminating hardbinding to unrestored MethodDescs
1644 // that belong to generic types.
1646 //@TODO: The reduction may be overkill, and we may consider refining the cases.
1648 // Specifically, InstantiatedMethodDescs can have preferred zap modules different than
1649 // the zap modules for their owning types. As such, in a soft-binding case a MethodDesc
1650 // may not be able to trace back to its owning Module without hitting an unrestored
1651 // fixup token. For example, the 64-bit JIT can not yet provide generic type arguments
1652 // and uses instantiating stubs to call static methods on generic types. If such an stub
1653 // belong to a module other than the module in which the generic type is declared, then
1654 // it is possible for the MethodTable::m_pEEClass or the EEClass::m_pModule pointers to
1655 // be unrestored. The complication arises when a call to the prestub/precode of such
1656 // an unrestored IMD causes us try to restore the IMD and this in turn causes us to
1657 // transition to preemptive GC and as such GC needs the metadata signature from the IMD
1658 // to iterate its arguments. But since we're currently restoring the IMD, we may not be
1659 // able to get to the signature, and as such we're stuck.
1661 // The same problem exists for instantiation arguments. We may need the instantiation
1662 // arguments while walking the signature during GC, and if they are not restored we're stuck.
1664 if (ftn->HasClassOrMethodInstantiation())
1666 if (ftn->NeedsRestore(m_image))
1673 BOOL CEECompileInfo::IsNativeCallableMethod(CORINFO_METHOD_HANDLE handle)
1675 WRAPPER_NO_CONTRACT;
1677 MethodDesc * pMethod = GetMethod(handle);
1678 return pMethod->HasNativeCallableAttribute();
1681 BOOL CEEPreloader::CanSkipDependencyActivation(CORINFO_METHOD_HANDLE context,
1682 CORINFO_MODULE_HANDLE moduleFrom,
1683 CORINFO_MODULE_HANDLE moduleTo)
1685 STANDARD_VM_CONTRACT;
1687 // Can't skip any fixups for speculative generic instantiations
1688 if (Module::GetPreferredZapModuleForMethodDesc(GetMethod(context)) != m_image->GetModule())
1691 // We don't need a fixup for eager bound dependencies since we are going to have
1692 // an uncontional one already.
1693 return m_image->CanEagerBindToModule((Module *)moduleTo);
1696 CORINFO_MODULE_HANDLE CEEPreloader::GetPreferredZapModuleForClassHandle(
1697 CORINFO_CLASS_HANDLE classHnd)
1699 STANDARD_VM_CONTRACT;
1701 return CORINFO_MODULE_HANDLE(Module::GetPreferredZapModuleForTypeHandle(TypeHandle(classHnd)));
1704 // This method is called directly from zapper
1705 extern BOOL CanDeduplicateCode(CORINFO_METHOD_HANDLE method, CORINFO_METHOD_HANDLE duplicateMethod);
1707 BOOL CanDeduplicateCode(CORINFO_METHOD_HANDLE method, CORINFO_METHOD_HANDLE duplicateMethod)
1716 ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE();
1718 // For now, the deduplication is supported for IL stubs only
1719 DynamicMethodDesc * pMethod = GetMethod(method)->AsDynamicMethodDesc();
1720 DynamicMethodDesc * pDuplicateMethod = GetMethod(duplicateMethod)->AsDynamicMethodDesc();
1723 // Make sure that the return types match (for code:Thread::HijackThread)
1727 MetaSig msig1(pMethod);
1728 MetaSig msig2(pDuplicateMethod);
1729 if (!msig1.HasFPReturn() != !msig2.HasFPReturn())
1731 #endif // _TARGET_X86_
1733 MetaSig::RETURNTYPE returnType = pMethod->ReturnsObject();
1734 MetaSig::RETURNTYPE returnTypeDuplicate = pDuplicateMethod->ReturnsObject();
1736 if (returnType != returnTypeDuplicate)
1740 // Do not enable deduplication of structs returned in registers
1743 if (returnType == MetaSig::RETVALUETYPE)
1747 // Make sure that the IL stub flags match
1750 if (pMethod->GetExtendedFlags() != pDuplicateMethod->GetExtendedFlags())
1756 void CEEPreloader::NoteDeduplicatedCode(CORINFO_METHOD_HANDLE method, CORINFO_METHOD_HANDLE duplicateMethod)
1758 STANDARD_VM_CONTRACT;
1760 #ifndef FEATURE_FULL_NGEN // Deduplication
1761 DuplicateMethodEntry e;
1762 e.pMD = GetMethod(method);
1763 e.pDuplicateMD = GetMethod(duplicateMethod);
1764 m_duplicateMethodsHash.Add(e);
1768 HRESULT CEECompileInfo::GetFieldDef(CORINFO_FIELD_HANDLE fieldHandle,
1771 STANDARD_VM_CONTRACT;
1773 CANNOTTHROWCOMPLUSEXCEPTION();
1775 *token = ((FieldDesc*)fieldHandle)->GetMemberDef();
1780 void CEECompileInfo::EncodeModuleAsIndexes(CORINFO_MODULE_HANDLE fromHandle,
1781 CORINFO_MODULE_HANDLE handle,
1782 DWORD* pAssemblyIndex,
1783 DWORD* pModuleIndex,
1784 IMetaDataAssemblyEmit* pAssemblyEmit)
1786 STANDARD_VM_CONTRACT;
1788 COOPERATIVE_TRANSITION_BEGIN();
1790 Module *fromModule = GetModule(fromHandle);
1791 Assembly *fromAssembly = fromModule->GetAssembly();
1793 Module *module = GetModule(handle);
1794 Assembly *assembly = module->GetAssembly();
1796 if (assembly == fromAssembly)
1797 *pAssemblyIndex = 0;
1803 CompilationDomain *pDomain = GetAppDomain()->ToCompilationDomain();
1805 RefCache *pRefCache = pDomain->GetRefCache(fromModule);
1810 if (!assembly->GetManifestFile()->HasBindableIdentity())
1812 // If the module that we'd like to encode for a later fixup doesn't have
1813 // a bindable identity, then this will fail at runtime. So, we ask the
1814 // compilation domain for a matching assembly with a bindable identity.
1815 // This is possible because this module must have been bound in the past,
1816 // and the compilation domain will keep track of at least one corresponding
1817 // bindable identity.
1818 AssemblySpec defSpec;
1819 defSpec.InitializeSpec(assembly->GetManifestFile());
1821 AssemblySpec* pRefSpec = pDomain->FindAssemblyRefSpecForDefSpec(&defSpec);
1822 _ASSERTE(pRefSpec != nullptr);
1824 IfFailThrow(pRefSpec->EmitToken(pAssemblyEmit, &token, TRUE, TRUE));
1825 token += fromModule->GetAssemblyRefMax();
1829 result = pRefCache->m_sAssemblyRefMap.LookupValue((UPTR)assembly, NULL);
1831 if (result == (UPTR)INVALIDENTRY)
1832 token = fromModule->FindAssemblyRef(assembly);
1834 token = (mdAssemblyRef) result;
1836 if (IsNilToken(token))
1838 token = fromAssembly->AddAssemblyRef(assembly, pAssemblyEmit);
1839 token += fromModule->GetAssemblyRefMax();
1843 *pAssemblyIndex = RidFromToken(token);
1845 pRefCache->m_sAssemblyRefMap.InsertValue((UPTR) assembly, (UPTR)token);
1848 if (module == assembly->GetManifestModule())
1852 _ASSERTE(module->GetModuleRef() != mdFileNil);
1853 *pModuleIndex = RidFromToken(module->GetModuleRef());
1856 COOPERATIVE_TRANSITION_END();
1859 void CEECompileInfo::EncodeClass(
1860 CORINFO_MODULE_HANDLE referencingModule,
1861 CORINFO_CLASS_HANDLE classHandle,
1862 SigBuilder * pSigBuilder,
1863 LPVOID pEncodeModuleContext,
1864 ENCODEMODULE_CALLBACK pfnEncodeModule)
1866 STANDARD_VM_CONTRACT;
1868 TypeHandle th(classHandle);
1870 ZapSig zapSig((Module *)referencingModule, pEncodeModuleContext, ZapSig::NormalTokens,
1871 (EncodeModuleCallback) pfnEncodeModule, NULL);
1873 COOPERATIVE_TRANSITION_BEGIN();
1876 fSuccess = zapSig.GetSignatureForTypeHandle(th, pSigBuilder);
1879 COOPERATIVE_TRANSITION_END();
1882 CORINFO_MODULE_HANDLE CEECompileInfo::GetLoaderModuleForMscorlib()
1884 STANDARD_VM_CONTRACT;
1886 return CORINFO_MODULE_HANDLE(SystemDomain::SystemModule());
1889 CORINFO_MODULE_HANDLE CEECompileInfo::GetLoaderModuleForEmbeddableType(CORINFO_CLASS_HANDLE clsHnd)
1891 STANDARD_VM_CONTRACT;
1893 TypeHandle t = TypeHandle(clsHnd);
1894 return CORINFO_MODULE_HANDLE(t.GetLoaderModule());
1897 CORINFO_MODULE_HANDLE CEECompileInfo::GetLoaderModuleForEmbeddableMethod(CORINFO_METHOD_HANDLE methHnd)
1899 STANDARD_VM_CONTRACT;
1901 MethodDesc *pMD = GetMethod(methHnd);
1902 return CORINFO_MODULE_HANDLE(pMD->GetLoaderModule());
1905 CORINFO_MODULE_HANDLE CEECompileInfo::GetLoaderModuleForEmbeddableField(CORINFO_FIELD_HANDLE fieldHnd)
1907 STANDARD_VM_CONTRACT;
1909 FieldDesc *pFD = (FieldDesc *) fieldHnd;
1910 return CORINFO_MODULE_HANDLE(pFD->GetLoaderModule());
1913 void CEECompileInfo::EncodeMethod(
1914 CORINFO_MODULE_HANDLE referencingModule,
1915 CORINFO_METHOD_HANDLE handle,
1916 SigBuilder * pSigBuilder,
1917 LPVOID pEncodeModuleContext,
1918 ENCODEMODULE_CALLBACK pfnEncodeModule,
1919 CORINFO_RESOLVED_TOKEN * pResolvedToken,
1920 CORINFO_RESOLVED_TOKEN * pConstrainedResolvedToken,
1921 BOOL fEncodeUsingResolvedTokenSpecStreams)
1923 STANDARD_VM_CONTRACT;
1925 COOPERATIVE_TRANSITION_BEGIN();
1926 MethodDesc *pMethod = GetMethod(handle);
1929 fSuccess = ZapSig::EncodeMethod(pMethod,
1930 (Module *) referencingModule,
1932 pEncodeModuleContext,
1933 pfnEncodeModule, NULL,
1934 pResolvedToken, pConstrainedResolvedToken,
1935 fEncodeUsingResolvedTokenSpecStreams);
1938 COOPERATIVE_TRANSITION_END();
1941 mdToken CEECompileInfo::TryEncodeMethodAsToken(
1942 CORINFO_METHOD_HANDLE handle,
1943 CORINFO_RESOLVED_TOKEN * pResolvedToken,
1944 CORINFO_MODULE_HANDLE * referencingModule)
1946 STANDARD_VM_CONTRACT;
1948 MethodDesc * pMethod = GetMethod(handle);
1950 #ifdef FEATURE_READYTORUN_COMPILER
1951 if (IsReadyToRunCompilation())
1953 _ASSERTE(pResolvedToken != NULL);
1955 Module * pReferencingModule = (Module *)pResolvedToken->tokenScope;
1957 if (!pReferencingModule->IsInCurrentVersionBubble())
1960 unsigned methodToken = pResolvedToken->token;
1962 switch (TypeFromToken(methodToken))
1965 if (pReferencingModule->LookupMethodDef(methodToken) != pMethod)
1970 if (pReferencingModule->LookupMemberRefAsMethod(methodToken) != pMethod)
1978 *referencingModule = CORINFO_MODULE_HANDLE(pReferencingModule);
1981 #endif // FEATURE_READYTORUN_COMPILER
1983 Module *pModule = pMethod->GetModule();
1984 if (!pModule->IsInCurrentVersionBubble())
1986 Module * pTargetModule = GetAppDomain()->ToCompilationDomain()->GetTargetModule();
1987 *referencingModule = CORINFO_MODULE_HANDLE(pTargetModule);
1988 return pTargetModule->LookupMemberRefByMethodDesc(pMethod);
1992 mdToken defToken = pMethod->GetMemberDef();
1993 if (pModule->LookupMethodDef(defToken) == pMethod)
1995 *referencingModule = CORINFO_MODULE_HANDLE(pModule);
2003 DWORD CEECompileInfo::TryEncodeMethodSlot(CORINFO_METHOD_HANDLE handle)
2005 STANDARD_VM_CONTRACT;
2007 MethodDesc * pMethod = GetMethod(handle);
2009 #ifdef FEATURE_READYTORUN_COMPILER
2010 if (IsReadyToRunCompilation())
2012 // We can only encode real interface methods as slots
2013 if (!pMethod->IsInterface() || pMethod->IsStatic())
2016 // And only if the interface lives in the current version bubble
2017 // If may be possible to relax this restriction if we can guarantee that the external interfaces are
2018 // really not changing. We will play it safe for now.
2019 if (!pMethod->GetModule()->IsInCurrentVersionBubble())
2024 return pMethod->GetSlot();
2027 void EncodeTypeInDictionarySignature(
2028 Module * pInfoModule,
2030 SigBuilder * pSigBuilder,
2031 LPVOID encodeContext,
2032 ENCODEMODULE_CALLBACK pfnEncodeModule)
2034 STANDARD_VM_CONTRACT;
2036 CorElementType typ = ELEMENT_TYPE_END;
2037 IfFailThrow(ptr.GetElemType(&typ));
2039 if (typ == ELEMENT_TYPE_INTERNAL)
2043 IfFailThrow(ptr.GetPointer((void**)&th));
2045 ZapSig zapSig(pInfoModule, encodeContext, ZapSig::NormalTokens,
2046 (EncodeModuleCallback) pfnEncodeModule, NULL);
2052 fSuccess = zapSig.GetSignatureForTypeHandle(th, pSigBuilder);
2058 if (typ == ELEMENT_TYPE_GENERICINST)
2061 // SigParser expects ELEMENT_TYPE_MODULE_ZAPSIG to be before ELEMENT_TYPE_GENERICINST
2063 SigPointer peek(ptr);
2065 IfFailThrow(peek.GetData(&instType));
2066 _ASSERTE(instType == ELEMENT_TYPE_INTERNAL);
2069 IfFailThrow(peek.GetPointer((void **)&th));
2071 Module * pTypeHandleModule = th.GetModule();
2073 if (!pTypeHandleModule->IsInCurrentVersionBubble())
2075 pTypeHandleModule = GetAppDomain()->ToCompilationDomain()->GetTargetModule();
2078 if (pTypeHandleModule != pInfoModule)
2080 DWORD index = pfnEncodeModule(encodeContext, (CORINFO_MODULE_HANDLE)pTypeHandleModule);
2081 _ASSERTE(index != ENCODE_MODULE_FAILED);
2083 pSigBuilder->AppendElementType((CorElementType) ELEMENT_TYPE_MODULE_ZAPSIG);
2084 pSigBuilder->AppendData(index);
2087 pSigBuilder->AppendElementType(ELEMENT_TYPE_GENERICINST);
2089 EncodeTypeInDictionarySignature(pTypeHandleModule, ptr, pSigBuilder, encodeContext, pfnEncodeModule);
2090 IfFailThrow(ptr.SkipExactlyOne());
2092 ULONG argCnt = 0; // Get number of parameters
2093 IfFailThrow(ptr.GetData(&argCnt));
2094 pSigBuilder->AppendData(argCnt);
2098 EncodeTypeInDictionarySignature(pInfoModule, ptr, pSigBuilder, encodeContext, pfnEncodeModule);
2099 IfFailThrow(ptr.SkipExactlyOne());
2105 pSigBuilder->AppendElementType(typ);
2107 if (!CorIsPrimitiveType(typ))
2111 case ELEMENT_TYPE_VAR:
2112 case ELEMENT_TYPE_MVAR:
2115 // Skip variable number
2116 IfFailThrow(ptr.GetData(&varNum));
2117 pSigBuilder->AppendData(varNum);
2120 case ELEMENT_TYPE_OBJECT:
2121 case ELEMENT_TYPE_STRING:
2122 case ELEMENT_TYPE_TYPEDBYREF:
2125 case ELEMENT_TYPE_BYREF: //fallthru
2126 case ELEMENT_TYPE_PTR:
2127 case ELEMENT_TYPE_PINNED:
2128 case ELEMENT_TYPE_SZARRAY:
2129 EncodeTypeInDictionarySignature(pInfoModule, ptr, pSigBuilder, encodeContext, pfnEncodeModule);
2130 IfFailThrow(ptr.SkipExactlyOne());
2133 case ELEMENT_TYPE_ARRAY:
2135 EncodeTypeInDictionarySignature(pInfoModule, ptr, pSigBuilder, encodeContext, pfnEncodeModule);
2136 IfFailThrow(ptr.SkipExactlyOne());
2138 ULONG rank = 0; // Get rank
2139 IfFailThrow(ptr.GetData(&rank));
2140 pSigBuilder->AppendData(rank);
2145 IfFailThrow(ptr.GetData(&nsizes));
2146 pSigBuilder->AppendData(nsizes);
2151 IfFailThrow(ptr.GetData(&data));
2152 pSigBuilder->AppendData(data);
2156 IfFailThrow(ptr.GetData(&nlbounds));
2157 pSigBuilder->AppendData(nlbounds);
2162 IfFailThrow(ptr.GetData(&data));
2163 pSigBuilder->AppendData(data);
2170 _ASSERTE(!"Unexpected element in signature");
2175 void CEECompileInfo::EncodeGenericSignature(
2178 SigBuilder * pSigBuilder,
2179 LPVOID encodeContext,
2180 ENCODEMODULE_CALLBACK pfnEncodeModule)
2182 STANDARD_VM_CONTRACT;
2184 Module * pInfoModule = MscorlibBinder::GetModule();
2186 SigPointer ptr((PCCOR_SIGNATURE)signature);
2188 ULONG entryKind; // DictionaryEntryKind
2189 IfFailThrow(ptr.GetData(&entryKind));
2190 pSigBuilder->AppendData(entryKind);
2194 ULONG dictionaryIndex = 0;
2195 IfFailThrow(ptr.GetData(&dictionaryIndex));
2197 pSigBuilder->AppendData(dictionaryIndex);
2202 case DeclaringTypeHandleSlot:
2203 EncodeTypeInDictionarySignature(pInfoModule, ptr, pSigBuilder, encodeContext, pfnEncodeModule);
2204 IfFailThrow(ptr.SkipExactlyOne());
2207 case TypeHandleSlot:
2208 EncodeTypeInDictionarySignature(pInfoModule, ptr, pSigBuilder, encodeContext, pfnEncodeModule);
2209 IfFailThrow(ptr.SkipExactlyOne());
2212 case ConstrainedMethodEntrySlot:
2213 EncodeTypeInDictionarySignature(pInfoModule, ptr, pSigBuilder, encodeContext, pfnEncodeModule);
2214 IfFailThrow(ptr.SkipExactlyOne());
2217 case MethodDescSlot:
2218 case MethodEntrySlot:
2219 case DispatchStubAddrSlot:
2221 EncodeTypeInDictionarySignature(pInfoModule, ptr, pSigBuilder, encodeContext, pfnEncodeModule);
2222 IfFailThrow(ptr.SkipExactlyOne());
2225 IfFailThrow(ptr.GetData(&methodFlags));
2226 pSigBuilder->AppendData(methodFlags);
2228 if ((methodFlags & ENCODE_METHOD_SIG_SlotInsteadOfToken) == 0)
2230 EncodeTypeInDictionarySignature(pInfoModule, ptr, pSigBuilder, encodeContext, pfnEncodeModule);
2231 IfFailThrow(ptr.SkipExactlyOne());
2235 IfFailThrow(ptr.GetData(&tokenOrSlot));
2236 pSigBuilder->AppendData(tokenOrSlot);
2238 if (methodFlags & ENCODE_METHOD_SIG_MethodInstantiation)
2240 DWORD nGenericMethodArgs;
2241 IfFailThrow(ptr.GetData(&nGenericMethodArgs));
2242 pSigBuilder->AppendData(nGenericMethodArgs);
2244 for (DWORD i = 0; i < nGenericMethodArgs; i++)
2246 EncodeTypeInDictionarySignature(pInfoModule, ptr, pSigBuilder, encodeContext, pfnEncodeModule);
2247 IfFailThrow(ptr.SkipExactlyOne());
2255 EncodeTypeInDictionarySignature(pInfoModule, ptr, pSigBuilder, encodeContext, pfnEncodeModule);
2256 IfFailThrow(ptr.SkipExactlyOne());
2259 IfFailThrow(ptr.GetData(&fieldIndex));
2260 pSigBuilder->AppendData(fieldIndex);
2268 ULONG dictionarySlot;
2269 IfFailThrow(ptr.GetData(&dictionarySlot));
2270 pSigBuilder->AppendData(dictionarySlot);
2273 void CEECompileInfo::EncodeField(
2274 CORINFO_MODULE_HANDLE referencingModule,
2275 CORINFO_FIELD_HANDLE handle,
2276 SigBuilder * pSigBuilder,
2277 LPVOID encodeContext,
2278 ENCODEMODULE_CALLBACK pfnEncodeModule,
2279 CORINFO_RESOLVED_TOKEN * pResolvedToken,
2280 BOOL fEncodeUsingResolvedTokenSpecStreams)
2282 STANDARD_VM_CONTRACT;
2284 COOPERATIVE_TRANSITION_BEGIN();
2286 ZapSig::EncodeField(GetField(handle),
2287 (Module *) referencingModule,
2292 fEncodeUsingResolvedTokenSpecStreams);
2294 COOPERATIVE_TRANSITION_END();
2297 BOOL CEECompileInfo::IsEmptyString(mdString token,
2298 CORINFO_MODULE_HANDLE module)
2300 STANDARD_VM_CONTRACT;
2304 COOPERATIVE_TRANSITION_BEGIN();
2306 EEStringData strData;
2307 ((Module *)module)->InitializeStringData(token, &strData, NULL);
2308 fRet = (strData.GetCharCount() == 0);
2310 COOPERATIVE_TRANSITION_END();
2315 #ifdef FEATURE_READYTORUN_COMPILER
2316 CORCOMPILE_FIXUP_BLOB_KIND CEECompileInfo::GetFieldBaseOffset(
2317 CORINFO_CLASS_HANDLE classHnd,
2318 DWORD * pBaseOffset)
2320 STANDARD_VM_CONTRACT;
2322 MethodTable * pMT = (MethodTable *)classHnd;
2323 Module * pModule = pMT->GetModule();
2325 if (!pMT->IsLayoutFixedInCurrentVersionBubble())
2327 return pMT->IsValueType() ? ENCODE_CHECK_FIELD_OFFSET : ENCODE_FIELD_OFFSET;
2330 if (pMT->IsValueType())
2335 if (pMT->GetParentMethodTable()->IsInheritanceChainLayoutFixedInCurrentVersionBubble())
2340 if (pMT->HasLayout())
2342 // We won't try to be smart for classes with layout.
2343 // They are complex to get right, and very rare anyway.
2344 return ENCODE_FIELD_OFFSET;
2347 *pBaseOffset = ReadyToRunInfo::GetFieldBaseOffset(pMT);
2348 return ENCODE_FIELD_BASE_OFFSET;
2351 BOOL CEECompileInfo::NeedsTypeLayoutCheck(CORINFO_CLASS_HANDLE classHnd)
2353 STANDARD_VM_CONTRACT;
2355 TypeHandle th(classHnd);
2357 if (th.IsTypeDesc())
2360 MethodTable * pMT = th.AsMethodTable();
2362 if (!pMT->IsValueType())
2365 // Skip this check for equivalent types. Equivalent types are used for interop that ensures
2367 if (pMT->GetClass()->IsEquivalentType())
2370 return !pMT->IsLayoutFixedInCurrentVersionBubble();
2373 extern void ComputeGCRefMap(MethodTable * pMT, BYTE * pGCRefMap, size_t cbGCRefMap);
2375 void CEECompileInfo::EncodeTypeLayout(CORINFO_CLASS_HANDLE classHandle, SigBuilder * pSigBuilder)
2377 STANDARD_VM_CONTRACT;
2379 MethodTable * pMT = TypeHandle(classHandle).AsMethodTable();
2380 _ASSERTE(pMT->IsValueType());
2382 DWORD dwSize = pMT->GetNumInstanceFieldBytes();
2383 DWORD dwAlignment = CEEInfo::getClassAlignmentRequirementStatic(pMT);
2389 dwFlags |= READYTORUN_LAYOUT_HFA;
2393 dwFlags |= READYTORUN_LAYOUT_Alignment;
2394 if (dwAlignment == sizeof(void *))
2395 dwFlags |= READYTORUN_LAYOUT_Alignment_Native;
2397 dwFlags |= READYTORUN_LAYOUT_GCLayout;
2398 if (!pMT->ContainsPointers())
2399 dwFlags |= READYTORUN_LAYOUT_GCLayout_Empty;
2401 pSigBuilder->AppendData(dwFlags);
2403 // Size is checked unconditionally
2404 pSigBuilder->AppendData(dwSize);
2407 if (dwFlags & READYTORUN_LAYOUT_HFA)
2409 pSigBuilder->AppendData(pMT->GetHFAType());
2413 if ((dwFlags & READYTORUN_LAYOUT_Alignment) && !(dwFlags & READYTORUN_LAYOUT_Alignment_Native))
2415 pSigBuilder->AppendData(dwAlignment);
2418 if ((dwFlags & READYTORUN_LAYOUT_GCLayout) && !(dwFlags & READYTORUN_LAYOUT_GCLayout_Empty))
2420 size_t cbGCRefMap = (dwSize / sizeof(TADDR) + 7) / 8;
2421 _ASSERTE(cbGCRefMap > 0);
2423 BYTE * pGCRefMap = (BYTE *)_alloca(cbGCRefMap);
2425 ComputeGCRefMap(pMT, pGCRefMap, cbGCRefMap);
2427 for (size_t i = 0; i < cbGCRefMap; i++)
2428 pSigBuilder->AppendByte(pGCRefMap[i]);
2432 BOOL CEECompileInfo::AreAllClassesFullyLoaded(CORINFO_MODULE_HANDLE moduleHandle)
2434 STANDARD_VM_CONTRACT;
2436 return ((Module *)moduleHandle)->AreAllClassesFullyLoaded();
2439 int CEECompileInfo::GetVersionResilientTypeHashCode(CORINFO_MODULE_HANDLE moduleHandle, mdToken token)
2441 STANDARD_VM_CONTRACT;
2444 if (!::GetVersionResilientTypeHashCode(((Module *)moduleHandle)->GetMDImport(), token, &dwHashCode))
2445 ThrowHR(COR_E_BADIMAGEFORMAT);
2450 int CEECompileInfo::GetVersionResilientMethodHashCode(CORINFO_METHOD_HANDLE methodHandle)
2452 STANDARD_VM_CONTRACT;
2454 return ::GetVersionResilientMethodHashCode(GetMethod(methodHandle));
2457 #endif // FEATURE_READYTORUN_COMPILER
2459 BOOL CEECompileInfo::HasCustomAttribute(CORINFO_METHOD_HANDLE method, LPCSTR customAttributeName)
2461 STANDARD_VM_CONTRACT;
2463 MethodDesc * pMD = GetMethod(method);
2464 return S_OK == pMD->GetMDImport()->GetCustomAttributeByName(pMD->GetMemberDef(), customAttributeName, NULL, NULL);
2467 #define OMFConst_Read 0x0001
2468 #define OMFConst_Write 0x0002
2469 #define OMFConst_Exec 0x0004
2470 #define OMFConst_F32Bit 0x0008
2471 #define OMFConst_ReservedBits1 0x00f0
2472 #define OMFConst_FSel 0x0100
2473 #define OMFConst_FAbs 0x0200
2474 #define OMFConst_ReservedBits2 0x0C00
2475 #define OMFConst_FGroup 0x1000
2476 #define OMFConst_ReservedBits3 0xE000
2478 #define OMF_StandardText (OMFConst_FSel|OMFConst_F32Bit|OMFConst_Exec|OMFConst_Read) // 0x10D
2479 #define OMF_SentinelType (OMFConst_FAbs|OMFConst_F32Bit) // 0x208
2482 // ----------------------------------------------------------------------------
2485 // The NGEN PDB format consists of structs stacked together into buffers, which are
2486 // passed to the PDB API. For a description of the structures, see
2487 // InternalApis\vctools\inc\cvinfo.h.
2489 // The interface to the PDB used below is NGEN-specific, and is exposed via
2490 // diasymreader.dll. For a description of this interface, see ISymNGenWriter2 inside
2491 // public\devdiv\inc\corsym.h and debugger\sh\symwrtr\ngenpdbwriter.h,cpp
2492 // ----------------------------------------------------------------------------
2494 #if defined(NO_NGENPDB) && !defined(FEATURE_PERFMAP)
2495 BOOL CEECompileInfo::GetIsGeneratingNgenPDB()
2500 void CEECompileInfo::SetIsGeneratingNgenPDB(BOOL fGeneratingNgenPDB)
2504 BOOL IsNgenPDBCompilationProcess()
2509 BOOL CEECompileInfo::GetIsGeneratingNgenPDB()
2511 LIMITED_METHOD_DAC_CONTRACT;
2512 return m_fGeneratingNgenPDB;
2515 void CEECompileInfo::SetIsGeneratingNgenPDB(BOOL fGeneratingNgenPDB)
2517 LIMITED_METHOD_DAC_CONTRACT;
2518 m_fGeneratingNgenPDB = fGeneratingNgenPDB;
2521 BOOL IsNgenPDBCompilationProcess()
2523 LIMITED_METHOD_DAC_CONTRACT;
2524 return IsCompilationProcess() && g_pCEECompileInfo->GetIsGeneratingNgenPDB();
2527 #endif // NO_NGENPDB && !FEATURE_PERFMAP
2530 // This is the prototype of "CreateNGenPdbWriter" exported by diasymreader.dll
2531 typedef HRESULT (__stdcall *CreateNGenPdbWriter_t)(const WCHAR *pwszNGenImagePath, const WCHAR *pwszPdbPath, void **ppvObj);
2533 // Allocator to specify when requesting boundaries information for PDB
2534 BYTE* SimpleNew(void *, size_t cBytes)
2544 BYTE * p = new BYTE[cBytes];
2548 // PDB convention has any IPs that don't map to source code (e.g., prolog, epilog, etc.)
2549 // to be mapped to line number "0xFeeFee".
2550 const int kUnmappedIP = 0xFeeFee;
2553 // ----------------------------------------------------------------------------
2554 // Simple pair of offsets for each source file name. Pair includes its offset into the
2555 // PDB string table, and its offset in the files checksum table.
2557 struct DocNameOffsets
2559 ULONG32 m_dwStrTableOffset;
2560 ULONG32 m_dwChksumTableOffset;
2561 DocNameOffsets(ULONG32 dwStrTableOffset, ULONG32 dwChksumTableOffset)
2562 : m_dwStrTableOffset(dwStrTableOffset), m_dwChksumTableOffset(dwChksumTableOffset)
2564 LIMITED_METHOD_CONTRACT;
2568 : m_dwStrTableOffset((ULONG32) -1), m_dwChksumTableOffset((ULONG32) -1)
2570 LIMITED_METHOD_CONTRACT;
2575 // ----------------------------------------------------------------------------
2576 // This is used when creating the hash table which maps source file names to
2577 // DocNameOffsets instances. The only interesting stuff here is that:
2578 // * Equality is determined by a case-insensitive comparison on the source file
2580 // * Hashing is done by hashing the source file names
2582 struct DocNameToOffsetMapTraits : public NoRemoveSHashTraits < MapSHashTraits<LPCSTR, DocNameOffsets> >
2585 static BOOL Equals(key_t k1, key_t k2)
2587 LIMITED_METHOD_CONTRACT;
2589 if (k1 == NULL && k2 == NULL)
2591 if (k1 == NULL || k2 == NULL)
2593 return _stricmp(k1, k2) == 0;
2596 static count_t Hash(key_t k)
2598 LIMITED_METHOD_CONTRACT;
2603 return HashiStringA(k);
2607 typedef DocNameOffsets VALUE;
2608 typedef NoRemoveSHashTraits < MapSHashTraits<LPCSTR, DocNameOffsets> > PARENT;
2609 typedef PARENT::element_t element_t;
2610 static const element_t Null() { LIMITED_METHOD_CONTRACT; return element_t((KEY)0,VALUE((ULONG32) -1, (ULONG32) -1)); }
2611 static bool IsNull(const element_t &e) { LIMITED_METHOD_CONTRACT; return e.Key() == (KEY)0; }
2615 // ----------------------------------------------------------------------------
2616 // Hash table that maps the UTF-8 string of a source file name to its corresponding
2617 // DocNameToOffsetMapTraits
2619 class DocNameToOffsetMap : public SHash<DocNameToOffsetMapTraits>
2621 typedef SHash<DocNameToOffsetMapTraits> PARENT;
2623 typedef DocNameOffsets VALUE;
2626 void Add(KEY key, VALUE value)
2632 PRECONDITION(key != (KEY)0);
2636 PARENT::Add(KeyValuePair<KEY,VALUE>(key, value));
2639 void AddOrReplace(KEY key, VALUE value)
2645 PRECONDITION(key != (KEY)0);
2649 PARENT::AddOrReplace(KeyValuePair<KEY,VALUE>(key, value));
2652 BOOL Lookup(KEY key, VALUE* pValue)
2658 PRECONDITION(key != (KEY)0);
2662 const KeyValuePair<KEY,VALUE> *pRet = PARENT::LookupPtr(key);
2666 *pValue = pRet->Value();
2671 // ----------------------------------------------------------------------------
2672 // Simple class to sort ICorDebugInfo::OffsetMapping arrays by IL offset
2674 class QuickSortILNativeMapByIL : public CQuickSort<ICorDebugInfo::OffsetMapping>
2677 QuickSortILNativeMapByIL(
2678 ICorDebugInfo::OffsetMapping * rgMap,
2680 : CQuickSort<ICorDebugInfo::OffsetMapping>(rgMap, cEntries)
2682 LIMITED_METHOD_CONTRACT;
2685 int Compare(ICorDebugInfo::OffsetMapping * pFirst,
2686 ICorDebugInfo::OffsetMapping * pSecond)
2688 LIMITED_METHOD_CONTRACT;
2690 if (pFirst->ilOffset < pSecond->ilOffset)
2692 else if (pFirst->ilOffset == pSecond->ilOffset)
2699 // ----------------------------------------------------------------------------
2700 // Simple class to sort IL to Native mapping arrays by Native offset
2702 class QuickSortILNativeMapByNativeOffset : public CQuickSort<ICorDebugInfo::OffsetMapping>
2705 QuickSortILNativeMapByNativeOffset(
2706 ICorDebugInfo::OffsetMapping * rgMap,
2708 : CQuickSort<ICorDebugInfo::OffsetMapping>(rgMap, cEntries)
2710 LIMITED_METHOD_CONTRACT;
2713 int Compare(ICorDebugInfo::OffsetMapping * pFirst,
2714 ICorDebugInfo::OffsetMapping * pSecond)
2716 LIMITED_METHOD_CONTRACT;
2718 if (pFirst->nativeOffset < pSecond->nativeOffset)
2720 else if (pFirst->nativeOffset == pSecond->nativeOffset)
2727 // ----------------------------------------------------------------------------
2728 // Simple structure used when merging the JIT manager's IL-to-native maps
2729 // (ICorDebugInfo::OffsetMapping) with the IL PDB's source-to-IL map.
2734 // Index into ICorDebugInfo::OffsetMapping
2735 ULONG32 m_iIlNativeMap;
2737 // Corresponding index into the IL PDB's sequence point arrays
2738 ULONG32 m_iSeqPoints;
2741 m_iIlNativeMap((ULONG32) -1),
2742 m_iSeqPoints((ULONG32) -1)
2744 LIMITED_METHOD_CONTRACT;
2748 // ----------------------------------------------------------------------------
2749 // Simple class to sort MapIndexPairs by native IP offset. A MapIndexPair sorts "earlier"
2750 // if its m_iIlNativeMap index gives you an IP offset (i.e.,
2751 // m_rgIlNativeMap[m_iIlNativeMap].nativeOffset) that is smaller.
2753 class QuickSortMapIndexPairsByNativeOffset : public CQuickSort<MapIndexPair>
2756 QuickSortMapIndexPairsByNativeOffset(
2757 MapIndexPair * rgMap,
2759 ICorDebugInfo::OffsetMapping * rgIlNativeMap,
2760 ULONG32 cIlNativeMap)
2761 : CQuickSort<MapIndexPair>(rgMap, cEntries),
2762 m_rgIlNativeMap(rgIlNativeMap),
2763 m_cIlNativeMap(cIlNativeMap)
2765 LIMITED_METHOD_CONTRACT;
2768 int Compare(MapIndexPair * pFirst,
2769 MapIndexPair * pSecond)
2771 LIMITED_METHOD_CONTRACT;
2773 _ASSERTE(pFirst->m_iIlNativeMap < m_cIlNativeMap);
2774 _ASSERTE(pSecond->m_iIlNativeMap < m_cIlNativeMap);
2776 DWORD dwFirstNativeOffset = m_rgIlNativeMap[pFirst->m_iIlNativeMap].nativeOffset;
2777 DWORD dwSecondNativeOffset = m_rgIlNativeMap[pSecond->m_iIlNativeMap].nativeOffset;
2779 if (dwFirstNativeOffset < dwSecondNativeOffset)
2781 else if (dwFirstNativeOffset == dwSecondNativeOffset)
2788 ICorDebugInfo::OffsetMapping * m_rgIlNativeMap;
2789 ULONG32 m_cIlNativeMap;
2792 // ----------------------------------------------------------------------------
2793 // The following 3 classes contain the code to generate PDBs
2796 // NGEN always generates PDBs with public symbols lists (so tools can map IP ranges to
2797 // methods). This bitmask indicates what extra info should be added to the PDB
2800 // Add string table subsection, files checksum subsection, and lines subsection to
2801 // allow tools to map IP ranges to source lines.
2802 kPDBLines = 0x00000001,
2806 // ----------------------------------------------------------------------------
2807 // Manages generating all PDB data for an NGENd image. One of these is instantiated per
2808 // run of "ngen createpdb"
2813 CreateNGenPdbWriter_t m_Create;
2815 ReleaseHolder<ISymUnmanagedBinder> m_pBinder;
2816 LPCWSTR m_wszPdbPath;
2817 DWORD m_dwExtraData;
2818 LPCWSTR m_wszManagedPDBSearchPath;
2821 NGenPdbWriter (LPCWSTR wszNativeImagePath, LPCWSTR wszPdbPath, DWORD dwExtraData, LPCWSTR wszManagedPDBSearchPath)
2824 m_wszPdbPath(wszPdbPath),
2825 m_dwExtraData(dwExtraData),
2826 m_wszManagedPDBSearchPath(wszManagedPDBSearchPath)
2828 LIMITED_METHOD_CONTRACT;
2831 #define WRITER_LOAD_ERROR_MESSAGE W("Unable to load ") NATIVE_SYMBOL_READER_DLL W(". Please ensure that ") NATIVE_SYMBOL_READER_DLL W(" is on the path. Error='%d'\n")
2833 HRESULT Load(LPCWSTR wszDiasymreaderPath = nullptr)
2835 STANDARD_VM_CONTRACT;
2839 m_hModule = WszLoadLibrary(wszDiasymreaderPath != nullptr ? wszDiasymreaderPath : (LPCWSTR)NATIVE_SYMBOL_READER_DLL);
2840 if (m_hModule == NULL)
2842 hr = HRESULT_FROM_WIN32(GetLastError());
2843 GetSvcLogger()->Printf(WRITER_LOAD_ERROR_MESSAGE, GetLastError());
2847 m_Create = reinterpret_cast<CreateNGenPdbWriter_t>(GetProcAddress(m_hModule, "CreateNGenPdbWriter"));
2848 if (m_Create == NULL)
2850 hr = HRESULT_FROM_WIN32(GetLastError());
2851 GetSvcLogger()->Printf(WRITER_LOAD_ERROR_MESSAGE, GetLastError());
2855 if ((m_dwExtraData & kPDBLines) != 0)
2857 hr = FakeCoCreateInstanceEx(
2858 CLSID_CorSymBinder_SxS,
2859 wszDiasymreaderPath != nullptr ? wszDiasymreaderPath : (LPCWSTR)NATIVE_SYMBOL_READER_DLL,
2860 IID_ISymUnmanagedBinder,
2868 HRESULT WritePDBDataForModule(Module * pModule);
2872 LIMITED_METHOD_CONTRACT;
2875 FreeLibrary(m_hModule);
2881 #define UNKNOWN_SOURCE_FILE_PATH L"unknown"
2883 // ----------------------------------------------------------------------------
2884 // Manages generating all PDB data for an EE Module. Directly responsible for writing the
2885 // string table and file checksum subsections. One of these is instantiated per Module
2886 // found when using the ModuleIterator over the CORINFO_ASSEMBLY_HANDLE corresponding to
2887 // this invocation of NGEN createpdb.
2889 class NGenModulePdbWriter
2892 // Simple holder to coordinate the PDB calls to OpenModW and CloseMod on a given PDB
2897 ReleaseHolder<ISymNGenWriter2> m_pWriter;
2905 LIMITED_METHOD_CONTRACT;
2910 LIMITED_METHOD_CONTRACT;
2912 if ((m_pWriter != NULL) && (m_pMod != NULL))
2914 m_pWriter->CloseMod(m_pMod);
2918 HRESULT Open(ISymNGenWriter2 * pWriter, LPCWSTR wszModule, LPCWSTR wszObjFile)
2920 LIMITED_METHOD_CONTRACT;
2922 _ASSERTE(m_pWriter == NULL);
2924 m_pWriter = pWriter;
2925 m_pWriter->AddRef();
2927 _ASSERTE(m_pMod == NULL);
2929 HRESULT hr = m_pWriter->OpenModW(wszModule, wszObjFile, &m_pMod);
2939 LIMITED_METHOD_CONTRACT;
2941 _ASSERTE(m_pMod != NULL);
2947 // This holder ensures we delete a half-generated PDB file if we manage to create it
2948 // on disk, but fail at some point after it was created. When NGenModulePdbWriter is
2949 // destroyed, m_deletePDBFileHolder's destructor will delete the PDB file if there
2950 // was a prior error.
2952 //************* NOTE! *************
2954 // These members should appear FIRST so that they get destructed last. That way, if
2955 // we encounter an error generating the PDB file, we ensure that we release all PDB
2956 // interfaces and close the PDB file BEFORE this holder tries to *delete* the PDB
2957 // file. Also, keep these two in this relative order, so that m_deletePDBFileHolder
2958 // is destructed before m_wszPDBFilePath.
2959 WCHAR m_wszPDBFilePath[MAX_LONGPATH];
2960 DeleteFileHolder m_deletePDBFileHolder;
2962 // ************* NOTE! *************
2964 CreateNGenPdbWriter_t m_Create;
2965 LPCWSTR m_wszPdbPath;
2966 ReleaseHolder<ISymNGenWriter2> m_pWriter;
2968 DWORD m_dwExtraData;
2969 LPCWSTR m_wszManagedPDBSearchPath;
2971 // Currently The DiasymWriter does not use the correct PDB signature for NGEN PDBS unless
2972 // the NGEN DLL whose symbols are being generated end in .ni.dll. Thus we copy
2973 // to this name if it does not follow this covention (as is true with readyToRun
2974 // dlls). This variable remembers this temp file path so we can delete it after
2975 // Pdb generation. If DiaSymWriter is fixed, we can remove this.
2976 SString m_tempSourceDllName;
2978 // Interfaces for reading IL PDB info
2979 ReleaseHolder<ISymUnmanagedBinder> m_pBinder;
2980 ReleaseHolder<ISymUnmanagedReader> m_pReader;
2981 NewInterfaceArrayHolder<ISymUnmanagedDocument> m_rgpDocs; // All docs in the PDB Mod
2982 // I know m_ilPdbCount and m_finalPdbDocCount are confusing.Here is the reason :
2983 // For NGenMethodLinesPdbWriter::WriteDebugSILLinesSubsection, we won't write the path info.
2984 // In order to let WriteDebugSILLinesSubsection find "UNKNOWN_SOURCE_FILE_PATH" which does
2985 // not exist in m_rgpDocs, no matter if we have IL PDB or not, we let m_finalPdbDocCount
2986 // equal m_ilPdbDocCount + 1 and write the extra one path as "UNKNOWN_SOURCE_FILE_PATH"
2987 ULONG32 m_ilPdbDocCount;
2988 ULONG32 m_finalPdbDocCount;
2990 // Keeps track of source file names and how they map to offsets in the relevant PDB
2992 DocNameToOffsetMap m_docNameToOffsetMap;
2994 // Holds a PDB Mod *
2995 PDBModHolder m_pdbMod;
2997 // Buffer in which to store the entire string table (i.e., list of all source file
2998 // names). This buffer is held alive as long as m_docNameToOffsetMap is needed, as
2999 // the latter contains offsets into this buffer.
3000 NewArrayHolder<BYTE> m_rgbStringTableSubsection;
3002 HRESULT InitILPdbData();
3003 HRESULT WriteStringTable();
3004 HRESULT WriteFileChecksums();
3007 NGenModulePdbWriter(CreateNGenPdbWriter_t Create, LPCWSTR wszPdbPath, DWORD dwExtraData, ISymUnmanagedBinder * pBinder, Module * pModule, LPCWSTR wszManagedPDBSearchPath)
3009 m_wszPdbPath(wszPdbPath),
3011 m_dwExtraData(dwExtraData),
3014 m_wszManagedPDBSearchPath(wszManagedPDBSearchPath),
3016 m_finalPdbDocCount(1)
3018 LIMITED_METHOD_CONTRACT;
3020 if (m_pBinder != NULL)
3021 m_pBinder->AddRef();
3023 ZeroMemory(m_wszPDBFilePath, sizeof(m_wszPDBFilePath));
3026 ~NGenModulePdbWriter();
3028 HRESULT WritePDBData();
3030 HRESULT WriteMethodPDBData(PEImageLayout * pLoadedLayout, USHORT iCodeSection, BYTE *pCodeBase, MethodDesc * hotDesc, PCODE start, bool isILPDBProvided);
3033 // ----------------------------------------------------------------------------
3034 // Manages generating the lines subsection in the PDB data for a given managed method.
3035 // One of these is instantiated per managed method we find when iterating through all
3036 // methods in a Module.
3038 class NGenMethodLinesPdbWriter
3041 ISymNGenWriter2 * m_pWriter;
3043 ISymUnmanagedReader * m_pReader;
3044 MethodDesc * m_hotDesc;
3046 USHORT m_iCodeSection;
3047 TADDR m_addrCodeSection;
3048 const IJitManager::MethodRegionInfo * m_pMethodRegionInfo;
3049 EECodeInfo * m_pCodeInfo;
3050 DocNameToOffsetMap * m_pDocNameToOffsetMap;
3051 bool m_isILPDBProvided;
3053 // IL-to-native map from JIT manager
3054 ULONG32 m_cIlNativeMap;
3055 NewArrayHolder<ICorDebugInfo::OffsetMapping> m_rgIlNativeMap;
3057 // IL PDB info for this one method
3058 NewInterfaceArrayHolder<ISymUnmanagedDocument> m_rgpDocs; // Source files defining this method.
3059 NewArrayHolder<ULONG32> m_rgilOffsets; // Array of IL offsets for this method
3060 NewArrayHolder<ULONG32> m_rgnLineStarts; // Array of source lines for this method
3061 ULONG32 m_cSeqPoints; // Count of above two parallel arrays
3063 HRESULT WriteNativeILMapPDBData();
3064 LPBYTE InitDebugLinesHeaderSection(
3065 DEBUG_S_SUBSECTION_TYPE type,
3066 ULONG32 ulCodeStartOffset,
3069 CV_DebugSSubsectionHeader_t **ppSubSectHeader /*out*/,
3070 CV_DebugSLinesHeader_t ** ppLinesHeader /*out*/,
3071 LPBYTE * ppbLinesSubsectionCur /*out*/);
3073 HRESULT WriteDebugSLinesSubsection(
3074 ULONG32 ulCodeStartOffset,
3076 MapIndexPair * rgMapIndexPairs,
3077 ULONG32 cMapIndexPairs);
3079 HRESULT WriteDebugSILLinesSubsection(
3080 ULONG32 ulCodeStartOffset,
3082 ICorDebugInfo::OffsetMapping * rgILNativeMap,
3083 ULONG32 rgILNativeMapAdjustSize);
3085 BOOL FinalizeLinesFileBlock(
3086 CV_DebugSLinesFileBlockHeader_t * pLinesFileBlockHeader,
3087 CV_Line_t * pLineBlockStart,
3088 CV_Line_t * pLineBlockAfterEnd
3090 , BOOL ignorekUnmappedIPCheck = false
3095 NGenMethodLinesPdbWriter(
3096 ISymNGenWriter2 * pWriter,
3098 ISymUnmanagedReader * pReader,
3099 MethodDesc * hotDesc,
3101 USHORT iCodeSection,
3102 TADDR addrCodeSection,
3103 const IJitManager::MethodRegionInfo * pMethodRegionInfo,
3104 EECodeInfo * pCodeInfo,
3105 DocNameToOffsetMap * pDocNameToOffsetMap,
3106 bool isILPDBProvided)
3107 : m_pWriter(pWriter),
3112 m_iCodeSection(iCodeSection),
3113 m_addrCodeSection(addrCodeSection),
3114 m_pMethodRegionInfo(pMethodRegionInfo),
3115 m_pCodeInfo(pCodeInfo),
3116 m_pDocNameToOffsetMap(pDocNameToOffsetMap),
3119 m_isILPDBProvided(isILPDBProvided)
3121 LIMITED_METHOD_CONTRACT;
3124 HRESULT WritePDBData();
3127 // ----------------------------------------------------------------------------
3128 // NGenPdbWriter implementation
3132 //---------------------------------------------------------------------------------------
3134 // Coordinates calling all the other classes & methods to generate PDB info for the
3138 // pModule - EE Module to write PDB data for
3141 HRESULT NGenPdbWriter::WritePDBDataForModule(Module * pModule)
3143 STANDARD_VM_CONTRACT;
3144 NGenModulePdbWriter ngenModulePdbWriter(m_Create, m_wszPdbPath, m_dwExtraData, m_pBinder, pModule, m_wszManagedPDBSearchPath);
3145 return ngenModulePdbWriter.WritePDBData();
3149 // ----------------------------------------------------------------------------
3150 // NGenModulePdbWriter implementation
3153 //---------------------------------------------------------------------------------------
3155 // Writes out all source files into the string table subsection for the PDB Mod*
3156 // controlled by this NGenModulePdbWriter. Updates m_docNameToOffsetMap to add string
3157 // table offset for each source file as it gets added.
3159 HRESULT NGenModulePdbWriter::WriteStringTable()
3161 STANDARD_VM_CONTRACT;
3163 _ASSERTE(m_pWriter != NULL);
3166 UINT64 cbStringTableEstimate =
3168 sizeof(CV_DebugSSubsectionHeader_t) +
3169 m_finalPdbDocCount * (MAX_LONGPATH + 1);
3170 if (!FitsIn<ULONG32>(cbStringTableEstimate))
3172 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
3175 m_rgbStringTableSubsection = new BYTE[ULONG32(cbStringTableEstimate)];
3176 LPBYTE pbStringTableSubsectionCur = m_rgbStringTableSubsection;
3178 // Subsection signature
3179 *((DWORD *) pbStringTableSubsectionCur) = CV_SIGNATURE_C13;
3180 pbStringTableSubsectionCur += sizeof(DWORD);
3182 // Subsection header
3183 CV_DebugSSubsectionHeader_t * pSubSectHeader = (CV_DebugSSubsectionHeader_t *) pbStringTableSubsectionCur;
3184 memset(pSubSectHeader, 0, sizeof(*pSubSectHeader));
3185 pSubSectHeader->type = DEBUG_S_STRINGTABLE;
3186 pbStringTableSubsectionCur += sizeof(*pSubSectHeader);
3187 // pSubSectHeader->cbLen counts the number of bytes that appear AFTER the subsection
3188 // header above (i.e., the size of the string table itself). We'll fill out
3189 // pSubSectHeader->cbLen below, once it's calculated
3191 LPBYTE pbStringTableStart = pbStringTableSubsectionCur;
3193 // The actual strings
3194 for (ULONG32 i = 0; i < m_finalPdbDocCount; i++)
3196 // For NGenMethodLinesPdbWriter::WriteDebugSILLinesSubsection, we won't write the path info.
3197 // In order to let WriteDebugSILLinesSubsection can find "UNKNOWN_SOURCE_FILE_PATH" which is
3198 // not existed in m_rgpDocs, no matter we have IL PDB or not, we let m_finalPdbDocCount equals to
3199 // m_ilPdbDocCount + 1 and write the extra one path as "UNKNOWN_SOURCE_FILE_PATH". That also explains
3200 // why we have a inconsistence between m_finalPdbDocCount and m_ilPdbDocCount.
3201 WCHAR wszURL[MAX_LONGPATH] = UNKNOWN_SOURCE_FILE_PATH;
3203 if (i < m_ilPdbDocCount)
3205 hr = m_rgpDocs[i]->GetURL(_countof(wszURL), &cchURL, wszURL);
3209 int cbWritten = WideCharToMultiByte(
3213 -1, // i.e., input is NULL-terminated
3214 (LPSTR) pbStringTableSubsectionCur, // output: UTF8 string starts here
3215 ULONG32(cbStringTableEstimate) -
3216 int(pbStringTableSubsectionCur - m_rgbStringTableSubsection), // Available space
3217 NULL, // lpDefaultChar
3218 NULL // lpUsedDefaultChar
3221 return HRESULT_FROM_WIN32(GetLastError());
3223 // Remember the string table offset for later
3224 m_docNameToOffsetMap.AddOrReplace(
3225 (LPCSTR) pbStringTableSubsectionCur,
3227 ULONG32(pbStringTableSubsectionCur - pbStringTableStart),
3230 pbStringTableSubsectionCur += cbWritten;
3231 if (pbStringTableSubsectionCur >= (m_rgbStringTableSubsection + cbStringTableEstimate))
3232 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
3235 // Now that we know pSubSectHeader->cbLen, fill it in
3236 pSubSectHeader->cbLen = CV_off32_t(pbStringTableSubsectionCur - pbStringTableStart);
3238 // Subsection is now filled out, so use the PDB API to add it
3239 hr = m_pWriter->ModAddSymbols(
3240 m_pdbMod.GetModPtr(),
3241 m_rgbStringTableSubsection,
3242 int(pbStringTableSubsectionCur - m_rgbStringTableSubsection));
3249 //---------------------------------------------------------------------------------------
3251 // This takes care of actually loading the IL PDB itself, and initializing the
3252 // ISymUnmanaged* interfaces with module-level data from the IL PDB.
3254 HRESULT NGenModulePdbWriter::InitILPdbData()
3256 // Load the managed PDB
3258 ReleaseHolder<IUnknown> pUnk = NULL;
3259 HRESULT hr = m_pModule->GetReadablePublicMetaDataInterface(ofReadOnly, IID_IMetaDataImport, (LPVOID *) &pUnk);
3262 GetSvcLogger()->Printf(
3263 W("Unable to obtain metadata for '%s' Error: '0x%x'.\n"),
3264 LPCWSTR(m_pModule->GetFile()->GetILimage()->GetPath()),
3269 hr = m_pBinder->GetReaderForFile(
3271 m_pModule->GetFile()->GetILimage()->GetPath(),
3272 m_wszManagedPDBSearchPath,
3276 GetSvcLogger()->Printf(
3277 W("Unable to find managed PDB matching '%s'. Managed PDB search path: '%s'\n"),
3278 LPCWSTR(m_pModule->GetFile()->GetILimage()->GetPath()),
3279 (((m_wszManagedPDBSearchPath == NULL) || (*m_wszManagedPDBSearchPath == W('\0'))) ?
3280 W("(not specified)") :
3281 m_wszManagedPDBSearchPath));
3285 GetSvcLogger()->Log(W("Loaded managed PDB"));
3287 // Grab the full path of the managed PDB so we can log it
3288 WCHAR wszIlPdbPath[MAX_LONGPATH];
3289 ULONG32 cchIlPdbPath;
3290 hr = m_pReader->GetSymbolStoreFileName(
3291 _countof(wszIlPdbPath),
3296 GetSvcLogger()->Log(W("\n"));
3300 GetSvcLogger()->Printf(W(": '%s'\n"), wszIlPdbPath);
3303 // Read all source files names from the IL PDB
3305 hr = m_pReader->GetDocuments(
3306 0, // cDocsRequested
3313 m_rgpDocs = new ISymUnmanagedDocument * [cDocs];
3314 hr = m_pReader->GetDocuments(
3320 m_finalPdbDocCount = m_ilPdbDocCount + 1;
3321 // Commit m_rgpDocs to calling Release() on each ISymUnmanagedDocument* in the array
3322 m_rgpDocs.SetElementCount(m_ilPdbDocCount);
3327 NGenModulePdbWriter::~NGenModulePdbWriter()
3329 // Delete any temporary files we created.
3330 if (m_tempSourceDllName.GetCount() != 0)
3331 DeleteFileW(m_tempSourceDllName);
3332 m_tempSourceDllName.Clear();
3335 //---------------------------------------------------------------------------------------
3337 // This manages writing all Module-level data to the PDB, including public symbols,
3338 // string table, files checksum, section contribution table, and, indirectly, the lines
3341 HRESULT NGenModulePdbWriter::WritePDBData()
3343 STANDARD_VM_CONTRACT;
3345 _ASSERTE(m_pWriter == NULL);
3349 // This will try to open the managed PDB if lines info was requested. This is a
3350 // likely failure point, so intentionally do this before creating the NGEN PDB file
3352 bool isILPDBProvided = false;
3353 if ((m_dwExtraData & kPDBLines) != 0)
3355 hr = InitILPdbData();
3358 isILPDBProvided = true;
3361 // Create the PDB file we will write into.
3363 _ASSERTE(m_Create != NULL);
3364 _ASSERTE(m_pModule != NULL);
3366 PEImageLayout * pLoadedLayout = m_pModule->GetFile()->GetLoaded();
3368 // Currently DiaSymReader does not work properly generating NGEN PDBS unless
3369 // the DLL whose PDB is being generated ends in .ni.*. Unfortunately, readyToRun
3370 // images do not follow this convention and end up producing bad PDBS. To fix
3371 // this (without changing diasymreader.dll which ships indepdendently of .Net Core)
3372 // we copy the file to somethign with this convention before generating the PDB
3373 // and delete it when we are done.
3374 SString dllPath = pLoadedLayout->GetPath();
3375 if (!dllPath.EndsWithCaseInsensitive(L".ni.dll") && !dllPath.EndsWithCaseInsensitive(L".ni.exe"))
3377 SString::Iterator fileNameStart = dllPath.Begin();
3378 dllPath.FindBack(fileNameStart, '\\');
3380 SString::Iterator ext = dllPath.End();
3381 dllPath.FindBack(ext, '.');
3383 // m_tempSourceDllName = Convertion of INPUT.dll to INPUT.ni.dll where the PDB lives.
3384 m_tempSourceDllName = m_wszPdbPath;
3385 m_tempSourceDllName += SString(dllPath, fileNameStart, ext - fileNameStart);
3386 m_tempSourceDllName += L".ni";
3387 m_tempSourceDllName += SString(dllPath, ext, dllPath.End() - ext);
3388 CopyFileW(dllPath, m_tempSourceDllName, false);
3389 dllPath = m_tempSourceDllName;
3392 ReleaseHolder<ISymNGenWriter> pWriter1;
3393 hr = m_Create(dllPath, m_wszPdbPath, &pWriter1);
3397 hr = pWriter1->QueryInterface(IID_ISymNGenWriter2, (LPVOID*) &m_pWriter);
3400 GetSvcLogger()->Printf(
3401 W("An incorrect version of diasymreader.dll was found. Please ensure that version 11 or greater of diasymreader.dll is on the path. You can typically find this DLL in the desktop .NET install directory for 4.5 or greater. Error='0x%x'\n"),
3406 // PDB file is now created. Get its path and initialize the holder so the PDB file
3407 // can be deleted if we don't make it successfully to the end
3409 hr = m_pWriter->QueryPDBNameExW(m_wszPDBFilePath, _countof(m_wszPDBFilePath));
3412 // A failure in QueryPDBNameExW above isn't fatal--it just means we can't
3413 // initialize m_deletePDBFileHolder, and thus may leave the PDB file on disk if
3414 // there's *another* error later on. And if we do hit another error, NGEN will
3415 // still return an error exit code, so the worst we'll have is a bogus PDB file
3416 // that no one should expect works anyway.
3417 m_deletePDBFileHolder.Assign(m_wszPDBFilePath);
3421 hr = m_pdbMod.Open(m_pWriter, pLoadedLayout->GetPath(), m_pModule->GetPath());
3425 hr = WriteStringTable();
3429 hr = WriteFileChecksums();
3434 COUNT_T sectionCount = pLoadedLayout->GetNumberOfSections();
3435 IMAGE_SECTION_HEADER *section = pLoadedLayout->FindFirstSection();
3436 COUNT_T sectionIndex = 0;
3437 USHORT iCodeSection = 0;
3438 BYTE *pCodeBase = NULL;
3439 while (sectionIndex < sectionCount)
3441 hr = m_pWriter->AddSection((USHORT)(sectionIndex + 1),
3444 section[sectionIndex].SizeOfRawData);
3448 if (strcmp((const char *)§ion[sectionIndex].Name[0], ".text") == 0) {
3449 _ASSERTE((iCodeSection == 0) && (pCodeBase == NULL));
3450 iCodeSection = (USHORT)(sectionIndex + 1);
3451 pCodeBase = (BYTE *)section[sectionIndex].VirtualAddress;
3454 // In order to support the DIA RVA-to-lines API against the PDB we're
3455 // generating, we need to update the section contribution table with each
3457 hr = m_pWriter->ModAddSecContribEx(
3458 m_pdbMod.GetModPtr(),
3459 (USHORT)(sectionIndex + 1),
3461 section[sectionIndex].SizeOfRawData,
3462 section[sectionIndex].Characteristics,
3472 _ASSERTE(iCodeSection != 0);
3473 _ASSERTE(pCodeBase != NULL);
3476 // To support lines info, we need a "dummy" section, indexed as 0, for use as a
3477 // sentinel when MSPDB sets up its section contribution table
3478 hr = m_pWriter->AddSection(0, // Dummy section 0
3486 #ifdef FEATURE_READYTORUN_COMPILER
3487 if (pLoadedLayout->HasReadyToRunHeader())
3489 ReadyToRunInfo::MethodIterator mi(m_pModule->GetReadyToRunInfo());
3492 MethodDesc *hotDesc = mi.GetMethodDesc();
3494 hr = WriteMethodPDBData(pLoadedLayout, iCodeSection, pCodeBase, hotDesc, mi.GetMethodStartAddress(), isILPDBProvided);
3500 #endif // FEATURE_READYTORUN_COMPILER
3502 MethodIterator mi(m_pModule);
3505 MethodDesc *hotDesc = mi.GetMethodDesc();
3506 hotDesc->CheckRestore();
3508 hr = WriteMethodPDBData(pLoadedLayout, iCodeSection, pCodeBase, hotDesc, mi.GetMethodStartAddress(), isILPDBProvided);
3514 // We made it successfully to the end, so don't delete the PDB file.
3515 m_deletePDBFileHolder.SuppressRelease();
3519 HRESULT NGenModulePdbWriter::WriteMethodPDBData(PEImageLayout * pLoadedLayout, USHORT iCodeSection, BYTE *pCodeBase, MethodDesc * hotDesc, PCODE start, bool isILPDBProvided)
3521 STANDARD_VM_CONTRACT;
3525 EECodeInfo codeInfo(start);
3526 _ASSERTE(codeInfo.IsValid());
3528 IJitManager::MethodRegionInfo methodRegionInfo;
3529 codeInfo.GetMethodRegionInfo(&methodRegionInfo);
3531 PCODE pHotCodeStart = methodRegionInfo.hotStartAddress;
3532 _ASSERTE(pHotCodeStart);
3534 PCODE pColdCodeStart = methodRegionInfo.coldStartAddress;
3535 SString mAssemblyName;
3536 mAssemblyName.SetUTF8(m_pModule->GetAssembly()->GetSimpleName());
3537 SString assemblyName;
3538 assemblyName.SetUTF8(hotDesc->GetAssembly()->GetSimpleName());
3539 SString methodToken;
3540 methodToken.Printf("%X", hotDesc->GetMemberDef());
3545 TypeString::AppendMethodInternal(
3548 TypeString::FormatNamespace | TypeString::FormatSignature);
3549 fullName.Append(L"$#");
3550 if (!mAssemblyName.Equals(assemblyName))
3551 fullName.Append(assemblyName);
3552 fullName.Append(L"#");
3553 fullName.Append(methodToken);
3554 BSTRHolder hotNameHolder(SysAllocString(fullName.GetUnicode()));
3555 hr = m_pWriter->AddSymbol(hotNameHolder,
3557 (pHotCodeStart - (TADDR)pLoadedLayout->GetBase() - (TADDR)pCodeBase));
3564 if (pColdCodeStart) {
3566 SString fullNameCold;
3567 fullNameCold.Append(W("[COLD] "));
3568 TypeString::AppendMethodInternal(
3571 TypeString::FormatNamespace | TypeString::FormatSignature);
3572 fullNameCold.Append(L"$#");
3573 if (!mAssemblyName.Equals(assemblyName))
3574 fullNameCold.Append(assemblyName);
3575 fullNameCold.Append(L"#");
3576 fullNameCold.Append(methodToken);
3578 BSTRHolder coldNameHolder(SysAllocString(fullNameCold.GetUnicode()));
3579 hr = m_pWriter->AddSymbol(coldNameHolder,
3581 (pColdCodeStart - (TADDR)pLoadedLayout->GetBase() - (TADDR)pCodeBase));
3589 // Offset / lines mapping
3590 // Skip functions that are too big for PDB lines format
3591 if (FitsIn<DWORD>(methodRegionInfo.hotSize) &&
3592 FitsIn<DWORD>(methodRegionInfo.coldSize))
3594 NGenMethodLinesPdbWriter methodLinesWriter(
3596 m_pdbMod.GetModPtr(),
3601 (TADDR)pLoadedLayout->GetBase() + (TADDR)pCodeBase,
3604 &m_docNameToOffsetMap,
3607 hr = methodLinesWriter.WritePDBData();
3615 // ----------------------------------------------------------------------------
3616 // Handles writing the file checksums subsection to the PDB
3618 HRESULT NGenModulePdbWriter::WriteFileChecksums()
3620 STANDARD_VM_CONTRACT;
3622 _ASSERTE(m_pWriter != NULL);
3624 // The file checksums subsection of the PDB (i.e., "DEBUG_S_FILECHKSMS"), is a blob
3625 // consisting of a few structs stacked one after the other:
3627 // * (1) DWORD = CV_SIGNATURE_C13 -- the usual subsection signature DWORD
3628 // * (2) CV_DebugSSubsectionHeader_t -- the usual subsection header, with type =
3629 // DEBUG_S_FILECHKSMS
3630 // * (3) Blob consisting of an array of checksum data -- the format of this piece is
3631 // not defined via structs (not sure why), but is defined in
3632 // vctools\PDB\doc\lines.docx
3636 // PDB format requires that the checksum size can always be expressed in a BYTE.
3637 const BYTE kcbEachChecksumEstimate = 0xFF;
3639 UINT64 cbChecksumSubsectionEstimate =
3641 sizeof(CV_DebugSSubsectionHeader_t) +
3642 m_finalPdbDocCount * kcbEachChecksumEstimate;
3643 if (!FitsIn<ULONG32>(cbChecksumSubsectionEstimate))
3645 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
3648 NewArrayHolder<BYTE> rgbChksumSubsection(new BYTE[ULONG32(cbChecksumSubsectionEstimate)]);
3649 LPBYTE pbChksumSubsectionCur = rgbChksumSubsection;
3651 // (1) Subsection signature
3652 *((DWORD *) pbChksumSubsectionCur) = CV_SIGNATURE_C13;
3653 pbChksumSubsectionCur += sizeof(DWORD);
3655 // (2) Subsection header
3656 CV_DebugSSubsectionHeader_t * pSubSectHeader = (CV_DebugSSubsectionHeader_t *) pbChksumSubsectionCur;
3657 memset(pSubSectHeader, 0, sizeof(*pSubSectHeader));
3658 pSubSectHeader->type = DEBUG_S_FILECHKSMS;
3659 pbChksumSubsectionCur += sizeof(*pSubSectHeader);
3660 // pSubSectHeader->cblen to be filled in later once we know the size
3662 LPBYTE pbChksumDataStart = pbChksumSubsectionCur;
3664 // (3) Iterate through source files, steal their checksum info from the IL PDB, and
3665 // write it into the NGEN PDB.
3666 for (ULONG32 i = 0; i < m_finalPdbDocCount; i++)
3668 WCHAR wszURL[MAX_LONGPATH] = UNKNOWN_SOURCE_FILE_PATH;
3669 char szURL[MAX_LONGPATH];
3673 bool isKnownSourcePath = i < m_ilPdbDocCount;
3674 if (isKnownSourcePath)
3676 // For NGenMethodLinesPdbWriter::WriteDebugSILLinesSubsection, we won't write the path info.
3677 // In order to let WriteDebugSILLinesSubsection can find "UNKNOWN_SOURCE_FILE_PATH" which is
3678 // not existed in m_rgpDocs, no matter we have IL PDB or not, we let m_finalPdbDocCount equals to
3679 // m_ilPdbDocCount + 1 and write the extra one path as "UNKNOWN_SOURCE_FILE_PATH". That also explains
3680 // why we have a inconsistence between m_finalPdbDocCount and m_ilPdbDocCount.
3681 hr = m_rgpDocs[i]->GetURL(_countof(wszURL), &cchURL, wszURL);
3686 int cbWritten = WideCharToMultiByte(
3690 -1, // i.e., input is NULL-terminated
3691 szURL, // output: UTF8 string starts here
3692 _countof(szURL), // Available space
3693 NULL, // lpDefaultChar
3694 NULL // lpUsedDefaultChar
3697 return HRESULT_FROM_WIN32(GetLastError());
3699 // find offset into string table and add to blob; meanwhile update hash to
3700 // remember the offset into the cksum table
3701 const KeyValuePair<LPCSTR,DocNameOffsets> * pMapEntry =
3702 m_docNameToOffsetMap.LookupPtr(szURL);
3703 if (pMapEntry == NULL)
3705 // Should never happen, as it implies we found a source file that was never
3706 // written to the string table
3707 return E_UNEXPECTED;
3709 DocNameOffsets docNameOffsets(pMapEntry->Value());
3710 docNameOffsets.m_dwChksumTableOffset = ULONG32(pbChksumSubsectionCur - pbChksumDataStart);
3712 // Update the map with the new docNameOffsets that contains the cksum table
3713 // offset as well. Note that we must ensure the key (LPCSTR) remains the same
3714 // (thus we explicitly ask for the Key()). This class guarantees that string
3715 // pointer (which comes from the string table buffer field) will remain allocated
3716 // as long as the map is.
3717 m_docNameToOffsetMap.AddOrReplace(pMapEntry->Key(), docNameOffsets);
3718 * (ULONG32 *) pbChksumSubsectionCur = docNameOffsets.m_dwStrTableOffset;
3719 pbChksumSubsectionCur += sizeof(ULONG32);
3721 // Checksum algorithm and bytes
3723 BYTE rgbChecksum[kcbEachChecksumEstimate];
3724 ULONG32 cbChecksum = 0;
3725 BYTE bChecksumAlgorithmType = CHKSUM_TYPE_NONE;
3726 if (isKnownSourcePath)
3728 GUID guidChecksumAlgorithm;
3729 hr = m_rgpDocs[i]->GetCheckSumAlgorithmId(&guidChecksumAlgorithm);
3732 // If we got the checksum algorithm, we can write it all out to the buffer.
3733 // Else, we'll just omit the checksum info
3734 if (memcmp(&guidChecksumAlgorithm, &CorSym_SourceHash_MD5, sizeof(GUID)) == 0)
3735 bChecksumAlgorithmType = CHKSUM_TYPE_MD5;
3736 else if (memcmp(&guidChecksumAlgorithm, &CorSym_SourceHash_SHA1, sizeof(GUID)) == 0)
3737 bChecksumAlgorithmType = CHKSUM_TYPE_SHA1;
3741 if (bChecksumAlgorithmType != CHKSUM_TYPE_NONE)
3743 hr = m_rgpDocs[i]->GetCheckSum(sizeof(rgbChecksum), &cbChecksum, rgbChecksum);
3744 if (FAILED(hr) || !FitsIn<BYTE>(cbChecksum))
3746 // Should never happen, but just in case checksum data is invalid, just put
3747 // no checksum into the NGEN PDB
3748 bChecksumAlgorithmType = CHKSUM_TYPE_NONE;
3753 // checksum length & algorithm
3754 *pbChksumSubsectionCur = (BYTE) cbChecksum;
3755 pbChksumSubsectionCur++;
3756 *pbChksumSubsectionCur = bChecksumAlgorithmType;
3757 pbChksumSubsectionCur++;
3759 // checksum data bytes
3760 memcpy(pbChksumSubsectionCur, rgbChecksum, cbChecksum);
3761 pbChksumSubsectionCur += cbChecksum;
3763 // Must align to the next 4-byte boundary
3764 LPBYTE pbChksumSubsectionCurAligned = (LPBYTE) ALIGN_UP(pbChksumSubsectionCur, 4);
3765 memset(pbChksumSubsectionCur, 0, pbChksumSubsectionCurAligned-pbChksumSubsectionCur);
3766 pbChksumSubsectionCur = pbChksumSubsectionCurAligned;
3769 // Now that we know pSubSectHeader->cbLen, fill it in
3770 pSubSectHeader->cbLen = CV_off32_t(pbChksumSubsectionCur - pbChksumDataStart);
3772 // Subsection is now filled out, so add it
3773 hr = m_pWriter->ModAddSymbols(
3774 m_pdbMod.GetModPtr(),
3775 rgbChksumSubsection,
3776 int(pbChksumSubsectionCur - rgbChksumSubsection));
3783 // ----------------------------------------------------------------------------
3784 // NGenMethodLinesPdbWriter implementation
3787 //---------------------------------------------------------------------------------------
3789 // Manages the writing of all lines-file subsections requred for a given method. if a
3790 // method is hot/cold split, this will write two line-file subsections to the PDB--one
3791 // for the hot region, and one for the cold.
3794 HRESULT NGenMethodLinesPdbWriter::WritePDBData()
3796 STANDARD_VM_CONTRACT;
3798 if (m_hotDesc->IsNoMetadata())
3800 // IL stubs will not have data in the IL PDB, so just skip them.
3805 // First, we'll need to merge the IL-to-native map from the JIT manager with the
3806 // IL-to-source map from the IL PDB. This merging is done into a single piece that
3807 // includes all regions of the code when it's split
3810 // Grab the IL-to-native map from the JIT manager
3811 DebugInfoRequest debugInfoRequest;
3812 debugInfoRequest.InitFromStartingAddr(m_hotDesc, m_start);
3813 BOOL fSuccess = m_pCodeInfo->GetJitManager()->GetBoundariesAndVars(
3815 SimpleNew, NULL, // Allocator
3821 // Shouldn't happen, but just skip this method if it does
3825 if (FAILED(hr = WriteNativeILMapPDBData()))
3830 if (!m_isILPDBProvided)
3835 // We will traverse this IL-to-native map (from the JIT) in parallel with the
3836 // source-to-IL map provided by the IL PDB (below). Both need to be sorted by IL so
3837 // we can easily find matching entries in the two maps
3838 QuickSortILNativeMapByIL sorterByIl(m_rgIlNativeMap, m_cIlNativeMap);
3841 // Now grab IL-to-source map from the IL PDBs (just known as "sequence points"
3842 // according to the IL PDB API)
3844 ReleaseHolder<ISymUnmanagedMethod> pMethod;
3845 hr = m_pReader->GetMethod(
3846 m_hotDesc->GetMemberDef(),
3850 // Ignore any methods not included in the IL PDB. Although we've already
3851 // excluded LCG & IL stubs from methods we're considering, there can still be
3852 // methods in the NGEN module that are not in the IL PDB (e.g., implicit ctors).
3856 ULONG32 cSeqPointsExpected;
3857 hr = pMethod->GetSequencePointCount(&cSeqPointsExpected);
3860 // Should never happen, but we can just skip this function if the IL PDB can't
3861 // find sequence point info
3865 ULONG32 cSeqPointsReturned;
3866 m_rgilOffsets = new ULONG32[cSeqPointsExpected];
3867 m_rgpDocs = new ISymUnmanagedDocument * [cSeqPointsExpected];
3868 m_rgnLineStarts = new ULONG32[cSeqPointsExpected];
3870 // This is guaranteed to return the sequence points sorted in order of the IL
3871 // offsets (m_rgilOffsets)
3872 hr = pMethod->GetSequencePoints(
3874 &cSeqPointsReturned,
3878 NULL, // ColumnStarts not needed
3879 NULL, // LineEnds not needed
3880 NULL); // ColumnEnds not needed
3883 // Shouldn't happen, but just skip this method if it does
3886 // Commit m_rgpDocs to calling Release() on all ISymUnmanagedDocument* returned into
3888 m_rgpDocs.SetElementCount(cSeqPointsReturned);
3890 // Now merge the two maps together into an array of MapIndexPair structures. Traverse
3891 // both maps in parallel (both ordered by IL offset), looking for IL offset matches.
3892 // Range matching: If an entry in the IL-to-native map has no matching entry in the
3893 // IL PDB, then seek up in the IL PDB to the previous sequence point and merge to
3894 // that (assuming that previous sequence point from the IL PDB did not already have
3895 // an exact match to some other entry in the IL-to-native map).
3896 ULONG32 cMapIndexPairsMax = m_cIlNativeMap;
3897 NewArrayHolder<MapIndexPair> rgMapIndexPairs(new MapIndexPair [cMapIndexPairsMax]);
3898 ULONG32 iSeqPoints = 0;
3900 // Keep track (via iSeqPointLastUnmatched) of the most recent entry in the IL PDB
3901 // that we passed over because it had no matching entry in the IL-to-native map. We
3902 // may use this to do a range-match if necessary. We'll set iSeqPointLastUnmatched to
3903 // the currently interated IL PDB entry after our cursor in the il-to-native map
3904 // passed it by, but only if fCurSeqPointMatched is FALSE
3905 ULONG32 iSeqPointLastUnmatched = (ULONG32) -1;
3906 BOOL fCurSeqPointMatched = FALSE;
3908 ULONG32 iIlNativeMap = 0;
3909 ULONG32 iMapIndexPairs = 0;
3911 // Traverse IL PDB entries and IL-to-native map entries (both sorted by IL) in
3914 // * Record matching indices in our output map, rgMapIndexPairs, indexed by
3917 // * We will have at most m_cIlNativeMap entries in rgMapIndexPairs by the time
3918 // we're done. (Each il-to-native map entry will be considered for inclusion
3919 // in this output. Those il-to-native map entries with a match in the il PDB
3920 // will be included, the rest skipped.)
3922 // * iSeqPointLastUnmatched != -1 iff it equals a prior entry in the IL PDB that
3923 // we skipped over because it could not be exactly matched to an entry in the
3924 // il-to-native map. In such a case, it will be considered for a
3925 // range-match to the next il-to-native map entry
3926 while (iIlNativeMap < m_cIlNativeMap)
3928 _ASSERTE (iMapIndexPairs < cMapIndexPairsMax);
3930 // IP addresses that map to "special" places (prolog, epilog, or
3931 // other hidden code), will just map to 0xFeeFee, as per convention
3932 if ((m_rgIlNativeMap[iIlNativeMap].ilOffset == NO_MAPPING) ||
3933 (m_rgIlNativeMap[iIlNativeMap].ilOffset == PROLOG) ||
3934 (m_rgIlNativeMap[iIlNativeMap].ilOffset == EPILOG))
3936 rgMapIndexPairs[iMapIndexPairs].m_iIlNativeMap = iIlNativeMap;
3937 rgMapIndexPairs[iMapIndexPairs].m_iSeqPoints = kUnmappedIP;
3940 // If we were remembering a prior unmatched entry in the IL PDB, reset it
3941 iSeqPointLastUnmatched = (ULONG32) -1;
3943 // Advance il-native map, NOT il-source map
3948 // Cases below actually look at the IL PDB sequence point, so ensure it's still
3949 // in range; otherwise, we're done.
3950 if (iSeqPoints >= cSeqPointsReturned)
3953 if (m_rgIlNativeMap[iIlNativeMap].ilOffset < m_rgilOffsets[iSeqPoints])
3955 // Our cursor over the ilnative map is behind the sourceil
3958 if (iSeqPointLastUnmatched != (ULONG32) -1)
3960 // Range matching: This ilnative entry is behind our cursor in the
3961 // sourceil map, but this ilnative entry is also ahead of the previous
3962 // (unmatched) entry in the sourceil map. So this is a case where the JIT
3963 // generated sequence points that surround, without matching, that
3964 // previous entry in the sourceil map. So match to that previous
3965 // (unmatched) entry in the sourceil map.
3966 _ASSERTE(m_rgilOffsets[iSeqPointLastUnmatched] < m_rgIlNativeMap[iIlNativeMap].ilOffset);
3967 rgMapIndexPairs[iMapIndexPairs].m_iIlNativeMap = iIlNativeMap;
3968 rgMapIndexPairs[iMapIndexPairs].m_iSeqPoints = iSeqPointLastUnmatched;
3971 // Reset our memory of the last unmatched entry in the IL PDB
3972 iSeqPointLastUnmatched = (ULONG32) -1;
3974 else if (iMapIndexPairs > 0)
3976 DWORD lastMatchedilNativeIndex = rgMapIndexPairs[iMapIndexPairs - 1].m_iIlNativeMap;
3977 if (m_rgIlNativeMap[iIlNativeMap].ilOffset == m_rgIlNativeMap[lastMatchedilNativeIndex].ilOffset &&
3978 m_rgIlNativeMap[iIlNativeMap].nativeOffset < m_rgIlNativeMap[lastMatchedilNativeIndex].nativeOffset)
3980 rgMapIndexPairs[iMapIndexPairs - 1].m_iIlNativeMap = iIlNativeMap;
3984 // Go to next ilnative map entry
3989 if (m_rgilOffsets[iSeqPoints] < m_rgIlNativeMap[iIlNativeMap].ilOffset)
3991 // Our cursor over the ilnative map is ahead of the sourceil
3992 // map, so go to next sourceil map entry. Remember that we're passing over
3993 // this entry in the sourceil map, in case we choose to match to it later.
3994 if (!fCurSeqPointMatched)
3996 iSeqPointLastUnmatched = iSeqPoints;
3999 fCurSeqPointMatched = FALSE;
4004 _ASSERTE(m_rgilOffsets[iSeqPoints] == m_rgIlNativeMap[iIlNativeMap].ilOffset);
4005 rgMapIndexPairs[iMapIndexPairs].m_iIlNativeMap = iIlNativeMap;
4006 rgMapIndexPairs[iMapIndexPairs].m_iSeqPoints = iSeqPoints;
4008 // If we were remembering a prior unmatched entry in the IL PDB, reset it
4009 iSeqPointLastUnmatched = (ULONG32) -1;
4011 // Advance il-native map, do not advance il-source map in case the next il-native
4012 // entry matches this current il-source map entry, but remember that this current
4013 // il-source map entry has found an exact match
4016 fCurSeqPointMatched = TRUE;
4019 ULONG32 cMapIndexPairs = iMapIndexPairs;
4021 // PDB format requires the lines array to be sorted by IP offset
4022 QuickSortMapIndexPairsByNativeOffset sorterByIp(rgMapIndexPairs, cMapIndexPairs, m_rgIlNativeMap, m_cIlNativeMap);
4026 // Now that the maps are merged and sorted, determine whether there's a hot/cold
4027 // split, where that split is, and then call WriteLinesSubsection to write out each
4028 // region into its own lines-file subsection
4031 // Find the point where the code got split
4032 ULONG32 iMapIndexPairsFirstEntryInColdSection = cMapIndexPairs;
4033 for (iMapIndexPairs = 0; iMapIndexPairs < cMapIndexPairs; iMapIndexPairs++)
4035 DWORD dwNativeOffset = m_rgIlNativeMap[rgMapIndexPairs[iMapIndexPairs].m_iIlNativeMap].nativeOffset;
4036 if (dwNativeOffset >= m_pMethodRegionInfo->hotSize)
4038 iMapIndexPairsFirstEntryInColdSection = iMapIndexPairs;
4043 // Adjust the cold offsets (if any) to be relative to the cold start
4044 for (iMapIndexPairs = iMapIndexPairsFirstEntryInColdSection; iMapIndexPairs < cMapIndexPairs; iMapIndexPairs++)
4046 DWORD dwNativeOffset = m_rgIlNativeMap[rgMapIndexPairs[iMapIndexPairs].m_iIlNativeMap].nativeOffset;
4047 _ASSERTE (dwNativeOffset >= m_pMethodRegionInfo->hotSize);
4049 // Adjust offset so it's relative to the cold region start
4050 dwNativeOffset -= DWORD(m_pMethodRegionInfo->hotSize);
4051 _ASSERTE(dwNativeOffset < m_pMethodRegionInfo->coldSize);
4052 m_rgIlNativeMap[rgMapIndexPairs[iMapIndexPairs].m_iIlNativeMap].nativeOffset = dwNativeOffset;
4055 // Write out the hot region into its own lines-file subsection
4056 hr = WriteDebugSLinesSubsection(
4057 ULONG32(m_pMethodRegionInfo->hotStartAddress - m_addrCodeSection),
4058 ULONG32(m_pMethodRegionInfo->hotSize),
4060 iMapIndexPairsFirstEntryInColdSection);
4064 // If there was a hot/cold split, write a separate lines-file subsection for the cold
4066 if (iMapIndexPairsFirstEntryInColdSection < cMapIndexPairs)
4068 hr = WriteDebugSLinesSubsection(
4069 ULONG32(m_pMethodRegionInfo->coldStartAddress - m_addrCodeSection),
4070 ULONG32(m_pMethodRegionInfo->coldSize),
4071 &rgMapIndexPairs[iMapIndexPairsFirstEntryInColdSection],
4072 cMapIndexPairs - iMapIndexPairsFirstEntryInColdSection);
4080 //---------------------------------------------------------------------------------------
4082 // Manages the writing of all native-IL subsections requred for a given method. Almost do
4083 // the same thing as NGenMethodLinesPdbWriter::WritePDBData. But we will write the native-IL
4087 HRESULT NGenMethodLinesPdbWriter::WriteNativeILMapPDBData()
4089 STANDARD_VM_CONTRACT;
4093 QuickSortILNativeMapByNativeOffset sorterByNativeOffset(m_rgIlNativeMap, m_cIlNativeMap);
4094 sorterByNativeOffset.Sort();
4096 ULONG32 iIlNativeMap = 0;
4097 ULONG32 ilNativeMapFirstEntryInColdeSection = m_cIlNativeMap;
4098 for (iIlNativeMap = 0; iIlNativeMap < m_cIlNativeMap; iIlNativeMap++)
4100 if (m_rgIlNativeMap[iIlNativeMap].nativeOffset >= m_pMethodRegionInfo->hotSize)
4102 ilNativeMapFirstEntryInColdeSection = iIlNativeMap;
4107 NewArrayHolder<ICorDebugInfo::OffsetMapping> coldRgIlNativeMap(new ICorDebugInfo::OffsetMapping[m_cIlNativeMap - ilNativeMapFirstEntryInColdeSection]);
4108 // Adjust the cold offsets (if any) to be relative to the cold start
4109 for (iIlNativeMap = ilNativeMapFirstEntryInColdeSection; iIlNativeMap < m_cIlNativeMap; iIlNativeMap++)
4111 DWORD dwNativeOffset = m_rgIlNativeMap[iIlNativeMap].nativeOffset;
4112 _ASSERTE(dwNativeOffset >= m_pMethodRegionInfo->hotSize);
4114 // Adjust offset so it's relative to the cold region start
4115 dwNativeOffset -= DWORD(m_pMethodRegionInfo->hotSize);
4116 _ASSERTE(dwNativeOffset < m_pMethodRegionInfo->coldSize);
4117 coldRgIlNativeMap[iIlNativeMap - ilNativeMapFirstEntryInColdeSection].ilOffset = m_rgIlNativeMap[iIlNativeMap].ilOffset;
4118 coldRgIlNativeMap[iIlNativeMap - ilNativeMapFirstEntryInColdeSection].nativeOffset = dwNativeOffset;
4119 coldRgIlNativeMap[iIlNativeMap - ilNativeMapFirstEntryInColdeSection].source = m_rgIlNativeMap[iIlNativeMap].source;
4122 // Write out the hot region into its own lines-file subsection
4123 hr = WriteDebugSILLinesSubsection(
4124 ULONG32(m_pMethodRegionInfo->hotStartAddress - m_addrCodeSection),
4125 ULONG32(m_pMethodRegionInfo->hotSize),
4127 ilNativeMapFirstEntryInColdeSection);
4131 // If there was a hot/cold split, write a separate lines-file subsection for the cold
4133 if (ilNativeMapFirstEntryInColdeSection < m_cIlNativeMap)
4135 hr = WriteDebugSILLinesSubsection(
4136 ULONG32(m_pMethodRegionInfo->coldStartAddress - m_addrCodeSection),
4137 ULONG32(m_pMethodRegionInfo->coldSize),
4139 m_cIlNativeMap - ilNativeMapFirstEntryInColdeSection);
4148 //---------------------------------------------------------------------------------------
4150 // Helper called by NGenMethodLinesPdbWriter::WriteDebugSLinesSubsection and
4151 // NGenMethodLinesPdbWriter::WriteDebugSILLinesSubsection to initial the DEBUG_S*_LINE
4152 // subsection headers.
4155 // * ulCodeStartOffset - Offset relative to the code section, or where this region
4157 // * type - the subsection's type
4158 // * lineSize - how many lines mapping the subsection will have.
4159 // * cbCode - Size in bytes of this region of code
4160 // * ppSubSectHeader - output value which returns the intialed CV_DebugSLinesHeader_t struct pointer.
4161 // * ppLinesHeader - output value which returns the initialed CV_DebugSLinesHeader_t struct pointer.
4162 // * ppbLinesSubsectionCur - output value which points to the address right after the DebugSLinesHeader
4165 // * Pointer which points the staring address of the SubSection.
4168 LPBYTE NGenMethodLinesPdbWriter::InitDebugLinesHeaderSection(
4169 DEBUG_S_SUBSECTION_TYPE type,
4170 ULONG32 ulCodeStartOffset,
4173 CV_DebugSSubsectionHeader_t **ppSubSectHeader /*out*/,
4174 CV_DebugSLinesHeader_t ** ppLinesHeader /*out*/,
4175 LPBYTE * ppbLinesSubsectionCur /*out*/)
4177 STANDARD_VM_CONTRACT;
4179 UINT64 cbLinesSubsectionEstimate =
4181 sizeof(CV_DebugSSubsectionHeader_t) +
4182 sizeof(CV_DebugSLinesHeader_t) +
4183 // Worst case: assume each sequence point will require its own
4184 // CV_DebugSLinesFileBlockHeader_t
4185 (lineSize * (sizeof(CV_DebugSLinesFileBlockHeader_t) + sizeof(CV_Line_t)));
4186 if (!FitsIn<ULONG32>(cbLinesSubsectionEstimate))
4191 LPBYTE rgbLinesSubsection = new BYTE[ULONG32(cbLinesSubsectionEstimate)];
4192 LPBYTE pbLinesSubsectionCur = rgbLinesSubsection;
4194 // * (1) DWORD = CV_SIGNATURE_C13 -- the usual subsection signature DWORD
4195 *((DWORD *)pbLinesSubsectionCur) = CV_SIGNATURE_C13;
4196 pbLinesSubsectionCur += sizeof(DWORD);
4198 // * (2) CV_DebugSSubsectionHeader_t
4199 CV_DebugSSubsectionHeader_t * pSubSectHeader = (CV_DebugSSubsectionHeader_t *)pbLinesSubsectionCur;
4200 memset(pSubSectHeader, 0, sizeof(*pSubSectHeader));
4201 pSubSectHeader->type = type;
4202 *ppSubSectHeader = pSubSectHeader;
4203 // pSubSectHeader->cblen to be filled in later once we know the size
4204 pbLinesSubsectionCur += sizeof(*pSubSectHeader);
4206 // * (3) CV_DebugSLinesHeader_t
4207 CV_DebugSLinesHeader_t * pLinesHeader = (CV_DebugSLinesHeader_t *)pbLinesSubsectionCur;
4208 memset(pLinesHeader, 0, sizeof(*pLinesHeader));
4209 pLinesHeader->offCon = ulCodeStartOffset;
4210 pLinesHeader->segCon = m_iCodeSection;
4211 pLinesHeader->flags = 0; // 0 means line info, but not column info, is included
4212 pLinesHeader->cbCon = cbCode;
4213 *ppLinesHeader = pLinesHeader;
4214 pbLinesSubsectionCur += sizeof(*pLinesHeader);
4215 *ppbLinesSubsectionCur = pbLinesSubsectionCur;
4216 return rgbLinesSubsection;
4219 //---------------------------------------------------------------------------------------
4221 // Helper called by NGenMethodLinesPdbWriter::WritePDBData to do the actual PDB writing of a single
4222 // lines-subsection. This is called once for the hot region, and once for the cold
4223 // region, of a given method that has been split. That means you get two
4224 // lines-subsections for split methods.
4227 // * ulCodeStartOffset - Offset relative to the code section, or where this region
4229 // * cbCode - Size in bytes of this region of code
4230 // * rgMapIndexPairs - Array of indices forming the merged data from the JIT
4231 // Manager's IL-to-native map and the IL PDB's IL-to-source map. It is assumed
4232 // that this array has indices sorted such that the native offsets increase
4233 // * cMapIndexPairs - Size in entries of above array.
4236 // rgMapIndexPairs must be sorted in order of nativeOffset, i.e.,
4237 // m_rgIlNativeMap[rgMapIndexPairs[i].m_iIlNativeMap].nativeOffset increases with i.
4240 HRESULT NGenMethodLinesPdbWriter::WriteDebugSLinesSubsection(
4241 ULONG32 ulCodeStartOffset,
4243 MapIndexPair * rgMapIndexPairs,
4244 ULONG32 cMapIndexPairs)
4246 STANDARD_VM_CONTRACT;
4248 // The lines subsection of the PDB (i.e., "DEBUG_S_LINES"), is a blob consisting of a
4249 // few structs stacked one after the other:
4251 // * (1) DWORD = CV_SIGNATURE_C13 -- the usual subsection signature DWORD
4252 // * (2) CV_DebugSSubsectionHeader_t -- the usual subsection header, with type =
4254 // * (3) CV_DebugSLinesHeader_t -- a single header for the entire subsection. Its
4255 // purpose is to specify the native function being described, and to specify the
4256 // size of the variable-sized "blocks" that follow
4257 // * (4) CV_DebugSLinesFileBlockHeader_t -- For each block, you get one of these. A
4258 // block is defined by a set of sequence points that map to the same source
4259 // file. While iterating through the offsets, we need to define new blocks
4260 // whenever the source file changes. In C#, this typically only happens when
4261 // you advance to (or away from) an unmapped IP (0xFeeFee).
4262 // * (5) CV_Line_t (Line array entries) -- For each block, you get several line
4263 // array entries, one entry for the beginning of each sequence point.
4268 CV_DebugSSubsectionHeader_t * pSubSectHeader = NULL;
4269 CV_DebugSLinesHeader_t * pLinesHeader = NULL;
4270 CV_DebugSLinesFileBlockHeader_t * LinesFileBlockHeader = NULL;
4272 // the InitDebugLinesHeaderSection will help us taking care of
4273 // * (1) DWORD = CV_SIGNATURE_C13
4274 // * (2) CV_DebugSSubsectionHeader_t
4275 // * (3) CV_DebugSLinesHeader_t
4276 LPBYTE pbLinesSubsectionCur;
4277 LPBYTE prgbLinesSubsection = InitDebugLinesHeaderSection(
4284 &pbLinesSubsectionCur);
4286 if (pbLinesSubsectionCur == NULL)
4288 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
4291 NewArrayHolder<BYTE> rgbLinesSubsection(prgbLinesSubsection);
4293 // The loop below takes care of
4294 // * (4) CV_DebugSLinesFileBlockHeader_t
4295 // * (5) CV_Line_t (Line array entries)
4297 BOOL fAtLeastOneBlockWritten = FALSE;
4298 CV_DebugSLinesFileBlockHeader_t * pLinesFileBlockHeader = NULL;
4299 CV_Line_t * pLineCur = NULL;
4300 CV_Line_t * pLinePrev = NULL;
4301 CV_Line_t * pLineBlockStart = NULL;
4302 BOOL fBeginNewBlock = TRUE;
4303 ULONG32 iSeqPointsPrev = (ULONG32) -1;
4304 DWORD dwNativeOffsetPrev = (DWORD) -1;
4305 DWORD ilOffsetPrev = (DWORD) -1;
4306 WCHAR wszURLPrev[MAX_LONGPATH];
4307 memset(&wszURLPrev, 0, sizeof(wszURLPrev));
4308 LPBYTE pbEnd = NULL;
4310 for (ULONG32 iMapIndexPairs=0; iMapIndexPairs < cMapIndexPairs; iMapIndexPairs++)
4312 ULONG32 iSeqPoints = rgMapIndexPairs[iMapIndexPairs].m_iSeqPoints;
4313 ULONG32 iIlNativeMap = rgMapIndexPairs[iMapIndexPairs].m_iIlNativeMap;
4315 // Sometimes the JIT manager will give us duplicate IPs in the IL-to-native
4316 // offset mapping. PDB format frowns on that. Since rgMapIndexPairs is being
4317 // iterated in native offset order, it's easy to find these dupes right now, and
4318 // skip all but the first map containing a given IP offset.
4319 if (pLinePrev != NULL && m_rgIlNativeMap[iIlNativeMap].nativeOffset == pLinePrev->offset)
4321 if (ilOffsetPrev == kUnmappedIP)
4323 // if the previous IL offset is kUnmappedIP, then we should rewrite it.
4324 pLineCur = pLinePrev;
4326 else if (iSeqPoints != kUnmappedIP &&
4327 m_rgilOffsets[iSeqPoints] < ilOffsetPrev)
4329 pLineCur = pLinePrev;
4333 // Found a native offset dupe, ignore the current map entry
4338 if ((iSeqPoints != kUnmappedIP) && (iSeqPoints != iSeqPointsPrev))
4340 // This is the first iteration where we're looking at this iSeqPoints. So
4341 // check whether the document name has changed on us. If it has, that means
4342 // we need to start a new block.
4343 WCHAR wszURL[MAX_LONGPATH];
4345 hr = m_rgpDocs[iSeqPoints]->GetURL(_countof(wszURL), &cchURL, wszURL);
4348 // Skip function if IL PDB has data missing
4352 // wszURL is the best we have for a unique identifier of documents. See
4353 // whether the previous document's URL is different
4354 if (_wcsicmp(wszURL, wszURLPrev) != 0)
4356 // New document. Update wszURLPrev, and remember that we need to start a
4358 if (wcscpy_s(wszURLPrev, _countof(wszURLPrev), wszURL) != 0)
4362 fBeginNewBlock = TRUE;
4365 iSeqPointsPrev = iSeqPoints;
4369 // We've determined that we need to start a new block. So perform fixups
4370 // against the previous block (if any) first
4371 if (FinalizeLinesFileBlock(pLinesFileBlockHeader, pLineBlockStart, pLineCur))
4373 fAtLeastOneBlockWritten = TRUE;
4375 else if (pLinesFileBlockHeader != NULL)
4377 // Previous block had no usable data. So rewind back to the previous
4378 // block header, and we'll start there with the next block
4379 pbLinesSubsectionCur = LPBYTE(pLinesFileBlockHeader);
4380 pLineCur = (CV_Line_t *) pbLinesSubsectionCur;
4383 // Now get the info we'll need for the next block
4384 char szURL[MAX_LONGPATH];
4385 int cbWritten = WideCharToMultiByte(
4389 -1, // i.e., input is NULL-terminated
4390 szURL, // output: UTF8 string starts here
4391 _countof(szURL), // Available space
4392 NULL, // lpDefaultChar
4393 NULL // lpUsedDefaultChar
4398 DocNameOffsets docNameOffsets;
4399 BOOL fExists = m_pDocNameToOffsetMap->Lookup(szURL, &docNameOffsets);
4402 _ASSERTE(docNameOffsets.m_dwChksumTableOffset != (ULONG32) -1);
4406 // We may get back an invalid document in the 0xFeeFee case (i.e., a
4407 // sequence point that intentionally doesn't map back to a publicly
4408 // available source code line). In that case, we'll use the bogus cksum
4409 // offset of -1 for now, and verify we're in the 0xFeeFee case later on
4410 // (see code:NGenMethodLinesPdbWriter::FinalizeLinesFileBlock).
4411 _ASSERTE(szURL[0] == '\0');
4412 _ASSERTE(docNameOffsets.m_dwChksumTableOffset == (ULONG32) -1);
4416 // * (4) CV_DebugSLinesFileBlockHeader_t
4417 if (pLineCur == NULL)
4419 // First lines file block, so begin the block header immediately after the
4420 // subsection headers
4421 pLinesFileBlockHeader = (CV_DebugSLinesFileBlockHeader_t *) pbLinesSubsectionCur;
4425 // We've had blocks before this one, so add this block at our current
4426 // location in the blob
4427 pLinesFileBlockHeader = (CV_DebugSLinesFileBlockHeader_t *) pLineCur;
4430 // PDB structure sizes guarantee this is the case, though their docs are
4431 // explicit that each lines-file block header must be 4-byte aligned.
4432 _ASSERTE(IS_ALIGNED(pLinesFileBlockHeader, 4));
4434 memset(pLinesFileBlockHeader, 0, sizeof(*pLinesFileBlockHeader));
4435 pLinesFileBlockHeader->offFile = docNameOffsets.m_dwChksumTableOffset;
4436 // pLinesFileBlockHeader->nLines to be filled in when block is complete
4437 // pLinesFileBlockHeader->cbBlock to be filled in when block is complete
4439 pLineCur = (CV_Line_t *) (pLinesFileBlockHeader + 1);
4440 pLineBlockStart = pLineCur;
4441 fBeginNewBlock = FALSE;
4445 pLineCur->offset = m_rgIlNativeMap[iIlNativeMap].nativeOffset;
4446 pLineCur->linenumStart =
4447 (iSeqPoints == kUnmappedIP) ?
4449 m_rgnLineStarts[iSeqPoints];
4450 pLineCur->deltaLineEnd = 0;
4451 pLineCur->fStatement = 1;
4452 ilOffsetPrev = (iSeqPoints == kUnmappedIP) ? kUnmappedIP : m_rgilOffsets[iSeqPoints];
4453 pLinePrev = pLineCur;
4455 } // for (ULONG32 iMapIndexPairs=0; iMapIndexPairs < cMapIndexPairs; iMapIndexPairs++)
4457 if (pLineCur == NULL)
4459 // There were no lines data for this function, so don't write anything
4463 // Perform fixups against the last block we wrote
4464 if (FinalizeLinesFileBlock(pLinesFileBlockHeader, pLineBlockStart, pLineCur))
4465 fAtLeastOneBlockWritten = TRUE;
4467 if (!fAtLeastOneBlockWritten)
4469 // There were no valid blocks to write for this function, so don't bother
4470 // calling PDB writing API. No problem.
4474 // Now that we know pSubSectHeader->cbLen, fill it in
4475 pSubSectHeader->cbLen = CV_off32_t(LPBYTE(pLineCur) - LPBYTE(pLinesHeader));
4477 // Subsection is now filled out, so add it.
4478 hr = m_pWriter->ModAddSymbols(
4482 // The size we pass here is the size of the entire byte array that we pass in.
4483 int(LPBYTE(pLineCur) - rgbLinesSubsection));
4491 //---------------------------------------------------------------------------------------
4493 // Helper called by NGenMethodLinesPdbWriter::WriteNativeILMapPDBData to do the actual PDB writing of a single
4494 // lines-subsection. This is called once for the hot region, and once for the cold
4495 // region, of a given method that has been split. That means you get two
4496 // lines-subsections for split methods.
4499 // * ulCodeStartOffset - Offset relative to the code section, or where this region
4501 // * cbCode - Size in bytes of this region of code
4502 // * rgIlNativeMap - IL to Native map array.
4503 // * rgILNativeMapAdjustSize - the number of elements we need to read in rgILNativeMap.
4506 HRESULT NGenMethodLinesPdbWriter::WriteDebugSILLinesSubsection(
4507 ULONG32 ulCodeStartOffset,
4509 ICorDebugInfo::OffsetMapping * rgIlNativeMap,
4510 ULONG32 rgILNativeMapAdjustSize)
4512 STANDARD_VM_CONTRACT;
4514 // The lines subsection of the PDB (i.e., "DEBUG_S_IL_LINES"), is a blob consisting of a
4515 // few structs stacked one after the other:
4517 // * (1) DWORD = CV_SIGNATURE_C13 -- the usual subsection signature DWORD
4518 // * (2) CV_DebugSSubsectionHeader_t -- the usual subsection header, with type =
4520 // * (3) CV_DebugSLinesHeader_t -- a single header for the entire subsection. Its
4521 // purpose is to specify the native function being described, and to specify the
4522 // size of the variable-sized "blocks" that follow
4523 // * (4) CV_DebugSLinesFileBlockHeader_t -- For each block, you get one of these. A
4524 // block is defined by a set of sequence points that map to the same source
4525 // file. While iterating through the offsets, we need to define new blocks
4526 // whenever the source file changes. In C#, this typically only happens when
4527 // you advance to (or away from) an unmapped IP (0xFeeFee).
4528 // * (5) CV_Line_t (Line array entries) -- For each block, you get several line
4529 // array entries, one entry for the beginning of each sequence point.
4533 CV_DebugSSubsectionHeader_t * pSubSectHeader = NULL;
4534 CV_DebugSLinesHeader_t * pLinesHeader = NULL;
4535 CV_DebugSLinesFileBlockHeader_t * pLinesFileBlockHeader = NULL;
4537 // the InitDebugLinesHeaderSection will help us taking care of
4538 // * (1) DWORD = CV_SIGNATURE_C13
4539 // * (2) CV_DebugSSubsectionHeader_t
4540 // * (3) CV_DebugSLinesHeader_t
4541 LPBYTE pbLinesSubsectionCur;
4542 LPBYTE prgbLinesSubsection = InitDebugLinesHeaderSection(
4546 rgILNativeMapAdjustSize,
4549 &pbLinesSubsectionCur);
4551 if (prgbLinesSubsection == NULL)
4553 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
4556 NewArrayHolder<BYTE> rgbLinesSubsection(prgbLinesSubsection);
4558 // The loop below takes care of
4559 // * (4) CV_DebugSLinesFileBlockHeader_t
4560 // * (5) CV_Line_t (Line array entries)
4562 CV_Line_t * pLineCur = NULL;
4563 CV_Line_t * pLineBlockStart = NULL;
4564 BOOL fBeginNewBlock = TRUE;
4565 LPBYTE pbEnd = NULL;
4567 pLinesFileBlockHeader = (CV_DebugSLinesFileBlockHeader_t *)pbLinesSubsectionCur;
4568 // PDB structure sizes guarantee this is the case, though their docs are
4569 // explicit that each lines-file block header must be 4-byte aligned.
4570 _ASSERTE(IS_ALIGNED(pLinesFileBlockHeader, 4));
4572 memset(pLinesFileBlockHeader, 0, sizeof(*pLinesFileBlockHeader));
4573 char szURL[MAX_PATH];
4574 int cbWritten = WideCharToMultiByte(
4577 UNKNOWN_SOURCE_FILE_PATH,
4578 -1, // i.e., input is NULL-terminated
4579 szURL, // output: UTF8 string starts here
4580 _countof(szURL), // Available space
4581 NULL, // lpDefaultChar
4582 NULL // lpUsedDefaultChar
4584 _ASSERTE(cbWritten > 0);
4585 DocNameOffsets docNameOffsets;
4586 m_pDocNameToOffsetMap->Lookup(szURL, &docNameOffsets);
4587 pLinesFileBlockHeader->offFile = docNameOffsets.m_dwChksumTableOffset;
4588 // pLinesFileBlockHeader->nLines to be filled in when block is complete
4589 // pLinesFileBlockHeader->cbBlock to be filled in when block is complete
4591 pLineCur = (CV_Line_t *)(pLinesFileBlockHeader + 1);
4592 pLineBlockStart = pLineCur;
4593 CV_Line_t * pLinePrev = NULL;
4595 for (ULONG32 iINativeMap = 0;iINativeMap < rgILNativeMapAdjustSize; iINativeMap++)
4597 if ((rgIlNativeMap[iINativeMap].ilOffset == NO_MAPPING) ||
4598 (rgIlNativeMap[iINativeMap].ilOffset == PROLOG) ||
4599 (rgIlNativeMap[iINativeMap].ilOffset == EPILOG))
4601 rgIlNativeMap[iINativeMap].ilOffset = kUnmappedIP;
4604 // Sometimes the JIT manager will give us duplicate native offset in the IL-to-native
4605 // offset mapping. PDB format frowns on that. Since rgMapIndexPairs is being
4606 // iterated in native offset order, it's easy to find these dupes right now, and
4607 // skip all but the first map containing a given IP offset.
4608 if (pLinePrev != NULL &&
4609 rgIlNativeMap[iINativeMap].nativeOffset == pLinePrev->offset)
4611 if (pLinePrev->linenumStart == kUnmappedIP)
4613 // if the previous IL offset is kUnmappedIP, then we should rewrite it.
4614 pLineCur = pLinePrev;
4616 else if (rgIlNativeMap[iINativeMap].ilOffset != kUnmappedIP &&
4617 rgIlNativeMap[iINativeMap].ilOffset < pLinePrev->linenumStart)
4619 pLineCur = pLinePrev;
4623 // Found a native offset dupe, ignore the current map entry
4628 pLineCur->linenumStart = rgIlNativeMap[iINativeMap].ilOffset;
4630 pLineCur->offset = rgIlNativeMap[iINativeMap].nativeOffset;
4631 pLineCur->fStatement = 1;
4632 pLineCur->deltaLineEnd = 0;
4633 pLinePrev = pLineCur;
4637 if (pLineCur == NULL)
4639 // There were no lines data for this function, so don't write anything
4643 if (!FinalizeLinesFileBlock(pLinesFileBlockHeader, pLineBlockStart, pLineCur
4652 // Now that we know pSubSectHeader->cbLen, fill it in
4653 pSubSectHeader->cbLen = CV_off32_t(LPBYTE(pLineCur) - LPBYTE(pLinesHeader));
4655 // Subsection is now filled out, so add it.
4656 hr = m_pWriter->ModAddSymbols(
4660 // The size we pass here is the size of the entire byte array that we pass in.
4661 long(LPBYTE(pLineCur) - rgbLinesSubsection));
4669 //---------------------------------------------------------------------------------------
4671 // Performs final fixups on the last lines-file block we completed, specifically writing
4672 // in the size of the block, now that it's known. Also responsible for determining
4673 // whether there is even any data to write in the first place.
4676 // * pLinesFileBlockHeader - lines-file block header to write to
4677 // * pLineBlockStart - First CV_Line_t * of this block
4678 // * pLineBlockAfterEnd - Last CV_Line_t * of this block plus 1
4681 // * TRUE: lines-file block was nonempty, and is now finalized
4682 // * FALSE: lines-file block was empty, and caller should toss it out.
4685 BOOL NGenMethodLinesPdbWriter::FinalizeLinesFileBlock(
4686 CV_DebugSLinesFileBlockHeader_t * pLinesFileBlockHeader,
4687 CV_Line_t * pLineBlockStart,
4688 CV_Line_t * pLineBlockAfterEnd
4690 , BOOL ignorekUnmappedIPCheck
4694 LIMITED_METHOD_CONTRACT;
4696 if (pLinesFileBlockHeader == NULL)
4698 // If a given function has no sequence points at all, pLinesFileBlockHeader can
4699 // be NULL. No problem
4703 if (pLineBlockStart == pLineBlockAfterEnd)
4705 // If we start a lines file block and then realize that there are no entries
4706 // (i.e., no valid sequence points to map), then we end up with an empty block.
4707 // No problem, just skip the block.
4711 _ASSERTE(pLineBlockStart != NULL);
4712 _ASSERTE(pLineBlockAfterEnd != NULL);
4713 _ASSERTE(pLineBlockAfterEnd > pLineBlockStart);
4715 if (pLinesFileBlockHeader->offFile == (ULONG32) -1)
4717 // The file offset we set for this block is invalid. This should be due to the
4718 // 0xFeeFee case (i.e., sequence points that intentionally don't map back to a
4719 // publicly available source code line). Fix up the offset to be valid (point it
4720 // at the first file), but the offset will generally be ignored by the PDB
4724 if (!ignorekUnmappedIPCheck)
4726 for (CV_Line_t * pLineCur = pLineBlockStart; pLineCur < pLineBlockAfterEnd; pLineCur++)
4728 _ASSERTE(pLineCur->linenumStart == kUnmappedIP);
4733 pLinesFileBlockHeader->offFile = 0;
4736 // Now that we know the size of the block, finish filling out the lines file block
4738 pLinesFileBlockHeader->nLines = CV_off32_t(pLineBlockAfterEnd - pLineBlockStart);
4739 pLinesFileBlockHeader->cbBlock = pLinesFileBlockHeader->nLines * sizeof(CV_Line_t);
4743 #endif // NO_NGENPDB
4744 #if defined(FEATURE_PERFMAP) || !defined(NO_NGENPDB)
4745 HRESULT __stdcall CreatePdb(CORINFO_ASSEMBLY_HANDLE hAssembly, BSTR pNativeImagePath, BSTR pPdbPath, BOOL pdbLines, BSTR pManagedPdbSearchPath, LPCWSTR pDiasymreaderPath)
4747 STANDARD_VM_CONTRACT;
4749 Assembly *pAssembly = reinterpret_cast<Assembly *>(hAssembly);
4750 _ASSERTE(pAssembly);
4751 _ASSERTE(pNativeImagePath);
4754 #if !defined(NO_NGENPDB)
4755 NGenPdbWriter pdbWriter(
4758 pdbLines ? kPDBLines : 0,
4759 pManagedPdbSearchPath);
4760 IfFailThrow(pdbWriter.Load(pDiasymreaderPath));
4761 #elif defined(FEATURE_PERFMAP)
4762 NativeImagePerfMap perfMap(pAssembly, pPdbPath);
4765 ModuleIterator moduleIterator = pAssembly->IterateModules();
4766 Module *pModule = NULL;
4767 BOOL fAtLeastOneNativeModuleFound = FALSE;
4769 while (moduleIterator.Next())
4771 pModule = moduleIterator.GetModule();
4773 if (pModule->HasNativeImage() || pModule->IsReadyToRun())
4775 #if !defined(NO_NGENPDB)
4776 IfFailThrow(pdbWriter.WritePDBDataForModule(pModule));
4777 #elif defined(FEATURE_PERFMAP)
4778 perfMap.LogDataForModule(pModule);
4780 fAtLeastOneNativeModuleFound = TRUE;
4784 if (!fAtLeastOneNativeModuleFound)
4786 GetSvcLogger()->Printf(
4787 W("Loaded image '%s' (for input file '%s') is not a native image.\n"),
4788 pAssembly->GetManifestFile()->GetPath().GetUnicode(),
4790 return CORDBG_E_NO_IMAGE_AVAILABLE;
4793 GetSvcLogger()->Printf(
4794 #if !defined(NO_NGENPDB)
4795 W("Successfully generated PDB for native assembly '%s'.\n"),
4796 #elif defined(FEATURE_PERFMAP)
4797 W("Successfully generated perfmap for native assembly '%s'.\n"),
4804 HRESULT __stdcall CreatePdb(CORINFO_ASSEMBLY_HANDLE hAssembly, BSTR pNativeImagePath, BSTR pPdbPath, BOOL pdbLines, BSTR pManagedPdbSearchPath, LPCWSTR pDiasymreaderPath)
4808 #endif // defined(FEATURE_PERFMAP) || !defined(NO_NGENPDB)
4810 // End of PDB writing code
4811 // ----------------------------------------------------------------------------
4814 BOOL CEEPreloader::CanPrerestoreEmbedClassHandle(CORINFO_CLASS_HANDLE handle)
4816 STANDARD_VM_CONTRACT;
4818 if (IsReadyToRunCompilation())
4821 TypeHandle th(handle);
4823 return m_image->CanPrerestoreEagerBindToTypeHandle(th, NULL);
4826 BOOL CEEPreloader::CanPrerestoreEmbedMethodHandle(CORINFO_METHOD_HANDLE handle)
4828 STANDARD_VM_CONTRACT;
4830 if (IsReadyToRunCompilation())
4833 MethodDesc *pMD = (MethodDesc*) handle;
4835 return m_image->CanPrerestoreEagerBindToMethodDesc(pMD, NULL);
4838 ICorCompilePreloader * CEECompileInfo::PreloadModule(CORINFO_MODULE_HANDLE module,
4839 ICorCompileDataStore *pData,
4840 CorProfileData *profileData)
4842 STANDARD_VM_CONTRACT;
4844 NewHolder<CEEPreloader> pPreloader(new CEEPreloader((Module *) module, pData));
4846 COOPERATIVE_TRANSITION_BEGIN();
4848 if (PartialNGenStressPercentage() == 0)
4850 pPreloader->Preload(profileData);
4853 COOPERATIVE_TRANSITION_END();
4855 return pPreloader.Extract();
4858 void CEECompileInfo::SetAssemblyHardBindList(
4859 __in_ecount( cHardBindList )
4860 LPWSTR *pHardBindList,
4861 DWORD cHardBindList)
4863 STANDARD_VM_CONTRACT;
4867 HRESULT CEECompileInfo::SetVerboseLevel(
4868 IN VerboseLevel level)
4870 LIMITED_METHOD_CONTRACT;
4872 g_CorCompileVerboseLevel = level;
4879 CEEPreloader::CEEPreloader(Module *pModule,
4880 ICorCompileDataStore *pData)
4883 m_image = new DataImage(pModule, this);
4885 CONSISTENCY_CHECK(pModule == GetAppDomain()->ToCompilationDomain()->GetTargetModule());
4887 GetAppDomain()->ToCompilationDomain()->SetTargetImage(m_image, this);
4889 m_methodCompileLimit = pModule->GetMDImport()->GetCountWithTokenKind(mdtMethodDef) * 10;
4891 #ifdef FEATURE_FULL_NGEN
4892 m_fSpeculativeTriage = FALSE;
4893 m_fDictionariesPopulated = FALSE;
4897 CEEPreloader::~CEEPreloader()
4899 WRAPPER_NO_CONTRACT;
4903 void CEEPreloader::Preload(CorProfileData * profileData)
4905 STANDARD_VM_CONTRACT;
4907 bool doNothingNgen = false;
4909 static ConfigDWORD fDoNothingNGen;
4910 doNothingNgen = !!fDoNothingNGen.val(CLRConfig::INTERNAL_ZapDoNothing);
4915 m_image->GetModule()->SetProfileData(profileData);
4916 m_image->GetModule()->ExpandAll(m_image);
4919 // Triage all items created by initial expansion.
4920 // We will try to accept all items created by initial expansion.
4925 // ICorCompilerPreloader
4928 DWORD CEEPreloader::MapMethodEntryPoint(CORINFO_METHOD_HANDLE handle)
4930 STANDARD_VM_CONTRACT;
4932 MethodDesc *pMD = GetMethod(handle);
4933 Precode * pPrecode = pMD->GetSavedPrecode(m_image);
4935 return m_image->GetRVA(pPrecode);
4938 DWORD CEEPreloader::MapClassHandle(CORINFO_CLASS_HANDLE handle)
4940 STANDARD_VM_CONTRACT;
4942 TypeHandle th = TypeHandle::FromPtr(handle);
4943 if (th.IsTypeDesc())
4944 return m_image->GetRVA(th.AsTypeDesc()) | 2;
4946 return m_image->GetRVA(th.AsMethodTable());
4949 DWORD CEEPreloader::MapMethodHandle(CORINFO_METHOD_HANDLE handle)
4951 STANDARD_VM_CONTRACT;
4953 return m_image->GetRVA(handle);
4956 DWORD CEEPreloader::MapFieldHandle(CORINFO_FIELD_HANDLE handle)
4958 STANDARD_VM_CONTRACT;
4960 return m_image->GetRVA(handle);
4963 DWORD CEEPreloader::MapAddressOfPInvokeFixup(CORINFO_METHOD_HANDLE handle)
4965 STANDARD_VM_CONTRACT;
4967 MethodDesc *pMD = GetMethod(handle);
4969 _ASSERTE(pMD->IsNDirect());
4970 NDirectWriteableData * pMDWriteableData = ((NDirectMethodDesc *)pMD)->GetWriteableData();
4972 return m_image->GetRVA(pMDWriteableData) + offsetof(NDirectWriteableData, m_pNDirectTarget);
4975 DWORD CEEPreloader::MapGenericHandle(CORINFO_GENERIC_HANDLE handle)
4977 STANDARD_VM_CONTRACT;
4979 return m_image->GetRVA(handle);
4982 DWORD CEEPreloader::MapModuleIDHandle(CORINFO_MODULE_HANDLE handle)
4984 STANDARD_VM_CONTRACT;
4986 return m_image->GetRVA(handle) + (DWORD)Module::GetOffsetOfModuleID();
4989 CORINFO_METHOD_HANDLE CEEPreloader::NextUncompiledMethod()
4991 STANDARD_VM_CONTRACT;
4993 // If we have run out of methods to compile, ensure that we have code for all methods
4994 // that we are about to save.
4995 if (m_uncompiledMethods.GetCount() == 0)
4997 #ifdef FEATURE_FULL_NGEN
4998 if (!m_fSpeculativeTriage)
5000 // We take one shot at smarter elimination of speculative instantiations
5001 // that are guaranteed to be found in other modules
5002 TriageSpeculativeInstantiations();
5003 m_fSpeculativeTriage = TRUE;
5007 if (m_uncompiledMethods.GetCount() == 0)
5009 #ifdef FEATURE_FULL_NGEN
5010 if (!m_fDictionariesPopulated)
5012 // Prepopulate dictionaries. Only the first population is done in expansive way.
5013 m_image->GetModule()->PrepopulateDictionaries(m_image, FALSE);
5014 m_fDictionariesPopulated = TRUE;
5019 // The subsequent populations are done in non-expansive way (won't load new types)
5020 m_image->GetModule()->PrepopulateDictionaries(m_image, TRUE);
5023 // Make sure that we have generated code for all instantiations that we are going to save
5024 // The new items that we encounter here were most likely side effects of verification or failed inlining,
5025 // so do not try to save them eagerly.
5026 while (TriageForZap(FALSE)) {
5027 // Loop as long as new types are added
5032 // Take next uncompiled method
5033 COUNT_T count = m_uncompiledMethods.GetCount();
5037 MethodDesc * pMD = m_uncompiledMethods[count - 1];
5038 m_uncompiledMethods.SetCount(count - 1);
5041 if (LoggingOn(LF_ZAP, LL_INFO10000))
5043 StackSString methodString;
5044 TypeString::AppendMethodDebug(methodString, pMD);
5046 LOG((LF_ZAP, LL_INFO10000, "CEEPreloader::NextUncompiledMethod: %S\n", methodString.GetUnicode()));
5050 return (CORINFO_METHOD_HANDLE) pMD;
5053 void CEEPreloader::AddMethodToTransitiveClosureOfInstantiations(CORINFO_METHOD_HANDLE handle)
5055 STANDARD_VM_CONTRACT;
5057 TriageMethodForZap(GetMethod(handle), TRUE);
5060 BOOL CEEPreloader::IsMethodInTransitiveClosureOfInstantiations(CORINFO_METHOD_HANDLE handle)
5062 STANDARD_VM_CONTRACT;
5064 MethodDesc *pMD = GetMethod(handle);
5066 return (m_acceptedMethods.Lookup(pMD) != NULL) && (m_rejectedMethods.Lookup(pMD) == NULL);
5069 BOOL CEEPreloader::IsTypeInTransitiveClosureOfInstantiations(CORINFO_CLASS_HANDLE handle)
5071 STANDARD_VM_CONTRACT;
5073 TypeHandle th = (TypeHandle) handle;
5075 return (m_acceptedTypes.Lookup(th) != NULL) && (m_rejectedTypes.Lookup(th) == NULL);
5078 void CEEPreloader::MethodReferencedByCompiledCode(CORINFO_METHOD_HANDLE handle)
5080 STANDARD_VM_CONTRACT;
5082 #ifndef FEATURE_FULL_NGEN // Unreferenced methods
5084 // Keep track of methods that are actually referenced by the code. We use this information
5085 // to avoid generating code for unreferenced methods not visible outside the assembly.
5086 // These methods are very unlikely to be ever used at runtime because of they only ever be
5087 // called via private reflection.
5089 MethodDesc *pMD = GetMethod(handle);
5091 const CompileMethodEntry * pEntry = m_compileMethodsHash.LookupPtr(pMD);
5094 if (pEntry->fReferenced)
5096 const_cast<CompileMethodEntry *>(pEntry)->fReferenced = true;
5098 if (pEntry->fScheduled)
5100 AppendUncompiledMethod(pMD);
5104 CompileMethodEntry entry;
5106 entry.fReferenced = true;
5107 entry.fScheduled = false;
5108 m_compileMethodsHash.Add(entry);
5111 if (pMD->IsWrapperStub())
5112 MethodReferencedByCompiledCode((CORINFO_METHOD_HANDLE)pMD->GetWrappedMethodDesc());
5113 #endif // FEATURE_FULL_NGEN
5116 BOOL CEEPreloader::IsUncompiledMethod(CORINFO_METHOD_HANDLE handle)
5118 STANDARD_VM_CONTRACT;
5120 MethodDesc *pMD = GetMethod(handle);
5122 #ifndef FEATURE_FULL_NGEN // Unreferenced methods
5123 const CompileMethodEntry * pEntry = m_compileMethodsHash.LookupPtr(pMD);
5124 return (pEntry != NULL) && (pEntry->fScheduled || !pEntry->fReferenced);
5126 return m_compileMethodsHash.LookupPtr(pMD) != NULL;
5130 static bool IsTypeAccessibleOutsideItsAssembly(TypeHandle th)
5132 STANDARD_VM_CONTRACT;
5134 if (th.IsTypeDesc())
5136 if (th.AsTypeDesc()->HasTypeParam())
5137 return IsTypeAccessibleOutsideItsAssembly(th.AsTypeDesc()->GetTypeParam());
5142 MethodTable * pMT = th.AsMethodTable();
5144 if (pMT == g_pCanonMethodTableClass)
5147 switch (pMT->GetClass()->GetProtection())
5151 case tdNestedPublic:
5152 case tdNestedFamily:
5153 case tdNestedFamORAssem:
5155 MethodTable * pMTEnclosing = pMT->LoadEnclosingMethodTable();
5156 if (pMTEnclosing == NULL)
5158 if (!IsTypeAccessibleOutsideItsAssembly(pMTEnclosing))
5167 if (pMT->HasInstantiation())
5169 Instantiation instantiation = pMT->GetInstantiation();
5170 for (DWORD i = 0; i < instantiation.GetNumArgs(); i++)
5172 if (!IsTypeAccessibleOutsideItsAssembly(instantiation[i]))
5180 static bool IsMethodAccessibleOutsideItsAssembly(MethodDesc * pMD)
5182 STANDARD_VM_CONTRACT;
5184 // Note that this ignores unrestricted friend access. This friend access allowed attribute can be used to
5185 // prevent methods from getting trimmed if necessary.
5186 if (pMD->GetMDImport()->GetCustomAttributeByName(pMD->GetMemberDef(), FRIEND_ACCESS_ALLOWED_ATTRIBUTE_TYPE, NULL, NULL) == S_OK)
5189 switch (pMD->GetAttrs() & mdMemberAccessMask)
5200 if (!IsTypeAccessibleOutsideItsAssembly(pMD->GetMethodTable()))
5203 if (pMD->HasMethodInstantiation())
5205 Instantiation instantiation = pMD->GetMethodInstantiation();
5206 for (DWORD i = 0; i < instantiation.GetNumArgs(); i++)
5208 if (!IsTypeAccessibleOutsideItsAssembly(instantiation[i]))
5216 static bool IsMethodCallableOutsideItsAssembly(MethodDesc * pMD)
5218 STANDARD_VM_CONTRACT;
5220 // Virtual methods can be called via interfaces, etc. We would need to do
5221 // more analysis to trim them. For now, assume that they can be referenced outside this assembly.
5222 if (pMD->IsVirtual())
5225 // Class constructors are often used with reflection. Always generate code for them.
5226 if (pMD->IsClassConstructorOrCtor())
5229 if (IsMethodAccessibleOutsideItsAssembly(pMD))
5235 BOOL IsGenericTooDeeplyNested(TypeHandle t);
5236 void CEEPreloader::AddToUncompiledMethods(MethodDesc *pMD, BOOL fForStubs)
5238 STANDARD_VM_CONTRACT;
5240 // TriageTypeForZap() and TriageMethodForZap() should ensure this.
5241 _ASSERTE(m_image->GetModule() == pMD->GetLoaderModule());
5248 if (!pMD->MayHaveNativeCode() && !pMD->IsWrapperStub())
5252 // If it's already been compiled, don't add it to the set of uncompiled methods
5253 if (m_image->GetCodeAddress(pMD) != NULL)
5256 // If it's already in the queue to be compiled don't add it again
5257 const CompileMethodEntry * pEntry = m_compileMethodsHash.LookupPtr(pMD);
5259 #ifndef FEATURE_FULL_NGEN // Unreferenced methods
5262 if (pEntry->fScheduled)
5265 if (!pEntry->fReferenced)
5268 const_cast<CompileMethodEntry *>(pEntry)->fScheduled = true;
5272 // The unreferenced methods optimization works for generic methods and methods on generic types only.
5273 // Non-generic methods take different path.
5275 // It unclear whether it is worth it to enable it for non-generic methods too. The benefit
5276 // for non-generic methods is small, and the non-generic methods are more likely to be called
5277 // via private reflection.
5279 bool fSchedule = fForStubs || IsMethodCallableOutsideItsAssembly(pMD);
5281 CompileMethodEntry entry;
5283 entry.fScheduled = fSchedule;
5284 entry.fReferenced = false;
5285 m_compileMethodsHash.Add(entry);
5290 #else // // FEATURE_FULL_NGEN
5291 // Schedule the method for compilation
5294 CompileMethodEntry entry;
5296 m_compileMethodsHash.Add(entry);
5297 #endif // FEATURE_FULL_NGEN
5299 if (pMD->HasMethodInstantiation())
5301 Instantiation instantiation = pMD->GetMethodInstantiation();
5302 for (DWORD i = 0; i < instantiation.GetNumArgs(); i++)
5304 if (IsGenericTooDeeplyNested(instantiation[i]))
5309 // Add it to the set of uncompiled methods
5310 AppendUncompiledMethod(pMD);
5314 // Used to validate instantiations produced by the production rules before we actually try to instantiate them.
5316 static BOOL CanSatisfyConstraints(Instantiation typicalInst, Instantiation candidateInst)
5318 STANDARD_VM_CONTRACT;
5320 // The dependency must be of the form C<T> --> D<T>
5321 _ASSERTE(typicalInst.GetNumArgs() == candidateInst.GetNumArgs());
5322 if (typicalInst.GetNumArgs() != candidateInst.GetNumArgs())
5325 SigTypeContext typeContext(candidateInst, Instantiation());
5327 for (DWORD i = 0; i < candidateInst.GetNumArgs(); i++)
5329 TypeHandle thArg = candidateInst[i];
5331 // If this is "__Canon" and we are code sharing then we can't rule out that some
5332 // compatible instantiation may meet the constraints
5333 if (thArg == TypeHandle(g_pCanonMethodTableClass))
5336 // Otherwise we approximate, and just assume that we have "parametric" constraints
5337 // of the form "T : IComparable<T>" rather than "odd" constraints such as "T : IComparable<string>".
5338 // That is, we assume checking the constraint at the canonical type is sufficient
5339 // to tell us if the constraint holds for all compatible types.
5341 // For example of where this does not hold, consider if
5343 // class D<T> where T : IComparable<T>
5344 // struct Struct<T> : IComparable<string>
5345 // Assume we generate C<Struct<object>>. Now the constraint
5346 // Struct<object> : IComparable<object>
5347 // does not hold, so we do not generate the instantiation, even though strictly speaking
5348 // the compatible instantiation C<Struct<string>> will satisfy the constraint
5349 // Struct<string> : IComparable<string>
5351 TypeVarTypeDesc* tyvar = typicalInst[i].AsGenericVariable();
5353 tyvar->LoadConstraints();
5355 if (!tyvar->SatisfiesConstraints(&typeContext,thArg)) {
5358 // In case we want to know which illegal instantiations we ngen'ed
5359 StackSString candidateInstName;
5360 StackScratchBuffer buffer;
5361 thArg.GetName(candidateInstName);
5363 _snprintf_s(output, _countof(output), _TRUNCATE, "Generics TypeDependencyAttribute processing: Couldn't satisfy a constraint. Class with Attribute: %s Bad candidate instantiated type: %s\r\n", pMT->GetDebugClassName(), candidateInstName.GetANSI(buffer));
5364 OutputDebugStringA(output);
5376 // This method has duplicated logic from bcl\system\collections\generic\comparer.cs
5378 static void SpecializeComparer(SString& ss, Instantiation& inst)
5380 STANDARD_VM_CONTRACT;
5382 if (inst.GetNumArgs() != 1) {
5383 _ASSERTE(!"Improper use of a TypeDependencyAttribute for Comparer");
5387 TypeHandle elemTypeHnd = inst[0];
5390 // Override the default ObjectComparer for special cases
5392 if (elemTypeHnd.CanCastTo(
5393 TypeHandle(MscorlibBinder::GetClass(CLASS__ICOMPARABLEGENERIC)).Instantiate(Instantiation(&elemTypeHnd, 1))))
5395 ss.Set(W("System.Collections.Generic.GenericComparer`1"));
5399 if (Nullable::IsNullableType(elemTypeHnd))
5401 Instantiation nullableInst = elemTypeHnd.AsMethodTable()->GetInstantiation();
5402 if (nullableInst[0].CanCastTo(
5403 TypeHandle(MscorlibBinder::GetClass(CLASS__ICOMPARABLEGENERIC)).Instantiate(nullableInst)))
5405 ss.Set(W("System.Collections.Generic.NullableComparer`1"));
5406 inst = nullableInst;
5411 if (elemTypeHnd.IsEnum())
5413 CorElementType et = elemTypeHnd.GetVerifierCorElementType();
5414 if (et == ELEMENT_TYPE_I1 ||
5415 et == ELEMENT_TYPE_I2 ||
5416 et == ELEMENT_TYPE_I4)
5418 ss.Set(W("System.Collections.Generic.Int32EnumComparer`1"));
5421 if (et == ELEMENT_TYPE_U1 ||
5422 et == ELEMENT_TYPE_U2 ||
5423 et == ELEMENT_TYPE_U4)
5425 ss.Set(W("System.Collections.Generic.UInt32EnumComparer`1"));
5428 if (et == ELEMENT_TYPE_I8)
5430 ss.Set(W("System.Collections.Generic.Int64EnumComparer`1"));
5433 if (et == ELEMENT_TYPE_U8)
5435 ss.Set(W("System.Collections.Generic.UInt64EnumComparer`1"));
5442 // This method has duplicated logic from bcl\system\collections\generic\equalitycomparer.cs
5444 static void SpecializeEqualityComparer(SString& ss, Instantiation& inst)
5446 STANDARD_VM_CONTRACT;
5448 if (inst.GetNumArgs() != 1) {
5449 _ASSERTE(!"Improper use of a TypeDependencyAttribute for EqualityComparer");
5453 TypeHandle elemTypeHnd = inst[0];
5456 // Override the default ObjectEqualityComparer for special cases
5458 if (elemTypeHnd.CanCastTo(
5459 TypeHandle(MscorlibBinder::GetClass(CLASS__IEQUATABLEGENERIC)).Instantiate(Instantiation(&elemTypeHnd, 1))))
5461 ss.Set(W("System.Collections.Generic.GenericEqualityComparer`1"));
5465 if (Nullable::IsNullableType(elemTypeHnd))
5467 Instantiation nullableInst = elemTypeHnd.AsMethodTable()->GetInstantiation();
5468 if (nullableInst[0].CanCastTo(
5469 TypeHandle(MscorlibBinder::GetClass(CLASS__IEQUATABLEGENERIC)).Instantiate(nullableInst)))
5471 ss.Set(W("System.Collections.Generic.NullableEqualityComparer`1"));
5472 inst = nullableInst;
5477 if (elemTypeHnd.IsEnum())
5479 // Note: We have different comparers for Short and SByte because for those types we need to make sure we call GetHashCode on the actual underlying type as the
5480 // implementation of GetHashCode is more complex than for the other types.
5481 CorElementType et = elemTypeHnd.GetVerifierCorElementType();
5482 if (et == ELEMENT_TYPE_I4 ||
5483 et == ELEMENT_TYPE_U4 ||
5484 et == ELEMENT_TYPE_U2 ||
5485 et == ELEMENT_TYPE_U1)
5487 ss.Set(W("System.Collections.Generic.EnumEqualityComparer`1"));
5490 else if (et == ELEMENT_TYPE_I2)
5492 ss.Set(W("System.Collections.Generic.ShortEnumEqualityComparer`1"));
5495 else if (et == ELEMENT_TYPE_I1)
5497 ss.Set(W("System.Collections.Generic.SByteEnumEqualityComparer`1"));
5500 else if (et == ELEMENT_TYPE_I8 ||
5501 et == ELEMENT_TYPE_U8)
5503 ss.Set(W("System.Collections.Generic.LongEnumEqualityComparer`1"));
5509 #ifdef FEATURE_COMINTEROP
5510 // Instantiation of WinRT types defined in non-WinRT module. This check is required to generate marshaling stubs for
5511 // instantiations of shadow WinRT types like EventHandler<ITracingStatusChangedEventArgs> in mscorlib.
5512 static BOOL IsInstantationOfShadowWinRTType(MethodTable * pMT)
5514 STANDARD_VM_CONTRACT;
5516 Instantiation inst = pMT->GetInstantiation();
5517 for (DWORD i = 0; i < inst.GetNumArgs(); i++)
5519 TypeHandle th = inst[i];
5520 if (th.IsProjectedFromWinRT() && !th.GetModule()->IsWindowsRuntimeModule())
5527 void CEEPreloader::ApplyTypeDependencyProductionsForType(TypeHandle t)
5529 STANDARD_VM_CONTRACT;
5531 // Only actual types
5535 MethodTable * pMT = t.AsMethodTable();
5537 if (!pMT->HasInstantiation() || pMT->ContainsGenericVariables())
5540 #ifdef FEATURE_COMINTEROP
5541 // At run-time, generic redirected interfaces and delegates need matching instantiations
5542 // of other types/methods in order to be marshaled across the interop boundary.
5543 if (m_image->GetModule()->IsWindowsRuntimeModule() || IsInstantationOfShadowWinRTType(pMT))
5545 // We only apply WinRT dependencies when compiling .winmd assemblies since redirected
5546 // types are heavily used in non-WinRT code as well and would bloat native images.
5547 if (pMT->IsLegalNonArrayWinRTType())
5550 WinMDAdapter::RedirectedTypeIndex index;
5551 if (WinRTInterfaceRedirector::ResolveRedirectedInterface(pMT, &index))
5553 // redirected interface needs the mscorlib-local definition of the corresponding WinRT type
5554 MethodTable *pWinRTMT = WinRTInterfaceRedirector::GetWinRTTypeForRedirectedInterfaceIndex(index);
5555 thWinRT = TypeHandle(pWinRTMT);
5557 // and matching stub methods
5558 WORD wNumSlots = pWinRTMT->GetNumVirtuals();
5559 for (WORD i = 0; i < wNumSlots; i++)
5561 MethodDesc *pAdapterMD = WinRTInterfaceRedirector::GetStubMethodForRedirectedInterface(
5564 TypeHandle::Interop_NativeToManaged,
5566 pMT->GetInstantiation());
5568 TriageMethodForZap(pAdapterMD, TRUE);
5571 if (WinRTDelegateRedirector::ResolveRedirectedDelegate(pMT, &index))
5573 // redirected delegate needs the mscorlib-local definition of the corresponding WinRT type
5574 thWinRT = TypeHandle(WinRTDelegateRedirector::GetWinRTTypeForRedirectedDelegateIndex(index));
5577 if (!thWinRT.IsNull())
5579 thWinRT = thWinRT.Instantiate(pMT->GetInstantiation());
5580 TriageTypeForZap(thWinRT, TRUE);
5584 #endif // FEATURE_COMINTEROP
5586 pMT = pMT->GetCanonicalMethodTable();
5588 // The TypeDependencyAttribute attribute is currently only allowed on mscorlib types
5589 // Don't even look for the attribute on types in other assemblies.
5590 if(!pMT->GetModule()->IsSystem()) {
5594 // Part 1. - check for an NGEN production rule specified by a use of CompilerServices.TypeDependencyAttribute
5595 // e.g. C<T> --> D<T>
5597 // For example, if C<int> is generated then we produce D<int>.
5599 // Normally NGEN can detect such productions through the process of compilation, but there are some
5600 // legitimate uses of reflection to generate generic instantiations which NGEN cannot detect.
5601 // In particular typically D<T> will have more constraints than C<T>, e.g.
5602 // class D<T> where T : IComparable<T>
5603 // Uses of dynamic constraints are an example - consider making a Comparer<T>, where we can have a
5604 // FastComparer<T> where T : IComparable<T>, and the "slow" version checks for the non-generic
5605 // IComparer interface.
5606 // Also, T[] : IList<T>, IReadOnlyList<T>, and both of those interfaces should have a type dependency on SZArrayHelper's generic methods.
5608 IMDInternalImport *pImport = pMT->GetMDImport();
5612 //walk all of the TypeDependencyAttributes
5613 MDEnumHolder hEnum(pImport);
5614 hr = pImport->EnumCustomAttributeByNameInit(pMT->GetCl(),
5615 g_CompilerServicesTypeDependencyAttribute, &hEnum);
5618 mdCustomAttribute tkAttribute;
5622 while (pImport->EnumNext(&hEnum, &tkAttribute))
5624 //get attribute and validate format
5625 if (FAILED(pImport->GetCustomAttributeAsBlob(
5627 reinterpret_cast<const void **>(&pbAttr),
5633 CustomAttributeParser cap(pbAttr, cbAttr);
5634 if (FAILED(cap.SkipProlog()))
5639 if (FAILED(cap.GetNonNullString(&szString, &cbString)))
5642 StackSString ss(SString::Utf8, szString, cbString);
5643 Instantiation inst = pMT->GetInstantiation();
5645 #ifndef FEATURE_FULL_NGEN
5646 // Do not expand non-canonical instantiations. They are not that expensive to create at runtime
5647 // using code:ClassLoader::CreateTypeHandleForNonCanonicalGenericInstantiation if necessary.
5648 if (!ClassLoader::IsCanonicalGenericInstantiation(inst))
5652 if (ss.Equals(W("System.Collections.Generic.ObjectComparer`1")))
5654 SpecializeComparer(ss, inst);
5657 if (ss.Equals(W("System.Collections.Generic.ObjectEqualityComparer`1")))
5659 SpecializeEqualityComparer(ss, inst);
5662 // Try to load the class using its name as a fully qualified name. If that fails,
5663 // then we try to load it in the assembly of the current class.
5664 TypeHandle typicalDepTH = TypeName::GetTypeUsingCASearchRules(ss.GetUnicode(), pMT->GetAssembly());
5666 _ASSERTE(!typicalDepTH.IsNull());
5667 // This attribute is currently only allowed to refer to mscorlib types
5668 _ASSERTE(typicalDepTH.GetModule()->IsSystem());
5669 if (!typicalDepTH.GetModule()->IsSystem())
5672 // For IList<T>, ICollection<T>, IEnumerable<T>, IReadOnlyCollection<T> & IReadOnlyList<T>, include SZArrayHelper's
5673 // generic methods (or at least the relevant ones) in the ngen image in
5674 // case someone casts a T[] to an IList<T> (or ICollection<T> or IEnumerable<T>, etc).
5675 if (MscorlibBinder::IsClass(typicalDepTH.AsMethodTable(), CLASS__SZARRAYHELPER))
5677 #ifdef FEATURE_FULL_NGEN
5678 if (pMT->GetNumGenericArgs() != 1 || !pMT->IsInterface()) {
5679 _ASSERTE(!"Improper use of a TypeDependencyAttribute for SZArrayHelper");
5682 TypeHandle elemTypeHnd = pMT->GetInstantiation()[0];
5683 if (elemTypeHnd.IsValueType())
5684 ApplyTypeDependencyForSZArrayHelper(pMT, elemTypeHnd);
5689 _ASSERTE(typicalDepTH.IsTypicalTypeDefinition());
5690 if (!typicalDepTH.IsTypicalTypeDefinition())
5693 // It certainly can't be immediately recursive...
5694 _ASSERTE(!typicalDepTH.GetMethodTable()->HasSameTypeDefAs(pMT));
5696 // We want to rule out some cases where we know for sure that the generated type
5697 // won't satisfy its constraints. However, some generated types may represent
5698 // canonicals in sets of shared instantaitions,
5700 if (CanSatisfyConstraints(typicalDepTH.GetInstantiation(), inst))
5702 TypeHandle instDepTH =
5703 ClassLoader::LoadGenericInstantiationThrowing(typicalDepTH.GetModule(), typicalDepTH.GetCl(), inst);
5705 _ASSERTE(!instDepTH.ContainsGenericVariables());
5706 _ASSERTE(instDepTH.GetNumGenericArgs() == typicalDepTH.GetNumGenericArgs());
5707 _ASSERTE(instDepTH.GetMethodTable()->HasSameTypeDefAs(typicalDepTH.GetMethodTable()));
5709 // OK, add the generated type to the dependency set
5710 TriageTypeForZap(instDepTH, TRUE);
5714 } // CEEPreloader::ApplyTypeDependencyProductionsForType
5717 // Given IEnumerable<Foo>, we want to add System.SZArrayHelper.GetEnumerator<Foo>
5718 // to the ngen image. This way we can cast a T[] to an IList<T> and
5719 // use methods on it (from SZArrayHelper) without pulling in the JIT.
5720 // Do the same for ICollection<T>/IReadOnlyCollection<T> and
5721 // IList<T>/IReadOnlyList<T>, but only add the relevant methods
5722 // from those interfaces.
5723 void CEEPreloader::ApplyTypeDependencyForSZArrayHelper(MethodTable * pInterfaceMT, TypeHandle elemTypeHnd)
5725 STANDARD_VM_CONTRACT;
5727 _ASSERTE(elemTypeHnd.AsMethodTable()->IsValueType());
5729 // We expect this to only be called for IList<T>/IReadOnlyList<T>, ICollection<T>/IReadOnlyCollection<T>, IEnumerable<T>.
5730 _ASSERTE(pInterfaceMT->IsInterface());
5731 _ASSERTE(pInterfaceMT->GetNumGenericArgs() == 1);
5733 // This is the list of methods that don't throw exceptions on SZArrayHelper.
5734 static const BinderMethodID SZArrayHelperMethodIDs[] = {
5735 // Read-only methods that are present on both regular and read-only interfaces.
5736 METHOD__SZARRAYHELPER__GETENUMERATOR,
5737 METHOD__SZARRAYHELPER__GET_COUNT,
5738 METHOD__SZARRAYHELPER__GET_ITEM,
5739 // The rest of the methods is present on regular interfaces only.
5740 METHOD__SZARRAYHELPER__SET_ITEM,
5741 METHOD__SZARRAYHELPER__COPYTO,
5742 METHOD__SZARRAYHELPER__INDEXOF,
5743 METHOD__SZARRAYHELPER__CONTAINS };
5745 static const int cReadOnlyMethods = 3;
5746 static const int cAllMethods = 7;
5748 static const BinderMethodID LastMethodOnGenericArrayInterfaces[] = {
5749 METHOD__SZARRAYHELPER__GETENUMERATOR, // Last method of IEnumerable<T>
5750 METHOD__SZARRAYHELPER__REMOVE, // Last method of ICollection<T>.
5751 METHOD__SZARRAYHELPER__REMOVEAT, // Last method of IList<T>
5754 // Assuming the binder ID's are properly laid out in mscorlib.h
5756 for(unsigned int i=0; i < NumItems(LastMethodOnGenericArrayInterfaces) - 1; i++) {
5757 _ASSERTE(LastMethodOnGenericArrayInterfaces[i] < LastMethodOnGenericArrayInterfaces[i+1]);
5761 MethodTable* pExactMT = MscorlibBinder::GetClass(CLASS__SZARRAYHELPER);
5763 // Subtract one from the non-generic IEnumerable that the generic IEnumerable<T>
5765 unsigned inheritanceDepth = pInterfaceMT->GetNumInterfaces() - 1;
5766 PREFIX_ASSUME(0 <= inheritanceDepth && inheritanceDepth < NumItems(LastMethodOnGenericArrayInterfaces));
5768 // Read-only interfaces happen to always have one method
5769 bool fIsReadOnly = pInterfaceMT->GetNumVirtuals() == 1;
5771 for(int i=0; i < (fIsReadOnly ? cReadOnlyMethods : cAllMethods); i++)
5773 // Check whether the method applies for this type.
5774 if (SZArrayHelperMethodIDs[i] > LastMethodOnGenericArrayInterfaces[inheritanceDepth])
5777 MethodDesc * pPrimaryMD = MscorlibBinder::GetMethod(SZArrayHelperMethodIDs[i]);
5779 MethodDesc * pInstantiatedMD = MethodDesc::FindOrCreateAssociatedMethodDesc(pPrimaryMD,
5780 pExactMT, false, Instantiation(&elemTypeHnd, 1), false);
5782 TriageMethodForZap(pInstantiatedMD, true);
5787 void CEEPreloader::AddTypeToTransitiveClosureOfInstantiations(CORINFO_CLASS_HANDLE handle)
5789 STANDARD_VM_CONTRACT;
5791 TriageTypeForZap((TypeHandle) handle, TRUE);
5794 const unsigned MAX_ZAP_INSTANTIATION_NESTING = 10;
5796 BOOL IsGenericTooDeeplyNested(TypeHandle t)
5804 //if this type is more than N levels nested deep, do not add it to the
5805 //closure. Build a queue for a DFS of the depth of instantiation.
5807 //the current index in the queue we're visiting
5808 int currentQueueIdx; //use -1 to indicate that we're done.
5809 //the current generic arg type.
5810 TypeHandle currentVisitingType[MAX_ZAP_INSTANTIATION_NESTING];
5812 //the ordinal in the GetInstantiation for the current type (over [0,
5813 //GetNumGenericArg())
5814 unsigned currentGenericArgEdge[MAX_ZAP_INSTANTIATION_NESTING];
5816 //initialize the DFS.
5817 memset(currentGenericArgEdge, 0, sizeof(currentGenericArgEdge));
5818 currentVisitingType[0] = t;
5819 currentQueueIdx = 0;
5821 while( currentQueueIdx >= 0 )
5823 //see if we're done with this node
5824 if( currentVisitingType[currentQueueIdx].GetNumGenericArgs()
5825 <= currentGenericArgEdge[currentQueueIdx] )
5831 //more edges to visit. So visit one edge
5832 _ASSERTE(currentGenericArgEdge[currentQueueIdx] < currentVisitingType[currentQueueIdx].GetNumGenericArgs());
5833 TypeHandle current = currentVisitingType[currentQueueIdx].GetInstantiation()[currentGenericArgEdge[currentQueueIdx]];
5834 ++currentGenericArgEdge[currentQueueIdx];
5835 //only value types cause a problem because of "approximate" type
5836 //loading, so only worry about scanning value type arguments.
5837 if( current.HasInstantiation() && current.IsValueType() )
5839 //new edge. Make sure there is space in the queue.
5840 if( (currentQueueIdx + 1) >= (int)NumItems(currentGenericArgEdge) )
5842 //exceeded the allowable depth. Stop processing.
5848 currentGenericArgEdge[currentQueueIdx] = 0;
5849 currentVisitingType[currentQueueIdx] = current;
5858 void CEEPreloader::TriageTypeForZap(TypeHandle th, BOOL fAcceptIfNotSure, BOOL fExpandDependencies)
5860 STANDARD_VM_CONTRACT;
5862 // We care about param types only
5863 if (th.IsTypicalTypeDefinition() && !th.IsTypeDesc())
5866 // We care about types from our module only
5867 if (m_image->GetModule() != th.GetLoaderModule())
5870 // Check if we have decided to accept this type already.
5871 if (m_acceptedTypes.Lookup(th) != NULL)
5874 // Check if we have decided to reject this type already.
5875 if (m_rejectedTypes.Lookup(th) != NULL)
5878 enum { Investigate, Accepted, Rejected } triage = Investigate;
5880 const char * rejectReason = NULL;
5882 // TypeVarTypeDesc are saved via links from code:Module::m_GenericParamToDescMap
5883 if (th.IsGenericVariable())
5886 rejectReason = "type is a Generic variable";
5890 /* Consider this example:
5893 class B<U> : A<U> {}
5906 The open instantiations can be divided into the following 3 categories:
5908 1. A<T>, B<U>, A<U>, C<V>, B<V>, A<V> are open instantiations involving
5909 ELEMENT_TYPE_VARs that need to be saved in the ngen image.
5910 2. List<V> is an instantiations that also involves ELEMENT_TYPE_VARs.
5911 However, it need not be saved since it will only be needed during the
5912 verification of foo<W>().
5913 3. C<W>, A<W>, B<A<W>> are open instantiations involving ELEMENT_TYPE_MVARs
5914 that need not be saved since they will only be needed during the
5915 verification of foo<W>().
5917 Distinguishing between 1 and 2 requires walking C<V> and determining
5918 which ones are field/parent/interface types required by c<V>. However,
5919 category 3 is easy to detect, and can easily be pruned out. Hence,
5920 we pass in methodTypeVarsOnly=TRUE here.
5922 if (th.ContainsGenericVariables(TRUE/*methodTypeVarsOnly*/))
5925 rejectReason = "type contains method generic variables";
5929 // Filter out weird cases we do not care about.
5930 if (!m_image->GetModule()->GetAvailableParamTypes()->ContainsValue(th))
5933 rejectReason = "type is not in the current module";
5937 // Reject invalid generic instantiations. They will not be fully loaded
5938 // as they will throw a TypeLoadException before they reach CLASS_LOAD_LEVEL_FINAL.
5939 if (!th.IsFullyLoaded())
5941 // This may load new types. May load new types.
5942 ClassLoader::TryEnsureLoaded(th);
5944 if (!th.IsFullyLoaded())
5947 rejectReason = "type could not be fully loaded, possibly because it does not satisfy its constraints";
5952 // Do not save any types containing generic class parameters from another module
5953 Module *pOpenModule;
5954 pOpenModule = th.GetDefiningModuleForOpenType();
5955 if (pOpenModule != NULL && pOpenModule != m_image->GetModule())
5958 rejectReason = "type contains generic variables from another module";
5962 // Always store items in their preferred zap module even if we are not sure
5963 if (Module::GetPreferredZapModuleForTypeHandle(th) == m_image->GetModule())
5969 #ifdef FEATURE_FULL_NGEN
5970 // Only save arrays and other param types in their preferred zap modules,
5971 // i.e. never duplicate them.
5972 if (th.IsTypeDesc() || th.IsArrayType())
5975 rejectReason = "type is a TypeDesc";
5980 // Do not save instantiations found in one of our hardbound dependencies
5981 PtrHashMap::PtrIterator iter = GetAppDomain()->ToCompilationDomain()->IterateHardBoundModules();
5982 for (/**/; !iter.end(); ++iter)
5984 Module * hardBoundModule = (Module*)iter.GetValue();
5985 if (hardBoundModule->GetAvailableParamTypes()->ContainsValue(th))
5988 rejectReason = "type was found in a hardbound dependency";
5994 // We are not really sure about this type. Accept it only if we have been asked to.
5995 if (fAcceptIfNotSure)
5997 if (!m_fSpeculativeTriage)
5999 // We will take a look later before we actually start compiling the instantiations
6000 m_speculativeTypes.Append(th);
6001 m_acceptedTypes.Add(th);
6009 rejectReason = "type is not in the preferred module";
6017 m_acceptedTypes.Add(th);
6018 if (fExpandDependencies)
6020 ExpandTypeDependencies(th);
6026 m_rejectedTypes.Add(th);
6029 // It is expensive to call th.GetName, only do it when we are actually logging
6030 if (LoggingEnabled())
6033 th.GetName(typeName);
6034 LOG((LF_ZAP, LL_INFO10000, "TriageTypeForZap rejects %S (%08x) because %s\n",
6035 typeName.GetUnicode(), th.AsPtr(), rejectReason));
6041 // We have not found a compeling reason to accept or reject the type yet. Maybe next time...
6046 void CEEPreloader::ExpandTypeDependencies(TypeHandle th)
6048 STANDARD_VM_CONTRACT;
6050 if (th.IsTypeDesc())
6053 MethodTable* pMT = th.AsMethodTable();
6055 if (pMT->IsCanonicalMethodTable())
6057 // Cutoff infinite recursion.
6058 if (!IsGenericTooDeeplyNested(th))
6060 // Make sure all methods are compiled
6061 // We only want to check the method bodies owned by this type,
6062 // and not any method bodies owned by a parent type, as the
6063 // parent type may not get saved in this ngen image.
6064 MethodTable::IntroducedMethodIterator itr(pMT);
6065 for (/**/; itr.IsValid(); itr.Next())
6067 AddToUncompiledMethods(itr.GetMethodDesc(), FALSE);
6073 // Make sure canonical method table is saved
6074 TriageTypeForZap(pMT->GetCanonicalMethodTable(), TRUE);
6077 if (pMT->SupportsGenericInterop(TypeHandle::Interop_ManagedToNative))
6079 MethodTable::IntroducedMethodIterator itr(pMT->GetCanonicalMethodTable());
6080 for (/**/; itr.IsValid(); itr.Next())
6082 MethodDesc *pMD = itr.GetMethodDesc();
6084 if (!pMD->HasMethodInstantiation())
6086 if (pMT->IsInterface() || !pMD->IsSharedByGenericInstantiations())
6088 pMD = MethodDesc::FindOrCreateAssociatedMethodDesc(
6091 FALSE, // forceBoxedEntryPoint
6092 Instantiation(), // methodInst
6093 FALSE, // allowInstParam
6094 TRUE); // forceRemotableMethod
6098 _ASSERTE(pMT->IsDelegate());
6099 pMD = InstantiatedMethodDesc::FindOrCreateExactClassMethod(pMT, pMD);
6102 AddToUncompiledMethods(pMD, TRUE);
6107 // Make sure parent type is saved
6108 TriageTypeForZap(pMT->GetParentMethodTable(), TRUE);
6110 // Make sure all instantiation arguments are saved
6111 Instantiation inst = pMT->GetInstantiation();
6112 for (DWORD iArg = 0; iArg < inst.GetNumArgs(); iArg++)
6114 TriageTypeForZap(inst[iArg], TRUE);
6117 // Make sure all interfaces implemeted by the class are saved
6118 MethodTable::InterfaceMapIterator intIterator = pMT->IterateInterfaceMap();
6119 while (intIterator.Next())
6121 TriageTypeForZap(intIterator.GetInterface(), TRUE);
6124 // Make sure aprox types for all fields are saved
6125 ApproxFieldDescIterator fdIterator(pMT, ApproxFieldDescIterator::ALL_FIELDS);
6127 while ((pFD = fdIterator.Next()) != NULL)
6129 if (pFD->GetFieldType() == ELEMENT_TYPE_VALUETYPE)
6131 TriageTypeForZap(pFD->GetFieldTypeHandleThrowing(), TRUE);
6135 // Make sure types for all generic static fields are saved
6137 if (pMT->HasGenericsStaticsInfo())
6139 FieldDesc *pGenStaticFields = pMT->GetGenericsStaticFieldDescs();
6140 DWORD nFields = pMT->GetNumStaticFields();
6141 for (DWORD iField = 0; iField < nFields; iField++)
6143 FieldDesc* pField = &pGenStaticFields[iField];
6144 if (pField->GetFieldType() == ELEMENT_TYPE_VALUETYPE)
6146 TriageTypeForZap(pField->GetFieldTypeHandleThrowing(), TRUE);
6151 // Expand type using the custom rules. May load new types.
6152 ApplyTypeDependencyProductionsForType(th);
6155 // Triage instantiations of generic methods
6157 void CEEPreloader::TriageMethodForZap(MethodDesc* pMD, BOOL fAcceptIfNotSure, BOOL fExpandDependencies)
6159 STANDARD_VM_CONTRACT;
6161 // Submit the method type for triage
6162 TriageTypeForZap(TypeHandle(pMD->GetMethodTable()), fAcceptIfNotSure);
6164 // We care about instantiated methods only
6165 if (pMD->IsTypicalMethodDefinition())
6168 // We care about methods from our module only
6169 if (m_image->GetModule() != pMD->GetLoaderModule())
6172 // Check if we have decided to accept this method already.
6173 if (m_acceptedMethods.Lookup(pMD) != NULL)
6176 // Check if we have decided to reject this method already.
6177 if (m_rejectedMethods.Lookup(pMD) != NULL)
6180 enum { Investigate, Accepted, Rejected } triage = Investigate;
6182 const char * rejectReason = NULL;
6184 // Do not save open methods
6185 if (pMD->ContainsGenericVariables())
6188 rejectReason = "method contains method generic variables";
6192 // Filter out other weird cases we do not care about.
6193 if (!m_image->GetModule()->GetInstMethodHashTable()->ContainsMethodDesc(pMD))
6196 rejectReason = "method is not in the current module";
6200 // Always store items in their preferred zap module even if we are not sure
6201 if (Module::GetPreferredZapModuleForMethodDesc(pMD) == m_image->GetModule())
6207 #ifdef FEATURE_FULL_NGEN
6209 // Do not save instantiations found in one of our hardbound dependencies
6210 PtrHashMap::PtrIterator iter = GetAppDomain()->ToCompilationDomain()->IterateHardBoundModules();
6211 for (/**/; !iter.end(); ++iter)
6213 Module * hardBoundModule = (Module*)iter.GetValue();
6214 if (hardBoundModule->GetInstMethodHashTable()->ContainsMethodDesc(pMD))
6217 rejectReason = "method was found in a hardbound dependency";
6223 // We are not really sure about this method. Accept it only if we have been asked to.
6224 if (fAcceptIfNotSure)
6226 // It does not seem worth it to go through extra hoops to eliminate redundant
6227 // speculative method instatiations from softbound dependencies like we do for types
6228 // if (!m_fSpeculativeTriage)
6230 // // We will take a look later before we actually start compiling the instantiations
6245 m_acceptedMethods.Add(pMD);
6246 if (fExpandDependencies)
6248 ExpandMethodDependencies(pMD);
6253 m_rejectedMethods.Add(pMD);
6254 LOG((LF_ZAP, LL_INFO10000, "TriageMethodForZap rejects %s (%08x) because %s\n",
6255 pMD->m_pszDebugMethodName, pMD, rejectReason));
6259 // We have not found a compeling reason to accept or reject the method yet. Maybe next time...
6264 void CEEPreloader::ExpandMethodDependencies(MethodDesc * pMD)
6266 STANDARD_VM_CONTRACT;
6268 AddToUncompiledMethods(pMD, FALSE);
6271 // Make sure all instantiation arguments are saved
6272 Instantiation inst = pMD->GetMethodInstantiation();
6273 for (DWORD iArg = 0; iArg < inst.GetNumArgs(); iArg++)
6275 TriageTypeForZap(inst[iArg], TRUE);
6279 // Make sure to add wrapped method desc
6280 if (pMD->IsWrapperStub())
6281 TriageMethodForZap(pMD->GetWrappedMethodDesc(), TRUE);
6284 void CEEPreloader::TriageTypeFromSoftBoundModule(TypeHandle th, Module * pSoftBoundModule)
6286 STANDARD_VM_CONTRACT;
6288 // We care about types from our module only
6289 if (m_image->GetModule() != th.GetLoaderModule())
6292 // Nothing to do if we have rejected the type already.
6293 if (m_rejectedTypes.Lookup(th) != NULL)
6296 // We make guarantees about types living in its own PZM only
6297 if (Module::GetPreferredZapModuleForTypeHandle(th) != pSoftBoundModule)
6300 // Reject the type - it is guaranteed to be saved in PZM
6301 m_rejectedTypes.Add(th);
6303 if (!th.IsTypeDesc())
6305 // Reject the canonical method table if possible.
6306 MethodTable* pMT = th.AsMethodTable();
6307 if (!pMT->IsCanonicalMethodTable())
6308 TriageTypeFromSoftBoundModule(pMT->GetCanonicalMethodTable(), pSoftBoundModule);
6310 // Reject parent method table if possible.
6311 TriageTypeFromSoftBoundModule(pMT->GetParentMethodTable(), pSoftBoundModule);
6313 // Reject all interfaces implemented by the type if possible.
6314 MethodTable::InterfaceMapIterator intIterator = pMT->IterateInterfaceMap();
6315 while (intIterator.Next())
6317 TriageTypeFromSoftBoundModule(intIterator.GetInterface(), pSoftBoundModule);
6320 // It does not seem worth it to reject the remaining items
6321 // expanded by CEEPreloader::ExpandTypeDependencies here.
6325 #ifdef FEATURE_FULL_NGEN
6326 static TypeHandle TryToLoadTypeSpecHelper(Module * pModule, PCCOR_SIGNATURE pSig, ULONG cSig)
6328 STANDARD_VM_CONTRACT;
6334 SigPointer p(pSig, cSig);
6335 SigTypeContext typeContext; // empty context is OK: encoding should not contain type variables.
6337 th = p.GetTypeHandleThrowing(pModule, &typeContext, ClassLoader::DontLoadTypes);
6342 EX_END_CATCH(SwallowAllExceptions)
6347 void CEEPreloader::TriageTypeSpecsFromSoftBoundModule(Module * pSoftBoundModule)
6349 STANDARD_VM_CONTRACT;
6352 // Reject all typespecs that are guranteed to be found in soft bound PZM
6355 IMDInternalImport *pInternalImport = pSoftBoundModule->GetMDImport();
6357 HENUMInternalHolder hEnum(pInternalImport);
6358 hEnum.EnumAllInit(mdtTypeSpec);
6361 while (pInternalImport->EnumNext(&hEnum, &tk))
6364 PCCOR_SIGNATURE pSig;
6366 if (FAILED(pInternalImport->GetTypeSpecFromToken(tk, &pSig, &cSig)))
6372 // Check all types specs that do not contain variables
6373 if (SigPointer(pSig, cSig).IsPolyType(NULL) == hasNoVars)
6375 TypeHandle th = TryToLoadTypeSpecHelper(pSoftBoundModule, pSig, cSig);
6380 TriageTypeFromSoftBoundModule(th, pSoftBoundModule);
6385 void CEEPreloader::TriageSpeculativeType(TypeHandle th)
6387 STANDARD_VM_CONTRACT;
6389 // Nothing to do if we have rejected the type already
6390 if (m_rejectedTypes.Lookup(th) != NULL)
6393 Module * pPreferredZapModule = Module::GetPreferredZapModuleForTypeHandle(th);
6394 BOOL fHardBoundPreferredZapModule = FALSE;
6397 // Even though we have done this check already earlier, do it again here in case we have picked up
6398 // any eager-bound dependency in the meantime
6400 // Do not save instantiations found in one of our eager-bound dependencies
6401 PtrHashMap::PtrIterator iter = GetAppDomain()->ToCompilationDomain()->IterateHardBoundModules();
6402 for (/**/; !iter.end(); ++iter)
6404 Module * hardBoundModule = (Module*)iter.GetValue();
6405 if (hardBoundModule->GetAvailableParamTypes()->ContainsValue(th))
6407 m_rejectedTypes.Add(th);
6411 if (hardBoundModule == pPreferredZapModule)
6413 fHardBoundPreferredZapModule = TRUE;
6417 if (!fHardBoundPreferredZapModule && !pPreferredZapModule->AreTypeSpecsTriaged())
6419 // Reject all types that are guaranteed to be instantiated in soft bound PZM
6420 TriageTypeSpecsFromSoftBoundModule(pPreferredZapModule);
6421 pPreferredZapModule->SetTypeSpecsTriaged();
6423 if (m_rejectedTypes.Lookup(th) != NULL)
6427 // We have to no other option but to accept and expand the type
6428 ExpandTypeDependencies(th);
6431 void CEEPreloader::TriageSpeculativeInstantiations()
6433 STANDARD_VM_CONTRACT;
6435 // Get definitive triage answer for speculative types that we have run into earlier
6436 // Note that m_speculativeTypes may be growing as this loop runs
6437 for (COUNT_T i = 0; i < m_speculativeTypes.GetCount(); i++)
6439 TriageSpeculativeType(m_speculativeTypes[i]);
6442 // We are done - the array of speculative types is no longer necessary
6443 m_speculativeTypes.Clear();
6445 #endif // FEATURE_FULL_NGEN
6447 BOOL CEEPreloader::TriageForZap(BOOL fAcceptIfNotSure, BOOL fExpandDependencies)
6449 STANDARD_VM_CONTRACT;
6451 DWORD dwNumTypes = m_image->GetModule()->GetAvailableParamTypes()->GetCount();
6452 DWORD dwNumMethods = m_image->GetModule()->GetInstMethodHashTable()->GetCount();
6456 // Create a local copy in case the new elements are added to the hashtable during population
6457 InlineSArray<TypeHandle, 20> pTypes;
6459 // Make sure the iterator is destroyed before there is a chance of loading new types
6461 EETypeHashTable* pTable = m_image->GetModule()->GetAvailableParamTypes();
6463 EETypeHashTable::Iterator it(pTable);
6464 EETypeHashEntry *pEntry;
6465 while (pTable->FindNext(&it, &pEntry))
6467 TypeHandle th = pEntry->GetTypeHandle();
6468 if (m_acceptedTypes.Lookup(th) == NULL && m_rejectedTypes.Lookup(th) == NULL)
6473 for(COUNT_T i = 0; i < pTypes.GetCount(); i ++)
6475 TriageTypeForZap(pTypes[i], fAcceptIfNotSure, fExpandDependencies);
6481 // Create a local copy in case the new elements are added to the hashtable during population
6482 InlineSArray<MethodDesc*, 20> pMethods;
6484 // Make sure the iterator is destroyed before there is a chance of loading new methods
6486 InstMethodHashTable* pTable = m_image->GetModule()->GetInstMethodHashTable();
6488 InstMethodHashTable::Iterator it(pTable);
6489 InstMethodHashEntry *pEntry;
6490 while (pTable->FindNext(&it, &pEntry))
6492 MethodDesc* pMD = pEntry->GetMethod();
6493 if (m_acceptedMethods.Lookup(pMD) == NULL && m_rejectedMethods.Lookup(pMD) == NULL)
6494 pMethods.Append(pMD);
6498 for(COUNT_T i = 0; i < pMethods.GetCount(); i ++)
6500 TriageMethodForZap(pMethods[i], fAcceptIfNotSure, fExpandDependencies);
6504 // Returns TRUE if new types or methods has been added by the triage
6505 return (dwNumTypes != m_image->GetModule()->GetAvailableParamTypes()->GetCount()) ||
6506 (dwNumMethods != m_image->GetModule()->GetInstMethodHashTable()->GetCount());
6509 void CEEPreloader::PrePrepareMethodIfNecessary(CORINFO_METHOD_HANDLE hMethod)
6511 STANDARD_VM_CONTRACT;
6514 ::PrePrepareMethodIfNecessary(hMethod);
6518 static void SetStubMethodDescOnInteropMethodDesc(MethodDesc* pInteropMD, MethodDesc* pStubMD, bool fReverseStub)
6526 // We store NGENed stubs on these MethodDesc types
6527 PRECONDITION(pInteropMD->IsNDirect() || pInteropMD->IsComPlusCall() || pInteropMD->IsGenericComPlusCall() || pInteropMD->IsEEImpl());
6531 if (pInteropMD->IsNDirect())
6533 _ASSERTE(!fReverseStub);
6534 NDirectMethodDesc* pNMD = (NDirectMethodDesc*)pInteropMD;
6535 pNMD->ndirect.m_pStubMD.SetValue(pStubMD);
6537 #ifdef FEATURE_COMINTEROP
6538 else if (pInteropMD->IsComPlusCall() || pInteropMD->IsGenericComPlusCall())
6540 _ASSERTE(!fReverseStub);
6541 ComPlusCallInfo *pComInfo = ComPlusCallInfo::FromMethodDesc(pInteropMD);
6542 pComInfo->m_pStubMD.SetValue(pStubMD);
6544 #endif // FEATURE_COMINTEROP
6545 else if (pInteropMD->IsEEImpl())
6547 DelegateEEClass* pDelegateClass = (DelegateEEClass*)pInteropMD->GetClass();
6550 pDelegateClass->m_pReverseStubMD = pStubMD;
6554 #ifdef FEATURE_COMINTEROP
6555 // We don't currently NGEN both the P/Invoke and WinRT stubs for WinRT delegates.
6556 // If that changes, this function will need an extra parameter to tell what kind
6557 // of stub is being passed.
6558 if (pInteropMD->GetMethodTable()->IsWinRTDelegate())
6560 pDelegateClass->m_pComPlusCallInfo->m_pStubMD.SetValue(pStubMD);
6563 #endif // FEATURE_COMINTEROP
6565 pDelegateClass->m_pForwardStubMD = pStubMD;
6571 UNREACHABLE_MSG("unexpected type of MethodDesc");
6575 MethodDesc * CEEPreloader::CompileMethodStubIfNeeded(
6577 MethodDesc *pStubMD,
6578 ICorCompilePreloader::CORCOMPILE_CompileStubCallback pfnCallback,
6579 LPVOID pCallbackContext)
6581 STANDARD_VM_CONTRACT;
6583 LOG((LF_ZAP, LL_INFO10000, "NGEN_ILSTUB: %s::%s -> %s::%s\n",
6584 pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName, pStubMD->m_pszDebugClassName, pStubMD->m_pszDebugMethodName));
6586 // It is possible that the StubMD is a normal method pointed by InteropStubMethodAttribute,
6587 // and in that case we don't need to compile it here
6588 if (pStubMD->IsDynamicMethod())
6590 if (!pStubMD->AsDynamicMethodDesc()->GetILStubResolver()->IsCompiled())
6592 CORJIT_FLAGS jitFlags = pStubMD->AsDynamicMethodDesc()->GetILStubResolver()->GetJitFlags();
6594 pfnCallback(pCallbackContext, (CORINFO_METHOD_HANDLE)pStubMD, jitFlags);
6597 #ifndef FEATURE_FULL_NGEN // Deduplication
6598 const DuplicateMethodEntry * pDuplicate = m_duplicateMethodsHash.LookupPtr(pStubMD);
6599 if (pDuplicate != NULL)
6600 return pDuplicate->pDuplicateMD;
6604 //We do not store ILStubs so if the compilation failed for them
6605 //It does not make sense to keep the MD corresponding to the IL
6606 if (pStubMD->IsILStub() && m_image->GetCodeAddress(pStubMD) == NULL)
6612 void CEEPreloader::GenerateMethodStubs(
6613 CORINFO_METHOD_HANDLE hMethod,
6614 bool fNgenProfilerImage,
6615 CORCOMPILE_CompileStubCallback pfnCallback,
6616 LPVOID pCallbackContext)
6621 PRECONDITION(hMethod != NULL && pfnCallback != NULL);
6625 MethodDesc* pMD = GetMethod(hMethod);
6626 MethodDesc* pStubMD = NULL;
6628 // Do not generate IL stubs when generating ReadyToRun images
6629 // This prevents versionability concerns around IL stubs exposing internal
6630 // implementation details of the CLR.
6631 if (IsReadyToRunCompilation())
6634 DWORD dwNGenStubFlags = NDIRECTSTUB_FL_NGENEDSTUB;
6636 if (fNgenProfilerImage)
6637 dwNGenStubFlags |= NDIRECTSTUB_FL_NGENEDSTUBFORPROFILING;
6640 // Generate IL stubs. If failed, we go through normal NGEN path
6641 // Catch any exceptions that occur when we try to create the IL_STUB
6646 // Take care of forward stubs
6648 if (pMD->IsNDirect())
6650 NDirectMethodDesc* pNMD = (NDirectMethodDesc*)pMD;
6651 PInvokeStaticSigInfo sigInfo;
6652 NDirect::PopulateNDirectMethodDesc(pNMD, &sigInfo);
6653 pStubMD = NDirect::GetILStubMethodDesc((NDirectMethodDesc*)pMD, &sigInfo, dwNGenStubFlags);
6655 #ifdef FEATURE_COMINTEROP
6656 else if (pMD->IsComPlusCall() || pMD->IsGenericComPlusCall())
6658 if (MethodNeedsForwardComStub(pMD, m_image))
6660 // Look for predefined IL stubs in forward com interop scenario.
6661 // If we've found a stub, that's what we'll use
6663 ComPlusCall::PopulateComPlusCallMethodDesc(pMD, &dwStubFlags);
6664 if (FAILED(FindPredefinedILStubMethod(pMD, dwStubFlags, &pStubMD)))
6666 pStubMD = ComPlusCall::GetILStubMethodDesc(pMD, dwStubFlags | dwNGenStubFlags);
6670 #endif // FEATURE_COMINTEROP
6671 else if (pMD->IsEEImpl())
6673 MethodTable* pMT = pMD->GetMethodTable();
6674 CONSISTENCY_CHECK(pMT->IsDelegate());
6676 // we can filter out non-WinRT generic delegates right off the top
6677 if (!pMD->HasClassOrMethodInstantiation() || pMT->IsProjectedFromWinRT()
6678 #ifdef FEATURE_COMINTEROP
6679 || WinRTTypeNameConverter::IsRedirectedType(pMT)
6680 #endif // FEATURE_COMINTEROP
6683 if (COMDelegate::IsDelegateInvokeMethod(pMD)) // build forward stub
6685 #ifdef FEATURE_COMINTEROP
6686 if ((pMT->IsProjectedFromWinRT() || WinRTTypeNameConverter::IsRedirectedType(pMT)) &&
6687 (!pMT->HasInstantiation() || pMT->SupportsGenericInterop(TypeHandle::Interop_ManagedToNative))) // filter out shared generics
6689 // Build the stub for all WinRT delegates, these will definitely be used for interop.
6690 if (pMT->IsLegalNonArrayWinRTType())
6692 COMDelegate::PopulateComPlusCallInfo(pMT);
6693 pStubMD = COMDelegate::GetILStubMethodDesc((EEImplMethodDesc *)pMD, dwNGenStubFlags);
6697 #endif // FEATURE_COMINTEROP
6699 // Build the stub only if the delegate is decorated with UnmanagedFunctionPointerAttribute.
6700 // Forward delegate stubs are rare so we require this opt-in to avoid bloating NGEN images.
6702 if (S_OK == pMT->GetMDImport()->GetCustomAttributeByName(
6703 pMT->GetCl(), g_UnmanagedFunctionPointerAttribute, NULL, NULL))
6705 pStubMD = COMDelegate::GetILStubMethodDesc((EEImplMethodDesc *)pMD, dwNGenStubFlags);
6712 // compile the forward stub
6713 if (pStubMD != NULL)
6715 pStubMD = CompileMethodStubIfNeeded(pMD, pStubMD, pfnCallback, pCallbackContext);
6717 // We store the MethodDesc of the Stub on the NDirectMethodDesc/ComPlusCallMethodDesc/DelegateEEClass
6718 // that we can recover the stub MethodDesc at prestub time, do the fixups, and wire up the native code
6719 if (pStubMD != NULL)
6721 SetStubMethodDescOnInteropMethodDesc(pMD, pStubMD, false /* fReverseStub */);
6729 LOG((LF_ZAP, LL_WARNING, "NGEN_ILSTUB: Generating forward interop stub FAILED: %s::%s\n", pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName));
6731 EX_END_CATCH(RethrowTransientExceptions);
6734 // Now take care of reverse P/Invoke stubs for delegates
6736 if (pMD->IsEEImpl() && COMDelegate::IsDelegateInvokeMethod(pMD))
6738 // Reverse P/Invoke is not supported for generic methods and WinRT delegates
6739 if (!pMD->HasClassOrMethodInstantiation() && !pMD->GetMethodTable()->IsProjectedFromWinRT())
6744 // on x86, we call the target directly if Invoke has a no-marshal signature
6745 if (NDirect::MarshalingRequired(pMD))
6746 #endif // _TARGET_X86_
6748 PInvokeStaticSigInfo sigInfo(pMD);
6749 pStubMD = UMThunkMarshInfo::GetILStubMethodDesc(pMD, &sigInfo, NDIRECTSTUB_FL_DELEGATE | dwNGenStubFlags);
6751 if (pStubMD != NULL)
6753 // compile the reverse stub
6754 pStubMD = CompileMethodStubIfNeeded(pMD, pStubMD, pfnCallback, pCallbackContext);
6756 // We store the MethodDesc of the Stub on the DelegateEEClass
6757 if (pStubMD != NULL)
6759 SetStubMethodDescOnInteropMethodDesc(pMD, pStubMD, true /* fReverseStub */);
6766 LOG((LF_ZAP, LL_WARNING, "NGEN_ILSTUB: Generating reverse interop stub for delegate FAILED: %s::%s\n", pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName));
6768 EX_END_CATCH(RethrowTransientExceptions);
6772 #ifdef FEATURE_COMINTEROP
6774 // And finally generate reverse COM stubs
6778 // The method doesn't have to have a special type to be exposed to COM, in particular it doesn't
6779 // have to be ComPlusCallMethodDesc. However, it must have certain properties (custom attributes,
6780 // public visibility, etc.)
6781 if (MethodNeedsReverseComStub(pMD))
6783 // initialize ComCallMethodDesc
6784 ComCallMethodDesc ccmd;
6785 ComCallMethodDescHolder ccmdHolder(&ccmd);
6786 ccmd.InitMethod(pMD, NULL);
6788 // generate the IL stub
6790 ComCall::PopulateComCallMethodDesc(&ccmd, &dwStubFlags);
6791 pStubMD = ComCall::GetILStubMethodDesc(pMD, dwStubFlags | dwNGenStubFlags);
6793 if (pStubMD != NULL)
6795 // compile the reverse stub
6796 pStubMD = CompileMethodStubIfNeeded(pMD, pStubMD, pfnCallback, pCallbackContext);
6798 if (pStubMD != NULL)
6800 // store the stub in a hash table on the module
6801 m_image->GetModule()->GetStubMethodHashTable()->InsertMethodDesc(pMD, pStubMD);
6808 LOG((LF_ZAP, LL_WARNING, "NGEN_ILSTUB: Generating reverse interop stub FAILED: %s::%s\n", pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName));
6810 EX_END_CATCH(RethrowTransientExceptions);
6811 #endif // FEATURE_COMINTEROP
6814 bool CEEPreloader::IsDynamicMethod(CORINFO_METHOD_HANDLE hMethod)
6816 STANDARD_VM_CONTRACT;
6818 MethodDesc* pMD = GetMethod(hMethod);
6822 return pMD->IsDynamicMethod();
6828 // Set method profiling flags for layout of EE datastructures
6829 void CEEPreloader::SetMethodProfilingFlags(CORINFO_METHOD_HANDLE hMethod, DWORD flags)
6831 STANDARD_VM_CONTRACT;
6833 _ASSERTE(hMethod != NULL);
6834 _ASSERTE(flags != 0);
6836 return m_image->SetMethodProfilingFlags(GetMethod(hMethod), flags);
6839 /*********************************************************************/
6840 // canSkipMethodPreparation: Is there a need for all calls from
6841 // NGEN'd code to a particular MethodDesc to go through DoPrestub,
6842 // depending on the method sematics? If so return FALSE.
6844 // This is used to rule out both ngen-hardbinds and intra-ngen-module
6847 // The cases where direct calls are not allowed are typically where
6848 // a stub must be inserted by DoPrestub (we do not save stubs) or where
6849 // we haven't saved the code for some reason or another, or where fixups
6850 // are required in the MethodDesc.
6852 // callerHnd=NULL implies any/unspecified caller.
6854 // Note that there may be other requirements for going through the prestub
6855 // which vary based on the scenario. These need to be handled separately
6857 bool CEEPreloader::CanSkipMethodPreparation (
6858 CORINFO_METHOD_HANDLE callerHnd,
6859 CORINFO_METHOD_HANDLE calleeHnd,
6860 CorInfoIndirectCallReason *pReason,
6861 CORINFO_ACCESS_FLAGS accessFlags/*=CORINFO_ACCESS_ANY*/)
6863 STANDARD_VM_CONTRACT;
6865 bool result = false;
6867 COOPERATIVE_TRANSITION_BEGIN();
6869 MethodDesc * calleeMD = (MethodDesc *)calleeHnd;
6870 MethodDesc * callerMD = (MethodDesc *)callerHnd;
6873 result = calleeMD->CanSkipDoPrestub(callerMD, pReason, accessFlags);
6876 COOPERATIVE_TRANSITION_END();
6881 CORINFO_METHOD_HANDLE CEEPreloader::LookupMethodDef(mdMethodDef token)
6883 STANDARD_VM_CONTRACT;
6885 MethodDesc *pMD = MemberLoader::GetMethodDescFromMethodDef(
6886 m_image->GetModule(),
6890 if (IsReadyToRunCompilation() && pMD->HasClassOrMethodInstantiation())
6892 _ASSERTE(IsCompilationProcess() && pMD->GetModule_NoLogging() == GetAppDomain()->ToCompilationDomain()->GetTargetModule());
6895 pMD = pMD->FindOrCreateTypicalSharedInstantiation();
6897 return CORINFO_METHOD_HANDLE(pMD);
6900 CorCompileILRegion CEEPreloader::GetILRegion(mdMethodDef token)
6902 STANDARD_VM_CONTRACT;
6904 // Since we are running managed code during NGen the inlining hint may be
6905 // changing underneeth us as the code is JITed. We need to prevent the inlining
6906 // hints from changing once we start to use them to place IL in the image.
6907 g_pCEECompileInfo->DisableCachingOfInliningHints();
6909 // Default if there is something completely wrong, e.g. the type failed to load.
6910 // We may need the IL at runtime.
6911 CorCompileILRegion region = CORCOMPILE_ILREGION_WARM;
6915 MethodDesc *pMD = m_image->GetModule()->LookupMethodDef(token);
6917 if (pMD == NULL || !pMD->GetMethodTable()->IsFullyLoaded())
6919 // Something is completely wrong - use the default
6922 if (m_image->IsStored(pMD))
6924 if (pMD->IsNotInline())
6926 if (pMD->HasClassOrMethodInstantiation())
6928 region = CORCOMPILE_ILREGION_GENERICS;
6932 region = CORCOMPILE_ILREGION_COLD;
6936 if (Security::MethodIsVisibleOutsideItsAssembly(pMD))
6938 // We are inlining only leaf methods, except for mscorlib. Thus we can assume that only methods
6939 // visible outside its assembly are likely to be inlined.
6940 region = CORCOMPILE_ILREGION_INLINEABLE;
6944 // We may still need the IL of the non-nonvisible methods for inlining in certain scenarios:
6945 // dynamically emitted IL, friend assemblies or JITing of generic instantiations
6946 region = CORCOMPILE_ILREGION_WARM;
6953 EX_END_CATCH(SwallowAllExceptions)
6958 CORINFO_CLASS_HANDLE CEEPreloader::FindTypeForProfileEntry(CORBBTPROF_BLOB_PARAM_SIG_ENTRY * profileBlobEntry)
6960 STANDARD_VM_CONTRACT;
6962 _ASSERTE(profileBlobEntry->blob.type == ParamTypeSpec);
6964 if (PartialNGenStressPercentage() != 0)
6965 return CORINFO_CLASS_HANDLE( NULL );
6967 Module * pModule = GetAppDomain()->ToCompilationDomain()->GetTargetModule();
6968 TypeHandle th = pModule->LoadIBCTypeHelper(profileBlobEntry);
6970 return CORINFO_CLASS_HANDLE(th.AsPtr());
6973 CORINFO_METHOD_HANDLE CEEPreloader::FindMethodForProfileEntry(CORBBTPROF_BLOB_PARAM_SIG_ENTRY * profileBlobEntry)
6975 STANDARD_VM_CONTRACT;
6977 _ASSERTE(profileBlobEntry->blob.type == ParamMethodSpec);
6979 if (PartialNGenStressPercentage() != 0)
6980 return CORINFO_METHOD_HANDLE( NULL );
6982 Module * pModule = GetAppDomain()->ToCompilationDomain()->GetTargetModule();
6983 MethodDesc * pMethod = pModule->LoadIBCMethodHelper(profileBlobEntry);
6985 return CORINFO_METHOD_HANDLE( pMethod );
6988 void CEEPreloader::ReportInlining(CORINFO_METHOD_HANDLE inliner, CORINFO_METHOD_HANDLE inlinee)
6990 STANDARD_VM_CONTRACT;
6991 m_image->ReportInlining(inliner, inlinee);
6994 void CEEPreloader::Link()
6996 STANDARD_VM_CONTRACT;
6998 COOPERATIVE_TRANSITION_BEGIN();
7002 m_image->GetModule()->Save(m_image);
7003 m_image->GetModule()->Arrange(m_image);
7004 m_image->GetModule()->Fixup(m_image);
7006 m_image->PostSave();
7008 COOPERATIVE_TRANSITION_END();
7011 void CEEPreloader::FixupRVAs()
7013 STANDARD_VM_CONTRACT;
7015 COOPERATIVE_TRANSITION_BEGIN();
7017 m_image->FixupRVAs();
7019 COOPERATIVE_TRANSITION_END();
7022 void CEEPreloader::SetRVAsForFields(IMetaDataEmit * pEmit)
7024 STANDARD_VM_CONTRACT;
7026 COOPERATIVE_TRANSITION_BEGIN();
7028 m_image->SetRVAsForFields(pEmit);
7030 COOPERATIVE_TRANSITION_END();
7033 void CEEPreloader::GetRVAFieldData(mdFieldDef fd, PVOID * ppData, DWORD * pcbSize, DWORD * pcbAlignment)
7035 STANDARD_VM_CONTRACT;
7037 COOPERATIVE_TRANSITION_BEGIN();
7039 FieldDesc * pFD = m_image->GetModule()->LookupFieldDef(fd);
7041 ThrowHR(COR_E_TYPELOAD);
7043 _ASSERTE(pFD->IsILOnlyRVAField());
7045 UINT size = pFD->LoadSize();
7048 // Compute an alignment for the data based on the alignment
7049 // of the RVA. We'll align up to 8 bytes.
7053 DWORD rva = pFD->GetOffset();
7054 DWORD rvaTemp = rva;
7056 while ((rvaTemp&1) == 0 && align < 8 && align < size)
7063 *ppData = pFD->GetStaticAddressHandle(NULL);
7065 *pcbAlignment = align;
7067 COOPERATIVE_TRANSITION_END();
7070 ULONG CEEPreloader::Release()
7082 void CEEPreloader::Error(mdToken token, Exception * pException)
7084 STANDARD_VM_CONTRACT;
7088 #ifdef CROSSGEN_COMPILE
7089 pException->GetMessage(msg);
7094 // Going though throwable gives more verbose error messages in certain cases that our tests depend on.
7095 OBJECTREF throwable = NingenEnabled() ? NULL : CLRException::GetThrowableFromException(pException);
7097 if (throwable != NULL)
7099 GetExceptionMessage(throwable, msg);
7103 pException->GetMessage(msg);
7108 m_pData->Error(token, pException->GetHR(), msg.GetUnicode());
7111 CEEInfo *g_pCEEInfo = NULL;
7113 ICorDynamicInfo * __stdcall GetZapJitInfo()
7115 STANDARD_VM_CONTRACT;
7117 if (g_pCEEInfo == NULL)
7119 CEEInfo * p = new CEEInfo();
7120 if (InterlockedCompareExchangeT(&g_pCEEInfo, p, NULL) != NULL)
7127 CEECompileInfo *g_pCEECompileInfo = NULL;
7129 ICorCompileInfo * __stdcall GetCompileInfo()
7131 STANDARD_VM_CONTRACT;
7133 if (g_pCEECompileInfo == NULL)
7135 CEECompileInfo * p = new CEECompileInfo();
7136 if (InterlockedCompareExchangeT(&g_pCEECompileInfo, p, NULL) != NULL)
7140 return g_pCEECompileInfo;
7144 // CompilationDomain
7147 CompilationDomain::CompilationDomain(BOOL fForceDebug,
7148 BOOL fForceProfiling,
7149 BOOL fForceInstrument)
7150 : m_fForceDebug(fForceDebug),
7151 m_fForceProfiling(fForceProfiling),
7152 m_fForceInstrument(fForceInstrument),
7153 m_pTargetAssembly(NULL),
7154 m_pTargetModule(NULL),
7155 m_pTargetImage(NULL),
7157 m_pDependencyRefSpecs(NULL),
7158 m_pDependencies(NULL),
7159 m_cDependenciesCount(0),
7160 m_cDependenciesAlloc(0)
7162 STANDARD_VM_CONTRACT;
7166 void CompilationDomain::ReleaseDependencyEmitter()
7168 m_pDependencyRefSpecs.Release();
7173 CompilationDomain::~CompilationDomain()
7183 if (m_pDependencies != NULL)
7184 delete [] m_pDependencies;
7186 ReleaseDependencyEmitter();
7188 for (unsigned i = 0; i < m_rRefCaches.Size(); i++)
7190 delete m_rRefCaches[i];
7191 m_rRefCaches[i]=NULL;
7196 void CompilationDomain::Init()
7198 STANDARD_VM_CONTRACT;
7200 #ifndef CROSSGEN_COMPILE
7204 #ifndef CROSSGEN_COMPILE
7205 // allocate a Virtual Call Stub Manager for the compilation domain
7209 Security::SetDefaultAppDomainProperty(GetSecurityDescriptor());
7210 SetCompilationDomain();
7214 g_pConfig->DisableGenerateStubForHost();
7218 HRESULT CompilationDomain::AddDependencyEntry(PEAssembly *pFile,
7223 // This method is not multi-thread safe. This is OK because it is only called by NGen compiling, which is
7224 // effectively single-threaded. The following code verifies that we're not called on multiple threads.
7225 static volatile LONG threadId = 0;
7228 InterlockedCompareExchange(&threadId, GetCurrentThreadId(), 0);
7230 _ASSERTE((LONG)GetCurrentThreadId() == threadId);
7233 _ASSERTE((pFile == NULL) == (def == mdAssemblyRefNil));
7235 if (m_cDependenciesCount == m_cDependenciesAlloc)
7237 // Save the new count in a local variable. Can't update m_cDependenciesAlloc until the new
7238 // CORCOMPILE_DEPENDENCY array is allocated, otherwise an out-of-memory exception from new[]
7239 // operator would put the data in an inconsistent state, causing heap corruption later.
7240 USHORT cNewDependenciesAlloc = m_cDependenciesAlloc == 0 ? 20 : m_cDependenciesAlloc * 2;
7242 // Grow m_pDependencies
7244 NewArrayHolder<CORCOMPILE_DEPENDENCY> pNewDependencies(new CORCOMPILE_DEPENDENCY[cNewDependenciesAlloc]);
7246 // This block must execute transactionally. No throwing allowed. No bailing allowed.
7249 memset(pNewDependencies, 0, cNewDependenciesAlloc*sizeof(CORCOMPILE_DEPENDENCY));
7251 if (m_pDependencies)
7253 memcpy(pNewDependencies, m_pDependencies,
7254 m_cDependenciesCount*sizeof(CORCOMPILE_DEPENDENCY));
7256 delete [] m_pDependencies;
7259 m_pDependencies = pNewDependencies.Extract();
7260 m_cDependenciesAlloc = cNewDependenciesAlloc;
7264 CORCOMPILE_DEPENDENCY *pDependency = &m_pDependencies[m_cDependenciesCount++];
7266 // Clear memory so that we won't write random data into the zapped file
7267 ZeroMemory(pDependency, sizeof(CORCOMPILE_DEPENDENCY));
7269 pDependency->dwAssemblyRef = ref;
7271 pDependency->dwAssemblyDef = def;
7273 pDependency->signNativeImage = INVALID_NGEN_SIGNATURE;
7277 DomainAssembly *pAssembly = GetAppDomain()->LoadDomainAssembly(NULL, pFile, FILE_LOAD_CREATE, NULL);
7278 // Note that this can trigger an assembly load (of mscorlib)
7279 pAssembly->GetOptimizedIdentitySignature(&pDependency->signAssemblyDef);
7284 // This is done in CompilationDomain::CanEagerBindToZapFile with full support for hardbinding
7286 if (pFile->IsSystem() && pFile->HasNativeImage())
7288 CORCOMPILE_VERSION_INFO * pNativeVersion = pFile->GetLoadedNative()->GetNativeVersionInfo();
7289 pDependency->signNativeImage = pNativeVersion->signature;
7297 HRESULT CompilationDomain::AddDependency(AssemblySpec *pRefSpec,
7303 // Record the dependency
7306 // This assert prevents dependencies from silently being loaded without being recorded.
7309 // Normalize any reference to mscorlib; we don't want to record other non-canonical
7310 // mscorlib references in the ngen image since fusion doesn't understand how to bind them.
7311 // (Not to mention the fact that they are redundant.)
7313 if (pRefSpec->IsMscorlib())
7315 _ASSERTE(pFile); // mscorlib had better not be missing
7317 return E_UNEXPECTED;
7319 // Don't store a binding from mscorlib to itself.
7320 if (m_pTargetAssembly == SystemDomain::SystemAssembly())
7323 spec.InitializeSpec(pFile);
7326 else if (m_pTargetAssembly == NULL && pFile)
7328 // If target assembly is still NULL, we must be loading either the target assembly or mscorlib.
7329 // Mscorlib is already handled above, so we must be loading the target assembly if we get here.
7330 // Use the assembly name given in the target assembly so that the native image is deterministic
7331 // regardless of how the target assembly is specified on the command line.
7332 spec.InitializeSpec(pFile);
7333 if (spec.IsStrongNamed() && spec.HasPublicKey())
7335 spec.ConvertPublicKeyToToken();
7339 else if (pRefSpec->IsStrongNamed() && pRefSpec->HasPublicKey())
7341 // Normalize to always use public key token. Otherwise we may insert one reference
7342 // using public key, and another reference using public key token.
7343 spec.CopyFrom(pRefSpec);
7344 spec.ConvertPublicKeyToToken();
7348 #ifdef FEATURE_COMINTEROP
7349 // Only cache ref specs that have a unique identity. This is needed to avoid caching
7350 // things like WinRT type specs, which would benefit very little from being cached.
7351 if (!pRefSpec->HasUniqueIdentity())
7353 // Successful bind of a reference with a non-unique assembly identity.
7354 _ASSERTE(pRefSpec->IsContentType_WindowsRuntime());
7356 AssemblySpec defSpec;
7359 defSpec.InitializeSpec(pFile);
7361 // Windows Runtime Native Image binding depends on details exclusively described by the definition winmd file.
7362 // Therefore we can actually drop the existing ref spec here entirely.
7363 // Also, Windows Runtime Native Image binding uses the simple name of the ref spec as the
7364 // resolution rule for PreBind when finding definition assemblies.
7365 // See comment on CLRPrivBinderWinRT::PreBind for further details.
7366 pRefSpec = &defSpec;
7369 // Unfortunately, we don't have any choice regarding failures (pFile == NULL) because
7370 // there is no value to canonicalize on (i.e., a def spec created from a non-NULL
7371 // pFile) and so we must cache all non-unique-assembly-id failures.
7372 const AssemblySpecDefRefMapEntry * pEntry = m_dependencyDefRefMap.LookupPtr(&defSpec);
7373 if (pFile == NULL || pEntry == NULL)
7375 mdAssemblyRef refToken = mdAssemblyRefNil;
7376 IfFailRet(pRefSpec->EmitToken(m_pEmit, &refToken, TRUE, TRUE));
7378 mdAssemblyRef defToken = mdAssemblyRefNil;
7381 IfFailRet(defSpec.EmitToken(m_pEmit, &defToken, TRUE, TRUE));
7383 NewHolder<AssemblySpec> pNewDefSpec = new AssemblySpec();
7384 pNewDefSpec->CopyFrom(&defSpec);
7385 pNewDefSpec->CloneFields();
7387 NewHolder<AssemblySpec> pNewRefSpec = new AssemblySpec();
7388 pNewRefSpec->CopyFrom(pRefSpec);
7389 pNewRefSpec->CloneFields();
7391 _ASSERTE(m_dependencyDefRefMap.LookupPtr(pNewDefSpec) == NULL);
7393 AssemblySpecDefRefMapEntry e;
7394 e.m_pDef = pNewDefSpec;
7395 e.m_pRef = pNewRefSpec;
7396 m_dependencyDefRefMap.Add(e);
7398 pNewDefSpec.SuppressRelease();
7399 pNewRefSpec.SuppressRelease();
7402 IfFailRet(AddDependencyEntry(pFile, refToken, defToken));
7406 #endif // FEATURE_COMINTEROP
7409 // See if we've already added the contents of the ref
7410 // Else, emit token for the ref
7413 if (m_pDependencyRefSpecs->Store(pRefSpec))
7416 mdAssemblyRef refToken;
7417 IfFailRet(pRefSpec->EmitToken(m_pEmit, &refToken));
7420 // Make a spec for the bound assembly
7423 mdAssemblyRef defToken = mdAssemblyRefNil;
7425 // All dependencies of a shared assembly need to be shared. So for a shared
7426 // assembly, we want to remember the missing assembly ref during ngen, so that
7427 // we can probe eagerly for the dependency at load time, and make sure that
7428 // it is loaded as shared.
7429 // In such a case, pFile will be NULL
7432 AssemblySpec assemblySpec;
7433 assemblySpec.InitializeSpec(pFile);
7435 IfFailRet(assemblySpec.EmitToken(m_pEmit, &defToken));
7439 // Add the entry. Include the PEFile if we are not doing explicit bindings.
7442 IfFailRet(AddDependencyEntry(pFile, refToken, defToken));
7448 //----------------------------------------------------------------------------
7449 AssemblySpec* CompilationDomain::FindAssemblyRefSpecForDefSpec(
7450 AssemblySpec* pDefSpec)
7452 WRAPPER_NO_CONTRACT;
7454 if (pDefSpec == nullptr)
7457 const AssemblySpecDefRefMapEntry * pEntry = m_dependencyDefRefMap.LookupPtr(pDefSpec);
7458 _ASSERTE(pEntry != NULL);
7460 return (pEntry != NULL) ? pEntry->m_pRef : NULL;
7464 //----------------------------------------------------------------------------
7465 // Is it OK to embed direct pointers to an ngen dependency?
7466 // true if hardbinding is OK, false otherwise
7468 // targetModule - The pointer points into the native image of this Module.
7469 // If this native image gets relocated, the native image of
7470 // the source Module is invalidated unless the embedded
7471 // pointer can be fixed up appropriately.
7472 // limitToHardBindList - Is it OK to hard-bind to a dependency even if it is
7473 // not asked for explicitly?
7475 BOOL CompilationDomain::CanEagerBindToZapFile(Module *targetModule, BOOL limitToHardBindList)
7477 // We do this check before checking the hashtables because m_cantHardBindModules
7478 // will contain non-manifest modules. However, we do want them to be able
7479 // to hard-bind to themselves
7480 if (targetModule == m_pTargetModule)
7486 // CoreCLR does not have attributes for fine grained eager binding control.
7487 // We hard bind to mscorlib.dll only.
7489 return targetModule->IsSystem();
7493 void CompilationDomain::SetTarget(Assembly *pAssembly, Module *pModule)
7495 STANDARD_VM_CONTRACT;
7497 m_pTargetAssembly = pAssembly;
7498 m_pTargetModule = pModule;
7501 void CompilationDomain::SetTargetImage(DataImage *pImage, CEEPreloader * pPreloader)
7503 STANDARD_VM_CONTRACT;
7505 m_pTargetImage = pImage;
7506 m_pTargetPreloader = pPreloader;
7508 _ASSERTE(pImage->GetModule() == GetTargetModule());
7511 void ReportMissingDependency(Exception * e)
7513 // Avoid duplicate error messages
7514 if (FAILED(g_hrFatalError))
7520 GetSvcLogger()->Printf(LogLevel_Error, W("Error: %s\n"), s.GetUnicode());
7522 g_hrFatalError = COR_E_FILELOAD;
7525 PEAssembly *CompilationDomain::BindAssemblySpec(
7526 AssemblySpec *pSpec,
7527 BOOL fThrowOnFileNotFound,
7528 BOOL fRaisePrebindEvents,
7529 StackCrawlMark *pCallerStackMark,
7530 AssemblyLoadSecurity *pLoadSecurity,
7531 BOOL fUseHostBinderIfAvailable)
7533 PEAssembly *pFile = NULL;
7541 // Use normal binding rules
7542 // (possibly with our custom IApplicationContext)
7544 pFile = AppDomain::BindAssemblySpec(
7546 fThrowOnFileNotFound,
7547 fRaisePrebindEvents,
7550 fUseHostBinderIfAvailable);
7554 if (!g_fNGenMissingDependenciesOk)
7556 ReportMissingDependency(GET_EXCEPTION());
7561 // Record missing dependencies
7563 #ifdef FEATURE_COMINTEROP
7564 if (!g_fNGenWinMDResilient || pSpec->HasUniqueIdentity())
7567 IfFailThrow(AddDependency(pSpec, NULL));
7572 #ifdef FEATURE_COMINTEROP
7573 if (!g_fNGenWinMDResilient || pSpec->HasUniqueIdentity())
7576 IfFailThrow(AddDependency(pSpec, pFile));
7583 CompilationDomain::SetContextInfo(LPCWSTR path, BOOL isExe)
7585 STANDARD_VM_CONTRACT;
7589 COOPERATIVE_TRANSITION_BEGIN();
7591 #ifdef FEATURE_FUSION
7594 if (NingenEnabled())
7596 WCHAR buf[MAX_LONGPATH + sizeof(CONFIGURATION_EXTENSION)/sizeof(WCHAR) + 1];
7597 if (0 != wcscpy_s(buf, sizeof(buf)/sizeof(*buf), path))
7599 COMPlusThrowHR(COR_E_PATHTOOLONG);
7601 WCHAR *pSlash = wcsrchr(buf, W('\\'));
7604 COMPlusThrowHR(COR_E_BAD_PATHNAME);
7607 *(pSlash + 1) = W('\0');
7608 hr = m_pFusionContext->Set(ACTAG_APP_BASE_URL, buf, (DWORD)((wcslen(buf) + 1) * sizeof(WCHAR)), 0);
7614 if (0 != wcscpy_s(buf, sizeof(buf)/sizeof(*buf), path + (pSlash - buf) + 1))
7616 COMPlusThrowHR(COR_E_PATHTOOLONG);
7619 if (0 != wcscat_s(buf, sizeof(buf)/sizeof(*buf), CONFIGURATION_EXTENSION))
7621 COMPlusThrowHR(COR_E_PATHTOOLONG);
7623 hr = m_pFusionContext->Set(ACTAG_APP_CONFIG_FILE, buf, (DWORD)((wcslen(buf) + 1) * sizeof(WCHAR)), 0);
7631 SetupExecutableFusionContext(path);
7636 hr = m_pFusionContext->Set(ACTAG_APP_BASE_URL,
7637 (void*) path, (DWORD) ((wcslen(path)+1) * sizeof(WCHAR)),
7640 #endif //FEATURE_FUSION
7642 COOPERATIVE_TRANSITION_END();
7647 void CompilationDomain::SetDependencyEmitter(IMetaDataAssemblyEmit *pEmit)
7649 STANDARD_VM_CONTRACT;
7654 m_pDependencyRefSpecs = new AssemblySpecHash();
7659 CompilationDomain::GetDependencies(CORCOMPILE_DEPENDENCY **ppDependencies,
7660 DWORD *pcDependencies)
7662 STANDARD_VM_CONTRACT;
7666 // Return the bindings.
7669 *ppDependencies = m_pDependencies;
7670 *pcDependencies = m_cDependenciesCount;
7672 // Cannot add any more dependencies
7673 ReleaseDependencyEmitter();
7678 #ifdef FEATURE_FUSION
7680 CompilationDomain::GetIBindContext(IBindContext **ppBindCtx)
7682 LIMITED_METHOD_CONTRACT;
7685 ReleaseHolder<IBindContext> pBindCtx;
7686 if (HasLoadContextHostBinder())
7688 IfFailRet(GetCurrentLoadContextHostBinder()->QueryInterface(__uuidof(IBindContext), &pBindCtx));
7692 GetBindContextFromApplicationContext(BaseDomain::GetFusionContext(), &pBindCtx); // Can't fail
7695 *ppBindCtx = pBindCtx.Extract();
7700 #ifdef CROSSGEN_COMPILE
7701 HRESULT CompilationDomain::SetPlatformWinmdPaths(LPCWSTR pwzPlatformWinmdPaths)
7703 STANDARD_VM_CONTRACT;
7705 #ifdef FEATURE_COMINTEROP
7706 // Create the array list on the heap since it will be passed off for the Crossgen RoResolveNamespace mockup to keep for the life of the process
7707 StringArrayList *saPaths = new StringArrayList();
7709 SString strPaths(pwzPlatformWinmdPaths);
7710 if (!strPaths.IsEmpty())
7712 for (SString::Iterator i = strPaths.Begin(); i != strPaths.End(); )
7714 // Skip any leading spaces or semicolons
7715 if (strPaths.Skip(i, W(';')))
7720 SString::Iterator iEnd = i; // Where current assembly name ends
7721 SString::Iterator iNext; // Where next assembly name starts
7722 if (strPaths.Find(iEnd, W(';')))
7728 iNext = iEnd = strPaths.End();
7734 saPaths->Append(SString(strPaths, i, iEnd));
7739 Crossgen::SetFirstPartyWinMDPaths(saPaths);
7740 #endif // FEATURE_COMINTEROP
7744 #endif // CROSSGEN_COMPILE
7747 #endif // FEATURE_PREJIT