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 "winit.h"
#pragma SECTION_PRE_LIBC(B) // early; whrt depends on us
WIN_REGISTER_FUNC(wcpu_Init);
#pragma SECTION_INIT(2) // early; whrt depends on us
WINIT_REGISTER_FUNC(wcpu_Init);
#pragma FORCE_INCLUDE(wcpu_Init)
#pragma SECTION_RESTORE

View File

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

View File

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

View File

@ -22,8 +22,8 @@
#include "wutil.h"
#pragma SECTION_POST_ATEXIT(J)
WIN_REGISTER_FUNC(wdir_watch_shutdown);
#pragma SECTION_SHUTDOWN(5)
WINIT_REGISTER_FUNC(wdir_watch_shutdown);
#pragma FORCE_INCLUDE(wdir_watch_shutdown)
#pragma SECTION_RESTORE
@ -155,10 +155,10 @@ static LibError wdir_watch_shutdown()
hIOCP = INVALID_HANDLE_VALUE;
// 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;
watches.clear();
*/
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.
* Description : return DLL version information.
* =========================================================================
@ -9,7 +9,7 @@
// license: GPL; see lib/license.txt
#include "precompiled.h"
#include "dll_ver.h"
#include "wdll_ver.h"
#include <stdio.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.
//
static char* dll_list_buf;
static size_t dll_list_chars;
static char* dll_list_pos;
static char* ver_list_buf;
static size_t ver_list_chars;
static char* ver_list_pos;
// 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;
dll_list_chars = chars;
ver_list_pos = ver_list_buf = buf;
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
// we don't inadvertently load another one on the library search path.
// 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
if(!dll_list_pos)
// not be called before wdll_ver_list_init or after failure
if(!ver_list_pos)
WARN_RETURN(ERR::LOGIC);
// 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));
// 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.
// not first time: prepend comma to string (room was reserved above).
if(dll_list_pos != dll_list_buf)
dll_list_pos += sprintf(dll_list_pos, ", ");
if(ver_list_pos != ver_list_buf)
ver_list_pos += sprintf(ver_list_pos, ", ");
// extract filename.
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
if(len > 0)
{
dll_list_pos += len;
ver_list_pos += len;
return INFO::OK;
}
// didn't fit; complain
sprintf(dll_list_pos, "..."); // (room was reserved above)
dll_list_pos = 0; // poison pill, prevent further calls
sprintf(ver_list_pos, "..."); // (room was reserved above)
ver_list_pos = 0; // poison pill, prevent further calls
WARN_RETURN(ERR::BUF_SIZE);
}

View File

@ -1,26 +1,26 @@
/**
* =========================================================================
* File : dll_ver.h
* File : wdll_ver.h
* Project : 0 A.D.
* Description : return DLL version information.
* Description : return DLL version information
* =========================================================================
*/
// license: GPL; see lib/license.txt
#ifndef INCLUDED_DLL_VER
#define INCLUDED_DLL_VER
#ifndef INCLUDED_WDLL_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.
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.
//
// 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 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 "lib/sysdep/gfx.h"
#include "dll_ver.h" // dll_list_*
#include "wdll_ver.h"
#include "win.h"
#if MSC_VERSION
@ -145,7 +145,7 @@ static LibError win_get_gfx_drv_ver()
DWORD i;
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;
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"
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);
}
@ -186,7 +186,7 @@ static LibError win_get_gfx_drv_ver()
if(err != ERROR_SUCCESS) // error or no more items - bail
break;
if(type == REG_SZ)
ret = dll_list_add(drv_name);
ret = wdll_ver_list_add(drv_name);
} // for each value
RegCloseKey(hkOglDrivers);

View File

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

View File

