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 ctor (void)
56 {
57 string_add_exclusive (G_STRINGIFY (PREFIX) "_" "ctor");
58 }
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 dtor (void)
68 {
69 string_add_exclusive (G_STRINGIFY (PREFIX) "_" "dtor");
70
71 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 _Exit (EXIT_SUCCESS);
77 }
78 }
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 load_library (const char *path)
150 {
151 #ifndef _WIN32
152 library = dlopen (path, RTLD_NOW);
153 if (!library)
154 {
155 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 }
171
172 static void
173 unload_library (void)
174 {
175 #ifndef _WIN32
176 if (dlclose (library) != 0)
177 {
178 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 }
188
189 static void
190 test_app (void)
191 {
192 #if G_HAS_CONSTRUCTORS
193 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 }
199
200 static void
201 test_lib (gconstpointer data)
202 {
203 const char *library_path = (const char*) data;
204
205 /* Constructors */
206 load_library (library_path);
207
208 #if G_HAS_CONSTRUCTORS
209 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 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 }
230
231 int
232 main (int argc, char *argv[])
233 {
234
235 const char *libname = G_STRINGIFY (LIB_NAME);
236 const char *builddir;
237 char *path;
238 int ret;
239
240 g_assert_nonnull ((builddir = g_getenv ("G_TEST_BUILDDIR")));
241
242 path = g_build_filename (builddir, libname, NULL);
243
244 g_test_init (&argc, &argv, NULL);
245
246 g_test_add_func ("/constructor/application", test_app);
247 g_test_add_data_func ("/constructor/library", path, test_lib);
248
249 ret = g_test_run ();
250 g_assert_cmpint (ret, ==, 0);
251
252 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 return EXIT_FAILURE;
258 }
259
260 #endif /* BUILD_TEST_EXECUTABLE */