1
0
forked from 0ad/0ad

make sure winit shutdown actually happens during self-test (fixes leaks and possibly failure to unload driver)

revamp module init: no more atexit and "premain" init => no longer need
to call any wstartup init function. winit section names now use group
numbers instead of letters.

delay_load -> wdll_delay_load
dll_ver -> wdll_ver
add aken build environment
cleanup wsock.cpp, have it import its functions properly
wsdl: defend against GameSetup calling SDL_Quit twice.

This was SVN commit r5108.
This commit is contained in:
janwas 2007-05-28 15:08:33 +00:00
parent 7fe6c3d77f
commit b879e344f1
26 changed files with 786 additions and 932 deletions

View File

@ -0,0 +1,7 @@
#
# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
# file to this component. This file merely indirects to the real make file
# that is shared by all the driver components of the Windows NT DDK
#
!INCLUDE $(NTMAKEENV)\makefile.def

View File

@ -0,0 +1,5 @@
TARGETNAME=aken
TARGETPATH=.
TARGETTYPE=DRIVER
SOURCES=aken.cpp

View File

@ -1,34 +0,0 @@
/**
* =========================================================================
* File : delay_load.h
* Project : 0 A.D.
* Description : allow delay-loading DLLs.
* =========================================================================
*/
// license: GPL; see lib/license.txt
struct DllLoadNotify;
extern void wdll_add_notify(DllLoadNotify*);
// note: this mechanism relies on the compiler calling non-local static
// object ctors, which doesn't happen if compiling this code into
// a static library. recommended workaround is to call wdll_add_notify via
// win.cpp module init mechanism.
struct DllLoadNotify
{
const char* dll_name;
LibError (*func)(void);
DllLoadNotify* next;
DllLoadNotify(const char* _dll_name, LibError (*_func)(void))
{
dll_name = _dll_name;
func = _func;
wdll_add_notify(this);
}
};
#define WDLL_LOAD_NOTIFY(dll_name, func)\
static DllLoadNotify func##_NOTIFY(dll_name, func)

View File

@ -17,8 +17,8 @@
#include "wutil.h" #include "wutil.h"
#include "winit.h" #include "winit.h"
#pragma SECTION_PRE_LIBC(B) // early; whrt depends on us #pragma SECTION_INIT(2) // early; whrt depends on us
WIN_REGISTER_FUNC(wcpu_Init); WINIT_REGISTER_FUNC(wcpu_Init);
#pragma FORCE_INCLUDE(wcpu_Init) #pragma FORCE_INCLUDE(wcpu_Init)
#pragma SECTION_RESTORE #pragma SECTION_RESTORE

View File

@ -26,8 +26,8 @@
#include "wutil.h" #include "wutil.h"
#pragma SECTION_PRE_LIBC(D) #pragma SECTION_INIT(5)
WIN_REGISTER_FUNC(wdbg_init); WINIT_REGISTER_FUNC(wdbg_init);
#pragma FORCE_INCLUDE(wdbg_init) #pragma FORCE_INCLUDE(wdbg_init)
#pragma SECTION_RESTORE #pragma SECTION_RESTORE

View File

@ -38,11 +38,11 @@
#endif #endif
#pragma SECTION_PRE_LIBC(D) #pragma SECTION_INIT(5)
WIN_REGISTER_FUNC(wdbg_sym_init); WINIT_REGISTER_FUNC(wdbg_sym_init);
#pragma FORCE_INCLUDE(wdbg_sym_init) #pragma FORCE_INCLUDE(wdbg_sym_init)
#pragma SECTION_POST_ATEXIT(J) #pragma SECTION_SHUTDOWN(5)
WIN_REGISTER_FUNC(wdbg_sym_shutdown); WINIT_REGISTER_FUNC(wdbg_sym_shutdown);
#pragma FORCE_INCLUDE(wdbg_sym_shutdown) #pragma FORCE_INCLUDE(wdbg_sym_shutdown)
#pragma SECTION_RESTORE #pragma SECTION_RESTORE

View File

@ -22,8 +22,8 @@
#include "wutil.h" #include "wutil.h"
#pragma SECTION_POST_ATEXIT(J) #pragma SECTION_SHUTDOWN(5)
WIN_REGISTER_FUNC(wdir_watch_shutdown); WINIT_REGISTER_FUNC(wdir_watch_shutdown);
#pragma FORCE_INCLUDE(wdir_watch_shutdown) #pragma FORCE_INCLUDE(wdir_watch_shutdown)
#pragma SECTION_RESTORE #pragma SECTION_RESTORE
@ -155,10 +155,10 @@ static LibError wdir_watch_shutdown()
hIOCP = INVALID_HANDLE_VALUE; hIOCP = INVALID_HANDLE_VALUE;
// free all (dynamically allocated) Watch objects // free all (dynamically allocated) Watch objects
for(WatchIt it = watches.begin(); it != watches.end(); ++it) /*si for(WatchIt it = watches.begin(); it != watches.end(); ++it)
delete it->second; delete it->second;
watches.clear(); watches.clear();
*/
return INFO::OK; return INFO::OK;
} }

View File

@ -0,0 +1,32 @@
/**
* =========================================================================
* File : wdll_delay_load.h
* Project : 0 A.D.
* Description : DLL delay loading and notification
* =========================================================================
*/
// license: GPL; see lib/license.txt
#ifndef INCLUDED_WDLL_DELAY_LOAD
#define INCLUDED_WDLL_DELAY_LOAD
// must be POD because it is used before static ctors run.
struct WdllLoadNotify
{
const char* dll_name;
LibError (*func)(void);
WdllLoadNotify* next;
};
extern void wdll_add_notify(WdllLoadNotify*);
// request that func be called if and when dll_name is ever delay-loaded.
// must be invoked at function scope.
#define WDLL_ADD_NOTIFY(dll_name, func)\
STMT(\
static WdllLoadNotify UID__ = { dll_name, func };\
wdll_add_notify(&UID__);\
)
#endif // #ifndef INCLUDED_WDLL_DELAY_LOAD

