1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 //*****************************************************************************
9 // This contains a bunch of C++ utility classes.
11 //*****************************************************************************
12 #include "stdafx.h" // Precompiled header key.
16 #include "pedecoder.h"
17 #include "loaderheap.h"
18 #include "sigparser.h"
23 #ifndef DACCESS_COMPILE
24 UINT32 g_nClrInstanceId = 0;
25 #endif //!DACCESS_COMPILE
27 //********** Code. ************************************************************
29 #if defined(FEATURE_COMINTEROP) && !defined(FEATURE_CORESYSTEM)
30 extern WinRTStatusEnum gWinRTStatus = WINRT_STATUS_UNINITED;
31 #endif // FEATURE_COMINTEROP && !FEATURE_CORESYSTEM
33 #if defined(FEATURE_COMINTEROP) && !defined(FEATURE_CORESYSTEM)
34 //------------------------------------------------------------------------------
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
42 void InitWinRTStatus()
44 STATIC_CONTRACT_NOTHROW;
45 STATIC_CONTRACT_GC_NOTRIGGER;
46 STATIC_CONTRACT_CANNOT_TAKE_LOCK;
48 WinRTStatusEnum winRTStatus = WINRT_STATUS_UNSUPPORTED;
50 const WCHAR wszComBaseDll[] = W("\\combase.dll");
51 const SIZE_T cchComBaseDll = _countof(wszComBaseDll);
53 WCHAR wszComBasePath[MAX_LONGPATH + 1];
54 const SIZE_T cchComBasePath = _countof(wszComBasePath);
56 ZeroMemory(wszComBasePath, cchComBasePath * sizeof(wszComBasePath[0]));
58 UINT cchSystemDirectory = WszGetSystemDirectory(wszComBasePath, MAX_LONGPATH);
60 // Make sure that we're only probing in the system directory. If we can't find the system directory, or
61 // we find it but combase.dll doesn't fit into it, we'll fall back to a safe default of saying that WinRT
62 // is simply not present.
63 if (cchSystemDirectory > 0 && cchComBasePath - cchSystemDirectory >= cchComBaseDll)
65 if (wcscat_s(wszComBasePath, wszComBaseDll) == 0)
67 HModuleHolder hComBase(WszLoadLibrary(wszComBasePath));
70 FARPROC activateInstace = GetProcAddress(hComBase, "RoInitialize");
71 if (activateInstace != NULL)
73 winRTStatus = WINRT_STATUS_SUPPORTED;
79 gWinRTStatus = winRTStatus;
81 #endif // FEATURE_COMINTEROP && !FEATURE_CORESYSTEM
82 //*****************************************************************************
83 // Convert a string of hex digits into a hex value of the specified # of bytes.
84 //*****************************************************************************
85 HRESULT GetHex( // Return status.
86 LPCSTR szStr, // String to convert.
87 int size, // # of bytes in pResult.
88 void *pResult) // Buffer for result.
96 int count = size * 2; // # of bytes to take from string.
97 unsigned int Result = 0; // Result value.
100 _ASSERTE(size == 1 || size == 2 || size == 4);
102 while (count-- && (ch = *szStr++) != '\0')
106 case '0': case '1': case '2': case '3': case '4':
107 case '5': case '6': case '7': case '8': case '9':
108 Result = 16 * Result + (ch - '0');
111 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
112 Result = 16 * Result + 10 + (ch - 'A');
115 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
116 Result = 16 * Result + 10 + (ch - 'a');
128 *((BYTE *) pResult) = (BYTE) Result;
132 *((WORD *) pResult) = (WORD) Result;
136 *((DWORD *) pResult) = Result;
146 //*****************************************************************************
147 // Convert a pointer to a string into a GUID.
148 //*****************************************************************************
149 HRESULT LPCSTRToGuid( // Return status.
150 LPCSTR szGuid, // String to convert.
151 GUID *psGuid) // Buffer for converted GUID.
161 // Verify the surrounding syntax.
162 if (strlen(szGuid) != 38 || szGuid[0] != '{' || szGuid[9] != '-' ||
163 szGuid[14] != '-' || szGuid[19] != '-' || szGuid[24] != '-' || szGuid[37] != '}')
168 // Parse the first 3 fields.
169 if (FAILED(GetHex(szGuid + 1, 4, &psGuid->Data1)))
171 if (FAILED(GetHex(szGuid + 10, 2, &psGuid->Data2)))
173 if (FAILED(GetHex(szGuid + 15, 2, &psGuid->Data3)))
176 // Get the last two fields (which are byte arrays).
177 for (i = 0; i < 2; ++i)
179 if (FAILED(GetHex(szGuid + 20 + (i * 2), 1, &psGuid->Data4[i])))
184 for (i=0; i < 6; ++i)
186 if (FAILED(GetHex(szGuid + 25 + (i * 2), 1, &psGuid->Data4[i+2])))
196 // Global utility functions.
202 typedef HRESULT __stdcall DLLGETCLASSOBJECT(REFCLSID rclsid,
206 EXTERN_C const IID _IID_IClassFactory =
207 {0x00000001, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
211 HRESULT FakeCoCallDllGetClassObject(
224 _ASSERTE(ppv != nullptr);
228 // Initialize [out] HMODULE (if it was requested)
229 if (phmodDll != nullptr)
232 bool fIsDllPathPrefix = (wszDllPath != nullptr) && (wcslen(wszDllPath) > 0) && (wszDllPath[wcslen(wszDllPath) - 1] == W('\\'));
234 // - An empty string will be treated as NULL.
235 // - A string ending will a backslash will be treated as a prefix for where to look for the DLL
236 // if the InProcServer32 value is just a DLL name and not a full path.
237 StackSString ssDllName;
238 if ((wszDllPath == nullptr) || (wszDllPath[0] == W('\0')) || fIsDllPathPrefix)
241 IfFailRet(Clr::Util::Com::FindInprocServer32UsingCLSID(rclsid, ssDllName));
245 if (fIsDllPathPrefix)
247 SString::Iterator i = ssDllName.Begin();
248 if (!ssDllName.Find(i, W('\\')))
249 { // If the InprocServer32 is just a DLL name (not a fully qualified path), then
250 // prefix wszFilePath with wszDllPath.
251 ssDllName.Insert(i, wszDllPath);
255 EX_CATCH_HRESULT(hr);
258 wszDllPath = ssDllName.GetUnicode();
259 #else // !FEATURE_PAL
261 #endif // !FEATURE_PAL
263 _ASSERTE(wszDllPath != nullptr);
265 // We've got the name of the DLL to load, so load it.
266 HModuleHolder hDll = WszLoadLibraryEx(wszDllPath, nullptr, GetLoadWithAlteredSearchPathFlag());
268 return HRESULT_FROM_GetLastError();
270 // We've loaded the DLL, so find the DllGetClassObject function.
271 DLLGETCLASSOBJECT *dllGetClassObject = (DLLGETCLASSOBJECT*)GetProcAddress(hDll, "DllGetClassObject");
272 if (dllGetClassObject == nullptr)
273 return HRESULT_FROM_GetLastError();
275 // Call the function to get a class object for the rclsid and riid passed in.
276 IfFailRet(dllGetClassObject(rclsid, riid, ppv));
278 hDll.SuppressRelease();
280 if (phmodDll != nullptr)
281 *phmodDll = hDll.GetValue();
287 // ----------------------------------------------------------------------------
288 // FakeCoCreateInstanceEx
291 // A private function to do the equivalent of a CoCreateInstance in cases where we
292 // can't make the real call. Use this when, for instance, you need to create a symbol
293 // reader in the Runtime but we're not CoInitialized. Obviously, this is only good
294 // for COM objects for which CoCreateInstance is just a glorified find-and-load-me
298 // * rclsid - [in] CLSID of object to instantiate
299 // * wszDllPath [in] - Path to profiler DLL. If wszDllPath is NULL, FakeCoCreateInstanceEx
300 // will look up the registry to find the path of the COM dll associated with rclsid.
301 // If the path ends in a backslash, FakeCoCreateInstanceEx will treat this as a prefix
302 // if the InprocServer32 found in the registry is a simple filename (not a full path).
303 // This allows the caller to specify the directory in which the InprocServer32 should
305 // * riid - [in] IID of interface on object to return in ppv
306 // * ppv - [out] Pointer to implementation of requested interface
307 // * phmodDll - [out] HMODULE of DLL that was loaded to instantiate the COM object.
308 // The caller may eventually call FreeLibrary() on this if it can be determined
309 // that we no longer reference the generated COM object or dependencies. Else, the
310 // caller may ignore this and the DLL will stay loaded forever. If caller
311 // specifies phmodDll==NULL, then this parameter is ignored and the HMODULE is not
315 // HRESULT indicating success or failure.
318 // * (*phmodDll) on [out] may always be trusted, even if this function returns an
319 // error. Therefore, even if creation of the COM object failed, if (*phmodDll !=
320 // NULL), then the DLL was actually loaded. The caller may wish to call
321 // FreeLibrary on (*phmodDll) in such a case.
322 HRESULT FakeCoCreateInstanceEx(REFCLSID rclsid,
336 // Call the function to get a class factory for the rclsid passed in.
338 ReleaseHolder<IClassFactory> classFactory;
339 IfFailRet(FakeCoCallDllGetClassObject(rclsid, wszDllPath, _IID_IClassFactory, (void**)&classFactory, &hDll));
341 // Ask the class factory to create an instance of the
343 IfFailRet(classFactory->CreateInstance(NULL, riid, ppv));
345 hDll.SuppressRelease();
347 if (phmodDll != NULL)
349 *phmodDll = hDll.GetValue();
355 #if USE_UPPER_ADDRESS
356 static BYTE * s_CodeMinAddr; // Preferred region to allocate the code in.
357 static BYTE * s_CodeMaxAddr;
358 static BYTE * s_CodeAllocStart;
359 static BYTE * s_CodeAllocHint; // Next address to try to allocate for code in the preferred region.
363 // Use this function to initialize the s_CodeAllocHint
364 // during startup. base is runtime .dll base address,
365 // size is runtime .dll virtual size.
367 void InitCodeAllocHint(SIZE_T base, SIZE_T size, int randomPageOffset)
369 #if USE_UPPER_ADDRESS
372 // If GetForceRelocs is enabled we don't constrain the pMinAddr
373 if (PEDecoder::GetForceRelocs())
378 // If we are using the UPPER_ADDRESS space (on Win64)
379 // then for any code heap that doesn't specify an address
380 // range using [pMinAddr..pMaxAddr] we place it in the
381 // upper address space
382 // This enables us to avoid having to use long JumpStubs
383 // to reach the code for our ngen-ed images.
384 // Which are also placed in the UPPER_ADDRESS space.
386 SIZE_T reach = 0x7FFF0000u;
388 // We will choose the preferred code region based on the address of clr.dll. The JIT helpers
389 // in clr.dll are the most heavily called functions.
390 s_CodeMinAddr = (base + size > reach) ? (BYTE *)(base + size - reach) : (BYTE *)0;
391 s_CodeMaxAddr = (base + reach > base) ? (BYTE *)(base + reach) : (BYTE *)-1;
395 if (s_CodeMinAddr <= (BYTE *)CODEHEAP_START_ADDRESS &&
396 (BYTE *)CODEHEAP_START_ADDRESS < s_CodeMaxAddr)
398 // clr.dll got loaded at its preferred base address? (OS without ASLR - pre-Vista)
399 // Use the code head start address that does not cause collisions with NGen images.
400 // This logic is coupled with scripts that we use to assign base addresses.
401 pStart = (BYTE *)CODEHEAP_START_ADDRESS;
404 if (base > UINT32_MAX)
406 // clr.dll got address assigned by ASLR?
407 // Try to occupy the space as far as possible to minimize collisions with other ASLR assigned
408 // addresses. Do not start at s_CodeMinAddr exactly so that we can also reach common native images
409 // that can be placed at higher addresses than clr.dll.
410 pStart = s_CodeMinAddr + (s_CodeMaxAddr - s_CodeMinAddr) / 8;
414 // clr.dll missed the base address?
415 // Try to occupy the space right after it.
416 pStart = (BYTE *)(base + size);
419 // Randomize the adddress space
420 pStart += GetOsPageSize() * randomPageOffset;
422 s_CodeAllocStart = pStart;
423 s_CodeAllocHint = pStart;
428 // Use this function to reset the s_CodeAllocHint
429 // after unloading an AppDomain
431 void ResetCodeAllocHint()
433 LIMITED_METHOD_CONTRACT;
434 #if USE_UPPER_ADDRESS
435 s_CodeAllocHint = s_CodeAllocStart;
440 // Returns TRUE if p is located in near clr.dll that allows us
441 // to use rel32 IP-relative addressing modes.
443 BOOL IsPreferredExecutableRange(void * p)
445 LIMITED_METHOD_CONTRACT;
446 #if USE_UPPER_ADDRESS
447 if (s_CodeMinAddr <= (BYTE *)p && (BYTE *)p < s_CodeMaxAddr)
454 // Allocate free memory that will be used for executable code
455 // Handles the special requirements that we have on 64-bit platforms
456 // where we want the executable memory to be located near clr.dll
458 BYTE * ClrVirtualAllocExecutable(SIZE_T dwSize,
459 DWORD flAllocationType,
468 #if USE_UPPER_ADDRESS
470 // If we are using the UPPER_ADDRESS space (on Win64)
471 // then for any heap that will contain executable code
472 // we will place it in the upper address space
474 // This enables us to avoid having to use JumpStubs
475 // to reach the code for our ngen-ed images on x64,
476 // since they are also placed in the UPPER_ADDRESS space.
478 BYTE * pHint = s_CodeAllocHint;
480 if (dwSize <= (SIZE_T)(s_CodeMaxAddr - s_CodeMinAddr) && pHint != NULL)
482 // Try to allocate in the preferred region after the hint
483 BYTE * pResult = ClrVirtualAllocWithinRange(pHint, s_CodeMaxAddr, dwSize, flAllocationType, flProtect);
487 s_CodeAllocHint = pResult + dwSize;
491 // Try to allocate in the preferred region before the hint
492 pResult = ClrVirtualAllocWithinRange(s_CodeMinAddr, pHint + dwSize, dwSize, flAllocationType, flProtect);
496 s_CodeAllocHint = pResult + dwSize;
500 s_CodeAllocHint = NULL;
504 #endif // USE_UPPER_ADDRESS
507 // Tell PAL to use the executable memory allocator to satisfy this request for virtual memory.
508 // This will allow us to place JIT'ed code close to the coreclr library
509 // and thus improve performance by avoiding jump stubs in managed code.
510 flAllocationType |= MEM_RESERVE_EXECUTABLE;
511 #endif // FEATURE_PAL
513 return (BYTE *) ClrVirtualAlloc (NULL, dwSize, flAllocationType, flProtect);
518 // Allocate free memory with specific alignment.
520 LPVOID ClrVirtualAllocAligned(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect, SIZE_T alignment)
522 // Verify that the alignment is a power of 2
523 _ASSERTE(alignment != 0);
524 _ASSERTE((alignment & (alignment - 1)) == 0);
528 // The VirtualAlloc on Windows ensures 64kB alignment
529 _ASSERTE(alignment <= 0x10000);
530 return ClrVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
532 #else // !FEATURE_PAL
534 if(alignment < GetOsPageSize()) alignment = GetOsPageSize();
536 // UNIXTODO: Add a specialized function to PAL so that we don't have to waste memory
538 SIZE_T addr = (SIZE_T)ClrVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
539 return (LPVOID)((addr + (alignment - 1)) & ~(alignment - 1));
541 #endif // !FEATURE_PAL
545 static DWORD ShouldInjectFaultInRange()
547 static DWORD fInjectFaultInRange = 99;
549 if (fInjectFaultInRange == 99)
550 fInjectFaultInRange = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_InjectFault) & 0x40);
551 return fInjectFaultInRange;
555 // Reserves free memory within the range [pMinAddr..pMaxAddr] using
556 // ClrVirtualQuery to find free memory and ClrVirtualAlloc to reserve it.
558 // This method only supports the flAllocationType of MEM_RESERVE, and expects that the memory
559 // is being reserved for the purpose of eventually storing executable code.
561 // Callers also should set dwSize to a multiple of sysInfo.dwAllocationGranularity (64k).
562 // That way they can reserve a large region and commit smaller sized pages
563 // from that region until it fills up.
565 // This functions returns the reserved memory block upon success
567 // It returns NULL when it fails to find any memory that satisfies
571 BYTE * ClrVirtualAllocWithinRange(const BYTE *pMinAddr,
572 const BYTE *pMaxAddr,
574 DWORD flAllocationType,
580 PRECONDITION(dwSize != 0);
581 PRECONDITION(flAllocationType == MEM_RESERVE);
585 BYTE *pResult = nullptr; // our return value;
587 static unsigned countOfCalls = 0; // We log the number of tims we call this method
588 countOfCalls++; // increment the call counter
596 // First lets normalize the pMinAddr and pMaxAddr values
598 // If pMinAddr is NULL then set it to BOT_MEMORY
599 if ((pMinAddr == 0) || (pMinAddr < (BYTE *) BOT_MEMORY))
601 pMinAddr = (BYTE *) BOT_MEMORY;
604 // If pMaxAddr is NULL then set it to TOP_MEMORY
605 if ((pMaxAddr == 0) || (pMaxAddr > (BYTE *) TOP_MEMORY))
607 pMaxAddr = (BYTE *) TOP_MEMORY;
610 // If pMaxAddr is not greater than pMinAddr we can not make an allocation
611 if (pMaxAddr <= pMinAddr)
616 // If pMinAddr is BOT_MEMORY and pMaxAddr is TOP_MEMORY
617 // then we can call ClrVirtualAlloc instead
618 if ((pMinAddr == (BYTE *) BOT_MEMORY) && (pMaxAddr == (BYTE *) TOP_MEMORY))
620 return (BYTE*) ClrVirtualAlloc(nullptr, dwSize, flAllocationType, flProtect);
624 pResult = (BYTE *)PAL_VirtualReserveFromExecutableMemoryAllocatorWithinRange(pMinAddr, pMaxAddr, dwSize);
625 if (pResult != nullptr)
629 #endif // FEATURE_PAL
631 // We will do one scan from [pMinAddr .. pMaxAddr]
632 // First align the tryAddr up to next 64k base address.
633 // See docs for VirtualAllocEx and lpAddress and 64k alignment for reasons.
635 BYTE * tryAddr = (BYTE *)ALIGN_UP((BYTE *)pMinAddr, VIRTUAL_ALLOC_RESERVE_GRANULARITY);
636 bool virtualQueryFailed = false;
637 bool faultInjected = false;
638 unsigned virtualQueryCount = 0;
640 // Now scan memory and try to find a free block of the size requested.
641 while ((tryAddr + dwSize) <= (BYTE *) pMaxAddr)
643 MEMORY_BASIC_INFORMATION mbInfo;
645 // Use VirtualQuery to find out if this address is MEM_FREE
648 if (!ClrVirtualQuery((LPCVOID)tryAddr, &mbInfo, sizeof(mbInfo)))
650 // Exit and return nullptr if the VirtualQuery call fails.
651 virtualQueryFailed = true;
655 // Is there enough memory free from this start location?
656 // Note that for most versions of UNIX the mbInfo.RegionSize returned will always be 0
657 if ((mbInfo.State == MEM_FREE) &&
658 (mbInfo.RegionSize >= (SIZE_T) dwSize || mbInfo.RegionSize == 0))
660 // Try reserving the memory using VirtualAlloc now
661 pResult = (BYTE*)ClrVirtualAlloc(tryAddr, dwSize, MEM_RESERVE, flProtect);
663 // Normally this will be successful
665 if (pResult != nullptr)
672 if (ShouldInjectFaultInRange())
674 // return nullptr (failure)
675 faultInjected = true;
680 // On UNIX we can also fail if our request size 'dwSize' is larger than 64K and
681 // and our tryAddr is pointing at a small MEM_FREE region (smaller than 'dwSize')
682 // However we can't distinguish between this and the race case.
684 // We might fail in a race. So just move on to next region and continue trying
685 tryAddr = tryAddr + VIRTUAL_ALLOC_RESERVE_GRANULARITY;
689 // Try another section of memory
690 tryAddr = max(tryAddr + VIRTUAL_ALLOC_RESERVE_GRANULARITY,
691 (BYTE*) mbInfo.BaseAddress + mbInfo.RegionSize);
695 STRESS_LOG7(LF_JIT, LL_INFO100,
696 "ClrVirtualAllocWithinRange request #%u for %08x bytes in [ %p .. %p ], query count was %u - returned %s: %p\n",
697 countOfCalls, (DWORD)dwSize, pMinAddr, pMaxAddr,
698 virtualQueryCount, (pResult != nullptr) ? "success" : "failure", pResult);
700 // If we failed this call the process will typically be terminated
701 // so we log any additional reason for failing this call.
703 if (pResult == nullptr)
705 if ((tryAddr + dwSize) > (BYTE *)pMaxAddr)
707 // Our tryAddr reached pMaxAddr
708 STRESS_LOG0(LF_JIT, LL_INFO100, "Additional reason: Address space exhausted.\n");
711 if (virtualQueryFailed)
713 STRESS_LOG0(LF_JIT, LL_INFO100, "Additional reason: VirtualQuery operation failed.\n");
718 STRESS_LOG0(LF_JIT, LL_INFO100, "Additional reason: fault injected.\n");
725 //******************************************************************************
727 //******************************************************************************
728 #if !defined(FEATURE_REDHAWK)
730 /*static*/ LPVOID NumaNodeInfo::VirtualAllocExNuma(HANDLE hProc, LPVOID lpAddr, SIZE_T dwSize,
731 DWORD allocType, DWORD prot, DWORD node)
733 return ::VirtualAllocExNuma(hProc, lpAddr, dwSize, allocType, prot, node);
737 /*static*/ BOOL NumaNodeInfo::GetNumaProcessorNodeEx(PPROCESSOR_NUMBER proc_no, PUSHORT node_no)
739 return ::GetNumaProcessorNodeEx(proc_no, node_no);
741 /*static*/ bool NumaNodeInfo::GetNumaInfo(PUSHORT total_nodes, DWORD* max_procs_per_node)
743 if (m_enableGCNumaAware)
745 DWORD currentProcsOnNode = 0;
746 for (int i = 0; i < m_nNodes; i++)
748 GROUP_AFFINITY processorMask;
749 if (GetNumaNodeProcessorMaskEx(i, &processorMask))
751 DWORD procsOnNode = 0;
752 uintptr_t mask = (uintptr_t)processorMask.Mask;
759 currentProcsOnNode = max(currentProcsOnNode, procsOnNode);
763 *max_procs_per_node = currentProcsOnNode;
764 *total_nodes = m_nNodes;
770 #else // !FEATURE_PAL
771 /*static*/ BOOL NumaNodeInfo::GetNumaProcessorNodeEx(USHORT proc_no, PUSHORT node_no)
773 return PAL_GetNumaProcessorNode(proc_no, node_no);
775 #endif // !FEATURE_PAL
778 /*static*/ BOOL NumaNodeInfo::m_enableGCNumaAware = FALSE;
779 /*static*/ uint16_t NumaNodeInfo::m_nNodes = 0;
780 /*static*/ BOOL NumaNodeInfo::InitNumaNodeInfoAPI()
782 #if !defined(FEATURE_REDHAWK)
783 //check for numa support if multiple heaps are used
786 if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_GCNumaAware) == 0)
789 // fail to get the highest numa node number
790 if (!::GetNumaHighestNodeNumber(&highest) || (highest == 0))
793 m_nNodes = (USHORT)(highest + 1);
801 /*static*/ BOOL NumaNodeInfo::CanEnableGCNumaAware()
803 return m_enableGCNumaAware;
806 /*static*/ void NumaNodeInfo::InitNumaNodeInfo()
808 m_enableGCNumaAware = InitNumaNodeInfoAPI();
813 //******************************************************************************
815 //******************************************************************************
816 #if !defined(FEATURE_REDHAWK)
817 /*static*/ //CPUGroupInfo::PNTQSIEx CPUGroupInfo::m_pNtQuerySystemInformationEx = NULL;
819 /*static*/ BOOL CPUGroupInfo::GetLogicalProcessorInformationEx(LOGICAL_PROCESSOR_RELATIONSHIP relationship,
820 SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *slpiex, PDWORD count)
822 LIMITED_METHOD_CONTRACT;
823 return ::GetLogicalProcessorInformationEx(relationship, slpiex, count);
826 /*static*/ BOOL CPUGroupInfo::SetThreadGroupAffinity(HANDLE h,
827 GROUP_AFFINITY *groupAffinity, GROUP_AFFINITY *previousGroupAffinity)
829 LIMITED_METHOD_CONTRACT;
830 return ::SetThreadGroupAffinity(h, groupAffinity, previousGroupAffinity);
833 /*static*/ BOOL CPUGroupInfo::GetThreadGroupAffinity(HANDLE h, GROUP_AFFINITY *groupAffinity)
835 LIMITED_METHOD_CONTRACT;
836 return ::GetThreadGroupAffinity(h, groupAffinity);
839 /*static*/ BOOL CPUGroupInfo::GetSystemTimes(FILETIME *idleTime, FILETIME *kernelTime, FILETIME *userTime)
841 LIMITED_METHOD_CONTRACT;
844 return ::GetSystemTimes(idleTime, kernelTime, userTime);
851 /*static*/ BOOL CPUGroupInfo::m_enableGCCPUGroups = FALSE;
852 /*static*/ BOOL CPUGroupInfo::m_threadUseAllCpuGroups = FALSE;
853 /*static*/ WORD CPUGroupInfo::m_nGroups = 0;
854 /*static*/ WORD CPUGroupInfo::m_nProcessors = 0;
855 /*static*/ WORD CPUGroupInfo::m_initialGroup = 0;
856 /*static*/ CPU_Group_Info *CPUGroupInfo::m_CPUGroupInfoArray = NULL;
857 /*static*/ LONG CPUGroupInfo::m_initialization = 0;
858 /*static*/ bool CPUGroupInfo::s_hadSingleProcessorAtStartup = false;
860 #if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
861 // Calculate greatest common divisor
862 DWORD GCD(DWORD u, DWORD v)
874 // Calculate least common multiple
875 DWORD LCM(DWORD u, DWORD v)
877 return u / GCD(u, v) * v;
881 /*static*/ BOOL CPUGroupInfo::InitCPUGroupInfoArray()
890 #if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
891 BYTE *bBuffer = NULL;
892 SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pSLPIEx = NULL;
893 SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pRecord = NULL;
895 DWORD byteOffset = 0;
896 DWORD dwNumElements = 0;
899 if (CPUGroupInfo::GetLogicalProcessorInformationEx(RelationGroup, pSLPIEx, &cbSLPIEx) &&
900 GetLastError() != ERROR_INSUFFICIENT_BUFFER)
905 // Fail to allocate buffer
906 bBuffer = new (nothrow) BYTE[ cbSLPIEx ];
910 pSLPIEx = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)bBuffer;
911 if (!::GetLogicalProcessorInformationEx(RelationGroup, pSLPIEx, &cbSLPIEx))
918 while (byteOffset < cbSLPIEx)
920 if (pRecord->Relationship == RelationGroup)
922 m_nGroups = pRecord->Group.ActiveGroupCount;
925 byteOffset += pRecord->Size;
926 pRecord = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)(bBuffer + byteOffset);
929 m_CPUGroupInfoArray = new (nothrow) CPU_Group_Info[m_nGroups];
930 if (m_CPUGroupInfoArray == NULL)
936 for (DWORD i = 0; i < m_nGroups; i++)
938 m_CPUGroupInfoArray[i].nr_active = (WORD)pRecord->Group.GroupInfo[i].ActiveProcessorCount;
939 m_CPUGroupInfoArray[i].active_mask = pRecord->Group.GroupInfo[i].ActiveProcessorMask;
940 m_nProcessors += m_CPUGroupInfoArray[i].nr_active;
941 dwWeight = LCM(dwWeight, (DWORD)m_CPUGroupInfoArray[i].nr_active);
944 // The number of threads per group that can be supported will depend on the number of CPU groups
945 // and the number of LPs within each processor group. For example, when the number of LPs in
946 // CPU groups is the same and is 64, the number of threads per group before weight overflow
947 // would be 2^32/2^6 = 2^26 (64M threads)
948 for (DWORD i = 0; i < m_nGroups; i++)
950 m_CPUGroupInfoArray[i].groupWeight = dwWeight / (DWORD)m_CPUGroupInfoArray[i].nr_active;
951 m_CPUGroupInfoArray[i].activeThreadWeight = 0;
954 delete[] bBuffer; // done with it; free it
961 /*static*/ BOOL CPUGroupInfo::InitCPUGroupInfoRange()
963 LIMITED_METHOD_CONTRACT;
965 #if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
969 for (WORD i = 0; i < m_nGroups; i++)
971 nr_proc += m_CPUGroupInfoArray[i].nr_active;
972 m_CPUGroupInfoArray[i].begin = begin;
973 m_CPUGroupInfoArray[i].end = nr_proc - 1;
982 /*static*/ void CPUGroupInfo::InitCPUGroupInfo()
991 #if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
992 BOOL enableGCCPUGroups = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_GCCpuGroup) != 0;
993 BOOL threadUseAllCpuGroups = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_Thread_UseAllCpuGroups) != 0;
995 if (!enableGCCPUGroups)
998 if (!InitCPUGroupInfoArray())
1001 if (!InitCPUGroupInfoRange())
1004 // initalGroup is whatever the CPU group that the main thread is running on
1005 GROUP_AFFINITY groupAffinity;
1006 CPUGroupInfo::GetThreadGroupAffinity(GetCurrentThread(), &groupAffinity);
1007 m_initialGroup = groupAffinity.Group;
1009 // only enable CPU groups if more than one group exists
1010 BOOL hasMultipleGroups = m_nGroups > 1;
1011 m_enableGCCPUGroups = enableGCCPUGroups && hasMultipleGroups;
1012 m_threadUseAllCpuGroups = threadUseAllCpuGroups && hasMultipleGroups;
1013 #endif // _TARGET_AMD64_ || _TARGET_ARM64_
1015 // Determine if the process is affinitized to a single processor (or if the system has a single processor)
1016 DWORD_PTR processAffinityMask, systemAffinityMask;
1017 if (GetProcessAffinityMask(GetCurrentProcess(), &processAffinityMask, &systemAffinityMask))
1019 processAffinityMask &= systemAffinityMask;
1020 if (processAffinityMask != 0 && // only one CPU group is involved
1021 (processAffinityMask & (processAffinityMask - 1)) == 0) // only one bit is set
1023 s_hadSingleProcessorAtStartup = true;
1028 /*static*/ BOOL CPUGroupInfo::IsInitialized()
1030 LIMITED_METHOD_CONTRACT;
1031 return (m_initialization == -1);
1034 /*static*/ void CPUGroupInfo::EnsureInitialized()
1043 // CPUGroupInfo needs to be initialized only once. This could happen in three cases
1044 // 1. CLR initialization at begining of EEStartup, or
1045 // 2. Sometimes, when hosted by ASP.NET, the hosting process may initialize ThreadPool
1046 // before initializing CLR, thus require CPUGroupInfo to be initialized to determine
1047 // if CPU group support should/could be enabled.
1048 // 3. Call into Threadpool functions before Threadpool _and_ CLR is initialized.
1049 // Vast majority of time, CPUGroupInfo is initialized in case 1. or 2.
1050 // The chance of contention will be extremely small, so the following code should be fine
1053 if (IsInitialized())
1056 if (InterlockedCompareExchange(&m_initialization, 1, 0) == 0)
1059 m_initialization = -1;
1061 else //some other thread started initialization, just wait until complete;
1063 while (m_initialization != -1)
1071 /*static*/ WORD CPUGroupInfo::GetNumActiveProcessors()
1073 LIMITED_METHOD_CONTRACT;
1074 return (WORD)m_nProcessors;
1077 /*static*/ void CPUGroupInfo::GetGroupForProcessor(WORD processor_number,
1078 WORD* group_number, WORD* group_processor_number)
1080 LIMITED_METHOD_CONTRACT;
1082 #if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
1084 WORD bDiff = processor_number - bTemp;
1086 for (WORD i=0; i < m_nGroups; i++)
1088 bTemp += m_CPUGroupInfoArray[i].nr_active;
1089 if (bTemp > processor_number)
1092 *group_processor_number = bDiff;
1096 bDiff = processor_number - bTemp;
1100 *group_processor_number = 0;
1104 /*static*/ DWORD CPUGroupInfo::CalculateCurrentProcessorNumber()
1113 #if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
1114 // m_enableGCCPUGroups and m_threadUseAllCpuGroups must be TRUE
1115 _ASSERTE(m_enableGCCPUGroups && m_threadUseAllCpuGroups);
1117 PROCESSOR_NUMBER proc_no;
1121 ::GetCurrentProcessorNumberEx(&proc_no);
1123 DWORD fullNumber = 0;
1124 for (WORD i = 0; i < proc_no.Group; i++)
1125 fullNumber += (DWORD)m_CPUGroupInfoArray[i].nr_active;
1126 fullNumber += (DWORD)(proc_no.Number);
1134 // There can be different numbers of procs in groups. We take the max.
1135 /*static*/ bool CPUGroupInfo::GetCPUGroupInfo(PUSHORT total_groups, DWORD* max_procs_per_group)
1137 if (m_enableGCCPUGroups)
1139 *total_groups = m_nGroups;
1140 DWORD currentProcsInGroup = 0;
1141 for (WORD i = 0; i < m_nGroups; i++)
1143 currentProcsInGroup = max(currentProcsInGroup, m_CPUGroupInfoArray[i].nr_active);
1145 *max_procs_per_group = currentProcsInGroup;
1152 #if !defined(FEATURE_REDHAWK)
1153 //Lock ThreadStore before calling this function, so that updates of weights/counts are consistent
1154 /*static*/ void CPUGroupInfo::ChooseCPUGroupAffinity(GROUP_AFFINITY *gf)
1163 #if (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
1164 WORD i, minGroup = 0;
1165 DWORD minWeight = 0;
1167 // m_enableGCCPUGroups and m_threadUseAllCpuGroups must be TRUE
1168 _ASSERTE(m_enableGCCPUGroups && m_threadUseAllCpuGroups);
1170 for (i = 0; i < m_nGroups; i++)
1172 minGroup = (m_initialGroup + i) % m_nGroups;
1174 // the group is not filled up, use it
1175 if (m_CPUGroupInfoArray[minGroup].activeThreadWeight / m_CPUGroupInfoArray[minGroup].groupWeight
1176 < (DWORD)m_CPUGroupInfoArray[minGroup].nr_active)
1180 // all groups filled up, distribute proportionally
1181 minGroup = m_initialGroup;
1182 minWeight = m_CPUGroupInfoArray[m_initialGroup].activeThreadWeight;
1183 for (i = 0; i < m_nGroups; i++)
1185 if (m_CPUGroupInfoArray[i].activeThreadWeight < minWeight)
1188 minWeight = m_CPUGroupInfoArray[i].activeThreadWeight;
1193 gf->Group = minGroup;
1194 gf->Mask = m_CPUGroupInfoArray[minGroup].active_mask;
1195 gf->Reserved[0] = 0;
1196 gf->Reserved[1] = 0;
1197 gf->Reserved[2] = 0;
1198 m_CPUGroupInfoArray[minGroup].activeThreadWeight += m_CPUGroupInfoArray[minGroup].groupWeight;
1202 //Lock ThreadStore before calling this function, so that updates of weights/counts are consistent
1203 /*static*/ void CPUGroupInfo::ClearCPUGroupAffinity(GROUP_AFFINITY *gf)
1205 LIMITED_METHOD_CONTRACT;
1206 #if (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
1207 // m_enableGCCPUGroups and m_threadUseAllCpuGroups must be TRUE
1208 _ASSERTE(m_enableGCCPUGroups && m_threadUseAllCpuGroups);
1210 WORD group = gf->Group;
1211 m_CPUGroupInfoArray[group].activeThreadWeight -= m_CPUGroupInfoArray[group].groupWeight;
1215 BOOL CPUGroupInfo::GetCPUGroupRange(WORD group_number, WORD* group_begin, WORD* group_size)
1217 if (group_number >= m_nGroups)
1222 *group_begin = m_CPUGroupInfoArray[group_number].begin;
1223 *group_size = m_CPUGroupInfoArray[group_number].nr_active;
1230 /*static*/ BOOL CPUGroupInfo::CanEnableGCCPUGroups()
1232 LIMITED_METHOD_CONTRACT;
1233 return m_enableGCCPUGroups;
1236 /*static*/ BOOL CPUGroupInfo::CanEnableThreadUseAllCpuGroups()
1238 LIMITED_METHOD_CONTRACT;
1239 return m_threadUseAllCpuGroups;
1241 #endif // !FEATURE_PAL
1243 //******************************************************************************
1244 // Returns the number of processors that a process has been configured to run on
1245 //******************************************************************************
1246 int GetCurrentProcessCpuCount()
1255 static int cCPUs = 0;
1260 unsigned int count = 0;
1263 DWORD_PTR pmask, smask;
1265 if (!GetProcessAffinityMask(GetCurrentProcess(), &pmask, &smask))
1275 pmask &= (pmask - 1);
1279 // GetProcessAffinityMask can return pmask=0 and smask=0 on systems with more
1280 // than 64 processors, which would leave us with a count of 0. Since the GC
1281 // expects there to be at least one processor to run on (and thus at least one
1282 // heap), we'll return 64 here if count is 0, since there are likely a ton of
1283 // processors available in that case. The GC also cannot (currently) handle
1284 // the case where there are more than 64 processors, so we will return a
1285 // maximum of 64 here.
1286 if (count == 0 || count > 64)
1290 #else // !FEATURE_PAL
1291 count = PAL_GetLogicalCpuCountFromOS();
1294 if (PAL_GetCpuLimit(&cpuLimit) && cpuLimit < count)
1296 #endif // !FEATURE_PAL
1304 DWORD_PTR GetCurrentProcessCpuMask()
1314 DWORD_PTR pmask, smask;
1316 if (!GetProcessAffinityMask(GetCurrentProcess(), &pmask, &smask))
1325 #endif // !FEATURE_PAL
1327 uint32_t GetOsPageSizeUncached()
1329 SYSTEM_INFO sysInfo;
1330 ::GetSystemInfo(&sysInfo);
1331 return sysInfo.dwAllocationGranularity ? sysInfo.dwAllocationGranularity : 0x1000;
1336 Volatile<uint32_t> g_pageSize = 0;
1339 uint32_t GetOsPageSize()
1342 size_t result = g_pageSize.LoadWithoutBarrier();
1346 result = GetOsPageSizeUncached();
1348 g_pageSize.StoreWithoutBarrier(result);
1357 /**************************************************************************/
1359 /**************************************************************************/
1360 void ConfigMethodSet::init(const CLRConfig::ConfigStringInfo & info)
1368 // make sure that the memory was zero initialized
1369 _ASSERTE(m_inited == 0 || m_inited == 1);
1371 LPWSTR str = CLRConfig::GetConfigValue(info);
1380 /**************************************************************************/
1381 bool ConfigMethodSet::contains(LPCUTF8 methodName, LPCUTF8 className, PCCOR_SIGNATURE sig)
1389 _ASSERTE(m_inited == 1);
1391 if (m_list.IsEmpty())
1393 return(m_list.IsInList(methodName, className, sig));
1396 /**************************************************************************/
1397 bool ConfigMethodSet::contains(LPCUTF8 methodName, LPCUTF8 className, CORINFO_SIG_INFO* pSigInfo)
1405 _ASSERTE(m_inited == 1);
1407 if (m_list.IsEmpty())
1409 return(m_list.IsInList(methodName, className, pSigInfo));
1412 /**************************************************************************/
1413 void ConfigDWORD::init_DontUse_(__in_z LPCWSTR keyName, DWORD defaultVal)
1421 // make sure that the memory was zero initialized
1422 _ASSERTE(m_inited == 0 || m_inited == 1);
1424 m_value = REGUTIL::GetConfigDWORD_DontUse_(keyName, defaultVal);
1428 /**************************************************************************/
1429 void ConfigString::init(const CLRConfig::ConfigStringInfo & info)
1437 // make sure that the memory was zero initialized
1438 _ASSERTE(m_inited == 0 || m_inited == 1);
1440 // Note: m_value will be leaking
1441 m_value = CLRConfig::GetConfigValue(info);
1445 //=============================================================================
1446 // AssemblyNamesList
1447 //=============================================================================
1448 // The string should be of the form
1450 // MyAssembly;mscorlib;System
1451 // MyAssembly;mscorlib System
1453 AssemblyNamesList::AssemblyNamesList(__in LPWSTR list)
1459 WCHAR prevChar = '?'; // dummy
1460 LPWSTR nameStart = NULL; // start of the name currently being processed. NULL if no current name
1461 AssemblyName ** ppPrevLink = &m_pNames;
1463 for (LPWSTR listWalk = list; prevChar != '\0'; prevChar = *listWalk, listWalk++)
1465 WCHAR curChar = *listWalk;
1467 if (iswspace(curChar) || curChar == ';' || curChar == '\0' )
1470 // Found white-space
1475 // Found the end of the current name
1477 AssemblyName * newName = new AssemblyName();
1478 size_t nameLen = listWalk - nameStart;
1480 MAKE_UTF8PTR_FROMWIDE(temp, nameStart);
1481 newName->m_assemblyName = new char[nameLen + 1];
1482 memcpy(newName->m_assemblyName, temp, nameLen * sizeof(newName->m_assemblyName[0]));
1483 newName->m_assemblyName[nameLen] = '\0';
1485 *ppPrevLink = newName;
1486 ppPrevLink = &newName->m_next;
1491 else if (!nameStart)
1494 // Found the start of a new name
1497 nameStart = listWalk;
1501 _ASSERTE(!nameStart); // cannot be in the middle of a name
1505 AssemblyNamesList::~AssemblyNamesList()
1513 for (AssemblyName * pName = m_pNames; pName; /**/)
1515 AssemblyName * cur = pName;
1516 pName = pName->m_next;
1518 delete [] cur->m_assemblyName;
1523 bool AssemblyNamesList::IsInList(LPCUTF8 assemblyName)
1528 for (AssemblyName * pName = m_pNames; pName; pName = pName->m_next)
1530 if (_stricmp(pName->m_assemblyName, assemblyName) == 0)
1537 //=============================================================================
1539 //=============================================================================
1540 // str should be of the form :
1541 // "foo1 MyNamespace.MyClass:foo3 *:foo4 foo5(x,y,z)"
1542 // "MyClass:foo2 MyClass:*" will match under _DEBUG
1545 void MethodNamesListBase::Insert(__in_z LPWSTR str)
1551 enum State { NO_NAME, CLS_NAME, FUNC_NAME, ARG_LIST }; // parsing state machine
1553 const char SEP_CHAR = ' '; // current character use to separate each entry
1554 // const char SEP_CHAR = ';'; // better character use to separate each entry
1556 WCHAR lastChar = '?'; // dummy
1557 LPWSTR nameStart = NULL; // while walking over the classname or methodname, this points to start
1558 MethodName nameBuf; // Buffer used while parsing the current entry
1559 MethodName** lastName = &pNames; // last entry inserted into the list
1560 bool bQuote = false;
1562 nameBuf.methodName = NULL;
1563 nameBuf.className = NULL;
1564 nameBuf.numArgs = -1;
1565 nameBuf.next = NULL;
1567 for(State state = NO_NAME; lastChar != '\0'; str++)
1574 if (*str != SEP_CHAR)
1577 state = CLS_NAME; // we have found the start of the next entry
1582 if (*nameStart == '"')
1584 while (*str && *str!='"')
1594 if (*nameStart == '*' && !bQuote)
1596 // Is the classname string a wildcard. Then set it to NULL
1597 nameBuf.className = NULL;
1601 int len = (int)(str - nameStart);
1603 // Take off the quote
1604 if (bQuote) { len--; bQuote=false; }
1606 nameBuf.className = new char[len + 1];
1607 MAKE_UTF8PTR_FROMWIDE(temp, nameStart);
1608 memcpy(nameBuf.className, temp, len*sizeof(nameBuf.className[0]));
1609 nameBuf.className[len] = '\0';
1611 if (str[1] == ':') // Accept class::name syntax too
1613 nameStart = str + 1;
1616 else if (*str == '\0' || *str == SEP_CHAR || *str == '(')
1618 /* This was actually a method name without any class */
1619 nameBuf.className = NULL;
1620 goto DONE_FUNC_NAME;
1625 if (*nameStart == '"')
1627 while ( (nameStart==str) || // workaround to handle when className!=NULL
1628 (*str && *str!='"'))
1637 if (*str == '\0' || *str == SEP_CHAR || *str == '(')
1640 _ASSERTE(*str == '\0' || *str == SEP_CHAR || *str == '(');
1642 if (*nameStart == '*' && !bQuote)
1644 // Is the name string a wildcard. Then set it to NULL
1645 nameBuf.methodName = NULL;
1649 int len = (int)(str - nameStart);
1651 // Take off the quote
1652 if (bQuote) { len--; bQuote=false; }
1654 nameBuf.methodName = new char[len + 1];
1655 MAKE_UTF8PTR_FROMWIDE(temp, nameStart);
1656 memcpy(nameBuf.methodName, temp, len*sizeof(nameBuf.methodName[0]));
1657 nameBuf.methodName[len] = '\0';
1660 if (*str == '\0' || *str == SEP_CHAR)
1662 nameBuf.numArgs = -1;
1667 _ASSERTE(*str == '(');
1668 nameBuf.numArgs = -1;
1675 if (*str == '\0' || *str == ')')
1677 if (nameBuf.numArgs == -1)
1678 nameBuf.numArgs = 0;
1681 _ASSERTE(*str == '\0' || *str == SEP_CHAR || *str == ')');
1683 // We have parsed an entire method name.
1684 // Create a new entry in the list for it
1686 MethodName * newName = new MethodName();
1688 newName->next = NULL;
1689 *lastName = newName;
1690 lastName = &newName->next;
1693 // Skip anything after the argument list until we find the next
1694 // separator character, otherwise if we see "func(a,b):foo" we
1695 // create entries for "func(a,b)" as well as ":foo".
1698 while (*str && *str != SEP_CHAR)
1707 if (*str != SEP_CHAR && nameBuf.numArgs == -1)
1708 nameBuf.numArgs = 1;
1714 default: _ASSERTE(!"Bad state"); break;
1719 /**************************************************************/
1721 void MethodNamesListBase::Destroy()
1729 for(MethodName * pName = pNames; pName; /**/)
1731 if (pName->className)
1732 delete [] pName->className;
1733 if (pName->methodName)
1734 delete [] pName->methodName;
1736 MethodName * curName = pName;
1737 pName = pName->next;
1742 /**************************************************************/
1743 bool MethodNamesListBase::IsInList(LPCUTF8 methName, LPCUTF8 clsName, PCCOR_SIGNATURE sig)
1754 sig++; // Skip calling convention
1755 numArgs = CorSigUncompressData(sig);
1758 return IsInList(methName, clsName, numArgs);
1761 /**************************************************************/
1762 bool MethodNamesListBase::IsInList(LPCUTF8 methName, LPCUTF8 clsName, CORINFO_SIG_INFO* pSigInfo)
1771 if (pSigInfo != NULL)
1773 numArgs = pSigInfo->numArgs;
1776 return IsInList(methName, clsName, numArgs);
1779 /**************************************************************/
1780 bool MethodNamesListBase::IsInList(LPCUTF8 methName, LPCUTF8 clsName, int numArgs)
1788 // Try to match all the entries in the list
1790 for(MethodName * pName = pNames; pName; pName = pName->next)
1792 // If numArgs is valid, check for mismatch
1793 if (pName->numArgs != -1 && pName->numArgs != numArgs)
1796 // If methodName is valid, check for mismatch
1797 if (pName->methodName) {
1798 if (strcmp(pName->methodName, methName) != 0) {
1800 // C++ embeds the class name into the method name,
1801 // deal with that here (workaround)
1802 const char* ptr = strchr(methName, ':');
1803 if (ptr != 0 && ptr[1] == ':' && strcmp(&ptr[2], pName->methodName) == 0) {
1804 unsigned clsLen = (unsigned)(ptr - methName);
1805 if (pName->className == 0 || strncmp(pName->className, methName, clsLen) == 0)
1812 // check for class Name exact match
1813 if (clsName == 0 || pName->className == 0 || strcmp(pName->className, clsName) == 0)
1816 // check for suffix wildcard like System.*
1817 unsigned len = (unsigned)strlen(pName->className);
1818 if (len > 0 && pName->className[len-1] == '*' && strncmp(pName->className, clsName, len-1) == 0)
1822 // Maybe className doesnt include namespace. Try to match that
1823 LPCUTF8 onlyClass = ns::FindSep(clsName);
1824 if (onlyClass && strcmp(pName->className, onlyClass+1) == 0)
1831 //=============================================================================
1832 // Signature Validation Functions (scaled down version from MDValidator
1833 //=============================================================================
1835 //*****************************************************************************
1836 // This function validates one argument given an offset into the signature
1837 // where the argument begins. This function assumes that the signature is well
1838 // formed as far as the compression scheme is concerned.
1839 // <TODO>@todo: Validate tokens embedded.</TODO>
1840 //*****************************************************************************
1841 HRESULT validateOneArg(
1842 mdToken tk, // [IN] Token whose signature needs to be validated.
1844 ULONG *pulNSentinels, // [IN/OUT] Number of sentinels
1845 IMDInternalImport* pImport, // [IN] Internal MD Import interface ptr
1846 BOOL bNoVoidAllowed) // [IN] Flag indicating whether "void" is disallowed for this arg
1855 BYTE elementType; // Current element type being processed.
1856 mdToken token; // Embedded token.
1857 ULONG ulArgCnt; // Argument count for function pointer.
1858 ULONG ulIndex; // Index for type parameters
1859 ULONG ulRank; // Rank of the array.
1860 ULONG ulSizes; // Count of sized dimensions of the array.
1861 ULONG ulLbnds; // Count of lower bounds of the array.
1864 HRESULT hr = S_OK; // Value returned.
1865 BOOL bRepeat = TRUE; // MODOPT and MODREQ belong to the arg after them
1870 // Validate that the argument is not missing.
1872 // Get the element type.
1873 if (FAILED(pSig->GetByte(&elementType)))
1875 IfFailGo(VLDTR_E_SIG_MISSARG);
1878 // Walk past all the modifier types.
1879 while (elementType & ELEMENT_TYPE_MODIFIER)
1881 if (elementType == ELEMENT_TYPE_SENTINEL)
1883 if(pulNSentinels) *pulNSentinels+=1;
1884 if(TypeFromToken(tk) != mdtMemberRef) IfFailGo(VLDTR_E_SIG_SENTINMETHODDEF);
1886 if (FAILED(pSig->GetByte(&elementType)))
1888 IfFailGo(VLDTR_E_SIG_MISSELTYPE);
1892 switch (elementType)
1894 case ELEMENT_TYPE_VOID:
1895 if(bNoVoidAllowed) IfFailGo(VLDTR_E_SIG_BADVOID);
1897 case ELEMENT_TYPE_BOOLEAN:
1898 case ELEMENT_TYPE_CHAR:
1899 case ELEMENT_TYPE_I1:
1900 case ELEMENT_TYPE_U1:
1901 case ELEMENT_TYPE_I2:
1902 case ELEMENT_TYPE_U2:
1903 case ELEMENT_TYPE_I4:
1904 case ELEMENT_TYPE_U4:
1905 case ELEMENT_TYPE_I8:
1906 case ELEMENT_TYPE_U8:
1907 case ELEMENT_TYPE_R4:
1908 case ELEMENT_TYPE_R8:
1909 case ELEMENT_TYPE_STRING:
1910 case ELEMENT_TYPE_OBJECT:
1911 case ELEMENT_TYPE_TYPEDBYREF:
1912 case ELEMENT_TYPE_U:
1913 case ELEMENT_TYPE_I:
1915 case ELEMENT_TYPE_PTR:
1916 // Validate the referenced type.
1917 if(FAILED(hr = validateOneArg(tk, pSig, pulNSentinels, pImport, FALSE))) IfFailGo(hr);
1919 case ELEMENT_TYPE_BYREF: //fallthru
1920 if(TypeFromToken(tk)==mdtFieldDef) IfFailGo(VLDTR_E_SIG_BYREFINFIELD);
1921 case ELEMENT_TYPE_PINNED:
1922 case ELEMENT_TYPE_SZARRAY:
1923 // Validate the referenced type.
1924 if(FAILED(hr = validateOneArg(tk, pSig, pulNSentinels, pImport, TRUE))) IfFailGo(hr);
1926 case ELEMENT_TYPE_CMOD_OPT:
1927 case ELEMENT_TYPE_CMOD_REQD:
1928 bRepeat = TRUE; // go on validating, we're not done with this arg
1929 case ELEMENT_TYPE_VALUETYPE: //fallthru
1930 case ELEMENT_TYPE_CLASS:
1931 // See if the token is missing.
1932 if (FAILED(pSig->GetToken(&token)))
1934 IfFailGo(VLDTR_E_SIG_MISSTKN);
1936 // Token validation .
1939 ULONG rid = RidFromToken(token);
1940 ULONG typ = TypeFromToken(token);
1941 ULONG maxrid = pImport->GetCountWithTokenKind(typ);
1942 if(typ == mdtTypeDef) maxrid++;
1943 if((rid==0)||(rid > maxrid)) IfFailGo(VLDTR_E_SIG_TKNBAD);
1947 case ELEMENT_TYPE_FNPTR:
1948 // <TODO>@todo: More function pointer validation?</TODO>
1949 // Validate that calling convention is present.
1950 if (FAILED(pSig->GetCallingConvInfo(&ulCallConv)))
1952 IfFailGo(VLDTR_E_SIG_MISSFPTR);
1954 if(((ulCallConv & IMAGE_CEE_CS_CALLCONV_MASK) >= IMAGE_CEE_CS_CALLCONV_MAX)
1955 ||((ulCallConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS)
1956 &&(!(ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS)))) IfFailGo(VLDTR_E_MD_BADCALLINGCONV);
1958 // Validate that argument count is present.
1959 if (FAILED(pSig->GetData(&ulArgCnt)))
1961 IfFailGo(VLDTR_E_SIG_MISSFPTRARGCNT);
1964 // FNPTR signature must follow the rules of MethodDef
1965 // Validate and consume return type.
1966 IfFailGo(validateOneArg(mdtMethodDef, pSig, NULL, pImport, FALSE));
1968 // Validate and consume the arguments.
1971 IfFailGo(validateOneArg(mdtMethodDef, pSig, NULL, pImport, TRUE));
1975 case ELEMENT_TYPE_ARRAY:
1976 // Validate and consume the base type.
1977 IfFailGo(validateOneArg(tk, pSig, pulNSentinels, pImport, TRUE));
1979 // Validate that the rank is present.
1980 if (FAILED(pSig->GetData(&ulRank)))
1982 IfFailGo(VLDTR_E_SIG_MISSRANK);
1985 // Process the sizes.
1988 // Validate that the count of sized-dimensions is specified.
1989 if (FAILED(pSig->GetData(&ulSizes)))
1991 IfFailGo(VLDTR_E_SIG_MISSNSIZE);
1994 // Loop over the sizes.
1997 // Validate the current size.
1998 if (FAILED(pSig->GetData(NULL)))
2000 IfFailGo(VLDTR_E_SIG_MISSSIZE);
2004 // Validate that the count of lower bounds is specified.
2005 if (FAILED(pSig->GetData(&ulLbnds)))
2007 IfFailGo(VLDTR_E_SIG_MISSNLBND);
2010 // Loop over the lower bounds.
2013 // Validate the current lower bound.
2014 if (FAILED(pSig->GetData(NULL)))
2016 IfFailGo(VLDTR_E_SIG_MISSLBND);
2021 case ELEMENT_TYPE_VAR:
2022 case ELEMENT_TYPE_MVAR:
2023 // Validate that index is present.
2024 if (FAILED(pSig->GetData(&ulIndex)))
2026 IfFailGo(VLDTR_E_SIG_MISSFPTRARGCNT);
2029 //@todo GENERICS: check that index is in range
2032 case ELEMENT_TYPE_GENERICINST:
2033 // Validate the generic type.
2034 IfFailGo(validateOneArg(tk, pSig, pulNSentinels, pImport, TRUE));
2036 // Validate that parameter count is present.
2037 if (FAILED(pSig->GetData(&ulArgCnt)))
2039 IfFailGo(VLDTR_E_SIG_MISSFPTRARGCNT);
2042 //@todo GENERICS: check that number of parameters matches definition?
2044 // Validate and consume the parameters.
2047 IfFailGo(validateOneArg(tk, pSig, NULL, pImport, TRUE));
2051 case ELEMENT_TYPE_SENTINEL: // this case never works because all modifiers are skipped before switch
2052 if(TypeFromToken(tk) == mdtMethodDef) IfFailGo(VLDTR_E_SIG_SENTINMETHODDEF);
2056 IfFailGo(VLDTR_E_SIG_BADELTYPE);
2058 } // switch (ulElementType)
2059 } // end while(bRepeat)
2062 } // validateOneArg()
2064 //*****************************************************************************
2065 // This function validates the given Method/Field/Standalone signature.
2066 //@todo GENERICS: MethodInstantiation?
2067 //*****************************************************************************
2068 HRESULT validateTokenSig(
2069 mdToken tk, // [IN] Token whose signature needs to be validated.
2070 PCCOR_SIGNATURE pbSig, // [IN] Signature.
2071 ULONG cbSig, // [IN] Size in bytes of the signature.
2072 DWORD dwFlags, // [IN] Method flags.
2073 IMDInternalImport* pImport) // [IN] Internal MD Import interface ptr
2081 ULONG ulCallConv; // Calling convention.
2082 ULONG ulArgCount = 1; // Count of arguments (1 because of the return type)
2083 ULONG ulTyArgCount = 0; // Count of type arguments
2084 ULONG ulArgIx = 0; // Starting index of argument (standalone sig: 1)
2085 ULONG i; // Looping index.
2086 HRESULT hr = S_OK; // Value returned.
2087 ULONG ulNSentinels = 0;
2088 SigParser sig(pbSig, cbSig);
2090 _ASSERTE(TypeFromToken(tk) == mdtMethodDef ||
2091 TypeFromToken(tk) == mdtMemberRef ||
2092 TypeFromToken(tk) == mdtSignature ||
2093 TypeFromToken(tk) == mdtFieldDef);
2095 // Check for NULL signature.
2096 if (!pbSig || !cbSig) return VLDTR_E_SIGNULL;
2098 // Validate the calling convention.
2100 // Moves behind calling convention
2101 IfFailRet(sig.GetCallingConvInfo(&ulCallConv));
2102 i = ulCallConv & IMAGE_CEE_CS_CALLCONV_MASK;
2103 switch(TypeFromToken(tk))
2105 case mdtMethodDef: // MemberRefs have no flags available
2106 // If HASTHIS is set on the calling convention, the method should not be static.
2107 if ((ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS) &&
2108 IsMdStatic(dwFlags)) return VLDTR_E_MD_THISSTATIC;
2110 // If HASTHIS is not set on the calling convention, the method should be static.
2111 if (!(ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS) &&
2112 !IsMdStatic(dwFlags)) return VLDTR_E_MD_NOTTHISNOTSTATIC;
2113 // fall thru to callconv check;
2116 if(i == IMAGE_CEE_CS_CALLCONV_FIELD) return validateOneArg(tk, &sig, NULL, pImport, TRUE);
2118 // EXPLICITTHIS and native call convs are for stand-alone sigs only (for calli)
2119 if(((i != IMAGE_CEE_CS_CALLCONV_DEFAULT)&&( i != IMAGE_CEE_CS_CALLCONV_VARARG))
2120 || (ulCallConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS)) return VLDTR_E_MD_BADCALLINGCONV;
2124 if(i != IMAGE_CEE_CS_CALLCONV_LOCAL_SIG) // then it is function sig for calli
2126 if((i >= IMAGE_CEE_CS_CALLCONV_MAX)
2127 ||((ulCallConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS)
2128 &&(!(ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS)))) return VLDTR_E_MD_BADCALLINGCONV;
2131 ulArgIx = 1; // Local variable signatures don't have a return type
2135 if(i != IMAGE_CEE_CS_CALLCONV_FIELD) return VLDTR_E_MD_BADCALLINGCONV;
2136 return validateOneArg(tk, &sig, NULL, pImport, TRUE);
2138 // Is there any sig left for arguments?
2140 // Get the type argument count
2141 if (ulCallConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
2143 if (FAILED(sig.GetData(&ulTyArgCount)))
2145 return VLDTR_E_MD_NOARGCNT;
2149 // Get the argument count.
2150 if (FAILED(sig.GetData(&ulArgCount)))
2152 return VLDTR_E_MD_NOARGCNT;
2155 // Validate the return type and the arguments.
2156 // (at this moment ulArgCount = num.args+1, ulArgIx = (standalone sig. ? 1 :0); )
2157 for(; ulArgIx < ulArgCount; ulArgIx++)
2159 if(FAILED(hr = validateOneArg(tk, &sig, &ulNSentinels, pImport, (ulArgIx!=0)))) return hr;
2162 // <TODO>@todo: we allow junk to be at the end of the signature (we may not consume it all)
2163 // do we care?</TODO>
2165 if((ulNSentinels != 0) && ((ulCallConv & IMAGE_CEE_CS_CALLCONV_MASK) != IMAGE_CEE_CS_CALLCONV_VARARG ))
2166 return VLDTR_E_SIG_SENTMUSTVARARG;
2167 if(ulNSentinels > 1) return VLDTR_E_SIG_MULTSENTINELS;
2169 } // validateTokenSig()
2171 HRESULT GetImageRuntimeVersionString(PVOID pMetaData, LPCSTR* pString)
2180 STORAGESIGNATURE* pSig = (STORAGESIGNATURE*) pMetaData;
2182 // Verify the signature.
2184 // If signature didn't match, you shouldn't be here.
2185 if (pSig->GetSignature() != STORAGE_MAGIC_SIG)
2186 return CLDB_E_FILE_CORRUPT;
2188 // The version started in version 1.1
2189 if (pSig->GetMajorVer() < 1)
2190 return CLDB_E_FILE_OLDVER;
2192 if (pSig->GetMajorVer() == 1 && pSig->GetMinorVer() < 1)
2193 return CLDB_E_FILE_OLDVER;
2195 // Header data starts after signature.
2196 *pString = (LPCSTR) pSig->pVersion;
2200 //*****************************************************************************
2201 // Convert a UTF8 string to Unicode, into a CQuickArray<WCHAR>.
2202 //*****************************************************************************
2204 LPCUTF8 pStr, // The string to convert.
2205 CQuickArray<WCHAR> &rStr, // The QuickArray<WCHAR> to convert it into.
2206 int iCurLen) // Inital characters in the array to leave (default 0).
2214 HRESULT hr = S_OK; // A result.
2215 int iReqLen; // Required additional length.
2217 int bAlloc = 0; // If non-zero, allocation was required.
2221 _ASSERTE_MSG(false, "Invalid current length");
2222 return E_INVALIDARG;
2225 // Calculate the space available
2226 S_SIZE_T cchAvail = S_SIZE_T(rStr.MaxSize()) - S_SIZE_T(iCurLen);
2227 if (cchAvail.IsOverflow() || cchAvail.Value() > INT_MAX)
2229 _ASSERTE_MSG(false, "Integer overflow/underflow");
2230 return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
2233 // Attempt the conversion.
2234 LPWSTR rNewStr = rStr.Ptr()+iCurLen;
2235 if(rNewStr < rStr.Ptr())
2237 _ASSERTE_MSG(false, "Integer overflow/underflow");
2238 return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
2240 iReqLen = WszMultiByteToWideChar(CP_UTF8, 0, pStr, -1, rNewStr, (int)(cchAvail.Value()));
2242 // If the buffer was too small, determine what is required.
2244 bAlloc = iReqLen = WszMultiByteToWideChar(CP_UTF8, 0, pStr, -1, 0, 0);
2245 // Resize the buffer. If the buffer was large enough, this just sets the internal
2246 // counter, but if it was too small, this will attempt a reallocation. Note that
2247 // the length includes the terminating W('/0').
2248 IfFailGo(rStr.ReSizeNoThrow(iCurLen+iReqLen));
2249 // If we had to realloc, then do the conversion again, now that the buffer is
2252 //recalculating cchAvail since MaxSize could have been changed.
2253 cchAvail = S_SIZE_T(rStr.MaxSize()) - S_SIZE_T(iCurLen);
2254 if (cchAvail.IsOverflow() || cchAvail.Value() > INT_MAX)
2256 _ASSERTE_MSG(false, "Integer overflow/underflow");
2257 return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
2259 //reculculating rNewStr
2260 rNewStr = rStr.Ptr()+iCurLen;
2262 if(rNewStr < rStr.Ptr())
2264 _ASSERTE_MSG(false, "Integer overflow/underflow");
2265 return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
2267 iActLen = WszMultiByteToWideChar(CP_UTF8, 0, pStr, -1, rNewStr, (int)(cchAvail.Value()));
2268 _ASSERTE(iReqLen == iActLen);
2272 } // HRESULT Utf2Quick()
2275 //*****************************************************************************
2276 // Extract the movl 64-bit unsigned immediate from an IA64 bundle
2278 //*****************************************************************************
2279 UINT64 GetIA64Imm64(UINT64 * pBundle)
2281 WRAPPER_NO_CONTRACT;
2283 UINT64 temp0 = PTR_UINT64(pBundle)[0];
2284 UINT64 temp1 = PTR_UINT64(pBundle)[1];
2286 return GetIA64Imm64(temp0, temp1);
2289 UINT64 GetIA64Imm64(UINT64 qword0, UINT64 qword1)
2291 LIMITED_METHOD_CONTRACT;
2297 // make certain we're decoding a movl opcode, with template 4 or 5
2299 UINT64 templa = (qword0 >> 0) & 0x1f;
2300 UINT64 opcode = (qword1 >> 60) & 0xf;
2302 _ASSERTE((opcode == 0x6) && ((templa == 0x4) || (templa == 0x5)));
2305 imm64 = (qword1 >> 59) << 63; // 1 i
2306 imm64 |= (qword1 << 41) >> 1; // 23 high bits of imm41
2307 imm64 |= (qword0 >> 46) << 22; // 18 low bits of imm41
2308 imm64 |= (qword1 >> 23) & 0x200000; // 1 ic
2309 imm64 |= (qword1 >> 29) & 0x1F0000; // 5 imm5c
2310 imm64 |= (qword1 >> 43) & 0xFF80; // 9 imm9d
2311 imm64 |= (qword1 >> 36) & 0x7F; // 7 imm7b
2316 //*****************************************************************************
2317 // Deposit the movl 64-bit unsigned immediate into an IA64 bundle
2319 //*****************************************************************************
2320 void PutIA64Imm64(UINT64 * pBundle, UINT64 imm64)
2322 LIMITED_METHOD_CONTRACT;
2326 // make certain we're decoding a movl opcode, with template 4 or 5
2328 UINT64 templa = (pBundle[0] >> 0) & 0x1f;
2329 UINT64 opcode = (pBundle[1] >> 60) & 0xf ;
2331 _ASSERTE((opcode == 0x6) && ((templa == 0x4) || (templa == 0x5)));
2334 const UINT64 mask0 = UI64(0x00003FFFFFFFFFFF);
2335 const UINT64 mask1 = UI64(0xF000080FFF800000);
2337 /* Clear all bits used as part of the imm64 */
2338 pBundle[0] &= mask0;
2339 pBundle[1] &= mask1;
2344 temp1 = (imm64 >> 63) << 59; // 1 i
2345 temp1 |= (imm64 & 0xFF80) << 43; // 9 imm9d
2346 temp1 |= (imm64 & 0x1F0000) << 29; // 5 imm5c
2347 temp1 |= (imm64 & 0x200000) << 23; // 1 ic
2348 temp1 |= (imm64 & 0x7F) << 36; // 7 imm7b
2349 temp1 |= (imm64 << 1) >> 41; // 23 high bits of imm41
2350 temp0 = (imm64 >> 22) << 46; // 18 low bits of imm41
2352 /* Or in the new bits used in the imm64 */
2353 pBundle[0] |= temp0;
2354 pBundle[1] |= temp1;
2355 FlushInstructionCache(GetCurrentProcess(),pBundle,16);
2358 //*****************************************************************************
2359 // Extract the IP-Relative signed 25-bit immediate from an IA64 bundle
2360 // (Formats B1, B2 or B3)
2361 // Note that due to branch target alignment requirements
2362 // the lowest four bits in the result will always be zero.
2363 //*****************************************************************************
2364 INT32 GetIA64Rel25(UINT64 * pBundle, UINT32 slot)
2366 WRAPPER_NO_CONTRACT;
2368 UINT64 temp0 = PTR_UINT64(pBundle)[0];
2369 UINT64 temp1 = PTR_UINT64(pBundle)[1];
2371 return GetIA64Rel25(temp0, temp1, slot);
2374 INT32 GetIA64Rel25(UINT64 qword0, UINT64 qword1, UINT32 slot)
2376 LIMITED_METHOD_CONTRACT;
2382 if ((qword1 >> 59) & 1)
2384 imm25 |= (qword1 >> 32) & 0x00FFFFF0; // 20 imm20b
2388 if ((qword1 >> 18) & 1)
2390 imm25 |= (qword1 << 9) & 0x00FFFE00; // high 15 of imm20b
2391 imm25 |= (qword0 >> 55) & 0x000001F0; // low 5 of imm20b
2395 if ((qword0 >> 41) & 1)
2397 imm25 |= (qword0 >> 14) & 0x00FFFFF0; // 20 imm20b
2403 //*****************************************************************************
2404 // Deposit the IP-Relative signed 25-bit immediate into an IA64 bundle
2405 // (Formats B1, B2 or B3)
2406 // Note that due to branch target alignment requirements
2407 // the lowest four bits are required to be zero.
2408 //*****************************************************************************
2409 void PutIA64Rel25(UINT64 * pBundle, UINT32 slot, INT32 imm25)
2411 LIMITED_METHOD_CONTRACT;
2413 _ASSERTE((imm25 & 0xF) == 0);
2417 const UINT64 mask1 = UI64(0xF700000FFFFFFFFF);
2418 /* Clear all bits used as part of the imm25 */
2419 pBundle[1] &= mask1;
2423 temp1 = (UINT64) (imm25 & 0x1000000) << 35; // 1 s
2424 temp1 |= (UINT64) (imm25 & 0x0FFFFF0) << 32; // 20 imm20b
2426 /* Or in the new bits used in the imm64 */
2427 pBundle[1] |= temp1;
2431 const UINT64 mask0 = UI64(0x0EFFFFFFFFFFFFFF);
2432 const UINT64 mask1 = UI64(0xFFFFFFFFFFFB8000);
2433 /* Clear all bits used as part of the imm25 */
2434 pBundle[0] &= mask0;
2435 pBundle[1] &= mask1;
2440 temp1 = (UINT64) (imm25 & 0x1000000) >> 7; // 1 s
2441 temp1 |= (UINT64) (imm25 & 0x0FFFE00) >> 9; // high 15 of imm20b
2442 temp0 = (UINT64) (imm25 & 0x00001F0) << 55; // low 5 of imm20b
2444 /* Or in the new bits used in the imm64 */
2445 pBundle[0] |= temp0;
2446 pBundle[1] |= temp1;
2450 const UINT64 mask0 = UI64(0xFFFFFDC00003FFFF);
2451 /* Clear all bits used as part of the imm25 */
2452 pBundle[0] &= mask0;
2456 temp0 = (UINT64) (imm25 & 0x1000000) << 16; // 1 s
2457 temp0 |= (UINT64) (imm25 & 0x0FFFFF0) << 14; // 20 imm20b
2459 /* Or in the new bits used in the imm64 */
2460 pBundle[0] |= temp0;
2463 FlushInstructionCache(GetCurrentProcess(),pBundle,16);
2466 //*****************************************************************************
2467 // Extract the IP-Relative signed 64-bit immediate from an IA64 bundle
2468 // (Formats X3 or X4)
2469 //*****************************************************************************
2470 INT64 GetIA64Rel64(UINT64 * pBundle)
2472 WRAPPER_NO_CONTRACT;
2474 UINT64 temp0 = PTR_UINT64(pBundle)[0];
2475 UINT64 temp1 = PTR_UINT64(pBundle)[1];
2477 return GetIA64Rel64(temp0, temp1);
2480 INT64 GetIA64Rel64(UINT64 qword0, UINT64 qword1)
2482 LIMITED_METHOD_CONTRACT;
2488 // make certain we're decoding a brl opcode, with template 4 or 5
2490 UINT64 templa = (qword0 >> 0) & 0x1f;
2491 UINT64 opcode = (qword1 >> 60) & 0xf;
2493 _ASSERTE(((opcode == 0xC) || (opcode == 0xD)) &&
2494 ((templa == 0x4) || (templa == 0x5)));
2497 imm64 = (qword1 >> 59) << 63; // 1 i
2498 imm64 |= (qword1 << 41) >> 1; // 23 high bits of imm39
2499 imm64 |= (qword0 >> 48) << 24; // 16 low bits of imm39
2500 imm64 |= (qword1 >> 32) & 0xFFFFF0; // 20 imm20b
2505 //*****************************************************************************
2506 // Deposit the IP-Relative signed 64-bit immediate into an IA64 bundle
2507 // (Formats X3 or X4)
2508 //*****************************************************************************
2509 void PutIA64Rel64(UINT64 * pBundle, INT64 imm64)
2511 LIMITED_METHOD_CONTRACT;
2515 // make certain we're decoding a brl opcode, with template 4 or 5
2517 UINT64 templa = (pBundle[0] >> 0) & 0x1f;
2518 UINT64 opcode = (pBundle[1] >> 60) & 0xf;
2520 _ASSERTE(((opcode == 0xC) || (opcode == 0xD)) &&
2521 ((templa == 0x4) || (templa == 0x5)));
2522 _ASSERTE((imm64 & 0xF) == 0);
2525 const UINT64 mask0 = UI64(0x00003FFFFFFFFFFF);
2526 const UINT64 mask1 = UI64(0xF700000FFF800000);
2528 /* Clear all bits used as part of the imm64 */
2529 pBundle[0] &= mask0;
2530 pBundle[1] &= mask1;
2532 UINT64 temp0 = (imm64 & UI64(0x000000FFFF000000)) << 24; // 16 low bits of imm39
2533 UINT64 temp1 = (imm64 & UI64(0x8000000000000000)) >> 4 // 1 i
2534 | (imm64 & UI64(0x7FFFFF0000000000)) >> 40 // 23 high bits of imm39
2535 | (imm64 & UI64(0x0000000000FFFFF0)) << 32; // 20 imm20b
2537 /* Or in the new bits used in the imm64 */
2538 pBundle[0] |= temp0;
2539 pBundle[1] |= temp1;
2540 FlushInstructionCache(GetCurrentProcess(),pBundle,16);
2543 //*****************************************************************************
2544 // Extract the 16-bit immediate from ARM Thumb2 Instruction (format T2_N)
2545 //*****************************************************************************
2546 static FORCEINLINE UINT16 GetThumb2Imm16(UINT16 * p)
2548 LIMITED_METHOD_CONTRACT;
2550 return ((p[0] << 12) & 0xf000) |
2551 ((p[0] << 1) & 0x0800) |
2552 ((p[1] >> 4) & 0x0700) |
2553 ((p[1] >> 0) & 0x00ff);
2556 //*****************************************************************************
2557 // Extract the 32-bit immediate from movw/movt sequence
2558 //*****************************************************************************
2559 UINT32 GetThumb2Mov32(UINT16 * p)
2561 LIMITED_METHOD_CONTRACT;
2563 // Make sure we are decoding movw/movt sequence
2564 _ASSERTE_IMPL((*(p+0) & 0xFBF0) == 0xF240);
2565 _ASSERTE_IMPL((*(p+2) & 0xFBF0) == 0xF2C0);
2567 return (UINT32)GetThumb2Imm16(p) + ((UINT32)GetThumb2Imm16(p + 2) << 16);
2570 //*****************************************************************************
2571 // Deposit the 16-bit immediate into ARM Thumb2 Instruction (format T2_N)
2572 //*****************************************************************************
2573 static FORCEINLINE void PutThumb2Imm16(UINT16 * p, UINT16 imm16)
2575 LIMITED_METHOD_CONTRACT;
2577 USHORT Opcode0 = p[0];
2578 USHORT Opcode1 = p[1];
2579 Opcode0 &= ~((0xf000 >> 12) | (0x0800 >> 1));
2580 Opcode1 &= ~((0x0700 << 4) | (0x00ff << 0));
2581 Opcode0 |= (imm16 & 0xf000) >> 12;
2582 Opcode0 |= (imm16 & 0x0800) >> 1;
2583 Opcode1 |= (imm16 & 0x0700) << 4;
2584 Opcode1 |= (imm16 & 0x00ff) << 0;
2589 //*****************************************************************************
2590 // Deposit the 32-bit immediate into movw/movt Thumb2 sequence
2591 //*****************************************************************************
2592 void PutThumb2Mov32(UINT16 * p, UINT32 imm32)
2594 LIMITED_METHOD_CONTRACT;
2596 // Make sure we are decoding movw/movt sequence
2597 _ASSERTE_IMPL((*(p+0) & 0xFBF0) == 0xF240);
2598 _ASSERTE_IMPL((*(p+2) & 0xFBF0) == 0xF2C0);
2600 PutThumb2Imm16(p, (UINT16)imm32);
2601 PutThumb2Imm16(p + 2, (UINT16)(imm32 >> 16));
2604 //*****************************************************************************
2605 // Extract the 24-bit rel offset from bl instruction
2606 //*****************************************************************************
2607 INT32 GetThumb2BlRel24(UINT16 * p)
2609 LIMITED_METHOD_CONTRACT;
2611 USHORT Opcode0 = p[0];
2612 USHORT Opcode1 = p[1];
2614 UINT32 S = Opcode0 >> 10;
2615 UINT32 J2 = Opcode1 >> 11;
2616 UINT32 J1 = Opcode1 >> 13;
2619 ((S << 24) & 0x1000000) |
2620 (((J1 ^ S ^ 1) << 23) & 0x0800000) |
2621 (((J2 ^ S ^ 1) << 22) & 0x0400000) |
2622 ((Opcode0 << 12) & 0x03FF000) |
2623 ((Opcode1 << 1) & 0x0000FFE);
2625 // Sign-extend and return
2626 return (ret << 7) >> 7;
2629 //*****************************************************************************
2630 // Extract the 24-bit rel offset from bl instruction
2631 //*****************************************************************************
2632 void PutThumb2BlRel24(UINT16 * p, INT32 imm24)
2634 LIMITED_METHOD_CONTRACT;
2636 // Verify that we got a valid offset
2637 _ASSERTE(FitsInThumb2BlRel24(imm24));
2639 #if defined(_TARGET_ARM_)
2640 // Ensure that the ThumbBit is not set on the offset
2641 // as it cannot be encoded.
2642 _ASSERTE(!(imm24 & THUMB_CODE));
2643 #endif // _TARGET_ARM_
2645 USHORT Opcode0 = p[0];
2646 USHORT Opcode1 = p[1];
2650 UINT32 S = (imm24 & 0x1000000) >> 24;
2651 UINT32 J1 = ((imm24 & 0x0800000) >> 23) ^ S ^ 1;
2652 UINT32 J2 = ((imm24 & 0x0400000) >> 22) ^ S ^ 1;
2654 Opcode0 |= ((imm24 & 0x03FF000) >> 12) | (S << 10);
2655 Opcode1 |= ((imm24 & 0x0000FFE) >> 1) | (J1 << 13) | (J2 << 11);
2660 _ASSERTE(GetThumb2BlRel24(p) == imm24);
2663 //*****************************************************************************
2664 // Extract the PC-Relative offset from a b or bl instruction
2665 //*****************************************************************************
2666 INT32 GetArm64Rel28(UINT32 * pCode)
2668 LIMITED_METHOD_CONTRACT;
2670 UINT32 branchInstr = *pCode;
2672 // first shift 6 bits left to set the sign bit,
2673 // then arithmetic shift right by 4 bits
2674 INT32 imm28 = (((INT32)(branchInstr & 0x03FFFFFF)) << 6) >> 4;
2679 //*****************************************************************************
2680 // Extract the PC-Relative offset from an adrp instruction
2681 //*****************************************************************************
2682 INT32 GetArm64Rel21(UINT32 * pCode)
2684 LIMITED_METHOD_CONTRACT;
2686 UINT32 addInstr = *pCode;
2688 // 23-5 bits for the high part. Shift it by 5.
2689 INT32 immhi = (((INT32)(addInstr & 0xFFFFE0))) >> 5;
2690 // 30,29 bits for the lower part. Shift it by 29.
2691 INT32 immlo = ((INT32)(addInstr & 0x60000000)) >> 29;
2694 INT32 imm21 = (immhi << 2) | immlo;
2699 //*****************************************************************************
2700 // Extract the PC-Relative offset from an add instruction
2701 //*****************************************************************************
2702 INT32 GetArm64Rel12(UINT32 * pCode)
2704 LIMITED_METHOD_CONTRACT;
2706 UINT32 addInstr = *pCode;
2708 // 21-10 contains value. Mask 12 bits and shift by 10 bits.
2709 INT32 imm12 = (INT32)(addInstr & 0x003FFC00) >> 10;
2714 //*****************************************************************************
2715 // Deposit the PC-Relative offset 'imm28' into a b or bl instruction
2716 //*****************************************************************************
2717 void PutArm64Rel28(UINT32 * pCode, INT32 imm28)
2719 LIMITED_METHOD_CONTRACT;
2721 // Verify that we got a valid offset
2722 _ASSERTE(FitsInRel28(imm28));
2723 _ASSERTE((imm28 & 0x3) == 0); // the low two bits must be zero
2725 UINT32 branchInstr = *pCode;
2727 branchInstr &= 0xFC000000; // keep bits 31-26
2729 // Assemble the pc-relative delta 'imm28' into the branch instruction
2730 branchInstr |= ((imm28 >> 2) & 0x03FFFFFF);
2732 *pCode = branchInstr; // write the assembled instruction
2734 _ASSERTE(GetArm64Rel28(pCode) == imm28);
2737 //*****************************************************************************
2738 // Deposit the PC-Relative offset 'imm21' into an adrp instruction
2739 //*****************************************************************************
2740 void PutArm64Rel21(UINT32 * pCode, INT32 imm21)
2742 LIMITED_METHOD_CONTRACT;
2744 // Verify that we got a valid offset
2745 _ASSERTE(FitsInRel21(imm21));
2747 UINT32 adrpInstr = *pCode;
2748 // Check adrp opcode 1ii1 0000 ...
2749 _ASSERTE((adrpInstr & 0x9F000000) == 0x90000000);
2751 adrpInstr &= 0x9F00001F; // keep bits 31, 28-24, 4-0.
2752 INT32 immlo = imm21 & 0x03; // Extract low 2 bits which will occupy 30-29 bits.
2753 INT32 immhi = (imm21 & 0x1FFFFC) >> 2; // Extract high 19 bits which will occupy 23-5 bits.
2754 adrpInstr |= ((immlo << 29) | (immhi << 5));
2756 *pCode = adrpInstr; // write the assembled instruction
2758 _ASSERTE(GetArm64Rel21(pCode) == imm21);
2761 //*****************************************************************************
2762 // Deposit the PC-Relative offset 'imm12' into an add instruction
2763 //*****************************************************************************
2764 void PutArm64Rel12(UINT32 * pCode, INT32 imm12)
2766 LIMITED_METHOD_CONTRACT;
2768 // Verify that we got a valid offset
2769 _ASSERTE(FitsInRel12(imm12));
2771 UINT32 addInstr = *pCode;
2772 // Check add opcode 1001 0001 00...
2773 _ASSERTE((addInstr & 0xFFC00000) == 0x91000000);
2775 addInstr &= 0xFFC003FF; // keep bits 31-22, 9-0
2776 addInstr |= (imm12 << 10); // Occupy 21-10.
2778 *pCode = addInstr; // write the assembled instruction
2780 _ASSERTE(GetArm64Rel12(pCode) == imm12);
2783 //---------------------------------------------------------------------
2784 // Splits a command line into argc/argv lists, using the VC7 parsing rules.
2786 // This functions interface mimics the CommandLineToArgvW api.
2788 // If function fails, returns NULL.
2790 // If function suceeds, call delete [] on return pointer when done.
2792 //---------------------------------------------------------------------
2793 // NOTE: Implementation-wise, once every few years it would be a good idea to
2794 // compare this code with the C runtime library's parse_cmdline method,
2795 // which is in vctools\crt\crtw32\startup\stdargv.c. (Note we don't
2796 // support wild cards, and we use Unicode characters exclusively.)
2797 // We are up to date as of ~6/2005.
2798 //---------------------------------------------------------------------
2799 LPWSTR *SegmentCommandLine(LPCWSTR lpCmdLine, DWORD *pNumArgs)
2801 STATIC_CONTRACT_NOTHROW;
2802 STATIC_CONTRACT_GC_NOTRIGGER;
2803 STATIC_CONTRACT_FAULT;
2808 int nch = (int)wcslen(lpCmdLine);
2810 // Calculate the worstcase storage requirement. (One pointer for
2811 // each argument, plus storage for the arguments themselves.)
2812 int cbAlloc = (nch+1)*sizeof(LPWSTR) + sizeof(WCHAR)*(nch + 1);
2813 LPWSTR pAlloc = new (nothrow) WCHAR[cbAlloc / sizeof(WCHAR)];
2817 LPWSTR *argv = (LPWSTR*) pAlloc; // We store the argv pointers in the first halt
2818 LPWSTR pdst = (LPWSTR)( ((BYTE*)pAlloc) + sizeof(LPWSTR)*(nch+1) ); // A running pointer to second half to store arguments
2819 LPCWSTR psrc = lpCmdLine;
2825 // First, parse the program name (argv[0]). Argv[0] is parsed under
2826 // special rules. Anything up to the first whitespace outside a quoted
2827 // subtring is accepted. Backslashes are treated as normal characters.
2828 argv[ (*pNumArgs)++ ] = pdst;
2831 if (*psrc == W('"') )
2841 } while ( (c != W('\0') && (inquote || (c != W(' ') && c != W('\t')))) );
2843 if ( c == W('\0') ) {
2846 *(pdst-1) = W('\0');
2853 /* loop on each argument */
2858 while (*psrc == W(' ') || *psrc == W('\t'))
2864 if (*psrc == W('\0'))
2865 break; /* end of args */
2867 /* scan an argument */
2868 argv[ (*pNumArgs)++ ] = pdst;
2870 /* loop through scanning one argument */
2874 /* Rules: 2N backslashes + " ==> N backslashes and begin/end quote
2875 2N+1 backslashes + " ==> N backslashes + literal "
2876 N backslashes ==> N backslashes */
2878 while (*psrc == W('\\'))
2880 /* count number of backslashes for use below */
2884 if (*psrc == W('"'))
2886 /* if 2N backslashes before, start/end quote, otherwise
2888 if (numslash % 2 == 0)
2890 if (inquote && psrc[1] == W('"'))
2892 psrc++; /* Double quote inside quoted string */
2896 /* skip first quote char and copy second */
2897 copychar = 0; /* don't copy quote */
2901 numslash /= 2; /* divide numslash by two */
2910 /* if at end of arg, break loop */
2911 if (*psrc == W('\0') || (!inquote && (*psrc == W(' ') || *psrc == W('\t'))))
2914 /* copy character into argument */
2922 /* null-terminate the argument */
2924 *pdst++ = W('\0'); /* terminate string */
2927 /* We put one last argument in -- a null ptr */
2928 argv[ (*pNumArgs) ] = NULL;
2930 // If we hit this assert, we overwrote our destination buffer.
2931 // Since we're supposed to allocate for the worst
2932 // case, either the parsing rules have changed or our worse case
2933 // formula is wrong.
2934 _ASSERTE((BYTE*)pdst <= (BYTE*)pAlloc + cbAlloc);
2938 Volatile<PVOID> ForbidCallsIntoHostOnThisThread::s_pvOwningFiber = NULL;
2940 //======================================================================
2941 // This function returns true, if it can determine that the instruction pointer
2942 // refers to a code address that belongs in the range of the given image.
2943 // <TODO>@TODO: Merge with IsIPInModule from vm\util.hpp</TODO>
2945 BOOL IsIPInModule(HMODULE_TGT hModule, PCODE ip)
2947 STATIC_CONTRACT_LEAF;
2952 HMODULE_TGT hModule;
2956 param.hModule = hModule;
2960 // UNIXTODO: implement a proper version for PAL
2962 PAL_TRY(Param *, pParam, ¶m)
2964 PTR_BYTE pBase = dac_cast<PTR_BYTE>(pParam->hModule);
2966 PTR_IMAGE_DOS_HEADER pDOS = NULL;
2967 PTR_IMAGE_NT_HEADERS pNT = NULL;
2972 // First, must validate the format of the PE headers to make sure that
2973 // the fields we're interested in using exist in the image.
2976 // Validate the DOS header.
2977 pDOS = PTR_IMAGE_DOS_HEADER(pBase);
2978 if (pDOS->e_magic != VAL16(IMAGE_DOS_SIGNATURE) ||
2979 pDOS->e_lfanew == 0)
2984 // Validate the NT header
2985 pNT = PTR_IMAGE_NT_HEADERS(pBase + VAL32(pDOS->e_lfanew));
2987 if (pNT->Signature != VAL32(IMAGE_NT_SIGNATURE))
2992 // Validate that the optional header is large enough to contain the fields
2993 // we're interested, namely IMAGE_OPTIONAL_HEADER::SizeOfImage. The reason
2994 // we don't just check that SizeOfOptionalHeader == IMAGE_SIZEOF_NT_OPTIONAL_HEADER
2995 // is due to VSW443590, which states that the extensibility of this structure
2996 // is such that it is possible to include only a portion of the optional header.
2997 cbOptHdr = pNT->FileHeader.SizeOfOptionalHeader;
2999 // Check that the magic field is contained by the optional header and set to the correct value.
3000 if (cbOptHdr < (offsetof(IMAGE_OPTIONAL_HEADER, Magic) + sizeofmember(IMAGE_OPTIONAL_HEADER, Magic)) ||
3001 pNT->OptionalHeader.Magic != VAL16(IMAGE_NT_OPTIONAL_HDR_MAGIC))
3006 // Check that the SizeOfImage is contained by the optional header.
3007 if (cbOptHdr < (offsetof(IMAGE_OPTIONAL_HEADER, SizeOfImage) + sizeofmember(IMAGE_OPTIONAL_HEADER, SizeOfImage)))
3016 baseAddr = dac_cast<PCODE>(pBase);
3017 if ((pParam->ip < baseAddr) || (pParam->ip >= (baseAddr + VAL32(pNT->OptionalHeader.SizeOfImage))))
3022 pParam->fRet = TRUE;
3026 PAL_EXCEPT (EXCEPTION_EXECUTE_HANDLER)
3030 #endif // !FEATURE_PAL
3035 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
3037 // To include definition of EXCEPTION_SOFTSO
3038 #include "corexcep.h"
3040 // These functions provide limited support for corrupting exceptions
3041 // outside the VM folder. Its limited since we don't have access to the
3044 // These functions are also wrapped by the corresponding CEHelper
3045 // methods in excep.cpp.
3047 // Given an exception code, this method returns a BOOL to indicate if the
3048 // code belongs to a corrupting exception or not.
3049 BOOL IsProcessCorruptedStateException(DWORD dwExceptionCode, BOOL fCheckForSO /*=TRUE*/)
3051 LIMITED_METHOD_CONTRACT;
3053 // By default, assume its not corrupting
3054 BOOL fIsCorruptedStateException = FALSE;
3056 if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_legacyCorruptedStateExceptionsPolicy) == 1)
3058 return fIsCorruptedStateException;
3061 // If we have been asked not to include SO in the CSE check
3062 // and the code represent SO, then exit now.
3063 if ((fCheckForSO == FALSE) && (dwExceptionCode == STATUS_STACK_OVERFLOW))
3065 return fIsCorruptedStateException;
3068 switch(dwExceptionCode)
3070 case STATUS_ACCESS_VIOLATION:
3071 case STATUS_STACK_OVERFLOW:
3072 case EXCEPTION_ILLEGAL_INSTRUCTION:
3073 case EXCEPTION_IN_PAGE_ERROR:
3074 case EXCEPTION_INVALID_DISPOSITION:
3075 case EXCEPTION_NONCONTINUABLE_EXCEPTION:
3076 case EXCEPTION_PRIV_INSTRUCTION:
3077 case STATUS_UNWIND_CONSOLIDATE:
3078 fIsCorruptedStateException = TRUE;
3082 return fIsCorruptedStateException;
3085 #endif // FEATURE_CORRUPTING_EXCEPTIONS
3087 void EnableTerminationOnHeapCorruption()
3089 HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
3096 static BOOL g_fLocalAppDataDirectoryInitted = FALSE;
3097 static WCHAR *g_wszLocalAppDataDirectory = NULL;
3099 // This api returns a pointer to a null-terminated string that contains the local appdata directory
3100 // or it returns NULL in the case that the directory could not be found. The return value from this function
3101 // is not actually checked for existence.
3102 HRESULT GetLocalAppDataDirectory(LPCWSTR *ppwzLocalAppDataDirectory)
3110 *ppwzLocalAppDataDirectory = NULL;
3114 if (!g_fLocalAppDataDirectoryInitted)
3116 WCHAR *wszLocalAppData = NULL;
3119 cCharsNeeded = GetEnvironmentVariableW(W("LOCALAPPDATA"), NULL, 0);
3121 if ((cCharsNeeded != 0) && (cCharsNeeded < MAX_LONGPATH))
3123 wszLocalAppData = new WCHAR[cCharsNeeded];
3124 cCharsNeeded = GetEnvironmentVariableW(W("LOCALAPPDATA"), wszLocalAppData, cCharsNeeded);
3125 if (cCharsNeeded != 0)
3127 // We've collected the appropriate app data directory into a local. Now publish it.
3128 if (InterlockedCompareExchangeT(&g_wszLocalAppDataDirectory, wszLocalAppData, NULL) == NULL)
3130 // This variable doesn't need to be freed, as it has been stored in the global
3131 wszLocalAppData = NULL;
3136 g_fLocalAppDataDirectoryInitted = TRUE;
3137 delete[] wszLocalAppData;
3140 EX_CATCH_HRESULT(hr);
3143 *ppwzLocalAppDataDirectory = g_wszLocalAppDataDirectory;
3148 HRESULT SetLocalAppDataDirectory(LPCWSTR pwzLocalAppDataDirectory)
3155 if (pwzLocalAppDataDirectory == NULL || *pwzLocalAppDataDirectory == W('\0'))
3156 return E_INVALIDARG;
3158 if (g_fLocalAppDataDirectoryInitted)
3159 return E_UNEXPECTED;
3165 size_t size = wcslen(pwzLocalAppDataDirectory) + 1;
3166 WCHAR *wszLocalAppData = new WCHAR[size];
3167 wcscpy_s(wszLocalAppData, size, pwzLocalAppDataDirectory);
3169 // We've collected the appropriate app data directory into a local. Now publish it.
3170 if (InterlockedCompareExchangeT(&g_wszLocalAppDataDirectory, wszLocalAppData, NULL) != NULL)
3172 // Someone else already set LocalAppData. Free our copy and return an error.
3173 delete[] wszLocalAppData;
3177 g_fLocalAppDataDirectoryInitted = TRUE;
3179 EX_CATCH_HRESULT(hr);
3185 // Struct used to scope suspension of client impersonation for the current thread.
3186 // https://docs.microsoft.com/en-us/windows/desktop/secauthz/client-impersonation
3187 class SuspendImpersonation
3190 SuspendImpersonation()
3193 // The approach used here matches what is used elsewhere in CLR (RevertIfImpersonated).
3194 // In general, OpenThreadToken fails with ERROR_NO_TOKEN if impersonation is not active,
3195 // fails with ERROR_CANT_OPEN_ANONYMOUS if anonymous impersonation is active, and otherwise
3196 // succeeds and returns the active impersonation token.
3197 BOOL res = ::OpenThreadToken(::GetCurrentThread(), TOKEN_IMPERSONATE, /* OpenAsSelf */ TRUE, &_token);
3208 ~SuspendImpersonation()
3210 if (_token != nullptr)
3211 ::SetThreadToken(nullptr, _token);
3215 HandleHolder _token;
3218 struct ProcessIntegrityResult
3224 HRESULT RecordAndReturnError(HRESULT hr)
3231 // The system calls in this code can fail if run with reduced privileges.
3232 // It is the caller's responsibility to choose an appropriate default in the event
3233 // that this function fails to retrieve the current process integrity.
3234 HRESULT GetCurrentProcessIntegrity(DWORD *integrity)
3236 static ProcessIntegrityResult s_Result = { FALSE, 0, S_FALSE };
3238 if (FALSE != InterlockedCompareExchangeT(&s_Result.Success, FALSE, FALSE))
3240 *integrity = s_Result.Integrity;
3244 // Temporarily suspend impersonation (if possible) while computing the integrity level.
3245 // If impersonation is active, the OpenProcessToken call below will check the impersonation
3246 // token against the process token ACL, and will generally fail with ERROR_ACCESS_DENIED if
3247 // the impersonation token is less privileged than this process's primary token.
3248 Clr::Util::SuspendImpersonation si;
3250 HandleHolder hToken;
3251 if(!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken))
3252 return s_Result.RecordAndReturnError(HRESULT_FROM_GetLastError());
3255 DWORD err = ERROR_SUCCESS;
3256 if(!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)TokenIntegrityLevel, nullptr, 0, &dwSize))
3257 err = GetLastError();
3259 // We need to make sure that GetTokenInformation failed in a predictable manner so we know that
3260 // dwSize has the correct buffer size in it.
3261 if (err != ERROR_INSUFFICIENT_BUFFER || dwSize == 0)
3262 return s_Result.RecordAndReturnError((err == ERROR_SUCCESS) ? E_FAIL : HRESULT_FROM_WIN32(err));
3264 NewArrayHolder<BYTE> pLabel = new (nothrow) BYTE[dwSize];
3266 return s_Result.RecordAndReturnError(E_OUTOFMEMORY);
3268 if(!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)TokenIntegrityLevel, pLabel, dwSize, &dwSize))
3269 return s_Result.RecordAndReturnError(HRESULT_FROM_GetLastError());
3271 TOKEN_MANDATORY_LABEL *ptml = (TOKEN_MANDATORY_LABEL *)(void*)pLabel;
3272 PSID psidIntegrityLevelLabel = ptml->Label.Sid;
3274 s_Result.Integrity = *GetSidSubAuthority(psidIntegrityLevelLabel, (*GetSidSubAuthorityCount(psidIntegrityLevelLabel) - 1));
3275 *integrity = s_Result.Integrity;
3276 InterlockedExchangeT(&s_Result.Success, TRUE);
3282 HRESULT ReadStringValue(HKEY hKey, LPCWSTR wszSubKeyName, LPCWSTR wszValueName, SString & ssValue)
3284 STANDARD_VM_CONTRACT;
3288 return E_INVALIDARG;
3291 RegKeyHolder hTargetKey;
3292 if (wszSubKeyName == NULL || *wszSubKeyName == W('\0'))
3293 { // No subkey was requested, use hKey as the resolved key.
3295 hTargetKey.SuppressRelease();
3298 { // Try to open the specified subkey.
3299 if (WszRegOpenKeyEx(hKey, wszSubKeyName, 0, KEY_READ, &hTargetKey) != ERROR_SUCCESS)
3300 return REGDB_E_CLASSNOTREG;
3305 if ((WszRegQueryValueEx(hTargetKey, wszValueName, 0, &type, 0, &size) == ERROR_SUCCESS) &&
3306 type == REG_SZ && size > 0)
3308 LPWSTR wszValueBuf = ssValue.OpenUnicodeBuffer(static_cast<COUNT_T>((size / sizeof(WCHAR)) - 1));
3309 LONG lResult = WszRegQueryValueEx(
3314 reinterpret_cast<LPBYTE>(wszValueBuf),
3317 _ASSERTE(lResult == ERROR_SUCCESS);
3318 if (lResult == ERROR_SUCCESS)
3320 // Can't count on the returned size being accurate - I've seen at least
3321 // one string with an extra NULL at the end that will cause the resulting
3322 // SString to count the extra NULL as part of the string. An extra
3323 // terminating NULL is not a legitimate scenario for REG_SZ - this must
3324 // be done using REG_MULTI_SZ - however this was tolerated in the
3325 // past and so it would be a breaking change to stop doing so.
3326 _ASSERTE(wcslen(wszValueBuf) <= (size / sizeof(WCHAR)) - 1);
3327 ssValue.CloseBuffer((COUNT_T)wcsnlen(wszValueBuf, (size_t)size));
3331 ssValue.CloseBuffer(0);
3332 return HRESULT_FROM_WIN32(lResult);
3339 return REGDB_E_KEYMISSING;
3343 HRESULT ReadStringValue(HKEY hKey, LPCWSTR wszSubKey, LPCWSTR wszName, __deref_out __deref_out_z LPWSTR* pwszValue)
3353 StackSString ssValue;
3354 if (SUCCEEDED(hr = ReadStringValue(hKey, wszSubKey, wszName, ssValue)))
3356 *pwszValue = new WCHAR[ssValue.GetCount() + 1];
3357 wcscpy_s(*pwszValue, ssValue.GetCount() + 1, ssValue.GetUnicode());
3360 EX_CATCH_HRESULT(hr);
3369 __success(return == S_OK)
3371 HRESULT FindSubKeyDefaultValueForCLSID(REFCLSID rclsid, LPCWSTR wszSubKeyName, SString & ssValue)
3373 STANDARD_VM_CONTRACT;
3376 if (GuidToLPWSTR(rclsid, wszClsid, NumItems(wszClsid)) == 0)
3377 return E_UNEXPECTED;
3379 StackSString ssKeyName;
3380 ssKeyName.Append(SL(W("CLSID\\")));
3381 ssKeyName.Append(wszClsid);
3382 ssKeyName.Append(SL(W("\\")));
3383 ssKeyName.Append(wszSubKeyName);
3385 // Query HKCR first to retain backwards compat with previous implementation where HKCR was only queried.
3386 // This is being done due to registry caching. This value will be used if the process integrity is medium or less.
3387 HRESULT hkcrResult = Clr::Util::Reg::ReadStringValue(HKEY_CLASSES_ROOT, ssKeyName.GetUnicode(), nullptr, ssValue);
3389 // HKCR is a virtualized registry hive that weaves together HKCU\Software\Classes and HKLM\Software\Classes
3390 // Processes with high integrity or greater should only read from HKLM to avoid being hijacked by medium
3391 // integrity processes writing to HKCU.
3392 DWORD integrity = SECURITY_MANDATORY_PROTECTED_PROCESS_RID;
3393 HRESULT hr = Clr::Util::GetCurrentProcessIntegrity(&integrity);
3396 // In the event that we are unable to get the current process integrity,
3397 // we assume that this process is running in an elevated state.
3398 // GetCurrentProcessIntegrity may fail if the process has insufficient rights to get the integrity level
3399 integrity = SECURITY_MANDATORY_PROTECTED_PROCESS_RID;
3402 if (integrity > SECURITY_MANDATORY_MEDIUM_RID)
3404 Clr::Util::SuspendImpersonation si;
3406 // Clear the previous HKCR queried value
3409 // Force to use HKLM
3410 StackSString ssHklmKeyName(SL(W("SOFTWARE\\Classes\\")));
3411 ssHklmKeyName.Append(ssKeyName);
3412 return Clr::Util::Reg::ReadStringValue(HKEY_LOCAL_MACHINE, ssHklmKeyName.GetUnicode(), nullptr, ssValue);
3419 HRESULT FindInprocServer32UsingCLSID(REFCLSID rclsid, SString & ssInprocServer32Name)
3421 WRAPPER_NO_CONTRACT;
3422 return __imp::FindSubKeyDefaultValueForCLSID(rclsid, W("InprocServer32"), ssInprocServer32Name);
3425 #endif // FEATURE_PAL
3429 void GetModuleFileName(
3431 SString & ssFileName,
3432 bool fAllowLongFileNames)
3434 STANDARD_VM_CONTRACT;
3436 // Try to use what the SString already has allocated. If it does not have anything allocated
3437 // or it has < 20 characters allocated, then bump the size requested to _MAX_PATH.
3439 DWORD dwResult = WszGetModuleFileName(hModule, ssFileName);
3443 ThrowHR(HRESULT_FROM_GetLastError());
3445 _ASSERTE(dwResult != 0 );
3448 // Returns heap-allocated string in *pwszFileName
3449 HRESULT GetModuleFileName(
3451 __deref_out_z LPWSTR * pwszFileName,
3452 bool fAllowLongFileNames)
3457 PRECONDITION(CheckPointer(pwszFileName));
3463 InlineSString<_MAX_PATH> ssFileName;
3464 GetModuleFileName(hModule, ssFileName);
3465 *pwszFileName = DuplicateStringThrowing(ssFileName.GetUnicode());
3467 EX_CATCH_HRESULT(hr);
3472 void GetFullPathName(
3473 SString const & ssFileName,
3474 SString & ssPathName,
3475 DWORD * pdwFilePartIdx,
3476 bool fAllowLongFileNames)
3478 STANDARD_VM_CONTRACT;
3480 // Get the required buffer length (including terminating NULL).
3481 DWORD dwLengthRequired = WszGetFullPathName(ssFileName.GetUnicode(), 0, NULL, NULL);
3483 if (dwLengthRequired == 0)
3484 ThrowHR(HRESULT_FROM_GetLastError());
3486 LPWSTR wszPathName = ssPathName.OpenUnicodeBuffer(dwLengthRequired - 1);
3487 LPWSTR wszFileName = NULL;
3488 DWORD dwLengthWritten = WszGetFullPathName(
3489 ssFileName.GetUnicode(),
3494 // Calculate the index while the buffer is open and the string pointer is stable.
3495 if (dwLengthWritten != 0 && dwLengthWritten < dwLengthRequired && pdwFilePartIdx != NULL)
3496 *pdwFilePartIdx = static_cast<DWORD>(wszFileName - wszPathName);
3498 ssPathName.CloseBuffer(dwLengthWritten < dwLengthRequired ? dwLengthWritten : 0);
3500 if (dwLengthRequired == 0)
3501 ThrowHR(HRESULT_FROM_GetLastError());
3503 // Overly defensive? Perhaps.
3504 if (!(dwLengthWritten < dwLengthRequired))
3505 ThrowHR(E_UNEXPECTED);
3507 } // namespace Win32