@ -15,24 +15,23 @@
typedef LibError (*PfnLibErrorVoid)(void);
// pointers to start and end of function tables.
// note: COFF tosses out empty segments, so we have to put in one value
// (zero, because CallFunctionPointers has to ignore entries =0 anyway).
#pragma SECTION_PRE_LIBC(A)
PfnLibErrorVoid pre_libc_begin = 0;
#pragma SECTION_PRE_LIBC(Z)
PfnLibErrorVoid pre_libc_end = 0;
#pragma SECTION_PRE_MAIN(A)
PfnLibErrorVoid pre_main_begin = 0;
#pragma SECTION_PRE_MAIN(Z)
PfnLibErrorVoid pre_main_end = 0;
#pragma SECTION_POST_ATEXIT(A)
PfnLibErrorVoid shutdown_begin = 0;
#pragma SECTION_POST_ATEXIT(Z)
PfnLibErrorVoid shutdown_end = 0;
// notes:
// - COFF tosses out empty segments, so we have to put in one value
// (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:.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);
}
void winit_CallPreMainFunctions()
{
CallFunctionPointers(&pre_main_begin, &pre_main_end);
CallFunctionPointers(&initBegin, &initEnd);
}
void winit_CallShutdownFunctions()
{
CallFunctionPointers(&shutdown_begin, &shutdown_end);
CallFunctionPointers(&shutdownBegin, &shutdownEnd);
}

View File

@ -11,9 +11,6 @@
#ifndef INCLUDED_WINIT
#define INCLUDED_WINIT
// register functions to be called before libc init, before main,
// or after atexit.
//
// overview:
// participating modules store function pointer(s) to their init and/or
// shutdown function in a specific COFF section. the sections are
@ -26,21 +23,18 @@
//
// details:
// the section names are of the format ".LIB${type}{group}".
// {type} is C for pre-libc init, I for pre-main init, or
// T for terminators (last of the atexit handlers).
// {group} is [B, Y]; all functions in a group are called before those of
// the next (alphabetically) 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.
// another consequence is that groups A and Z must not be used!
// (data placed there might end up outside the start/end markers)
// {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_PRE_LIBC(G))
// WIN_REGISTER_FUNC(wtime_init);
// #pragma SECTION_INIT(7))
// WINIT_REGISTER_FUNC(wtime_init);
// #pragma FORCE_INCLUDE(wtime_init)
// #pragma SECTION_POST_ATEXIT(D))
// WIN_REGISTER_FUNC(wtime_shutdown);
// #pragma SECTION_SHUTDOWN(3))
// WINIT_REGISTER_FUNC(wtime_shutdown);
// #pragma FORCE_INCLUDE(wtime_shutdown)
// #pragma SECTION_RESTORE
//
@ -50,7 +44,7 @@
// 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: 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
// require a graph or separate lists for each priority (clunky).
// worse, a fatal flaw is that other C++ constructors may depend on the
@ -68,23 +62,21 @@
// notes:
// - #pragma cannot be packaged in macros due to expansion rules.
// - __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
// it, since we want to hide the section name behind a macro, which
// would require the abovementioned merging.
// note: the purpose of pre-libc init (with the resulting requirement that
// no CRT functions be used during init!) is to allow the use of the
// initialized module in static ctors.
#define SECTION_PRE_LIBC(group) data_seg(".LIB$C" #group)
#define SECTION_PRE_MAIN(group) data_seg(".LIB$I" #group)
#define SECTION_POST_ATEXIT(group) data_seg(".LIB$T" #group)
// 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)
#define WIN_REGISTER_FUNC(func)\
#define WINIT_REGISTER_FUNC(func)\
static LibError func(void);\
EXTERN_C LibError (*p##func)(void) = func
@ -92,8 +84,7 @@
* call each registered function.
* these are invoked by wstartup at the appropriate times.
**/
extern void winit_CallPreLibcFunctions();
extern void winit_CallPreMainFunctions();
extern void winit_CallInitFunctions();
extern void winit_CallShutdownFunctions();
#endif // #ifndef INCLUDED_WINIT

View File

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

View File

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

View File

@ -11,103 +11,115 @@
#include "precompiled.h"
#include "wsock.h"
#include "../delay_load.h"
#include "lib/sysdep/win/wdll_delay_load.h"
#include "wposix_internal.h"
#include "wsock_internal.h"
#include "lib/module_init.h"
#if MSC_VERSION
#pragma comment(lib, "ws2_32.lib")
#endif
#pragma SECTION_PRE_MAIN(K)
WIN_REGISTER_FUNC(wsock_init);
#pragma SECTION_INIT(5)
WINIT_REGISTER_FUNC(wsock_init);
#pragma FORCE_INCLUDE(wsock_init)
#pragma SECTION_POST_ATEXIT(D)
WIN_REGISTER_FUNC(wsock_shutdown);
#pragma SECTION_SHUTDOWN(5)
WINIT_REGISTER_FUNC(wsock_shutdown);
#pragma FORCE_INCLUDE(wsock_shutdown)
#pragma SECTION_RESTORE
uint16_t htons(uint16_t s)
{
return (s >> 8) | ((s & 0xff) << 8);
}
// IPv6 globals
// These are included in the linux C libraries and in newer platform SDKs,
// so should only be needed in VC++6 or earlier.
const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; // ::
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
// (shortly before the actual wsock function is called).
static LibError wsock_actual_init()
{
if(!ModuleShouldInitialize(&initState))
return INFO::OK;
hWs2_32Dll = LoadLibrary("ws2_32.dll");
// first time: call WSAStartup
if(!dll_refs++)
{
char d[1024];
if(WSAStartup(0x0002, d) != 0) // want 2.0
debug_warn("WSAStartup failed");
}
char d[1024];
int ret = WSAStartup(0x0002, d); // want 2.0
debug_assert(ret == 0);
ImportOptionalFunctions();
return INFO::OK;
}
// called via module init mechanism. triggers wsock_actual_init when
// someone first calls a wsock function.
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;
}
static LibError wsock_shutdown()
{
// call WSACleanup if DLL was used
// (this way is easier to understand than ONCE in loop below)
if(dll_refs > 0)
if(WSACleanup() < 0)
debug_warn("WSACleanup failed");
if(!ModuleShouldShutdown(&initState))
return INFO::OK;
// remove all references
while(dll_refs-- > 0)
FreeLibrary(hWs2_32Dll);
int ret = WSACleanup();
debug_assert(ret >= 0);
FreeLibrary(hWs2_32Dll);
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
size_t ai_addrlen; // Length of ai_addr
char *ai_canonname; // Canonical name for nodename
struct sockaddr *ai_addr; // Binary address
struct addrinfo *ai_next; // Next structure in linked list
struct sockaddr* ai_addr; // Binary address
struct addrinfo* ai_next; // Next structure in linked list
};
// Hint flags for getaddrinfo
@ -169,22 +169,9 @@ struct addrinfo
#define NI_MAXHOST 1025
#define NI_MAXSERV 32
// Note that these are function pointers. They will be initialized by the
// wsock_init in wsock.cpp
typedef int (__stdcall *fp_getnameinfo_t)(const struct sockaddr *sa, socklen_t salen, char *node,
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())
extern int getnameinfo(const struct sockaddr*, socklen_t, char*, socklen_t, char*, socklen_t, unsigned int);
extern int getaddrinfo(const char*, const char*, const struct addrinfo*, struct addrinfo**);
extern void freeaddrinfo(struct addrinfo*);
// getaddr/nameinfo error codes

