aboutsummaryrefslogtreecommitdiff
path: root/src/include/autoinit_funcs.h
blob: c4f3d014f52d904a83a1a7ef4b65b6700f4c60e8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
/*
 *  AutoinitFuncs: Automatic Initialization and Deinitialization Functions
 *  Copyright(C) 2014-2017  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
   initialization and one for deinitialization, 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 doesn'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 initializer or deinitializer 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 optimized (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.
* 0x01093001 = 1.9.30-1.
*/
#define AUTOINIT_FUNCS_VERSION 0x01000100

#if defined(__GNUC__)
/* 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__ */

/* "_attribute__ ((constructor))" is supported by GCC, clang and
   Sun/Oracle compiler starting from version 12.1. */
#if (defined(__GNUC__) && ! 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) (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 initialization 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)

/* required for atexit() */
#include <stdlib.h>

/* 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(_M_X64) || defined(_M_AMD64)
#define W32_VARDECORPREFIX
#define W32_DECORVARNAME(v) v
#define W32_VARDECORPEFIXSTR ""
#elif defined(_M_IX86) || defined(_X86_)
#define W32_VARDECORPREFIX _
#define W32_DECORVARNAME(v) _ ## v
#define W32_VARDECORPEFIXSTR "_"
#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_VARDECORPEFIXSTR _STRMACRO ( \
    W32_INITHELPERVARNAME (f))

/* Declare section (segment), put variable pointing to init function to chosen segment,
   force linker to include variable to avoid omitting by optimizer */
/* Initialization function must be declared as
   int __cdecl FuncName(void) */
/* Return value is ignored for C++ initializers */
/* For C initializers: startup process is aborted if initializer return non-zero */
#define W32_FPTR_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

/* Section (segment) names for pointers to initializers */
#define W32_SEG_INIT_C_USER   ".CRT$XCU"
#define W32_SEG_INIT_C_LIB    ".CRT$XCL"
#define W32_SEG_INIT_CXX_USER ".CRT$XIU"
#define W32_SEG_INIT_CXX_LIB  ".CRT$XIL"

/* Declare macro for different initializers sections */
/* Macro can be used several times to register several initializers */
/* Once function is registered as initializer, it will be called automatically
   during application startup */
/* "lib" initializers are called before "user" initializers */
/* "C" initializers are called before "C++" initializers */
#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 initializers */
/* Once function is registered as initializer, it will be called automatically
   during application startup */
/* Define AUTOINIT_FUNCS_FORCE_USER_LVL_INIT to register initializers
   at user level even if building library */
#ifdef __cplusplus
#if ((defined(_LIB) && ! defined(_CONSOLE)) || defined(_USRDLL)) && \
  ! defined(AUTOINIT_FUNCS_FORCE_USER_LVL_INIT)
#define W32_REGISTER_INIT(F) W32_REG_INIT_CXX_LIB (F)
#else  /* ! _LIB && ! _DLL */
#define W32_REGISTER_INIT(F) W32_REG_INIT_CXX_USER (F)
#endif /* ! _LIB && ! _DLL */
#else  /* !__cplusplus*/
#if ((defined(_LIB) && ! defined(_CONSOLE)) || defined(_USRDLL)) && \
  ! defined(AUTOINIT_FUNCS_FORCE_USER_LVL_INIT)
#define W32_REGISTER_INIT(F) W32_REG_INIT_C_LIB (F)
#else  /* ! _LIB && ! _DLL */
#define W32_REGISTER_INIT(F) W32_REG_INIT_C_USER (F)
#endif /* ! _LIB && ! _DLL */
#endif /* !__cplusplus*/

#else /* _USRDLL */

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif /* WIN32_LEAN_AND_MEAN */
/* Required for DllMain */
#include <Windows.h>
#endif /* _USRDLL */


#if ! defined(_USRDLL) || defined(AUTOINIT_FUNCS_FORCE_STATIC_REG)
#define W32_SET_INIT_AND_DEINIT(FI,FD) \
  void __cdecl _W32_deinit_helper_ ## FD (void) \
  { (void) (FD) (); } \
  int __cdecl _W32_init_helper_ ## FI (void) \
  { (void) (FI) (); atexit (_W32_deinit_helper_ ## FD); return 0; } \
  W32_REGISTER_INIT (_W32_init_helper_ ## FI)
#else  /* _USRDLL */

/* 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) \
  { 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) \
  { 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 initializers/deinitializers 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 initializers/deinitializers are not supported */
#ifdef EMIT_ERROR_IF_AUTOINIT_FUNCS_ARE_NOT_SUPPORTED
#error \
  Compiler/platform don 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 initializers/deinitializers are not supported */
#define _AUTOINIT_FUNCS_ARE_NOT_SUPPORTED 1

#endif /* !__GNUC__ && !_MSC_FULL_VER */
#endif /* !AUTOINIT_FUNCS_INCLUDED */