Branch data Line data Source code
1 : : /* constructor.c - Test for constructors 2 : : * 3 : : * Copyright © 2023 Luca Bacci 4 : : * 5 : : * SPDX-License-Identifier: LGPL-2.1-or-later 6 : : * 7 : : * This library is free software; you can redistribute it and/or 8 : : * modify it under the terms of the GNU Lesser General Public 9 : : * License as published by the Free Software Foundation; either 10 : : * version 2.1 of the License, or (at your option) any later version. 11 : : * 12 : : * This library is distributed in the hope that it will be useful, 13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 : : * Lesser General Public License for more details. 16 : : * 17 : : * You should have received a copy of the GNU Lesser General Public License 18 : : * along with this library; if not, see <http://www.gnu.org/licenses/>. 19 : : */ 20 : : 21 : : #include <glib.h> 22 : : #include "../gconstructorprivate.h" 23 : : 24 : : #ifndef _WIN32 25 : : #include <dlfcn.h> 26 : : #else 27 : : #include <windows.h> 28 : : #endif 29 : : 30 : : #if defined(_WIN32) 31 : : #define MODULE_IMPORT \ 32 : : __declspec (dllimport) 33 : : #else 34 : : #define MODULE_IMPORT 35 : : #endif 36 : : 37 : : MODULE_IMPORT 38 : : void string_add_exclusive (const char *string); 39 : : 40 : : MODULE_IMPORT 41 : : void string_check (const char *string); 42 : : 43 : : MODULE_IMPORT 44 : : int string_find (const char *string); 45 : : 46 : : #if G_HAS_CONSTRUCTORS 47 : : 48 : : #ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA 49 : : #pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS (ctor) 50 : : #endif 51 : : 52 : : G_DEFINE_CONSTRUCTOR (ctor) 53 : : 54 : : static void 55 : 1 : ctor (void) 56 : : { 57 : 1 : string_add_exclusive (G_STRINGIFY (PREFIX) "_" "ctor"); 58 : 1 : } 59 : : 60 : : #ifdef G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA 61 : : #pragma G_DEFINE_DESTRUCTOR_PRAGMA_ARGS (dtor) 62 : : #endif 63 : : 64 : : G_DEFINE_DESTRUCTOR (dtor) 65 : : 66 : : static void 67 : 1 : dtor (void) 68 : : { 69 : 1 : string_add_exclusive (G_STRINGIFY (PREFIX) "_" "dtor"); 70 : : 71 [ - + - - ]: 1 : if (string_find ("app_dtor") && string_find ("lib_dtor")) 72 : : { 73 : : /* All destructors were invoked, this is the last. 74 : : * Call _Exit (EXIT_SUCCESS) to exit immediately 75 : : * with a success code */ 76 : 0 : _Exit (EXIT_SUCCESS); 77 : : } 78 : 1 : } 79 : : 80 : : #endif /* G_HAS_CONSTRUCTORS */ 81 : : 82 : : 83 : : #if defined (_WIN32) && defined (G_HAS_TLS_CALLBACKS) 84 : : 85 : : extern IMAGE_DOS_HEADER __ImageBase; 86 : : 87 : : static inline HMODULE 88 : : this_module (void) 89 : : { 90 : : return (HMODULE) &__ImageBase; 91 : : } 92 : : 93 : : G_DEFINE_TLS_CALLBACK (tls_callback) 94 : : 95 : : static void NTAPI 96 : : tls_callback (PVOID hInstance, 97 : : DWORD dwReason, 98 : : LPVOID lpvReserved) 99 : : { 100 : : /* The HINSTANCE we get must match the address of __ImageBase */ 101 : : g_assert_true (hInstance == this_module ()); 102 : : 103 : : #ifdef BUILD_TEST_EXECUTABLE 104 : : /* Yes, we can call GetModuleHandle (NULL) with the loader lock */ 105 : : g_assert_true (hInstance == GetModuleHandle (NULL)); 106 : : #endif 107 : : 108 : : switch (dwReason) 109 : : { 110 : : case DLL_PROCESS_ATTACH: 111 : : { 112 : : #ifndef BUILD_TEST_EXECUTABLE 113 : : /* the library is explicitly loaded */ 114 : : g_assert_null (lpvReserved); 115 : : #endif 116 : : string_add_exclusive (G_STRINGIFY (PREFIX) "_" "tlscb_process_attach"); 117 : : } 118 : : break; 119 : : 120 : : case DLL_THREAD_ATTACH: 121 : : break; 122 : : 123 : : case DLL_THREAD_DETACH: 124 : : break; 125 : : 126 : : case DLL_PROCESS_DETACH: 127 : : { 128 : : #ifndef BUILD_TEST_EXECUTABLE 129 : : /* the library is explicitly unloaded */ 130 : : g_assert_null (lpvReserved); 131 : : #endif 132 : : string_add_exclusive (G_STRINGIFY (PREFIX) "_" "tlscb_process_detach"); 133 : : } 134 : : break; 135 : : 136 : : default: 137 : : g_assert_not_reached (); 138 : : break; 139 : : } 140 : : } 141 : : 142 : : #endif /* _WIN32 && G_HAS_TLS_CALLBACKS */ 143 : : 144 : : #ifdef BUILD_TEST_EXECUTABLE 145 : : 146 : : void *library; 147 : : 148 : : static void 149 : 0 : load_library (const char *path) 150 : : { 151 : : #ifndef _WIN32 152 : 0 : library = dlopen (path, RTLD_NOW); 153 [ # # ]: 0 : if (!library) 154 : : { 155 : 0 : g_error ("%s (%s) failed: %s", "dlopen", path, dlerror ()); 156 : : } 157 : : #else 158 : : wchar_t *path_utf16 = g_utf8_to_utf16 (path, -1, NULL, NULL, NULL); 159 : : g_assert_nonnull (path_utf16); 160 : : 161 : : library = LoadLibraryW (path_utf16); 162 : : if (!library) 163 : : { 164 : : g_error ("%s (%s) failed with error code %u", 165 : : "FreeLibrary", path, (unsigned int) GetLastError ()); 166 : : } 167 : : 168 : : g_free (path_utf16); 169 : : #endif 170 : 0 : } 171 : : 172 : : static void 173 : 0 : unload_library (void) 174 : : { 175 : : #ifndef _WIN32 176 [ # # ]: 0 : if (dlclose (library) != 0) 177 : : { 178 : 0 : g_error ("%s failed: %s", "dlclose", dlerror ()); 179 : : } 180 : : #else 181 : : if (!FreeLibrary (library)) 182 : : { 183 : : g_error ("%s failed with error code %u", 184 : : "FreeLibrary", (unsigned int) GetLastError ()); 185 : : } 186 : : #endif 187 : 0 : } 188 : : 189 : : static void 190 : 0 : test_app (void) 191 : : { 192 : : #if G_HAS_CONSTRUCTORS 193 : 0 : string_check ("app_" "ctor"); 194 : : #endif 195 : : #if defined (_WIN32) && defined (G_HAS_TLS_CALLBACKS) 196 : : string_check ("app_" "tlscb_process_attach"); 197 : : #endif 198 : 0 : } 199 : : 200 : : static void 201 : 0 : test_lib (gconstpointer data) 202 : : { 203 : 0 : const char *library_path = (const char*) data; 204 : : 205 : : /* Constructors */ 206 : 0 : load_library (library_path); 207 : : 208 : : #if G_HAS_CONSTRUCTORS 209 : 0 : string_check ("lib_" "ctor"); 210 : : #endif 211 : : #if defined (_WIN32) && defined (G_HAS_TLS_CALLBACKS) 212 : : string_check ("lib_" "tlscb_process_attach"); 213 : : #endif 214 : : 215 : : /* Destructors */ 216 : 0 : unload_library (); 217 : : 218 : : #if G_HAS_DESTRUCTORS 219 : : /* Destructors in dynamically-loaded libraries do not 220 : : * necessarily run on dlclose. On some systems dlclose 221 : : * is effectively a no-op (e.g with the Musl LibC) and 222 : : * destructors run at program exit */ 223 : : g_test_message ("Destructors run on module unload: %s\n", 224 : : string_find ("lib_" "dtor") ? "yes" : "no"); 225 : : #endif 226 : : #if defined (_WIN32) && defined (G_HAS_TLS_CALLBACKS) 227 : : string_check ("lib_" "tlscb_process_detach"); 228 : : #endif 229 : 0 : } 230 : : 231 : : int 232 : 0 : main (int argc, char *argv[]) 233 : : { 234 : : 235 : 0 : const char *libname = G_STRINGIFY (LIB_NAME); 236 : : const char *builddir; 237 : : char *path; 238 : : int ret; 239 : : 240 : 0 : g_assert_nonnull ((builddir = g_getenv ("G_TEST_BUILDDIR"))); 241 : : 242 : 0 : path = g_build_filename (builddir, libname, NULL); 243 : : 244 : 0 : g_test_init (&argc, &argv, NULL); 245 : : 246 : 0 : g_test_add_func ("/constructor/application", test_app); 247 : 0 : g_test_add_data_func ("/constructor/library", path, test_lib); 248 : : 249 : 0 : ret = g_test_run (); 250 : 0 : g_assert_cmpint (ret, ==, 0); 251 : : 252 : 0 : g_free (path); 253 : : 254 : : /* Return EXIT_FAILURE from main. The last destructor will 255 : : * call _Exit (EXIT_SUCCESS) if everything went well. This 256 : : * is a way to test that destructors get invoked */ 257 : 0 : return EXIT_FAILURE; 258 : : } 259 : : 260 : : #endif /* BUILD_TEST_EXECUTABLE */