blob: 0c2e47bda212851e56b47fe78f23f24d21ed65c1 [file] [log] [blame]
/*
* 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
* <http://www.gnu.org/licenses/>.
*/
/*
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 <stdlib.h>
#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 <stdlib.h> /* 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 <Windows.h> /* 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 */