The attached patch implements premature DLL unloading prevention in __cxa_thread_atexit for Cygwin and MinGW-w64 targets. The mingw.org target is welcome to do the same in their os_defines.h, but this code does require Windows XP/2003, and they have historically catered to older platforms.

MinGW cannot practically implement __cxa_thread_atexit_impl because it has no control over the contents of its libc (msvcrt), and implementing this in the (static) mingw32 support library would be of little benefit because it would still end up statically linked into libstdc++. Also, the GPL-with-exceptions libstdc++ implementation cannot be copied as a basis of our own __cxa_thread_atexit_impl due to licensing conflicts with both Cygwin (copyright assignment) and MinGW (public domain).

The testsuite shows no regressions with this patch.

--
Yaakov Selkowitz
Associate Software Engineer, ARM
Red Hat, Inc.
2014-08-13  Yaakov Selkowitz  <yselk...@redhat.com>

        libstdc++-v3/
        * config/os/mingw32-w64/os_defines.h (_GLIBCXX_THREAD_ATEXIT_WIN32):
        Define.
        * config/os/newlib/os_defines.h (_GLIBCXX_THREAD_ATEXIT_WIN32):
        Ditto.
        * libsupc++/atexit_thread.cc [_GLIBCXX_THREAD_ATEXIT_WIN32]:
        #include <windows.h>.
        (struct elt): Add dll member.
        (run): Decrement dll refcount.
        (__cxxabiv1::__cxa_thread_atexit): Increment dll refcount.

Index: libstdc++-v3/config/os/mingw32-w64/os_defines.h
===================================================================
--- libstdc++-v3/config/os/mingw32-w64/os_defines.h     (revision 213759)
+++ libstdc++-v3/config/os/mingw32-w64/os_defines.h     (working copy)
@@ -78,4 +78,9 @@
 #define _GLIBCXX_LLP64 1
 #endif
 
+// Enable use of GetModuleHandleEx (requires Windows XP/2003) in
+// __cxa_thread_atexit to prevent modules from being unloaded before
+// their dtors are called
+#define _GLIBCXX_THREAD_ATEXIT_WIN32 1
+
 #endif
Index: libstdc++-v3/config/os/newlib/os_defines.h
===================================================================
--- libstdc++-v3/config/os/newlib/os_defines.h  (revision 213759)
+++ libstdc++-v3/config/os/newlib/os_defines.h  (working copy)
@@ -47,6 +47,12 @@
 
 // See libstdc++/20806.
 #define _GLIBCXX_HAVE_DOS_BASED_FILESYSTEM 1
+
+// Enable use of GetModuleHandleEx (requires Windows XP/2003) in
+// __cxa_thread_atexit to prevent modules from being unloaded before
+// their dtors are called
+#define _GLIBCXX_THREAD_ATEXIT_WIN32 1
+
 #endif
 
 #endif
Index: libstdc++-v3/libsupc++/atexit_thread.cc
===================================================================
--- libstdc++-v3/libsupc++/atexit_thread.cc     (revision 213759)
+++ libstdc++-v3/libsupc++/atexit_thread.cc     (working copy)
@@ -25,6 +25,10 @@
 #include <cstdlib>
 #include <new>
 #include "bits/gthr.h"
+#ifdef _GLIBCXX_THREAD_ATEXIT_WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
 
 #if _GLIBCXX_HAVE___CXA_THREAD_ATEXIT_IMPL
 
@@ -47,6 +51,9 @@
     void (*destructor)(void *);
     void *object;
     elt *next;
+#ifdef _GLIBCXX_THREAD_ATEXIT_WIN32
+    HMODULE dll;
+#endif
   };
 
   // Keep a per-thread list of cleanups in gthread_key storage.
@@ -62,6 +69,11 @@
       {
        elt *old_e = e;
        e->destructor (e->object);
+#ifdef _GLIBCXX_THREAD_ATEXIT_WIN32
+       /* Decrement DLL count */
+       if (e->dll)
+         FreeLibrary (e->dll);
+#endif
        e = e->next;
        delete (old_e);
       }
@@ -133,6 +145,14 @@
   new_elt->destructor = dtor;
   new_elt->object = obj;
   new_elt->next = first;
+#ifdef _GLIBCXX_THREAD_ATEXIT_WIN32
+  /* Store the DLL address for a later call to FreeLibrary in new_elt and
+     increment DLL load count.  This blocks the unloading of the DLL
+     before the thread-local dtors have been called.  This does NOT help
+     if FreeLibrary/dlclose is called in excess. */
+  GetModuleHandleExW (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
+                     (LPCWSTR) dtor, &new_elt->dll);
+#endif
 
   if (__gthread_active_p ())
     __gthread_setspecific (key, new_elt);

Reply via email to