From b7749822ed50355d6d9f09d5b45ab3a98d7dcaae Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Mon, 27 Oct 2014 21:19:40 +0000 Subject: [PATCH] * dcrt0.cc (cygwin_atexit): Add workaround for broken atexit calls in __gcc_register_frame of DLLs built with gcc-4.8.3-3. --- winsup/cygwin/ChangeLog | 5 +++++ winsup/cygwin/dcrt0.cc | 30 ++++++++++++++++++++++++++++++ winsup/cygwin/release/1.7.33 | 3 +++ 3 files changed, 38 insertions(+) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index bf6b00715..669f71237 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,8 @@ +2014-10-27 Corinna Vinschen + + * dcrt0.cc (cygwin_atexit): Add workaround for broken atexit calls + in __gcc_register_frame of DLLs built with gcc-4.8.3-3. + 2014-10-27 Corinna Vinschen * dlfcn.cc (dlopen): Drop patch accommodating SetDllDiretory from diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc index 930397f06..b2bbe8657 100644 --- a/winsup/cygwin/dcrt0.cc +++ b/winsup/cygwin/dcrt0.cc @@ -1236,7 +1236,37 @@ extern "C" int cygwin_atexit (void (*fn) (void)) { int res; + dll *d = dlls.find ((void *) _my_tls.retaddr ()); +#ifdef __x86_64__ + /* x86_64 DLLs created with GCC 4.8.3-3 register __gcc_deregister_frame + as atexit function using a call to atexit, rather than __cxa_atexit. + Due to GCC's aggressive optimizing, cygwin_atexit doesn't get the correct + return address on the stack. As a result it fails to get the HMODULE of + the caller and thus calls atexit rather than __cxa_atexit. Then, if the + module gets dlclosed, __cxa_finalize (called from dll_list::detach) can't + remove __gcc_deregister_frame from the atexit function chain. So at + process exit, __call_exitprocs calls __gcc_deregister_frame while the + module is already unloaded and the __gcc_deregister_frame function not + available ==> SEGV. + + Workaround: If dlls.find fails, and _my_tls.retaddr is a Cygwin function + address, and fn is a function address in another DLL, try to find the + dll entry of the DLL containing fn. Then check if fn is the address of + the DLLs __gcc_deregister_frame function. If so, proceed by calling + __cxa_atexit, otherwise call atexit. */ + extern void *__image_base__; + if (!d + && (uintptr_t) _my_tls.retaddr () >= (uintptr_t) &__image_base__ + && (uintptr_t) _my_tls.retaddr () <= (uintptr_t) &_cygheap_start + && (uintptr_t) fn > (uintptr_t) &_cygheap_start) + { + d = dlls.find ((void *) fn); + if (d && (void *) GetProcAddress (d->handle, "__gcc_deregister_frame") + != fn) + d = NULL; + } +#endif res = d ? __cxa_atexit ((void (*) (void *)) fn, NULL, d->handle) : atexit (fn); return res; } diff --git a/winsup/cygwin/release/1.7.33 b/winsup/cygwin/release/1.7.33 index c7acd071d..13d278648 100644 --- a/winsup/cygwin/release/1.7.33 +++ b/winsup/cygwin/release/1.7.33 @@ -83,3 +83,6 @@ Bug Fixes - Fix a SEGV in cygcheck if the environment variable COMSPEC is not, or incorrectly set. Addresses: https://cygwin.com/ml/cygwin/2014-10/msg00292.html + +- Fix a SEGV in some 64 bit applications explicitely dlclosing DLLs. + Addresses: https://cygwin.com/ml/cygwin/2014-10/msg00402.html