View File

@ -1,6 +1,6 @@
/** /**
* ========================================================================= * =========================================================================
* File : dll_ver.cpp * File : wdll_ver.cpp
* Project : 0 A.D. * Project : 0 A.D.
* Description : return DLL version information. * Description : return DLL version information.
* ========================================================================= * =========================================================================
@ -9,7 +9,7 @@
// license: GPL; see lib/license.txt // license: GPL; see lib/license.txt
#include "precompiled.h" #include "precompiled.h"
#include "dll_ver.h" #include "wdll_ver.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -93,15 +93,15 @@ static LibError get_ver(const char* module_path, char* out_ver, size_t out_ver_l
// build a string containing DLL filename(s) and their version info. // build a string containing DLL filename(s) and their version info.
// //
static char* dll_list_buf; static char* ver_list_buf;
static size_t dll_list_chars; static size_t ver_list_chars;
static char* dll_list_pos; static char* ver_list_pos;
// set output buffer into which DLL names and their versions will be written. // set output buffer into which DLL names and their versions will be written.
void dll_list_init(char* buf, size_t chars) void wdll_ver_list_init(char* buf, size_t chars)
{ {
dll_list_pos = dll_list_buf = buf; ver_list_pos = ver_list_buf = buf;
dll_list_chars = chars; ver_list_chars = chars;
} }
@ -110,10 +110,10 @@ void dll_list_init(char* buf, size_t chars)
// name should preferably be the complete path to DLL, to make sure // name should preferably be the complete path to DLL, to make sure
// we don't inadvertently load another one on the library search path. // we don't inadvertently load another one on the library search path.
// we add the .dll extension if necessary. // we add the .dll extension if necessary.
LibError dll_list_add(const char* name) LibError wdll_ver_list_add(const char* name)
{ {
// not be called before dll_list_init or after failure // not be called before wdll_ver_list_init or after failure
if(!dll_list_pos) if(!ver_list_pos)
WARN_RETURN(ERR::LOGIC); WARN_RETURN(ERR::LOGIC);
// some driver names are stored in the registry without .dll extension. // some driver names are stored in the registry without .dll extension.
@ -134,26 +134,26 @@ LibError dll_list_add(const char* name)
(void)get_ver(dll_name, dll_ver, sizeof(dll_ver)); (void)get_ver(dll_name, dll_ver, sizeof(dll_ver));
// if this fails, default is already set and we don't want to abort. // if this fails, default is already set and we don't want to abort.
const ssize_t max_chars_to_write = (ssize_t)dll_list_chars - (dll_list_pos-dll_list_buf) - 10; const ssize_t max_chars_to_write = (ssize_t)ver_list_chars - (ver_list_pos-ver_list_buf) - 10;
// reserves enough room for subsequent comma and "..." strings. // reserves enough room for subsequent comma and "..." strings.
// not first time: prepend comma to string (room was reserved above). // not first time: prepend comma to string (room was reserved above).
if(dll_list_pos != dll_list_buf) if(ver_list_pos != ver_list_buf)
dll_list_pos += sprintf(dll_list_pos, ", "); ver_list_pos += sprintf(ver_list_pos, ", ");
// extract filename. // extract filename.
const char* dll_fn = path_name_only(dll_name); const char* dll_fn = path_name_only(dll_name);
int len = snprintf(dll_list_pos, max_chars_to_write, "%s (%s)", dll_fn, dll_ver); int len = snprintf(ver_list_pos, max_chars_to_write, "%s (%s)", dll_fn, dll_ver);
// success // success
if(len > 0) if(len > 0)
{ {
dll_list_pos += len; ver_list_pos += len;
return INFO::OK; return INFO::OK;
} }
// didn't fit; complain // didn't fit; complain
sprintf(dll_list_pos, "..."); // (room was reserved above) sprintf(ver_list_pos, "..."); // (room was reserved above)
dll_list_pos = 0; // poison pill, prevent further calls ver_list_pos = 0; // poison pill, prevent further calls
WARN_RETURN(ERR::BUF_SIZE); WARN_RETURN(ERR::BUF_SIZE);
} }

View File

@ -1,26 +1,26 @@
/** /**
* ========================================================================= * =========================================================================
* File : dll_ver.h * File : wdll_ver.h
* Project : 0 A.D. * Project : 0 A.D.
* Description : return DLL version information. * Description : return DLL version information
* ========================================================================= * =========================================================================
*/ */
// license: GPL; see lib/license.txt // license: GPL; see lib/license.txt
#ifndef INCLUDED_DLL_VER #ifndef INCLUDED_WDLL_VER
#define INCLUDED_DLL_VER #define INCLUDED_WDLL_VER
// note: this module is not re-entrant or thread-safe! // WARNING: not re-entrant or thread-safe!
// set output buffer into which DLL names and their versions will be written. // set output buffer into which DLL names and their versions will be written.
extern void dll_list_init(char* buf, size_t chars); extern void wdll_ver_list_init(char* buf, size_t chars);
// read DLL file version and append that and its name to the list. // read DLL file version and append that and its name to the list.
// //
// name should preferably be the complete path to DLL, to make sure // name should preferably be the complete path to DLL, to make sure
// we don't inadvertently load another one on the library search path. // we don't inadvertently load another one on the library search path.
// we add the .dll extension if necessary. // we add the .dll extension if necessary.
extern LibError dll_list_add(const char* name); extern LibError wdll_ver_list_add(const char* name);
#endif // #ifndef INCLUDED_DLL_VER #endif // #ifndef INCLUDED_WDLL_VER

View File

