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);
736 /*static*/ BOOL NumaNodeInfo::GetNumaProcessorNodeEx(PPROCESSOR_NUMBER proc_no, PUSHORT node_no)
738 return ::GetNumaProcessorNodeEx(proc_no, node_no);
742 /*static*/ BOOL NumaNodeInfo::m_enableGCNumaAware = FALSE;
743 /*static*/ BOOL NumaNodeInfo::InitNumaNodeInfoAPI()
745 #if !defined(FEATURE_REDHAWK)
746 //check for numa support if multiple heaps are used
749 if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_GCNumaAware) == 0)
753 // check if required APIs are supported
754 HMODULE hMod = GetModuleHandleW(WINDOWS_KERNEL32_DLLNAME_W);
756 HMODULE hMod = GetCLRModule();
761 // fail to get the highest numa node number
762 if (!::GetNumaHighestNodeNumber(&highest) || (highest == 0))
771 /*static*/ BOOL NumaNodeInfo::CanEnableGCNumaAware()
773 return m_enableGCNumaAware;
776 /*static*/ void NumaNodeInfo::InitNumaNodeInfo()
778 m_enableGCNumaAware = InitNumaNodeInfoAPI();
781 //******************************************************************************
783 //******************************************************************************
784 #if !defined(FEATURE_REDHAWK)
785 /*static*/ //CPUGroupInfo::PNTQSIEx CPUGroupInfo::m_pNtQuerySystemInformationEx = NULL;
787 /*static*/ BOOL CPUGroupInfo::GetLogicalProcessorInformationEx(LOGICAL_PROCESSOR_RELATIONSHIP relationship,
788 SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *slpiex, PDWORD count)
790 LIMITED_METHOD_CONTRACT;
791 return ::GetLogicalProcessorInformationEx(relationship, slpiex, count);
794 /*static*/ BOOL CPUGroupInfo::SetThreadGroupAffinity(HANDLE h,
795 GROUP_AFFINITY *groupAffinity, GROUP_AFFINITY *previousGroupAffinity)
797 LIMITED_METHOD_CONTRACT;
798 return ::SetThreadGroupAffinity(h, groupAffinity, previousGroupAffinity);
801 /*static*/ BOOL CPUGroupInfo::GetThreadGroupAffinity(HANDLE h, GROUP_AFFINITY *groupAffinity)
803 LIMITED_METHOD_CONTRACT;
804 return ::GetThreadGroupAffinity(h, groupAffinity);
807 /*static*/ BOOL CPUGroupInfo::GetSystemTimes(FILETIME *idleTime, FILETIME *kernelTime, FILETIME *userTime)
809 LIMITED_METHOD_CONTRACT;
812 return ::GetSystemTimes(idleTime, kernelTime, userTime);
819 /*static*/ BOOL CPUGroupInfo::m_enableGCCPUGroups = FALSE;
820 /*static*/ BOOL CPUGroupInfo::m_threadUseAllCpuGroups = FALSE;
821 /*static*/ WORD CPUGroupInfo::m_nGroups = 0;
822 /*static*/ WORD CPUGroupInfo::m_nProcessors = 0;
823 /*static*/ WORD CPUGroupInfo::m_initialGroup = 0;
824 /*static*/ CPU_Group_Info *CPUGroupInfo::m_CPUGroupInfoArray = NULL;
825 /*static*/ LONG CPUGroupInfo::m_initialization = 0;
826 /*static*/ bool CPUGroupInfo::s_hadSingleProcessorAtStartup = false;
828 #if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
829 // Calculate greatest common divisor
830 DWORD GCD(DWORD u, DWORD v)
842 // Calculate least common multiple
843 DWORD LCM(DWORD u, DWORD v)
845 return u / GCD(u, v) * v;
849 /*static*/ BOOL CPUGroupInfo::InitCPUGroupInfoArray()
858 #if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
859 BYTE *bBuffer = NULL;
860 SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pSLPIEx = NULL;
861 SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pRecord = NULL;
863 DWORD byteOffset = 0;
864 DWORD dwNumElements = 0;
867 if (CPUGroupInfo::GetLogicalProcessorInformationEx(RelationGroup, pSLPIEx, &cbSLPIEx) &&
868 GetLastError() != ERROR_INSUFFICIENT_BUFFER)
873 // Fail to allocate buffer
874 bBuffer = new (nothrow) BYTE[ cbSLPIEx ];
878 pSLPIEx = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)bBuffer;
879 if (!::GetLogicalProcessorInformationEx(RelationGroup, pSLPIEx, &cbSLPIEx))
886 while (byteOffset < cbSLPIEx)
888 if (pRecord->Relationship == RelationGroup)
890 m_nGroups = pRecord->Group.ActiveGroupCount;
893 byteOffset += pRecord->Size;
894 pRecord = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)(bBuffer + byteOffset);
897 m_CPUGroupInfoArray = new (nothrow) CPU_Group_Info[m_nGroups];
898 if (m_CPUGroupInfoArray == NULL)
904 for (DWORD i = 0; i < m_nGroups; i++)
906 m_CPUGroupInfoArray[i].nr_active = (WORD)pRecord->Group.GroupInfo[i].ActiveProcessorCount;
907 m_CPUGroupInfoArray[i].active_mask = pRecord->Group.GroupInfo[i].ActiveProcessorMask;
908 m_nProcessors += m_CPUGroupInfoArray[i].nr_active;
909 dwWeight = LCM(dwWeight, (DWORD)m_CPUGroupInfoArray[i].nr_active);
912 // The number of threads per group that can be supported will depend on the number of CPU groups
913 // and the number of LPs within each processor group. For example, when the number of LPs in
914 // CPU groups is the same and is 64, the number of threads per group before weight overflow
915 // would be 2^32/2^6 = 2^26 (64M threads)
916 for (DWORD i = 0; i < m_nGroups; i++)
918 m_CPUGroupInfoArray[i].groupWeight = dwWeight / (DWORD)m_CPUGroupInfoArray[i].nr_active;
919 m_CPUGroupInfoArray[i].activeThreadWeight = 0;
922 delete[] bBuffer; // done with it; free it
929 /*static*/ BOOL CPUGroupInfo::InitCPUGroupInfoRange()
931 LIMITED_METHOD_CONTRACT;
933 #if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
937 for (WORD i = 0; i < m_nGroups; i++)
939 nr_proc += m_CPUGroupInfoArray[i].nr_active;
940 m_CPUGroupInfoArray[i].begin = begin;
941 m_CPUGroupInfoArray[i].end = nr_proc - 1;
950 /*static*/ void CPUGroupInfo::InitCPUGroupInfo()
959 #if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
960 BOOL enableGCCPUGroups = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_GCCpuGroup) != 0;
961 BOOL threadUseAllCpuGroups = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_Thread_UseAllCpuGroups) != 0;
963 if (!enableGCCPUGroups)
966 if (!InitCPUGroupInfoArray())
969 if (!InitCPUGroupInfoRange())
972 // initalGroup is whatever the CPU group that the main thread is running on
973 GROUP_AFFINITY groupAffinity;
974 CPUGroupInfo::GetThreadGroupAffinity(GetCurrentThread(), &groupAffinity);
975 m_initialGroup = groupAffinity.Group;
977 // only enable CPU groups if more than one group exists
978 BOOL hasMultipleGroups = m_nGroups > 1;
979 m_enableGCCPUGroups = enableGCCPUGroups && hasMultipleGroups;
980 m_threadUseAllCpuGroups = threadUseAllCpuGroups && hasMultipleGroups;
981 #endif // _TARGET_AMD64_ || _TARGET_ARM64_
983 // Determine if the process is affinitized to a single processor (or if the system has a single processor)
984 DWORD_PTR processAffinityMask, systemAffinityMask;
985 if (GetProcessAffinityMask(GetCurrentProcess(), &processAffinityMask, &systemAffinityMask))
987 processAffinityMask &= systemAffinityMask;
988 if (processAffinityMask != 0 && // only one CPU group is involved
989 (processAffinityMask & (processAffinityMask - 1)) == 0) // only one bit is set
991 s_hadSingleProcessorAtStartup = true;
996 /*static*/ BOOL CPUGroupInfo::IsInitialized()
998 LIMITED_METHOD_CONTRACT;
999 return (m_initialization == -1);
1002 /*static*/ void CPUGroupInfo::EnsureInitialized()
1011 // CPUGroupInfo needs to be initialized only once. This could happen in three cases
1012 // 1. CLR initialization at begining of EEStartup, or
1013 // 2. Sometimes, when hosted by ASP.NET, the hosting process may initialize ThreadPool
1014 // before initializing CLR, thus require CPUGroupInfo to be initialized to determine
1015 // if CPU group support should/could be enabled.
1016 // 3. Call into Threadpool functions before Threadpool _and_ CLR is initialized.
1017 // Vast majority of time, CPUGroupInfo is initialized in case 1. or 2.
1018 // The chance of contention will be extremely small, so the following code should be fine
1021 if (IsInitialized())
1024 if (InterlockedCompareExchange(&m_initialization, 1, 0) == 0)
1027 m_initialization = -1;
1029 else //some other thread started initialization, just wait until complete;
1031 while (m_initialization != -1)
1039 /*static*/ WORD CPUGroupInfo::GetNumActiveProcessors()
1041 LIMITED_METHOD_CONTRACT;
1042 return (WORD)m_nProcessors;
1045 /*static*/ void CPUGroupInfo::GetGroupForProcessor(WORD processor_number,
1046 WORD* group_number, WORD* group_processor_number)
1048 LIMITED_METHOD_CONTRACT;
1050 #if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
1052 WORD bDiff = processor_number - bTemp;
1054 for (WORD i=0; i < m_nGroups; i++)
1056 bTemp += m_CPUGroupInfoArray[i].nr_active;
1057 if (bTemp > processor_number)
1060 *group_processor_number = bDiff;
1063 bDiff = processor_number - bTemp;
1067 *group_processor_number = 0;
1071 /*static*/ DWORD CPUGroupInfo::CalculateCurrentProcessorNumber()
1080 #if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
1081 // m_enableGCCPUGroups and m_threadUseAllCpuGroups must be TRUE
1082 _ASSERTE(m_enableGCCPUGroups && m_threadUseAllCpuGroups);
1084 PROCESSOR_NUMBER proc_no;
1088 ::GetCurrentProcessorNumberEx(&proc_no);
1090 DWORD fullNumber = 0;
1091 for (WORD i = 0; i < proc_no.Group; i++)
1092 fullNumber += (DWORD)m_CPUGroupInfoArray[i].nr_active;
1093 fullNumber += (DWORD)(proc_no.Number);
1101 #if !defined(FEATURE_REDHAWK)
1102 //Lock ThreadStore before calling this function, so that updates of weights/counts are consistent
1103 /*static*/ void CPUGroupInfo::ChooseCPUGroupAffinity(GROUP_AFFINITY *gf)
1112 #if (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
1113 WORD i, minGroup = 0;
1114 DWORD minWeight = 0;
1116 // m_enableGCCPUGroups and m_threadUseAllCpuGroups must be TRUE
1117 _ASSERTE(m_enableGCCPUGroups && m_threadUseAllCpuGroups);
1119 for (i = 0; i < m_nGroups; i++)
1121 minGroup = (m_initialGroup + i) % m_nGroups;
1123 // the group is not filled up, use it
1124 if (m_CPUGroupInfoArray[minGroup].activeThreadWeight / m_CPUGroupInfoArray[minGroup].groupWeight
1125 < (DWORD)m_CPUGroupInfoArray[minGroup].nr_active)
1129 // all groups filled up, distribute proportionally
1130 minGroup = m_initialGroup;
1131 minWeight = m_CPUGroupInfoArray[m_initialGroup].activeThreadWeight;
1132 for (i = 0; i < m_nGroups; i++)
1134 if (m_CPUGroupInfoArray[i].activeThreadWeight < minWeight)
1137 minWeight = m_CPUGroupInfoArray[i].activeThreadWeight;
1142 gf->Group = minGroup;
1143 gf->Mask = m_CPUGroupInfoArray[minGroup].active_mask;
1144 gf->Reserved[0] = 0;
1145 gf->Reserved[1] = 0;
1146 gf->Reserved[2] = 0;
1147 m_CPUGroupInfoArray[minGroup].activeThreadWeight += m_CPUGroupInfoArray[minGroup].groupWeight;
1151 //Lock ThreadStore before calling this function, so that updates of weights/counts are consistent
1152 /*static*/ void CPUGroupInfo::ClearCPUGroupAffinity(GROUP_AFFINITY *gf)
1154 LIMITED_METHOD_CONTRACT;
1155 #if (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
1156 // m_enableGCCPUGroups and m_threadUseAllCpuGroups must be TRUE
1157 _ASSERTE(m_enableGCCPUGroups && m_threadUseAllCpuGroups);
1159 WORD group = gf->Group;
1160 m_CPUGroupInfoArray[group].activeThreadWeight -= m_CPUGroupInfoArray[group].groupWeight;
1164 BOOL CPUGroupInfo::GetCPUGroupRange(WORD group_number, WORD* group_begin, WORD* group_size)
1166 if (group_number >= m_nGroups)
1171 *group_begin = m_CPUGroupInfoArray[group_number].begin;
1172 *group_size = m_CPUGroupInfoArray[group_number].nr_active;
1179 /*static*/ BOOL CPUGroupInfo::CanEnableGCCPUGroups()
1181 LIMITED_METHOD_CONTRACT;
1182 return m_enableGCCPUGroups;
1185 /*static*/ BOOL CPUGroupInfo::CanEnableThreadUseAllCpuGroups()
1187 LIMITED_METHOD_CONTRACT;
1188 return m_threadUseAllCpuGroups;
1191 //******************************************************************************
1192 // Returns the number of processors that a process has been configured to run on
1193 //******************************************************************************
1194 int GetCurrentProcessCpuCount()
1203 static int cCPUs = 0;
1208 unsigned int count = 0;
1209 DWORD_PTR pmask, smask;
1211 if (!GetProcessAffinityMask(GetCurrentProcess(), &pmask, &smask))
1221 pmask &= (pmask - 1);
1225 // GetProcessAffinityMask can return pmask=0 and smask=0 on systems with more
1226 // than 64 processors, which would leave us with a count of 0. Since the GC
1227 // expects there to be at least one processor to run on (and thus at least one
1228 // heap), we'll return 64 here if count is 0, since there are likely a ton of
1229 // processors available in that case. The GC also cannot (currently) handle
1230 // the case where there are more than 64 processors, so we will return a
1231 // maximum of 64 here.
1232 if (count == 0 || count > 64)
1239 if (PAL_GetCpuLimit(&cpuLimit) && cpuLimit < count)
1248 DWORD_PTR GetCurrentProcessCpuMask()
1258 DWORD_PTR pmask, smask;
1260 if (!GetProcessAffinityMask(GetCurrentProcess(), &pmask, &smask))
1270 uint32_t GetOsPageSizeUncached()
1272 SYSTEM_INFO sysInfo;
1273 ::GetSystemInfo(&sysInfo);
1274 return sysInfo.dwAllocationGranularity ? sysInfo.dwAllocationGranularity : 0x1000;
1279 Volatile<uint32_t> g_pageSize = 0;
1282 uint32_t GetOsPageSize()
1285 size_t result = g_pageSize.LoadWithoutBarrier();
1289 result = GetOsPageSizeUncached();
1291 g_pageSize.StoreWithoutBarrier(result);
1300 /**************************************************************************/
1302 /**************************************************************************/
1303 void ConfigMethodSet::init(const CLRConfig::ConfigStringInfo & info)
1311 // make sure that the memory was zero initialized
1312 _ASSERTE(m_inited == 0 || m_inited == 1);
1314 LPWSTR str = CLRConfig::GetConfigValue(info);
1323 /**************************************************************************/
1324 bool ConfigMethodSet::contains(LPCUTF8 methodName, LPCUTF8 className, PCCOR_SIGNATURE sig)
1332 _ASSERTE(m_inited == 1);
1334 if (m_list.IsEmpty())
1336 return(m_list.IsInList(methodName, className, sig));
1339 /**************************************************************************/
1340 bool ConfigMethodSet::contains(LPCUTF8 methodName, LPCUTF8 className, CORINFO_SIG_INFO* pSigInfo)
1348 _ASSERTE(m_inited == 1);
1350 if (m_list.IsEmpty())
1352 return(m_list.IsInList(methodName, className, pSigInfo));
1355 /**************************************************************************/
1356 void ConfigDWORD::init_DontUse_(__in_z LPCWSTR keyName, DWORD defaultVal)
1364 // make sure that the memory was zero initialized
1365 _ASSERTE(m_inited == 0 || m_inited == 1);
1367 m_value = REGUTIL::GetConfigDWORD_DontUse_(keyName, defaultVal);
1371 /**************************************************************************/
1372 void ConfigString::init(const CLRConfig::ConfigStringInfo & info)
1380 // make sure that the memory was zero initialized
1381 _ASSERTE(m_inited == 0 || m_inited == 1);
1383 // Note: m_value will be leaking
1384 m_value = CLRConfig::GetConfigValue(info);
1388 //=============================================================================
1389 // AssemblyNamesList
1390 //=============================================================================
1391 // The string should be of the form
1393 // MyAssembly;mscorlib;System
1394 // MyAssembly;mscorlib System
1396 AssemblyNamesList::AssemblyNamesList(__in LPWSTR list)
1402 WCHAR prevChar = '?'; // dummy
1403 LPWSTR nameStart = NULL; // start of the name currently being processed. NULL if no current name
1404 AssemblyName ** ppPrevLink = &m_pNames;
1406 for (LPWSTR listWalk = list; prevChar != '\0'; prevChar = *listWalk, listWalk++)
1408 WCHAR curChar = *listWalk;
1410 if (iswspace(curChar) || curChar == ';' || curChar == '\0' )
1413 // Found white-space
1418 // Found the end of the current name
1420 AssemblyName * newName = new AssemblyName();
1421 size_t nameLen = listWalk - nameStart;
1423 MAKE_UTF8PTR_FROMWIDE(temp, nameStart);
1424 newName->m_assemblyName = new char[nameLen + 1];
1425 memcpy(newName->m_assemblyName, temp, nameLen * sizeof(newName->m_assemblyName[0]));
1426 newName->m_assemblyName[nameLen] = '\0';
1428 *ppPrevLink = newName;
1429 ppPrevLink = &newName->m_next;
1434 else if (!nameStart)
1437 // Found the start of a new name
1440 nameStart = listWalk;
1444 _ASSERTE(!nameStart); // cannot be in the middle of a name
1448 AssemblyNamesList::~AssemblyNamesList()
1456 for (AssemblyName * pName = m_pNames; pName; /**/)
1458 AssemblyName * cur = pName;
1459 pName = pName->m_next;
1461 delete [] cur->m_assemblyName;
1466 bool AssemblyNamesList::IsInList(LPCUTF8 assemblyName)
1471 for (AssemblyName * pName = m_pNames; pName; pName = pName->m_next)
1473 if (_stricmp(pName->m_assemblyName, assemblyName) == 0)
1480 //=============================================================================
1482 //=============================================================================
1483 // str should be of the form :
1484 // "foo1 MyNamespace.MyClass:foo3 *:foo4 foo5(x,y,z)"
1485 // "MyClass:foo2 MyClass:*" will match under _DEBUG
1488 void MethodNamesListBase::Insert(__in_z LPWSTR str)
1494 enum State { NO_NAME, CLS_NAME, FUNC_NAME, ARG_LIST }; // parsing state machine
1496 const char SEP_CHAR = ' '; // current character use to separate each entry
1497 // const char SEP_CHAR = ';'; // better character use to separate each entry
1499 WCHAR lastChar = '?'; // dummy
1500 LPWSTR nameStart = NULL; // while walking over the classname or methodname, this points to start
1501 MethodName nameBuf; // Buffer used while parsing the current entry
1502 MethodName** lastName = &pNames; // last entry inserted into the list
1503 bool bQuote = false;
1505 nameBuf.methodName = NULL;
1506 nameBuf.className = NULL;
1507 nameBuf.numArgs = -1;
1508 nameBuf.next = NULL;
1510 for(State state = NO_NAME; lastChar != '\0'; str++)
1517 if (*str != SEP_CHAR)
1520 state = CLS_NAME; // we have found the start of the next entry
1525 if (*nameStart == '"')
1527 while (*str && *str!='"')
1537 if (*nameStart == '*' && !bQuote)
1539 // Is the classname string a wildcard. Then set it to NULL
1540 nameBuf.className = NULL;
1544 int len = (int)(str - nameStart);
1546 // Take off the quote
1547 if (bQuote) { len--; bQuote=false; }
1549 nameBuf.className = new char[len + 1];
1550 MAKE_UTF8PTR_FROMWIDE(temp, nameStart);
1551 memcpy(nameBuf.className, temp, len*sizeof(nameBuf.className[0]));
1552 nameBuf.className[len] = '\0';
1554 if (str[1] == ':') // Accept class::name syntax too
1556 nameStart = str + 1;
1559 else if (*str == '\0' || *str == SEP_CHAR || *str == '(')
1561 /* This was actually a method name without any class */
1562 nameBuf.className = NULL;
1563 goto DONE_FUNC_NAME;
1568 if (*nameStart == '"')
1570 while ( (nameStart==str) || // workaround to handle when className!=NULL
1571 (*str && *str!='"'))
1580 if (*str == '\0' || *str == SEP_CHAR || *str == '(')
1583 _ASSERTE(*str == '\0' || *str == SEP_CHAR || *str == '(');
1585 if (*nameStart == '*' && !bQuote)
1587 // Is the name string a wildcard. Then set it to NULL
1588 nameBuf.methodName = NULL;
1592 int len = (int)(str - nameStart);
1594 // Take off the quote
1595 if (bQuote) { len--; bQuote=false; }
1597 nameBuf.methodName = new char[len + 1];
1598 MAKE_UTF8PTR_FROMWIDE(temp, nameStart);
1599 memcpy(nameBuf.methodName, temp, len*sizeof(nameBuf.methodName[0]));
1600 nameBuf.methodName[len] = '\0';
1603 if (*str == '\0' || *str == SEP_CHAR)
1605 nameBuf.numArgs = -1;
1610 _ASSERTE(*str == '(');
1611 nameBuf.numArgs = -1;
1618 if (*str == '\0' || *str == ')')
1620 if (nameBuf.numArgs == -1)
1621 nameBuf.numArgs = 0;
1624 _ASSERTE(*str == '\0' || *str == SEP_CHAR || *str == ')');
1626 // We have parsed an entire method name.
1627 // Create a new entry in the list for it
1629 MethodName * newName = new MethodName();
1631 newName->next = NULL;
1632 *lastName = newName;
1633 lastName = &newName->next;
1636 // Skip anything after the argument list until we find the next
1637 // separator character, otherwise if we see "func(a,b):foo" we
1638 // create entries for "func(a,b)" as well as ":foo".
1641 while (*str && *str != SEP_CHAR)
1650 if (*str != SEP_CHAR && nameBuf.numArgs == -1)
1651 nameBuf.numArgs = 1;
1657 default: _ASSERTE(!"Bad state"); break;
1662 /**************************************************************/
1664 void MethodNamesListBase::Destroy()
1672 for(MethodName * pName = pNames; pName; /**/)
1674 if (pName->className)
1675 delete [] pName->className;
1676 if (pName->methodName)
1677 delete [] pName->methodName;
1679 MethodName * curName = pName;
1680 pName = pName->next;
1685 /**************************************************************/
1686 bool MethodNamesListBase::IsInList(LPCUTF8 methName, LPCUTF8 clsName, PCCOR_SIGNATURE sig)
1697 sig++; // Skip calling convention
1698 numArgs = CorSigUncompressData(sig);
1701 return IsInList(methName, clsName, numArgs);
1704 /**************************************************************/
1705 bool MethodNamesListBase::IsInList(LPCUTF8 methName, LPCUTF8 clsName, CORINFO_SIG_INFO* pSigInfo)
1714 if (pSigInfo != NULL)
1716 numArgs = pSigInfo->numArgs;
1719 return IsInList(methName, clsName, numArgs);
1722 /**************************************************************/
1723 bool MethodNamesListBase::IsInList(LPCUTF8 methName, LPCUTF8 clsName, int numArgs)
1731 // Try to match all the entries in the list
1733 for(MethodName * pName = pNames; pName; pName = pName->next)
1735 // If numArgs is valid, check for mismatch
1736 if (pName->numArgs != -1 && pName->numArgs != numArgs)
1739 // If methodName is valid, check for mismatch
1740 if (pName->methodName) {
1741 if (strcmp(pName->methodName, methName) != 0) {
1743 // C++ embeds the class name into the method name,
1744 // deal with that here (workaround)
1745 const char* ptr = strchr(methName, ':');
1746 if (ptr != 0 && ptr[1] == ':' && strcmp(&ptr[2], pName->methodName) == 0) {
1747 unsigned clsLen = (unsigned)(ptr - methName);
1748 if (pName->className == 0 || strncmp(pName->className, methName, clsLen) == 0)
1755 // check for class Name exact match
1756 if (clsName == 0 || pName->className == 0 || strcmp(pName->className, clsName) == 0)
1759 // check for suffix wildcard like System.*
1760 unsigned len = (unsigned)strlen(pName->className);
1761 if (len > 0 && pName->className[len-1] == '*' && strncmp(pName->className, clsName, len-1) == 0)
1765 // Maybe className doesnt include namespace. Try to match that
1766 LPCUTF8 onlyClass = ns::FindSep(clsName);
1767 if (onlyClass && strcmp(pName->className, onlyClass+1) == 0)
1774 //=============================================================================
1775 // Signature Validation Functions (scaled down version from MDValidator
1776 //=============================================================================
1778 //*****************************************************************************
1779 // This function validates one argument given an offset into the signature
1780 // where the argument begins. This function assumes that the signature is well
1781 // formed as far as the compression scheme is concerned.
1782 // <TODO>@todo: Validate tokens embedded.</TODO>
1783 //*****************************************************************************
1784 HRESULT validateOneArg(
1785 mdToken tk, // [IN] Token whose signature needs to be validated.
1787 ULONG *pulNSentinels, // [IN/OUT] Number of sentinels
1788 IMDInternalImport* pImport, // [IN] Internal MD Import interface ptr
1789 BOOL bNoVoidAllowed) // [IN] Flag indicating whether "void" is disallowed for this arg
1798 BYTE elementType; // Current element type being processed.
1799 mdToken token; // Embedded token.
1800 ULONG ulArgCnt; // Argument count for function pointer.
1801 ULONG ulIndex; // Index for type parameters
1802 ULONG ulRank; // Rank of the array.
1803 ULONG ulSizes; // Count of sized dimensions of the array.
1804 ULONG ulLbnds; // Count of lower bounds of the array.
1807 HRESULT hr = S_OK; // Value returned.
1808 BOOL bRepeat = TRUE; // MODOPT and MODREQ belong to the arg after them
1813 // Validate that the argument is not missing.
1815 // Get the element type.
1816 if (FAILED(pSig->GetByte(&elementType)))
1818 IfFailGo(VLDTR_E_SIG_MISSARG);
1821 // Walk past all the modifier types.
1822 while (elementType & ELEMENT_TYPE_MODIFIER)
1824 if (elementType == ELEMENT_TYPE_SENTINEL)
1826 if(pulNSentinels) *pulNSentinels+=1;
1827 if(TypeFromToken(tk) != mdtMemberRef) IfFailGo(VLDTR_E_SIG_SENTINMETHODDEF);
1829 if (FAILED(pSig->GetByte(&elementType)))
1831 IfFailGo(VLDTR_E_SIG_MISSELTYPE);
1835 switch (elementType)
1837 case ELEMENT_TYPE_VOID:
1838 if(bNoVoidAllowed) IfFailGo(VLDTR_E_SIG_BADVOID);
1840 case ELEMENT_TYPE_BOOLEAN:
1841 case ELEMENT_TYPE_CHAR:
1842 case ELEMENT_TYPE_I1:
1843 case ELEMENT_TYPE_U1:
1844 case ELEMENT_TYPE_I2:
1845 case ELEMENT_TYPE_U2:
1846 case ELEMENT_TYPE_I4:
1847 case ELEMENT_TYPE_U4:
1848 case ELEMENT_TYPE_I8:
1849 case ELEMENT_TYPE_U8:
1850 case ELEMENT_TYPE_R4:
1851 case ELEMENT_TYPE_R8:
1852 case ELEMENT_TYPE_STRING:
1853 case ELEMENT_TYPE_OBJECT:
1854 case ELEMENT_TYPE_TYPEDBYREF:
1855 case ELEMENT_TYPE_U:
1856 case ELEMENT_TYPE_I:
1858 case ELEMENT_TYPE_PTR:
1859 // Validate the referenced type.
1860 if(FAILED(hr = validateOneArg(tk, pSig, pulNSentinels, pImport, FALSE))) IfFailGo(hr);
1862 case ELEMENT_TYPE_BYREF: //fallthru
1863 if(TypeFromToken(tk)==mdtFieldDef) IfFailGo(VLDTR_E_SIG_BYREFINFIELD);
1864 case ELEMENT_TYPE_PINNED:
1865 case ELEMENT_TYPE_SZARRAY:
1866 // Validate the referenced type.
1867 if(FAILED(hr = validateOneArg(tk, pSig, pulNSentinels, pImport, TRUE))) IfFailGo(hr);
1869 case ELEMENT_TYPE_CMOD_OPT:
1870 case ELEMENT_TYPE_CMOD_REQD:
1871 bRepeat = TRUE; // go on validating, we're not done with this arg
1872 case ELEMENT_TYPE_VALUETYPE: //fallthru
1873 case ELEMENT_TYPE_CLASS:
1874 // See if the token is missing.
1875 if (FAILED(pSig->GetToken(&token)))
1877 IfFailGo(VLDTR_E_SIG_MISSTKN);
1879 // Token validation .
1882 ULONG rid = RidFromToken(token);
1883 ULONG typ = TypeFromToken(token);
1884 ULONG maxrid = pImport->GetCountWithTokenKind(typ);
1885 if(typ == mdtTypeDef) maxrid++;
1886 if((rid==0)||(rid > maxrid)) IfFailGo(VLDTR_E_SIG_TKNBAD);
1890 case ELEMENT_TYPE_FNPTR:
1891 // <TODO>@todo: More function pointer validation?</TODO>
1892 // Validate that calling convention is present.
1893 if (FAILED(pSig->GetCallingConvInfo(&ulCallConv)))
1895 IfFailGo(VLDTR_E_SIG_MISSFPTR);
1897 if(((ulCallConv & IMAGE_CEE_CS_CALLCONV_MASK) >= IMAGE_CEE_CS_CALLCONV_MAX)
1898 ||((ulCallConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS)
1899 &&(!(ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS)))) IfFailGo(VLDTR_E_MD_BADCALLINGCONV);
1901 // Validate that argument count is present.
1902 if (FAILED(pSig->GetData(&ulArgCnt)))
1904 IfFailGo(VLDTR_E_SIG_MISSFPTRARGCNT);
1907 // FNPTR signature must follow the rules of MethodDef
1908 // Validate and consume return type.
1909 IfFailGo(validateOneArg(mdtMethodDef, pSig, NULL, pImport, FALSE));
1911 // Validate and consume the arguments.
1914 IfFailGo(validateOneArg(mdtMethodDef, pSig, NULL, pImport, TRUE));
1918 case ELEMENT_TYPE_ARRAY:
1919 // Validate and consume the base type.
1920 IfFailGo(validateOneArg(tk, pSig, pulNSentinels, pImport, TRUE));
1922 // Validate that the rank is present.
1923 if (FAILED(pSig->GetData(&ulRank)))
1925 IfFailGo(VLDTR_E_SIG_MISSRANK);
1928 // Process the sizes.
1931 // Validate that the count of sized-dimensions is specified.
1932 if (FAILED(pSig->GetData(&ulSizes)))
1934 IfFailGo(VLDTR_E_SIG_MISSNSIZE);
1937 // Loop over the sizes.
1940 // Validate the current size.
1941 if (FAILED(pSig->GetData(NULL)))
1943 IfFailGo(VLDTR_E_SIG_MISSSIZE);
1947 // Validate that the count of lower bounds is specified.
1948 if (FAILED(pSig->GetData(&ulLbnds)))
1950 IfFailGo(VLDTR_E_SIG_MISSNLBND);
1953 // Loop over the lower bounds.
1956 // Validate the current lower bound.
1957 if (FAILED(pSig->GetData(NULL)))
1959 IfFailGo(VLDTR_E_SIG_MISSLBND);
1964 case ELEMENT_TYPE_VAR:
1965 case ELEMENT_TYPE_MVAR:
1966 // Validate that index is present.
1967 if (FAILED(pSig->GetData(&ulIndex)))
1969 IfFailGo(VLDTR_E_SIG_MISSFPTRARGCNT);
1972 //@todo GENERICS: check that index is in range
1975 case ELEMENT_TYPE_GENERICINST:
1976 // Validate the generic type.
1977 IfFailGo(validateOneArg(tk, pSig, pulNSentinels, pImport, TRUE));
1979 // Validate that parameter count is present.
1980 if (FAILED(pSig->GetData(&ulArgCnt)))
1982 IfFailGo(VLDTR_E_SIG_MISSFPTRARGCNT);
1985 //@todo GENERICS: check that number of parameters matches definition?
1987 // Validate and consume the parameters.
1990 IfFailGo(validateOneArg(tk, pSig, NULL, pImport, TRUE));
1994 case ELEMENT_TYPE_SENTINEL: // this case never works because all modifiers are skipped before switch
1995 if(TypeFromToken(tk) == mdtMethodDef) IfFailGo(VLDTR_E_SIG_SENTINMETHODDEF);
1999 IfFailGo(VLDTR_E_SIG_BADELTYPE);
2001 } // switch (ulElementType)
2002 } // end while(bRepeat)
2005 } // validateOneArg()
2007 //*****************************************************************************
2008 // This function validates the given Method/Field/Standalone signature.
2009 //@todo GENERICS: MethodInstantiation?
2010 //*****************************************************************************
2011 HRESULT validateTokenSig(
2012 mdToken tk, // [IN] Token whose signature needs to be validated.
2013 PCCOR_SIGNATURE pbSig, // [IN] Signature.
2014 ULONG cbSig, // [IN] Size in bytes of the signature.
2015 DWORD dwFlags, // [IN] Method flags.
2016 IMDInternalImport* pImport) // [IN] Internal MD Import interface ptr
2024 ULONG ulCallConv; // Calling convention.
2025 ULONG ulArgCount = 1; // Count of arguments (1 because of the return type)
2026 ULONG ulTyArgCount = 0; // Count of type arguments
2027 ULONG ulArgIx = 0; // Starting index of argument (standalone sig: 1)
2028 ULONG i; // Looping index.
2029 HRESULT hr = S_OK; // Value returned.
2030 ULONG ulNSentinels = 0;
2031 SigParser sig(pbSig, cbSig);
2033 _ASSERTE(TypeFromToken(tk) == mdtMethodDef ||
2034 TypeFromToken(tk) == mdtMemberRef ||
2035 TypeFromToken(tk) == mdtSignature ||
2036 TypeFromToken(tk) == mdtFieldDef);
2038 // Check for NULL signature.
2039 if (!pbSig || !cbSig) return VLDTR_E_SIGNULL;
2041 // Validate the calling convention.
2043 // Moves behind calling convention
2044 IfFailRet(sig.GetCallingConvInfo(&ulCallConv));
2045 i = ulCallConv & IMAGE_CEE_CS_CALLCONV_MASK;
2046 switch(TypeFromToken(tk))
2048 case mdtMethodDef: // MemberRefs have no flags available
2049 // If HASTHIS is set on the calling convention, the method should not be static.
2050 if ((ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS) &&
2051 IsMdStatic(dwFlags)) return VLDTR_E_MD_THISSTATIC;
2053 // If HASTHIS is not set on the calling convention, the method should be static.
2054 if (!(ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS) &&
2055 !IsMdStatic(dwFlags)) return VLDTR_E_MD_NOTTHISNOTSTATIC;
2056 // fall thru to callconv check;
2059 if(i == IMAGE_CEE_CS_CALLCONV_FIELD) return validateOneArg(tk, &sig, NULL, pImport, TRUE);
2061 // EXPLICITTHIS and native call convs are for stand-alone sigs only (for calli)
2062 if(((i != IMAGE_CEE_CS_CALLCONV_DEFAULT)&&( i != IMAGE_CEE_CS_CALLCONV_VARARG))
2063 || (ulCallConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS)) return VLDTR_E_MD_BADCALLINGCONV;
2067 if(i != IMAGE_CEE_CS_CALLCONV_LOCAL_SIG) // then it is function sig for calli
2069 if((i >= IMAGE_CEE_CS_CALLCONV_MAX)
2070 ||((ulCallConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS)
2071 &&(!(ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS)))) return VLDTR_E_MD_BADCALLINGCONV;
2074 ulArgIx = 1; // Local variable signatures don't have a return type
2078 if(i != IMAGE_CEE_CS_CALLCONV_FIELD) return VLDTR_E_MD_BADCALLINGCONV;
2079 return validateOneArg(tk, &sig, NULL, pImport, TRUE);
2081 // Is there any sig left for arguments?
2083 // Get the type argument count
2084 if (ulCallConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
2086 if (FAILED(sig.GetData(&ulTyArgCount)))
2088 return VLDTR_E_MD_NOARGCNT;
2092 // Get the argument count.
2093 if (FAILED(sig.GetData(&ulArgCount)))
2095 return VLDTR_E_MD_NOARGCNT;
2098 // Validate the return type and the arguments.
2099 // (at this moment ulArgCount = num.args+1, ulArgIx = (standalone sig. ? 1 :0); )
2100 for(; ulArgIx < ulArgCount; ulArgIx++)
2102 if(FAILED(hr = validateOneArg(tk, &sig, &ulNSentinels, pImport, (ulArgIx!=0)))) return hr;
2105 // <TODO>@todo: we allow junk to be at the end of the signature (we may not consume it all)
2106 // do we care?</TODO>
2108 if((ulNSentinels != 0) && ((ulCallConv & IMAGE_CEE_CS_CALLCONV_MASK) != IMAGE_CEE_CS_CALLCONV_VARARG ))
2109 return VLDTR_E_SIG_SENTMUSTVARARG;
2110 if(ulNSentinels > 1) return VLDTR_E_SIG_MULTSENTINELS;
2112 } // validateTokenSig()
2114 HRESULT GetImageRuntimeVersionString(PVOID pMetaData, LPCSTR* pString)
2123 STORAGESIGNATURE* pSig = (STORAGESIGNATURE*) pMetaData;
2125 // Verify the signature.
2127 // If signature didn't match, you shouldn't be here.
2128 if (pSig->GetSignature() != STORAGE_MAGIC_SIG)
2129 return CLDB_E_FILE_CORRUPT;
2131 // The version started in version 1.1
2132 if (pSig->GetMajorVer() < 1)
2133 return CLDB_E_FILE_OLDVER;
2135 if (pSig->GetMajorVer() == 1 && pSig->GetMinorVer() < 1)
2136 return CLDB_E_FILE_OLDVER;
2138 // Header data starts after signature.
2139 *pString = (LPCSTR) pSig->pVersion;
2143 //*****************************************************************************
2144 // Convert a UTF8 string to Unicode, into a CQuickArray<WCHAR>.
2145 //*****************************************************************************
2147 LPCUTF8 pStr, // The string to convert.
2148 CQuickArray<WCHAR> &rStr, // The QuickArray<WCHAR> to convert it into.
2149 int iCurLen) // Inital characters in the array to leave (default 0).
2157 HRESULT hr = S_OK; // A result.
2158 int iReqLen; // Required additional length.
2160 int bAlloc = 0; // If non-zero, allocation was required.
2164 _ASSERTE_MSG(false, "Invalid current length");
2165 return E_INVALIDARG;
2168 // Calculate the space available
2169 S_SIZE_T cchAvail = S_SIZE_T(rStr.MaxSize()) - S_SIZE_T(iCurLen);
2170 if (cchAvail.IsOverflow() || cchAvail.Value() > INT_MAX)
2172 _ASSERTE_MSG(false, "Integer overflow/underflow");
2173 return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
2176 // Attempt the conversion.
2177 LPWSTR rNewStr = rStr.Ptr()+iCurLen;
2178 if(rNewStr < rStr.Ptr())
2180 _ASSERTE_MSG(false, "Integer overflow/underflow");
2181 return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
2183 iReqLen = WszMultiByteToWideChar(CP_UTF8, 0, pStr, -1, rNewStr, (int)(cchAvail.Value()));
2185 // If the buffer was too small, determine what is required.
2187 bAlloc = iReqLen = WszMultiByteToWideChar(CP_UTF8, 0, pStr, -1, 0, 0);
2188 // Resize the buffer. If the buffer was large enough, this just sets the internal
2189 // counter, but if it was too small, this will attempt a reallocation. Note that
2190 // the length includes the terminating W('/0').
2191 IfFailGo(rStr.ReSizeNoThrow(iCurLen+iReqLen));
2192 // If we had to realloc, then do the conversion again, now that the buffer is
2195 //recalculating cchAvail since MaxSize could have been changed.
2196 cchAvail = S_SIZE_T(rStr.MaxSize()) - S_SIZE_T(iCurLen);
2197 if (cchAvail.IsOverflow() || cchAvail.Value() > INT_MAX)
2199 _ASSERTE_MSG(false, "Integer overflow/underflow");
2200 return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
2202 //reculculating rNewStr
2203 rNewStr = rStr.Ptr()+iCurLen;
2205 if(rNewStr < rStr.Ptr())
2207 _ASSERTE_MSG(false, "Integer overflow/underflow");
2208 return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
2210 iActLen = WszMultiByteToWideChar(CP_UTF8, 0, pStr, -1, rNewStr, (int)(cchAvail.Value()));
2211 _ASSERTE(iReqLen == iActLen);
2215 } // HRESULT Utf2Quick()
2218 //*****************************************************************************
2219 // Extract the movl 64-bit unsigned immediate from an IA64 bundle
2221 //*****************************************************************************
2222 UINT64 GetIA64Imm64(UINT64 * pBundle)
2224 WRAPPER_NO_CONTRACT;
2226 UINT64 temp0 = PTR_UINT64(pBundle)[0];
2227 UINT64 temp1 = PTR_UINT64(pBundle)[1];
2229 return GetIA64Imm64(temp0, temp1);
2232 UINT64 GetIA64Imm64(UINT64 qword0, UINT64 qword1)
2234 LIMITED_METHOD_CONTRACT;
2240 // make certain we're decoding a movl opcode, with template 4 or 5
2242 UINT64 templa = (qword0 >> 0) & 0x1f;
2243 UINT64 opcode = (qword1 >> 60) & 0xf;
2245 _ASSERTE((opcode == 0x6) && ((templa == 0x4) || (templa == 0x5)));
2248 imm64 = (qword1 >> 59) << 63; // 1 i
2249 imm64 |= (qword1 << 41) >> 1; // 23 high bits of imm41
2250 imm64 |= (qword0 >> 46) << 22; // 18 low bits of imm41
2251 imm64 |= (qword1 >> 23) & 0x200000; // 1 ic
2252 imm64 |= (qword1 >> 29) & 0x1F0000; // 5 imm5c
2253 imm64 |= (qword1 >> 43) & 0xFF80; // 9 imm9d
2254 imm64 |= (qword1 >> 36) & 0x7F; // 7 imm7b
2259 //*****************************************************************************
2260 // Deposit the movl 64-bit unsigned immediate into an IA64 bundle
2262 //*****************************************************************************
2263 void PutIA64Imm64(UINT64 * pBundle, UINT64 imm64)
2265 LIMITED_METHOD_CONTRACT;
2269 // make certain we're decoding a movl opcode, with template 4 or 5
2271 UINT64 templa = (pBundle[0] >> 0) & 0x1f;
2272 UINT64 opcode = (pBundle[1] >> 60) & 0xf ;
2274 _ASSERTE((opcode == 0x6) && ((templa == 0x4) || (templa == 0x5)));
2277 const UINT64 mask0 = UI64(0x00003FFFFFFFFFFF);
2278 const UINT64 mask1 = UI64(0xF000080FFF800000);
2280 /* Clear all bits used as part of the imm64 */
2281 pBundle[0] &= mask0;
2282 pBundle[1] &= mask1;
2287 temp1 = (imm64 >> 63) << 59; // 1 i
2288 temp1 |= (imm64 & 0xFF80) << 43; // 9 imm9d
2289 temp1 |= (imm64 & 0x1F0000) << 29; // 5 imm5c
2290 temp1 |= (imm64 & 0x200000) << 23; // 1 ic
2291 temp1 |= (imm64 & 0x7F) << 36; // 7 imm7b
2292 temp1 |= (imm64 << 1) >> 41; // 23 high bits of imm41
2293 temp0 = (imm64 >> 22) << 46; // 18 low bits of imm41
2295 /* Or in the new bits used in the imm64 */
2296 pBundle[0] |= temp0;
2297 pBundle[1] |= temp1;
2298 FlushInstructionCache(GetCurrentProcess(),pBundle,16);
2301 //*****************************************************************************
2302 // Extract the IP-Relative signed 25-bit immediate from an IA64 bundle
2303 // (Formats B1, B2 or B3)
2304 // Note that due to branch target alignment requirements
2305 // the lowest four bits in the result will always be zero.
2306 //*****************************************************************************
2307 INT32 GetIA64Rel25(UINT64 * pBundle, UINT32 slot)
2309 WRAPPER_NO_CONTRACT;
2311 UINT64 temp0 = PTR_UINT64(pBundle)[0];
2312 UINT64 temp1 = PTR_UINT64(pBundle)[1];
2314 return GetIA64Rel25(temp0, temp1, slot);
2317 INT32 GetIA64Rel25(UINT64 qword0, UINT64 qword1, UINT32 slot)
2319 LIMITED_METHOD_CONTRACT;
2325 if ((qword1 >> 59) & 1)
2327 imm25 |= (qword1 >> 32) & 0x00FFFFF0; // 20 imm20b
2331 if ((qword1 >> 18) & 1)
2333 imm25 |= (qword1 << 9) & 0x00FFFE00; // high 15 of imm20b
2334 imm25 |= (qword0 >> 55) & 0x000001F0; // low 5 of imm20b
2338 if ((qword0 >> 41) & 1)
2340 imm25 |= (qword0 >> 14) & 0x00FFFFF0; // 20 imm20b
2346 //*****************************************************************************
2347 // Deposit the IP-Relative signed 25-bit immediate into an IA64 bundle
2348 // (Formats B1, B2 or B3)
2349 // Note that due to branch target alignment requirements
2350 // the lowest four bits are required to be zero.
2351 //*****************************************************************************
2352 void PutIA64Rel25(UINT64 * pBundle, UINT32 slot, INT32 imm25)
2354 LIMITED_METHOD_CONTRACT;
2356 _ASSERTE((imm25 & 0xF) == 0);
2360 const UINT64 mask1 = UI64(0xF700000FFFFFFFFF);
2361 /* Clear all bits used as part of the imm25 */
2362 pBundle[1] &= mask1;
2366 temp1 = (UINT64) (imm25 & 0x1000000) << 35; // 1 s
2367 temp1 |= (UINT64) (imm25 & 0x0FFFFF0) << 32; // 20 imm20b
2369 /* Or in the new bits used in the imm64 */
2370 pBundle[1] |= temp1;
2374 const UINT64 mask0 = UI64(0x0EFFFFFFFFFFFFFF);
2375 const UINT64 mask1 = UI64(0xFFFFFFFFFFFB8000);
2376 /* Clear all bits used as part of the imm25 */
2377 pBundle[0] &= mask0;
2378 pBundle[1] &= mask1;
2383 temp1 = (UINT64) (imm25 & 0x1000000) >> 7; // 1 s
2384 temp1 |= (UINT64) (imm25 & 0x0FFFE00) >> 9; // high 15 of imm20b
2385 temp0 = (UINT64) (imm25 & 0x00001F0) << 55; // low 5 of imm20b
2387 /* Or in the new bits used in the imm64 */
2388 pBundle[0] |= temp0;
2389 pBundle[1] |= temp1;
2393 const UINT64 mask0 = UI64(0xFFFFFDC00003FFFF);
2394 /* Clear all bits used as part of the imm25 */
2395 pBundle[0] &= mask0;
2399 temp0 = (UINT64) (imm25 & 0x1000000) << 16; // 1 s
2400 temp0 |= (UINT64) (imm25 & 0x0FFFFF0) << 14; // 20 imm20b
2402 /* Or in the new bits used in the imm64 */
2403 pBundle[0] |= temp0;
2406 FlushInstructionCache(GetCurrentProcess(),pBundle,16);
2409 //*****************************************************************************
2410 // Extract the IP-Relative signed 64-bit immediate from an IA64 bundle
2411 // (Formats X3 or X4)
2412 //*****************************************************************************
2413 INT64 GetIA64Rel64(UINT64 * pBundle)
2415 WRAPPER_NO_CONTRACT;
2417 UINT64 temp0 = PTR_UINT64(pBundle)[0];
2418 UINT64 temp1 = PTR_UINT64(pBundle)[1];
2420 return GetIA64Rel64(temp0, temp1);
2423 INT64 GetIA64Rel64(UINT64 qword0, UINT64 qword1)
2425 LIMITED_METHOD_CONTRACT;
2431 // make certain we're decoding a brl opcode, with template 4 or 5
2433 UINT64 templa = (qword0 >> 0) & 0x1f;
2434 UINT64 opcode = (qword1 >> 60) & 0xf;
2436 _ASSERTE(((opcode == 0xC) || (opcode == 0xD)) &&
2437 ((templa == 0x4) || (templa == 0x5)));
2440 imm64 = (qword1 >> 59) << 63; // 1 i
2441 imm64 |= (qword1 << 41) >> 1; // 23 high bits of imm39
2442 imm64 |= (qword0 >> 48) << 24; // 16 low bits of imm39
2443 imm64 |= (qword1 >> 32) & 0xFFFFF0; // 20 imm20b
2448 //*****************************************************************************
2449 // Deposit the IP-Relative signed 64-bit immediate into an IA64 bundle
2450 // (Formats X3 or X4)
2451 //*****************************************************************************
2452 void PutIA64Rel64(UINT64 * pBundle, INT64 imm64)
2454 LIMITED_METHOD_CONTRACT;
2458 // make certain we're decoding a brl opcode, with template 4 or 5
2460 UINT64 templa = (pBundle[0] >> 0) & 0x1f;
2461 UINT64 opcode = (pBundle[1] >> 60) & 0xf;
2463 _ASSERTE(((opcode == 0xC) || (opcode == 0xD)) &&
2464 ((templa == 0x4) || (templa == 0x5)));
2465 _ASSERTE((imm64 & 0xF) == 0);
2468 const UINT64 mask0 = UI64(0x00003FFFFFFFFFFF);
2469 const UINT64 mask1 = UI64(0xF700000FFF800000);
2471 /* Clear all bits used as part of the imm64 */
2472 pBundle[0] &= mask0;
2473 pBundle[1] &= mask1;
2475 UINT64 temp0 = (imm64 & UI64(0x000000FFFF000000)) << 24; // 16 low bits of imm39
2476 UINT64 temp1 = (imm64 & UI64(0x8000000000000000)) >> 4 // 1 i
2477 | (imm64 & UI64(0x7FFFFF0000000000)) >> 40 // 23 high bits of imm39
2478 | (imm64 & UI64(0x0000000000FFFFF0)) << 32; // 20 imm20b
2480 /* Or in the new bits used in the imm64 */
2481 pBundle[0] |= temp0;
2482 pBundle[1] |= temp1;
2483 FlushInstructionCache(GetCurrentProcess(),pBundle,16);
2486 //*****************************************************************************
2487 // Extract the 16-bit immediate from ARM Thumb2 Instruction (format T2_N)
2488 //*****************************************************************************
2489 static FORCEINLINE UINT16 GetThumb2Imm16(UINT16 * p)
2491 LIMITED_METHOD_CONTRACT;
2493 return ((p[0] << 12) & 0xf000) |
2494 ((p[0] << 1) & 0x0800) |
2495 ((p[1] >> 4) & 0x0700) |
2496 ((p[1] >> 0) & 0x00ff);
2499 //*****************************************************************************
2500 // Extract the 32-bit immediate from movw/movt sequence
2501 //*****************************************************************************
2502 UINT32 GetThumb2Mov32(UINT16 * p)
2504 LIMITED_METHOD_CONTRACT;
2506 // Make sure we are decoding movw/movt sequence
2507 _ASSERTE_IMPL((*(p+0) & 0xFBF0) == 0xF240);
2508 _ASSERTE_IMPL((*(p+2) & 0xFBF0) == 0xF2C0);
2510 return (UINT32)GetThumb2Imm16(p) + ((UINT32)GetThumb2Imm16(p + 2) << 16);
2513 //*****************************************************************************
2514 // Deposit the 16-bit immediate into ARM Thumb2 Instruction (format T2_N)
2515 //*****************************************************************************
2516 static FORCEINLINE void PutThumb2Imm16(UINT16 * p, UINT16 imm16)
2518 LIMITED_METHOD_CONTRACT;
2520 USHORT Opcode0 = p[0];
2521 USHORT Opcode1 = p[1];
2522 Opcode0 &= ~((0xf000 >> 12) | (0x0800 >> 1));
2523 Opcode1 &= ~((0x0700 << 4) | (0x00ff << 0));
2524 Opcode0 |= (imm16 & 0xf000) >> 12;
2525 Opcode0 |= (imm16 & 0x0800) >> 1;
2526 Opcode1 |= (imm16 & 0x0700) << 4;
2527 Opcode1 |= (imm16 & 0x00ff) << 0;
2532 //*****************************************************************************
2533 // Deposit the 32-bit immediate into movw/movt Thumb2 sequence
2534 //*****************************************************************************
2535 void PutThumb2Mov32(UINT16 * p, UINT32 imm32)
2537 LIMITED_METHOD_CONTRACT;
2539 // Make sure we are decoding movw/movt sequence
2540 _ASSERTE_IMPL((*(p+0) & 0xFBF0) == 0xF240);
2541 _ASSERTE_IMPL((*(p+2) & 0xFBF0) == 0xF2C0);
2543 PutThumb2Imm16(p, (UINT16)imm32);
2544 PutThumb2Imm16(p + 2, (UINT16)(imm32 >> 16));
2547 //*****************************************************************************
2548 // Extract the 24-bit rel offset from bl instruction
2549 //*****************************************************************************
2550 INT32 GetThumb2BlRel24(UINT16 * p)
2552 LIMITED_METHOD_CONTRACT;
2554 USHORT Opcode0 = p[0];
2555 USHORT Opcode1 = p[1];
2557 UINT32 S = Opcode0 >> 10;
2558 UINT32 J2 = Opcode1 >> 11;
2559 UINT32 J1 = Opcode1 >> 13;
2562 ((S << 24) & 0x1000000) |
2563 (((J1 ^ S ^ 1) << 23) & 0x0800000) |
2564 (((J2 ^ S ^ 1) << 22) & 0x0400000) |
2565 ((Opcode0 << 12) & 0x03FF000) |
2566 ((Opcode1 << 1) & 0x0000FFE);
2568 // Sign-extend and return
2569 return (ret << 7) >> 7;
2572 //*****************************************************************************
2573 // Extract the 24-bit rel offset from bl instruction
2574 //*****************************************************************************
2575 void PutThumb2BlRel24(UINT16 * p, INT32 imm24)
2577 LIMITED_METHOD_CONTRACT;
2579 // Verify that we got a valid offset
2580 _ASSERTE(FitsInThumb2BlRel24(imm24));
2582 #if defined(_TARGET_ARM_)
2583 // Ensure that the ThumbBit is not set on the offset
2584 // as it cannot be encoded.
2585 _ASSERTE(!(imm24 & THUMB_CODE));
2586 #endif // _TARGET_ARM_
2588 USHORT Opcode0 = p[0];
2589 USHORT Opcode1 = p[1];
2593 UINT32 S = (imm24 & 0x1000000) >> 24;
2594 UINT32 J1 = ((imm24 & 0x0800000) >> 23) ^ S ^ 1;
2595 UINT32 J2 = ((imm24 & 0x0400000) >> 22) ^ S ^ 1;
2597 Opcode0 |= ((imm24 & 0x03FF000) >> 12) | (S << 10);
2598 Opcode1 |= ((imm24 & 0x0000FFE) >> 1) | (J1 << 13) | (J2 << 11);
2603 _ASSERTE(GetThumb2BlRel24(p) == imm24);
2606 //*****************************************************************************
2607 // Extract the PC-Relative offset from a b or bl instruction
2608 //*****************************************************************************
2609 INT32 GetArm64Rel28(UINT32 * pCode)
2611 LIMITED_METHOD_CONTRACT;
2613 UINT32 branchInstr = *pCode;
2615 // first shift 6 bits left to set the sign bit,
2616 // then arithmetic shift right by 4 bits
2617 INT32 imm28 = (((INT32)(branchInstr & 0x03FFFFFF)) << 6) >> 4;
2622 //*****************************************************************************
2623 // Extract the PC-Relative offset from an adrp instruction
2624 //*****************************************************************************
2625 INT32 GetArm64Rel21(UINT32 * pCode)
2627 LIMITED_METHOD_CONTRACT;
2629 UINT32 addInstr = *pCode;
2631 // 23-5 bits for the high part. Shift it by 5.
2632 INT32 immhi = (((INT32)(addInstr & 0xFFFFE0))) >> 5;
2633 // 30,29 bits for the lower part. Shift it by 29.
2634 INT32 immlo = ((INT32)(addInstr & 0x60000000)) >> 29;
2637 INT32 imm21 = (immhi << 2) | immlo;
2642 //*****************************************************************************
2643 // Extract the PC-Relative offset from an add instruction
2644 //*****************************************************************************
2645 INT32 GetArm64Rel12(UINT32 * pCode)
2647 LIMITED_METHOD_CONTRACT;
2649 UINT32 addInstr = *pCode;
2651 // 21-10 contains value. Mask 12 bits and shift by 10 bits.
2652 INT32 imm12 = (INT32)(addInstr & 0x003FFC00) >> 10;
2657 //*****************************************************************************
2658 // Deposit the PC-Relative offset 'imm28' into a b or bl instruction
2659 //*****************************************************************************
2660 void PutArm64Rel28(UINT32 * pCode, INT32 imm28)
2662 LIMITED_METHOD_CONTRACT;
2664 // Verify that we got a valid offset
2665 _ASSERTE(FitsInRel28(imm28));
2666 _ASSERTE((imm28 & 0x3) == 0); // the low two bits must be zero
2668 UINT32 branchInstr = *pCode;
2670 branchInstr &= 0xFC000000; // keep bits 31-26
2672 // Assemble the pc-relative delta 'imm28' into the branch instruction
2673 branchInstr |= ((imm28 >> 2) & 0x03FFFFFF);
2675 *pCode = branchInstr; // write the assembled instruction
2677 _ASSERTE(GetArm64Rel28(pCode) == imm28);
2680 //*****************************************************************************
2681 // Deposit the PC-Relative offset 'imm21' into an adrp instruction
2682 //*****************************************************************************
2683 void PutArm64Rel21(UINT32 * pCode, INT32 imm21)
2685 LIMITED_METHOD_CONTRACT;
2687 // Verify that we got a valid offset
2688 _ASSERTE(FitsInRel21(imm21));
2690 UINT32 adrpInstr = *pCode;
2691 // Check adrp opcode 1ii1 0000 ...
2692 _ASSERTE((adrpInstr & 0x9F000000) == 0x90000000);
2694 adrpInstr &= 0x9F00001F; // keep bits 31, 28-24, 4-0.
2695 INT32 immlo = imm21 & 0x03; // Extract low 2 bits which will occupy 30-29 bits.
2696 INT32 immhi = (imm21 & 0x1FFFFC) >> 2; // Extract high 19 bits which will occupy 23-5 bits.
2697 adrpInstr |= ((immlo << 29) | (immhi << 5));
2699 *pCode = adrpInstr; // write the assembled instruction
2701 _ASSERTE(GetArm64Rel21(pCode) == imm21);
2704 //*****************************************************************************
2705 // Deposit the PC-Relative offset 'imm12' into an add instruction
2706 //*****************************************************************************
2707 void PutArm64Rel12(UINT32 * pCode, INT32 imm12)
2709 LIMITED_METHOD_CONTRACT;
2711 // Verify that we got a valid offset
2712 _ASSERTE(FitsInRel12(imm12));
2714 UINT32 addInstr = *pCode;
2715 // Check add opcode 1001 0001 00...
2716 _ASSERTE((addInstr & 0xFFC00000) == 0x91000000);
2718 addInstr &= 0xFFC003FF; // keep bits 31-22, 9-0
2719 addInstr |= (imm12 << 10); // Occupy 21-10.
2721 *pCode = addInstr; // write the assembled instruction
2723 _ASSERTE(GetArm64Rel12(pCode) == imm12);
2726 //---------------------------------------------------------------------
2727 // Splits a command line into argc/argv lists, using the VC7 parsing rules.
2729 // This functions interface mimics the CommandLineToArgvW api.
2731 // If function fails, returns NULL.
2733 // If function suceeds, call delete [] on return pointer when done.
2735 //---------------------------------------------------------------------
2736 // NOTE: Implementation-wise, once every few years it would be a good idea to
2737 // compare this code with the C runtime library's parse_cmdline method,
2738 // which is in vctools\crt\crtw32\startup\stdargv.c. (Note we don't
2739 // support wild cards, and we use Unicode characters exclusively.)
2740 // We are up to date as of ~6/2005.
2741 //---------------------------------------------------------------------
2742 LPWSTR *SegmentCommandLine(LPCWSTR lpCmdLine, DWORD *pNumArgs)
2744 STATIC_CONTRACT_NOTHROW;
2745 STATIC_CONTRACT_GC_NOTRIGGER;
2746 STATIC_CONTRACT_FAULT;
2751 int nch = (int)wcslen(lpCmdLine);
2753 // Calculate the worstcase storage requirement. (One pointer for
2754 // each argument, plus storage for the arguments themselves.)
2755 int cbAlloc = (nch+1)*sizeof(LPWSTR) + sizeof(WCHAR)*(nch + 1);
2756 LPWSTR pAlloc = new (nothrow) WCHAR[cbAlloc / sizeof(WCHAR)];
2760 LPWSTR *argv = (LPWSTR*) pAlloc; // We store the argv pointers in the first halt
2761 LPWSTR pdst = (LPWSTR)( ((BYTE*)pAlloc) + sizeof(LPWSTR)*(nch+1) ); // A running pointer to second half to store arguments
2762 LPCWSTR psrc = lpCmdLine;
2768 // First, parse the program name (argv[0]). Argv[0] is parsed under
2769 // special rules. Anything up to the first whitespace outside a quoted
2770 // subtring is accepted. Backslashes are treated as normal characters.
2771 argv[ (*pNumArgs)++ ] = pdst;
2774 if (*psrc == W('"') )
2784 } while ( (c != W('\0') && (inquote || (c != W(' ') && c != W('\t')))) );
2786 if ( c == W('\0') ) {
2789 *(pdst-1) = W('\0');
2796 /* loop on each argument */
2801 while (*psrc == W(' ') || *psrc == W('\t'))
2807 if (*psrc == W('\0'))
2808 break; /* end of args */
2810 /* scan an argument */
2811 argv[ (*pNumArgs)++ ] = pdst;
2813 /* loop through scanning one argument */
2817 /* Rules: 2N backslashes + " ==> N backslashes and begin/end quote
2818 2N+1 backslashes + " ==> N backslashes + literal "
2819 N backslashes ==> N backslashes */
2821 while (*psrc == W('\\'))
2823 /* count number of backslashes for use below */
2827 if (*psrc == W('"'))
2829 /* if 2N backslashes before, start/end quote, otherwise
2831 if (numslash % 2 == 0)
2833 if (inquote && psrc[1] == W('"'))
2835 psrc++; /* Double quote inside quoted string */
2839 /* skip first quote char and copy second */
2840 copychar = 0; /* don't copy quote */
2844 numslash /= 2; /* divide numslash by two */
2853 /* if at end of arg, break loop */
2854 if (*psrc == W('\0') || (!inquote && (*psrc == W(' ') || *psrc == W('\t'))))
2857 /* copy character into argument */
2865 /* null-terminate the argument */
2867 *pdst++ = W('\0'); /* terminate string */
2870 /* We put one last argument in -- a null ptr */
2871 argv[ (*pNumArgs) ] = NULL;
2873 // If we hit this assert, we overwrote our destination buffer.
2874 // Since we're supposed to allocate for the worst
2875 // case, either the parsing rules have changed or our worse case
2876 // formula is wrong.
2877 _ASSERTE((BYTE*)pdst <= (BYTE*)pAlloc + cbAlloc);
2881 Volatile<PVOID> ForbidCallsIntoHostOnThisThread::s_pvOwningFiber = NULL;
2883 //======================================================================
2884 // This function returns true, if it can determine that the instruction pointer
2885 // refers to a code address that belongs in the range of the given image.
2886 // <TODO>@TODO: Merge with IsIPInModule from vm\util.hpp</TODO>
2888 BOOL IsIPInModule(HMODULE_TGT hModule, PCODE ip)
2890 STATIC_CONTRACT_LEAF;
2895 HMODULE_TGT hModule;
2899 param.hModule = hModule;
2903 // UNIXTODO: implement a proper version for PAL
2905 PAL_TRY(Param *, pParam, ¶m)
2907 PTR_BYTE pBase = dac_cast<PTR_BYTE>(pParam->hModule);
2909 PTR_IMAGE_DOS_HEADER pDOS = NULL;
2910 PTR_IMAGE_NT_HEADERS pNT = NULL;
2915 // First, must validate the format of the PE headers to make sure that
2916 // the fields we're interested in using exist in the image.
2919 // Validate the DOS header.
2920 pDOS = PTR_IMAGE_DOS_HEADER(pBase);
2921 if (pDOS->e_magic != VAL16(IMAGE_DOS_SIGNATURE) ||
2922 pDOS->e_lfanew == 0)
2927 // Validate the NT header
2928 pNT = PTR_IMAGE_NT_HEADERS(pBase + VAL32(pDOS->e_lfanew));
2930 if (pNT->Signature != VAL32(IMAGE_NT_SIGNATURE))
2935 // Validate that the optional header is large enough to contain the fields
2936 // we're interested, namely IMAGE_OPTIONAL_HEADER::SizeOfImage. The reason
2937 // we don't just check that SizeOfOptionalHeader == IMAGE_SIZEOF_NT_OPTIONAL_HEADER
2938 // is due to VSW443590, which states that the extensibility of this structure
2939 // is such that it is possible to include only a portion of the optional header.
2940 cbOptHdr = pNT->FileHeader.SizeOfOptionalHeader;
2942 // Check that the magic field is contained by the optional header and set to the correct value.
2943 if (cbOptHdr < (offsetof(IMAGE_OPTIONAL_HEADER, Magic) + sizeofmember(IMAGE_OPTIONAL_HEADER, Magic)) ||
2944 pNT->OptionalHeader.Magic != VAL16(IMAGE_NT_OPTIONAL_HDR_MAGIC))
2949 // Check that the SizeOfImage is contained by the optional header.
2950 if (cbOptHdr < (offsetof(IMAGE_OPTIONAL_HEADER, SizeOfImage) + sizeofmember(IMAGE_OPTIONAL_HEADER, SizeOfImage)))
2959 baseAddr = dac_cast<PCODE>(pBase);
2960 if ((pParam->ip < baseAddr) || (pParam->ip >= (baseAddr + VAL32(pNT->OptionalHeader.SizeOfImage))))
2965 pParam->fRet = TRUE;
2969 PAL_EXCEPT (EXCEPTION_EXECUTE_HANDLER)
2973 #endif // !FEATURE_PAL
2978 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
2980 // To include definition of EXCEPTION_SOFTSO
2981 #include "corexcep.h"
2983 // These functions provide limited support for corrupting exceptions
2984 // outside the VM folder. Its limited since we don't have access to the
2987 // These functions are also wrapped by the corresponding CEHelper
2988 // methods in excep.cpp.
2990 // Given an exception code, this method returns a BOOL to indicate if the
2991 // code belongs to a corrupting exception or not.
2992 BOOL IsProcessCorruptedStateException(DWORD dwExceptionCode, BOOL fCheckForSO /*=TRUE*/)
2994 LIMITED_METHOD_CONTRACT;
2996 // By default, assume its not corrupting
2997 BOOL fIsCorruptedStateException = FALSE;
2999 if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_legacyCorruptedStateExceptionsPolicy) == 1)
3001 return fIsCorruptedStateException;
3004 // If we have been asked not to include SO in the CSE check
3005 // and the code represent SO, then exit now.
3006 if ((fCheckForSO == FALSE) && (dwExceptionCode == STATUS_STACK_OVERFLOW))
3008 return fIsCorruptedStateException;
3011 switch(dwExceptionCode)
3013 case STATUS_ACCESS_VIOLATION:
3014 case STATUS_STACK_OVERFLOW:
3015 case EXCEPTION_ILLEGAL_INSTRUCTION:
3016 case EXCEPTION_IN_PAGE_ERROR:
3017 case EXCEPTION_INVALID_DISPOSITION:
3018 case EXCEPTION_NONCONTINUABLE_EXCEPTION:
3019 case EXCEPTION_PRIV_INSTRUCTION:
3020 case STATUS_UNWIND_CONSOLIDATE:
3021 fIsCorruptedStateException = TRUE;
3025 return fIsCorruptedStateException;
3028 #endif // FEATURE_CORRUPTING_EXCEPTIONS
3030 void EnableTerminationOnHeapCorruption()
3032 HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
3039 static BOOL g_fLocalAppDataDirectoryInitted = FALSE;
3040 static WCHAR *g_wszLocalAppDataDirectory = NULL;
3042 // This api returns a pointer to a null-terminated string that contains the local appdata directory
3043 // or it returns NULL in the case that the directory could not be found. The return value from this function
3044 // is not actually checked for existence.
3045 HRESULT GetLocalAppDataDirectory(LPCWSTR *ppwzLocalAppDataDirectory)
3053 *ppwzLocalAppDataDirectory = NULL;
3057 if (!g_fLocalAppDataDirectoryInitted)
3059 WCHAR *wszLocalAppData = NULL;
3062 cCharsNeeded = GetEnvironmentVariableW(W("LOCALAPPDATA"), NULL, 0);
3064 if ((cCharsNeeded != 0) && (cCharsNeeded < MAX_LONGPATH))
3066 wszLocalAppData = new WCHAR[cCharsNeeded];
3067 cCharsNeeded = GetEnvironmentVariableW(W("LOCALAPPDATA"), wszLocalAppData, cCharsNeeded);
3068 if (cCharsNeeded != 0)
3070 // We've collected the appropriate app data directory into a local. Now publish it.
3071 if (InterlockedCompareExchangeT(&g_wszLocalAppDataDirectory, wszLocalAppData, NULL) == NULL)
3073 // This variable doesn't need to be freed, as it has been stored in the global
3074 wszLocalAppData = NULL;
3079 g_fLocalAppDataDirectoryInitted = TRUE;
3080 delete[] wszLocalAppData;
3083 EX_CATCH_HRESULT(hr);
3086 *ppwzLocalAppDataDirectory = g_wszLocalAppDataDirectory;
3091 HRESULT SetLocalAppDataDirectory(LPCWSTR pwzLocalAppDataDirectory)
3098 if (pwzLocalAppDataDirectory == NULL || *pwzLocalAppDataDirectory == W('\0'))
3099 return E_INVALIDARG;
3101 if (g_fLocalAppDataDirectoryInitted)
3102 return E_UNEXPECTED;
3108 size_t size = wcslen(pwzLocalAppDataDirectory) + 1;
3109 WCHAR *wszLocalAppData = new WCHAR[size];
3110 wcscpy_s(wszLocalAppData, size, pwzLocalAppDataDirectory);
3112 // We've collected the appropriate app data directory into a local. Now publish it.
3113 if (InterlockedCompareExchangeT(&g_wszLocalAppDataDirectory, wszLocalAppData, NULL) != NULL)
3115 // Someone else already set LocalAppData. Free our copy and return an error.
3116 delete[] wszLocalAppData;
3120 g_fLocalAppDataDirectoryInitted = TRUE;
3122 EX_CATCH_HRESULT(hr);
3128 // Struct used to scope suspension of client impersonation for the current thread.
3129 // https://docs.microsoft.com/en-us/windows/desktop/secauthz/client-impersonation
3130 class SuspendImpersonation
3133 SuspendImpersonation()
3136 // The approach used here matches what is used elsewhere in CLR (RevertIfImpersonated).
3137 // In general, OpenThreadToken fails with ERROR_NO_TOKEN if impersonation is not active,
3138 // fails with ERROR_CANT_OPEN_ANONYMOUS if anonymous impersonation is active, and otherwise
3139 // succeeds and returns the active impersonation token.
3140 BOOL res = ::OpenThreadToken(::GetCurrentThread(), TOKEN_IMPERSONATE, /* OpenAsSelf */ TRUE, &_token);
3151 ~SuspendImpersonation()
3153 if (_token != nullptr)
3154 ::SetThreadToken(nullptr, _token);
3158 HandleHolder _token;
3161 struct ProcessIntegrityResult
3167 HRESULT RecordAndReturnError(HRESULT hr)
3174 // The system calls in this code can fail if run with reduced privileges.
3175 // It is the caller's responsibility to choose an appropriate default in the event
3176 // that this function fails to retrieve the current process integrity.
3177 HRESULT GetCurrentProcessIntegrity(DWORD *integrity)
3179 static ProcessIntegrityResult s_Result = { FALSE, 0, S_FALSE };
3181 if (FALSE != InterlockedCompareExchangeT(&s_Result.Success, FALSE, FALSE))
3183 *integrity = s_Result.Integrity;
3187 // Temporarily suspend impersonation (if possible) while computing the integrity level.
3188 // If impersonation is active, the OpenProcessToken call below will check the impersonation
3189 // token against the process token ACL, and will generally fail with ERROR_ACCESS_DENIED if
3190 // the impersonation token is less privileged than this process's primary token.
3191 Clr::Util::SuspendImpersonation si;
3193 HandleHolder hToken;
3194 if(!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken))
3195 return s_Result.RecordAndReturnError(HRESULT_FROM_GetLastError());
3198 DWORD err = ERROR_SUCCESS;
3199 if(!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)TokenIntegrityLevel, nullptr, 0, &dwSize))
3200 err = GetLastError();
3202 // We need to make sure that GetTokenInformation failed in a predictable manner so we know that
3203 // dwSize has the correct buffer size in it.
3204 if (err != ERROR_INSUFFICIENT_BUFFER || dwSize == 0)
3205 return s_Result.RecordAndReturnError((err == ERROR_SUCCESS) ? E_FAIL : HRESULT_FROM_WIN32(err));
3207 NewArrayHolder<BYTE> pLabel = new (nothrow) BYTE[dwSize];
3209 return s_Result.RecordAndReturnError(E_OUTOFMEMORY);
3211 if(!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)TokenIntegrityLevel, pLabel, dwSize, &dwSize))
3212 return s_Result.RecordAndReturnError(HRESULT_FROM_GetLastError());
3214 TOKEN_MANDATORY_LABEL *ptml = (TOKEN_MANDATORY_LABEL *)(void*)pLabel;
3215 PSID psidIntegrityLevelLabel = ptml->Label.Sid;
3217 s_Result.Integrity = *GetSidSubAuthority(psidIntegrityLevelLabel, (*GetSidSubAuthorityCount(psidIntegrityLevelLabel) - 1));
3218 *integrity = s_Result.Integrity;
3219 InterlockedExchangeT(&s_Result.Success, TRUE);
3225 HRESULT ReadStringValue(HKEY hKey, LPCWSTR wszSubKeyName, LPCWSTR wszValueName, SString & ssValue)
3227 STANDARD_VM_CONTRACT;
3231 return E_INVALIDARG;
3234 RegKeyHolder hTargetKey;
3235 if (wszSubKeyName == NULL || *wszSubKeyName == W('\0'))
3236 { // No subkey was requested, use hKey as the resolved key.
3238 hTargetKey.SuppressRelease();
3241 { // Try to open the specified subkey.
3242 if (WszRegOpenKeyEx(hKey, wszSubKeyName, 0, KEY_READ, &hTargetKey) != ERROR_SUCCESS)
3243 return REGDB_E_CLASSNOTREG;
3248 if ((WszRegQueryValueEx(hTargetKey, wszValueName, 0, &type, 0, &size) == ERROR_SUCCESS) &&
3249 type == REG_SZ && size > 0)
3251 LPWSTR wszValueBuf = ssValue.OpenUnicodeBuffer(static_cast<COUNT_T>((size / sizeof(WCHAR)) - 1));
3252 LONG lResult = WszRegQueryValueEx(
3257 reinterpret_cast<LPBYTE>(wszValueBuf),
3260 _ASSERTE(lResult == ERROR_SUCCESS);
3261 if (lResult == ERROR_SUCCESS)
3263 // Can't count on the returned size being accurate - I've seen at least
3264 // one string with an extra NULL at the end that will cause the resulting
3265 // SString to count the extra NULL as part of the string. An extra
3266 // terminating NULL is not a legitimate scenario for REG_SZ - this must
3267 // be done using REG_MULTI_SZ - however this was tolerated in the
3268 // past and so it would be a breaking change to stop doing so.
3269 _ASSERTE(wcslen(wszValueBuf) <= (size / sizeof(WCHAR)) - 1);
3270 ssValue.CloseBuffer((COUNT_T)wcsnlen(wszValueBuf, (size_t)size));
3274 ssValue.CloseBuffer(0);
3275 return HRESULT_FROM_WIN32(lResult);
3282 return REGDB_E_KEYMISSING;
3286 HRESULT ReadStringValue(HKEY hKey, LPCWSTR wszSubKey, LPCWSTR wszName, __deref_out __deref_out_z LPWSTR* pwszValue)
3296 StackSString ssValue;
3297 if (SUCCEEDED(hr = ReadStringValue(hKey, wszSubKey, wszName, ssValue)))
3299 *pwszValue = new WCHAR[ssValue.GetCount() + 1];
3300 wcscpy_s(*pwszValue, ssValue.GetCount() + 1, ssValue.GetUnicode());
3303 EX_CATCH_HRESULT(hr);
3312 __success(return == S_OK)
3314 HRESULT FindSubKeyDefaultValueForCLSID(REFCLSID rclsid, LPCWSTR wszSubKeyName, SString & ssValue)
3316 STANDARD_VM_CONTRACT;
3319 if (GuidToLPWSTR(rclsid, wszClsid, NumItems(wszClsid)) == 0)
3320 return E_UNEXPECTED;
3322 StackSString ssKeyName;
3323 ssKeyName.Append(SL(W("CLSID\\")));
3324 ssKeyName.Append(wszClsid);
3325 ssKeyName.Append(SL(W("\\")));
3326 ssKeyName.Append(wszSubKeyName);
3328 // Query HKCR first to retain backwards compat with previous implementation where HKCR was only queried.
3329 // This is being done due to registry caching. This value will be used if the process integrity is medium or less.
3330 HRESULT hkcrResult = Clr::Util::Reg::ReadStringValue(HKEY_CLASSES_ROOT, ssKeyName.GetUnicode(), nullptr, ssValue);
3332 // HKCR is a virtualized registry hive that weaves together HKCU\Software\Classes and HKLM\Software\Classes
3333 // Processes with high integrity or greater should only read from HKLM to avoid being hijacked by medium
3334 // integrity processes writing to HKCU.
3335 DWORD integrity = SECURITY_MANDATORY_PROTECTED_PROCESS_RID;
3336 HRESULT hr = Clr::Util::GetCurrentProcessIntegrity(&integrity);
3339 // In the event that we are unable to get the current process integrity,
3340 // we assume that this process is running in an elevated state.
3341 // GetCurrentProcessIntegrity may fail if the process has insufficient rights to get the integrity level
3342 integrity = SECURITY_MANDATORY_PROTECTED_PROCESS_RID;
3345 if (integrity > SECURITY_MANDATORY_MEDIUM_RID)
3347 Clr::Util::SuspendImpersonation si;
3349 // Clear the previous HKCR queried value
3352 // Force to use HKLM
3353 StackSString ssHklmKeyName(SL(W("SOFTWARE\\Classes\\")));
3354 ssHklmKeyName.Append(ssKeyName);
3355 return Clr::Util::Reg::ReadStringValue(HKEY_LOCAL_MACHINE, ssHklmKeyName.GetUnicode(), nullptr, ssValue);
3362 HRESULT FindInprocServer32UsingCLSID(REFCLSID rclsid, SString & ssInprocServer32Name)
3364 WRAPPER_NO_CONTRACT;
3365 return __imp::FindSubKeyDefaultValueForCLSID(rclsid, W("InprocServer32"), ssInprocServer32Name);
3368 #endif // FEATURE_PAL
3372 void GetModuleFileName(
3374 SString & ssFileName,
3375 bool fAllowLongFileNames)
3377 STANDARD_VM_CONTRACT;
3379 // Try to use what the SString already has allocated. If it does not have anything allocated
3380 // or it has < 20 characters allocated, then bump the size requested to _MAX_PATH.
3382 DWORD dwResult = WszGetModuleFileName(hModule, ssFileName);
3386 ThrowHR(HRESULT_FROM_GetLastError());
3388 _ASSERTE(dwResult != 0 );
3391 // Returns heap-allocated string in *pwszFileName
3392 HRESULT GetModuleFileName(
3394 __deref_out_z LPWSTR * pwszFileName,
3395 bool fAllowLongFileNames)
3400 PRECONDITION(CheckPointer(pwszFileName));
3406 InlineSString<_MAX_PATH> ssFileName;
3407 GetModuleFileName(hModule, ssFileName);
3408 *pwszFileName = DuplicateStringThrowing(ssFileName.GetUnicode());
3410 EX_CATCH_HRESULT(hr);
3415 void GetFullPathName(
3416 SString const & ssFileName,
3417 SString & ssPathName,
3418 DWORD * pdwFilePartIdx,
3419 bool fAllowLongFileNames)
3421 STANDARD_VM_CONTRACT;
3423 // Get the required buffer length (including terminating NULL).
3424 DWORD dwLengthRequired = WszGetFullPathName(ssFileName.GetUnicode(), 0, NULL, NULL);
3426 if (dwLengthRequired == 0)
3427 ThrowHR(HRESULT_FROM_GetLastError());
3429 LPWSTR wszPathName = ssPathName.OpenUnicodeBuffer(dwLengthRequired - 1);
3430 LPWSTR wszFileName = NULL;
3431 DWORD dwLengthWritten = WszGetFullPathName(
3432 ssFileName.GetUnicode(),
3437 // Calculate the index while the buffer is open and the string pointer is stable.
3438 if (dwLengthWritten != 0 && dwLengthWritten < dwLengthRequired && pdwFilePartIdx != NULL)
3439 *pdwFilePartIdx = static_cast<DWORD>(wszFileName - wszPathName);
3441 ssPathName.CloseBuffer(dwLengthWritten < dwLengthRequired ? dwLengthWritten : 0);
3443 if (dwLengthRequired == 0)
3444 ThrowHR(HRESULT_FROM_GetLastError());
3446 // Overly defensive? Perhaps.
3447 if (!(dwLengthWritten < dwLengthRequired))
3448 ThrowHR(E_UNEXPECTED);
3450 } // namespace Win32