(update-workspace required)

found another means of doing startup/shutdown that doesn't require
hooking and allows init callbacks to use CRT functions (avoiding
problems similar to the recent rash of pre-libc bugs). also, callback
registration no longer needs ugly #pragma syntax.

remove 'Detours' (evil and no longer needed)

This was SVN commit r5137.
This commit is contained in:
janwas
2007-06-04 00:00:57 +00:00
parent 92cd6472da
commit db189468a9
20 changed files with 199 additions and 1925 deletions
-3
View File
@@ -71,9 +71,6 @@ extern_lib_defs = {
devil = {
unix_names = { "IL", "ILU" },
},
detours = {
dbg_suffix = "",
},
directx = {
win_names = { "ddraw", },
dbg_suffix = "",
+6 -10
View File
@@ -404,15 +404,14 @@ function setup_all_libs ()
"libjpg",
"dbghelp",
"directx",
"cryptopp",
"detours"
"cryptopp"
}
setup_static_lib_package("lowlevel", source_dirs, extern_libs, {})
sysdep_dirs = {
linux = { "lib/sysdep/unix" },
-- note: RC file must be added to main_exe package.
-- note: don't add "lib/sysdep/win/aken.cpp" because that must be compiled with the DDK.
windows = { "lib/sysdep/win", "lib/sysdep/win/wposix", "lib/sysdep/win/whrt", "lib/sysdep/win/detours" },
windows = { "lib/sysdep/win", "lib/sysdep/win/wposix", "lib/sysdep/win/whrt" },
macosx = { "lib/sysdep/osx", "lib/sysdep/unix" },
}
tinsert(package.files, sourcesfromdirs(source_root, sysdep_dirs[OS]));
@@ -441,8 +440,7 @@ used_extern_libs = {
"boost",
"dbghelp",
"cxxtest",
"directx",
"detours"
"directx"
}
-- Bundles static libs together with main.cpp and builds game executable.
@@ -476,8 +474,8 @@ function setup_main_exe ()
end
package.linkoptions = {
-- required for winit.cpp's init mechanism
"/ENTRY:entry",
-- required since main.cpp uses main instead of WinMain and subsystem=Win32
"/ENTRY:mainCRTStartup",
-- delay loading of various Windows DLLs (not specific to any of the
-- external libraries; those are handled separately)
@@ -801,9 +799,7 @@ function setup_tests()
package_add_extern_libs(used_extern_libs)
if OS == "windows" then
-- required for win.cpp's init mechanism
tinsert(package.linkoptions, "/ENTRY:entry_noSEH")
-- from "lowlevel" static lib; must be added here to be linked in
tinsert(package.files, source_root.."lib/sysdep/win/error_dialog.rc")
-505
View File
@@ -1,505 +0,0 @@
//////////////////////////////////////////////////////////////////////////////
//
// Core Detours Functionality (detours.cpp of detours.lib)
//
// Microsoft Research Detours Package, Version 2.1.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
#include "precompiled.h"
#include "lib/sysdep/win/win.h"
#if (_MSC_VER < 1299)
#pragma warning(disable: 4710)
#endif
//#define DETOUR_DEBUG 1
#define DETOURS_INTERNAL
#include "detours.h"
#if !CPU_IA32
#error "detours currently only supports x86"
#endif
//////////////////////////////////////////////////////////////////////////////
//
static bool detour_is_imported(PBYTE pbCode, PBYTE pbAddress)
{
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery((PVOID)pbCode, &mbi, sizeof(mbi));
__try {
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mbi.AllocationBase;
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
return false;
}
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +
pDosHeader->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
return false;
}
if (pbAddress >= ((PBYTE)pDosHeader +
pNtHeader->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress) &&
pbAddress < ((PBYTE)pDosHeader +
pNtHeader->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress +
pNtHeader->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size)) {
return true;
}
return false;
}
__except(EXCEPTION_EXECUTE_HANDLER) {
return false;
}
}
///////////////////////////////////////////////////////////////////////// X86.
//
#ifdef DETOURS_X86
struct _DETOUR_TRAMPOLINE
{
BYTE rbCode[23]; // target code + jmp to pbRemain
BYTE cbTarget; // size of target code moved.
PBYTE pbRemain; // first instruction after moved code. [free list]
PBYTE pbDetour; // first instruction of detour function.
};
enum {
SIZE_OF_JMP = 5
};
inline PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE pbJmpVal)
{
PBYTE pbJmpSrc = pbCode + 5;
*pbCode++ = 0xE9; // jmp +imm32
*((INT32*&)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc);
return pbCode;
}
inline PBYTE detour_gen_jmp_indirect(PBYTE pbCode, PBYTE *ppbJmpVal)
{
PBYTE pbJmpSrc = pbCode + 6;
*pbCode++ = 0xff; // jmp [+imm32]
*pbCode++ = 0x25;
*((INT32*&)pbCode)++ = (INT32)((PBYTE)ppbJmpVal - pbJmpSrc);
return pbCode;
}
inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)
{
while (pbCode < pbLimit) {
*pbCode++ = 0xcc; // brk;
}
return pbCode;
}
inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
{
if (pbCode == NULL) {
return NULL;
}
if (ppGlobals != NULL) {
*ppGlobals = NULL;
}
if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32]
// Looks like an import alias jump, then get the code it points to.
PBYTE pbTarget = *(PBYTE *)&pbCode[2];
if (detour_is_imported(pbCode, pbTarget)) {
PBYTE pbNew = *(PBYTE *)pbTarget;
DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew));
return pbNew;
}
}
else if (pbCode[0] == 0xeb) { // jmp +imm8
// These just started appearing with CL13.
PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1];
DETOUR_TRACE(("%p->%p: skipped over short jump.\n", pbCode, pbNew));
if (pbNew[0] == 0xe9) { // jmp +imm32
pbCode = pbNew;
pbNew = pbCode + *(INT32 *)&pbCode[1];
DETOUR_TRACE(("%p->%p: skipped over short jump.\n", pbCode, pbNew));
}
return pbNew;
}
return pbCode;
}
inline BOOL detour_does_code_end_function(PBYTE pbCode)
{
if (pbCode[0] == 0xe9 || // jmp +imm32
pbCode[0] == 0xe0 || // jmp eax
pbCode[0] == 0xc2 || // ret +imm8
pbCode[0] == 0xc3 || // ret
pbCode[0] == 0xcc) { // brk
return TRUE;
}
else if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32]
return TRUE;
}
else if ((pbCode[0] == 0x26 || // jmp es:
pbCode[0] == 0x2e || // jmp cs:
pbCode[0] == 0x36 || // jmp ss:
pbCode[0] == 0xe3 || // jmp ds:
pbCode[0] == 0x64 || // jmp fs:
pbCode[0] == 0x65) && // jmp gs:
pbCode[1] == 0xff && // jmp [+imm32]
pbCode[2] == 0x25) {
return TRUE;
}
return FALSE;
}
#endif // DETOURS_X86
//////////////////////////////////////////////// Trampoline Memory Management.
//
struct DETOUR_REGION
{
ULONG dwSignature;
DETOUR_REGION * pNext; // Next region in list of regions.
DETOUR_TRAMPOLINE * pFree; // List of free trampolines in this region.
};
typedef DETOUR_REGION * PDETOUR_REGION;
const ULONG DETOUR_REGION_SIGNATURE = 'Rrtd';
const ULONG DETOUR_REGION_SIZE = 0x10000;
const ULONG DETOUR_TRAMPOLINES_PER_REGION = (DETOUR_REGION_SIZE
/ sizeof(DETOUR_TRAMPOLINE)) - 1;
static PDETOUR_REGION s_pRegions = NULL; // List of all regions.
static PDETOUR_REGION s_pRegion = NULL; // Default region.
static void detour_writable_trampoline_regions()
{
// Mark all of the regions as writable.
for (PDETOUR_REGION pRegion = s_pRegions; pRegion != NULL; pRegion = pRegion->pNext) {
DWORD dwOld;
VirtualProtect(pRegion, DETOUR_REGION_SIZE, PAGE_EXECUTE_READWRITE, &dwOld);
}
}
static void detour_runnable_trampoline_regions()
{
// Mark all of the regions as executable.
for (PDETOUR_REGION pRegion = s_pRegions; pRegion != NULL; pRegion = pRegion->pNext) {
DWORD dwOld;
VirtualProtect(pRegion, DETOUR_REGION_SIZE, PAGE_EXECUTE_READ, &dwOld);
}
}
static PDETOUR_TRAMPOLINE detour_alloc_trampoline(PBYTE pbTarget)
{
// We have to place trampolines within +/- 2GB of target.
// The allocation code assumes that
PDETOUR_TRAMPOLINE pLo = (PDETOUR_TRAMPOLINE)
((pbTarget > (PBYTE)0x7ff80000)
? pbTarget - 0x7ff80000 : (PBYTE)(ULONG_PTR)DETOUR_REGION_SIZE);
PDETOUR_TRAMPOLINE pHi = (PDETOUR_TRAMPOLINE)
((pbTarget < (PBYTE)0xffffffff80000000)
? pbTarget + 0x7ff80000 : (PBYTE)0xfffffffffff80000);
DETOUR_TRACE(("[%p..%p..%p]\n", pLo, pbTarget, pHi));
PDETOUR_TRAMPOLINE pTrampoline = NULL;
// Insure that there is a default region.
if (s_pRegion == NULL && s_pRegions != NULL) {
s_pRegion = s_pRegions;
}
// First check the default region for an valid free block.
if (s_pRegion != NULL && s_pRegion->pFree != NULL &&
s_pRegion->pFree >= pLo && s_pRegion->pFree <= pHi) {
found_region:
pTrampoline = s_pRegion->pFree;
// do a last sanity check on region.
if (pTrampoline < pLo || pTrampoline > pHi) {
return NULL;
}
s_pRegion->pFree = (PDETOUR_TRAMPOLINE)pTrampoline->pbRemain;
memset(pTrampoline, 0xcc, sizeof(*pTrampoline));
return pTrampoline;
}
// Then check the existing regions for a valid free block.
for (s_pRegion = s_pRegions; s_pRegion != NULL; s_pRegion = s_pRegion->pNext) {
if (s_pRegion != NULL && s_pRegion->pFree != NULL &&
s_pRegion->pFree >= pLo && s_pRegion->pFree <= pHi) {
goto found_region;
}
}
// We need to allocate a new region.
// Round pbTarget down to 64K block.
pbTarget = pbTarget - (PtrToUlong(pbTarget) & 0xffff);
// First we search down (within the valid region)
DETOUR_TRACE((" Looking for free region below %p:\n", pbTarget));
PBYTE pbTry;
for (pbTry = pbTarget; pbTry > (PBYTE)pLo;) {
MEMORY_BASIC_INFORMATION mbi;
DETOUR_TRACE((" Try %p\n", pbTry));
if (pbTry >= (PBYTE)(ULONG_PTR)0x70000000 &&
pbTry <= (PBYTE)(ULONG_PTR)0x80000000) {
// Skip region reserved for system DLLs.
pbTry = (PBYTE)(ULONG_PTR)(0x70000000 - DETOUR_REGION_SIZE);
}
if (!VirtualQuery(pbTry, &mbi, sizeof(mbi))) {
break;
}
DETOUR_TRACE((" Try %p => %p..%p %6x\n",
pbTry,
mbi.BaseAddress,
(PBYTE)mbi.BaseAddress + mbi.RegionSize - 1,
mbi.State));
if (mbi.State == MEM_FREE && mbi.RegionSize >= DETOUR_REGION_SIZE) {
s_pRegion = (DETOUR_REGION*)VirtualAlloc(pbTry,
DETOUR_REGION_SIZE,
MEM_COMMIT|MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
if (s_pRegion != NULL) {
alloced_region:
s_pRegion->dwSignature = DETOUR_REGION_SIGNATURE;
s_pRegion->pFree = NULL;
s_pRegion->pNext = s_pRegions;
s_pRegions = s_pRegion;
DETOUR_TRACE((" Allocated region %p..%p\n\n",
s_pRegion, ((PBYTE)s_pRegion) + DETOUR_REGION_SIZE - 1));
// Put everything but the first trampoline on the free list.
PBYTE pFree = NULL;
pTrampoline = ((PDETOUR_TRAMPOLINE)s_pRegion) + 1;
for (int i = DETOUR_TRAMPOLINES_PER_REGION - 1; i > 1; i--) {
pTrampoline[i].pbRemain = pFree;
pFree = (PBYTE)&pTrampoline[i];
}
s_pRegion->pFree = (PDETOUR_TRAMPOLINE)pFree;
goto found_region;
}
else {
DETOUR_TRACE(("Error: %p %d\n", pbTry, GetLastError()));
break;
}
}
pbTry = (PBYTE)mbi.AllocationBase - DETOUR_REGION_SIZE;
}
DETOUR_TRACE((" Looking for free region above %p:\n", pbTarget));
for (pbTry = pbTarget; pbTry < (PBYTE)pHi;) {
MEMORY_BASIC_INFORMATION mbi;
if (pbTry >= (PBYTE)(ULONG_PTR)0x70000000 &&
pbTry <= (PBYTE)(ULONG_PTR)0x80000000) {
// Skip region reserved for system DLLs.
pbTry = (PBYTE)(ULONG_PTR)(0x80000000 + DETOUR_REGION_SIZE);
}
if (!VirtualQuery(pbTry, &mbi, sizeof(mbi))) {
break;
}
DETOUR_TRACE((" Try %p => %p..%p %6x\n",
pbTry,
mbi.BaseAddress,
(PBYTE)mbi.BaseAddress + mbi.RegionSize - 1,
mbi.State));
if (mbi.State == MEM_FREE && mbi.RegionSize >= DETOUR_REGION_SIZE) {
ULONG_PTR extra = ((ULONG_PTR)pbTry) & (DETOUR_REGION_SIZE - 1);
if (extra != 0) {
// WinXP64 returns free areas that aren't REGION aligned to
// 32-bit applications.
ULONG_PTR adjust = DETOUR_REGION_SIZE - extra;
mbi.RegionSize -= adjust;
((PBYTE&)mbi.BaseAddress) += adjust;
DETOUR_TRACE(("--Try %p => %p..%p %6x\n",
pbTry,
mbi.BaseAddress,
(PBYTE)mbi.BaseAddress + mbi.RegionSize - 1,
mbi.State));
pbTry = (PBYTE)mbi.BaseAddress;
}
s_pRegion = (DETOUR_REGION*)VirtualAlloc(pbTry,
DETOUR_REGION_SIZE,
MEM_COMMIT|MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
if (s_pRegion != NULL) {
goto alloced_region;
}
else {
DETOUR_TRACE(("Error: %p %d\n", pbTry, GetLastError()));
}
}
pbTry = (PBYTE)mbi.BaseAddress + mbi.RegionSize;
}
DETOUR_TRACE(("Couldn't find available memory region!\n"));
throw std::bad_alloc("");
}
///////////////////////////////////////////////////////// Transaction Structs.
//
struct DetourOperation
{
DetourOperation * pNext;
PBYTE * ppbPointer;
PBYTE pbTarget;
PDETOUR_TRAMPOLINE pTrampoline;
ULONG dwPerm;
};
static DetourOperation * s_pPendingOperations = NULL;
//////////////////////////////////////////////////////////////////////////////
//
PVOID DetourCodeFromPointer(PVOID pPointer, PVOID *ppGlobals)
{
return detour_skip_jmp((PBYTE)pPointer, ppGlobals);
}
//////////////////////////////////////////////////////////// Transaction APIs.
//
LONG DetourTransactionBegin()
{
// Make sure only one thread can start a transaction.
/// if (InterlockedCompareExchange(&s_nPendingThreadId, (LONG)GetCurrentThreadId(), 0) != 0)
/// return ERROR_INVALID_OPERATION;
s_pPendingOperations = NULL;
// Make sure the trampoline pages are writable.
detour_writable_trampoline_regions();
return NO_ERROR;
}
LONG DetourTransactionCommit()
{
// Common variables.
DetourOperation *o;
// Insert or remove each of the detours.
for (o = s_pPendingOperations; o != NULL; o = o->pNext)
{
#ifdef DETOURS_X86
PBYTE pbCode = detour_gen_jmp_immediate(o->pbTarget, o->pTrampoline->pbDetour);
pbCode = detour_gen_brk(pbCode, o->pTrampoline->pbRemain);
*o->ppbPointer = o->pTrampoline->rbCode;
#endif // DETOURS_X86
}
// Restore all of the page permissions and flush the icache.
HANDLE hProcess = GetCurrentProcess();
for (o = s_pPendingOperations; o != NULL;) {
// We don't care if this fails, because the code is still accessible.
DWORD dwOld;
VirtualProtect(o->pbTarget, o->pTrampoline->cbTarget, o->dwPerm, &dwOld);
FlushInstructionCache(hProcess, o->pbTarget, o->pTrampoline->cbTarget);
DetourOperation *n = o->pNext;
delete o;
o = n;
}
s_pPendingOperations = NULL;
// Make sure the trampoline pages are no longer writable.
detour_runnable_trampoline_regions();
return NO_ERROR;
}
///////////////////////////////////////////////////////////// Transacted APIs.
//
LONG DetourAttach(PVOID *ppPointer, PVOID pDetour)
{
LONG error = NO_ERROR;
if (*ppPointer == NULL) {
error = ERROR_INVALID_HANDLE;
DETOUR_TRACE(("*ppPointer is null (ppPointer=%p)\n", ppPointer));
DETOUR_BREAK();
return error;
}
PBYTE pbTarget = (PBYTE)*ppPointer;
PDETOUR_TRAMPOLINE pTrampoline = NULL;
DetourOperation *o = NULL;
pbTarget = (PBYTE)DetourCodeFromPointer(pbTarget, NULL);
pDetour = DetourCodeFromPointer(pDetour, NULL);
// Don't follow a jump if its destination is the target function.
// This happens when the detour does nothing other than call the target.
if (pDetour == (PVOID)pbTarget)
exit(1);
o = new DetourOperation;
pTrampoline = detour_alloc_trampoline(pbTarget);
// Determine the number of movable target instructions.
PBYTE pbSrc = pbTarget;
LONG cbTarget = 0;
while (cbTarget < SIZE_OF_JMP) {
PBYTE pbOp = pbSrc;
LONG lExtra = 0;
DETOUR_TRACE((" DetourCopyInstructionEx(%p,%p)\n", pTrampoline->rbCode + cbTarget, pbSrc));
pbSrc = (PBYTE)DetourCopyInstructionEx(pTrampoline->rbCode + cbTarget, pbSrc, NULL, &lExtra);
DETOUR_TRACE((" DetourCopyInstructionEx() = %p (%d bytes)\n", pbSrc, (int)(pbSrc - pbOp)));
if (lExtra != 0) {
break; // Abort if offset doesn't fit.
}
cbTarget = (LONG)(pbSrc - pbTarget);
if (detour_does_code_end_function(pbOp)) {
break;
}
}
// Too few instructions.
if (cbTarget < SIZE_OF_JMP)
exit(2);
// Too many instructions.
if (cbTarget > sizeof(pTrampoline->rbCode) - SIZE_OF_JMP)
exit(3);
pTrampoline->pbRemain = pbTarget + cbTarget;
pTrampoline->pbDetour = (PBYTE)pDetour;
pTrampoline->cbTarget = (BYTE)cbTarget;
#ifdef DETOURS_X86
pbSrc = detour_gen_jmp_immediate(pTrampoline->rbCode + cbTarget, pTrampoline->pbRemain);
pbSrc = detour_gen_brk(pbSrc,
pTrampoline->rbCode + sizeof(pTrampoline->rbCode));
#endif // DETOURS_X86
DWORD dwOld = 0;
if (!VirtualProtect(pbTarget, cbTarget, PAGE_EXECUTE_READWRITE, &dwOld))
exit(4);
o->ppbPointer = (PBYTE*)ppPointer;
o->pTrampoline = pTrampoline;
o->pbTarget = pbTarget;
o->dwPerm = dwOld;
o->pNext = s_pPendingOperations;
s_pPendingOperations = o;
return NO_ERROR;
}
-51
View File
@@ -1,51 +0,0 @@
//////////////////////////////////////////////////////////////////////////////
//
// Core Detours Functionality (detours.h of detours.lib)
//
// Microsoft Research Detours Package, Version 2.1.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
#ifndef _DETOURS_H_
#define _DETOURS_H_
#define DETOURS_X86
#define DETOURS_VERSION 20100 // 2.1.0
#define DETOUR_INSTRUCTION_TARGET_NONE ((PVOID)0)
#define DETOUR_INSTRUCTION_TARGET_DYNAMIC ((PVOID)(LONG_PTR)-1)
#define DETOUR_TRAMPOLINE_SIGNATURE 0x21727444 // Dtr!
typedef struct _DETOUR_TRAMPOLINE DETOUR_TRAMPOLINE, *PDETOUR_TRAMPOLINE;
LONG DetourTransactionBegin();
LONG DetourTransactionCommit();
LONG DetourAttach(PVOID *ppPointer, PVOID pDetour);
PVOID WINAPI DetourCopyInstructionEx(PVOID pDst, PVOID pSrc, PVOID *ppTarget, LONG *plExtra);
//////////////////////////////////////////////////////////////////////////////
//
#include <dbghelp.h>
#ifndef DETOUR_TRACE
#if DETOUR_DEBUG
#define DETOUR_TRACE(x) printf x
#define DETOUR_BREAK() DebugBreak()
#include <stdio.h>
#include <limits.h>
#else
#define DETOUR_TRACE(x)
#define DETOUR_BREAK()
#endif
#endif
#endif // _DETOURS_H_
File diff suppressed because it is too large Load Diff
+1 -5
View File
@@ -17,11 +17,7 @@
#include "wutil.h"
#include "winit.h"
#pragma SECTION_INIT(2) // early; whrt depends on us
WINIT_REGISTER_FUNC(wcpu_Init);
#pragma FORCE_INCLUDE(wcpu_Init)
#pragma SECTION_RESTORE
WINIT_REGISTER_INIT_EARLY(wcpu_Init); // wcpu -> whrt
static uint numProcessors = 0;
+3 -8
View File
@@ -25,16 +25,11 @@
#include "winit.h"
#include "wutil.h"
#pragma SECTION_INIT(5)
WINIT_REGISTER_FUNC(wdbg_init);
#pragma FORCE_INCLUDE(wdbg_init)
#pragma SECTION_RESTORE
WINIT_REGISTER_INIT_MAIN(wdbg_Init);
// used to prevent the vectored exception handler from taking charge when
// an exception is raised from the main thread (allows __try blocks to
// get control). latched in wdbg_init.
// get control). latched in wdbg_Init.
static DWORD main_thread_id;
@@ -710,7 +705,7 @@ static LONG WINAPI vectored_exception_handler(EXCEPTION_POINTERS* ep)
}
static LibError wdbg_init(void)
static LibError wdbg_Init(void)
{
// see decl
main_thread_id = GetCurrentThreadId();
+6 -13
View File
@@ -37,15 +37,8 @@
#pragma comment(lib, "oleaut32.lib") // VariantChangeType
#endif
#pragma SECTION_INIT(5)
WINIT_REGISTER_FUNC(wdbg_sym_init);
#pragma FORCE_INCLUDE(wdbg_sym_init)
#pragma SECTION_SHUTDOWN(5)
WINIT_REGISTER_FUNC(wdbg_sym_shutdown);
#pragma FORCE_INCLUDE(wdbg_sym_shutdown)
#pragma SECTION_RESTORE
WINIT_REGISTER_INIT_MAIN(wdbg_sym_Init);
WINIT_REGISTER_SHUTDOWN_MAIN(wdbg_sym_Shutdown);
// note: it is safe to use debug_assert/debug_warn/CHECK_ERR even during a
// stack trace (which is triggered by debug_assert et al. in app code) because
@@ -117,7 +110,7 @@ static LibError sym_init()
}
// called from wdbg_sym_shutdown.
// called from wdbg_sym_Shutdown.
static LibError sym_shutdown()
{
SymCleanup(hProcess);
@@ -1312,7 +1305,7 @@ static PtrSet* already_visited_ptrs;
// if we put it in a function, construction still fails on VC7 because
// the atexit table will not have been initialized yet.
// called by debug_dump_stack and wdbg_sym_shutdown
// called by debug_dump_stack and wdbg_sym_Shutdown
static void ptr_reset_visited()
{
delete already_visited_ptrs;
@@ -1922,7 +1915,7 @@ fail:
static LibError wdbg_sym_init()
static LibError wdbg_sym_Init()
{
// try to import RtlCaptureContext (available on WinXP and later).
// it's used in walk_stack; import here to avoid overhead of doing so
@@ -1937,7 +1930,7 @@ static LibError wdbg_sym_init()
}
static LibError wdbg_sym_shutdown()
static LibError wdbg_sym_Shutdown()
{
ptr_reset_visited();
return sym_shutdown();
+2 -9
View File
@@ -22,15 +22,8 @@
#include "winit.h"
#include "wutil.h"
#pragma SECTION_INIT(5)
WINIT_REGISTER_FUNC(wdir_watch_Init);
#pragma FORCE_INCLUDE(wdir_watch_Init)
#pragma SECTION_SHUTDOWN(5)
WINIT_REGISTER_FUNC(wdir_watch_Shutdown);
#pragma FORCE_INCLUDE(wdir_watch_Shutdown)
#pragma SECTION_RESTORE
WINIT_REGISTER_INIT_MAIN(wdir_watch_Init);
WINIT_REGISTER_SHUTDOWN_MAIN(wdir_watch_Shutdown);
// rationale for polling:
// much simpler than pure asynchronous notification: no need for a
+1 -6
View File
@@ -15,12 +15,7 @@
#include "win.h"
#include "winit.h"
#pragma SECTION_SHUTDOWN(9) // last, since DLLs are unloaded here
WINIT_REGISTER_FUNC(wdll_Shutdown);
#pragma FORCE_INCLUDE(wdll_Shutdown)
#pragma SECTION_RESTORE
WINIT_REGISTER_SHUTDOWN_LATE2(wdll_Shutdown); // last - DLLs are unloaded here
//-----------------------------------------------------------------------------
// delay loading (modified from VC7 DelayHlp.cpp and DelayImp.h)
+2 -8
View File
@@ -22,14 +22,8 @@
#include "counter.h"
#pragma SECTION_INIT(4) // wtime depends on us
WINIT_REGISTER_FUNC(whrt_Init);
#pragma FORCE_INCLUDE(whrt_Init)
#pragma SECTION_SHUTDOWN(8)
WINIT_REGISTER_FUNC(whrt_Shutdown);
#pragma FORCE_INCLUDE(whrt_Shutdown)
#pragma SECTION_RESTORE
WINIT_REGISTER_INIT_EARLY2(whrt_Init); // whrt -> wtime
WINIT_REGISTER_SHUTDOWN_LATE(whrt_Shutdown);
namespace ERR
+8 -12
View File
@@ -13,6 +13,8 @@
#include "win.h" // GetTickCount
// see http://blogs.msdn.com/larryosterman/archive/2004/09/27/234840.aspx
typedef LibError (*PfnLibErrorVoid)(void);
@@ -22,18 +24,12 @@ typedef LibError (*PfnLibErrorVoid)(void);
// (zero, because CallFunctionPointers has to ignore entries =0 anyway).
// - ASCII '$' and 'Z' come before resp. after '0'..'9', so use that to
// bound the section names.
#pragma SECTION_INIT($)
PfnLibErrorVoid initBegin = 0;
#pragma SECTION_INIT(Z)
PfnLibErrorVoid initEnd = 0;
#pragma SECTION_SHUTDOWN($)
PfnLibErrorVoid shutdownBegin = 0;
#pragma SECTION_SHUTDOWN(Z)
PfnLibErrorVoid shutdownEnd = 0;
#pragma SECTION_RESTORE
// note: /include is not necessary, since these are referenced below.
#pragma comment(linker, "/merge:.WINIT=.data")
__declspec(allocate("WINIT$I$")) PfnLibErrorVoid initBegin = 0;
__declspec(allocate("WINIT$IZ")) PfnLibErrorVoid initEnd = 0;
__declspec(allocate("WINIT$S$")) PfnLibErrorVoid shutdownBegin = 0;
__declspec(allocate("WINIT$SZ")) PfnLibErrorVoid shutdownEnd = 0;
// note: #pragma comment(linker, "/include") is not necessary since
// these are referenced below.
/**
+121 -64
View File
@@ -11,78 +11,135 @@
#ifndef INCLUDED_WINIT
#define INCLUDED_WINIT
// overview:
// participating modules store function pointer(s) to their init and/or
// shutdown function in a specific COFF section. the sections are
// grouped according to the desired notification and the order in which
// functions are to be called (useful if one module depends on another).
// they are then gathered by the linker and arranged in alphabetical order.
// placeholder variables in the sections indicate where the series of
// functions begins and ends for a given notification time.
// at runtime, all of the function pointers between the markers are invoked.
//
// details:
// the section names are of the format ".LIB${type}{group}".
/*
Overview
--------
participating modules store function pointer(s) to their init and/or
shutdown function in a specific COFF section. the sections are
grouped according to the desired notification and the order in which
functions are to be called (useful if one module depends on another).
they are then gathered by the linker and arranged in alphabetical order.
placeholder variables in the sections indicate where the series of
functions begins and ends for a given notification time.
at runtime, all of the function pointers between the markers are invoked.
Example
-------
WINIT_REGISTER_INIT_MAIN(wtime_Init); // whrt -> wtime
Rationale
---------
several methods of module init are possible: (see Large Scale C++ Design)
- on-demand initialization: each exported function would have to check
if init already happened. that would be brittle and hard to verify.
- singleton: variant of the above, but not applicable to a
procedural interface (and quite ugly to boot).
- registration: static constructors call a central notification function.
module dependencies would be quite difficult to express - this would
require a graph or separate lists for each priority (clunky).
worse, a fatal flaw is that other C++ constructors may depend on the
modules we are initializing and already have run. there is no way
to influence ctor call order between separate source files, so
this is out of the question.
- linker-based registration: same as above, but the linker takes care
of assembling various functions into one sorted table. the list of
init functions is available before C++ ctors have run. incidentally,
zero runtime overhead is incurred. unfortunately, this approach is
MSVC-specific. however, the MS CRT uses a similar method for its
init, so this is expected to remain supported.
*/
//-----------------------------------------------------------------------------
// section declarations
// section names are of the format "WINIT${type}{group}".
// {type} is I for initialization- or S for shutdown functions.
// {group} is [0, 9]; all functions in a group are called before those of
// the next higher group, but order within the group is undefined.
// (this is because the linker sorts sections alphabetically but doesn't
// specify the order in which object files are processed.)
//
// example:
// #pragma SECTION_INIT(7))
// WINIT_REGISTER_FUNC(wtime_init);
// #pragma FORCE_INCLUDE(wtime_init)
// #pragma SECTION_SHUTDOWN(3))
// WINIT_REGISTER_FUNC(wtime_shutdown);
// #pragma FORCE_INCLUDE(wtime_shutdown)
// #pragma SECTION_RESTORE
//
// {group} is [0, 9] - see below.
// note: __declspec(allocate) requires sections to be 'declared'
// before using them.
#pragma section("WINIT$I$", read)
#pragma section("WINIT$I0", read)
#pragma section("WINIT$I1", read)
#pragma section("WINIT$I2", read)
#pragma section("WINIT$I6", read)
#pragma section("WINIT$I7", read)
#pragma section("WINIT$IZ", read)
#pragma section("WINIT$S$", read)
#pragma section("WINIT$S0", read)
#pragma section("WINIT$S1", read)
#pragma section("WINIT$S6", read)
#pragma section("WINIT$S7", read)
#pragma section("WINIT$S8", read)
#pragma section("WINIT$SZ", read)
#pragma comment(linker, "/merge:WINIT=.rdata")
//-----------------------------------------------------------------------------
// Function groups
// to allow correct ordering of module init in the face of dependencies,
// we introduce 'groups'. all functions in one are called before those in
// the next higher group, but order within the group is undefined.
// (this is because the linker sorts sections alphabetically but doesn't
// specify the order in which object files are processed.)
// register a function to be called at the given time.
// usage: invoke at file scope, passing a function identifier/symbol.
// rationale:
// several methods of module init are possible: (see Large Scale C++ Design)
// - on-demand initialization: each exported function would have to check
// if init already happened. that would be brittle and hard to verify.
// - singleton: variant of the above, but not applicable to a
// procedural interface (and quite ugly to boot).
// - registration: static constructors call a central notification function.
// module dependencies would be quite difficult to express - this would
// require a graph or separate lists for each priority (clunky).
// worse, a fatal flaw is that other C++ constructors may depend on the
// modules we are initializing and already have run. there is no way
// to influence ctor call order between separate source files, so
// this is out of the question.
// - linker-based registration: same as above, but the linker takes care
// of assembling various functions into one sorted table. the list of
// init functions is available before C++ ctors have run. incidentally,
// zero runtime overhead is incurred. unfortunately, this approach is
// MSVC-specific. however, the MS CRT uses a similar method for its
// init, so this is expected to remain supported.
// - __declspec(allocate) requires section declarations, but allows users to
// write only one line (instead of needing an additional #pragma data_seg)
// - fixed groups instead of passing a group number are more clear and
// encourage thinking about init order. (__declspec(allocate) requires
// a single string literal anyway and doesn't support merging)
// - why EXTERN_C and __pragma? VC8's link-stage optimizer believes
// the static function pointers defined by WINIT_REGISTER_* to be unused;
// unless action is taken, they would be removed. to prevent this, we
// forcibly include the function pointer symbols. this means the variable
// must be extern, not static. the linker needs to know the decorated
// symbol name, so we disable mangling via EXTERN_C.
// macros to simplify usage.
// notes:
// - #pragma cannot be packaged in macros due to expansion rules.
// - __declspec(allocate) would be tempting, since that could be
// wrapped in WINIT_REGISTER_FUNC. unfortunately it inexplicably cannot
// cope with split string literals (e.g. "ab" "c"). that disqualifies
// it, since we want to hide the section name behind a macro, which
// would require the abovementioned merging.
// very early init; must not fail, since error handling code *crashes*
// if called before these have completed.
#define WINIT_REGISTER_INIT_CRITICAL(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$I0")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
// note: init functions are called before _cinit and MUST NOT use
// any stateful CRT functions (e.g. atexit)!
#define SECTION_INIT(group) data_seg(".WINIT$I" #group)
#define SECTION_SHUTDOWN(group) data_seg(".WINIT$S" #group)
#define SECTION_RESTORE data_seg()
// use to make sure the link-stage optimizer doesn't discard the
// function pointers (happens on VC8)
#define FORCE_INCLUDE(id) comment(linker, "/include:_p"#id)
// meant for modules with dependents but whose init is complicated and may
// raise error/warning messages (=> can't go in WINIT_REGISTER_INIT_CRITICAL)
#define WINIT_REGISTER_INIT_EARLY(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$I1")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
#define WINIT_REGISTER_FUNC(func)\
static LibError func(void);\
EXTERN_C LibError (*p##func)(void) = func
// available for dependents of WINIT_REGISTER_INIT_EARLY-modules that
// must still come before WINIT_REGISTER_INIT_MAIN.
#define WINIT_REGISTER_INIT_EARLY2(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$I2")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
// most modules will go here unless they are often used or
// have many dependents.
#define WINIT_REGISTER_INIT_MAIN(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$I6")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
// available for any modules that may need to come after
// WINIT_REGISTER_INIT_MAIN (unlikely)
#define WINIT_REGISTER_INIT_LATE(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$I7")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
#define WINIT_REGISTER_SHUTDOWN_EARLY(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$S0")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
#define WINIT_REGISTER_SHUTDOWN_EARLY2(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$S1")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
#define WINIT_REGISTER_SHUTDOWN_MAIN(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$S6")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
#define WINIT_REGISTER_SHUTDOWN_LATE(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$S7")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
#define WINIT_REGISTER_SHUTDOWN_LATE2(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$S8")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
//-----------------------------------------------------------------------------
/**
* call each registered function.
* these are invoked by wstartup at the appropriate times.
*
* if this is called before CRT initialization, callbacks must not use any
* non-stateless CRT functions such as atexit. see wstartup.h for the
* current status on this issue.
**/
extern void winit_CallInitFunctions();
extern void winit_CallShutdownFunctions();
+4 -10
View File
@@ -20,14 +20,8 @@
#include "lib/sysdep/cpu.h"
#include "lib/bits.h"
#pragma SECTION_INIT(5)
WINIT_REGISTER_FUNC(waio_init);
#pragma FORCE_INCLUDE(waio_init)
#pragma SECTION_SHUTDOWN(5)
WINIT_REGISTER_FUNC(waio_shutdown);
#pragma FORCE_INCLUDE(waio_shutdown)
#pragma SECTION_RESTORE
WINIT_REGISTER_INIT_MAIN(waio_Init);
WINIT_REGISTER_SHUTDOWN_MAIN(waio_Shutdown);
static void lock()
@@ -889,14 +883,14 @@ int aio_fsync(int, struct aiocb*)
//////////////////////////////////////////////////////////////////////////////
static LibError waio_init()
static LibError waio_Init()
{
req_init();
return INFO::OK;
}
static LibError waio_shutdown()
static LibError waio_Shutdown()
{
req_cleanup();
aio_h_cleanup();
+7 -7
View File
@@ -15,12 +15,7 @@
#include "crt_posix.h" // _getcwd
#include "lib/bits.h"
#pragma SECTION_INIT(0) // very early (pageSize is needed by debug_error_message_build)
WINIT_REGISTER_FUNC(wposix_Init);
#pragma FORCE_INCLUDE(wposix_Init)
#pragma SECTION_RESTORE
WINIT_REGISTER_INIT_CRITICAL(wposix_Init); // wposix -> error handling
//-----------------------------------------------------------------------------
// sysconf
@@ -46,7 +41,12 @@ static void InitSysconf()
long sysconf(int name)
{
debug_assert(pageSize); // must not be called before InitSysconf
// called before InitSysconf => winit/wstartup are broken. this is
// going to cause a hard crash because debug.cpp's error reporting
// code requires the page size to be known. we'll exit with a unique
// value to hopefully help track down the problem.
if(!pageSize)
exit(239786); // 0x3A8AA
switch(name)
{
+4 -11
View File
@@ -20,15 +20,8 @@
#pragma comment(lib, "ws2_32.lib")
#endif
#pragma SECTION_INIT(5)
WINIT_REGISTER_FUNC(wsock_init);
#pragma FORCE_INCLUDE(wsock_init)
#pragma SECTION_SHUTDOWN(5)
WINIT_REGISTER_FUNC(wsock_shutdown);
#pragma FORCE_INCLUDE(wsock_shutdown)
#pragma SECTION_RESTORE
WINIT_REGISTER_INIT_MAIN(wsock_Init);
WINIT_REGISTER_SHUTDOWN_MAIN(wsock_Shutdown);
uint16_t htons(uint16_t s)
{
@@ -102,7 +95,7 @@ static LibError wsock_actual_init()
return INFO::OK;
}
static LibError wsock_init()
static LibError wsock_Init()
{
// trigger wsock_actual_init when someone first calls a wsock function.
static WdllLoadNotify loadNotify = { "ws2_32", wsock_actual_init };
@@ -111,7 +104,7 @@ static LibError wsock_init()
}
static LibError wsock_shutdown()
static LibError wsock_Shutdown()
{
if(!ModuleShouldShutdown(&initState))
return INFO::OK;
+1 -6
View File
@@ -19,12 +19,7 @@
#include "lib/sysdep/cpu.h" // cpu_i64FromDouble
#include "lib/sysdep/win/whrt/whrt.h"
#pragma SECTION_INIT(7) // depends on whrt
WINIT_REGISTER_FUNC(wtime_Init);
#pragma FORCE_INCLUDE(wtime_Init)
#pragma SECTION_RESTORE
WINIT_REGISTER_INIT_MAIN(wtime_Init); // whrt -> wtime
// NT system time and FILETIME are hectonanoseconds since Jan. 1, 1601 UTC.
// SYSTEMTIME is a struct containing month, year, etc.
+21 -99
View File
@@ -2,35 +2,20 @@
* =========================================================================
* File : wstartup.cpp
* Project : 0 A.D.
* Description : windows-specific entry point and startup code
* Description : windows-specific startup code
* =========================================================================
*/
// license: GPL; see lib/license.txt
// this module can wrap the program in a SEH __try block and takes care of
// calling winit's functions at the appropriate times.
// to use it, set Linker Options -> Advanced -> Entry Point to
// "entry" (without quotes).
//
// besides commandeering the entry point, it hooks ExitProcess.
// control flow overview: entry [-> RunWithinTryBlock] -> InitAndCallMain ->
// (init) -> mainCRTStartup -> main -> exit -> HookedExitProcess ->
// (shutdown) -> ExitProcess.
#include "precompiled.h"
#include "wstartup.h"
#include "win.h"
#include <process.h> // __security_init_cookie
#include "lib/sysdep/win/detours/detours.h"
#include "winit.h"
#include "wdbg.h" // wdbg_exception_filter
//-----------------------------------------------------------------------------
// do shutdown at exit
// shutdown
// note: the alternative of using atexit has two disadvantages.
// - the call to atexit must come after _cinit, which means we'd need to be
@@ -39,30 +24,12 @@
// for static objects would cause those handlers to be called after ours,
// which may cause shutdown order bugs.
static VOID (WINAPI *RealExitProcess)(UINT uExitCode);
static VOID WINAPI HookedExitProcess(UINT uExitCode)
{
winit_CallShutdownFunctions();
RealExitProcess(uExitCode);
}
static void InstallExitHook()
{
// (can't do this in a static initializer because they haven't run yet!)
RealExitProcess = ExitProcess;
DetourTransactionBegin();
DetourAttach(&(PVOID&)RealExitProcess, HookedExitProcess);
DetourTransactionCommit();
}
//-----------------------------------------------------------------------------
// init
// the init functions need to be called before any use of Windows-specific
// code.
// code. in particular, static ctors may use whrt or wpthread, so we ought to
// be initialized before them as well.
//
// one possibility is using WinMain as the entry point, and then calling the
// application's main(), but this is expressly forbidden by the C standard.
@@ -78,41 +45,32 @@ static void InstallExitHook()
// the 0ad executable as well as the separate self-test. this means
// we can't enable the main() hook for one and disable it in the other.
//
// requiring users to call us at the beginning of main is brittle in general
// and not possible with the self-test's auto-generated main file.
// requiring users to call us at the beginning of main is brittle in general,
// comes after static ctors, and is difficult to achieve in external code
// such as the (automatically generated) self-test.
//
// the only alternative is to commandeer the entry point and do all init
// before calling mainCRTStartup. this means init will be finished before
// C++ static ctors run (allowing our APIs to be called from those ctors),
// but also denies init the use of any non-stateless CRT functions!
// commandeering the entry point, doing init there and then calling
// mainCRTStartup would work, but doesn't help with shutdown - additional
// measures are required (see above). note that this approach means we're
// initialized before _cinit, denying the use of non-stateless CRT functions.
// these aren't defined in VC include files, so we have to do it manually.
#ifdef USE_WINMAIN
EXTERN_C int WinMainCRTStartup(void);
#else
EXTERN_C int mainCRTStartup(void);
#endif
// (moved into a separate function because it's used by both
// entry and entry_noSEH)
static int InitAndCallMain()
EXTERN_C void InitAndRegisterShutdown()
{
winit_CallInitFunctions();
InstallExitHook();
int ret;
#ifdef USE_WINMAIN
ret = WinMainCRTStartup(); // calls _cinit and then our WinMain
#else
ret = mainCRTStartup(); // calls _cinit and then our main
#endif
return ret;
atexit(winit_CallShutdownFunctions);
}
#pragma data_seg(".CRT$XCB")
EXTERN_C void(*pInitAndRegisterShutdown)() = InitAndRegisterShutdown;
#pragma comment(linker, "/include:_pInitAndRegisterShutdown")
#pragma data_seg()
//-----------------------------------------------------------------------------
// entry point and SEH wrapper
// SEH wrapper
#include "wdbg.h" // wdbg_exception_filter
typedef int(*PfnIntVoid)(void);
@@ -129,39 +87,3 @@ static int RunWithinTryBlock(PfnIntVoid func)
}
return ret;
}
int entry()
{
#if MSC_VERSION >= 1400
// 2006-02-16 workaround for R6035 on VC8:
//
// SEH code compiled with /GS pushes a "security cookie" onto the
// stack. since we're called before _cinit, the cookie won't have
// been initialized yet, which would cause the CRT to FatalAppExit.
// to solve this, we must call __security_init_cookie before any
// hidden compiler-generated SEH registration code runs,
// which means the __try block must be moved into a helper function.
//
// NB: entry() must not contain local string buffers, either -
// /GS would install a cookie here as well (same problem).
//
// see http://msdn2.microsoft.com/en-US/library/ms235603.aspx
__security_init_cookie();
#endif
return RunWithinTryBlock(InitAndCallMain);
}
// Alternative entry point, for programs that don't want the SEH handler
// (e.g. unit tests, where it's better to let the debugger handle any errors)
int entry_noSEH()
{
#if MSC_VERSION >= 1400
// see above. this is also necessary here in case pre-libc init
// functions use SEH.
__security_init_cookie();
#endif
return InitAndCallMain();
}
+9 -5
View File
@@ -2,17 +2,21 @@
* =========================================================================
* File : wstartup.h
* Project : 0 A.D.
* Description : windows-specific entry point and startup code
* Description : windows-specific startup code
* =========================================================================
*/
// license: GPL; see lib/license.txt
// linking with this component automatically causes winit's functions to
// be called at the appropriate times.
//
// the current implementation manages to trigger winit initialization
// between CRT init and static C++ ctors. that means wpthread etc. APIs
// are safe to use from the ctors and also that winit initializers are
// allowed to use non-stateless CRT functions such as atexit.
#ifndef INCLUDED_WSTARTUP
#define INCLUDED_WSTARTUP
// entry points (normal and without SEH wrapper; see definition)
EXTERN_C int entry();
EXTERN_C int entry_noSEH();
#endif // #ifndef INCLUDED_WSTARTUP
+3 -10
View File
@@ -19,15 +19,8 @@
#include "win.h"
#include "winit.h"
#pragma SECTION_INIT(1) // early, several modules depend on us
WINIT_REGISTER_FUNC(wutil_PreLibcInit);
#pragma FORCE_INCLUDE(wutil_PreLibcInit)
#pragma SECTION_SHUTDOWN(8)
WINIT_REGISTER_FUNC(wutil_Shutdown);
#pragma FORCE_INCLUDE(wutil_Shutdown)
#pragma SECTION_RESTORE
WINIT_REGISTER_INIT_EARLY(wutil_Init);
WINIT_REGISTER_SHUTDOWN_LATE(wutil_Shutdown);
//-----------------------------------------------------------------------------
// safe allocator
@@ -331,7 +324,7 @@ void wutil_RevertWow64Redirection(void* wasRedirectionEnabled)
//-----------------------------------------------------------------------------
static LibError wutil_PreLibcInit()
static LibError wutil_Init()
{
InitLocks();