@ -11,7 +11,7 @@
#include "precompiled.h" #include "precompiled.h"
#include "lib/sysdep/gfx.h" #include "lib/sysdep/gfx.h"
#include "dll_ver.h" // dll_list_* #include "wdll_ver.h"
#include "win.h" #include "win.h"
#if MSC_VERSION #if MSC_VERSION
@ -145,7 +145,7 @@ static LibError win_get_gfx_drv_ver()
DWORD i; DWORD i;
char drv_name[MAX_PATH+1]; char drv_name[MAX_PATH+1];
dll_list_init(gfx_drv_ver, GFX_DRV_VER_LEN); wdll_ver_list_init(gfx_drv_ver, GFX_DRV_VER_LEN);
HKEY hkOglDrivers; HKEY hkOglDrivers;
const char* key = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\OpenGLDrivers"; const char* key = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\OpenGLDrivers";
@ -166,7 +166,7 @@ static LibError win_get_gfx_drv_ver()
{ {
DWORD drv_name_len = ARRAY_SIZE(drv_name)-5; // for ".dll" DWORD drv_name_len = ARRAY_SIZE(drv_name)-5; // for ".dll"
if(RegQueryValueEx(hkSet, "Dll", 0, 0, (LPBYTE)drv_name, &drv_name_len) == 0) if(RegQueryValueEx(hkSet, "Dll", 0, 0, (LPBYTE)drv_name, &drv_name_len) == 0)
ret = dll_list_add(drv_name); ret = wdll_ver_list_add(drv_name);
RegCloseKey(hkSet); RegCloseKey(hkSet);
} }
@ -186,7 +186,7 @@ static LibError win_get_gfx_drv_ver()
if(err != ERROR_SUCCESS) // error or no more items - bail if(err != ERROR_SUCCESS) // error or no more items - bail
break; break;
if(type == REG_SZ) if(type == REG_SZ)
ret = dll_list_add(drv_name); ret = wdll_ver_list_add(drv_name);
} // for each value } // for each value
RegCloseKey(hkOglDrivers); RegCloseKey(hkOglDrivers);

View File

@ -28,11 +28,11 @@
// insert a case in ConstructCounterAt's switch statement. // insert a case in ConstructCounterAt's switch statement.
#pragma SECTION_PRE_LIBC(D) // wposix depends on us #pragma SECTION_INIT(4) // wposix depends on us
WIN_REGISTER_FUNC(whrt_Init); WINIT_REGISTER_FUNC(whrt_Init);
#pragma FORCE_INCLUDE(whrt_Init) #pragma FORCE_INCLUDE(whrt_Init)
#pragma SECTION_POST_ATEXIT(V) #pragma SECTION_SHUTDOWN(8)
WIN_REGISTER_FUNC(whrt_Shutdown); WINIT_REGISTER_FUNC(whrt_Shutdown);
#pragma FORCE_INCLUDE(whrt_Shutdown) #pragma FORCE_INCLUDE(whrt_Shutdown)
#pragma SECTION_RESTORE #pragma SECTION_RESTORE

View File

