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// util.cpp
6//
7
8//
9// This contains a bunch of C++ utility classes.
10//
11//*****************************************************************************
12#include "stdafx.h" // Precompiled header key.
13#include "utilcode.h"
14#include "metadata.h"
15#include "ex.h"
16#include "pedecoder.h"
17#include "loaderheap.h"
18#include "sigparser.h"
19#include "cor.h"
20#include "corinfo.h"
21#include "volatile.h"
22
23#ifndef DACCESS_COMPILE
24UINT32 g_nClrInstanceId = 0;
25#endif //!DACCESS_COMPILE
26
27//********** Code. ************************************************************
28
29#if defined(FEATURE_COMINTEROP) && !defined(FEATURE_CORESYSTEM)
30extern WinRTStatusEnum gWinRTStatus = WINRT_STATUS_UNINITED;
31#endif // FEATURE_COMINTEROP && !FEATURE_CORESYSTEM
32
33#if defined(FEATURE_COMINTEROP) && !defined(FEATURE_CORESYSTEM)
34//------------------------------------------------------------------------------
35//
36// Attempt to detect the presense of Windows Runtime support on the current OS.
37// Our algorithm to do this is to ensure that:
38// 1. combase.dll exists
39// 2. combase.dll contains a RoInitialize export
40//
41
42void InitWinRTStatus()
43{
44 STATIC_CONTRACT_NOTHROW;
45 STATIC_CONTRACT_GC_NOTRIGGER;
46 STATIC_CONTRACT_CANNOT_TAKE_LOCK;
47 STATIC_CONTRACT_SO_TOLERANT;
48
49 WinRTStatusEnum winRTStatus = WINRT_STATUS_UNSUPPORTED;
50
51 const WCHAR wszComBaseDll[] = W("\\combase.dll");
52 const SIZE_T cchComBaseDll = _countof(wszComBaseDll);
53
54 WCHAR wszComBasePath[MAX_LONGPATH + 1];
55 const SIZE_T cchComBasePath = _countof(wszComBasePath);
56
57 ZeroMemory(wszComBasePath, cchComBasePath * sizeof(wszComBasePath[0]));
58
59 UINT cchSystemDirectory = WszGetSystemDirectory(wszComBasePath, MAX_LONGPATH);
60
61 // Make sure that we're only probing in the system directory. If we can't find the system directory, or
62 // we find it but combase.dll doesn't fit into it, we'll fall back to a safe default of saying that WinRT
63 // is simply not present.
64 if (cchSystemDirectory > 0 && cchComBasePath - cchSystemDirectory >= cchComBaseDll)
65 {
66 if (wcscat_s(wszComBasePath, wszComBaseDll) == 0)
67 {
68 HModuleHolder hComBase(WszLoadLibrary(wszComBasePath));
69 if (hComBase != NULL)
70 {
71 FARPROC activateInstace = GetProcAddress(hComBase, "RoInitialize");
72 if (activateInstace != NULL)
73 {
74 winRTStatus = WINRT_STATUS_SUPPORTED;
75 }
76 }
77 }
78 }
79
80 gWinRTStatus = winRTStatus;
81}
82#endif // FEATURE_COMINTEROP && !FEATURE_CORESYSTEM
83//*****************************************************************************
84// Convert a string of hex digits into a hex value of the specified # of bytes.
85//*****************************************************************************
86HRESULT GetHex( // Return status.
87 LPCSTR szStr, // String to convert.
88 int size, // # of bytes in pResult.
89 void *pResult) // Buffer for result.
90{
91 CONTRACTL
92 {
93 NOTHROW;
94 }
95 CONTRACTL_END;
96
97 int count = size * 2; // # of bytes to take from string.
98 unsigned int Result = 0; // Result value.
99 char ch;
100
101 _ASSERTE(size == 1 || size == 2 || size == 4);
102
103 while (count-- && (ch = *szStr++) != '\0')
104 {
105 switch (ch)
106 {
107 case '0': case '1': case '2': case '3': case '4':
108 case '5': case '6': case '7': case '8': case '9':
109 Result = 16 * Result + (ch - '0');
110 break;
111
112 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
113 Result = 16 * Result + 10 + (ch - 'A');
114 break;
115
116 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
117 Result = 16 * Result + 10 + (ch - 'a');
118 break;
119
120 default:
121 return (E_FAIL);
122 }
123 }
124
125 // Set the output.
126 switch (size)
127 {
128 case 1:
129 *((BYTE *) pResult) = (BYTE) Result;
130 break;
131
132 case 2:
133 *((WORD *) pResult) = (WORD) Result;
134 break;
135
136 case 4:
137 *((DWORD *) pResult) = Result;
138 break;
139
140 default:
141 _ASSERTE(0);
142 break;
143 }
144 return (S_OK);
145}
146
147//*****************************************************************************
148// Convert a pointer to a string into a GUID.
149//*****************************************************************************
150HRESULT LPCSTRToGuid( // Return status.
151 LPCSTR szGuid, // String to convert.
152 GUID *psGuid) // Buffer for converted GUID.
153{
154 CONTRACTL
155 {
156 NOTHROW;
157 }
158 CONTRACTL_END;
159
160 int i;
161
162 // Verify the surrounding syntax.
163 if (strlen(szGuid) != 38 || szGuid[0] != '{' || szGuid[9] != '-' ||
164 szGuid[14] != '-' || szGuid[19] != '-' || szGuid[24] != '-' || szGuid[37] != '}')
165 {
166 return (E_FAIL);
167 }
168
169 // Parse the first 3 fields.
170 if (FAILED(GetHex(szGuid + 1, 4, &psGuid->Data1)))
171 return E_FAIL;
172 if (FAILED(GetHex(szGuid + 10, 2, &psGuid->Data2)))
173 return E_FAIL;
174 if (FAILED(GetHex(szGuid + 15, 2, &psGuid->Data3)))
175 return E_FAIL;
176
177 // Get the last two fields (which are byte arrays).
178 for (i = 0; i < 2; ++i)
179 {
180 if (FAILED(GetHex(szGuid + 20 + (i * 2), 1, &psGuid->Data4[i])))
181 {
182 return E_FAIL;
183 }
184 }
185 for (i=0; i < 6; ++i)
186 {
187 if (FAILED(GetHex(szGuid + 25 + (i * 2), 1, &psGuid->Data4[i+2])))
188 {
189 return E_FAIL;
190 }
191 }
192 return S_OK;
193}
194
195//
196//
197// Global utility functions.
198//
199//
200
201
202
203typedef HRESULT __stdcall DLLGETCLASSOBJECT(REFCLSID rclsid,
204 REFIID riid,
205 void **ppv);
206
207EXTERN_C const IID _IID_IClassFactory =
208 {0x00000001, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
209
210// ----------------------------------------------------------------------------
211// FakeCoCreateInstanceEx
212//
213// Description:
214// A private function to do the equivalent of a CoCreateInstance in cases where we
215// can't make the real call. Use this when, for instance, you need to create a symbol
216// reader in the Runtime but we're not CoInitialized. Obviously, this is only good
217// for COM objects for which CoCreateInstance is just a glorified find-and-load-me
218// operation.
219//
220// Arguments:
221// * rclsid - [in] CLSID of object to instantiate
222// * wszDllPath [in] - Path to profiler DLL. If wszDllPath is NULL, FakeCoCreateInstanceEx
223// will look up the registry to find the path of the COM dll associated with rclsid.
224// If the path ends in a backslash, FakeCoCreateInstanceEx will treat this as a prefix
225// if the InprocServer32 found in the registry is a simple filename (not a full path).
226// This allows the caller to specify the directory in which the InprocServer32 should
227// be found. Also, if this path is provided and the InprocServer32 is MSCOREE.DLL, then
228// the Server value is used instead, if it exists.
229// * riid - [in] IID of interface on object to return in ppv
230// * ppv - [out] Pointer to implementation of requested interface
231// * phmodDll - [out] HMODULE of DLL that was loaded to instantiate the COM object.
232// The caller may eventually call FreeLibrary() on this if it can be determined
233// that we no longer reference the generated COM object or dependencies. Else, the
234// caller may ignore this and the DLL will stay loaded forever. If caller
235// specifies phmodDll==NULL, then this parameter is ignored and the HMODULE is not
236// returned.
237//
238// Return Value:
239// HRESULT indicating success or failure.
240//
241// Notes:
242// * (*phmodDll) on [out] may always be trusted, even if this function returns an
243// error. Therefore, even if creation of the COM object failed, if (*phmodDll !=
244// NULL), then the DLL was actually loaded. The caller may wish to call
245// FreeLibrary on (*phmodDll) in such a case.
246HRESULT FakeCoCreateInstanceEx(REFCLSID rclsid,
247 LPCWSTR wszDllPath,
248 REFIID riid,
249 void ** ppv,
250 HMODULE * phmodDll)
251{
252 CONTRACTL
253 {
254 THROWS;
255 }
256 CONTRACTL_END;
257
258 HRESULT hr = S_OK;
259
260 // Call the function to get a class factory for the rclsid passed in.
261 HModuleHolder hDll;
262 ReleaseHolder<IClassFactory> classFactory;
263 IfFailRet(FakeCoCallDllGetClassObject(rclsid, wszDllPath, _IID_IClassFactory, (void**)&classFactory, &hDll));
264
265 // Ask the class factory to create an instance of the
266 // necessary object.
267 IfFailRet(classFactory->CreateInstance(NULL, riid, ppv));
268
269 hDll.SuppressRelease();
270
271 if (phmodDll != NULL)
272 {
273 *phmodDll = hDll.GetValue();
274 }
275
276 return hr;
277}
278
279HRESULT FakeCoCallDllGetClassObject(REFCLSID rclsid,
280 LPCWSTR wszDllPath,
281 REFIID riid,
282 void ** ppv,
283 HMODULE * phmodDll)
284{
285 CONTRACTL
286 {
287 THROWS;
288 }
289 CONTRACTL_END;
290
291 _ASSERTE(ppv != NULL);
292
293 HRESULT hr = S_OK;
294
295 if (phmodDll != NULL)
296 { // Initialize [out] HMODULE (if it was requested)
297 *phmodDll = NULL;
298 }
299
300 bool fIsDllPathPrefix = (wszDllPath != NULL) && (wszDllPath[wcslen(wszDllPath) - 1] == W('\\'));
301
302 // - An empty string will be treated as NULL.
303 // - A string ending will a backslash will be treated as a prefix for where to look for the DLL
304 // if the InProcServer32 value is just a DLL name and not a full path.
305 StackSString ssDllName;
306 if ((wszDllPath == NULL) || (wszDllPath[0] == W('\0')) || fIsDllPathPrefix)
307 {
308#ifndef FEATURE_PAL
309 IfFailRet(Clr::Util::Com::FindInprocServer32UsingCLSID(rclsid, ssDllName));
310
311 EX_TRY
312 {
313 if (fIsDllPathPrefix)
314 {
315 if (Clr::Util::Com::IsMscoreeInprocServer32(ssDllName))
316 { // If the InprocServer32 is mscoree.dll, then we skip the shim and look for
317 // the corresponding server DLL (if it exists) in the directory provided.
318 hr = Clr::Util::Com::FindServerUsingCLSID(rclsid, ssDllName);
319
320 if (FAILED(hr))
321 { // We don't fail if there is no server object, because in this case we assume that
322 // the clsid is implemented in the runtime itself (clr.dll) and we do not place
323 // entries in the registry for this case.
324 ssDllName.Set(MAIN_CLR_MODULE_NAME_W);
325 }
326 }
327
328 SString::Iterator i = ssDllName.Begin();
329 if (!ssDllName.Find(i, W('\\')))
330 { // If the InprocServer32 is just a DLL name (not a fully qualified path), then
331 // prefix wszFilePath with wszDllPath.
332 ssDllName.Insert(i, wszDllPath);
333 }
334 }
335 }
336 EX_CATCH_HRESULT(hr);
337 IfFailRet(hr);
338
339 wszDllPath = ssDllName.GetUnicode();
340#else // !FEATURE_PAL
341 return E_FAIL;
342#endif // !FEATURE_PAL
343 }
344 _ASSERTE(wszDllPath != NULL);
345
346 // We've got the name of the DLL to load, so load it.
347 HModuleHolder hDll = WszLoadLibraryEx(wszDllPath, NULL, GetLoadWithAlteredSearchPathFlag());
348 if (hDll == NULL)
349 {
350 return HRESULT_FROM_GetLastError();
351 }
352
353 // We've loaded the DLL, so find the DllGetClassObject function.
354 DLLGETCLASSOBJECT *dllGetClassObject = (DLLGETCLASSOBJECT*)GetProcAddress(hDll, "DllGetClassObject");
355 if (dllGetClassObject == NULL)
356 {
357 return HRESULT_FROM_GetLastError();
358 }
359
360 // Call the function to get a class object for the rclsid and riid passed in.
361 IfFailRet(dllGetClassObject(rclsid, riid, ppv));
362
363 hDll.SuppressRelease();
364
365 if (phmodDll != NULL)
366 {
367 *phmodDll = hDll.GetValue();
368 }
369
370 return hr;
371}
372
373#if USE_UPPER_ADDRESS
374static BYTE * s_CodeMinAddr; // Preferred region to allocate the code in.
375static BYTE * s_CodeMaxAddr;
376static BYTE * s_CodeAllocStart;
377static BYTE * s_CodeAllocHint; // Next address to try to allocate for code in the preferred region.
378#endif
379
380//
381// Use this function to initialize the s_CodeAllocHint
382// during startup. base is runtime .dll base address,
383// size is runtime .dll virtual size.
384//
385void InitCodeAllocHint(SIZE_T base, SIZE_T size, int randomPageOffset)
386{
387#if USE_UPPER_ADDRESS
388
389#ifdef _DEBUG
390 // If GetForceRelocs is enabled we don't constrain the pMinAddr
391 if (PEDecoder::GetForceRelocs())
392 return;
393#endif
394
395//
396 // If we are using the UPPER_ADDRESS space (on Win64)
397 // then for any code heap that doesn't specify an address
398 // range using [pMinAddr..pMaxAddr] we place it in the
399 // upper address space
400 // This enables us to avoid having to use long JumpStubs
401 // to reach the code for our ngen-ed images.
402 // Which are also placed in the UPPER_ADDRESS space.
403 //
404 SIZE_T reach = 0x7FFF0000u;
405
406 // We will choose the preferred code region based on the address of clr.dll. The JIT helpers
407 // in clr.dll are the most heavily called functions.
408 s_CodeMinAddr = (base + size > reach) ? (BYTE *)(base + size - reach) : (BYTE *)0;
409 s_CodeMaxAddr = (base + reach > base) ? (BYTE *)(base + reach) : (BYTE *)-1;
410
411 BYTE * pStart;
412
413 if (s_CodeMinAddr <= (BYTE *)CODEHEAP_START_ADDRESS &&
414 (BYTE *)CODEHEAP_START_ADDRESS < s_CodeMaxAddr)
415 {
416 // clr.dll got loaded at its preferred base address? (OS without ASLR - pre-Vista)
417 // Use the code head start address that does not cause collisions with NGen images.
418 // This logic is coupled with scripts that we use to assign base addresses.
419 pStart = (BYTE *)CODEHEAP_START_ADDRESS;
420 }
421 else
422 if (base > UINT32_MAX)
423 {
424 // clr.dll got address assigned by ASLR?
425 // Try to occupy the space as far as possible to minimize collisions with other ASLR assigned
426 // addresses. Do not start at s_CodeMinAddr exactly so that we can also reach common native images
427 // that can be placed at higher addresses than clr.dll.
428 pStart = s_CodeMinAddr + (s_CodeMaxAddr - s_CodeMinAddr) / 8;
429 }
430 else
431 {
432 // clr.dll missed the base address?
433 // Try to occupy the space right after it.
434 pStart = (BYTE *)(base + size);
435 }
436
437 // Randomize the adddress space
438 pStart += GetOsPageSize() * randomPageOffset;
439
440 s_CodeAllocStart = pStart;
441 s_CodeAllocHint = pStart;
442#endif
443}
444
445//
446// Use this function to reset the s_CodeAllocHint
447// after unloading an AppDomain
448//
449void ResetCodeAllocHint()
450{
451 LIMITED_METHOD_CONTRACT;
452#if USE_UPPER_ADDRESS
453 s_CodeAllocHint = s_CodeAllocStart;
454#endif
455}
456
457//
458// Returns TRUE if p is located in near clr.dll that allows us
459// to use rel32 IP-relative addressing modes.
460//
461BOOL IsPreferredExecutableRange(void * p)
462{
463 LIMITED_METHOD_CONTRACT;
464#if USE_UPPER_ADDRESS
465 if (s_CodeMinAddr <= (BYTE *)p && (BYTE *)p < s_CodeMaxAddr)
466 return TRUE;
467#endif
468 return FALSE;
469}
470
471//
472// Allocate free memory that will be used for executable code
473// Handles the special requirements that we have on 64-bit platforms
474// where we want the executable memory to be located near clr.dll
475//
476BYTE * ClrVirtualAllocExecutable(SIZE_T dwSize,
477 DWORD flAllocationType,
478 DWORD flProtect)
479{
480 CONTRACTL
481 {
482 NOTHROW;
483 }
484 CONTRACTL_END;
485
486#if USE_UPPER_ADDRESS
487 //
488 // If we are using the UPPER_ADDRESS space (on Win64)
489 // then for any heap that will contain executable code
490 // we will place it in the upper address space
491 //
492 // This enables us to avoid having to use JumpStubs
493 // to reach the code for our ngen-ed images on x64,
494 // since they are also placed in the UPPER_ADDRESS space.
495 //
496 BYTE * pHint = s_CodeAllocHint;
497
498 if (dwSize <= (SIZE_T)(s_CodeMaxAddr - s_CodeMinAddr) && pHint != NULL)
499 {
500 // Try to allocate in the preferred region after the hint
501 BYTE * pResult = ClrVirtualAllocWithinRange(pHint, s_CodeMaxAddr, dwSize, flAllocationType, flProtect);
502
503 if (pResult != NULL)
504 {
505 s_CodeAllocHint = pResult + dwSize;
506 return pResult;
507 }
508
509 // Try to allocate in the preferred region before the hint
510 pResult = ClrVirtualAllocWithinRange(s_CodeMinAddr, pHint + dwSize, dwSize, flAllocationType, flProtect);
511
512 if (pResult != NULL)
513 {
514 s_CodeAllocHint = pResult + dwSize;
515 return pResult;
516 }
517
518 s_CodeAllocHint = NULL;
519 }
520
521 // Fall through to
522#endif // USE_UPPER_ADDRESS
523
524#ifdef FEATURE_PAL
525 // Tell PAL to use the executable memory allocator to satisfy this request for virtual memory.
526 // This will allow us to place JIT'ed code close to the coreclr library
527 // and thus improve performance by avoiding jump stubs in managed code.
528 flAllocationType |= MEM_RESERVE_EXECUTABLE;
529#endif // FEATURE_PAL
530
531 return (BYTE *) ClrVirtualAlloc (NULL, dwSize, flAllocationType, flProtect);
532
533}
534
535//
536// Allocate free memory with specific alignment.
537//
538LPVOID ClrVirtualAllocAligned(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect, SIZE_T alignment)
539{
540 // Verify that the alignment is a power of 2
541 _ASSERTE(alignment != 0);
542 _ASSERTE((alignment & (alignment - 1)) == 0);
543
544#ifndef FEATURE_PAL
545
546 // The VirtualAlloc on Windows ensures 64kB alignment
547 _ASSERTE(alignment <= 0x10000);
548 return ClrVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
549
550#else // !FEATURE_PAL
551
552 if(alignment < GetOsPageSize()) alignment = GetOsPageSize();
553
554 // UNIXTODO: Add a specialized function to PAL so that we don't have to waste memory
555 dwSize += alignment;
556 SIZE_T addr = (SIZE_T)ClrVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
557 return (LPVOID)((addr + (alignment - 1)) & ~(alignment - 1));
558
559#endif // !FEATURE_PAL
560}
561
562#ifdef _DEBUG
563static DWORD ShouldInjectFaultInRange()
564{
565 static DWORD fInjectFaultInRange = 99;
566
567 if (fInjectFaultInRange == 99)
568 fInjectFaultInRange = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_InjectFault) & 0x40);
569 return fInjectFaultInRange;
570}
571#endif
572
573// Reserves free memory within the range [pMinAddr..pMaxAddr] using
574// ClrVirtualQuery to find free memory and ClrVirtualAlloc to reserve it.
575//
576// This method only supports the flAllocationType of MEM_RESERVE, and expects that the memory
577// is being reserved for the purpose of eventually storing executable code.
578//
579// Callers also should set dwSize to a multiple of sysInfo.dwAllocationGranularity (64k).
580// That way they can reserve a large region and commit smaller sized pages
581// from that region until it fills up.
582//
583// This functions returns the reserved memory block upon success
584//
585// It returns NULL when it fails to find any memory that satisfies
586// the range.
587//
588
589BYTE * ClrVirtualAllocWithinRange(const BYTE *pMinAddr,
590 const BYTE *pMaxAddr,
591 SIZE_T dwSize,
592 DWORD flAllocationType,
593 DWORD flProtect)
594{
595 CONTRACTL
596 {
597 NOTHROW;
598 PRECONDITION(dwSize != 0);
599 PRECONDITION(flAllocationType == MEM_RESERVE);
600 }
601 CONTRACTL_END;
602
603 BYTE *pResult = nullptr; // our return value;
604
605 static unsigned countOfCalls = 0; // We log the number of tims we call this method
606 countOfCalls++; // increment the call counter
607
608 if (dwSize == 0)
609 {
610 return nullptr;
611 }
612
613 //
614 // First lets normalize the pMinAddr and pMaxAddr values
615 //
616 // If pMinAddr is NULL then set it to BOT_MEMORY
617 if ((pMinAddr == 0) || (pMinAddr < (BYTE *) BOT_MEMORY))
618 {
619 pMinAddr = (BYTE *) BOT_MEMORY;
620 }
621
622 // If pMaxAddr is NULL then set it to TOP_MEMORY
623 if ((pMaxAddr == 0) || (pMaxAddr > (BYTE *) TOP_MEMORY))
624 {
625 pMaxAddr = (BYTE *) TOP_MEMORY;
626 }
627
628 // If pMaxAddr is not greater than pMinAddr we can not make an allocation
629 if (pMaxAddr <= pMinAddr)
630 {
631 return nullptr;
632 }
633
634 // If pMinAddr is BOT_MEMORY and pMaxAddr is TOP_MEMORY
635 // then we can call ClrVirtualAlloc instead
636 if ((pMinAddr == (BYTE *) BOT_MEMORY) && (pMaxAddr == (BYTE *) TOP_MEMORY))
637 {
638 return (BYTE*) ClrVirtualAlloc(nullptr, dwSize, flAllocationType, flProtect);
639 }
640
641#ifdef FEATURE_PAL
642 pResult = (BYTE *)PAL_VirtualReserveFromExecutableMemoryAllocatorWithinRange(pMinAddr, pMaxAddr, dwSize);
643 if (pResult != nullptr)
644 {
645 return pResult;
646 }
647#endif // FEATURE_PAL
648
649 // We will do one scan from [pMinAddr .. pMaxAddr]
650 // First align the tryAddr up to next 64k base address.
651 // See docs for VirtualAllocEx and lpAddress and 64k alignment for reasons.
652 //
653 BYTE * tryAddr = (BYTE *)ALIGN_UP((BYTE *)pMinAddr, VIRTUAL_ALLOC_RESERVE_GRANULARITY);
654 bool virtualQueryFailed = false;
655 bool faultInjected = false;
656 unsigned virtualQueryCount = 0;
657
658 // Now scan memory and try to find a free block of the size requested.
659 while ((tryAddr + dwSize) <= (BYTE *) pMaxAddr)
660 {
661 MEMORY_BASIC_INFORMATION mbInfo;
662
663 // Use VirtualQuery to find out if this address is MEM_FREE
664 //
665 virtualQueryCount++;
666 if (!ClrVirtualQuery((LPCVOID)tryAddr, &mbInfo, sizeof(mbInfo)))
667 {
668 // Exit and return nullptr if the VirtualQuery call fails.
669 virtualQueryFailed = true;
670 break;
671 }
672
673 // Is there enough memory free from this start location?
674 // Note that for most versions of UNIX the mbInfo.RegionSize returned will always be 0
675 if ((mbInfo.State == MEM_FREE) &&
676 (mbInfo.RegionSize >= (SIZE_T) dwSize || mbInfo.RegionSize == 0))
677 {
678 // Try reserving the memory using VirtualAlloc now
679 pResult = (BYTE*)ClrVirtualAlloc(tryAddr, dwSize, MEM_RESERVE, flProtect);
680
681 // Normally this will be successful
682 //
683 if (pResult != nullptr)
684 {
685 // return pResult
686 break;
687 }
688
689#ifdef _DEBUG
690 if (ShouldInjectFaultInRange())
691 {
692 // return nullptr (failure)
693 faultInjected = true;
694 break;
695 }
696#endif // _DEBUG
697
698 // On UNIX we can also fail if our request size 'dwSize' is larger than 64K and
699 // and our tryAddr is pointing at a small MEM_FREE region (smaller than 'dwSize')
700 // However we can't distinguish between this and the race case.
701
702 // We might fail in a race. So just move on to next region and continue trying
703 tryAddr = tryAddr + VIRTUAL_ALLOC_RESERVE_GRANULARITY;
704 }
705 else
706 {
707 // Try another section of memory
708 tryAddr = max(tryAddr + VIRTUAL_ALLOC_RESERVE_GRANULARITY,
709 (BYTE*) mbInfo.BaseAddress + mbInfo.RegionSize);
710 }
711 }
712
713 STRESS_LOG7(LF_JIT, LL_INFO100,
714 "ClrVirtualAllocWithinRange request #%u for %08x bytes in [ %p .. %p ], query count was %u - returned %s: %p\n",
715 countOfCalls, (DWORD)dwSize, pMinAddr, pMaxAddr,
716 virtualQueryCount, (pResult != nullptr) ? "success" : "failure", pResult);
717
718 // If we failed this call the process will typically be terminated
719 // so we log any additional reason for failing this call.
720 //
721 if (pResult == nullptr)
722 {
723 if ((tryAddr + dwSize) > (BYTE *)pMaxAddr)
724 {
725 // Our tryAddr reached pMaxAddr
726 STRESS_LOG0(LF_JIT, LL_INFO100, "Additional reason: Address space exhausted.\n");
727 }
728
729 if (virtualQueryFailed)
730 {
731 STRESS_LOG0(LF_JIT, LL_INFO100, "Additional reason: VirtualQuery operation failed.\n");
732 }
733
734 if (faultInjected)
735 {
736 STRESS_LOG0(LF_JIT, LL_INFO100, "Additional reason: fault injected.\n");
737 }
738 }
739
740 return pResult;
741}
742
743//******************************************************************************
744// NumaNodeInfo
745//******************************************************************************
746#if !defined(FEATURE_REDHAWK)
747/*static*/ NumaNodeInfo::PGNHNN NumaNodeInfo::m_pGetNumaHighestNodeNumber = NULL;
748/*static*/ NumaNodeInfo::PVAExN NumaNodeInfo::m_pVirtualAllocExNuma = NULL;
749
750/*static*/ LPVOID NumaNodeInfo::VirtualAllocExNuma(HANDLE hProc, LPVOID lpAddr, SIZE_T dwSize,
751 DWORD allocType, DWORD prot, DWORD node)
752{
753 return (*m_pVirtualAllocExNuma)(hProc, lpAddr, dwSize, allocType, prot, node);
754}
755/*static*/ NumaNodeInfo::PGNPNEx NumaNodeInfo::m_pGetNumaProcessorNodeEx = NULL;
756
757/*static*/ BOOL NumaNodeInfo::GetNumaProcessorNodeEx(PPROCESSOR_NUMBER proc_no, PUSHORT node_no)
758{
759 return (*m_pGetNumaProcessorNodeEx)(proc_no, node_no);
760}
761#endif
762
763/*static*/ BOOL NumaNodeInfo::m_enableGCNumaAware = FALSE;
764/*static*/ BOOL NumaNodeInfo::InitNumaNodeInfoAPI()
765{
766#if !defined(FEATURE_REDHAWK)
767 //check for numa support if multiple heaps are used
768 ULONG highest = 0;
769
770 if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_GCNumaAware) == 0)
771 return FALSE;
772
773#ifndef FEATURE_PAL
774 // check if required APIs are supported
775 HMODULE hMod = GetModuleHandleW(WINDOWS_KERNEL32_DLLNAME_W);
776#else
777 HMODULE hMod = GetCLRModule();
778#endif
779 if (hMod == NULL)
780 return FALSE;
781
782 m_pGetNumaHighestNodeNumber = (PGNHNN) GetProcAddress(hMod, "GetNumaHighestNodeNumber");
783 if (m_pGetNumaHighestNodeNumber == NULL)
784 return FALSE;
785
786 // fail to get the highest numa node number
787 if (!m_pGetNumaHighestNodeNumber(&highest) || (highest == 0))
788 return FALSE;
789
790 m_pGetNumaProcessorNodeEx = (PGNPNEx) GetProcAddress(hMod, "GetNumaProcessorNodeEx");
791 if (m_pGetNumaProcessorNodeEx == NULL)
792 return FALSE;
793
794 m_pVirtualAllocExNuma = (PVAExN) GetProcAddress(hMod, "VirtualAllocExNuma");
795 if (m_pVirtualAllocExNuma == NULL)
796 return FALSE;
797
798 return TRUE;
799#else
800 return FALSE;
801#endif
802}
803
804/*static*/ BOOL NumaNodeInfo::CanEnableGCNumaAware()
805{
806 return m_enableGCNumaAware;
807}
808
809/*static*/ void NumaNodeInfo::InitNumaNodeInfo()
810{
811 m_enableGCNumaAware = InitNumaNodeInfoAPI();
812}
813
814//******************************************************************************
815// NumaNodeInfo
816//******************************************************************************
817#if !defined(FEATURE_REDHAWK)
818/*static*/ CPUGroupInfo::PGLPIEx CPUGroupInfo::m_pGetLogicalProcessorInformationEx = NULL;
819/*static*/ CPUGroupInfo::PSTGA CPUGroupInfo::m_pSetThreadGroupAffinity = NULL;
820/*static*/ CPUGroupInfo::PGTGA CPUGroupInfo::m_pGetThreadGroupAffinity = NULL;
821/*static*/ CPUGroupInfo::PGCPNEx CPUGroupInfo::m_pGetCurrentProcessorNumberEx = NULL;
822/*static*/ CPUGroupInfo::PGST CPUGroupInfo::m_pGetSystemTimes = NULL;
823/*static*/ //CPUGroupInfo::PNTQSIEx CPUGroupInfo::m_pNtQuerySystemInformationEx = NULL;
824
825/*static*/ BOOL CPUGroupInfo::GetLogicalProcessorInformationEx(DWORD relationship,
826 SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *slpiex, PDWORD count)
827{
828 LIMITED_METHOD_CONTRACT;
829 return (*m_pGetLogicalProcessorInformationEx)(relationship, slpiex, count);
830}
831
832/*static*/ BOOL CPUGroupInfo::SetThreadGroupAffinity(HANDLE h,
833 GROUP_AFFINITY *groupAffinity, GROUP_AFFINITY *previousGroupAffinity)
834{
835 LIMITED_METHOD_CONTRACT;
836 return (*m_pSetThreadGroupAffinity)(h, groupAffinity, previousGroupAffinity);
837}
838
839/*static*/ BOOL CPUGroupInfo::GetThreadGroupAffinity(HANDLE h, GROUP_AFFINITY *groupAffinity)
840{
841 LIMITED_METHOD_CONTRACT;
842 return (*m_pGetThreadGroupAffinity)(h, groupAffinity);
843}
844
845/*static*/ BOOL CPUGroupInfo::GetSystemTimes(FILETIME *idleTime, FILETIME *kernelTime, FILETIME *userTime)
846{
847 LIMITED_METHOD_CONTRACT;
848 return (*m_pGetSystemTimes)(idleTime, kernelTime, userTime);
849}
850#endif
851
852/*static*/ BOOL CPUGroupInfo::m_enableGCCPUGroups = FALSE;
853/*static*/ BOOL CPUGroupInfo::m_threadUseAllCpuGroups = FALSE;
854/*static*/ WORD CPUGroupInfo::m_nGroups = 0;
855/*static*/ WORD CPUGroupInfo::m_nProcessors = 0;
856/*static*/ WORD CPUGroupInfo::m_initialGroup = 0;
857/*static*/ CPU_Group_Info *CPUGroupInfo::m_CPUGroupInfoArray = NULL;
858/*static*/ LONG CPUGroupInfo::m_initialization = 0;
859/*static*/ bool CPUGroupInfo::s_hadSingleProcessorAtStartup = false;
860
861// Check and setup function pointers for >64 LP Support
862/*static*/ BOOL CPUGroupInfo::InitCPUGroupInfoAPI()
863{
864 CONTRACTL
865 {
866 NOTHROW;
867 GC_NOTRIGGER;
868 }
869 CONTRACTL_END;
870
871#if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
872#ifndef FEATURE_PAL
873 HMODULE hMod = GetModuleHandleW(WINDOWS_KERNEL32_DLLNAME_W);
874#else
875 HMODULE hMod = GetCLRModule();
876#endif
877 if (hMod == NULL)
878 return FALSE;
879
880 m_pGetLogicalProcessorInformationEx = (PGLPIEx)GetProcAddress(hMod, "GetLogicalProcessorInformationEx");
881 if (m_pGetLogicalProcessorInformationEx == NULL)
882 return FALSE;
883
884 m_pSetThreadGroupAffinity = (PSTGA)GetProcAddress(hMod, "SetThreadGroupAffinity");
885 if (m_pSetThreadGroupAffinity == NULL)
886 return FALSE;
887
888 m_pGetThreadGroupAffinity = (PGTGA)GetProcAddress(hMod, "GetThreadGroupAffinity");
889 if (m_pGetThreadGroupAffinity == NULL)
890 return FALSE;
891
892 m_pGetCurrentProcessorNumberEx = (PGCPNEx)GetProcAddress(hMod, "GetCurrentProcessorNumberEx");
893 if (m_pGetCurrentProcessorNumberEx == NULL)
894 return FALSE;
895
896#ifndef FEATURE_PAL
897 m_pGetSystemTimes = (PGST)GetProcAddress(hMod, "GetSystemTimes");
898 if (m_pGetSystemTimes == NULL)
899 return FALSE;
900#endif
901
902 return TRUE;
903#else
904 return FALSE;
905#endif
906}
907
908#if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
909// Calculate greatest common divisor
910DWORD GCD(DWORD u, DWORD v)
911{
912 while (v != 0)
913 {
914 DWORD dwTemp = v;
915 v = u % v;
916 u = dwTemp;
917 }
918
919 return u;
920}
921
922// Calculate least common multiple
923DWORD LCM(DWORD u, DWORD v)
924{
925 return u / GCD(u, v) * v;
926}
927#endif
928
929/*static*/ BOOL CPUGroupInfo::InitCPUGroupInfoArray()
930{
931 CONTRACTL
932 {
933 NOTHROW;
934 SO_TOLERANT;
935 GC_NOTRIGGER;
936 }
937 CONTRACTL_END;
938
939#if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
940 BYTE *bBuffer = NULL;
941 SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pSLPIEx = NULL;
942 SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pRecord = NULL;
943 DWORD cbSLPIEx = 0;
944 DWORD byteOffset = 0;
945 DWORD dwNumElements = 0;
946 DWORD dwWeight = 1;
947
948 if (CPUGroupInfo::GetLogicalProcessorInformationEx(RelationGroup, pSLPIEx, &cbSLPIEx) &&
949 GetLastError() != ERROR_INSUFFICIENT_BUFFER)
950 return FALSE;
951
952 _ASSERTE(cbSLPIEx);
953
954 // Fail to allocate buffer
955 bBuffer = new (nothrow) BYTE[ cbSLPIEx ];
956 if (bBuffer == NULL)
957 return FALSE;
958
959 pSLPIEx = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)bBuffer;
960 if (!m_pGetLogicalProcessorInformationEx(RelationGroup, pSLPIEx, &cbSLPIEx))
961 {
962 delete[] bBuffer;
963 return FALSE;
964 }
965
966 pRecord = pSLPIEx;
967 while (byteOffset < cbSLPIEx)
968 {
969 if (pRecord->Relationship == RelationGroup)
970 {
971 m_nGroups = pRecord->Group.ActiveGroupCount;
972 break;
973 }
974 byteOffset += pRecord->Size;
975 pRecord = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)(bBuffer + byteOffset);
976 }
977
978 m_CPUGroupInfoArray = new (nothrow) CPU_Group_Info[m_nGroups];
979 if (m_CPUGroupInfoArray == NULL)
980 {
981 delete[] bBuffer;
982 return FALSE;
983 }
984
985 for (DWORD i = 0; i < m_nGroups; i++)
986 {
987 m_CPUGroupInfoArray[i].nr_active = (WORD)pRecord->Group.GroupInfo[i].ActiveProcessorCount;
988 m_CPUGroupInfoArray[i].active_mask = pRecord->Group.GroupInfo[i].ActiveProcessorMask;
989 m_nProcessors += m_CPUGroupInfoArray[i].nr_active;
990 dwWeight = LCM(dwWeight, (DWORD)m_CPUGroupInfoArray[i].nr_active);
991 }
992
993 // The number of threads per group that can be supported will depend on the number of CPU groups
994 // and the number of LPs within each processor group. For example, when the number of LPs in
995 // CPU groups is the same and is 64, the number of threads per group before weight overflow
996 // would be 2^32/2^6 = 2^26 (64M threads)
997 for (DWORD i = 0; i < m_nGroups; i++)
998 {
999 m_CPUGroupInfoArray[i].groupWeight = dwWeight / (DWORD)m_CPUGroupInfoArray[i].nr_active;
1000 m_CPUGroupInfoArray[i].activeThreadWeight = 0;
1001 }
1002
1003 delete[] bBuffer; // done with it; free it
1004 return TRUE;
1005#else
1006 return FALSE;
1007#endif
1008}
1009
1010/*static*/ BOOL CPUGroupInfo::InitCPUGroupInfoRange()
1011{
1012 LIMITED_METHOD_CONTRACT;
1013
1014#if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
1015 WORD begin = 0;
1016 WORD nr_proc = 0;
1017
1018 for (WORD i = 0; i < m_nGroups; i++)
1019 {
1020 nr_proc += m_CPUGroupInfoArray[i].nr_active;
1021 m_CPUGroupInfoArray[i].begin = begin;
1022 m_CPUGroupInfoArray[i].end = nr_proc - 1;
1023 begin = nr_proc;
1024 }
1025 return TRUE;
1026#else
1027 return FALSE;
1028#endif
1029}
1030
1031/*static*/ void CPUGroupInfo::InitCPUGroupInfo()
1032{
1033 CONTRACTL
1034 {
1035 NOTHROW;
1036 SO_TOLERANT;
1037 GC_NOTRIGGER;
1038 }
1039 CONTRACTL_END;
1040
1041#if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
1042 BOOL enableGCCPUGroups = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_GCCpuGroup) != 0;
1043 BOOL threadUseAllCpuGroups = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_Thread_UseAllCpuGroups) != 0;
1044
1045 if (!enableGCCPUGroups)
1046 return;
1047
1048 if (!InitCPUGroupInfoAPI())
1049 return;
1050
1051 if (!InitCPUGroupInfoArray())
1052 return;
1053
1054 if (!InitCPUGroupInfoRange())
1055 return;
1056
1057 // initalGroup is whatever the CPU group that the main thread is running on
1058 GROUP_AFFINITY groupAffinity;
1059 CPUGroupInfo::GetThreadGroupAffinity(GetCurrentThread(), &groupAffinity);
1060 m_initialGroup = groupAffinity.Group;
1061
1062 // only enable CPU groups if more than one group exists
1063 BOOL hasMultipleGroups = m_nGroups > 1;
1064 m_enableGCCPUGroups = enableGCCPUGroups && hasMultipleGroups;
1065 m_threadUseAllCpuGroups = threadUseAllCpuGroups && hasMultipleGroups;
1066#endif // _TARGET_AMD64_ || _TARGET_ARM64_
1067
1068 // Determine if the process is affinitized to a single processor (or if the system has a single processor)
1069 DWORD_PTR processAffinityMask, systemAffinityMask;
1070 if (GetProcessAffinityMask(GetCurrentProcess(), &processAffinityMask, &systemAffinityMask))
1071 {
1072 processAffinityMask &= systemAffinityMask;
1073 if (processAffinityMask != 0 && // only one CPU group is involved
1074 (processAffinityMask & (processAffinityMask - 1)) == 0) // only one bit is set
1075 {
1076 s_hadSingleProcessorAtStartup = true;
1077 }
1078 }
1079}
1080
1081/*static*/ BOOL CPUGroupInfo::IsInitialized()
1082{
1083 LIMITED_METHOD_CONTRACT;
1084 return (m_initialization == -1);
1085}
1086
1087/*static*/ void CPUGroupInfo::EnsureInitialized()
1088{
1089 CONTRACTL
1090 {
1091 NOTHROW;
1092 SO_TOLERANT;
1093 GC_NOTRIGGER;
1094 }
1095 CONTRACTL_END;
1096
1097 // CPUGroupInfo needs to be initialized only once. This could happen in three cases
1098 // 1. CLR initialization at begining of EEStartup, or
1099 // 2. Sometimes, when hosted by ASP.NET, the hosting process may initialize ThreadPool
1100 // before initializing CLR, thus require CPUGroupInfo to be initialized to determine
1101 // if CPU group support should/could be enabled.
1102 // 3. Call into Threadpool functions before Threadpool _and_ CLR is initialized.
1103 // Vast majority of time, CPUGroupInfo is initialized in case 1. or 2.
1104 // The chance of contention will be extremely small, so the following code should be fine
1105 //
1106retry:
1107 if (IsInitialized())
1108 return;
1109
1110 if (InterlockedCompareExchange(&m_initialization, 1, 0) == 0)
1111 {
1112 InitCPUGroupInfo();
1113 m_initialization = -1;
1114 }
1115 else //some other thread started initialization, just wait until complete;
1116 {
1117 while (m_initialization != -1)
1118 {
1119 SwitchToThread();
1120 }
1121 goto retry;
1122 }
1123}
1124
1125/*static*/ WORD CPUGroupInfo::GetNumActiveProcessors()
1126{
1127 LIMITED_METHOD_CONTRACT;
1128 return (WORD)m_nProcessors;
1129}
1130
1131/*static*/ void CPUGroupInfo::GetGroupForProcessor(WORD processor_number,
1132 WORD* group_number, WORD* group_processor_number)
1133{
1134 LIMITED_METHOD_CONTRACT;
1135
1136#if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
1137 WORD bTemp = 0;
1138 WORD bDiff = processor_number - bTemp;
1139
1140 for (WORD i=0; i < m_nGroups; i++)
1141 {
1142 bTemp += m_CPUGroupInfoArray[i].nr_active;
1143 if (bTemp > processor_number)
1144 {
1145 *group_number = i;
1146 *group_processor_number = bDiff;
1147 break;
1148 }
1149 bDiff = processor_number - bTemp;
1150 }
1151#else
1152 *group_number = 0;
1153 *group_processor_number = 0;
1154#endif
1155}
1156
1157/*static*/ DWORD CPUGroupInfo::CalculateCurrentProcessorNumber()
1158{
1159 CONTRACTL
1160 {
1161 NOTHROW;
1162 SO_TOLERANT;
1163 GC_NOTRIGGER;
1164 }
1165 CONTRACTL_END;
1166
1167#if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
1168 // m_enableGCCPUGroups and m_threadUseAllCpuGroups must be TRUE
1169 _ASSERTE(m_enableGCCPUGroups && m_threadUseAllCpuGroups);
1170
1171 PROCESSOR_NUMBER proc_no;
1172 proc_no.Group=0;
1173 proc_no.Number=0;
1174 proc_no.Reserved=0;
1175 (*m_pGetCurrentProcessorNumberEx)(&proc_no);
1176
1177 DWORD fullNumber = 0;
1178 for (WORD i = 0; i < proc_no.Group; i++)
1179 fullNumber += (DWORD)m_CPUGroupInfoArray[i].nr_active;
1180 fullNumber += (DWORD)(proc_no.Number);
1181
1182 return fullNumber;
1183#else
1184 return 0;
1185#endif
1186}
1187
1188#if !defined(FEATURE_REDHAWK)
1189//Lock ThreadStore before calling this function, so that updates of weights/counts are consistent
1190/*static*/ void CPUGroupInfo::ChooseCPUGroupAffinity(GROUP_AFFINITY *gf)
1191{
1192 CONTRACTL
1193 {
1194 NOTHROW;
1195 GC_NOTRIGGER;
1196 }
1197 CONTRACTL_END;
1198
1199#if (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
1200 WORD i, minGroup = 0;
1201 DWORD minWeight = 0;
1202
1203 // m_enableGCCPUGroups and m_threadUseAllCpuGroups must be TRUE
1204 _ASSERTE(m_enableGCCPUGroups && m_threadUseAllCpuGroups);
1205
1206 for (i = 0; i < m_nGroups; i++)
1207 {
1208 minGroup = (m_initialGroup + i) % m_nGroups;
1209
1210 // the group is not filled up, use it
1211 if (m_CPUGroupInfoArray[minGroup].activeThreadWeight / m_CPUGroupInfoArray[minGroup].groupWeight
1212 < (DWORD)m_CPUGroupInfoArray[minGroup].nr_active)
1213 goto found;
1214 }
1215
1216 // all groups filled up, distribute proportionally
1217 minGroup = m_initialGroup;
1218 minWeight = m_CPUGroupInfoArray[m_initialGroup].activeThreadWeight;
1219 for (i = 0; i < m_nGroups; i++)
1220 {
1221 if (m_CPUGroupInfoArray[i].activeThreadWeight < minWeight)
1222 {
1223 minGroup = i;
1224 minWeight = m_CPUGroupInfoArray[i].activeThreadWeight;
1225 }
1226 }
1227
1228found:
1229 gf->Group = minGroup;
1230 gf->Mask = m_CPUGroupInfoArray[minGroup].active_mask;
1231 gf->Reserved[0] = 0;
1232 gf->Reserved[1] = 0;
1233 gf->Reserved[2] = 0;
1234 m_CPUGroupInfoArray[minGroup].activeThreadWeight += m_CPUGroupInfoArray[minGroup].groupWeight;
1235#endif
1236}
1237
1238//Lock ThreadStore before calling this function, so that updates of weights/counts are consistent
1239/*static*/ void CPUGroupInfo::ClearCPUGroupAffinity(GROUP_AFFINITY *gf)
1240{
1241 LIMITED_METHOD_CONTRACT;
1242#if (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
1243 // m_enableGCCPUGroups and m_threadUseAllCpuGroups must be TRUE
1244 _ASSERTE(m_enableGCCPUGroups && m_threadUseAllCpuGroups);
1245
1246 WORD group = gf->Group;
1247 m_CPUGroupInfoArray[group].activeThreadWeight -= m_CPUGroupInfoArray[group].groupWeight;
1248#endif
1249}
1250#endif
1251
1252/*static*/ BOOL CPUGroupInfo::CanEnableGCCPUGroups()
1253{
1254 LIMITED_METHOD_CONTRACT;
1255 return m_enableGCCPUGroups;
1256}
1257
1258/*static*/ BOOL CPUGroupInfo::CanEnableThreadUseAllCpuGroups()
1259{
1260 LIMITED_METHOD_CONTRACT;
1261 return m_threadUseAllCpuGroups;
1262}
1263
1264//******************************************************************************
1265// Returns the number of processors that a process has been configured to run on
1266//******************************************************************************
1267int GetCurrentProcessCpuCount()
1268{
1269 CONTRACTL
1270 {
1271 NOTHROW;
1272 SO_TOLERANT;
1273 CANNOT_TAKE_LOCK;
1274 }
1275 CONTRACTL_END;
1276
1277 static int cCPUs = 0;
1278
1279 if (cCPUs != 0)
1280 return cCPUs;
1281
1282 int count = 0;
1283 DWORD_PTR pmask, smask;
1284
1285 if (!GetProcessAffinityMask(GetCurrentProcess(), &pmask, &smask))
1286 {
1287 count = 1;
1288 }
1289 else
1290 {
1291 pmask &= smask;
1292
1293 while (pmask)
1294 {
1295 pmask &= (pmask - 1);
1296 count++;
1297 }
1298
1299 // GetProcessAffinityMask can return pmask=0 and smask=0 on systems with more
1300 // than 64 processors, which would leave us with a count of 0. Since the GC
1301 // expects there to be at least one processor to run on (and thus at least one
1302 // heap), we'll return 64 here if count is 0, since there are likely a ton of
1303 // processors available in that case. The GC also cannot (currently) handle
1304 // the case where there are more than 64 processors, so we will return a
1305 // maximum of 64 here.
1306 if (count == 0 || count > 64)
1307 count = 64;
1308 }
1309
1310#ifdef FEATURE_PAL
1311 uint32_t cpuLimit;
1312
1313 if (PAL_GetCpuLimit(&cpuLimit) && cpuLimit < count)
1314 count = cpuLimit;
1315#endif
1316
1317 cCPUs = count;
1318
1319 return count;
1320}
1321
1322DWORD_PTR GetCurrentProcessCpuMask()
1323{
1324 CONTRACTL
1325 {
1326 NOTHROW;
1327 SO_TOLERANT;
1328 CANNOT_TAKE_LOCK;
1329 }
1330 CONTRACTL_END;
1331
1332#ifndef FEATURE_PAL
1333 DWORD_PTR pmask, smask;
1334
1335 if (!GetProcessAffinityMask(GetCurrentProcess(), &pmask, &smask))
1336 return 1;
1337
1338 pmask &= smask;
1339 return pmask;
1340#else
1341 return 0;
1342#endif
1343}
1344
1345uint32_t GetOsPageSizeUncached()
1346{
1347 SYSTEM_INFO sysInfo;
1348 ::GetSystemInfo(&sysInfo);
1349 return sysInfo.dwAllocationGranularity ? sysInfo.dwAllocationGranularity : 0x1000;
1350}
1351
1352namespace
1353{
1354 Volatile<uint32_t> g_pageSize = 0;
1355}
1356
1357uint32_t GetOsPageSize()
1358{
1359#ifdef FEATURE_PAL
1360 size_t result = g_pageSize.LoadWithoutBarrier();
1361
1362 if(!result)
1363 {
1364 result = GetOsPageSizeUncached();
1365
1366 g_pageSize.StoreWithoutBarrier(result);
1367 }
1368
1369 return result;
1370#else
1371 return 0x1000;
1372#endif
1373}
1374
1375/**************************************************************************/
1376
1377/**************************************************************************/
1378void ConfigMethodSet::init(const CLRConfig::ConfigStringInfo & info)
1379{
1380 CONTRACTL
1381 {
1382 THROWS;
1383 }
1384 CONTRACTL_END;
1385
1386 // make sure that the memory was zero initialized
1387 _ASSERTE(m_inited == 0 || m_inited == 1);
1388
1389 LPWSTR str = CLRConfig::GetConfigValue(info);
1390 if (str)
1391 {
1392 m_list.Insert(str);
1393 delete[] str;
1394 }
1395 m_inited = 1;
1396}
1397
1398/**************************************************************************/
1399bool ConfigMethodSet::contains(LPCUTF8 methodName, LPCUTF8 className, PCCOR_SIGNATURE sig)
1400{
1401 CONTRACTL
1402 {
1403 NOTHROW;
1404 }
1405 CONTRACTL_END;
1406
1407 _ASSERTE(m_inited == 1);
1408
1409 if (m_list.IsEmpty())
1410 return false;
1411 return(m_list.IsInList(methodName, className, sig));
1412}
1413
1414/**************************************************************************/
1415bool ConfigMethodSet::contains(LPCUTF8 methodName, LPCUTF8 className, CORINFO_SIG_INFO* pSigInfo)
1416{
1417 CONTRACTL
1418 {
1419 NOTHROW;
1420 }
1421 CONTRACTL_END;
1422
1423 _ASSERTE(m_inited == 1);
1424
1425 if (m_list.IsEmpty())
1426 return false;
1427 return(m_list.IsInList(methodName, className, pSigInfo));
1428}
1429
1430/**************************************************************************/
1431void ConfigDWORD::init_DontUse_(__in_z LPCWSTR keyName, DWORD defaultVal)
1432{
1433 CONTRACTL
1434 {
1435 NOTHROW;
1436 }
1437 CONTRACTL_END;
1438
1439 // make sure that the memory was zero initialized
1440 _ASSERTE(m_inited == 0 || m_inited == 1);
1441
1442 m_value = REGUTIL::GetConfigDWORD_DontUse_(keyName, defaultVal);
1443 m_inited = 1;
1444}
1445
1446/**************************************************************************/
1447void ConfigString::init(const CLRConfig::ConfigStringInfo & info)
1448{
1449 CONTRACTL
1450 {
1451 NOTHROW;
1452 }
1453 CONTRACTL_END;
1454
1455 // make sure that the memory was zero initialized
1456 _ASSERTE(m_inited == 0 || m_inited == 1);
1457
1458 // Note: m_value will be leaking
1459 m_value = CLRConfig::GetConfigValue(info);
1460 m_inited = 1;
1461}
1462
1463//=============================================================================
1464// AssemblyNamesList
1465//=============================================================================
1466// The string should be of the form
1467// MyAssembly
1468// MyAssembly;mscorlib;System
1469// MyAssembly;mscorlib System
1470
1471AssemblyNamesList::AssemblyNamesList(__in LPWSTR list)
1472{
1473 CONTRACTL {
1474 THROWS;
1475 } CONTRACTL_END;
1476
1477 WCHAR prevChar = '?'; // dummy
1478 LPWSTR nameStart = NULL; // start of the name currently being processed. NULL if no current name
1479 AssemblyName ** ppPrevLink = &m_pNames;
1480
1481 for (LPWSTR listWalk = list; prevChar != '\0'; prevChar = *listWalk, listWalk++)
1482 {
1483 WCHAR curChar = *listWalk;
1484
1485 if (iswspace(curChar) || curChar == ';' || curChar == '\0' )
1486 {
1487 //
1488 // Found white-space
1489 //
1490
1491 if (nameStart)
1492 {
1493 // Found the end of the current name
1494
1495 AssemblyName * newName = new AssemblyName();
1496 size_t nameLen = listWalk - nameStart;
1497
1498 MAKE_UTF8PTR_FROMWIDE(temp, nameStart);
1499 newName->m_assemblyName = new char[nameLen + 1];
1500 memcpy(newName->m_assemblyName, temp, nameLen * sizeof(newName->m_assemblyName[0]));
1501 newName->m_assemblyName[nameLen] = '\0';
1502
1503 *ppPrevLink = newName;
1504 ppPrevLink = &newName->m_next;
1505
1506 nameStart = NULL;
1507 }
1508 }
1509 else if (!nameStart)
1510 {
1511 //
1512 // Found the start of a new name
1513 //
1514
1515 nameStart = listWalk;
1516 }
1517 }
1518
1519 _ASSERTE(!nameStart); // cannot be in the middle of a name
1520 *ppPrevLink = NULL;
1521}
1522
1523AssemblyNamesList::~AssemblyNamesList()
1524{
1525 CONTRACTL
1526 {
1527 NOTHROW;
1528 }
1529 CONTRACTL_END;
1530
1531 for (AssemblyName * pName = m_pNames; pName; /**/)
1532 {
1533 AssemblyName * cur = pName;
1534 pName = pName->m_next;
1535
1536 delete [] cur->m_assemblyName;
1537 delete cur;
1538 }
1539}
1540
1541bool AssemblyNamesList::IsInList(LPCUTF8 assemblyName)
1542{
1543 if (IsEmpty())
1544 return false;
1545
1546 for (AssemblyName * pName = m_pNames; pName; pName = pName->m_next)
1547 {
1548 if (_stricmp(pName->m_assemblyName, assemblyName) == 0)
1549 return true;
1550 }
1551
1552 return false;
1553}
1554
1555//=============================================================================
1556// MethodNamesList
1557//=============================================================================
1558// str should be of the form :
1559// "foo1 MyNamespace.MyClass:foo3 *:foo4 foo5(x,y,z)"
1560// "MyClass:foo2 MyClass:*" will match under _DEBUG
1561//
1562
1563void MethodNamesListBase::Insert(__in_z LPWSTR str)
1564{
1565 CONTRACTL {
1566 THROWS;
1567 } CONTRACTL_END;
1568
1569 enum State { NO_NAME, CLS_NAME, FUNC_NAME, ARG_LIST }; // parsing state machine
1570
1571 const char SEP_CHAR = ' '; // current character use to separate each entry
1572// const char SEP_CHAR = ';'; // better character use to separate each entry
1573
1574 WCHAR lastChar = '?'; // dummy
1575 LPWSTR nameStart = NULL; // while walking over the classname or methodname, this points to start
1576 MethodName nameBuf; // Buffer used while parsing the current entry
1577 MethodName** lastName = &pNames; // last entry inserted into the list
1578 bool bQuote = false;
1579
1580 nameBuf.methodName = NULL;
1581 nameBuf.className = NULL;
1582 nameBuf.numArgs = -1;
1583 nameBuf.next = NULL;
1584
1585 for(State state = NO_NAME; lastChar != '\0'; str++)
1586 {
1587 lastChar = *str;
1588
1589 switch(state)
1590 {
1591 case NO_NAME:
1592 if (*str != SEP_CHAR)
1593 {
1594 nameStart = str;
1595 state = CLS_NAME; // we have found the start of the next entry
1596 }
1597 break;
1598
1599 case CLS_NAME:
1600 if (*nameStart == '"')
1601 {
1602 while (*str && *str!='"')
1603 {
1604 str++;
1605 }
1606 nameStart++;
1607 bQuote=true;
1608 }
1609
1610 if (*str == ':')
1611 {
1612 if (*nameStart == '*' && !bQuote)
1613 {
1614 // Is the classname string a wildcard. Then set it to NULL
1615 nameBuf.className = NULL;
1616 }
1617 else
1618 {
1619 int len = (int)(str - nameStart);
1620
1621 // Take off the quote
1622 if (bQuote) { len--; bQuote=false; }
1623
1624 nameBuf.className = new char[len + 1];
1625 MAKE_UTF8PTR_FROMWIDE(temp, nameStart);
1626 memcpy(nameBuf.className, temp, len*sizeof(nameBuf.className[0]));
1627 nameBuf.className[len] = '\0';
1628 }
1629 if (str[1] == ':') // Accept class::name syntax too
1630 str++;
1631 nameStart = str + 1;
1632 state = FUNC_NAME;
1633 }
1634 else if (*str == '\0' || *str == SEP_CHAR || *str == '(')
1635 {
1636 /* This was actually a method name without any class */
1637 nameBuf.className = NULL;
1638 goto DONE_FUNC_NAME;
1639 }
1640 break;
1641
1642 case FUNC_NAME:
1643 if (*nameStart == '"')
1644 {
1645 while ( (nameStart==str) || // workaround to handle when className!=NULL
1646 (*str && *str!='"'))
1647 {
1648 str++;
1649 }
1650
1651 nameStart++;
1652 bQuote=true;
1653 }
1654
1655 if (*str == '\0' || *str == SEP_CHAR || *str == '(')
1656 {
1657 DONE_FUNC_NAME:
1658 _ASSERTE(*str == '\0' || *str == SEP_CHAR || *str == '(');
1659
1660 if (*nameStart == '*' && !bQuote)
1661 {
1662 // Is the name string a wildcard. Then set it to NULL
1663 nameBuf.methodName = NULL;
1664 }
1665 else
1666 {
1667 int len = (int)(str - nameStart);
1668
1669 // Take off the quote
1670 if (bQuote) { len--; bQuote=false; }
1671
1672 nameBuf.methodName = new char[len + 1];
1673 MAKE_UTF8PTR_FROMWIDE(temp, nameStart);
1674 memcpy(nameBuf.methodName, temp, len*sizeof(nameBuf.methodName[0]));
1675 nameBuf.methodName[len] = '\0';
1676 }
1677
1678 if (*str == '\0' || *str == SEP_CHAR)
1679 {
1680 nameBuf.numArgs = -1;
1681 goto DONE_ARG_LIST;
1682 }
1683 else
1684 {
1685 _ASSERTE(*str == '(');
1686 nameBuf.numArgs = -1;
1687 state = ARG_LIST;
1688 }
1689 }
1690 break;
1691
1692 case ARG_LIST:
1693 if (*str == '\0' || *str == ')')
1694 {
1695 if (nameBuf.numArgs == -1)
1696 nameBuf.numArgs = 0;
1697
1698 DONE_ARG_LIST:
1699 _ASSERTE(*str == '\0' || *str == SEP_CHAR || *str == ')');
1700
1701 // We have parsed an entire method name.
1702 // Create a new entry in the list for it
1703
1704 MethodName * newName = new MethodName();
1705 *newName = nameBuf;
1706 newName->next = NULL;
1707 *lastName = newName;
1708 lastName = &newName->next;
1709 state = NO_NAME;
1710
1711 // Skip anything after the argument list until we find the next
1712 // separator character, otherwise if we see "func(a,b):foo" we
1713 // create entries for "func(a,b)" as well as ":foo".
1714 if (*str == ')')
1715 {
1716 while (*str && *str != SEP_CHAR)
1717 {
1718 str++;
1719 }
1720 lastChar = *str;
1721 }
1722 }
1723 else
1724 {
1725 if (*str != SEP_CHAR && nameBuf.numArgs == -1)
1726 nameBuf.numArgs = 1;
1727 if (*str == ',')
1728 nameBuf.numArgs++;
1729 }
1730 break;
1731
1732 default: _ASSERTE(!"Bad state"); break;
1733 }
1734 }
1735}
1736
1737/**************************************************************/
1738
1739void MethodNamesListBase::Destroy()
1740{
1741 CONTRACTL
1742 {
1743 NOTHROW;
1744 }
1745 CONTRACTL_END;
1746
1747 for(MethodName * pName = pNames; pName; /**/)
1748 {
1749 if (pName->className)
1750 delete [] pName->className;
1751 if (pName->methodName)
1752 delete [] pName->methodName;
1753
1754 MethodName * curName = pName;
1755 pName = pName->next;
1756 delete curName;
1757 }
1758}
1759
1760/**************************************************************/
1761bool MethodNamesListBase::IsInList(LPCUTF8 methName, LPCUTF8 clsName, PCCOR_SIGNATURE sig)
1762{
1763 CONTRACTL
1764 {
1765 NOTHROW;
1766 }
1767 CONTRACTL_END;
1768
1769 int numArgs = -1;
1770 if (sig != NULL)
1771 {
1772 sig++; // Skip calling convention
1773 numArgs = CorSigUncompressData(sig);
1774 }
1775
1776 return IsInList(methName, clsName, numArgs);
1777}
1778
1779/**************************************************************/
1780bool MethodNamesListBase::IsInList(LPCUTF8 methName, LPCUTF8 clsName, CORINFO_SIG_INFO* pSigInfo)
1781{
1782 CONTRACTL
1783 {
1784 NOTHROW;
1785 }
1786 CONTRACTL_END;
1787
1788 int numArgs = -1;
1789 if (pSigInfo != NULL)
1790 {
1791 numArgs = pSigInfo->numArgs;
1792 }
1793
1794 return IsInList(methName, clsName, numArgs);
1795}
1796
1797/**************************************************************/
1798bool MethodNamesListBase::IsInList(LPCUTF8 methName, LPCUTF8 clsName, int numArgs)
1799{
1800 CONTRACTL
1801 {
1802 NOTHROW;
1803 }
1804 CONTRACTL_END;
1805
1806 // Try to match all the entries in the list
1807
1808 for(MethodName * pName = pNames; pName; pName = pName->next)
1809 {
1810 // If numArgs is valid, check for mismatch
1811 if (pName->numArgs != -1 && pName->numArgs != numArgs)
1812 continue;
1813
1814 // If methodName is valid, check for mismatch
1815 if (pName->methodName) {
1816 if (strcmp(pName->methodName, methName) != 0) {
1817
1818 // C++ embeds the class name into the method name,
1819 // deal with that here (workaround)
1820 const char* ptr = strchr(methName, ':');
1821 if (ptr != 0 && ptr[1] == ':' && strcmp(&ptr[2], pName->methodName) == 0) {
1822 unsigned clsLen = (unsigned)(ptr - methName);
1823 if (pName->className == 0 || strncmp(pName->className, methName, clsLen) == 0)
1824 return true;
1825 }
1826 continue;
1827 }
1828 }
1829
1830 // check for class Name exact match
1831 if (clsName == 0 || pName->className == 0 || strcmp(pName->className, clsName) == 0)
1832 return true;
1833
1834 // check for suffix wildcard like System.*
1835 unsigned len = (unsigned)strlen(pName->className);
1836 if (len > 0 && pName->className[len-1] == '*' && strncmp(pName->className, clsName, len-1) == 0)
1837 return true;
1838
1839#ifdef _DEBUG
1840 // Maybe className doesnt include namespace. Try to match that
1841 LPCUTF8 onlyClass = ns::FindSep(clsName);
1842 if (onlyClass && strcmp(pName->className, onlyClass+1) == 0)
1843 return true;
1844#endif
1845 }
1846 return(false);
1847}
1848
1849//=============================================================================
1850// Signature Validation Functions (scaled down version from MDValidator
1851//=============================================================================
1852
1853//*****************************************************************************
1854// This function validates one argument given an offset into the signature
1855// where the argument begins. This function assumes that the signature is well
1856// formed as far as the compression scheme is concerned.
1857// <TODO>@todo: Validate tokens embedded.</TODO>
1858//*****************************************************************************
1859HRESULT validateOneArg(
1860 mdToken tk, // [IN] Token whose signature needs to be validated.
1861 SigParser *pSig,
1862 ULONG *pulNSentinels, // [IN/OUT] Number of sentinels
1863 IMDInternalImport* pImport, // [IN] Internal MD Import interface ptr
1864 BOOL bNoVoidAllowed) // [IN] Flag indicating whether "void" is disallowed for this arg
1865
1866{
1867 CONTRACTL
1868 {
1869 NOTHROW;
1870 SO_TOLERANT;
1871 }
1872 CONTRACTL_END;
1873
1874 BYTE elementType; // Current element type being processed.
1875 mdToken token; // Embedded token.
1876 ULONG ulArgCnt; // Argument count for function pointer.
1877 ULONG ulIndex; // Index for type parameters
1878 ULONG ulRank; // Rank of the array.
1879 ULONG ulSizes; // Count of sized dimensions of the array.
1880 ULONG ulLbnds; // Count of lower bounds of the array.
1881 ULONG ulCallConv;
1882
1883 HRESULT hr = S_OK; // Value returned.
1884 BOOL bRepeat = TRUE; // MODOPT and MODREQ belong to the arg after them
1885
1886 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW);
1887 while(bRepeat)
1888 {
1889 bRepeat = FALSE;
1890 // Validate that the argument is not missing.
1891
1892 // Get the element type.
1893 if (FAILED(pSig->GetByte(&elementType)))
1894 {
1895 IfFailGo(VLDTR_E_SIG_MISSARG);
1896 }
1897
1898 // Walk past all the modifier types.
1899 while (elementType & ELEMENT_TYPE_MODIFIER)
1900 {
1901 if (elementType == ELEMENT_TYPE_SENTINEL)
1902 {
1903 if(pulNSentinels) *pulNSentinels+=1;
1904 if(TypeFromToken(tk) != mdtMemberRef) IfFailGo(VLDTR_E_SIG_SENTINMETHODDEF);
1905 }
1906 if (FAILED(pSig->GetByte(&elementType)))
1907 {
1908 IfFailGo(VLDTR_E_SIG_MISSELTYPE);
1909 }
1910 }
1911
1912 switch (elementType)
1913 {
1914 case ELEMENT_TYPE_VOID:
1915 if(bNoVoidAllowed) IfFailGo(VLDTR_E_SIG_BADVOID);
1916
1917 case ELEMENT_TYPE_BOOLEAN:
1918 case ELEMENT_TYPE_CHAR:
1919 case ELEMENT_TYPE_I1:
1920 case ELEMENT_TYPE_U1:
1921 case ELEMENT_TYPE_I2:
1922 case ELEMENT_TYPE_U2:
1923 case ELEMENT_TYPE_I4:
1924 case ELEMENT_TYPE_U4:
1925 case ELEMENT_TYPE_I8:
1926 case ELEMENT_TYPE_U8:
1927 case ELEMENT_TYPE_R4:
1928 case ELEMENT_TYPE_R8:
1929 case ELEMENT_TYPE_STRING:
1930 case ELEMENT_TYPE_OBJECT:
1931 case ELEMENT_TYPE_TYPEDBYREF:
1932 case ELEMENT_TYPE_U:
1933 case ELEMENT_TYPE_I:
1934 break;
1935 case ELEMENT_TYPE_PTR:
1936 // Validate the referenced type.
1937 if(FAILED(hr = validateOneArg(tk, pSig, pulNSentinels, pImport, FALSE))) IfFailGo(hr);
1938 break;
1939 case ELEMENT_TYPE_BYREF: //fallthru
1940 if(TypeFromToken(tk)==mdtFieldDef) IfFailGo(VLDTR_E_SIG_BYREFINFIELD);
1941 case ELEMENT_TYPE_PINNED:
1942 case ELEMENT_TYPE_SZARRAY:
1943 // Validate the referenced type.
1944 if(FAILED(hr = validateOneArg(tk, pSig, pulNSentinels, pImport, TRUE))) IfFailGo(hr);
1945 break;
1946 case ELEMENT_TYPE_CMOD_OPT:
1947 case ELEMENT_TYPE_CMOD_REQD:
1948 bRepeat = TRUE; // go on validating, we're not done with this arg
1949 case ELEMENT_TYPE_VALUETYPE: //fallthru
1950 case ELEMENT_TYPE_CLASS:
1951 // See if the token is missing.
1952 if (FAILED(pSig->GetToken(&token)))
1953 {
1954 IfFailGo(VLDTR_E_SIG_MISSTKN);
1955 }
1956 // Token validation .
1957 if(pImport)
1958 {
1959 ULONG rid = RidFromToken(token);
1960 ULONG typ = TypeFromToken(token);
1961 ULONG maxrid = pImport->GetCountWithTokenKind(typ);
1962 if(typ == mdtTypeDef) maxrid++;
1963 if((rid==0)||(rid > maxrid)) IfFailGo(VLDTR_E_SIG_TKNBAD);
1964 }
1965 break;
1966
1967 case ELEMENT_TYPE_FNPTR:
1968 // <TODO>@todo: More function pointer validation?</TODO>
1969 // Validate that calling convention is present.
1970 if (FAILED(pSig->GetCallingConvInfo(&ulCallConv)))
1971 {
1972 IfFailGo(VLDTR_E_SIG_MISSFPTR);
1973 }
1974 if(((ulCallConv & IMAGE_CEE_CS_CALLCONV_MASK) >= IMAGE_CEE_CS_CALLCONV_MAX)
1975 ||((ulCallConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS)
1976 &&(!(ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS)))) IfFailGo(VLDTR_E_MD_BADCALLINGCONV);
1977
1978 // Validate that argument count is present.
1979 if (FAILED(pSig->GetData(&ulArgCnt)))
1980 {
1981 IfFailGo(VLDTR_E_SIG_MISSFPTRARGCNT);
1982 }
1983
1984 // FNPTR signature must follow the rules of MethodDef
1985 // Validate and consume return type.
1986 IfFailGo(validateOneArg(mdtMethodDef, pSig, NULL, pImport, FALSE));
1987
1988 // Validate and consume the arguments.
1989 while(ulArgCnt--)
1990 {
1991 IfFailGo(validateOneArg(mdtMethodDef, pSig, NULL, pImport, TRUE));
1992 }
1993 break;
1994
1995 case ELEMENT_TYPE_ARRAY:
1996 // Validate and consume the base type.
1997 IfFailGo(validateOneArg(tk, pSig, pulNSentinels, pImport, TRUE));
1998
1999 // Validate that the rank is present.
2000 if (FAILED(pSig->GetData(&ulRank)))
2001 {
2002 IfFailGo(VLDTR_E_SIG_MISSRANK);
2003 }
2004
2005 // Process the sizes.
2006 if (ulRank)
2007 {
2008 // Validate that the count of sized-dimensions is specified.
2009 if (FAILED(pSig->GetData(&ulSizes)))
2010 {
2011 IfFailGo(VLDTR_E_SIG_MISSNSIZE);
2012 }
2013
2014 // Loop over the sizes.
2015 while(ulSizes--)
2016 {
2017 // Validate the current size.
2018 if (FAILED(pSig->GetData(NULL)))
2019 {
2020 IfFailGo(VLDTR_E_SIG_MISSSIZE);
2021 }
2022 }
2023
2024 // Validate that the count of lower bounds is specified.
2025 if (FAILED(pSig->GetData(&ulLbnds)))
2026 {
2027 IfFailGo(VLDTR_E_SIG_MISSNLBND);
2028 }
2029
2030 // Loop over the lower bounds.
2031 while(ulLbnds--)
2032 {
2033 // Validate the current lower bound.
2034 if (FAILED(pSig->GetData(NULL)))
2035 {
2036 IfFailGo(VLDTR_E_SIG_MISSLBND);
2037 }
2038 }
2039 }
2040 break;
2041 case ELEMENT_TYPE_VAR:
2042 case ELEMENT_TYPE_MVAR:
2043 // Validate that index is present.
2044 if (FAILED(pSig->GetData(&ulIndex)))
2045 {
2046 IfFailGo(VLDTR_E_SIG_MISSFPTRARGCNT);
2047 }
2048
2049 //@todo GENERICS: check that index is in range
2050 break;
2051
2052 case ELEMENT_TYPE_GENERICINST:
2053 // Validate the generic type.
2054 IfFailGo(validateOneArg(tk, pSig, pulNSentinels, pImport, TRUE));
2055
2056 // Validate that parameter count is present.
2057 if (FAILED(pSig->GetData(&ulArgCnt)))
2058 {
2059 IfFailGo(VLDTR_E_SIG_MISSFPTRARGCNT);
2060 }
2061
2062 //@todo GENERICS: check that number of parameters matches definition?
2063
2064 // Validate and consume the parameters.
2065 while(ulArgCnt--)
2066 {
2067 IfFailGo(validateOneArg(tk, pSig, NULL, pImport, TRUE));
2068 }
2069 break;
2070
2071 case ELEMENT_TYPE_SENTINEL: // this case never works because all modifiers are skipped before switch
2072 if(TypeFromToken(tk) == mdtMethodDef) IfFailGo(VLDTR_E_SIG_SENTINMETHODDEF);
2073 break;
2074
2075 default:
2076 IfFailGo(VLDTR_E_SIG_BADELTYPE);
2077 break;
2078 } // switch (ulElementType)
2079 } // end while(bRepeat)
2080ErrExit:
2081
2082 END_SO_INTOLERANT_CODE;
2083 return hr;
2084} // validateOneArg()
2085
2086//*****************************************************************************
2087// This function validates the given Method/Field/Standalone signature.
2088//@todo GENERICS: MethodInstantiation?
2089//*****************************************************************************
2090HRESULT validateTokenSig(
2091 mdToken tk, // [IN] Token whose signature needs to be validated.
2092 PCCOR_SIGNATURE pbSig, // [IN] Signature.
2093 ULONG cbSig, // [IN] Size in bytes of the signature.
2094 DWORD dwFlags, // [IN] Method flags.
2095 IMDInternalImport* pImport) // [IN] Internal MD Import interface ptr
2096{
2097 CONTRACTL
2098 {
2099 NOTHROW;
2100 }
2101 CONTRACTL_END;
2102
2103 ULONG ulCallConv; // Calling convention.
2104 ULONG ulArgCount = 1; // Count of arguments (1 because of the return type)
2105 ULONG ulTyArgCount = 0; // Count of type arguments
2106 ULONG ulArgIx = 0; // Starting index of argument (standalone sig: 1)
2107 ULONG i; // Looping index.
2108 HRESULT hr = S_OK; // Value returned.
2109 ULONG ulNSentinels = 0;
2110 SigParser sig(pbSig, cbSig);
2111
2112 _ASSERTE(TypeFromToken(tk) == mdtMethodDef ||
2113 TypeFromToken(tk) == mdtMemberRef ||
2114 TypeFromToken(tk) == mdtSignature ||
2115 TypeFromToken(tk) == mdtFieldDef);
2116
2117 // Check for NULL signature.
2118 if (!pbSig || !cbSig) return VLDTR_E_SIGNULL;
2119
2120 // Validate the calling convention.
2121
2122 // Moves behind calling convention
2123 IfFailRet(sig.GetCallingConvInfo(&ulCallConv));
2124 i = ulCallConv & IMAGE_CEE_CS_CALLCONV_MASK;
2125 switch(TypeFromToken(tk))
2126 {
2127 case mdtMethodDef: // MemberRefs have no flags available
2128 // If HASTHIS is set on the calling convention, the method should not be static.
2129 if ((ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS) &&
2130 IsMdStatic(dwFlags)) return VLDTR_E_MD_THISSTATIC;
2131
2132 // If HASTHIS is not set on the calling convention, the method should be static.
2133 if (!(ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS) &&
2134 !IsMdStatic(dwFlags)) return VLDTR_E_MD_NOTTHISNOTSTATIC;
2135 // fall thru to callconv check;
2136
2137 case mdtMemberRef:
2138 if(i == IMAGE_CEE_CS_CALLCONV_FIELD) return validateOneArg(tk, &sig, NULL, pImport, TRUE);
2139
2140 // EXPLICITTHIS and native call convs are for stand-alone sigs only (for calli)
2141 if(((i != IMAGE_CEE_CS_CALLCONV_DEFAULT)&&( i != IMAGE_CEE_CS_CALLCONV_VARARG))
2142 || (ulCallConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS)) return VLDTR_E_MD_BADCALLINGCONV;
2143 break;
2144
2145 case mdtSignature:
2146 if(i != IMAGE_CEE_CS_CALLCONV_LOCAL_SIG) // then it is function sig for calli
2147 {
2148 if((i >= IMAGE_CEE_CS_CALLCONV_MAX)
2149 ||((ulCallConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS)
2150 &&(!(ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS)))) return VLDTR_E_MD_BADCALLINGCONV;
2151 }
2152 else
2153 ulArgIx = 1; // Local variable signatures don't have a return type
2154 break;
2155
2156 case mdtFieldDef:
2157 if(i != IMAGE_CEE_CS_CALLCONV_FIELD) return VLDTR_E_MD_BADCALLINGCONV;
2158 return validateOneArg(tk, &sig, NULL, pImport, TRUE);
2159 }
2160 // Is there any sig left for arguments?
2161
2162 // Get the type argument count
2163 if (ulCallConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
2164 {
2165 if (FAILED(sig.GetData(&ulTyArgCount)))
2166 {
2167 return VLDTR_E_MD_NOARGCNT;
2168 }
2169 }
2170
2171 // Get the argument count.
2172 if (FAILED(sig.GetData(&ulArgCount)))
2173 {
2174 return VLDTR_E_MD_NOARGCNT;
2175 }
2176
2177 // Validate the return type and the arguments.
2178 // (at this moment ulArgCount = num.args+1, ulArgIx = (standalone sig. ? 1 :0); )
2179 for(; ulArgIx < ulArgCount; ulArgIx++)
2180 {
2181 if(FAILED(hr = validateOneArg(tk, &sig, &ulNSentinels, pImport, (ulArgIx!=0)))) return hr;
2182 }
2183
2184 // <TODO>@todo: we allow junk to be at the end of the signature (we may not consume it all)
2185 // do we care?</TODO>
2186
2187 if((ulNSentinels != 0) && ((ulCallConv & IMAGE_CEE_CS_CALLCONV_MASK) != IMAGE_CEE_CS_CALLCONV_VARARG ))
2188 return VLDTR_E_SIG_SENTMUSTVARARG;
2189 if(ulNSentinels > 1) return VLDTR_E_SIG_MULTSENTINELS;
2190 return S_OK;
2191} // validateTokenSig()
2192
2193HRESULT GetImageRuntimeVersionString(PVOID pMetaData, LPCSTR* pString)
2194{
2195 CONTRACTL
2196 {
2197 NOTHROW;
2198 }
2199 CONTRACTL_END;
2200
2201 _ASSERTE(pString);
2202 STORAGESIGNATURE* pSig = (STORAGESIGNATURE*) pMetaData;
2203
2204 // Verify the signature.
2205
2206 // If signature didn't match, you shouldn't be here.
2207 if (pSig->GetSignature() != STORAGE_MAGIC_SIG)
2208 return CLDB_E_FILE_CORRUPT;
2209
2210 // The version started in version 1.1
2211 if (pSig->GetMajorVer() < 1)
2212 return CLDB_E_FILE_OLDVER;
2213
2214 if (pSig->GetMajorVer() == 1 && pSig->GetMinorVer() < 1)
2215 return CLDB_E_FILE_OLDVER;
2216
2217 // Header data starts after signature.
2218 *pString = (LPCSTR) pSig->pVersion;
2219 return S_OK;
2220}
2221
2222//*****************************************************************************
2223// Convert a UTF8 string to Unicode, into a CQuickArray<WCHAR>.
2224//*****************************************************************************
2225HRESULT Utf2Quick(
2226 LPCUTF8 pStr, // The string to convert.
2227 CQuickArray<WCHAR> &rStr, // The QuickArray<WCHAR> to convert it into.
2228 int iCurLen) // Inital characters in the array to leave (default 0).
2229{
2230 CONTRACTL
2231 {
2232 NOTHROW;
2233 }
2234 CONTRACTL_END;
2235
2236 HRESULT hr = S_OK; // A result.
2237 int iReqLen; // Required additional length.
2238 int iActLen;
2239 int bAlloc = 0; // If non-zero, allocation was required.
2240
2241 if (iCurLen < 0 )
2242 {
2243 _ASSERTE_MSG(false, "Invalid current length");
2244 return E_INVALIDARG;
2245 }
2246
2247 // Calculate the space available
2248 S_SIZE_T cchAvail = S_SIZE_T(rStr.MaxSize()) - S_SIZE_T(iCurLen);
2249 if (cchAvail.IsOverflow() || cchAvail.Value() > INT_MAX)
2250 {
2251 _ASSERTE_MSG(false, "Integer overflow/underflow");
2252 return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
2253 }
2254
2255 // Attempt the conversion.
2256 LPWSTR rNewStr = rStr.Ptr()+iCurLen;
2257 if(rNewStr < rStr.Ptr())
2258 {
2259 _ASSERTE_MSG(false, "Integer overflow/underflow");
2260 return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
2261 }
2262 iReqLen = WszMultiByteToWideChar(CP_UTF8, 0, pStr, -1, rNewStr, (int)(cchAvail.Value()));
2263
2264 // If the buffer was too small, determine what is required.
2265 if (iReqLen == 0)
2266 bAlloc = iReqLen = WszMultiByteToWideChar(CP_UTF8, 0, pStr, -1, 0, 0);
2267 // Resize the buffer. If the buffer was large enough, this just sets the internal
2268 // counter, but if it was too small, this will attempt a reallocation. Note that
2269 // the length includes the terminating W('/0').
2270 IfFailGo(rStr.ReSizeNoThrow(iCurLen+iReqLen));
2271 // If we had to realloc, then do the conversion again, now that the buffer is
2272 // large enough.
2273 if (bAlloc) {
2274 //recalculating cchAvail since MaxSize could have been changed.
2275 cchAvail = S_SIZE_T(rStr.MaxSize()) - S_SIZE_T(iCurLen);
2276 if (cchAvail.IsOverflow() || cchAvail.Value() > INT_MAX)
2277 {
2278 _ASSERTE_MSG(false, "Integer overflow/underflow");
2279 return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
2280 }
2281 //reculculating rNewStr
2282 rNewStr = rStr.Ptr()+iCurLen;
2283
2284 if(rNewStr < rStr.Ptr())
2285 {
2286 _ASSERTE_MSG(false, "Integer overflow/underflow");
2287 return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
2288 }
2289 iActLen = WszMultiByteToWideChar(CP_UTF8, 0, pStr, -1, rNewStr, (int)(cchAvail.Value()));
2290 _ASSERTE(iReqLen == iActLen);
2291 }
2292ErrExit:
2293 return hr;
2294} // HRESULT Utf2Quick()
2295
2296
2297//*****************************************************************************
2298// Extract the movl 64-bit unsigned immediate from an IA64 bundle
2299// (Format X2)
2300//*****************************************************************************
2301UINT64 GetIA64Imm64(UINT64 * pBundle)
2302{
2303 WRAPPER_NO_CONTRACT;
2304
2305 UINT64 temp0 = PTR_UINT64(pBundle)[0];
2306 UINT64 temp1 = PTR_UINT64(pBundle)[1];
2307
2308 return GetIA64Imm64(temp0, temp1);
2309}
2310
2311UINT64 GetIA64Imm64(UINT64 qword0, UINT64 qword1)
2312{
2313 LIMITED_METHOD_CONTRACT;
2314
2315 UINT64 imm64 = 0;
2316
2317#ifdef _DEBUG_IMPL
2318 //
2319 // make certain we're decoding a movl opcode, with template 4 or 5
2320 //
2321 UINT64 templa = (qword0 >> 0) & 0x1f;
2322 UINT64 opcode = (qword1 >> 60) & 0xf;
2323
2324 _ASSERTE((opcode == 0x6) && ((templa == 0x4) || (templa == 0x5)));
2325#endif
2326
2327 imm64 = (qword1 >> 59) << 63; // 1 i
2328 imm64 |= (qword1 << 41) >> 1; // 23 high bits of imm41
2329 imm64 |= (qword0 >> 46) << 22; // 18 low bits of imm41
2330 imm64 |= (qword1 >> 23) & 0x200000; // 1 ic
2331 imm64 |= (qword1 >> 29) & 0x1F0000; // 5 imm5c
2332 imm64 |= (qword1 >> 43) & 0xFF80; // 9 imm9d
2333 imm64 |= (qword1 >> 36) & 0x7F; // 7 imm7b
2334
2335 return imm64;
2336}
2337
2338//*****************************************************************************
2339// Deposit the movl 64-bit unsigned immediate into an IA64 bundle
2340// (Format X2)
2341//*****************************************************************************
2342void PutIA64Imm64(UINT64 * pBundle, UINT64 imm64)
2343{
2344 LIMITED_METHOD_CONTRACT;
2345
2346#ifdef _DEBUG_IMPL
2347 //
2348 // make certain we're decoding a movl opcode, with template 4 or 5
2349 //
2350 UINT64 templa = (pBundle[0] >> 0) & 0x1f;
2351 UINT64 opcode = (pBundle[1] >> 60) & 0xf ;
2352
2353 _ASSERTE((opcode == 0x6) && ((templa == 0x4) || (templa == 0x5)));
2354#endif
2355
2356 const UINT64 mask0 = UI64(0x00003FFFFFFFFFFF);
2357 const UINT64 mask1 = UI64(0xF000080FFF800000);
2358
2359 /* Clear all bits used as part of the imm64 */
2360 pBundle[0] &= mask0;
2361 pBundle[1] &= mask1;
2362
2363 UINT64 temp0;
2364 UINT64 temp1;
2365
2366 temp1 = (imm64 >> 63) << 59; // 1 i
2367 temp1 |= (imm64 & 0xFF80) << 43; // 9 imm9d
2368 temp1 |= (imm64 & 0x1F0000) << 29; // 5 imm5c
2369 temp1 |= (imm64 & 0x200000) << 23; // 1 ic
2370 temp1 |= (imm64 & 0x7F) << 36; // 7 imm7b
2371 temp1 |= (imm64 << 1) >> 41; // 23 high bits of imm41
2372 temp0 = (imm64 >> 22) << 46; // 18 low bits of imm41
2373
2374 /* Or in the new bits used in the imm64 */
2375 pBundle[0] |= temp0;
2376 pBundle[1] |= temp1;
2377 FlushInstructionCache(GetCurrentProcess(),pBundle,16);
2378}
2379
2380//*****************************************************************************
2381// Extract the IP-Relative signed 25-bit immediate from an IA64 bundle
2382// (Formats B1, B2 or B3)
2383// Note that due to branch target alignment requirements
2384// the lowest four bits in the result will always be zero.
2385//*****************************************************************************
2386INT32 GetIA64Rel25(UINT64 * pBundle, UINT32 slot)
2387{
2388 WRAPPER_NO_CONTRACT;
2389
2390 UINT64 temp0 = PTR_UINT64(pBundle)[0];
2391 UINT64 temp1 = PTR_UINT64(pBundle)[1];
2392
2393 return GetIA64Rel25(temp0, temp1, slot);
2394}
2395
2396INT32 GetIA64Rel25(UINT64 qword0, UINT64 qword1, UINT32 slot)
2397{
2398 LIMITED_METHOD_CONTRACT;
2399
2400 INT32 imm25 = 0;
2401
2402 if (slot == 2)
2403 {
2404 if ((qword1 >> 59) & 1)
2405 imm25 = 0xFF000000;
2406 imm25 |= (qword1 >> 32) & 0x00FFFFF0; // 20 imm20b
2407 }
2408 else if (slot == 1)
2409 {
2410 if ((qword1 >> 18) & 1)
2411 imm25 = 0xFF000000;
2412 imm25 |= (qword1 << 9) & 0x00FFFE00; // high 15 of imm20b
2413 imm25 |= (qword0 >> 55) & 0x000001F0; // low 5 of imm20b
2414 }
2415 else if (slot == 0)
2416 {
2417 if ((qword0 >> 41) & 1)
2418 imm25 = 0xFF000000;
2419 imm25 |= (qword0 >> 14) & 0x00FFFFF0; // 20 imm20b
2420 }
2421
2422 return imm25;
2423}
2424
2425//*****************************************************************************
2426// Deposit the IP-Relative signed 25-bit immediate into an IA64 bundle
2427// (Formats B1, B2 or B3)
2428// Note that due to branch target alignment requirements
2429// the lowest four bits are required to be zero.
2430//*****************************************************************************
2431void PutIA64Rel25(UINT64 * pBundle, UINT32 slot, INT32 imm25)
2432{
2433 LIMITED_METHOD_CONTRACT;
2434
2435 _ASSERTE((imm25 & 0xF) == 0);
2436
2437 if (slot == 2)
2438 {
2439 const UINT64 mask1 = UI64(0xF700000FFFFFFFFF);
2440 /* Clear all bits used as part of the imm25 */
2441 pBundle[1] &= mask1;
2442
2443 UINT64 temp1;
2444
2445 temp1 = (UINT64) (imm25 & 0x1000000) << 35; // 1 s
2446 temp1 |= (UINT64) (imm25 & 0x0FFFFF0) << 32; // 20 imm20b
2447
2448 /* Or in the new bits used in the imm64 */
2449 pBundle[1] |= temp1;
2450 }
2451 else if (slot == 1)
2452 {
2453 const UINT64 mask0 = UI64(0x0EFFFFFFFFFFFFFF);
2454 const UINT64 mask1 = UI64(0xFFFFFFFFFFFB8000);
2455 /* Clear all bits used as part of the imm25 */
2456 pBundle[0] &= mask0;
2457 pBundle[1] &= mask1;
2458
2459 UINT64 temp0;
2460 UINT64 temp1;
2461
2462 temp1 = (UINT64) (imm25 & 0x1000000) >> 7; // 1 s
2463 temp1 |= (UINT64) (imm25 & 0x0FFFE00) >> 9; // high 15 of imm20b
2464 temp0 = (UINT64) (imm25 & 0x00001F0) << 55; // low 5 of imm20b
2465
2466 /* Or in the new bits used in the imm64 */
2467 pBundle[0] |= temp0;
2468 pBundle[1] |= temp1;
2469 }
2470 else if (slot == 0)
2471 {
2472 const UINT64 mask0 = UI64(0xFFFFFDC00003FFFF);
2473 /* Clear all bits used as part of the imm25 */
2474 pBundle[0] &= mask0;
2475
2476 UINT64 temp0;
2477
2478 temp0 = (UINT64) (imm25 & 0x1000000) << 16; // 1 s
2479 temp0 |= (UINT64) (imm25 & 0x0FFFFF0) << 14; // 20 imm20b
2480
2481 /* Or in the new bits used in the imm64 */
2482 pBundle[0] |= temp0;
2483
2484 }
2485 FlushInstructionCache(GetCurrentProcess(),pBundle,16);
2486}
2487
2488//*****************************************************************************
2489// Extract the IP-Relative signed 64-bit immediate from an IA64 bundle
2490// (Formats X3 or X4)
2491//*****************************************************************************
2492INT64 GetIA64Rel64(UINT64 * pBundle)
2493{
2494 WRAPPER_NO_CONTRACT;
2495
2496 UINT64 temp0 = PTR_UINT64(pBundle)[0];
2497 UINT64 temp1 = PTR_UINT64(pBundle)[1];
2498
2499 return GetIA64Rel64(temp0, temp1);
2500}
2501
2502INT64 GetIA64Rel64(UINT64 qword0, UINT64 qword1)
2503{
2504 LIMITED_METHOD_CONTRACT;
2505
2506 INT64 imm64 = 0;
2507
2508#ifdef _DEBUG_IMPL
2509 //
2510 // make certain we're decoding a brl opcode, with template 4 or 5
2511 //
2512 UINT64 templa = (qword0 >> 0) & 0x1f;
2513 UINT64 opcode = (qword1 >> 60) & 0xf;
2514
2515 _ASSERTE(((opcode == 0xC) || (opcode == 0xD)) &&
2516 ((templa == 0x4) || (templa == 0x5)));
2517#endif
2518
2519 imm64 = (qword1 >> 59) << 63; // 1 i
2520 imm64 |= (qword1 << 41) >> 1; // 23 high bits of imm39
2521 imm64 |= (qword0 >> 48) << 24; // 16 low bits of imm39
2522 imm64 |= (qword1 >> 32) & 0xFFFFF0; // 20 imm20b
2523 // 4 bits of zeros
2524 return imm64;
2525}
2526
2527//*****************************************************************************
2528// Deposit the IP-Relative signed 64-bit immediate into an IA64 bundle
2529// (Formats X3 or X4)
2530//*****************************************************************************
2531void PutIA64Rel64(UINT64 * pBundle, INT64 imm64)
2532{
2533 LIMITED_METHOD_CONTRACT;
2534
2535#ifdef _DEBUG_IMPL
2536 //
2537 // make certain we're decoding a brl opcode, with template 4 or 5
2538 //
2539 UINT64 templa = (pBundle[0] >> 0) & 0x1f;
2540 UINT64 opcode = (pBundle[1] >> 60) & 0xf;
2541
2542 _ASSERTE(((opcode == 0xC) || (opcode == 0xD)) &&
2543 ((templa == 0x4) || (templa == 0x5)));
2544 _ASSERTE((imm64 & 0xF) == 0);
2545#endif
2546
2547 const UINT64 mask0 = UI64(0x00003FFFFFFFFFFF);
2548 const UINT64 mask1 = UI64(0xF700000FFF800000);
2549
2550 /* Clear all bits used as part of the imm64 */
2551 pBundle[0] &= mask0;
2552 pBundle[1] &= mask1;
2553
2554 UINT64 temp0 = (imm64 & UI64(0x000000FFFF000000)) << 24; // 16 low bits of imm39
2555 UINT64 temp1 = (imm64 & UI64(0x8000000000000000)) >> 4 // 1 i
2556 | (imm64 & UI64(0x7FFFFF0000000000)) >> 40 // 23 high bits of imm39
2557 | (imm64 & UI64(0x0000000000FFFFF0)) << 32; // 20 imm20b
2558
2559 /* Or in the new bits used in the imm64 */
2560 pBundle[0] |= temp0;
2561 pBundle[1] |= temp1;
2562 FlushInstructionCache(GetCurrentProcess(),pBundle,16);
2563}
2564
2565//*****************************************************************************
2566// Extract the 16-bit immediate from ARM Thumb2 Instruction (format T2_N)
2567//*****************************************************************************
2568static FORCEINLINE UINT16 GetThumb2Imm16(UINT16 * p)
2569{
2570 LIMITED_METHOD_CONTRACT;
2571
2572 return ((p[0] << 12) & 0xf000) |
2573 ((p[0] << 1) & 0x0800) |
2574 ((p[1] >> 4) & 0x0700) |
2575 ((p[1] >> 0) & 0x00ff);
2576}
2577
2578//*****************************************************************************
2579// Extract the 32-bit immediate from movw/movt sequence
2580//*****************************************************************************
2581UINT32 GetThumb2Mov32(UINT16 * p)
2582{
2583 LIMITED_METHOD_CONTRACT;
2584
2585 // Make sure we are decoding movw/movt sequence
2586 _ASSERTE_IMPL((*(p+0) & 0xFBF0) == 0xF240);
2587 _ASSERTE_IMPL((*(p+2) & 0xFBF0) == 0xF2C0);
2588
2589 return (UINT32)GetThumb2Imm16(p) + ((UINT32)GetThumb2Imm16(p + 2) << 16);
2590}
2591
2592//*****************************************************************************
2593// Deposit the 16-bit immediate into ARM Thumb2 Instruction (format T2_N)
2594//*****************************************************************************
2595static FORCEINLINE void PutThumb2Imm16(UINT16 * p, UINT16 imm16)
2596{
2597 LIMITED_METHOD_CONTRACT;
2598
2599 USHORT Opcode0 = p[0];
2600 USHORT Opcode1 = p[1];
2601 Opcode0 &= ~((0xf000 >> 12) | (0x0800 >> 1));
2602 Opcode1 &= ~((0x0700 << 4) | (0x00ff << 0));
2603 Opcode0 |= (imm16 & 0xf000) >> 12;
2604 Opcode0 |= (imm16 & 0x0800) >> 1;
2605 Opcode1 |= (imm16 & 0x0700) << 4;
2606 Opcode1 |= (imm16 & 0x00ff) << 0;
2607 p[0] = Opcode0;
2608 p[1] = Opcode1;
2609}
2610
2611//*****************************************************************************
2612// Deposit the 32-bit immediate into movw/movt Thumb2 sequence
2613//*****************************************************************************
2614void PutThumb2Mov32(UINT16 * p, UINT32 imm32)
2615{
2616 LIMITED_METHOD_CONTRACT;
2617
2618 // Make sure we are decoding movw/movt sequence
2619 _ASSERTE_IMPL((*(p+0) & 0xFBF0) == 0xF240);
2620 _ASSERTE_IMPL((*(p+2) & 0xFBF0) == 0xF2C0);
2621
2622 PutThumb2Imm16(p, (UINT16)imm32);
2623 PutThumb2Imm16(p + 2, (UINT16)(imm32 >> 16));
2624}
2625
2626//*****************************************************************************
2627// Extract the 24-bit rel offset from bl instruction
2628//*****************************************************************************
2629INT32 GetThumb2BlRel24(UINT16 * p)
2630{
2631 LIMITED_METHOD_CONTRACT;
2632
2633 USHORT Opcode0 = p[0];
2634 USHORT Opcode1 = p[1];
2635
2636 UINT32 S = Opcode0 >> 10;
2637 UINT32 J2 = Opcode1 >> 11;
2638 UINT32 J1 = Opcode1 >> 13;
2639
2640 INT32 ret =
2641 ((S << 24) & 0x1000000) |
2642 (((J1 ^ S ^ 1) << 23) & 0x0800000) |
2643 (((J2 ^ S ^ 1) << 22) & 0x0400000) |
2644 ((Opcode0 << 12) & 0x03FF000) |
2645 ((Opcode1 << 1) & 0x0000FFE);
2646
2647 // Sign-extend and return
2648 return (ret << 7) >> 7;
2649}
2650
2651//*****************************************************************************
2652// Extract the 24-bit rel offset from bl instruction
2653//*****************************************************************************
2654void PutThumb2BlRel24(UINT16 * p, INT32 imm24)
2655{
2656 LIMITED_METHOD_CONTRACT;
2657
2658 // Verify that we got a valid offset
2659 _ASSERTE(FitsInThumb2BlRel24(imm24));
2660
2661#if defined(_TARGET_ARM_)
2662 // Ensure that the ThumbBit is not set on the offset
2663 // as it cannot be encoded.
2664 _ASSERTE(!(imm24 & THUMB_CODE));
2665#endif // _TARGET_ARM_
2666
2667 USHORT Opcode0 = p[0];
2668 USHORT Opcode1 = p[1];
2669 Opcode0 &= 0xF800;
2670 Opcode1 &= 0xD000;
2671
2672 UINT32 S = (imm24 & 0x1000000) >> 24;
2673 UINT32 J1 = ((imm24 & 0x0800000) >> 23) ^ S ^ 1;
2674 UINT32 J2 = ((imm24 & 0x0400000) >> 22) ^ S ^ 1;
2675
2676 Opcode0 |= ((imm24 & 0x03FF000) >> 12) | (S << 10);
2677 Opcode1 |= ((imm24 & 0x0000FFE) >> 1) | (J1 << 13) | (J2 << 11);
2678
2679 p[0] = Opcode0;
2680 p[1] = Opcode1;
2681
2682 _ASSERTE(GetThumb2BlRel24(p) == imm24);
2683}
2684
2685//*****************************************************************************
2686// Extract the PC-Relative offset from a b or bl instruction
2687//*****************************************************************************
2688INT32 GetArm64Rel28(UINT32 * pCode)
2689{
2690 LIMITED_METHOD_CONTRACT;
2691
2692 UINT32 branchInstr = *pCode;
2693
2694 // first shift 6 bits left to set the sign bit,
2695 // then arithmetic shift right by 4 bits
2696 INT32 imm28 = (((INT32)(branchInstr & 0x03FFFFFF)) << 6) >> 4;
2697
2698 return imm28;
2699}
2700
2701//*****************************************************************************
2702// Extract the PC-Relative offset from an adrp instruction
2703//*****************************************************************************
2704INT32 GetArm64Rel21(UINT32 * pCode)
2705{
2706 LIMITED_METHOD_CONTRACT;
2707
2708 UINT32 addInstr = *pCode;
2709
2710 // 23-5 bits for the high part. Shift it by 5.
2711 INT32 immhi = (((INT32)(addInstr & 0xFFFFE0))) >> 5;
2712 // 30,29 bits for the lower part. Shift it by 29.
2713 INT32 immlo = ((INT32)(addInstr & 0x60000000)) >> 29;
2714
2715 // Merge them
2716 INT32 imm21 = (immhi << 2) | immlo;
2717
2718 return imm21;
2719}
2720
2721//*****************************************************************************
2722// Extract the PC-Relative offset from an add instruction
2723//*****************************************************************************
2724INT32 GetArm64Rel12(UINT32 * pCode)
2725{
2726 LIMITED_METHOD_CONTRACT;
2727
2728 UINT32 addInstr = *pCode;
2729
2730 // 21-10 contains value. Mask 12 bits and shift by 10 bits.
2731 INT32 imm12 = (INT32)(addInstr & 0x003FFC00) >> 10;
2732
2733 return imm12;
2734}
2735
2736//*****************************************************************************
2737// Deposit the PC-Relative offset 'imm28' into a b or bl instruction
2738//*****************************************************************************
2739void PutArm64Rel28(UINT32 * pCode, INT32 imm28)
2740{
2741 LIMITED_METHOD_CONTRACT;
2742
2743 // Verify that we got a valid offset
2744 _ASSERTE(FitsInRel28(imm28));
2745 _ASSERTE((imm28 & 0x3) == 0); // the low two bits must be zero
2746
2747 UINT32 branchInstr = *pCode;
2748
2749 branchInstr &= 0xFC000000; // keep bits 31-26
2750
2751 // Assemble the pc-relative delta 'imm28' into the branch instruction
2752 branchInstr |= ((imm28 >> 2) & 0x03FFFFFF);
2753
2754 *pCode = branchInstr; // write the assembled instruction
2755
2756 _ASSERTE(GetArm64Rel28(pCode) == imm28);
2757}
2758
2759//*****************************************************************************
2760// Deposit the PC-Relative offset 'imm21' into an adrp instruction
2761//*****************************************************************************
2762void PutArm64Rel21(UINT32 * pCode, INT32 imm21)
2763{
2764 LIMITED_METHOD_CONTRACT;
2765
2766 // Verify that we got a valid offset
2767 _ASSERTE(FitsInRel21(imm21));
2768
2769 UINT32 adrpInstr = *pCode;
2770 // Check adrp opcode 1ii1 0000 ...
2771 _ASSERTE((adrpInstr & 0x9F000000) == 0x90000000);
2772
2773 adrpInstr &= 0x9F00001F; // keep bits 31, 28-24, 4-0.
2774 INT32 immlo = imm21 & 0x03; // Extract low 2 bits which will occupy 30-29 bits.
2775 INT32 immhi = (imm21 & 0x1FFFFC) >> 2; // Extract high 19 bits which will occupy 23-5 bits.
2776 adrpInstr |= ((immlo << 29) | (immhi << 5));
2777
2778 *pCode = adrpInstr; // write the assembled instruction
2779
2780 _ASSERTE(GetArm64Rel21(pCode) == imm21);
2781}
2782
2783//*****************************************************************************
2784// Deposit the PC-Relative offset 'imm12' into an add instruction
2785//*****************************************************************************
2786void PutArm64Rel12(UINT32 * pCode, INT32 imm12)
2787{
2788 LIMITED_METHOD_CONTRACT;
2789
2790 // Verify that we got a valid offset
2791 _ASSERTE(FitsInRel12(imm12));
2792
2793 UINT32 addInstr = *pCode;
2794 // Check add opcode 1001 0001 00...
2795 _ASSERTE((addInstr & 0xFFC00000) == 0x91000000);
2796
2797 addInstr &= 0xFFC003FF; // keep bits 31-22, 9-0
2798 addInstr |= (imm12 << 10); // Occupy 21-10.
2799
2800 *pCode = addInstr; // write the assembled instruction
2801
2802 _ASSERTE(GetArm64Rel12(pCode) == imm12);
2803}
2804
2805//---------------------------------------------------------------------
2806// Splits a command line into argc/argv lists, using the VC7 parsing rules.
2807//
2808// This functions interface mimics the CommandLineToArgvW api.
2809//
2810// If function fails, returns NULL.
2811//
2812// If function suceeds, call delete [] on return pointer when done.
2813//
2814//---------------------------------------------------------------------
2815// NOTE: Implementation-wise, once every few years it would be a good idea to
2816// compare this code with the C runtime library's parse_cmdline method,
2817// which is in vctools\crt\crtw32\startup\stdargv.c. (Note we don't
2818// support wild cards, and we use Unicode characters exclusively.)
2819// We are up to date as of ~6/2005.
2820//---------------------------------------------------------------------
2821LPWSTR *SegmentCommandLine(LPCWSTR lpCmdLine, DWORD *pNumArgs)
2822{
2823 STATIC_CONTRACT_NOTHROW;
2824 STATIC_CONTRACT_GC_NOTRIGGER;
2825 STATIC_CONTRACT_FAULT;
2826
2827
2828 *pNumArgs = 0;
2829
2830 int nch = (int)wcslen(lpCmdLine);
2831
2832 // Calculate the worstcase storage requirement. (One pointer for
2833 // each argument, plus storage for the arguments themselves.)
2834 int cbAlloc = (nch+1)*sizeof(LPWSTR) + sizeof(WCHAR)*(nch + 1);
2835 LPWSTR pAlloc = new (nothrow) WCHAR[cbAlloc / sizeof(WCHAR)];
2836 if (!pAlloc)
2837 return NULL;
2838
2839 LPWSTR *argv = (LPWSTR*) pAlloc; // We store the argv pointers in the first halt
2840 LPWSTR pdst = (LPWSTR)( ((BYTE*)pAlloc) + sizeof(LPWSTR)*(nch+1) ); // A running pointer to second half to store arguments
2841 LPCWSTR psrc = lpCmdLine;
2842 WCHAR c;
2843 BOOL inquote;
2844 BOOL copychar;
2845 int numslash;
2846
2847 // First, parse the program name (argv[0]). Argv[0] is parsed under
2848 // special rules. Anything up to the first whitespace outside a quoted
2849 // subtring is accepted. Backslashes are treated as normal characters.
2850 argv[ (*pNumArgs)++ ] = pdst;
2851 inquote = FALSE;
2852 do {
2853 if (*psrc == W('"') )
2854 {
2855 inquote = !inquote;
2856 c = *psrc++;
2857 continue;
2858 }
2859 *pdst++ = *psrc;
2860
2861 c = *psrc++;
2862
2863 } while ( (c != W('\0') && (inquote || (c != W(' ') && c != W('\t')))) );
2864
2865 if ( c == W('\0') ) {
2866 psrc--;
2867 } else {
2868 *(pdst-1) = W('\0');
2869 }
2870
2871 inquote = FALSE;
2872
2873
2874
2875 /* loop on each argument */
2876 for(;;)
2877 {
2878 if ( *psrc )
2879 {
2880 while (*psrc == W(' ') || *psrc == W('\t'))
2881 {
2882 ++psrc;
2883 }
2884 }
2885
2886 if (*psrc == W('\0'))
2887 break; /* end of args */
2888
2889 /* scan an argument */
2890 argv[ (*pNumArgs)++ ] = pdst;
2891
2892 /* loop through scanning one argument */
2893 for (;;)
2894 {
2895 copychar = 1;
2896 /* Rules: 2N backslashes + " ==> N backslashes and begin/end quote
2897 2N+1 backslashes + " ==> N backslashes + literal "
2898 N backslashes ==> N backslashes */
2899 numslash = 0;
2900 while (*psrc == W('\\'))
2901 {
2902 /* count number of backslashes for use below */
2903 ++psrc;
2904 ++numslash;
2905 }
2906 if (*psrc == W('"'))
2907 {
2908 /* if 2N backslashes before, start/end quote, otherwise
2909 copy literally */
2910 if (numslash % 2 == 0)
2911 {
2912 if (inquote && psrc[1] == W('"'))
2913 {
2914 psrc++; /* Double quote inside quoted string */
2915 }
2916 else
2917 {
2918 /* skip first quote char and copy second */
2919 copychar = 0; /* don't copy quote */
2920 inquote = !inquote;
2921 }
2922 }
2923 numslash /= 2; /* divide numslash by two */
2924 }
2925
2926 /* copy slashes */
2927 while (numslash--)
2928 {
2929 *pdst++ = W('\\');
2930 }
2931
2932 /* if at end of arg, break loop */
2933 if (*psrc == W('\0') || (!inquote && (*psrc == W(' ') || *psrc == W('\t'))))
2934 break;
2935
2936 /* copy character into argument */
2937 if (copychar)
2938 {
2939 *pdst++ = *psrc;
2940 }
2941 ++psrc;
2942 }
2943
2944 /* null-terminate the argument */
2945
2946 *pdst++ = W('\0'); /* terminate string */
2947 }
2948
2949 /* We put one last argument in -- a null ptr */
2950 argv[ (*pNumArgs) ] = NULL;
2951
2952 // If we hit this assert, we overwrote our destination buffer.
2953 // Since we're supposed to allocate for the worst
2954 // case, either the parsing rules have changed or our worse case
2955 // formula is wrong.
2956 _ASSERTE((BYTE*)pdst <= (BYTE*)pAlloc + cbAlloc);
2957 return argv;
2958}
2959
2960Volatile<PVOID> ForbidCallsIntoHostOnThisThread::s_pvOwningFiber = NULL;
2961
2962//======================================================================
2963// This function returns true, if it can determine that the instruction pointer
2964// refers to a code address that belongs in the range of the given image.
2965// <TODO>@TODO: Merge with IsIPInModule from vm\util.hpp</TODO>
2966
2967BOOL IsIPInModule(HMODULE_TGT hModule, PCODE ip)
2968{
2969 STATIC_CONTRACT_LEAF;
2970 SUPPORTS_DAC;
2971
2972 struct Param
2973 {
2974 HMODULE_TGT hModule;
2975 PCODE ip;
2976 BOOL fRet;
2977 } param;
2978 param.hModule = hModule;
2979 param.ip = ip;
2980 param.fRet = FALSE;
2981
2982// UNIXTODO: implement a proper version for PAL
2983#ifndef FEATURE_PAL
2984 PAL_TRY(Param *, pParam, &param)
2985 {
2986 PTR_BYTE pBase = dac_cast<PTR_BYTE>(pParam->hModule);
2987
2988 PTR_IMAGE_DOS_HEADER pDOS = NULL;
2989 PTR_IMAGE_NT_HEADERS pNT = NULL;
2990 USHORT cbOptHdr;
2991 PCODE baseAddr;
2992
2993 //
2994 // First, must validate the format of the PE headers to make sure that
2995 // the fields we're interested in using exist in the image.
2996 //
2997
2998 // Validate the DOS header.
2999 pDOS = PTR_IMAGE_DOS_HEADER(pBase);
3000 if (pDOS->e_magic != VAL16(IMAGE_DOS_SIGNATURE) ||
3001 pDOS->e_lfanew == 0)
3002 {
3003 goto lDone;
3004 }
3005
3006 // Validate the NT header
3007 pNT = PTR_IMAGE_NT_HEADERS(pBase + VAL32(pDOS->e_lfanew));
3008
3009 if (pNT->Signature != VAL32(IMAGE_NT_SIGNATURE))
3010 {
3011 goto lDone;
3012 }
3013
3014 // Validate that the optional header is large enough to contain the fields
3015 // we're interested, namely IMAGE_OPTIONAL_HEADER::SizeOfImage. The reason
3016 // we don't just check that SizeOfOptionalHeader == IMAGE_SIZEOF_NT_OPTIONAL_HEADER
3017 // is due to VSW443590, which states that the extensibility of this structure
3018 // is such that it is possible to include only a portion of the optional header.
3019 cbOptHdr = pNT->FileHeader.SizeOfOptionalHeader;
3020
3021 // Check that the magic field is contained by the optional header and set to the correct value.
3022 if (cbOptHdr < (offsetof(IMAGE_OPTIONAL_HEADER, Magic) + sizeofmember(IMAGE_OPTIONAL_HEADER, Magic)) ||
3023 pNT->OptionalHeader.Magic != VAL16(IMAGE_NT_OPTIONAL_HDR_MAGIC))
3024 {
3025 goto lDone;
3026 }
3027
3028 // Check that the SizeOfImage is contained by the optional header.
3029 if (cbOptHdr < (offsetof(IMAGE_OPTIONAL_HEADER, SizeOfImage) + sizeofmember(IMAGE_OPTIONAL_HEADER, SizeOfImage)))
3030 {
3031 goto lDone;
3032 }
3033
3034 //
3035 // The real check
3036 //
3037
3038 baseAddr = dac_cast<PCODE>(pBase);
3039 if ((pParam->ip < baseAddr) || (pParam->ip >= (baseAddr + VAL32(pNT->OptionalHeader.SizeOfImage))))
3040 {
3041 goto lDone;
3042 }
3043
3044 pParam->fRet = TRUE;
3045
3046lDone: ;
3047 }
3048 PAL_EXCEPT (EXCEPTION_EXECUTE_HANDLER)
3049 {
3050 }
3051 PAL_ENDTRY
3052#endif // !FEATURE_PAL
3053
3054 return param.fRet;
3055}
3056
3057#ifdef FEATURE_CORRUPTING_EXCEPTIONS
3058
3059// To include definition of EXCEPTION_SOFTSO
3060#include "corexcep.h"
3061
3062// These functions provide limited support for corrupting exceptions
3063// outside the VM folder. Its limited since we don't have access to the
3064// throwable.
3065//
3066// These functions are also wrapped by the corresponding CEHelper
3067// methods in excep.cpp.
3068
3069// Given an exception code, this method returns a BOOL to indicate if the
3070// code belongs to a corrupting exception or not.
3071BOOL IsProcessCorruptedStateException(DWORD dwExceptionCode, BOOL fCheckForSO /*=TRUE*/)
3072{
3073 LIMITED_METHOD_CONTRACT;
3074
3075 // By default, assume its not corrupting
3076 BOOL fIsCorruptedStateException = FALSE;
3077
3078 if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_legacyCorruptedStateExceptionsPolicy) == 1)
3079 {
3080 return fIsCorruptedStateException;
3081 }
3082
3083 // If we have been asked not to include SO in the CSE check
3084 // and the code represent SO, then exit now.
3085 if ((fCheckForSO == FALSE) && (dwExceptionCode == STATUS_STACK_OVERFLOW))
3086 {
3087 return fIsCorruptedStateException;
3088 }
3089
3090 switch(dwExceptionCode)
3091 {
3092 case STATUS_ACCESS_VIOLATION:
3093 case STATUS_STACK_OVERFLOW:
3094 case EXCEPTION_ILLEGAL_INSTRUCTION:
3095 case EXCEPTION_IN_PAGE_ERROR:
3096 case EXCEPTION_INVALID_DISPOSITION:
3097 case EXCEPTION_NONCONTINUABLE_EXCEPTION:
3098 case EXCEPTION_PRIV_INSTRUCTION:
3099 case STATUS_UNWIND_CONSOLIDATE:
3100 fIsCorruptedStateException = TRUE;
3101 break;
3102 }
3103
3104 return fIsCorruptedStateException;
3105}
3106
3107#endif // FEATURE_CORRUPTING_EXCEPTIONS
3108
3109void EnableTerminationOnHeapCorruption()
3110{
3111 HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
3112}
3113
3114#ifdef FEATURE_COMINTEROP
3115BOOL IsClrHostedLegacyComObject(REFCLSID rclsid)
3116{
3117 // let's simply check for all CLSIDs that are known to be runtime implemented and capped to 2.0
3118 return (
3119 rclsid == CLSID_ComCallUnmarshal ||
3120 rclsid == CLSID_CorMetaDataDispenser ||
3121 rclsid == CLSID_CorMetaDataDispenserRuntime ||
3122 rclsid == CLSID_TypeNameFactory);
3123}
3124#endif // FEATURE_COMINTEROP
3125
3126
3127
3128
3129namespace Clr
3130{
3131namespace Util
3132{
3133 static BOOL g_fLocalAppDataDirectoryInitted = FALSE;
3134 static WCHAR *g_wszLocalAppDataDirectory = NULL;
3135
3136// This api returns a pointer to a null-terminated string that contains the local appdata directory
3137// or it returns NULL in the case that the directory could not be found. The return value from this function
3138// is not actually checked for existence.
3139 HRESULT GetLocalAppDataDirectory(LPCWSTR *ppwzLocalAppDataDirectory)
3140 {
3141 CONTRACTL {
3142 NOTHROW;
3143 GC_NOTRIGGER;
3144 } CONTRACTL_END;
3145
3146 HRESULT hr = S_OK;
3147 *ppwzLocalAppDataDirectory = NULL;
3148
3149 EX_TRY
3150 {
3151 if (!g_fLocalAppDataDirectoryInitted)
3152 {
3153 WCHAR *wszLocalAppData = NULL;
3154
3155 DWORD cCharsNeeded;
3156 cCharsNeeded = GetEnvironmentVariableW(W("LOCALAPPDATA"), NULL, 0);
3157
3158 if ((cCharsNeeded != 0) && (cCharsNeeded < MAX_LONGPATH))
3159 {
3160 wszLocalAppData = new WCHAR[cCharsNeeded];
3161 cCharsNeeded = GetEnvironmentVariableW(W("LOCALAPPDATA"), wszLocalAppData, cCharsNeeded);
3162 if (cCharsNeeded != 0)
3163 {
3164 // We've collected the appropriate app data directory into a local. Now publish it.
3165 if (InterlockedCompareExchangeT(&g_wszLocalAppDataDirectory, wszLocalAppData, NULL) == NULL)
3166 {
3167 // This variable doesn't need to be freed, as it has been stored in the global
3168 wszLocalAppData = NULL;
3169 }
3170 }
3171 }
3172
3173 g_fLocalAppDataDirectoryInitted = TRUE;
3174 delete[] wszLocalAppData;
3175 }
3176 }
3177 EX_CATCH_HRESULT(hr);
3178
3179 if (SUCCEEDED(hr))
3180 *ppwzLocalAppDataDirectory = g_wszLocalAppDataDirectory;
3181
3182 return hr;
3183 }
3184
3185 HRESULT SetLocalAppDataDirectory(LPCWSTR pwzLocalAppDataDirectory)
3186 {
3187 CONTRACTL {
3188 NOTHROW;
3189 GC_NOTRIGGER;
3190 } CONTRACTL_END;
3191
3192 if (pwzLocalAppDataDirectory == NULL || *pwzLocalAppDataDirectory == W('\0'))
3193 return E_INVALIDARG;
3194
3195 if (g_fLocalAppDataDirectoryInitted)
3196 return E_UNEXPECTED;
3197
3198 HRESULT hr = S_OK;
3199
3200 EX_TRY
3201 {
3202 size_t size = wcslen(pwzLocalAppDataDirectory) + 1;
3203 WCHAR *wszLocalAppData = new WCHAR[size];
3204 wcscpy_s(wszLocalAppData, size, pwzLocalAppDataDirectory);
3205
3206 // We've collected the appropriate app data directory into a local. Now publish it.
3207 if (InterlockedCompareExchangeT(&g_wszLocalAppDataDirectory, wszLocalAppData, NULL) != NULL)
3208 {
3209 // Someone else already set LocalAppData. Free our copy and return an error.
3210 delete[] wszLocalAppData;
3211 hr = E_UNEXPECTED;
3212 }
3213
3214 g_fLocalAppDataDirectoryInitted = TRUE;
3215 }
3216 EX_CATCH_HRESULT(hr);
3217
3218 return hr;
3219 }
3220
3221#ifndef FEATURE_PAL
3222namespace Reg
3223{
3224 HRESULT ReadStringValue(HKEY hKey, LPCWSTR wszSubKeyName, LPCWSTR wszValueName, SString & ssValue)
3225 {
3226 STANDARD_VM_CONTRACT;
3227
3228 if (hKey == NULL)
3229 {
3230 return E_INVALIDARG;
3231 }
3232
3233 RegKeyHolder hTargetKey;
3234 if (wszSubKeyName == NULL || *wszSubKeyName == W('\0'))
3235 { // No subkey was requested, use hKey as the resolved key.
3236 hTargetKey = hKey;
3237 hTargetKey.SuppressRelease();
3238 }
3239 else
3240 { // Try to open the specified subkey.
3241 if (WszRegOpenKeyEx(hKey, wszSubKeyName, 0, KEY_READ, &hTargetKey) != ERROR_SUCCESS)
3242 return REGDB_E_CLASSNOTREG;
3243 }
3244
3245 DWORD type;
3246 DWORD size;
3247 if ((WszRegQueryValueEx(hTargetKey, wszValueName, 0, &type, 0, &size) == ERROR_SUCCESS) &&
3248 type == REG_SZ && size > 0)
3249 {
3250 LPWSTR wszValueBuf = ssValue.OpenUnicodeBuffer(static_cast<COUNT_T>((size / sizeof(WCHAR)) - 1));
3251 LONG lResult = WszRegQueryValueEx(
3252 hTargetKey,
3253 wszValueName,
3254 0,
3255 0,
3256 reinterpret_cast<LPBYTE>(wszValueBuf),
3257 &size);
3258
3259 _ASSERTE(lResult == ERROR_SUCCESS);
3260 if (lResult == ERROR_SUCCESS)
3261 {
3262 // Can't count on the returned size being accurate - I've seen at least
3263 // one string with an extra NULL at the end that will cause the resulting
3264 // SString to count the extra NULL as part of the string. An extra
3265 // terminating NULL is not a legitimate scenario for REG_SZ - this must
3266 // be done using REG_MULTI_SZ - however this was tolerated in the
3267 // past and so it would be a breaking change to stop doing so.
3268 _ASSERTE(wcslen(wszValueBuf) <= (size / sizeof(WCHAR)) - 1);
3269 ssValue.CloseBuffer((COUNT_T)wcsnlen(wszValueBuf, (size_t)size));
3270 }
3271 else
3272 {
3273 ssValue.CloseBuffer(0);
3274 return HRESULT_FROM_WIN32(lResult);
3275 }
3276
3277 return S_OK;
3278 }
3279 else
3280 {
3281 return REGDB_E_KEYMISSING;
3282 }
3283 }
3284
3285 HRESULT ReadStringValue(HKEY hKey, LPCWSTR wszSubKey, LPCWSTR wszName, __deref_out __deref_out_z LPWSTR* pwszValue)
3286 {
3287 CONTRACTL {
3288 NOTHROW;
3289 GC_NOTRIGGER;
3290 } CONTRACTL_END;
3291
3292 HRESULT hr = S_OK;
3293 EX_TRY
3294 {
3295 StackSString ssValue;
3296 if (SUCCEEDED(hr = ReadStringValue(hKey, wszSubKey, wszName, ssValue)))
3297 {
3298 *pwszValue = new WCHAR[ssValue.GetCount() + 1];
3299 wcscpy_s(*pwszValue, ssValue.GetCount() + 1, ssValue.GetUnicode());
3300 }
3301 }
3302 EX_CATCH_HRESULT(hr);
3303 return hr;
3304 }
3305} // namespace Reg
3306
3307namespace Com
3308{
3309 namespace __imp
3310 {
3311 __success(return == S_OK)
3312 static
3313 HRESULT FindSubKeyDefaultValueForCLSID(REFCLSID rclsid, LPCWSTR wszSubKeyName, SString & ssValue)
3314 {
3315 STANDARD_VM_CONTRACT;
3316
3317 HRESULT hr = S_OK;
3318
3319 WCHAR wszClsid[39];
3320 if (GuidToLPWSTR(rclsid, wszClsid, NumItems(wszClsid)) == 0)
3321 return E_UNEXPECTED;
3322
3323 StackSString ssKeyName;
3324 ssKeyName.Append(SL(W("CLSID\\")));
3325 ssKeyName.Append(wszClsid);
3326 ssKeyName.Append(SL(W("\\")));
3327 ssKeyName.Append(wszSubKeyName);
3328
3329 return Clr::Util::Reg::ReadStringValue(HKEY_CLASSES_ROOT, ssKeyName.GetUnicode(), NULL, ssValue);
3330 }
3331
3332 __success(return == S_OK)
3333 static
3334 HRESULT FindSubKeyDefaultValueForCLSID(REFCLSID rclsid, LPCWSTR wszSubKeyName, __deref_out __deref_out_z LPWSTR* pwszValue)
3335 {
3336 CONTRACTL {
3337 NOTHROW;
3338 GC_NOTRIGGER;
3339 } CONTRACTL_END;
3340
3341 HRESULT hr = S_OK;
3342 EX_TRY
3343 {
3344 StackSString ssValue;
3345 if (SUCCEEDED(hr = FindSubKeyDefaultValueForCLSID(rclsid, wszSubKeyName, ssValue)))
3346 {
3347 *pwszValue = new WCHAR[ssValue.GetCount() + 1];
3348 wcscpy_s(*pwszValue, ssValue.GetCount() + 1, ssValue.GetUnicode());
3349 }
3350 }
3351 EX_CATCH_HRESULT(hr);
3352 return hr;
3353 }
3354 }
3355
3356 HRESULT FindServerUsingCLSID(REFCLSID rclsid, __deref_out __deref_out_z LPWSTR* pwszServerName)
3357 {
3358 WRAPPER_NO_CONTRACT;
3359 return __imp::FindSubKeyDefaultValueForCLSID(rclsid, W("Server"), pwszServerName);
3360 }
3361
3362 HRESULT FindServerUsingCLSID(REFCLSID rclsid, SString & ssServerName)
3363 {
3364 WRAPPER_NO_CONTRACT;
3365 return __imp::FindSubKeyDefaultValueForCLSID(rclsid, W("Server"), ssServerName);
3366 }
3367
3368 HRESULT FindInprocServer32UsingCLSID(REFCLSID rclsid, __deref_out __deref_out_z LPWSTR* pwszInprocServer32Name)
3369 {
3370 WRAPPER_NO_CONTRACT;
3371 return __imp::FindSubKeyDefaultValueForCLSID(rclsid, W("InprocServer32"), pwszInprocServer32Name);
3372 }
3373
3374 HRESULT FindInprocServer32UsingCLSID(REFCLSID rclsid, SString & ssInprocServer32Name)
3375 {
3376 WRAPPER_NO_CONTRACT;
3377 return __imp::FindSubKeyDefaultValueForCLSID(rclsid, W("InprocServer32"), ssInprocServer32Name);
3378 }
3379
3380 BOOL IsMscoreeInprocServer32(const SString & ssInprocServer32Name)
3381 {
3382 WRAPPER_NO_CONTRACT;
3383
3384 return (ssInprocServer32Name.EqualsCaseInsensitive(SL(MSCOREE_SHIM_W)) ||
3385 ssInprocServer32Name.EndsWithCaseInsensitive(SL(W("\\") MSCOREE_SHIM_W)));
3386 }
3387
3388 BOOL CLSIDHasMscoreeAsInprocServer32(REFCLSID rclsid)
3389 {
3390 WRAPPER_NO_CONTRACT;
3391
3392 StackSString ssInprocServer32;
3393 FindInprocServer32UsingCLSID(rclsid, ssInprocServer32);
3394 return IsMscoreeInprocServer32(ssInprocServer32);
3395 }
3396
3397} // namespace Com
3398#endif // FEATURE_PAL
3399
3400namespace Win32
3401{
3402 void GetModuleFileName(
3403 HMODULE hModule,
3404 SString & ssFileName,
3405 bool fAllowLongFileNames)
3406 {
3407 STANDARD_VM_CONTRACT;
3408
3409 // Try to use what the SString already has allocated. If it does not have anything allocated
3410 // or it has < 20 characters allocated, then bump the size requested to _MAX_PATH.
3411
3412 DWORD dwResult = WszGetModuleFileName(hModule, ssFileName);
3413
3414
3415 if (dwResult == 0)
3416 ThrowHR(HRESULT_FROM_GetLastError());
3417
3418 _ASSERTE(dwResult != 0 );
3419 }
3420
3421 // Returns heap-allocated string in *pwszFileName
3422 HRESULT GetModuleFileName(
3423 HMODULE hModule,
3424 __deref_out_z LPWSTR * pwszFileName,
3425 bool fAllowLongFileNames)
3426 {
3427 CONTRACTL {
3428 NOTHROW;
3429 GC_NOTRIGGER;
3430 PRECONDITION(CheckPointer(pwszFileName));
3431 } CONTRACTL_END;
3432
3433 HRESULT hr = S_OK;
3434 EX_TRY
3435 {
3436 InlineSString<_MAX_PATH> ssFileName;
3437 GetModuleFileName(hModule, ssFileName);
3438 *pwszFileName = DuplicateStringThrowing(ssFileName.GetUnicode());
3439 }
3440 EX_CATCH_HRESULT(hr);
3441
3442 return hr;
3443 }
3444
3445 void GetFullPathName(
3446 SString const & ssFileName,
3447 SString & ssPathName,
3448 DWORD * pdwFilePartIdx,
3449 bool fAllowLongFileNames)
3450 {
3451 STANDARD_VM_CONTRACT;
3452
3453 // Get the required buffer length (including terminating NULL).
3454 DWORD dwLengthRequired = WszGetFullPathName(ssFileName.GetUnicode(), 0, NULL, NULL);
3455
3456 if (dwLengthRequired == 0)
3457 ThrowHR(HRESULT_FROM_GetLastError());
3458
3459 LPWSTR wszPathName = ssPathName.OpenUnicodeBuffer(dwLengthRequired - 1);
3460 LPWSTR wszFileName = NULL;
3461 DWORD dwLengthWritten = WszGetFullPathName(
3462 ssFileName.GetUnicode(),
3463 dwLengthRequired,
3464 wszPathName,
3465 &wszFileName);
3466
3467 // Calculate the index while the buffer is open and the string pointer is stable.
3468 if (dwLengthWritten != 0 && dwLengthWritten < dwLengthRequired && pdwFilePartIdx != NULL)
3469 *pdwFilePartIdx = static_cast<DWORD>(wszFileName - wszPathName);
3470
3471 ssPathName.CloseBuffer(dwLengthWritten < dwLengthRequired ? dwLengthWritten : 0);
3472
3473 if (dwLengthRequired == 0)
3474 ThrowHR(HRESULT_FROM_GetLastError());
3475
3476 // Overly defensive? Perhaps.
3477 if (!(dwLengthWritten < dwLengthRequired))
3478 ThrowHR(E_UNEXPECTED);
3479
3480 }
3481} // namespace Win32
3482
3483} // namespace Util
3484} // namespace Clr
3485