master
1/**
2 * This file has no copyright assigned and is placed in the Public Domain.
3 * This file is part of the mingw-w64 runtime package.
4 * No warranty is given; refer to the file DISCLAIMER.PD within this package.
5 */
6
7#include <sect_attribs.h>
8
9#ifndef WIN32_LEAN_AND_MEAN
10#define WIN32_LEAN_AND_MEAN
11#endif
12#include <windows.h>
13
14#include <assert.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <corecrt_startup.h>
18#include <process.h>
19
20
21typedef void (__thiscall * dtor_fn)(void*);
22int __mingw_cxa_atexit(dtor_fn dtor, void *obj, void *dso);
23int __mingw_cxa_thread_atexit(dtor_fn dtor, void *obj, void *dso);
24
25typedef struct dtor_obj dtor_obj;
26struct dtor_obj {
27 dtor_fn dtor;
28 void *obj;
29 dtor_obj *next;
30};
31
32HANDLE __dso_handle;
33extern char __mingw_module_is_dll;
34
35static CRITICAL_SECTION lock;
36static int inited = 0;
37static dtor_obj *global_dtors = NULL;
38static DWORD tls_dtors_slot = TLS_OUT_OF_INDEXES;
39
40int __mingw_cxa_atexit(dtor_fn dtor, void *obj, void *dso) {
41 if (!inited)
42 return 1;
43 assert(!dso || dso == &__dso_handle);
44 dtor_obj *handler = (dtor_obj *) calloc(1, sizeof(*handler));
45 if (!handler)
46 return 1;
47 handler->dtor = dtor;
48 handler->obj = obj;
49 EnterCriticalSection(&lock);
50 handler->next = global_dtors;
51 global_dtors = handler;
52 LeaveCriticalSection(&lock);
53 return 0;
54}
55
56static void run_dtor_list(dtor_obj **ptr) {
57 if (!ptr)
58 return;
59 while (*ptr) {
60 dtor_obj *cur = *ptr;
61 *ptr = cur->next;
62 cur->dtor(cur->obj);
63 free(cur);
64 }
65}
66
67int __mingw_cxa_thread_atexit(dtor_fn dtor, void *obj, void *dso) {
68 if (!inited)
69 return 1;
70 assert(!dso || dso == &__dso_handle);
71
72 dtor_obj **head = (dtor_obj **)TlsGetValue(tls_dtors_slot);
73 if (!head) {
74 head = (dtor_obj **) calloc(1, sizeof(*head));
75 if (!head)
76 return 1;
77 TlsSetValue(tls_dtors_slot, head);
78 }
79 dtor_obj *handler = (dtor_obj *) calloc(1, sizeof(*handler));
80 if (!handler)
81 return 1;
82 handler->dtor = dtor;
83 handler->obj = obj;
84 handler->next = *head;
85 *head = handler;
86 return 0;
87}
88
89static void WINAPI tls_atexit_callback(HANDLE __UNUSED_PARAM(hDllHandle), DWORD dwReason, LPVOID __UNUSED_PARAM(lpReserved)) {
90 if (dwReason == DLL_PROCESS_DETACH) {
91 dtor_obj **p = (dtor_obj **)TlsGetValue(tls_dtors_slot);
92 run_dtor_list(p);
93 free(p);
94 TlsSetValue(tls_dtors_slot, NULL);
95 TlsFree(tls_dtors_slot);
96 run_dtor_list(&global_dtors);
97 }
98}
99
100static void WINAPI tls_callback(HANDLE hDllHandle, DWORD dwReason, LPVOID __UNUSED_PARAM(lpReserved)) {
101 dtor_obj **p;
102 switch (dwReason) {
103 case DLL_PROCESS_ATTACH:
104 if (inited == 0) {
105 InitializeCriticalSection(&lock);
106 __dso_handle = hDllHandle;
107 tls_dtors_slot = TlsAlloc();
108 /*
109 * We can only call _register_thread_local_exe_atexit_callback once
110 * in a process; if we call it a second time the process terminates.
111 * When DLLs are unloaded, this callback is invoked before we run the
112 * _onexit tables, but for exes, we need to ask this to be called before
113 * all other registered atexit functions.
114 * Since we are registered as a normal TLS callback, we will be called
115 * another time later as well, but that doesn't matter, it's safe to
116 * invoke this with DLL_PROCESS_DETACH twice.
117 */
118 if (!__mingw_module_is_dll)
119 _register_thread_local_exe_atexit_callback(tls_atexit_callback);
120 }
121 inited = 1;
122 break;
123 case DLL_PROCESS_DETACH:
124 /*
125 * If there are other threads still running that haven't been detached,
126 * we don't attempt to run their destructors (MSVC doesn't either), but
127 * simply leak the destructor list and whatever resources the destructors
128 * would have released.
129 *
130 * From Vista onwards, we could have used FlsAlloc to get a TLS key that
131 * runs a destructor on each thread that has a value attached ot it, but
132 * since MSVC doesn't run destructors on other threads in this case,
133 * users shouldn't assume it and we don't attempt to do anything potentially
134 * risky about it. TL;DR, threads with pending TLS destructors for a DLL
135 * need to be joined before unloading the DLL.
136 *
137 * This gets called both when exiting cleanly (via exit or returning from
138 * main, or when a DLL is unloaded), and when exiting bypassing some of
139 * the cleanup, by calling _exit or ExitProcess. In the latter cases,
140 * destructors (both TLS and global) in loaded DLLs still get called,
141 * but none get called for the main executable. This matches what the
142 * standard says, but differs from what MSVC does with a dynamically
143 * linked CRT (which still runs TLS destructors for the main thread).
144 */
145 if (__mingw_module_is_dll) {
146 p = (dtor_obj **)TlsGetValue(tls_dtors_slot);
147 run_dtor_list(p);
148 free(p);
149 TlsSetValue(tls_dtors_slot, NULL);
150 /* For DLLs, run dtors when detached. For EXEs, run dtors via the
151 * thread local atexit callback, to make sure they don't run when
152 * exiting the process with _exit or ExitProcess. */
153 run_dtor_list(&global_dtors);
154 TlsFree(tls_dtors_slot);
155 }
156 if (inited == 1) {
157 inited = 0;
158 DeleteCriticalSection(&lock);
159 }
160 break;
161 case DLL_THREAD_ATTACH:
162 break;
163 case DLL_THREAD_DETACH:
164 p = (dtor_obj **)TlsGetValue(tls_dtors_slot);
165 run_dtor_list(p);
166 free(p);
167 TlsSetValue(tls_dtors_slot, NULL);
168 break;
169 }
170}
171
172_CRTALLOC(".CRT$XLB") PIMAGE_TLS_CALLBACK __xl_b = tls_callback;