@ -15,24 +15,23 @@
typedef LibError (*PfnLibErrorVoid)(void); typedef LibError (*PfnLibErrorVoid)(void);
// pointers to start and end of function tables. // pointers to start and end of function tables.
// note: COFF tosses out empty segments, so we have to put in one value // notes:
// (zero, because CallFunctionPointers has to ignore entries =0 anyway). // - COFF tosses out empty segments, so we have to put in one value
#pragma SECTION_PRE_LIBC(A) // (zero, because CallFunctionPointers has to ignore entries =0 anyway).
PfnLibErrorVoid pre_libc_begin = 0; // - ASCII '$' and 'Z' come before resp. after '0'..'9', so use that to
#pragma SECTION_PRE_LIBC(Z) // bound the section names.
PfnLibErrorVoid pre_libc_end = 0; #pragma SECTION_INIT($)
#pragma SECTION_PRE_MAIN(A) PfnLibErrorVoid initBegin = 0;
PfnLibErrorVoid pre_main_begin = 0; #pragma SECTION_INIT(Z)
#pragma SECTION_PRE_MAIN(Z) PfnLibErrorVoid initEnd = 0;
PfnLibErrorVoid pre_main_end = 0; #pragma SECTION_SHUTDOWN($)
#pragma SECTION_POST_ATEXIT(A) PfnLibErrorVoid shutdownBegin = 0;
PfnLibErrorVoid shutdown_begin = 0; #pragma SECTION_SHUTDOWN(Z)
#pragma SECTION_POST_ATEXIT(Z) PfnLibErrorVoid shutdownEnd = 0;
PfnLibErrorVoid shutdown_end = 0;
#pragma SECTION_RESTORE #pragma SECTION_RESTORE
// note: /include is not necessary, since these are referenced below. // note: /include is not necessary, since these are referenced below.
#pragma comment(linker, "/merge:.LIB=.data") #pragma comment(linker, "/merge:.WINIT=.data")
/** /**
@ -53,17 +52,12 @@ static void CallFunctionPointers(PfnLibErrorVoid* begin, PfnLibErrorVoid* end)
} }
void winit_CallPreLibcFunctions() void winit_CallInitFunctions()
{ {
CallFunctionPointers(&pre_libc_begin, &pre_libc_end); CallFunctionPointers(&initBegin, &initEnd);
}
void winit_CallPreMainFunctions()
{
CallFunctionPointers(&pre_main_begin, &pre_main_end);
} }
void winit_CallShutdownFunctions() void winit_CallShutdownFunctions()
{ {
CallFunctionPointers(&shutdown_begin, &shutdown_end); CallFunctionPointers(&shutdownBegin, &shutdownEnd);
} }

View File

@ -11,9 +11,6 @@
#ifndef INCLUDED_WINIT #ifndef INCLUDED_WINIT
#define INCLUDED_WINIT #define INCLUDED_WINIT
// register functions to be called before libc init, before main,
// or after atexit.
//
// overview: // overview:
// participating modules store function pointer(s) to their init and/or // participating modules store function pointer(s) to their init and/or
// shutdown function in a specific COFF section. the sections are // shutdown function in a specific COFF section. the sections are
@ -26,21 +23,18 @@
// //
// details: // details:
// the section names are of the format ".LIB${type}{group}". // the section names are of the format ".LIB${type}{group}".
// {type} is C for pre-libc init, I for pre-main init, or // {type} is I for initialization- or S for shutdown functions.
// T for terminators (last of the atexit handlers). // {group} is [0, 9]; all functions in a group are called before those of
// {group} is [B, Y]; all functions in a group are called before those of // the next higher group, but order within the group is undefined.
// the next (alphabetically) higher group, but order within the group is // (this is because the linker sorts sections alphabetically but doesn't
// undefined. this is because the linker sorts sections alphabetically, // specify the order in which object files are processed.)
// but doesn't specify the order in which object files are processed.
// another consequence is that groups A and Z must not be used!
// (data placed there might end up outside the start/end markers)
// //
// example: // example:
// #pragma SECTION_PRE_LIBC(G)) // #pragma SECTION_INIT(7))
// WIN_REGISTER_FUNC(wtime_init); // WINIT_REGISTER_FUNC(wtime_init);
// #pragma FORCE_INCLUDE(wtime_init) // #pragma FORCE_INCLUDE(wtime_init)
// #pragma SECTION_POST_ATEXIT(D)) // #pragma SECTION_SHUTDOWN(3))
// WIN_REGISTER_FUNC(wtime_shutdown); // WINIT_REGISTER_FUNC(wtime_shutdown);
// #pragma FORCE_INCLUDE(wtime_shutdown) // #pragma FORCE_INCLUDE(wtime_shutdown)
// #pragma SECTION_RESTORE // #pragma SECTION_RESTORE
// //
@ -50,7 +44,7 @@
// if init already happened. that would be brittle and hard to verify. // if init already happened. that would be brittle and hard to verify.
// - singleton: variant of the above, but not applicable to a // - singleton: variant of the above, but not applicable to a
// procedural interface (and quite ugly to boot). // procedural interface (and quite ugly to boot).
// - registration: NLSO constructors call a central notification function. // - registration: static constructors call a central notification function.
// module dependencies would be quite difficult to express - this would // module dependencies would be quite difficult to express - this would
// require a graph or separate lists for each priority (clunky). // require a graph or separate lists for each priority (clunky).
// worse, a fatal flaw is that other C++ constructors may depend on the // worse, a fatal flaw is that other C++ constructors may depend on the
@ -68,23 +62,21 @@
// notes: // notes:
// - #pragma cannot be packaged in macros due to expansion rules. // - #pragma cannot be packaged in macros due to expansion rules.
// - __declspec(allocate) would be tempting, since that could be // - __declspec(allocate) would be tempting, since that could be
// wrapped in WIN_REGISTER_FUNC. unfortunately it inexplicably cannot // wrapped in WINIT_REGISTER_FUNC. unfortunately it inexplicably cannot
// cope with split string literals (e.g. "ab" "c"). that disqualifies // cope with split string literals (e.g. "ab" "c"). that disqualifies
// it, since we want to hide the section name behind a macro, which // it, since we want to hide the section name behind a macro, which
// would require the abovementioned merging. // would require the abovementioned merging.
// note: the purpose of pre-libc init (with the resulting requirement that // note: init functions are called before _cinit and MUST NOT use
// no CRT functions be used during init!) is to allow the use of the // any stateful CRT functions (e.g. atexit)!
// initialized module in static ctors. #define SECTION_INIT(group) data_seg(".WINIT$I" #group)
#define SECTION_PRE_LIBC(group) data_seg(".LIB$C" #group) #define SECTION_SHUTDOWN(group) data_seg(".WINIT$S" #group)
#define SECTION_PRE_MAIN(group) data_seg(".LIB$I" #group)
#define SECTION_POST_ATEXIT(group) data_seg(".LIB$T" #group)
#define SECTION_RESTORE data_seg() #define SECTION_RESTORE data_seg()
// use to make sure the link-stage optimizer doesn't discard the // use to make sure the link-stage optimizer doesn't discard the
// function pointers (happens on VC8) // function pointers (happens on VC8)
#define FORCE_INCLUDE(id) comment(linker, "/include:_p"#id) #define FORCE_INCLUDE(id) comment(linker, "/include:_p"#id)
#define WIN_REGISTER_FUNC(func)\ #define WINIT_REGISTER_FUNC(func)\
static LibError func(void);\ static LibError func(void);\
EXTERN_C LibError (*p##func)(void) = func EXTERN_C LibError (*p##func)(void) = func
@ -92,8 +84,7 @@
* call each registered function. * call each registered function.
* these are invoked by wstartup at the appropriate times. * these are invoked by wstartup at the appropriate times.
**/ **/
extern void winit_CallPreLibcFunctions(); extern void winit_CallInitFunctions();
extern void winit_CallPreMainFunctions();
extern void winit_CallShutdownFunctions(); extern void winit_CallShutdownFunctions();
#endif // #ifndef INCLUDED_WINIT #endif // #ifndef INCLUDED_WINIT

View File

@ -21,11 +21,11 @@
#include "lib/bits.h" #include "lib/bits.h"
#pragma SECTION_PRE_LIBC(J) #pragma SECTION_INIT(5)
WIN_REGISTER_FUNC(waio_init); WINIT_REGISTER_FUNC(waio_init);
#pragma FORCE_INCLUDE(waio_init) #pragma FORCE_INCLUDE(waio_init)
#pragma SECTION_POST_ATEXIT(D) #pragma SECTION_SHUTDOWN(5)
WIN_REGISTER_FUNC(waio_shutdown); WINIT_REGISTER_FUNC(waio_shutdown);
#pragma FORCE_INCLUDE(waio_shutdown) #pragma FORCE_INCLUDE(waio_shutdown)
#pragma SECTION_RESTORE #pragma SECTION_RESTORE

