aboutsummaryrefslogtreecommitdiff
path: root/src/include/autoinit_funcs.h
blob: 0c2e47bda212851e56b47fe78f23f24d21ed65c1 (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
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
/*
 *  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 */