1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5
6#include "common.h"
7
8#include "coregen.h"
9
10#include "clr/fs/dir.h"
11
12#pragma warning(push)
13#pragma warning(disable: 4995)
14#include "shlwapi.h"
15#pragma warning(pop)
16
17extern const WCHAR g_pwBaseLibrary[];
18extern bool g_fAllowNativeImages;
19bool g_fNGenMissingDependenciesOk;
20bool g_fNGenWinMDResilient;
21
22#ifdef FEATURE_READYTORUN_COMPILER
23bool g_fReadyToRunCompilation;
24#endif
25
26static bool s_fNGenNoMetaData;
27
28/* --------------------------------------------------------------------------- *
29 * Public entry points for ngen
30 * --------------------------------------------------------------------------- */
31// For side by side issues, it's best to use the exported API calls to generate a
32// Zapper Object instead of creating one on your own.
33
34
35STDAPI NGenWorker(LPCWSTR pwzFilename, DWORD dwFlags, LPCWSTR pwzPlatformAssembliesPaths, LPCWSTR pwzTrustedPlatformAssemblies, LPCWSTR pwzPlatformResourceRoots, LPCWSTR pwzAppPaths, LPCWSTR pwzOutputFilename=NULL, LPCWSTR pwzPlatformWinmdPaths=NULL, ICorSvcLogger *pLogger = NULL, LPCWSTR pwszCLRJITPath = nullptr)
36{
37 HRESULT hr = S_OK;
38
39 BEGIN_ENTRYPOINT_NOTHROW;
40
41 Zapper* zap = NULL;
42
43 EX_TRY
44 {
45 NGenOptions ngo = {0};
46 ngo.dwSize = sizeof(NGenOptions);
47
48 // V1
49
50 ngo.fDebug = false;
51 ngo.fDebugOpt = false;
52 ngo.fProf = false;
53 ngo.fSilent = (dwFlags & NGENWORKER_FLAGS_SILENT) != 0;
54 ngo.lpszExecutableFileName = pwzFilename;
55
56 // V2 (Whidbey)
57
58 ngo.fInstrument = !!(dwFlags & NGENWORKER_FLAGS_TUNING);
59 ngo.fWholeProgram = false;
60 ngo.fProfInfo = false;
61
62 ngo.lpszRepositoryDir = NULL;
63 ngo.repositoryFlags = RepositoryDefault;
64
65 ngo.dtRequested = DT_NIL;
66 ngo.lpszDebugDir = NULL;
67
68 ngo.fNoInstall = false;
69
70 ngo.fEmitFixups = false;
71 ngo.fFatHeaders = false;
72
73 ngo.fVerbose = (dwFlags & NGENWORKER_FLAGS_VERBOSE) != 0;
74 ngo.uStats = false;
75
76 ngo.fNgenLastRetry = false;
77
78 s_fNGenNoMetaData = (dwFlags & NGENWORKER_FLAGS_NO_METADATA) != 0;
79
80 zap = Zapper::NewZapper(&ngo);
81
82 if (pwzOutputFilename)
83 zap->SetOutputFilename(pwzOutputFilename);
84
85 if (pwzPlatformAssembliesPaths != nullptr)
86 zap->SetPlatformAssembliesPaths(pwzPlatformAssembliesPaths);
87
88 if (pwzTrustedPlatformAssemblies != nullptr)
89 zap->SetTrustedPlatformAssemblies(pwzTrustedPlatformAssemblies);
90
91 if (pwzPlatformResourceRoots != nullptr)
92 zap->SetPlatformResourceRoots(pwzPlatformResourceRoots);
93
94 if (pwzAppPaths != nullptr)
95 zap->SetAppPaths(pwzAppPaths);
96
97 if (pwzPlatformWinmdPaths != nullptr)
98 zap->SetPlatformWinmdPaths(pwzPlatformWinmdPaths);
99
100#if !defined(FEATURE_MERGE_JIT_AND_ENGINE)
101 if (pwszCLRJITPath != nullptr)
102 zap->SetCLRJITPath(pwszCLRJITPath);
103#endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE)
104
105 g_fNGenMissingDependenciesOk = !!(dwFlags & NGENWORKER_FLAGS_MISSINGDEPENDENCIESOK);
106
107#ifdef FEATURE_WINMD_RESILIENT
108 g_fNGenWinMDResilient = !!(dwFlags & NGENWORKER_FLAGS_WINMD_RESILIENT);
109#endif
110
111#ifdef FEATURE_READYTORUN_COMPILER
112 g_fReadyToRunCompilation = !!(dwFlags & NGENWORKER_FLAGS_READYTORUN);
113#endif
114
115 if (pLogger != NULL)
116 {
117 GetSvcLogger()->SetSvcLogger(pLogger);
118 }
119
120 hr = zap->Compile(pwzFilename);
121 }
122 EX_CATCH_HRESULT(hr);
123
124 END_ENTRYPOINT_NOTHROW;
125
126 return hr;
127}
128
129STDAPI CreatePDBWorker(LPCWSTR pwzAssemblyPath, LPCWSTR pwzPlatformAssembliesPaths, LPCWSTR pwzTrustedPlatformAssemblies, LPCWSTR pwzPlatformResourceRoots, LPCWSTR pwzAppPaths, LPCWSTR pwzAppNiPaths, LPCWSTR pwzPdbPath, BOOL fGeneratePDBLinesInfo, LPCWSTR pwzManagedPdbSearchPath, LPCWSTR pwzPlatformWinmdPaths, LPCWSTR pwzDiasymreaderPath)
130{
131 HRESULT hr = S_OK;
132
133 BEGIN_ENTRYPOINT_NOTHROW;
134
135 Zapper* zap = NULL;
136
137 EX_TRY
138 {
139 GetCompileInfo()->SetIsGeneratingNgenPDB(TRUE);
140
141 NGenOptions ngo = {0};
142 ngo.dwSize = sizeof(NGenOptions);
143
144 zap = Zapper::NewZapper(&ngo);
145
146#if !defined(FEATURE_MERGE_JIT_AND_ENGINE)
147 zap->SetDontLoadJit();
148#endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE)
149
150 if (pwzPlatformAssembliesPaths != nullptr)
151 zap->SetPlatformAssembliesPaths(pwzPlatformAssembliesPaths);
152
153 if (pwzTrustedPlatformAssemblies != nullptr)
154 zap->SetTrustedPlatformAssemblies(pwzTrustedPlatformAssemblies);
155
156 if (pwzPlatformResourceRoots != nullptr)
157 zap->SetPlatformResourceRoots(pwzPlatformResourceRoots);
158
159 if (pwzAppPaths != nullptr)
160 zap->SetAppPaths(pwzAppPaths);
161
162 if (pwzAppNiPaths != nullptr)
163 zap->SetAppNiPaths(pwzAppNiPaths);
164
165 if (pwzPlatformWinmdPaths != nullptr)
166 zap->SetPlatformWinmdPaths(pwzPlatformWinmdPaths);
167
168#if !defined(NO_NGENPDB)
169 if (pwzDiasymreaderPath != nullptr)
170 zap->SetDiasymreaderPath(pwzDiasymreaderPath);
171#endif // !defined(NO_NGENPDB)
172
173 BSTRHolder strAssemblyPath(::SysAllocString(pwzAssemblyPath));
174 BSTRHolder strPdbPath(::SysAllocString(pwzPdbPath));
175 BSTRHolder strManagedPdbSearchPath(::SysAllocString(pwzManagedPdbSearchPath));
176
177 // Zapper::CreatePdb is shared code for both desktop NGEN PDBs and coreclr
178 // crossgen PDBs.
179 zap->CreatePdb(
180 strAssemblyPath,
181
182 // On desktop with fusion AND on the phone, the various binders always expect
183 // the native image to be specified here.
184 strAssemblyPath,
185
186 strPdbPath,
187 fGeneratePDBLinesInfo,
188 strManagedPdbSearchPath);
189 }
190 EX_CATCH_HRESULT(hr);
191
192 END_ENTRYPOINT_NOTHROW;
193
194 return hr;
195}
196
197
198/* --------------------------------------------------------------------------- *
199 * Options class
200 * --------------------------------------------------------------------------- */
201
202ZapperOptions::ZapperOptions() :
203 m_repositoryDir(NULL),
204 m_repositoryFlags(RepositoryDefault),
205 m_autodebug(false),
206 m_onlyOneMethod(0),
207 m_silent(true),
208 m_verbose(false),
209 m_ignoreErrors(true),
210 m_statOptions(0),
211 m_ngenProfileImage(false),
212 m_ignoreProfileData(false),
213 m_noProcedureSplitting(false),
214 m_fHasAnyProfileData(false),
215 m_fPartialNGen(false),
216 m_fPartialNGenSet(false),
217 m_fNGenLastRetry(false),
218 m_compilerFlags(),
219 m_fNoMetaData(s_fNGenNoMetaData)
220{
221 SetCompilerFlags();
222
223 m_zapSet = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_ZapSet);
224 if (m_zapSet != NULL && wcslen(m_zapSet) > 3)
225 {
226 delete [] m_zapSet;
227 m_zapSet = NULL;
228 }
229
230 if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_DisableIBC))
231 {
232 m_ignoreProfileData = true;
233 }
234
235 if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_NoProcedureSplitting))
236 {
237 m_noProcedureSplitting = true;
238 }
239
240 DWORD partialNGen = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_PartialNGen);
241 if (partialNGen != (DWORD)(-1))
242 {
243 m_fPartialNGenSet = true;
244 m_fPartialNGen = partialNGen != 0;
245 }
246
247#ifdef _DEBUG
248 m_onlyOneMethod = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_NGenOnlyOneMethod);
249#endif
250}
251
252ZapperOptions::~ZapperOptions()
253{
254 if (m_zapSet != NULL)
255 delete [] m_zapSet;
256
257 if (m_repositoryDir != NULL)
258 delete [] m_repositoryDir;
259}
260
261void ZapperOptions::SetCompilerFlags(void)
262{
263 m_compilerFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_RELOC);
264 m_compilerFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_PREJIT);
265
266#if defined(_TARGET_ARM_)
267# if defined(PLATFORM_UNIX)
268 m_compilerFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_RELATIVE_CODE_RELOCS);
269# endif // defined(PLATFORM_UNIX)
270#endif // defined(_TARGET_ARM_)
271}
272
273/* --------------------------------------------------------------------------- *
274 * Zapper class
275 * --------------------------------------------------------------------------- */
276
277Zapper::Zapper(NGenOptions *pOptions, bool fromDllHost)
278{
279 ZapperOptions *zo = new ZapperOptions();
280
281 // We can version NGenOptions by looking at the dwSize variable
282
283 NGenOptions currentVersionOptions;
284 memcpy(&currentVersionOptions, pOptions, pOptions->dwSize);
285
286 if (pOptions->dwSize < sizeof(currentVersionOptions))
287 {
288 // Clear out the memory. This is redundant with the explicit
289 // initializations below, but is meant to be a backup.
290
291 ZeroMemory(PBYTE(&currentVersionOptions) + pOptions->dwSize,
292 sizeof(currentVersionOptions) - pOptions->dwSize);
293
294 // Initialize all the fields added in the current version
295
296 currentVersionOptions.fInstrument = false;
297 currentVersionOptions.fWholeProgram = false;
298 currentVersionOptions.fProfInfo = false;
299
300 currentVersionOptions.dtRequested = DT_NIL;
301 currentVersionOptions.lpszDebugDir = NULL;
302
303 currentVersionOptions.lpszRepositoryDir = NULL;
304 currentVersionOptions.repositoryFlags = RepositoryDefault;
305
306 currentVersionOptions.fNoInstall = false;
307
308 currentVersionOptions.fEmitFixups = false;
309 currentVersionOptions.fFatHeaders = false;
310
311 currentVersionOptions.fVerbose = false;
312 currentVersionOptions.uStats = 0;
313
314 currentVersionOptions.fNgenLastRetry = false;
315
316 currentVersionOptions.fAutoNGen = false;
317
318 currentVersionOptions.fRepositoryOnly = false;
319 }
320
321 pOptions = &currentVersionOptions;
322
323 zo->m_compilerFlags.Reset();
324 zo->SetCompilerFlags();
325 zo->m_autodebug = true;
326
327 if (pOptions->fDebug)
328 {
329 zo->m_compilerFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_INFO);
330 zo->m_compilerFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_CODE);
331 zo->m_autodebug = false;
332 }
333
334 if (pOptions->fProf)
335 {
336 zo->m_compilerFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_PROF_ENTERLEAVE);
337 }
338
339
340 if (pOptions->fInstrument)
341 zo->m_compilerFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR);
342
343 zo->m_verbose = pOptions->fVerbose;
344 zo->m_statOptions = pOptions->uStats;
345
346 zo->m_silent = pOptions->fSilent;
347
348 if (pOptions->lpszExecutableFileName != NULL)
349 {
350 m_exeName.Set(pOptions->lpszExecutableFileName);
351 }
352
353 if (pOptions->fNgenLastRetry)
354 {
355 zo->m_fNGenLastRetry = true;
356
357 //
358 // Things that we turn off when this is the last retry for ngen
359 //
360 zo->m_ignoreProfileData = true; // ignore any IBC profile data
361
362#ifdef _TARGET_ARM_
363 //
364 // On ARM, we retry compilation for large images when we hit overflow of IMAGE_REL_BASED_THUMB_BRANCH24 relocations.
365 // Disable procedure spliting for retry because of it depends on IMAGE_REL_BASED_THUMB_BRANCH24 relocations.
366 // See related code in code:ZapInfo::getRelocTypeHint
367 //
368 zo->m_noProcedureSplitting = true;
369#endif
370 }
371
372 zo->m_fAutoNGen = pOptions->fAutoNGen;
373
374 zo->m_fRepositoryOnly = pOptions->fRepositoryOnly;
375
376 m_fromDllHost = fromDllHost;
377
378 Init(zo, true);
379}
380
381
382Zapper::Zapper(ZapperOptions *pOptions)
383{
384 Init(pOptions);
385}
386
387void Zapper::Init(ZapperOptions *pOptions, bool fFreeZapperOptions)
388{
389 m_pEEJitInfo = NULL;
390 m_pEECompileInfo = NULL;
391 m_pJitCompiler = NULL;
392 m_pMetaDataDispenser = NULL;
393 m_hJitLib = NULL;
394#ifdef _TARGET_AMD64_
395 m_hJitLegacy = NULL;
396#endif
397
398 m_pOpt = pOptions;
399
400 m_pDomain = NULL;
401 m_hAssembly = NULL;
402 m_pAssemblyImport = NULL;
403 m_failed = FALSE;
404 m_currentRegionKind = CORINFO_REGION_NONE;
405
406 m_pAssemblyEmit = NULL;
407 m_fFreeZapperOptions = fFreeZapperOptions;
408
409#ifdef LOGGING
410 InitializeLogging();
411#endif
412
413 HRESULT hr;
414
415 //
416 // Get metadata dispenser interface
417 //
418
419 IfFailThrow(MetaDataGetDispenser(CLSID_CorMetaDataDispenser,
420 IID_IMetaDataDispenserEx, (void **)&m_pMetaDataDispenser));
421
422 //
423 // Make sure we don't duplicate assembly refs and file refs
424 //
425
426 VARIANT opt;
427 hr = m_pMetaDataDispenser->GetOption(MetaDataCheckDuplicatesFor, &opt);
428 _ASSERTE(SUCCEEDED(hr));
429
430 _ASSERTE(V_VT(&opt) == VT_UI4);
431 V_UI4(&opt) |= MDDupAssemblyRef | MDDupFile;
432
433 hr = m_pMetaDataDispenser->SetOption(MetaDataCheckDuplicatesFor, &opt);
434 _ASSERTE(SUCCEEDED(hr));
435
436
437#if !defined(FEATURE_MERGE_JIT_AND_ENGINE)
438 m_fDontLoadJit = false;
439#endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE)
440}
441
442// LoadAndInitializeJITForNgen: load the JIT dll into the process, and initialize it (call the UtilCode initialization function,
443// check the JIT-EE interface GUID, etc.). (This is similar to LoadAndInitializeJIT.)
444//
445// Parameters:
446//
447// pwzJitName - The filename of the JIT .dll file to load. E.g., "altjit.dll".
448// phJit - On return, *phJit is the Windows module handle of the loaded JIT dll. It will be NULL if the load failed.
449// ppICorJitCompiler - On return, *ppICorJitCompiler is the ICorJitCompiler* returned by the JIT's getJit() entrypoint.
450// It is NULL if the JIT returns a NULL interface pointer, or if the JIT-EE interface GUID is mismatched.
451//
452// Note that both *phJit and *ppICorJitCompiler will be non-NULL on success. On failure, an exception is thrown.
453void Zapper::LoadAndInitializeJITForNgen(LPCWSTR pwzJitName, OUT HINSTANCE* phJit, OUT ICorJitCompiler** ppICorJitCompiler)
454{
455 _ASSERTE(phJit != NULL);
456 _ASSERTE(ppICorJitCompiler != NULL);
457
458 *phJit = NULL;
459 *ppICorJitCompiler = NULL;
460
461 HRESULT hr = E_FAIL;
462
463 // Note: FEATURE_MERGE_JIT_AND_ENGINE is defined for the Desktop crossgen compilation as well.
464 //
465 PathString CoreClrFolder;
466 extern HINSTANCE g_hThisInst;
467
468#if !defined(FEATURE_MERGE_JIT_AND_ENGINE)
469 if (m_fDontLoadJit)
470 {
471 return;
472 }
473
474 if (m_CLRJITPath.GetCount() > 0)
475 {
476 // If we have been asked to load a specific JIT binary, load it.
477 CoreClrFolder.Set(m_CLRJITPath);
478 hr = S_OK;
479 }
480 else
481#endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE)
482 if (WszGetModuleFileName(g_hThisInst, CoreClrFolder))
483 {
484 hr = CopySystemDirectory(CoreClrFolder, CoreClrFolder);
485 if (SUCCEEDED(hr))
486 {
487 CoreClrFolder.Append(pwzJitName);
488
489 }
490 }
491
492 if (SUCCEEDED(hr))
493 {
494 *phJit = ::WszLoadLibrary(CoreClrFolder);
495 if (*phJit == NULL)
496 {
497 hr = HRESULT_FROM_GetLastError();
498 }
499 else
500 {
501 hr = S_OK;
502 }
503 }
504
505 if (FAILED(hr))
506 {
507 Error(W("Unable to load Jit Compiler: %s, hr=0x%08x\r\n"), pwzJitName, hr);
508 ThrowLastError();
509 }
510
511#if !defined(CROSSGEN_COMPILE)
512 typedef void (__stdcall* pSxsJitStartup) (CoreClrCallbacks const & cccallbacks);
513 pSxsJitStartup sxsJitStartupFn = (pSxsJitStartup) GetProcAddress(*phJit, "sxsJitStartup");
514 if (sxsJitStartupFn == NULL)
515 {
516 Error(W("Unable to load Jit startup function: %s\r\n"), pwzJitName);
517 ThrowLastError();
518 }
519
520 CoreClrCallbacks cccallbacks = GetClrCallbacks();
521 (*sxsJitStartupFn) (cccallbacks);
522#endif
523
524 typedef void (__stdcall* pJitStartup)(ICorJitHost* host);
525 pJitStartup jitStartupFn = (pJitStartup)GetProcAddress(*phJit, "jitStartup");
526 if (jitStartupFn != nullptr)
527 {
528 jitStartupFn(m_pEECompileInfo->GetJitHost());
529 }
530
531 //get the appropriate compiler interface
532 typedef ICorJitCompiler* (__stdcall* pGetJitFn)();
533 pGetJitFn getJitFn = (pGetJitFn) GetProcAddress(*phJit, "getJit");
534 if (getJitFn == NULL)
535 {
536 Error(W("Unable to load main Jit function: %s\r\n"), pwzJitName);
537 ThrowLastError();
538 }
539
540 ICorJitCompiler* pICorJitCompiler = (*getJitFn)();
541 if (pICorJitCompiler == NULL)
542 {
543 Error(W("Unable to initialize Jit Compiler: %s\r\n"), pwzJitName);
544 ThrowLastError();
545 }
546
547 // Check the JIT version identifier.
548
549 GUID versionId;
550 memset(&versionId, 0, sizeof(GUID));
551 pICorJitCompiler->getVersionIdentifier(&versionId);
552
553 if (memcmp(&versionId, &JITEEVersionIdentifier, sizeof(GUID)) != 0)
554 {
555 // Mismatched version ID. Fail the load.
556 Error(W("Jit Compiler has wrong version identifier\r\n"));
557 ThrowHR(E_FAIL);
558 }
559
560 // The JIT has loaded and passed the version identifier test, so publish the JIT interface to the caller.
561 *ppICorJitCompiler = pICorJitCompiler;
562}
563
564void Zapper::InitEE(BOOL fForceDebug, BOOL fForceProfile, BOOL fForceInstrument)
565{
566 if (m_pEECompileInfo != NULL)
567 return;
568
569#if defined(FEATURE_COMINTEROP)
570 //
571 // Initialize COM
572 //
573
574 if (!m_fromDllHost)
575 {
576 CoInitializeEx(NULL, COINIT_MULTITHREADED);
577 }
578
579#endif // FEATURE_COMINTEROP
580
581 //
582 // Get EE compiler interface and initialize the EE
583 //
584
585 m_pEECompileInfo = GetCompileInfo();
586
587 if (m_pOpt->m_statOptions)
588 IfFailThrow(m_pEECompileInfo->SetVerboseLevel (CORCOMPILE_STATS));
589
590 if (m_pOpt->m_verbose)
591 IfFailThrow(m_pEECompileInfo->SetVerboseLevel (CORCOMPILE_VERBOSE));
592
593 IfFailThrow(m_pEECompileInfo->Startup(fForceDebug, fForceProfile, fForceInstrument));
594
595 m_pEEJitInfo = GetZapJitInfo();
596
597 //
598 // Get JIT interface
599 //
600
601#ifdef FEATURE_MERGE_JIT_AND_ENGINE
602 jitStartup(m_pEECompileInfo->GetJitHost());
603 m_pJitCompiler = getJit();
604
605 if (m_pJitCompiler == NULL)
606 {
607 Error(W("Unable to initialize Jit Compiler\r\n"));
608 ThrowLastError();
609 }
610#else
611
612 CorCompileRuntimeDlls ngenDllId;
613
614 ngenDllId = CROSSGEN_COMPILER_INFO;
615
616 LPCWSTR pwzJitName = CorCompileGetRuntimeDllName(ngenDllId);
617 LoadAndInitializeJITForNgen(pwzJitName, &m_hJitLib, &m_pJitCompiler);
618
619#endif // FEATURE_MERGE_JIT_AND_ENGINE
620
621#ifdef ALLOW_SXS_JIT_NGEN
622
623 // Do not load altjit unless COMPlus_AltJitNgen is set.
624 LPWSTR altJit;
625 HRESULT hr = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_AltJitNgen, &altJit);
626 if (FAILED(hr))
627 {
628 Error(W("Unable to load alternative Jit Compiler\r\n"));
629 ThrowHR(hr);
630 }
631
632 m_hAltJITCompiler = NULL;
633 m_alternateJit = NULL;
634
635 if (altJit != NULL)
636 {
637 // Allow a second jit to be loaded into the system.
638 //
639 LPCWSTR altName;
640 hr = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_AltJitName, (LPWSTR*)&altName);
641 if (FAILED(hr))
642 {
643 Error(W("Unable to load alternative Jit Compiler\r\n"));
644 ThrowHR(hr);
645 }
646
647 if (altName == NULL)
648 {
649 altName = MAKEDLLNAME_W(W("protojit"));
650 }
651
652 LoadAndInitializeJITForNgen(altName, &m_hAltJITCompiler, &m_alternateJit);
653 }
654#endif // ALLOW_SXS_JIT_NGEN
655}
656
657Zapper::~Zapper()
658{
659 DestroyDomain();
660
661 if (m_pMetaDataDispenser != NULL)
662 m_pMetaDataDispenser->Release();
663
664 if (m_fFreeZapperOptions)
665 {
666 if (m_pOpt != NULL)
667 delete m_pOpt;
668 m_pOpt = NULL;
669 }
670
671 if (m_pEECompileInfo != NULL)
672 {
673#ifndef FEATURE_MERGE_JIT_AND_ENGINE
674#ifdef _TARGET_AMD64_
675 if (m_hJitLegacy != NULL)
676 {
677 _ASSERTE(m_hJitLib != NULL);
678 _ASSERTE(m_pJitCompiler != NULL);
679
680 // We're unloading the fallback JIT dll, so clear the fallback shim. We do this even though
681 // we'll unload the main JIT below, in case there are other places that have loaded the main JIT
682 // but not the fallback JIT (note that LoadLibrary reference counts the number of loads that have been done).
683 m_pJitCompiler->setRealJit(nullptr);
684
685 FreeLibrary(m_hJitLegacy);
686 }
687#endif
688 if (m_hJitLib != NULL)
689 FreeLibrary(m_hJitLib);
690#endif
691
692#ifdef ALLOW_SXS_JIT_NGEN
693 if (m_hAltJITCompiler != NULL)
694 FreeLibrary(m_hAltJITCompiler);
695#endif
696
697#ifdef FEATURE_COMINTEROP
698 if (!m_fromDllHost)
699 {
700 CoUninitialize();
701 }
702#endif // FEATURE_COMINTEROP
703 }
704}
705
706void Zapper::DestroyDomain()
707{
708 CleanupAssembly();
709
710 //
711 // Shut down JIT compiler.
712 //
713
714 if (m_pJitCompiler != NULL)
715 {
716 m_pJitCompiler->ProcessShutdownWork(NULL);
717 }
718
719 //
720 // Get rid of domain.
721 //
722
723 if (m_pDomain != NULL)
724 {
725 m_pEECompileInfo->DestroyDomain(m_pDomain);
726 m_pDomain = NULL;
727 }
728}
729
730void Zapper::CleanupAssembly()
731{
732 if (m_pAssemblyEmit != NULL)
733 {
734 m_pAssemblyEmit->Release();
735 m_pAssemblyEmit = NULL;
736 }
737
738 if (m_pAssemblyImport != NULL)
739 {
740 m_pAssemblyImport->Release();
741 m_pAssemblyImport = NULL;
742 }
743}
744
745
746//**********************************************************************
747// Copy of vm\i386\cgenCpu.h
748// @TODO : clean this up. There has to be a way of sharing this code
749
750//**********************************************************************
751// To be used with GetSpecificCpuInfo()
752#ifdef _TARGET_X86_
753
754#define CPU_X86_FAMILY(cpuType) (((cpuType) & 0x0F00) >> 8)
755#define CPU_X86_MODEL(cpuType) (((cpuType) & 0x00F0) >> 4)
756// Stepping is masked out by GetSpecificCpuInfo()
757// #define CPU_X86_STEPPING(cpuType) (((cpuType) & 0x000F) )
758
759#define CPU_X86_USE_CMOV(cpuFeat) ((cpuFeat & 0x00008001) == 0x00008001)
760#define CPU_X86_USE_SSE2(cpuFeat) ((cpuFeat & 0x04000000) == 0x04000000)
761
762// Values for CPU_X86_FAMILY(cpuType)
763#define CPU_X86_486 4
764#define CPU_X86_PENTIUM 5
765#define CPU_X86_PENTIUM_PRO 6
766#define CPU_X86_PENTIUM_4 0xF
767
768// Values for CPU_X86_MODEL(cpuType) for CPU_X86_PENTIUM_PRO
769#define CPU_X86_MODEL_PENTIUM_PRO_BANIAS 9 // Pentium M (Mobile PPro with P4 feautres)
770
771#endif
772
773
774BOOL Zapper::IsAssembly(LPCWSTR path)
775{
776 HandleHolder hFile = WszCreateFile(path,
777 GENERIC_READ,
778 FILE_SHARE_READ | FILE_SHARE_WRITE,
779 NULL, OPEN_EXISTING, 0, NULL);
780
781 if (hFile == INVALID_HANDLE_VALUE)
782 return FALSE;
783
784 IMAGE_DOS_HEADER dos;
785
786 DWORD count;
787 if (!ReadFile(hFile, &dos, sizeof(dos), &count, NULL)
788 || count != sizeof(dos))
789 return FALSE;
790
791 if (dos.e_magic != IMAGE_DOS_SIGNATURE || dos.e_lfanew == 0)
792 return FALSE;
793
794 if( SetFilePointer(hFile, dos.e_lfanew, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
795 return FALSE;
796
797 IMAGE_NT_HEADERS nt;
798 if (!ReadFile(hFile, &nt, sizeof(nt), &count, NULL)
799 || count != sizeof(nt))
800 return FALSE;
801
802 if (nt.Signature != IMAGE_NT_SIGNATURE)
803 return FALSE;
804
805 IMAGE_DATA_DIRECTORY* pDataDirectory = NULL;
806
807 // We accept pe32 or pe64 file formats
808
809 if (nt.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)
810 {
811 if (nt.FileHeader.SizeOfOptionalHeader != sizeof(IMAGE_OPTIONAL_HEADER32))
812 return FALSE;
813
814 IMAGE_NT_HEADERS32* pNTHeader32 = (IMAGE_NT_HEADERS32*) &nt.OptionalHeader;
815 pDataDirectory = &pNTHeader32->OptionalHeader.DataDirectory[0];
816 }
817 else if (nt.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
818 {
819 if (nt.FileHeader.SizeOfOptionalHeader != sizeof(IMAGE_OPTIONAL_HEADER64))
820 return FALSE;
821
822 IMAGE_NT_HEADERS64* pNTHeader64 = (IMAGE_NT_HEADERS64*) &nt.OptionalHeader;
823 pDataDirectory = &pNTHeader64->OptionalHeader.DataDirectory[0];
824 }
825 else
826 return FALSE;
827
828 if ((pDataDirectory[IMAGE_DIRECTORY_ENTRY_COMHEADER].VirtualAddress == 0) ||
829 (pDataDirectory[IMAGE_DIRECTORY_ENTRY_COMHEADER].Size < sizeof(IMAGE_COR20_HEADER)))
830 {
831 return FALSE;
832 }
833
834 return TRUE;
835}
836
837/*static*/ HRESULT Zapper::GenericDomainCallback(LPVOID pvArgs)
838{
839 STATIC_CONTRACT_ENTRY_POINT;
840
841 HRESULT hr = S_OK;
842
843 BEGIN_ENTRYPOINT_NOTHROW;
844 EX_TRY
845 {
846 REMOVE_STACK_GUARD;
847 ((DomainCallback *) pvArgs)->doCallback();
848 }
849 EX_CATCH_HRESULT(hr);
850
851 END_ENTRYPOINT_NOTHROW;
852
853 return hr;
854}
855
856void Zapper::InvokeDomainCallback(DomainCallback *callback)
857{
858#ifdef CROSSGEN_COMPILE
859 // Call the callback directly for better error messages (avoids exception swallowing and rethrow)
860 callback->doCallback();
861#else
862 IfFailThrow(m_pEECompileInfo->MakeCrossDomainCallback(m_pDomain,
863 Zapper::GenericDomainCallback,
864 (LPVOID) callback));
865#endif
866}
867
868void Zapper::SetContextInfo(LPCWSTR assemblyName)
869{
870 // A special case: If we're compiling mscorlib, ignore m_exeName and don't set any context.
871 // There can only be one mscorlib in the runtime, independent of any context. If we don't
872 // check for mscorlib, and isExe == true, then CompilationDomain::SetContextInfo will call
873 // into mscorlib and cause the resulting mscorlib.ni.dll to be slightly different (checked
874 // build only).
875 if (assemblyName != NULL && _wcsnicmp(assemblyName, CoreLibName_W, CoreLibNameLen) == 0 && (wcslen(assemblyName) == CoreLibNameLen || assemblyName[CoreLibNameLen] == W(',')))
876 {
877 return;
878 }
879
880 // Determine whether the root is an exe or a dll
881
882 StackSString s(m_exeName);
883
884 //These locals seem necessary for gcc do resolve the overload correctly
885 SString literalExe(SString::Literal, W(".exe"));
886 SString literalDll(SString::Literal, W(".dll"));
887 BOOL isExe = (s.GetCount() > 4) && s.MatchCaseInsensitive(s.End() - 4, literalExe);
888 BOOL isDll = (s.GetCount() > 4) && s.MatchCaseInsensitive(s.End() - 4, literalDll);
889
890 DWORD attributes = WszGetFileAttributes(m_exeName);
891 if (attributes != INVALID_FILE_ATTRIBUTES)
892 {
893 if (isExe)
894 {
895 // If it's an exe, set it as the config
896 m_pDomain->SetContextInfo(m_exeName, TRUE);
897 }
898 else if ((attributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
899 {
900 // If it's a directory, set it as the default app base
901 m_pDomain->SetContextInfo(m_exeName, FALSE);
902 }
903 else if (isDll)
904 {
905 // If it's a dll, then set a default app base of the directory containing the dll
906 SString::Iterator i = s.End();
907 if (s.FindBack(i, W("\\")))
908 {
909 s.Truncate(i);
910 m_pDomain->SetContextInfo(s.GetUnicode(), FALSE);
911 }
912 }
913 }
914}
915
916void Zapper::CreateDependenciesLookupDomainInCurrentDomain()
917{
918 SetContextInfo();
919}
920
921
922void ZapperSetBindingPaths(ICorCompilationDomain *pDomain, SString &trustedPlatformAssemblies, SString &platformResourceRoots, SString &appPaths, SString &appNiPaths);
923
924void Zapper::CreateCompilationDomain()
925{
926
927 BOOL fForceDebug = FALSE;
928 if (!m_pOpt->m_autodebug)
929 fForceDebug = m_pOpt->m_compilerFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_INFO);
930
931 BOOL fForceProfile = m_pOpt->m_compilerFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_PROF_ENTERLEAVE);
932 BOOL fForceInstrument = m_pOpt->m_compilerFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR);
933
934 InitEE(fForceDebug, fForceProfile, fForceInstrument);
935
936 // Make a compilation domain for the assembly. Note that this will
937 // collect the assembly dependencies for use in the version info, as
938 // well as isolating the compilation code.
939
940 IfFailThrow(m_pEECompileInfo->CreateDomain(&m_pDomain,
941 CreateAssemblyEmitter(),
942 fForceDebug,
943 fForceProfile,
944 fForceInstrument));
945
946#ifdef CROSSGEN_COMPILE
947 IfFailThrow(m_pDomain->SetPlatformWinmdPaths(m_platformWinmdPaths));
948#endif
949
950 // we support only TPA binding on CoreCLR
951
952 if (!m_trustedPlatformAssemblies.IsEmpty())
953 {
954 // TPA-based binding
955 // Add the trusted paths and apppath to the binding list
956 ZapperSetBindingPaths(m_pDomain, m_trustedPlatformAssemblies, m_platformResourceRoots, m_appPaths, m_appNiPaths);
957 }
958}
959
960void Zapper::CreateDependenciesLookupDomain()
961{
962 class Callback : public DomainCallback
963 {
964 public:
965 Callback(Zapper *pZapper)
966 {
967 this->pZapper = pZapper;
968 }
969
970 virtual void doCallback()
971 {
972 pZapper->CreateDependenciesLookupDomainInCurrentDomain();
973 }
974
975 Zapper* pZapper;
976 };
977
978
979 CreateCompilationDomain();
980
981 Callback callback(this);
982 InvokeDomainCallback(&callback);
983}
984
985void Zapper::CreatePdb(BSTR pAssemblyPathOrName, BSTR pNativeImagePath, BSTR pPdbPath, BOOL pdbLines, BSTR pManagedPdbSearchPath)
986{
987 class Callback : public DomainCallback {
988
989 public:
990
991 Callback(Zapper *pZapper, BSTR pAssemblyPathOrName, BSTR pNativeImagePath, BSTR pPdbPath, BOOL pdbLines, BSTR pManagedPdbSearchPath)
992 : m_pZapper(pZapper),
993 m_pAssemblyPathOrName(pAssemblyPathOrName),
994 m_pNativeImagePath(pNativeImagePath),
995 m_pPdbPath(pPdbPath),
996 m_pdbLines(pdbLines),
997 m_pManagedPdbSearchPath(pManagedPdbSearchPath)
998 {
999 }
1000
1001 virtual void doCallback()
1002 {
1003 m_pZapper->CreatePdbInCurrentDomain(m_pAssemblyPathOrName, m_pNativeImagePath, m_pPdbPath, m_pdbLines, m_pManagedPdbSearchPath);
1004 };
1005
1006 private:
1007
1008 Zapper *m_pZapper;
1009 BSTR m_pAssemblyPathOrName;
1010 BSTR m_pNativeImagePath;
1011 BSTR m_pPdbPath;
1012 BOOL m_pdbLines;
1013 BSTR m_pManagedPdbSearchPath;
1014
1015 };
1016
1017#ifdef CROSSGEN_COMPILE
1018 CreateCompilationDomain();
1019#endif
1020 _ASSERTE(m_pDomain);
1021
1022 Callback callback(this, pAssemblyPathOrName, pNativeImagePath, pPdbPath, pdbLines, pManagedPdbSearchPath);
1023 InvokeDomainCallback(&callback);
1024}
1025
1026
1027void Zapper::CreatePdbInCurrentDomain(BSTR pAssemblyPathOrName, BSTR pNativeImagePath, BSTR pPdbPath, BOOL pdbLines, BSTR pManagedPdbSearchPath)
1028{
1029 EX_TRY
1030 {
1031 CORINFO_ASSEMBLY_HANDLE hAssembly = NULL;
1032
1033 IfFailThrow(m_pEECompileInfo->LoadAssemblyByPath(
1034 pAssemblyPathOrName,
1035
1036 // fExplicitBindToNativeImage: On the phone, a path to the NI is specified
1037 // explicitly (even with .ni. in the name). All other callers specify a path to
1038 // the IL, and the NI is inferred, so this is normally FALSE in those other
1039 // cases.
1040 TRUE,
1041
1042 &hAssembly));
1043
1044
1045 // Ensure all modules belonging to this assembly get loaded. The CreatePdb() call
1046 // below will iterate through them via the ModuleIterator to generate PDB files for each
1047 // one, and the ModuleIterator assumes they've been loaded.
1048 IMDInternalImport * pMDImport = m_pEECompileInfo->GetAssemblyMetaDataImport(hAssembly);
1049 HENUMInternalHolder hEnum(pMDImport);
1050 hEnum.EnumAllInit(mdtFile);
1051 mdFile tkFile;
1052 while (pMDImport->EnumNext(&hEnum, &tkFile))
1053 {
1054 LPCSTR szName;
1055 DWORD flags;
1056 IfFailThrow(pMDImport->GetFileProps(tkFile, &szName, NULL, NULL, &flags));
1057
1058 if (!IsFfContainsMetaData(flags))
1059 continue;
1060
1061 CORINFO_MODULE_HANDLE hModule;
1062 IfFailThrow(m_pEECompileInfo->LoadAssemblyModule(hAssembly,
1063 tkFile, &hModule));
1064 }
1065
1066 LPCWSTR pDiasymreaderPath = nullptr;
1067#if !defined(NO_NGENPDB)
1068 if (m_DiasymreaderPath.GetCount() > 0)
1069 {
1070 pDiasymreaderPath = m_DiasymreaderPath.GetUnicode();
1071 }
1072#endif //!defined(NO_NGENPDB)
1073
1074 IfFailThrow(::CreatePdb(hAssembly, pNativeImagePath, pPdbPath, pdbLines, pManagedPdbSearchPath, pDiasymreaderPath));
1075 }
1076 EX_CATCH
1077 {
1078 // Print the error message
1079
1080 Error(W("Error generating PDB for '%s': "), pNativeImagePath);
1081 PrintErrorMessage(CORZAP_LOGLEVEL_ERROR, GET_EXCEPTION());
1082 Error(W("\n"));
1083 EX_RETHROW;
1084 }
1085 EX_END_CATCH(RethrowTerminalExceptions);
1086}
1087
1088void Zapper::ComputeDependencies(LPCWSTR pAssemblyName, CORCOMPILE_NGEN_SIGNATURE * pNativeImageSig)
1089{
1090}
1091
1092
1093//
1094// Compile a module by name
1095//
1096
1097HRESULT Zapper::Compile(LPCWSTR string, CORCOMPILE_NGEN_SIGNATURE * pNativeImageSig)
1098{
1099 class Callback : public DomainCallback
1100 {
1101 public:
1102 Callback(Zapper *pZapper, LPCWSTR pAssemblyName, CORCOMPILE_NGEN_SIGNATURE * pNativeImageSig)
1103 {
1104 this->pZapper = pZapper;
1105 this->pAssemblyName = pAssemblyName;
1106 this->pNativeImageSig = pNativeImageSig;
1107 }
1108
1109 virtual void doCallback()
1110 {
1111 pZapper->CompileInCurrentDomain(pAssemblyName, pNativeImageSig);
1112 }
1113
1114 Zapper* pZapper;
1115 LPCWSTR pAssemblyName;
1116 CORCOMPILE_NGEN_SIGNATURE * pNativeImageSig;
1117 };
1118
1119 HRESULT hr = S_OK;
1120
1121 bool fMscorlib = false;
1122 LPCWSTR fileName = PathFindFileName(string);
1123 if (fileName != NULL && SString::_wcsicmp(fileName, g_pwBaseLibrary) == 0)
1124 {
1125 fMscorlib = true;
1126 }
1127
1128
1129 if (fMscorlib)
1130 {
1131 //
1132 // Disallow use of native image to force a new native image generation for mscorlib
1133 //
1134 g_fAllowNativeImages = false;
1135 }
1136
1137 // the errors in CreateCompilationDomain are fatal - propogate them up
1138 CreateCompilationDomain();
1139
1140 EX_TRY
1141 {
1142 Callback callback(this, string, pNativeImageSig);
1143 InvokeDomainCallback(&callback);
1144 }
1145 EX_CATCH
1146 {
1147 // Print the error message
1148
1149 Error(W("Error compiling %s: "), string);
1150 PrintErrorMessage(CORZAP_LOGLEVEL_ERROR, GET_EXCEPTION());
1151 Error(W("\n"));
1152
1153 hr = GET_EXCEPTION()->GetHR();
1154 }
1155 EX_END_CATCH(SwallowAllExceptions);
1156
1157 // the errors in DestroyDomain are fatal - propogate them up
1158 DestroyDomain();
1159
1160 return hr;
1161}
1162
1163void Zapper::DontUseProfileData()
1164{
1165 // Call this before calling Compile()
1166
1167 m_pOpt->m_ignoreProfileData = true;
1168}
1169
1170bool Zapper::HasProfileData()
1171{
1172 // Only valid after calling Compile()
1173 return m_pOpt->m_fHasAnyProfileData;
1174}
1175
1176// Helper function for Zapper::Compile(LPCWSTR string)
1177//
1178
1179void Zapper::CompileInCurrentDomain(__in LPCWSTR string, CORCOMPILE_NGEN_SIGNATURE * pNativeImageSig)
1180{
1181 STATIC_CONTRACT_ENTRY_POINT;
1182
1183 BEGIN_ENTRYPOINT_VOIDRET;
1184
1185
1186
1187 //
1188 // Load the assembly.
1189 //
1190 // "string" may be a path or assembly display name.
1191 // To decide, we see if it is the name of a valid file.
1192 //
1193
1194 _ASSERTE(m_hAssembly == NULL);
1195
1196 //without fusion, this has to be a file name
1197 {
1198 IfFailThrow(m_pEECompileInfo->LoadAssemblyByPath(string, FALSE /* fExplicitBindToNativeImage */, &m_hAssembly));
1199 }
1200
1201
1202 //
1203 // Compile the assembly
1204 //
1205 CompileAssembly(pNativeImageSig);
1206
1207 goto Exit; // Avoid warning about unreferenced label
1208
1209Exit:
1210 END_ENTRYPOINT_VOIDRET;
1211
1212 return;
1213}
1214
1215
1216//------------------------------------------------------------------------------
1217
1218// Is this is a valid file-system file name?
1219
1220bool StringHasLegalFileNameChars(LPCWSTR assemblyName)
1221{
1222 if (wcschr(assemblyName, ':') || wcschr(assemblyName, '/') || wcschr(assemblyName, '\\') ||
1223 wcschr(assemblyName, '*') || wcschr(assemblyName, '?') || wcschr(assemblyName, '"') ||
1224 wcschr(assemblyName, '<') || wcschr(assemblyName, '>') || wcschr(assemblyName, '|'))
1225 return false;
1226
1227 return true;
1228}
1229
1230IMetaDataAssemblyEmit * Zapper::CreateAssemblyEmitter()
1231{
1232 _ASSERTE(!m_pAssemblyEmit);
1233
1234 //
1235 // Make a manifest emitter for the zapped assembly.
1236 //
1237
1238 // Hardwire the metadata version to be the current runtime version so that the ngen image
1239 // does not change when the directory runtime is installed in different directory (e.g. v2.0.x86chk vs. v2.0.80826).
1240 BSTRHolder strVersion(SysAllocString(W("v")VER_PRODUCTVERSION_NO_QFE_STR_L));
1241 VARIANT versionOption;
1242 V_VT(&versionOption) = VT_BSTR;
1243 V_BSTR(&versionOption) = strVersion;
1244 IfFailThrow(m_pMetaDataDispenser->SetOption(MetaDataRuntimeVersion, &versionOption));
1245
1246 IfFailThrow(m_pMetaDataDispenser->DefineScope(CLSID_CorMetaDataRuntime,
1247 0, IID_IMetaDataAssemblyEmit,
1248 (IUnknown **) &m_pAssemblyEmit));
1249
1250 return m_pAssemblyEmit;
1251}
1252
1253void Zapper::InitializeCompilerFlags(CORCOMPILE_VERSION_INFO * pVersionInfo)
1254{
1255 m_pOpt->m_compilerFlags.Clear(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_INFO);
1256 m_pOpt->m_compilerFlags.Clear(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_CODE);
1257 m_pOpt->m_compilerFlags.Clear(CORJIT_FLAGS::CORJIT_FLAG_PROF_ENTERLEAVE);
1258 m_pOpt->m_compilerFlags.Clear(CORJIT_FLAGS::CORJIT_FLAG_PROF_NO_PINVOKE_INLINE);
1259
1260 // We track debug info all the time in the ngen image
1261 m_pOpt->m_compilerFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_INFO);
1262
1263 if (pVersionInfo->wCodegenFlags & CORCOMPILE_CODEGEN_DEBUGGING)
1264 {
1265 m_pOpt->m_compilerFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_INFO);
1266 m_pOpt->m_compilerFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_CODE);
1267 }
1268
1269 if (pVersionInfo->wCodegenFlags & CORCOMPILE_CODEGEN_PROFILING)
1270 {
1271 m_pOpt->m_compilerFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_PROF_ENTERLEAVE);
1272 m_pOpt->m_compilerFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_PROF_NO_PINVOKE_INLINE);
1273 m_pOpt->m_ngenProfileImage = true;
1274 }
1275
1276 if (pVersionInfo->wCodegenFlags & CORCOMPILE_CODEGEN_PROF_INSTRUMENTING)
1277 m_pOpt->m_compilerFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR);
1278
1279 // Set CORJIT_FLAG_MIN_OPT only if COMPlus_JitMinOpts == 1
1280 static ConfigDWORD g_jitMinOpts;
1281 if (g_jitMinOpts.val_DontUse_(CLRConfig::UNSUPPORTED_JITMinOpts, 0) == 1)
1282 {
1283 m_pOpt->m_compilerFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_MIN_OPT);
1284 }
1285
1286#if defined(_TARGET_X86_)
1287
1288 // @TODO: This is a copy of SetCpuInfo() in vm\codeman.cpp. Unify the implementaion
1289
1290 switch (CPU_X86_FAMILY(pVersionInfo->cpuInfo.dwCPUType))
1291 {
1292 case CPU_X86_PENTIUM_4:
1293 m_pOpt->m_compilerFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_TARGET_P4);
1294 break;
1295
1296 default:
1297 break;
1298 }
1299
1300 if (CPU_X86_USE_CMOV(pVersionInfo->cpuInfo.dwFeatures))
1301 {
1302 m_pOpt->m_compilerFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_USE_CMOV);
1303 m_pOpt->m_compilerFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_USE_FCOMI);
1304 }
1305
1306 // .NET Core requires SSE2.
1307 m_pOpt->m_compilerFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_USE_SSE2);
1308
1309#endif // _TARGET_X86_
1310
1311 if ( m_pOpt->m_compilerFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_INFO)
1312 && m_pOpt->m_compilerFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_CODE)
1313 && m_pOpt->m_compilerFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_PROF_ENTERLEAVE))
1314 {
1315 //
1316 // We've decided not to support debugging + optimizations disabled + profiling to
1317 // cut down on the testing matrix, under the assertion that the combination isn't
1318 // particularly common or useful. (It's still supported in normal jit mode though.)
1319 //
1320
1321 Error(W("The CLR doesn't support precompiled code when using both debugging and profiling")
1322 W(" instrumentation.\n"));
1323 ThrowHR(E_NOTIMPL);
1324 }
1325}
1326
1327void Zapper::DefineOutputAssembly(SString& strAssemblyName, ULONG * pHashAlgId)
1328{
1329 mdAssembly tkAssembly;
1330 IfFailThrow(m_pAssemblyImport->GetAssemblyFromScope(&tkAssembly));
1331
1332 AssemblyMetaDataInternal internalMetadata;
1333
1334 const void *pbPublicKey;
1335 ULONG cbPublicKey;
1336 ULONG hashAlgId;
1337 LPCSTR szAssemblyName;
1338 DWORD flags;
1339
1340 IfFailThrow(m_pAssemblyImport->GetAssemblyProps(tkAssembly, &pbPublicKey, &cbPublicKey,
1341 &hashAlgId, &szAssemblyName, &internalMetadata, &flags));
1342
1343 strAssemblyName.SetUTF8(szAssemblyName);
1344
1345 ASSEMBLYMETADATA metadata;
1346 SString strLocale;
1347
1348 metadata.usMajorVersion = internalMetadata.usMajorVersion;
1349 metadata.usMinorVersion = internalMetadata.usMinorVersion;
1350 metadata.usBuildNumber = internalMetadata.usBuildNumber;
1351 metadata.usRevisionNumber = internalMetadata.usRevisionNumber;
1352 if (internalMetadata.szLocale)
1353 {
1354 strLocale.SetUTF8(internalMetadata.szLocale);
1355 metadata.szLocale = (LPWSTR)strLocale.GetUnicode();
1356 metadata.cbLocale = strLocale.GetCount() + 1;
1357 }
1358 else
1359 {
1360 metadata.szLocale = NULL;
1361 metadata.cbLocale = 0;
1362 }
1363 metadata.rProcessor = internalMetadata.rProcessor;
1364 metadata.ulProcessor = internalMetadata.ulProcessor;
1365 metadata.rOS = internalMetadata.rOS;
1366 metadata.ulOS = internalMetadata.ulOS;
1367
1368 LPCWSTR wszAssemblyName = strAssemblyName.GetUnicode();
1369
1370 // If assembly name (and hence fileName) has invalid characters,
1371 // GenerateFile() will fail later on.
1372 // VerifyBindingString is a Runtime requirement, but StringHasLegalFileNameChars
1373 // is a ngen restriction.
1374
1375 if (!StringHasLegalFileNameChars(wszAssemblyName))
1376 {
1377 Error(W("Error: Assembly name \"%s\" contains illegal (unsupported) file name characters.\n"), wszAssemblyName);
1378 ThrowHR(HRESULT_FROM_WIN32(ERROR_INVALID_NAME));
1379 }
1380
1381#ifndef PLATFORM_UNIX
1382 //
1383 // We always need a hash since our assembly module is separate from the manifest.
1384 // Use MD5 by default.
1385 //
1386
1387 if (hashAlgId == 0)
1388 hashAlgId = CALG_MD5;
1389#endif
1390
1391 mdAssembly tkEmitAssembly;
1392 IfFailThrow(m_pAssemblyEmit->DefineAssembly(pbPublicKey, cbPublicKey, hashAlgId,
1393 wszAssemblyName, &metadata,
1394 flags, &tkEmitAssembly));
1395
1396 *pHashAlgId = hashAlgId;
1397}
1398
1399//
1400// This is the main worker function (at the assembly level).
1401//
1402// Zapper::CompileInCurrentDomain() ends up calling this function.
1403
1404void Zapper::CompileAssembly(CORCOMPILE_NGEN_SIGNATURE * pNativeImageSig)
1405{
1406 _ASSERTE(m_pDomain != NULL); // This must be set up by this time.
1407 _ASSERTE(m_hAssembly != NULL);
1408
1409 CORINFO_MODULE_HANDLE hAssemblyModule = m_pEECompileInfo->GetAssemblyModule(m_hAssembly);
1410
1411 CORCOMPILE_VERSION_INFO versionInfo;
1412
1413 IfFailThrow(m_pEECompileInfo->GetAssemblyVersionInfo(m_hAssembly,
1414 &versionInfo));
1415
1416 InitializeCompilerFlags(&versionInfo);
1417
1418 //
1419 // Set up the output path.
1420 //
1421
1422 //
1423 // If we don't have fusion, we just create the file right at the target. No need to do an install.
1424 //
1425 {
1426 SString strAssemblyPath = GetOutputFileName();
1427
1428 if (strAssemblyPath.IsEmpty())
1429 {
1430 m_pEECompileInfo->GetModuleFileName(hAssemblyModule, strAssemblyPath);
1431 }
1432
1433 const WCHAR * assemblyPath = strAssemblyPath.GetUnicode();
1434 const WCHAR * pathend = wcsrchr( assemblyPath, DIRECTORY_SEPARATOR_CHAR_W );
1435 if( pathend )
1436 {
1437 m_outputPath.Set(assemblyPath, COUNT_T(pathend - assemblyPath));
1438 }
1439 else
1440 {
1441 m_outputPath.Set(W(".") DIRECTORY_SEPARATOR_STR_W);
1442 }
1443 }
1444
1445 //
1446 // Get the manifest metadata.
1447 //
1448
1449 m_pAssemblyImport = m_pEECompileInfo->GetAssemblyMetaDataImport(m_hAssembly);
1450
1451 //
1452 // Copy the relevant assembly info to the zap manifest
1453 //
1454
1455 SString strAssemblyName;
1456 ULONG hashAlgId;
1457 DefineOutputAssembly(strAssemblyName, &hashAlgId);
1458
1459 SString strNativeImagePath;
1460 SString strNativeImageTempPath;
1461 HANDLE hFile = INVALID_HANDLE_VALUE;
1462 StackSArray<HANDLE> hFiles;
1463
1464 {
1465 //
1466 // Compile the manifest module (if manifest is not stand-alone).
1467 //
1468
1469 NewHolder<ZapImage> pAssemblyModule;
1470
1471 pAssemblyModule = CompileModule(hAssemblyModule, m_pAssemblyEmit);
1472
1473 //
1474 // The loader (currently) requires all modules of an ngen-ed multi-module
1475 // assembly to be ngen-ed.
1476 //
1477
1478
1479 //
1480 // Record the version info
1481 //
1482
1483 pAssemblyModule->SetVersionInfo(&versionInfo);
1484
1485 //
1486 // Record Dependencies
1487 //
1488
1489 if (!IsReadyToRunCompilation())
1490 {
1491 CORCOMPILE_DEPENDENCY *pDependencies;
1492 DWORD cDependencies;
1493 IfFailThrow(m_pDomain->GetDependencies(&pDependencies, &cDependencies));
1494
1495 pAssemblyModule->SetDependencies(pDependencies, cDependencies);
1496 }
1497
1498 // Write the main assembly module
1499 strNativeImagePath = GetOutputFileName();
1500
1501 if (strNativeImagePath.IsEmpty())
1502 {
1503 strNativeImagePath.Set(m_outputPath, SL(DIRECTORY_SEPARATOR_STR_W), strAssemblyName,
1504 pAssemblyModule->m_ModuleDecoder.IsDll() ? SL(W(".ni.dll")) : SL(W(".ni.exe")));
1505 }
1506
1507 pAssemblyModule->SetPdbFileName(SString(strAssemblyName, SL(W(".ni.pdb"))));
1508
1509 strNativeImageTempPath = strNativeImagePath;
1510 strNativeImageTempPath.Append(W(".tmp"));
1511
1512 hFile = pAssemblyModule->SaveImage(strNativeImageTempPath.GetUnicode(), strNativeImagePath.GetUnicode(), pNativeImageSig);
1513 }
1514
1515 // Throw away the assembly if we have hit fatal error during compilation
1516 if (FAILED(g_hrFatalError))
1517 ThrowHR(g_hrFatalError);
1518
1519
1520 // Close the file
1521 CloseHandle(hFile);
1522 for (SArray<HANDLE>::Iterator i = hFiles.Begin(); i != hFiles.End(); ++i)
1523 {
1524 CloseHandle(*i);
1525 }
1526
1527 if (!WszMoveFileEx(strNativeImageTempPath.GetUnicode(), strNativeImagePath.GetUnicode(), MOVEFILE_REPLACE_EXISTING))
1528 {
1529 ThrowLastError();
1530 }
1531
1532 if (!m_pOpt->m_silent)
1533 {
1534 GetSvcLogger()->Printf(W("Native image %s generated successfully.\n"), strNativeImagePath.GetUnicode());
1535 }
1536
1537}
1538
1539
1540ZapImage * Zapper::CompileModule(CORINFO_MODULE_HANDLE hModule,
1541 IMetaDataAssemblyEmit *pAssemblyEmit)
1542{
1543 NewHolder<ZapImage> module;
1544
1545 BYTE * pMemory = new BYTE[sizeof(ZapImage)];
1546 ZeroMemory(pMemory, sizeof(ZapImage));
1547
1548 module = new (pMemory) ZapImage(this);
1549
1550 // Finish initializing the ZapImage object
1551 // any calls that could throw an exception are done in ZapImage::Initialize()
1552 //
1553 module->ZapWriter::Initialize();
1554
1555 //
1556 // Set target
1557 //
1558
1559 IfFailThrow(m_pEECompileInfo->SetCompilationTarget(m_hAssembly, hModule));
1560
1561 //
1562 // Open input file
1563 //
1564
1565 Info(W("Opening input file\n"));
1566
1567 module->Open(hModule, pAssemblyEmit);
1568
1569 //
1570 // input module.
1571 //
1572
1573 Info(W("Preloading input file %s\n"), module->m_pModuleFileName);
1574
1575 module->Preload();
1576
1577 //
1578 // Compile input module
1579 //
1580
1581 Info(W("Compiling input file %s\n"), module->m_pModuleFileName);
1582
1583 module->Compile();
1584
1585 if (!IsReadyToRunCompilation())
1586 {
1587 //
1588 // Link preloaded module.
1589 //
1590
1591 Info(W("Linking preloaded input file %s\n"), module->m_pModuleFileName);
1592
1593 module->LinkPreload();
1594 }
1595 return module.Extract();
1596}
1597
1598
1599void Zapper::Success(LPCWSTR format, ...)
1600{
1601 va_list args;
1602 va_start(args, format);
1603
1604 Print(CORZAP_LOGLEVEL_SUCCESS, format, args);
1605
1606 va_end(args);
1607}
1608
1609void Zapper::Error(LPCWSTR format, ...)
1610{
1611 // Preserve last error so we can still do ThrowLastError() after printing the message
1612 DWORD err = GetLastError();
1613
1614 va_list args;
1615 va_start(args, format);
1616
1617 Print(CORZAP_LOGLEVEL_ERROR, format, args);
1618
1619 va_end(args);
1620
1621 SetLastError(err);
1622}
1623
1624void Zapper::Warning(LPCWSTR format, ...)
1625{
1626 // Preserve last error so we can still do ThrowLastError() after printing the message
1627 DWORD err = GetLastError();
1628
1629 va_list args;
1630 va_start(args, format);
1631
1632 Print(CORZAP_LOGLEVEL_WARNING, format, args);
1633
1634 va_end(args);
1635
1636 SetLastError(err);
1637}
1638
1639void Zapper::Info(LPCWSTR format, ...)
1640{
1641 va_list args;
1642 va_start(args, format);
1643
1644 Print(CORZAP_LOGLEVEL_INFO, format, args);
1645
1646 va_end(args);
1647}
1648
1649void Zapper::Print(CorZapLogLevel level, LPCWSTR format, ...)
1650{
1651 va_list args;
1652 va_start(args, format);
1653
1654 Print(level, format, args);
1655
1656 va_end(args);
1657}
1658
1659void Zapper::Print(CorZapLogLevel level, LPCWSTR format, va_list args)
1660{
1661 const int BUFF_SIZE = 1024;
1662 WCHAR output[BUFF_SIZE];
1663
1664 switch (level)
1665 {
1666 case CORZAP_LOGLEVEL_INFO:
1667 if (!m_pOpt->m_verbose)
1668 return;
1669 break;
1670
1671 case CORZAP_LOGLEVEL_ERROR:
1672 case CORZAP_LOGLEVEL_WARNING:
1673 break;
1674
1675 case CORZAP_LOGLEVEL_SUCCESS:
1676 if (m_pOpt->m_silent)
1677 return;
1678 break;
1679 }
1680
1681 CorSvcLogLevel logLevel = (CorSvcLogLevel)0;
1682 switch (level)
1683 {
1684 case CORZAP_LOGLEVEL_INFO:
1685 logLevel = LogLevel_Info;
1686 break;
1687
1688 case CORZAP_LOGLEVEL_ERROR:
1689 logLevel = LogLevel_Error;
1690 break;
1691
1692 case CORZAP_LOGLEVEL_WARNING:
1693 logLevel = LogLevel_Warning;
1694 break;
1695
1696 case CORZAP_LOGLEVEL_SUCCESS:
1697 logLevel = LogLevel_Success;
1698 break;
1699 }
1700
1701 _vsnwprintf_s(output, BUFF_SIZE, BUFF_SIZE - 1, format, args);
1702 output[BUFF_SIZE-1] = 0;
1703
1704 GetSvcLogger()->Log(output, logLevel);
1705}
1706
1707void Zapper::PrintErrorMessage(CorZapLogLevel level, Exception *ex)
1708{
1709 StackSString message;
1710 ex->GetMessage(message);
1711 Print(level, W("%s"), message.GetUnicode());
1712}
1713
1714void Zapper::PrintErrorMessage(CorZapLogLevel level, HRESULT hr)
1715{
1716 StackSString message;
1717 GetHRMsg(hr, message);
1718 Print(level, W("%s"), message.GetUnicode());
1719}
1720
1721#if !defined(FEATURE_MERGE_JIT_AND_ENGINE)
1722void Zapper::SetCLRJITPath(LPCWSTR pwszCLRJITPath)
1723{
1724 m_CLRJITPath.Set(pwszCLRJITPath);
1725}
1726
1727void Zapper::SetDontLoadJit()
1728{
1729 m_fDontLoadJit = true;
1730}
1731#endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE)
1732
1733#if !defined(NO_NGENPDB)
1734void Zapper::SetDiasymreaderPath(LPCWSTR pwzDiasymreaderPath)
1735{
1736 m_DiasymreaderPath.Set(pwzDiasymreaderPath);
1737}
1738#endif // !defined(NO_NGENPDB)
1739
1740
1741void Zapper::SetPlatformAssembliesPaths(LPCWSTR pwzPlatformAssembliesPaths)
1742{
1743 m_platformAssembliesPaths.Set(pwzPlatformAssembliesPaths);
1744}
1745
1746void Zapper::SetTrustedPlatformAssemblies(LPCWSTR pwzTrustedPlatformAssemblies)
1747{
1748 m_trustedPlatformAssemblies.Set(pwzTrustedPlatformAssemblies);
1749}
1750
1751void Zapper::SetPlatformResourceRoots(LPCWSTR pwzPlatformResourceRoots)
1752{
1753 m_platformResourceRoots.Set(pwzPlatformResourceRoots);
1754}
1755
1756void Zapper::SetAppPaths(LPCWSTR pwzAppPaths)
1757{
1758 m_appPaths.Set(pwzAppPaths);
1759}
1760
1761void Zapper::SetAppNiPaths(LPCWSTR pwzAppNiPaths)
1762{
1763 m_appNiPaths.Set(pwzAppNiPaths);
1764}
1765
1766void Zapper::SetPlatformWinmdPaths(LPCWSTR pwzPlatformWinmdPaths)
1767{
1768 m_platformWinmdPaths.Set(pwzPlatformWinmdPaths);
1769}
1770
1771
1772void Zapper::SetOutputFilename(LPCWSTR pwzOutputFilename)
1773{
1774 m_outputFilename.Set(pwzOutputFilename);
1775}
1776
1777
1778SString Zapper::GetOutputFileName()
1779{
1780 return m_outputFilename;
1781}
1782