View File

@ -16,8 +16,8 @@
#include "lib/bits.h" #include "lib/bits.h"
#pragma SECTION_PRE_LIBC(J) #pragma SECTION_INIT(5)
WIN_REGISTER_FUNC(wposix_Init); WINIT_REGISTER_FUNC(wposix_Init);
#pragma FORCE_INCLUDE(wposix_Init) #pragma FORCE_INCLUDE(wposix_Init)
#pragma SECTION_RESTORE #pragma SECTION_RESTORE

View File

@ -11,103 +11,115 @@
#include "precompiled.h" #include "precompiled.h"
#include "wsock.h" #include "wsock.h"
#include "../delay_load.h" #include "lib/sysdep/win/wdll_delay_load.h"
#include "wposix_internal.h" #include "wposix_internal.h"
#include "wsock_internal.h" #include "wsock_internal.h"
#include "lib/module_init.h"
#if MSC_VERSION #if MSC_VERSION
#pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "ws2_32.lib")
#endif #endif
#pragma SECTION_PRE_MAIN(K) #pragma SECTION_INIT(5)
WIN_REGISTER_FUNC(wsock_init); WINIT_REGISTER_FUNC(wsock_init);
#pragma FORCE_INCLUDE(wsock_init) #pragma FORCE_INCLUDE(wsock_init)
#pragma SECTION_POST_ATEXIT(D) #pragma SECTION_SHUTDOWN(5)
WIN_REGISTER_FUNC(wsock_shutdown); WINIT_REGISTER_FUNC(wsock_shutdown);
#pragma FORCE_INCLUDE(wsock_shutdown) #pragma FORCE_INCLUDE(wsock_shutdown)
#pragma SECTION_RESTORE #pragma SECTION_RESTORE
uint16_t htons(uint16_t s)
{
return (s >> 8) | ((s & 0xff) << 8);
}
// IPv6 globals // IPv6 globals
// These are included in the linux C libraries and in newer platform SDKs, // These are included in the linux C libraries and in newer platform SDKs,
// so should only be needed in VC++6 or earlier. // so should only be needed in VC++6 or earlier.
const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; // :: const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; // ::
const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; // ::_1 const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; // ::_1
static HMODULE hWs2_32Dll;
static int dll_refs;
//-----------------------------------------------------------------------------
// manual import of certain functions
// don't delay-load because we don't want to require these functions to be
// present. the user must be able to check if they are available (currently,
// on Win2k with IPv6 update or WinXP). can't use compile-time HAVE_* to
// make that decision because we don't want to distribute a separate EXE.
// function pointers, automatically initialized before any use of ws2_32.dll
static int (WINAPI *pgetnameinfo)(const struct sockaddr*, socklen_t, char*, socklen_t, char*, socklen_t, unsigned int);
static int (WINAPI *pgetaddrinfo)(const char*, const char*, const struct addrinfo*, struct addrinfo**);
static void (WINAPI *pfreeaddrinfo)(struct addrinfo*);
static HMODULE hWs2_32Dll;
static void ImportOptionalFunctions()
{
*(void**)&pgetnameinfo = GetProcAddress(hWs2_32Dll, "getnameinfo");
*(void**)&pgetaddrinfo = GetProcAddress(hWs2_32Dll, "getaddrinfo");
*(void**)&pfreeaddrinfo = GetProcAddress(hWs2_32Dll, "freeaddrinfo");
}
int getnameinfo(const struct sockaddr* sa, socklen_t salen, char* host, socklen_t hostlen, char* serv, socklen_t servlen, unsigned int flags)
{
return pgetnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
}
int getaddrinfo(const char* nodename, const char* servname, const struct addrinfo* hints, struct addrinfo** res)
{
return pgetaddrinfo(nodename, servname, hints, res);
}
void freeaddrinfo(struct addrinfo* ai)
{
pfreeaddrinfo(ai);
}
//-----------------------------------------------------------------------------
static ModuleInitState initState;
// called from delay loader the first time a wsock function is called // called from delay loader the first time a wsock function is called
// (shortly before the actual wsock function is called). // (shortly before the actual wsock function is called).
static LibError wsock_actual_init() static LibError wsock_actual_init()
{ {
if(!ModuleShouldInitialize(&initState))
return INFO::OK;
hWs2_32Dll = LoadLibrary("ws2_32.dll"); hWs2_32Dll = LoadLibrary("ws2_32.dll");
// first time: call WSAStartup char d[1024];
if(!dll_refs++) int ret = WSAStartup(0x0002, d); // want 2.0
{ debug_assert(ret == 0);
char d[1024];
if(WSAStartup(0x0002, d) != 0) // want 2.0 ImportOptionalFunctions();
debug_warn("WSAStartup failed");
}
return INFO::OK; return INFO::OK;
} }
// called via module init mechanism. triggers wsock_actual_init when
// someone first calls a wsock function.
static LibError wsock_init() static LibError wsock_init()
{ {
WDLL_LOAD_NOTIFY("ws2_32", wsock_actual_init); // trigger wsock_actual_init when someone first calls a wsock function.
static WdllLoadNotify loadNotify = { "ws2_32", wsock_actual_init };
wdll_add_notify(&loadNotify);
return INFO::OK; return INFO::OK;
} }
static LibError wsock_shutdown() static LibError wsock_shutdown()
{ {
// call WSACleanup if DLL was used if(!ModuleShouldShutdown(&initState))
// (this way is easier to understand than ONCE in loop below) return INFO::OK;
if(dll_refs > 0)
if(WSACleanup() < 0)
debug_warn("WSACleanup failed");
// remove all references int ret = WSACleanup();
while(dll_refs-- > 0) debug_assert(ret >= 0);
FreeLibrary(hWs2_32Dll);
FreeLibrary(hWs2_32Dll);
return INFO::OK; return INFO::OK;
} }
// manual import instead of delay-load because we don't want to require
// these functions to be present. the user must be able to check if they
// are available (currently, on Win2k with IPv6 update or WinXP).
// can't use compile-time HAVE_* to make that decision because
// we don't want to distribute a separate binary for this.
//
// note: can't import at startup because we don't want to load wsock unless necessary
// don't use delay load because we don't want to confuse error handling for other users
//
// don't bother caching - these functions themselves take a while and aren't time-critical
static void* import(const char* name)
{
return GetProcAddress(hWs2_32Dll, name);
}
fp_getnameinfo_t import_getnameinfo() { return (fp_getnameinfo_t )import("getnameinfo" ); }
fp_getaddrinfo_t import_getaddrinfo() { return (fp_getaddrinfo_t )import("getaddrinfo" ); }
fp_freeaddrinfo_t import_freeaddrinfo() { return (fp_freeaddrinfo_t)import("freeaddrinfo"); }
uint16_t htons(uint16_t s)
{
return (s >> 8) | ((s & 0xff) << 8);
}

