mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-21 07:43:59 -07:00
detours is configured as a static lib, which would require using the same compiler, but we have several in action. fix: include a stripped down version of its code in the main source tree This was SVN commit r5126.
167 lines
5.4 KiB
C++
167 lines
5.4 KiB
C++
/**
|
|
* =========================================================================
|
|
* File : wstartup.cpp
|
|
* Project : 0 A.D.
|
|
* Description : windows-specific entry point and 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
|
|
|
|
// note: the alternative of using atexit has two disadvantages.
|
|
// - the call to atexit must come after _cinit, which means we'd need to be
|
|
// called manually from main (see discussion on init below)
|
|
// - other calls to atexit from ctors or hidden compiler-generated init code
|
|
// 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.
|
|
//
|
|
// 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.
|
|
// VC apparently makes use of this and changes its calling convention.
|
|
// if we call it, everything appears to work but stack traces in
|
|
// release mode are incorrect (symbol address is off by 4).
|
|
//
|
|
// another alternative is re#defining the app's main function to app_main,
|
|
// having the OS call our main, and then dispatching to app_main.
|
|
// however, this leads to trouble when another library (e.g. SDL) wants to
|
|
// do the same.
|
|
// moreover, this file is compiled into a static library and used both for
|
|
// 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.
|
|
//
|
|
// 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!
|
|
|
|
// 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()
|
|
{
|
|
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;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// entry point and SEH wrapper
|
|
|
|
typedef int(*PfnIntVoid)(void);
|
|
|
|
static int RunWithinTryBlock(PfnIntVoid func)
|
|
{
|
|
int ret;
|
|
//__try
|
|
{
|
|
ret = func();
|
|
}
|
|
//__except(wdbg_exception_filter(GetExceptionInformation()))
|
|
{
|
|
ret = -1;
|
|
}
|
|
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();
|
|
}
|