• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
21 typedef void (__thiscall * dtor_fn)(void*);
22 int __cxa_atexit(dtor_fn dtor, void *obj, void *dso);
23 int __cxa_thread_atexit(dtor_fn dtor, void *obj, void *dso);
24 
25 typedef struct dtor_obj dtor_obj;
26 struct dtor_obj {
27   dtor_fn dtor;
28   void *obj;
29   dtor_obj *next;
30 };
31 
32 HANDLE __dso_handle;
33 extern char __mingw_module_is_dll;
34 
35 static CRITICAL_SECTION lock;
36 static int inited = 0;
37 static dtor_obj *global_dtors = NULL;
38 static __thread dtor_obj *tls_dtors = NULL;
39 
__cxa_atexit(dtor_fn dtor,void * obj,void * dso)40 int __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 
run_dtor_list(dtor_obj ** ptr)56 static void run_dtor_list(dtor_obj **ptr) {
57   dtor_obj *list = *ptr;
58   while (list) {
59     list->dtor(list->obj);
60     dtor_obj *next = list->next;
61     free(list);
62     list = next;
63   }
64   *ptr = NULL;
65 }
66 
__cxa_thread_atexit(dtor_fn dtor,void * obj,void * dso)67 int __cxa_thread_atexit(dtor_fn dtor, void *obj, void *dso) {
68   if (!inited)
69     return 1;
70   assert(!dso || dso == &__dso_handle);
71   dtor_obj *handler = (dtor_obj *) calloc(1, sizeof(*handler));
72   if (!handler)
73     return 1;
74   handler->dtor = dtor;
75   handler->obj = obj;
76   handler->next = tls_dtors;
77   tls_dtors = handler;
78   return 0;
79 }
80 
tls_atexit_callback(HANDLE __UNUSED_PARAM (hDllHandle),DWORD dwReason,LPVOID __UNUSED_PARAM (lpReserved))81 static void WINAPI tls_atexit_callback(HANDLE __UNUSED_PARAM(hDllHandle), DWORD dwReason, LPVOID __UNUSED_PARAM(lpReserved)) {
82   if (dwReason == DLL_PROCESS_DETACH) {
83     run_dtor_list(&tls_dtors);
84     run_dtor_list(&global_dtors);
85   }
86 }
87 
tls_callback(HANDLE hDllHandle,DWORD dwReason,LPVOID __UNUSED_PARAM (lpReserved))88 static void WINAPI tls_callback(HANDLE hDllHandle, DWORD dwReason, LPVOID __UNUSED_PARAM(lpReserved)) {
89   switch (dwReason) {
90   case DLL_PROCESS_ATTACH:
91     if (inited == 0) {
92       InitializeCriticalSection(&lock);
93       __dso_handle = hDllHandle;
94       /*
95        * We can only call _register_thread_local_exe_atexit_callback once
96        * in a process; if we call it a second time the process terminates.
97        * When DLLs are unloaded, this callback is invoked before we run the
98        * _onexit tables, but for exes, we need to ask this to be called before
99        * all other registered atexit functions.
100        * Since we are registered as a normal TLS callback, we will be called
101        * another time later as well, but that doesn't matter, it's safe to
102        * invoke this with DLL_PROCESS_DETACH twice.
103        */
104       if (!__mingw_module_is_dll)
105         _register_thread_local_exe_atexit_callback(tls_atexit_callback);
106     }
107     inited = 1;
108     break;
109   case DLL_PROCESS_DETACH:
110     /*
111      * If there are other threads still running that haven't been detached,
112      * we don't attempt to run their destructors (MSVC doesn't either), but
113      * simply leak the destructor list and whatever resources the destructors
114      * would have released.
115      *
116      * From Vista onwards, we could have used FlsAlloc to get a TLS key that
117      * runs a destructor on each thread that has a value attached ot it, but
118      * since MSVC doesn't run destructors on other threads in this case,
119      * users shouldn't assume it and we don't attempt to do anything potentially
120      * risky about it. TL;DR, threads with pending TLS destructors for a DLL
121      * need to be joined before unloading the DLL.
122      *
123      * This gets called both when exiting cleanly (via exit or returning from
124      * main, or when a DLL is unloaded), and when exiting bypassing some of
125      * the cleanup, by calling _exit or ExitProcess. In the latter cases,
126      * destructors (both TLS and global) in loaded DLLs still get called,
127      * but only TLS destructors get called for the main executable, global
128      * variables' destructors don't run. (This matches what MSVC does with
129      * a dynamically linked CRT.)
130      */
131     run_dtor_list(&tls_dtors);
132     if (__mingw_module_is_dll) {
133       /* For DLLs, run dtors when detached. For EXEs, run dtors via the
134        * thread local atexit callback, to make sure they don't run when
135        * exiting the process with _exit or ExitProcess. */
136       run_dtor_list(&global_dtors);
137     }
138     if (inited == 1) {
139       inited = 0;
140       DeleteCriticalSection(&lock);
141     }
142     break;
143   case DLL_THREAD_ATTACH:
144     break;
145   case DLL_THREAD_DETACH:
146     run_dtor_list(&tls_dtors);
147     break;
148   }
149 }
150 
151 _CRTALLOC(".CRT$XLE") PIMAGE_TLS_CALLBACK __xl_e = (PIMAGE_TLS_CALLBACK) tls_callback;
152