1 /* Copyright (C) 2010 Wildfire Games. 2 * 3 * Permission is hereby granted, free of charge, to any person obtaining 4 * a copy of this software and associated documentation files (the 5 * "Software"), to deal in the Software without restriction, including 6 * without limitation the rights to use, copy, modify, merge, publish, 7 * distribute, sublicense, and/or sell copies of the Software, and to 8 * permit persons to whom the Software is furnished to do so, subject to 9 * the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included 12 * in all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 */ 22 23 /* 24 * windows-specific module init and shutdown mechanism 25 */ 26 27 #ifndef INCLUDED_WINIT 28 #define INCLUDED_WINIT 29 30 /* 31 32 Overview 33 -------- 34 35 This facility allows registering init and shutdown functions with only 36 one line of code and zero runtime overhead. It provides for dependencies 37 between modules, allowing groups of functions to run before others. 38 39 40 Details 41 ------- 42 43 Participating modules store function pointer(s) to their init and/or 44 shutdown function in a specific COFF section. The sections are 45 grouped according to the desired notification and the order in which 46 functions are to be called (useful if one module depends on another). 47 They are then gathered by the linker and arranged in alphabetical order. 48 Placeholder variables in the sections indicate where the series of 49 functions begins and ends for a given notification time. 50 At runtime, all of the function pointers between the markers are invoked. 51 52 53 Example 54 ------- 55 56 (at file scope:) 57 WINIT_REGISTER_MAIN_INIT(InitCallback); 58 59 60 Rationale 61 --------- 62 63 Several methods of module init are possible: (see Large Scale C++ Design) 64 - on-demand initialization: each exported function would have to check 65 if init already happened. that would be brittle and hard to verify. 66 - singleton: variant of the above, but not applicable to a 67 procedural interface (and quite ugly to boot). 68 - registration: static constructors call a central notification function. 69 module dependencies would be quite difficult to express - this would 70 require a graph or separate lists for each priority (clunky). 71 worse, a fatal flaw is that other C++ constructors may depend on the 72 modules we are initializing and already have run. there is no way 73 to influence ctor call order between separate source files, so 74 this is out of the question. 75 - linker-based registration: same as above, but the linker takes care 76 of assembling various functions into one sorted table. the list of 77 init functions is available before C++ ctors have run. incidentally, 78 zero runtime overhead is incurred. unfortunately, this approach is 79 MSVC-specific. however, the MS CRT uses a similar method for its 80 init, so this is expected to remain supported. 81 82 */ 83 84 85 //----------------------------------------------------------------------------- 86 // section declarations 87 88 // section names are of the format ".WINIT${type}{group}". 89 // {type} is I for initialization- or S for shutdown functions. 90 // {group} is [0, 9] - see below. 91 // note: __declspec(allocate) requires declaring segments in advance via 92 // #pragma section. 93 #pragma section(".WINIT$I$", read) 94 #pragma section(".WINIT$I0", read) 95 #pragma section(".WINIT$I1", read) 96 #pragma section(".WINIT$I2", read) 97 #pragma section(".WINIT$I6", read) 98 #pragma section(".WINIT$I7", read) 99 #pragma section(".WINIT$IZ", read) 100 #pragma section(".WINIT$S$", read) 101 #pragma section(".WINIT$S0", read) 102 #pragma section(".WINIT$S1", read) 103 #pragma section(".WINIT$S6", read) 104 #pragma section(".WINIT$S7", read) 105 #pragma section(".WINIT$S8", read) 106 #pragma section(".WINIT$SZ", read) 107 #pragma comment(linker, "/merge:.WINIT=.rdata") 108 109 110 //----------------------------------------------------------------------------- 111 // Function groups 112 113 // to allow correct ordering of module init in the face of dependencies, 114 // we introduce 'groups'. all functions in one are called before those in 115 // the next higher group, but order within the group is undefined. 116 // (this is because the linker sorts sections alphabetically but doesn't 117 // specify the order in which object files are processed.) 118 119 // these macros register a function to be called at the given time. 120 // usage: invoke at file scope, passing a function identifier/symbol. 121 // rationale: 122 // - __declspec(allocate) requires section declarations, but allows users to 123 // write only one line (instead of needing an additional #pragma data_seg) 124 // - fixed groups instead of passing a group number are more clear and 125 // encourage thinking about init order. (__declspec(allocate) requires 126 // a single string literal anyway and doesn't support string merging) 127 // - why EXTERN_C and __pragma? VC8's link-stage optimizer believes 128 // the static function pointers defined by WINIT_REGISTER_* to be unused; 129 // unless action is taken, they would be removed. to prevent this, we 130 // forcibly include the function pointer symbols. this means the variable 131 // must be extern, not static. the linker needs to know the decorated 132 // symbol name, so we disable mangling via EXTERN_C. 133 134 // very early init; must not fail, since error handling code *crashes* 135 // if called before these have completed. 136 #define WINIT_REGISTER_CRITICAL_INIT(func) __pragma(comment(linker, "/include:" STRINGIZE(DECORATED_NAME(p##func)))) static Status func(); EXTERN_C __declspec(allocate(".WINIT$I0")) Status (*p##func)(void) = func 137 138 // meant for modules with dependents but whose init is complicated and may 139 // raise error/warning messages (=> can't go in WINIT_REGISTER_CRITICAL_INIT) 140 #define WINIT_REGISTER_EARLY_INIT(func) __pragma(comment(linker, "/include:" STRINGIZE(DECORATED_NAME(p##func)))) static Status func(); EXTERN_C __declspec(allocate(".WINIT$I1")) Status (*p##func)(void) = func 141 142 // available for dependents of WINIT_REGISTER_EARLY_INIT-modules that 143 // must still come before WINIT_REGISTER_MAIN_INIT. 144 #define WINIT_REGISTER_EARLY_INIT2(func) __pragma(comment(linker, "/include:" STRINGIZE(DECORATED_NAME(p##func)))) static Status func(); EXTERN_C __declspec(allocate(".WINIT$I2")) Status (*p##func)(void) = func 145 146 // most modules will go here unless they are often used or 147 // have many dependents. 148 #define WINIT_REGISTER_MAIN_INIT(func) __pragma(comment(linker, "/include:" STRINGIZE(DECORATED_NAME(p##func)))) static Status func(); EXTERN_C __declspec(allocate(".WINIT$I6")) Status (*p##func)(void) = func 149 150 // available for any modules that may need to come after 151 // WINIT_REGISTER_MAIN_INIT (unlikely) 152 #define WINIT_REGISTER_LATE_INIT(func) __pragma(comment(linker, "/include:" STRINGIZE(DECORATED_NAME(p##func)))) static Status func(); EXTERN_C __declspec(allocate(".WINIT$I7")) Status (*p##func)(void) = func 153 154 #define WINIT_REGISTER_EARLY_SHUTDOWN(func) __pragma(comment(linker, "/include:" STRINGIZE(DECORATED_NAME(p##func)))) static Status func(); EXTERN_C __declspec(allocate(".WINIT$S0")) Status (*p##func)(void) = func 155 #define WINIT_REGISTER_EARLY_SHUTDOWN2(func) __pragma(comment(linker, "/include:" STRINGIZE(DECORATED_NAME(p##func)))) static Status func(); EXTERN_C __declspec(allocate(".WINIT$S1")) Status (*p##func)(void) = func 156 #define WINIT_REGISTER_MAIN_SHUTDOWN(func) __pragma(comment(linker, "/include:" STRINGIZE(DECORATED_NAME(p##func)))) static Status func(); EXTERN_C __declspec(allocate(".WINIT$S6")) Status (*p##func)(void) = func 157 #define WINIT_REGISTER_LATE_SHUTDOWN(func) __pragma(comment(linker, "/include:" STRINGIZE(DECORATED_NAME(p##func)))) static Status func(); EXTERN_C __declspec(allocate(".WINIT$S7")) Status (*p##func)(void) = func 158 #define WINIT_REGISTER_LATE_SHUTDOWN2(func) __pragma(comment(linker, "/include:" STRINGIZE(DECORATED_NAME(p##func)))) static Status func(); EXTERN_C __declspec(allocate(".WINIT$S8")) Status (*p##func)(void) = func 159 160 //----------------------------------------------------------------------------- 161 162 /** 163 * call each registered function. 164 * 165 * if this is called before CRT initialization, callbacks must not use any 166 * non-stateless CRT functions such as atexit. see wstartup.h for the 167 * current status on this issue. 168 **/ 169 extern void winit_CallInitFunctions(); 170 extern void winit_CallShutdownFunctions(); 171 172 #endif // #ifndef INCLUDED_WINIT 173