View File

@ -156,8 +156,8 @@ struct addrinfo
int ai_protocol; // 0 or IPPROTO_xxx for IPv4 and IPv6 int ai_protocol; // 0 or IPPROTO_xxx for IPv4 and IPv6
size_t ai_addrlen; // Length of ai_addr size_t ai_addrlen; // Length of ai_addr
char *ai_canonname; // Canonical name for nodename char *ai_canonname; // Canonical name for nodename
struct sockaddr *ai_addr; // Binary address struct sockaddr* ai_addr; // Binary address
struct addrinfo *ai_next; // Next structure in linked list struct addrinfo* ai_next; // Next structure in linked list
}; };
// Hint flags for getaddrinfo // Hint flags for getaddrinfo
@ -169,22 +169,9 @@ struct addrinfo
#define NI_MAXHOST 1025 #define NI_MAXHOST 1025
#define NI_MAXSERV 32 #define NI_MAXSERV 32
// Note that these are function pointers. They will be initialized by the extern int getnameinfo(const struct sockaddr*, socklen_t, char*, socklen_t, char*, socklen_t, unsigned int);
// wsock_init in wsock.cpp extern int getaddrinfo(const char*, const char*, const struct addrinfo*, struct addrinfo**);
typedef int (__stdcall *fp_getnameinfo_t)(const struct sockaddr *sa, socklen_t salen, char *node, extern void freeaddrinfo(struct addrinfo*);
socklen_t nodelen, char *serv, socklen_t servlen, unsigned int flags);
typedef int (__stdcall *fp_getaddrinfo_t)(const char *nodename, const char *servname,
const struct addrinfo *hints, struct addrinfo **res);
typedef void (__stdcall *fp_freeaddrinfo_t)(struct addrinfo *ai);
extern fp_getnameinfo_t import_getnameinfo();
extern fp_getaddrinfo_t import_getaddrinfo();
extern fp_freeaddrinfo_t import_freeaddrinfo();
#define getnameinfo (import_getnameinfo())
#define getaddrinfo (import_getaddrinfo())
#define freeaddrinfo (import_freeaddrinfo())
// getaddr/nameinfo error codes // getaddr/nameinfo error codes

View File

@ -20,8 +20,8 @@
#include "lib/sysdep/win/whrt/whrt.h" #include "lib/sysdep/win/whrt/whrt.h"
#pragma SECTION_PRE_LIBC(M) // late; dependent on whrt #pragma SECTION_INIT(7) // depends on whrt
WIN_REGISTER_FUNC(wtime_Init); WINIT_REGISTER_FUNC(wtime_Init);
#pragma FORCE_INCLUDE(wtime_Init) #pragma FORCE_INCLUDE(wtime_Init)
#pragma SECTION_RESTORE #pragma SECTION_RESTORE

View File

