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;