173 lines
5.4 KiB
C
173 lines
5.4 KiB
C
/**
|
|
* This file has no copyright assigned and is placed in the Public Domain.
|
|
* This file is part of the mingw-w64 runtime package.
|
|
* No warranty is given; refer to the file DISCLAIMER.PD within this package.
|
|
*/
|
|
|
|
#include <sect_attribs.h>
|
|
|
|
#ifndef WIN32_LEAN_AND_MEAN
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#endif
|
|
#include <windows.h>
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <corecrt_startup.h>
|
|
#include <process.h>
|
|
|
|
|
|
typedef void (__thiscall * dtor_fn)(void*);
|
|
int __mingw_cxa_atexit(dtor_fn dtor, void *obj, void *dso);
|
|
int __mingw_cxa_thread_atexit(dtor_fn dtor, void *obj, void *dso);
|
|
|
|
typedef struct dtor_obj dtor_obj;
|
|
struct dtor_obj {
|
|
dtor_fn dtor;
|
|
void *obj;
|
|
dtor_obj *next;
|
|
};
|
|
|
|
HANDLE __dso_handle;
|
|
extern char __mingw_module_is_dll;
|
|
|
|
static CRITICAL_SECTION lock;
|
|
static int inited = 0;
|
|
static dtor_obj *global_dtors = NULL;
|
|
static DWORD tls_dtors_slot = TLS_OUT_OF_INDEXES;
|
|
|
|
int __mingw_cxa_atexit(dtor_fn dtor, void *obj, void *dso) {
|
|
if (!inited)
|
|
return 1;
|
|
assert(!dso || dso == &__dso_handle);
|
|
dtor_obj *handler = (dtor_obj *) calloc(1, sizeof(*handler));
|
|
if (!handler)
|
|
return 1;
|
|
handler->dtor = dtor;
|
|
handler->obj = obj;
|
|
EnterCriticalSection(&lock);
|
|
handler->next = global_dtors;
|
|
global_dtors = handler;
|
|
LeaveCriticalSection(&lock);
|
|
return 0;
|
|
}
|
|
|
|
static void run_dtor_list(dtor_obj **ptr) {
|
|
if (!ptr)
|
|
return;
|
|
while (*ptr) {
|
|
dtor_obj *cur = *ptr;
|
|
*ptr = cur->next;
|
|
cur->dtor(cur->obj);
|
|
free(cur);
|
|
}
|
|
}
|
|
|
|
int __mingw_cxa_thread_atexit(dtor_fn dtor, void *obj, void *dso) {
|
|
if (!inited)
|
|
return 1;
|
|
assert(!dso || dso == &__dso_handle);
|
|
|
|
dtor_obj **head = (dtor_obj **)TlsGetValue(tls_dtors_slot);
|
|
if (!head) {
|
|
head = (dtor_obj **) calloc(1, sizeof(*head));
|
|
if (!head)
|
|
return 1;
|
|
TlsSetValue(tls_dtors_slot, head);
|
|
}
|
|
dtor_obj *handler = (dtor_obj *) calloc(1, sizeof(*handler));
|
|
if (!handler)
|
|
return 1;
|
|
handler->dtor = dtor;
|
|
handler->obj = obj;
|
|
handler->next = *head;
|
|
*head = handler;
|
|
return 0;
|
|
}
|
|
|
|
static void WINAPI tls_atexit_callback(HANDLE __UNUSED_PARAM(hDllHandle), DWORD dwReason, LPVOID __UNUSED_PARAM(lpReserved)) {
|
|
if (dwReason == DLL_PROCESS_DETACH) {
|
|
dtor_obj **p = (dtor_obj **)TlsGetValue(tls_dtors_slot);
|
|
run_dtor_list(p);
|
|
free(p);
|
|
TlsSetValue(tls_dtors_slot, NULL);
|
|
TlsFree(tls_dtors_slot);
|
|
run_dtor_list(&global_dtors);
|
|
}
|
|
}
|
|
|
|
static void WINAPI tls_callback(HANDLE hDllHandle, DWORD dwReason, LPVOID __UNUSED_PARAM(lpReserved)) {
|
|
dtor_obj **p;
|
|
switch (dwReason) {
|
|
case DLL_PROCESS_ATTACH:
|
|
if (inited == 0) {
|
|
InitializeCriticalSection(&lock);
|
|
__dso_handle = hDllHandle;
|
|
tls_dtors_slot = TlsAlloc();
|
|
/*
|
|
* We can only call _register_thread_local_exe_atexit_callback once
|
|
* in a process; if we call it a second time the process terminates.
|
|
* When DLLs are unloaded, this callback is invoked before we run the
|
|
* _onexit tables, but for exes, we need to ask this to be called before
|
|
* all other registered atexit functions.
|
|
* Since we are registered as a normal TLS callback, we will be called
|
|
* another time later as well, but that doesn't matter, it's safe to
|
|
* invoke this with DLL_PROCESS_DETACH twice.
|
|
*/
|
|
if (!__mingw_module_is_dll)
|
|
_register_thread_local_exe_atexit_callback(tls_atexit_callback);
|
|
}
|
|
inited = 1;
|
|
break;
|
|
case DLL_PROCESS_DETACH:
|
|
/*
|
|
* If there are other threads still running that haven't been detached,
|
|
* we don't attempt to run their destructors (MSVC doesn't either), but
|
|
* simply leak the destructor list and whatever resources the destructors
|
|
* would have released.
|
|
*
|
|
* From Vista onwards, we could have used FlsAlloc to get a TLS key that
|
|
* runs a destructor on each thread that has a value attached ot it, but
|
|
* since MSVC doesn't run destructors on other threads in this case,
|
|
* users shouldn't assume it and we don't attempt to do anything potentially
|
|
* risky about it. TL;DR, threads with pending TLS destructors for a DLL
|
|
* need to be joined before unloading the DLL.
|
|
*
|
|
* This gets called both when exiting cleanly (via exit or returning from
|
|
* main, or when a DLL is unloaded), and when exiting bypassing some of
|
|
* the cleanup, by calling _exit or ExitProcess. In the latter cases,
|
|
* destructors (both TLS and global) in loaded DLLs still get called,
|
|
* but none get called for the main executable. This matches what the
|
|
* standard says, but differs from what MSVC does with a dynamically
|
|
* linked CRT (which still runs TLS destructors for the main thread).
|
|
*/
|
|
if (__mingw_module_is_dll) {
|
|
p = (dtor_obj **)TlsGetValue(tls_dtors_slot);
|
|
run_dtor_list(p);
|
|
free(p);
|
|
TlsSetValue(tls_dtors_slot, NULL);
|
|
/* For DLLs, run dtors when detached. For EXEs, run dtors via the
|
|
* thread local atexit callback, to make sure they don't run when
|
|
* exiting the process with _exit or ExitProcess. */
|
|
run_dtor_list(&global_dtors);
|
|
TlsFree(tls_dtors_slot);
|
|
}
|
|
if (inited == 1) {
|
|
inited = 0;
|
|
DeleteCriticalSection(&lock);
|
|
}
|
|
break;
|
|
case DLL_THREAD_ATTACH:
|
|
break;
|
|
case DLL_THREAD_DETACH:
|
|
p = (dtor_obj **)TlsGetValue(tls_dtors_slot);
|
|
run_dtor_list(p);
|
|
free(p);
|
|
TlsSetValue(tls_dtors_slot, NULL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
_CRTALLOC(".CRT$XLB") PIMAGE_TLS_CALLBACK __xl_b = (PIMAGE_TLS_CALLBACK) tls_callback;
|