@ -23,7 +23,7 @@
#include "lib/posix/posix_pthread.h" #include "lib/posix/posix_pthread.h"
#include "lib/ogl.h" // needed to pull in the delay-loaded opengl32.dll #include "lib/ogl.h" // needed to pull in the delay-loaded opengl32.dll
#include "winit.h" #include "lib/module_init.h"
#include "wutil.h" #include "wutil.h"
@ -43,16 +43,6 @@
#endif #endif
#pragma SECTION_PRE_MAIN(K)
WIN_REGISTER_FUNC(wsdl_init);
#pragma FORCE_INCLUDE(wsdl_init)
#pragma SECTION_POST_ATEXIT(D)
WIN_REGISTER_FUNC(wsdl_shutdown);
#pragma FORCE_INCLUDE(wsdl_shutdown)
#pragma SECTION_RESTORE
// in fullscreen mode, i.e. not windowed. // in fullscreen mode, i.e. not windowed.
// video mode will be restored when app is deactivated. // video mode will be restored when app is deactivated.
static bool fullscreen; static bool fullscreen;
@ -1303,16 +1293,26 @@ inline void* SDL_GL_GetProcAddress(const char* name)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// init/shutdown // init/shutdown
static LibError wsdl_init() // note: winit no longer supports pre-main init since that requires users
// to manually call an init function at the start of main() (unreliable).
// we do all init in SDL_Init, which means that any printfs before then
// are lost. oh well.
// defend against calling SDL_Quit twice (GameSetup does this to work
// around ATI driver breakage)
static ModuleInitState initState;
int SDL_Init(Uint32 UNUSED(flags))
{ {
if(!ModuleShouldInitialize(&initState))
return 0;
hInst = GetModuleHandle(0); hInst = GetModuleHandle(0);
// redirect stdout to file (otherwise it's simply ignored on Win32). // redirect stdout to file (otherwise it's simply ignored on Win32).
// notes: // notes:
// - use full path for safety (works even if someone does chdir) // - use full path for safety (works even if someone does chdir)
// - SDL does this in its WinMain hook. we need to do this here // - SDL does this in its WinMain hook; see note above.
// (before main is called) instead of in SDL_Init to completely
// emulate SDL; bonus: we don't miss any output before SDL_Init.
char path[MAX_PATH]; char path[MAX_PATH];
snprintf(path, ARRAY_SIZE(path), "%s\\stdout.txt", win_exe_dir); snprintf(path, ARRAY_SIZE(path), "%s\\stdout.txt", win_exe_dir);
// ignore BoundsChecker warnings here. subsystem is set to "Windows" // ignore BoundsChecker warnings here. subsystem is set to "Windows"
@ -1320,8 +1320,7 @@ static LibError wsdl_init()
// stdout isn't associated with a lowio handle; _close ends up // stdout isn't associated with a lowio handle; _close ends up
// getting called with fd = -1. oh well, nothing we can do. // getting called with fd = -1. oh well, nothing we can do.
FILE* f = freopen(path, "wt", stdout); FILE* f = freopen(path, "wt", stdout);
if(!f) debug_assert(f);
debug_warn("stdout freopen failed");
#if CONFIG_PARANOIA #if CONFIG_PARANOIA
// disable buffering, so that no writes are lost even if the program // disable buffering, so that no writes are lost even if the program
@ -1331,12 +1330,14 @@ static LibError wsdl_init()
enable_kbd_hook(true); enable_kbd_hook(true);
return INFO::OK; return 0;
} }
void SDL_Quit()
static LibError wsdl_shutdown()
{ {
if(!ModuleShouldShutdown(&initState))
return;
is_shutdown = true; is_shutdown = true;
// redirected to stdout.txt in SDL_Init; // redirected to stdout.txt in SDL_Init;
@ -1348,19 +1349,4 @@ static LibError wsdl_shutdown()
video_shutdown(); video_shutdown();
enable_kbd_hook(false); enable_kbd_hook(false);
return INFO::OK;
}
// these are placebos. since some init needs to happen before main(),
// we take care of it all in the module init/shutdown hooks.
int SDL_Init(Uint32 UNUSED(flags))
{
return 0;
}
void SDL_Quit()
{
} }

View File

