mirror of
https://gitea.wildfiregames.com/0ad/0ad.git
synced 2026-07-07 08:45:26 +00:00
(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:
@@ -71,9 +71,6 @@ extern_lib_defs = {
|
||||
devil = {
|
||||
unix_names = { "IL", "ILU" },
|
||||
},
|
||||
detours = {
|
||||
dbg_suffix = "",
|
||||
},
|
||||
directx = {
|
||||
win_names = { "ddraw", },
|
||||
dbg_suffix = "",
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user