View File

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

View File

@ -23,7 +23,7 @@
#include "lib/posix/posix_pthread.h"
#include "lib/ogl.h" // needed to pull in the delay-loaded opengl32.dll
#include "winit.h"
#include "lib/module_init.h"
#include "wutil.h"
@ -43,16 +43,6 @@
#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.
// video mode will be restored when app is deactivated.
static bool fullscreen;
@ -1303,16 +1293,26 @@ inline void* SDL_GL_GetProcAddress(const char* name)
//-----------------------------------------------------------------------------
// 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);
// redirect stdout to file (otherwise it's simply ignored on Win32).
// notes:
// - use full path for safety (works even if someone does chdir)
// - SDL does this in its WinMain hook. we need to do this here
// (before main is called) instead of in SDL_Init to completely
// emulate SDL; bonus: we don't miss any output before SDL_Init.
// - SDL does this in its WinMain hook; see note above.
char path[MAX_PATH];
snprintf(path, ARRAY_SIZE(path), "%s\\stdout.txt", win_exe_dir);
// 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
// getting called with fd = -1. oh well, nothing we can do.
FILE* f = freopen(path, "wt", stdout);
if(!f)
debug_warn("stdout freopen failed");
debug_assert(f);
#if CONFIG_PARANOIA
// disable buffering, so that no writes are lost even if the program
@ -1331,12 +1330,14 @@ static LibError wsdl_init()
enable_kbd_hook(true);
return INFO::OK;
return 0;
}
static LibError wsdl_shutdown()
void SDL_Quit()
{
if(!ModuleShouldShutdown(&initState))
return;
is_shutdown = true;
// redirected to stdout.txt in SDL_Init;
@ -1348,19 +1349,4 @@ static LibError wsdl_shutdown()
video_shutdown();
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/res/file/file.h"
#include "dll_ver.h" // dll_list_*
#include "wdll_ver.h"
#include "win.h"
#include "wutil.h"
@ -53,7 +53,7 @@ typedef std::set<std::string> StringSet;
// <dir>: no trailing.
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.
PathPackage pp;
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))
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);
if(!ret.second) // insert failed - element already there
continue;
(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);
@ -140,9 +140,9 @@ LibError win_get_snd_info()
{
// find all DLLs related to OpenAL, retrieve their versions,
// 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())
(void)dll_list_add(GetDirectSoundDriverPath());
(void)wdll_ver_list_add(GetDirectSoundDriverPath());
StringSet dlls; // ensures uniqueness
(void)add_oal_dlls_in_dir(win_exe_dir, &dlls);
(void)add_oal_dlls_in_dir(win_sys_dir, &dlls);

View File

@ -8,39 +8,90 @@
// 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 "winit.h"
#include "wdbg.h" // wdbg_exception_filter
#include "win.h" // GetExceptionInformation
#include "win.h"
#include <process.h> // __security_init_cookie
#include <detours.h>
#if MSC_VERSION >= 1400
#include <process.h> // __security_init_cookie
#define NEED_COOKIE_INIT
#include "winit.h"
#include "wdbg.h" // wdbg_exception_filter
#if MSC_VERSION
#pragma comment(lib, "detours.lib")
#pragma comment(lib, "detoured.lib")
#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);
// 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.
RealExitProcess(uExitCode);
}
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.
#ifdef USE_WINMAIN
@ -53,10 +104,9 @@ EXTERN_C int mainCRTStartup(void);
// entry and entry_noSEH)
static int InitAndCallMain()
{
// perform all initialization that needs to run before _cinit
// (i.e. when C++ ctors are called).
// be very careful to avoid non-stateless libc functions!
winit_CallPreLibcFunctions();
winit_CallInitFunctions();
InstallExitHook();
int ret;
#ifdef USE_WINMAIN
@ -68,6 +118,9 @@ static int InitAndCallMain()
}
//-----------------------------------------------------------------------------
// entry point and SEH wrapper
typedef int(*PfnIntVoid)(void);
static int RunWithinTryBlock(PfnIntVoid func)
@ -87,7 +140,7 @@ static int RunWithinTryBlock(PfnIntVoid func)
int entry()
{
#ifdef NEED_COOKIE_INIT
#if MSC_VERSION >= 1400
// 2006-02-16 workaround for R6035 on VC8:
//
// 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)
int entry_noSEH()
{
#ifdef NEED_COOKIE_INIT
#if MSC_VERSION >= 1400
// see above. this is also necessary here in case pre-libc init
// functions use SEH.
__security_init_cookie();

View File

@ -11,34 +11,6 @@
#ifndef 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)
EXTERN_C int entry();
EXTERN_C int entry_noSEH();

View File

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

View File

@ -13,9 +13,6 @@
#include "lib/res/sound/snd_mgr.h"
#include "lib/res/graphics/tex.h"
#include "lib/res/graphics/cursor.h"
#if OS_WIN
# include "lib/sysdep/win/wstartup.h"
#endif
#include "ps/CConsole.h"
#include "ps/CLogger.h"
@ -878,11 +875,6 @@ void Shutdown(uint flags)
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");
// If you ever want to catch a particular allocation: