3 Copyright (c) Scott Gasch
11 Windows(R) operating system dependent code.
19 $Id: win32.c 345 2007-12-02 22:56:42Z scott $
24 #define _WIN32_WINNT 0x403
26 #pragma warning(disable:4142) // benign redefinition of type
28 #pragma warning(default:4142)
30 #define SYS_MAX_HEAP_ALLOC_SIZE_BYTES 0xfff
35 CRITICAL_SECTION g_SystemLock;
36 #define LOCK_SYSTEM EnterCriticalSection(&g_SystemLock)
37 #define UNLOCK_SYSTEM LeaveCriticalSection(&g_SystemLock)
42 typedef struct _SYSTEM_THREAD_TABLE_ENTRY
48 THREAD_ENTRYPOINT *pEntry;
52 SYSTEM_THREAD_TABLE_ENTRY;
53 DLIST_ENTRY g_SystemThreadList;
56 // Global buffer to hold a bunch of system dependent numbers / settings
58 typedef struct _SYS_INFORMATION_BUFFER
65 SYS_INFORMATION_BUFFER;
66 SYS_INFORMATION_BUFFER g_SystemInfoBuffer;
69 // The frequency and type of our TimeStamp timer
71 double g_dTimeStampFrequency = 0.0;
72 ULONG g_uTimeStampType = 0;
73 ULONG g_uMaxHeapAllocationSize = 0;
76 // A history of allocations
79 ULONG g_uTotalAlloced = 0;
81 #define ALLOC_HASH_SIZE 0x4000
83 typedef struct _ALLOC_RECORD
88 ALLOC_RECORD g_AllocHash[ALLOC_HASH_SIZE];
90 #define PTR_TO_ALLOC_HASH(x) ((((ULONG_PTR)(x)) >> 3) & (ALLOC_HASH_SIZE - 1))
93 GetHeapMemoryUsage(void)
95 Trace("There are now %u bytes outstanding.\n", g_uTotalAlloced);
96 return(g_uTotalAlloced);
100 ReleaseAllocHashEntry(void *p)
102 ULONG u = PTR_TO_ALLOC_HASH(p);
106 while(g_AllocHash[u].p != p)
109 u &= (ALLOC_HASH_SIZE - 1);
112 g_AllocHash[u].p = NULL;
113 g_uTotalAlloced -= g_AllocHash[u].uSize;
116 // Trace("Freed %u bytes at 0x%p.\n", g_AllocHash[u].uSize, p);
120 MarkAllocHashEntry(void *p, ULONG uSize)
122 ULONG u = PTR_TO_ALLOC_HASH(p);
124 if (uSize > (1024 * 1024 * 30)) return;
127 while(g_AllocHash[u].p != NULL)
130 u &= (ALLOC_HASH_SIZE - 1);
132 g_AllocHash[u].p = p;
133 g_AllocHash[u].uSize = uSize;
134 g_uTotalAlloced += uSize;
137 // Trace("Alloc %u bytes at 0x%p.\n", g_AllocHash[u].uSize, p);
143 SystemStrDup(CHAR *p)
148 Local implementation of strdup
160 CHAR *q = SystemAllocateMemory((ULONG)strlen(p) + 1);
162 ASSERT(strlen(p) < (size_t)MAX_ULONG);
169 SystemGetDateString(void)
174 Get the current date as a string.
191 snprintf(buf, ARRAY_LENGTH(buf),
192 "%u.%02u.%02u", st.wYear, st.wMonth, st.wDay);
198 SystemGetTimeString(void)
203 Get the current time as a string.
220 snprintf(buf, ARRAY_LENGTH(buf),
221 "%u:%02u:%02u", st.wHour, st.wMinute, st.wSecond);
227 SystemDeferExecution(ULONG uMs)
232 Sleep for some number of milliseconds.
247 static SYSTEM_THREAD_TABLE_ENTRY *
248 _SystemGetThreadTableEntry(ULONG uThreadHandle)
253 Support code for the thread functionality wrapper. Given a thread
254 "handle" return its thread table struct.
262 SYSTEM_THREAD_TABLE_ENTRY *
267 SYSTEM_THREAD_TABLE_ENTRY *q;
270 p = g_SystemThreadList.pFlink;
271 while(p != &g_SystemThreadList)
273 q = CONTAINING_STRUCT(p, SYSTEM_THREAD_TABLE_ENTRY, links);
274 if (q->uWrapperHandle == uThreadHandle)
287 SystemThreadEntryPoint(VOID *pParam)
292 The real entry point of all new threads. It calls into the user
293 specified entry point.
305 SYSTEM_THREAD_TABLE_ENTRY *p = (SYSTEM_THREAD_TABLE_ENTRY *)pParam;
312 uParam = p->uThreadParam;
313 i = (int)(*(p->pEntry))(uParam);
318 SystemCreateThread(THREAD_ENTRYPOINT *pEntry, ULONG uParam, ULONG *puHandle)
323 Wrapper function to expose create thread functionality.
327 THREAD_ENTRYPOINT *pEntry,
337 SYSTEM_THREAD_TABLE_ENTRY *p;
339 p = HeapAlloc(GetProcessHeap(),
341 sizeof(SYSTEM_THREAD_TABLE_ENTRY));
350 p->uThreadParam = uParam;
351 p->hThread = CreateThread(NULL,
353 SystemThreadEntryPoint,
357 if (NULL == p->hThread)
359 (void)HeapFree(GetProcessHeap(), 0, p);
362 *puHandle = p->uWrapperHandle = p->uTid;
363 InsertHeadList(&g_SystemThreadList, &(p->links));
370 SystemWaitForThreadToExit(ULONG uThreadHandle)
375 Wait for the specified thread to exit.
387 SYSTEM_THREAD_TABLE_ENTRY *q = _SystemGetThreadTableEntry(uThreadHandle);
389 if (WAIT_OBJECT_0 != WaitForSingleObject(q->hThread, INFINITE))
399 SystemGetThreadExitCode(ULONG uThreadHandle, ULONG *puCode)
404 Get the specified thread's exit value.
417 SYSTEM_THREAD_TABLE_ENTRY *q = _SystemGetThreadTableEntry(uThreadHandle);
420 if (FALSE == GetExitCodeThread(q->hThread, &uCode))
424 if (STILL_ACTIVE == uCode)
434 SystemDeleteThread(ULONG uThreadHandle)
451 SYSTEM_THREAD_TABLE_ENTRY *q = _SystemGetThreadTableEntry(uThreadHandle);
456 if (FALSE == SystemGetThreadExitCode(uThreadHandle, &u))
458 SystemWaitForThreadToExit(uThreadHandle);
460 CloseHandle(q->hThread);
462 RemoveEntryList(&(q->links));
463 HeapFree(GetProcessHeap(), 0, q);
472 _SystemPopulateSystemInformationBuffer(void)
477 Populates global structure SYS_INFORMATION_BUFFER. This code should
478 be called only once from SystemDependentInitialization. It should
479 definitely never be called by more than one thread at a time.
492 static FLAG fInit = FALSE;
496 Bug("_SystemPopulateSystemInformationBuffer: This code should "
497 "not be called more than once!\n");
504 // Make some calls to populate the global buffer
506 GetSystemInfo(&(g_SystemInfoBuffer.si));
509 // Note: This API is not present on Win9x
511 g_SystemInfoBuffer.vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
512 if (FALSE == GetVersionEx((OSVERSIONINFO *)&(g_SystemInfoBuffer.vi)))
514 Bug("_SystemPopulateSystemInformationBuffer: GetVersionEx "
515 "call failed, error=%u.\n", GetLastError());
519 GlobalMemoryStatus(&(g_SystemInfoBuffer.ms));
521 g_SystemInfoBuffer.fPopulated = TRUE;
526 _SystemEnablePrivilege(TCHAR *szPrivilegeName,
532 This code is called by SystemDependentInitialization to add some
533 privileges to our process' security token.
537 TCHAR *szPrivilegeName,
546 FLAG fMyRetVal = FALSE; // our return value
547 HANDLE hToken = INVALID_HANDLE_VALUE; // handle to process token
548 PTOKEN_PRIVILEGES pNewPrivileges; // privs to enable/disable
549 LUID Luid; // priv as a LUID
550 BYTE buf[sizeof(TOKEN_PRIVILEGES)+
551 ((1 - ANYSIZE_ARRAY) * sizeof(LUID_AND_ATTRIBUTES))];
553 pNewPrivileges = (PTOKEN_PRIVILEGES)buf;
556 // Try to open the thread's security token first.
558 if (FALSE == OpenThreadToken(GetCurrentThread(),
559 TOKEN_ADJUST_PRIVILEGES,
564 // Well that failed. If it is because there is no thread token then
565 // try to open the process' security token.
567 if (ERROR_NO_TOKEN == GetLastError())
569 if (FALSE == OpenProcessToken(GetCurrentProcess(),
570 TOKEN_ADJUST_PRIVILEGES,
574 // Can't open the process token either. Fail.
582 // Failed to open the thread token for some other reason. Fail.
589 // Convert priv name to an LUID.
591 if (FALSE == LookupPrivilegeValue(NULL,
599 // Construct new data struct to enable / disable the privi.
601 pNewPrivileges->PrivilegeCount = 1;
602 pNewPrivileges->Privileges[0].Luid = Luid;
603 pNewPrivileges->Privileges[0].Attributes =
604 fEnable ? SE_PRIVILEGE_ENABLED : 0;
607 // Adjust the privileges on the token.
609 fMyRetVal = AdjustTokenPrivileges(hToken,
615 if (TRUE == fMyRetVal)
618 // This boneheaded API returns TRUE and then expects the
619 // caller to test GetLastError to see if it really worked.
621 if (ERROR_SUCCESS != GetLastError())
628 if (INVALID_HANDLE_VALUE != hToken)
637 SystemDebugBreakpoint(void)
642 A hardcoded breakpoint.
657 #define SYS_HIGH_PERFORMANCE_TIMER (1)
658 #define SYS_GET_TICK_COUNT (2)
659 #define SYS_MS_PER_SECOND (1000)
662 SystemTimeStamp(void)
667 Return a timestamp to the caller. The precision of this timestamp
668 is based on whether the machine supports a high resolution timer.
669 If not, this code rolls back to use GetTickCount().
671 This code relies on some initialization code in SystemDependent-
672 Initialization. Once that code has executed, this code is thread
688 switch(g_uTimeStampType)
690 case SYS_HIGH_PERFORMANCE_TIMER:
691 QueryPerformanceCounter((LARGE_INTEGER *)&u64);
692 dRetVal = u64 * g_dTimeStampFrequency;
694 case SYS_GET_TICK_COUNT:
695 dRetVal = GetTickCount() / SYS_MS_PER_SECOND;
699 Bug("SystemTimeStamp: You have to call SystemDependent"
700 "Initialization before you can call this code.\n");
709 SystemDoesFileExist(CHAR *szFilename)
714 Determine if a file exists (non-atomically).
726 if (INVALID_FILE_ATTRIBUTES == GetFileAttributesA(szFilename))
728 if (GetLastError() == ERROR_FILE_NOT_FOUND)
738 _SystemIsAdministrator(void)
743 This code is called by SystemDependentInitialization. Its job is to
744 determine whether this process is running with administrative powers.
756 BOOL fIsMember = FALSE; // our return value
757 SID *pAdminSid = NULL; // ptr to admin sid
758 SID_IDENTIFIER_AUTHORITY NtAuthority = // used to create admin sid
759 SECURITY_NT_AUTHORITY;
762 // Allocate and initialize a SID for the admin group.
764 if (FALSE == AllocateAndInitializeSid(&NtAuthority,
766 SECURITY_BUILTIN_DOMAIN_RID,
767 DOMAIN_ALIAS_RID_ADMINS,
775 // SID allocation succeeded, check this thread or process token
776 // for membership in the admin group.
778 if (FALSE == CheckTokenMembership(NULL,
786 if (NULL != pAdminSid)
796 _SystemCallVirtualAlloc(ULONG dwSizeBytes)
801 Wrapper around VirtualAlloc
813 SIZE_T size = dwSizeBytes;
816 pMem = VirtualAlloc(NULL,
818 MEM_RESERVE | MEM_COMMIT,
822 UtilPanic(UNEXPECTED_SYSTEM_CALL_FAILURE,
823 NULL, "VirtualAlloc", GetLastError(), size,
830 _SystemCallVirtualFree(void *pMem)
835 Wrapper around VirtualFree
847 BOOL fRetVal = FALSE;
851 fRetVal = VirtualFree(pMem, 0, MEM_RELEASE);
852 if (FALSE == fRetVal)
854 UtilPanic(UNEXPECTED_SYSTEM_CALL_FAILURE,
855 NULL, "VirtualFree", GetLastError(), pMem,
862 _SystemCallHeapAlloc(ULONG dwSizeBytes)
867 Wrapper around HeapAlloc. Returns a zeroed buffer from the heap.
868 While on the subject of heaps, let me take a moment to extol the
869 value of the Win32 Application Verifier / PageHeap code. This is
870 code that sanity checks the actions of an app. It is especially
871 good at finding heap misuse (double frees, reuse after free,
872 buffer overruns, etc...) It also catches things like
873 CRITICAL_SECTION abuse and HANDLE abuse. To enable it, use
886 void *p = HeapAlloc(GetProcessHeap(),
891 UtilPanic(UNEXPECTED_SYSTEM_CALL_FAILURE,
892 NULL, "HeapAlloc", GetLastError(), dwSizeBytes,
899 _SystemCallHeapFree(void *pMem)
904 Wrapper around HeapFree
916 if (FALSE == HeapFree(GetProcessHeap(),
920 UtilPanic(UNEXPECTED_SYSTEM_CALL_FAILURE,
921 NULL, "HeapFree", GetLastError(), pMem,
927 SystemFreeMemory(void *pMem)
944 _SystemCallHeapFree(pMem);
946 ReleaseAllocHashEntry(pMem);
951 SystemAllocateMemory(ULONG dwSizeBytes)
956 Allocate some memory, either from the heap or from VirtualAlloc
957 depending on the size of the request. The buffer returned by
958 this call is guaranteed to be zeroed out. Buffers allocated by
959 this call should be freed via a call to SystemFreeMemory when
960 they are no longer needed.
964 ULONG dwSizeBytes : size of the buffer needed in bytes
974 p = _SystemCallHeapAlloc(dwSizeBytes);
976 MarkAllocHashEntry(p, dwSizeBytes);
983 SystemAllocateLockedMemory(ULONG dwSizeBytes)
988 Allocate a buffer that is locked in memory (i.e. non-pagable).
1000 void *p = _SystemCallVirtualAlloc(dwSizeBytes);
1004 if (FALSE == VirtualLock(p, dwSizeBytes))
1006 UtilPanic(UNEXPECTED_SYSTEM_CALL_FAILURE,
1007 NULL, "VirtualLock", GetLastError(), p,
1008 __FILE__, __LINE__);
1012 MarkAllocHashEntry(p, dwSizeBytes);
1019 SystemMakeMemoryReadOnly(void *pMemory,
1023 Routine description:
1036 BOOL fRetVal = FALSE;
1039 fRetVal = VirtualProtect(pMemory,
1043 if (FALSE == fRetVal)
1045 UtilPanic(UNEXPECTED_SYSTEM_CALL_FAILURE,
1046 NULL, "VirtualProtect", GetLastError(), pMemory,
1047 __FILE__, __LINE__);
1049 return((FLAG)fRetVal);
1053 SystemMakeMemoryNoAccess(void *pMemory,
1057 Routine description:
1070 BOOL fRetVal = FALSE;
1073 fRetVal = VirtualProtect(pMemory,
1077 if (FALSE == fRetVal)
1079 UtilPanic(UNEXPECTED_SYSTEM_CALL_FAILURE,
1080 NULL, "VirtualProtect", GetLastError(), pMemory,
1081 __FILE__, __LINE__);
1083 return((FLAG)fRetVal);
1088 SystemCopyFile(CHAR *szSource, CHAR *szDest)
1091 Routine description:
1104 return(CopyFileA(szSource, szDest, TRUE));
1109 SystemDeleteFile(CHAR *szFile)
1112 Routine description:
1124 return(DeleteFileA(szFile));
1129 SystemMakeMemoryReadWrite(void *pMemory, ULONG dwSizeBytes)
1132 Routine description:
1145 BOOL fRetVal = FALSE;
1148 fRetVal = VirtualProtect(pMemory,
1152 if (FALSE == fRetVal)
1154 Bug("SystemMakeMemoryReadWrite: VirtualProtect for buffer at %p "
1155 "(size %u) failed, error=%u.\n", pMemory, dwSizeBytes,
1163 SystemReadTimeStampCounter(void)
1166 Routine description:
1182 #elif defined(_AMD64_)
1186 #elif defined(_IA64_)
1188 itc = __getReg(CV_IA64_ApITC);
1191 #error Unknown architecture
1195 #define MAX_LOCKS (8)
1196 typedef struct _WIN32_LOCK_ENTRY
1198 CRITICAL_SECTION lock;
1201 WIN32_LOCK_ENTRY g_rgLockTable[MAX_LOCKS];
1204 SystemCreateLock(void)
1209 for (u = 0; u < MAX_LOCKS; u++)
1211 if (FALSE == g_rgLockTable[u].fInUse)
1213 InitializeCriticalSection(&(g_rgLockTable[u].lock));
1214 g_rgLockTable[u].fInUse = TRUE;
1218 u = (ULONG)-1; // no free slot
1226 SystemDeleteLock(ULONG u)
1229 if ((u < MAX_LOCKS) && (g_rgLockTable[u].fInUse == TRUE))
1231 DeleteCriticalSection(&(g_rgLockTable[u].lock));
1232 g_rgLockTable[u].fInUse = FALSE;
1241 SystemBlockingWaitForLock(ULONG u)
1243 CRITICAL_SECTION *p = NULL;
1245 if ((u < MAX_LOCKS) && (g_rgLockTable[u].fInUse == TRUE))
1247 p = &(g_rgLockTable[u].lock);
1252 EnterCriticalSection(p);
1253 Trace("lock at %p is obtained.\n", p);
1261 SystemReleaseLock(ULONG u)
1263 CRITICAL_SECTION *p = NULL;
1265 if ((u < MAX_LOCKS) && (g_rgLockTable[u].fInUse == TRUE))
1267 p = &(g_rgLockTable[u].lock);
1272 LeaveCriticalSection(p);
1273 Trace("lock at %p is released.\n", p);
1280 #define MAX_SEMS (8)
1281 HANDLE g_rgSemHandles[MAX_SEMS];
1284 SystemCreateSemaphore(ULONG uValue)
1288 for (u = 0; u < MAX_SEMS; u++)
1290 if (g_rgSemHandles[u] == (HANDLE)NULL)
1292 g_rgSemHandles[u] = CreateSemaphore(NULL, uValue, uValue, NULL);
1293 if (NULL == g_rgSemHandles[u])
1308 SystemDeleteSemaphore(ULONG u)
1311 if ((u < MAX_SEMS) && (g_rgSemHandles[u] != (HANDLE)NULL))
1313 (void)CloseHandle(g_rgSemHandles[u]);
1314 g_rgSemHandles[u] = (HANDLE)NULL;
1323 SystemReleaseSemaphoreResource(ULONG u)
1326 if ((u < MAX_SEMS) && (g_rgSemHandles[u] != (HANDLE)NULL))
1328 ReleaseSemaphore(g_rgSemHandles[u], +1, NULL);
1334 SystemObtainSemaphoreResource(ULONG u)
1338 if ((u < MAX_SEMS) && (g_rgSemHandles[u] != (HANDLE)NULL))
1340 h = g_rgSemHandles[u];
1342 (void)WaitForSingleObject(h, INFINITE);
1351 SystemDependentInitialization(void)
1354 Routine description:
1356 This code is called to initialize operating system dependent code.
1357 It is unsynchronized. It should only ever be called once. It
1358 should definitely never be called by two threads at once!
1366 FLAG : TRUE on success, FALSE otherwise
1372 static FLAG fInit = FALSE;
1376 Bug("SystemDependentInitialization code called more than once!\n");
1384 // Initialize the system dependent lock
1388 InitializeCriticalSection(&g_SystemLock);
1390 __except(EXCEPTION_EXECUTE_HANDLER)
1392 Bug("SystemDependentInitialization: InitializeCriticalSection "
1393 "raised an exception, bailing out.\n");
1398 // Clear a pool of handles
1400 memset(g_rgLockTable, 0, sizeof(g_rgLockTable));
1401 memset(g_rgSemHandles, 0, sizeof(g_rgSemHandles));
1404 // Populate global system info buffer
1406 _SystemPopulateSystemInformationBuffer();
1409 // Enable some privileges in this process' security token
1411 if (FALSE == _SystemEnablePrivilege(SE_INC_BASE_PRIORITY_NAME, TRUE))
1413 Trace("NOTICE: Can't enable SE_INC_BASE_PRIORITY_NAME privilege, "
1414 "error=%u\n", GetLastError());
1416 if (FALSE == _SystemEnablePrivilege(SE_LOCK_MEMORY_NAME, TRUE))
1418 Trace("NOTICE: Can't enable SE_LOCK_MEMORY_NAME privilege, "
1419 "error=%u\n", GetLastError());
1423 // Don't let the system hibernate/suspend while the chess program
1426 (void)SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
1429 // Determine the precision of our SystemTimeStamp() code
1431 ASSERT(g_uTimeStampType == 0);
1432 if (FALSE != QueryPerformanceFrequency((LARGE_INTEGER *)&u64))
1434 g_dTimeStampFrequency = 1.0 / u64;
1435 g_uTimeStampType = SYS_HIGH_PERFORMANCE_TIMER;
1439 g_dTimeStampFrequency = 1.0 / 1000.0;
1440 g_uTimeStampType = SYS_GET_TICK_COUNT;
1442 ASSERT(g_dTimeStampFrequency > 0.0);
1444 g_uMaxHeapAllocationSize = 0x1000;
1445 InitializeListHead(&g_SystemThreadList);