@ -18,7 +18,7 @@
#include "lib/path_util.h" #include "lib/path_util.h"
#include "lib/res/file/file.h" #include "lib/res/file/file.h"
#include "dll_ver.h" // dll_list_* #include "wdll_ver.h"
#include "win.h" #include "win.h"
#include "wutil.h" #include "wutil.h"
@ -53,7 +53,7 @@ typedef std::set<std::string> StringSet;
// <dir>: no trailing. // <dir>: no trailing.
static LibError add_oal_dlls_in_dir(const char* dir, StringSet* dlls) static LibError add_oal_dlls_in_dir(const char* dir, StringSet* dlls)
{ {
// note: dll_list_add requires the full DLL path but DirEnt only gives us // note: wdll_ver_list_add requires the full DLL path but DirEnt only gives us
// the name. for efficiency, we append this via PathPackage. // the name. for efficiency, we append this via PathPackage.
PathPackage pp; PathPackage pp;
RETURN_ERR(path_package_set_dir(&pp, dir)); RETURN_ERR(path_package_set_dir(&pp, dir));
@ -71,13 +71,13 @@ static LibError add_oal_dlls_in_dir(const char* dir, StringSet* dlls)
if(!IsOpenAlDll(&ent)) if(!IsOpenAlDll(&ent))
continue; continue;
// already in StringSet (i.e. has already been dll_list_add-ed) // already in StringSet (i.e. has already been wdll_ver_list_add-ed)
std::pair<StringSet::iterator, bool> ret = dlls->insert(ent.name); std::pair<StringSet::iterator, bool> ret = dlls->insert(ent.name);
if(!ret.second) // insert failed - element already there if(!ret.second) // insert failed - element already there
continue; continue;
(void)path_package_append_file(&pp, ent.name); (void)path_package_append_file(&pp, ent.name);
(void)dll_list_add(pp.path); (void)wdll_ver_list_add(pp.path);
} }
(void)dir_close(&d); (void)dir_close(&d);
@ -140,9 +140,9 @@ LibError win_get_snd_info()
{ {
// find all DLLs related to OpenAL, retrieve their versions, // find all DLLs related to OpenAL, retrieve their versions,
// and store in snd_drv_ver string. // and store in snd_drv_ver string.
dll_list_init(snd_drv_ver, SND_DRV_VER_LEN); wdll_ver_list_init(snd_drv_ver, SND_DRV_VER_LEN);
if(!wutil_IsVista()) if(!wutil_IsVista())
(void)dll_list_add(GetDirectSoundDriverPath()); (void)wdll_ver_list_add(GetDirectSoundDriverPath());
StringSet dlls; // ensures uniqueness StringSet dlls; // ensures uniqueness
(void)add_oal_dlls_in_dir(win_exe_dir, &dlls); (void)add_oal_dlls_in_dir(win_exe_dir, &dlls);
(void)add_oal_dlls_in_dir(win_sys_dir, &dlls); (void)add_oal_dlls_in_dir(win_sys_dir, &dlls);

View File

@ -8,39 +8,90 @@
// license: GPL; see lib/license.txt // 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 "precompiled.h"
#include "wstartup.h" #include "wstartup.h"
#include "winit.h" #include "win.h"
#include "wdbg.h" // wdbg_exception_filter #include <process.h> // __security_init_cookie
#include "win.h" // GetExceptionInformation #include <detours.h>
#if MSC_VERSION >= 1400 #include "winit.h"
#include <process.h> // __security_init_cookie #include "wdbg.h" // wdbg_exception_filter
#define NEED_COOKIE_INIT
#if MSC_VERSION
#pragma comment(lib, "detours.lib")
#pragma comment(lib, "detoured.lib")
#endif #endif
// this module is responsible for startup and triggering winit's calls to
// registered functions at the appropriate times. control flow overview:
// entry [-> RunWithinTryBlock] -> InitAndCallMain -> MainCRTStartup ->
// main -> wstartup_PreMainInit.
// our atexit handler is called as the last of them, provided constructors
// do not use atexit! (this requirement is documented)
//
// rationale: see declaration of wstartup_PreMainInit.
//-----------------------------------------------------------------------------
// do shutdown at exit
void wstartup_PreMainInit() // 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_CallPreMainFunctions(); winit_CallShutdownFunctions();
atexit(winit_CallShutdownFunctions); RealExitProcess(uExitCode);
// no point redirecting stdout yet - the current directory
// may be incorrect (file_set_root not yet called).
// (w)sdl will take care of it anyway.
} }
static void InstallExitHook()
{
// (can't do this in a static initializer because they haven't run yet!)
RealExitProcess = ExitProcess;
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
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. // these aren't defined in VC include files, so we have to do it manually.
#ifdef USE_WINMAIN #ifdef USE_WINMAIN
@ -53,10 +104,9 @@ EXTERN_C int mainCRTStartup(void);
// entry and entry_noSEH) // entry and entry_noSEH)
static int InitAndCallMain() static int InitAndCallMain()
{ {
// perform all initialization that needs to run before _cinit winit_CallInitFunctions();
// (i.e. when C++ ctors are called).
// be very careful to avoid non-stateless libc functions! InstallExitHook();
winit_CallPreLibcFunctions();
int ret; int ret;
#ifdef USE_WINMAIN #ifdef USE_WINMAIN
@ -68,6 +118,9 @@ static int InitAndCallMain()
} }
//-----------------------------------------------------------------------------
// entry point and SEH wrapper
typedef int(*PfnIntVoid)(void); typedef int(*PfnIntVoid)(void);
static int RunWithinTryBlock(PfnIntVoid func) static int RunWithinTryBlock(PfnIntVoid func)
@ -87,7 +140,7 @@ static int RunWithinTryBlock(PfnIntVoid func)
int entry() int entry()
{ {
#ifdef NEED_COOKIE_INIT #if MSC_VERSION >= 1400
// 2006-02-16 workaround for R6035 on VC8: // 2006-02-16 workaround for R6035 on VC8:
// //
// SEH code compiled with /GS pushes a "security cookie" onto the // SEH code compiled with /GS pushes a "security cookie" onto the
@ -111,7 +164,7 @@ int entry()
// (e.g. unit tests, where it's better to let the debugger handle any errors) // (e.g. unit tests, where it's better to let the debugger handle any errors)
int entry_noSEH() int entry_noSEH()
{ {
#ifdef NEED_COOKIE_INIT #if MSC_VERSION >= 1400
// see above. this is also necessary here in case pre-libc init // see above. this is also necessary here in case pre-libc init
// functions use SEH. // functions use SEH.
__security_init_cookie(); __security_init_cookie();

View File

@ -11,34 +11,6 @@
#ifndef INCLUDED_WSTARTUP #ifndef INCLUDED_WSTARTUP
#define INCLUDED_WSTARTUP #define INCLUDED_WSTARTUP
/**
* call at the beginning of main(). exact requirements: after _cinit AND
* before any use of sysdep/win/* or call to atexit.
*
* rationale:
* our Windows-specific init code needs to run before the rest of the
* main() code. ideally this would happen automagically.
* 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.
*
* the consequence is that automatic init isn't viable. this is
* unfortunate because integration into new projects requires
* remembering to call the init function, but it can't be helped.
**/
extern void wstartup_PreMainInit();
// entry points (normal and without SEH wrapper; see definition) // entry points (normal and without SEH wrapper; see definition)
EXTERN_C int entry(); EXTERN_C int entry();
EXTERN_C int entry_noSEH(); EXTERN_C int entry_noSEH();

View File

@ -20,11 +20,11 @@
#include "winit.h" #include "winit.h"
#pragma SECTION_PRE_LIBC(B) #pragma SECTION_INIT(1) // early, several modules depend on us
WIN_REGISTER_FUNC(wutil_PreLibcInit); WINIT_REGISTER_FUNC(wutil_PreLibcInit);
#pragma FORCE_INCLUDE(wutil_PreLibcInit) #pragma FORCE_INCLUDE(wutil_PreLibcInit)
#pragma SECTION_POST_ATEXIT(Y) #pragma SECTION_SHUTDOWN(8)
WIN_REGISTER_FUNC(wutil_Shutdown); WINIT_REGISTER_FUNC(wutil_Shutdown);
#pragma FORCE_INCLUDE(wutil_Shutdown) #pragma FORCE_INCLUDE(wutil_Shutdown)
#pragma SECTION_RESTORE #pragma SECTION_RESTORE

View File

@ -13,9 +13,6 @@
#include "lib/res/sound/snd_mgr.h" #include "lib/res/sound/snd_mgr.h"
#include "lib/res/graphics/tex.h" #include "lib/res/graphics/tex.h"
#include "lib/res/graphics/cursor.h" #include "lib/res/graphics/cursor.h"
#if OS_WIN
# include "lib/sysdep/win/wstartup.h"
#endif
#include "ps/CConsole.h" #include "ps/CConsole.h"
#include "ps/CLogger.h" #include "ps/CLogger.h"
@ -878,11 +875,6 @@ void Shutdown(uint flags)
void EarlyInit() void EarlyInit()
{ {
#if OS_WIN
// see discussion at declaration of wstartup_PreMainInit.
wstartup_PreMainInit(); // must come before any use of lib/sysdep/win
#endif
MICROLOG(L"EarlyInit"); MICROLOG(L"EarlyInit");
// If you ever want to catch a particular allocation: // If you ever want to catch a particular allocation: