/* * AutoinitFuncs: Automatic Initialization and Deinitialization Functions * Copyright(C) 2014-2023 Karlson2k (Evgeny Grin) * * This header is free software; you can redistribute it and / or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This header is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this header; if not, see * . */ /* General usage is simple: include this header, declare or define two functions with zero parameters (void) and any return type: one for initialisation and one for deinitialisation, add _SET_INIT_AND_DEINIT_FUNCS(FuncInitName, FuncDeInitName) to the code and functions will be automatically called during application startup and shutdown. This is useful for libraries as libraries don't have direct access to main() functions. Example: ------------------------------------------------- #include #include "autoinit_funcs.h" int someVar; void* somePtr; void libInit(void) { someVar = 3; somePtr = malloc(100); } void libDeinit(void) { free(somePtr); } _SET_INIT_AND_DEINIT_FUNCS(libInit,libDeinit); ------------------------------------------------- If initialiser or deinitialiser function is not needed, just define it as empty function. This header should work with GCC, clang, MSVC (2010 or later) and SunPro / Sun Studio / Oracle Solaris Studio / Oracle Developer Studio compiler. Supported C and C++ languages; application, static and dynamic (DLL) libraries; non-optimized (Debug) and optimised (Release) compilation and linking. For more information see header code and comments in code. */ #ifndef AUTOINIT_FUNCS_INCLUDED #define AUTOINIT_FUNCS_INCLUDED 1 /** * Current version of the header in packed BCD form. * 0x01093001 = 1.9.30-1. */ #define AUTOINIT_FUNCS_VERSION 0x01001000 #if defined(__GNUC__) || defined(__clang__) /* if possible - check for supported attribute */ #ifdef __has_attribute #if ! __has_attribute (constructor) || ! __has_attribute (destructor) #define _GNUC_ATTR_CONSTR_NOT_SUPPORTED 1 #endif /* !__has_attribute(constructor) || !__has_attribute(destructor) */ #endif /* __has_attribute */ #endif /* __GNUC__ */ /* "__has_attribute__ ((constructor))" is supported by GCC, clang and Sun/Oracle compiler starting from version 12.1. */ #if ((defined(__GNUC__) || defined(__clang__)) && \ ! defined(_GNUC_ATTR_CONSTR_NOT_SUPPORTED)) || \ (defined(__SUNPRO_C) && __SUNPRO_C + 0 >= 0x5100) #define GNUC_SET_INIT_AND_DEINIT(FI,FD) \ void __attribute__ ((constructor)) _GNUC_init_helper_ ## FI (void); \ void __attribute__ ((destructor)) _GNUC_deinit_helper_ ## FD (void); \ void __attribute__ ((constructor)) _GNUC_init_helper_ ## FI (void) \ { (void) (FI) (); } \ void __attribute__ ((destructor)) _GNUC_deinit_helper_ ## FD (void) \ { (void) (FD) (); } \ struct _GNUC_dummy_str_ ## FI {int i;} #define _SET_INIT_AND_DEINIT_FUNCS(FI,FD) GNUC_SET_INIT_AND_DEINIT (FI,FD) #define _AUTOINIT_FUNCS_ARE_SUPPORTED 1 #elif defined(_MSC_FULL_VER) && _MSC_VER + 0 >= 1600 /* Make sure that your project/sources define: _LIB if building a static library (_LIB is ignored if _CONSOLE is defined); _USRDLL if building DLL-library; not defined both _LIB and _USRDLL if building an application */ /* Define AUTOINIT_FUNCS_DECLARE_STATIC_REG if you need macro declaration for registering static initialisation functions even if you building DLL */ /* Define AUTOINIT_FUNCS_FORCE_STATIC_REG if you want to set main macro _SET_INIT_AND_DEINIT_FUNCS to static version even if building a DLL */ /* Stringify macros */ #define _INSTRMACRO(a) #a #define _STRMACRO(a) _INSTRMACRO (a) #if ! defined(_USRDLL) || defined(AUTOINIT_FUNCS_DECLARE_STATIC_REG) \ || defined(AUTOINIT_FUNCS_FORCE_STATIC_REG) /* Use "C" linkage for variable to simplify variable decoration */ #ifdef __cplusplus #define W32_INITVARDECL extern "C" #else #define W32_INITVARDECL extern #endif /* How variable is decorated by compiler */ #if (defined(_WIN32) || defined(_WIN64)) \ && ! defined(_M_IX86) && ! defined(_X86_) #if ! defined(_M_X64) && ! defined(_M_AMD64) && ! defined(_x86_64_) \ && ! defined(_M_ARM) && ! defined(_M_ARM64) #pragma message(__FILE__ "(" _STRMACRO(__LINE__) ") : warning AIFW001 : " \ "Untested architecture, linker may fail with unresolved symbol") #endif /* ! _M_X64 && ! _M_AMD64 && ! _x86_64_ && ! _M_ARM && ! _M_ARM64 */ #define W32_VARDECORPREFIX #define W32_DECORVARNAME(v) v #define W32_VARDECORPREFIXSTR "" #elif defined(_WIN32) && (defined(_M_IX86) || defined(_X86_)) #define W32_VARDECORPREFIX _ #define W32_DECORVARNAME(v) _ ## v #define W32_VARDECORPREFIXSTR "_" #else #error Do not know how to decorate symbols for this architecture #endif /* Internal variable prefix (can be any) */ #define W32_INITHELPERVARNAME(f) _initHelperDummy_ ## f #define W32_INITHELPERVARNAMEDECORSTR(f) \ W32_VARDECORPREFIXSTR _STRMACRO (W32_INITHELPERVARNAME (f)) /* Declare section (segment), put variable pointing to init function to chosen segment, force linker to always include variable to avoid omitting by optimiser */ /* Initialisation function must be declared as void __cdecl FuncName(void) */ /* "extern" with initialisation value means that variable is declared AND defined. */ #define W32_VFPTR_IN_SEG(S,F) \ __pragma (section (S,long,read)) \ __pragma (comment (linker, "/INCLUDE:" W32_INITHELPERVARNAMEDECORSTR (F))) \ W32_INITVARDECL __declspec(allocate (S))void \ (__cdecl * W32_INITHELPERVARNAME (F))(void) = &F /* Sections (segments) for pointers to initialisers/deinitialisers */ /* Semi-officially suggested section for early initialisers (called before C++ objects initialisers), "void" return type */ #define W32_SEG_INIT_EARLY ".CRT$XCT" /* Semi-officially suggested section for late initialisers (called after C++ objects initialisers), "void" return type */ #define W32_SEG_INIT_LATE ".CRT$XCV" /* Unsafe sections (segments) for pointers to initialisers/deinitialisers */ /* C++ lib initialisers, "void" return type (reserved by the system!) */ #define W32_SEG_INIT_CXX_LIB ".CRT$XCL" /* C++ user initialisers, "void" return type (reserved by the system!) */ #define W32_SEG_INIT_CXX_USER ".CRT$XCU" /* Declare section (segment), put variable pointing to init function to chosen segment, force linker to always include variable to avoid omitting by optimiser */ /* Initialisation function must be declared as int __cdecl FuncName(void) */ /* Startup process is aborted if initialiser returns non-zero */ /* "extern" with initialisation value means that variable is declared AND defined. */ #define W32_IFPTR_IN_SEG(S,F) \ __pragma (section (S,long,read)) \ __pragma (comment (linker, "/INCLUDE:" W32_INITHELPERVARNAMEDECORSTR (F))) \ W32_INITVARDECL __declspec(allocate (S))int \ (__cdecl * W32_INITHELPERVARNAME (F))(void) = &F /* Unsafe sections (segments) for pointers to initialisers with "int" return type */ /* C lib initialisers, "int" return type (reserved by the system!). These initialisers are called before others. */ #define W32_SEG_INIT_C_LIB ".CRT$XIL" /* C user initialisers, "int" return type (reserved by the system!). These initialisers are called before others. */ #define W32_SEG_INIT_C_USER ".CRT$XIU" /* Declare macro for different initialisers sections */ /* Macro can be used several times to register several initialisers */ /* Once function is registered as initialiser, it will be called automatically during application startup */ #define W32_REG_INIT_EARLY(F) W32_VFPTR_IN_SEG (W32_SEG_INIT_EARLY,F) #define W32_REG_INIT_LATE(F) W32_VFPTR_IN_SEG (W32_SEG_INIT_LATE,F) /* Not recommended / unsafe */ /* "lib" initialisers are called before "user" initialisers */ /* "C" initialisers are called before "C++" initialisers */ #define W32_REG_INIT_C_USER(F) W32_FPTR_IN_SEG (W32_SEG_INIT_C_USER,F) #define W32_REG_INIT_C_LIB(F) W32_FPTR_IN_SEG (W32_SEG_INIT_C_LIB,F) #define W32_REG_INIT_CXX_USER(F) W32_FPTR_IN_SEG (W32_SEG_INIT_CXX_USER,F) #define W32_REG_INIT_CXX_LIB(F) W32_FPTR_IN_SEG (W32_SEG_INIT_CXX_LIB,F) /* Choose main register macro based on language and program type */ /* Assuming that _LIB or _USRDLL is defined for static or DLL-library */ /* Macro can be used several times to register several initialisers */ /* Once function is registered as initialiser, it will be called automatically during application startup */ /* Define AUTOINIT_FUNCS_FORCE_EARLY_INIT to force register as early initialiser */ /* Define AUTOINIT_FUNCS_FORCE_LATE_INIT to force register as late initialiser */ /* By default C++ static or DLL-library code and any C code and will be registered as early initialiser, while C++ non-library code will be registered as late initialiser */ #if (! defined(__cplusplus) || \ ((defined(_LIB) && ! defined(_CONSOLE)) || defined(_USRDLL)) || \ defined(AUTOINIT_FUNCS_FORCE_EARLY_INIT)) && \ ! defined(AUTOINIT_FUNCS_FORCE_LATE_INIT) #define W32_REGISTER_INIT(F) W32_REG_INIT_EARLY(F) #else #define W32_REGISTER_INIT(F) W32_REG_INIT_LATE(F) #endif #endif /* ! _USRDLL || ! AUTOINIT_FUNCS_DECLARE_STATIC_REG || AUTOINIT_FUNCS_FORCE_STATIC_REG */ #if ! defined(_USRDLL) || defined(AUTOINIT_FUNCS_FORCE_STATIC_REG) #include /* required for atexit() */ #define W32_SET_INIT_AND_DEINIT(FI,FD) \ void __cdecl _W32_init_helper_ ## FI (void); \ void __cdecl _W32_deinit_helper_ ## FD (void); \ void __cdecl _W32_init_helper_ ## FI (void) \ { (void) (FI) (); atexit (_W32_deinit_helper_ ## FD); } \ void __cdecl _W32_deinit_helper_ ## FD (void) \ { (void) (FD) (); } \ W32_REGISTER_INIT (_W32_init_helper_ ## FI) #else /* _USRDLL */ #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN 1 #endif /* WIN32_LEAN_AND_MEAN */ #include /* Required for DllMain */ /* If DllMain is already present in code, define AUTOINIT_FUNCS_CALL_USR_DLLMAIN and rename DllMain to usr_DllMain */ #ifndef AUTOINIT_FUNCS_CALL_USR_DLLMAIN #define W32_SET_INIT_AND_DEINIT(FI,FD) \ BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused); \ BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused) \ { (void) hinst; (void) unused; \ if (DLL_PROCESS_ATTACH==reason) {(void) (FI) ();} \ else if (DLL_PROCESS_DETACH==reason) {(void) (FD) ();} \ return TRUE; \ } struct _W32_dummy_strc_ ## FI {int i;} #else /* AUTOINIT_FUNCS_CALL_USR_DLLMAIN */ #define W32_SET_INIT_AND_DEINIT(FI,FD) \ BOOL WINAPI usr_DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused); \ BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused); \ BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused) \ { if (DLL_PROCESS_ATTACH==reason) {(void) (FI) ();} \ else if (DLL_PROCESS_DETACH==reason) {(void) (FD) ();} \ return usr_DllMain (hinst,reason,unused); \ } struct _W32_dummy_strc_ ## FI {int i;} #endif /* AUTOINIT_FUNCS_CALL_USR_DLLMAIN */ #endif /* _USRDLL */ #define _SET_INIT_AND_DEINIT_FUNCS(FI,FD) W32_SET_INIT_AND_DEINIT (FI,FD) /* Indicate that automatic initialisers/deinitialisers are supported */ #define _AUTOINIT_FUNCS_ARE_SUPPORTED 1 #else /* !__GNUC__ && !_MSC_FULL_VER */ /* Define EMIT_ERROR_IF_AUTOINIT_FUNCS_ARE_NOT_SUPPORTED before inclusion of header to abort compilation if automatic initialisers/deinitialisers are not supported */ #ifdef EMIT_ERROR_IF_AUTOINIT_FUNCS_ARE_NOT_SUPPORTED #error \ Compiler/platform does not support automatic calls of user-defined initializer and deinitializer #endif /* EMIT_ERROR_IF_AUTOINIT_FUNCS_ARE_NOT_SUPPORTED */ /* Do nothing */ #define _SET_INIT_AND_DEINIT_FUNCS(FI,FD) /* Indicate that automatic initialisers/deinitialisers are not supported */ #define _AUTOINIT_FUNCS_ARE_NOT_SUPPORTED 1 #endif /* !__GNUC__ && !_MSC_FULL_VER */ #endif /* !AUTOINIT_FUNCS_INCLUDED */