diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index bba6ce4cf..69ad486e0 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,12 @@ +2014-11-05 Corinna Vinschen + + * Makefile.in (NEW_FUNCTIONS): Add atexit to be not exported. + * lib/atexit.c (atexit): New, statically linkable version of atexit. + * dcrt0.cc (cygwin_atexit): Add comment to mark this function as old + entry point. Indiscriminately check for DSO of function pointer for + all functions, if checking for DSO of return address fails on x86_64. + Change comment accordingly. + 2014-11-05 Corinna Vinschen * Makefile.in (NEW_FUNCTIONS): Define target-independent. Add target diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in index 2d8c1d620..6f603d887 100644 --- a/winsup/cygwin/Makefile.in +++ b/winsup/cygwin/Makefile.in @@ -330,6 +330,7 @@ endif GMON_OFILES:=gmon.o mcount.o profil.o mcountFunc.o NEW_FUNCTIONS:=$(addprefix --replace=,\ + atexit= \ timezone= \ __xdrrec_getrec= \ __xdrrec_setnonblock= \ diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc index f021f0281..673f87a15 100644 --- a/winsup/cygwin/dcrt0.cc +++ b/winsup/cygwin/dcrt0.cc @@ -1250,6 +1250,13 @@ cygwin__cxa_atexit (void (*fn)(void *), void *obj, void *dso_handle) return __cxa_atexit (fn, obj, dso_handle); } +/* This function is only called for applications built with Cygwin versions + up to 1.7.32. Starting with 1.7.33, atexit is a statically linked function + inside of libcygwin.a. The reason is that the old method to fetch the + caller return address is unreliable given GCCs ability to perform tail call + elimination. For the details, see the below comment. + The atexit replacement is defined in libcygwin.a to allow reliable access + to the correct DSO handle. */ extern "C" int cygwin_atexit (void (*fn) (void)) { @@ -1259,7 +1266,7 @@ cygwin_atexit (void (*fn) (void)) #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 + Due to GCC's tail call 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 @@ -1268,22 +1275,16 @@ cygwin_atexit (void (*fn) (void)) 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; - } + This also occurs for other functions. + + Workaround: If dlls.find fails, try to find the dll entry of the DLL + containing fn. If that works, proceed by calling __cxa_atexit, otherwise + call atexit. + + This *should* be sufficiently safe. Ultimately, new applications will + use the statically linked atexit function though, as outlined above. */ + if (!d) + d = dlls.find ((void *) fn); #endif res = d ? __cxa_atexit ((void (*) (void *)) fn, NULL, d->handle) : atexit (fn); return res; diff --git a/winsup/cygwin/lib/atexit.c b/winsup/cygwin/lib/atexit.c new file mode 100644 index 000000000..0b724771a --- /dev/null +++ b/winsup/cygwin/lib/atexit.c @@ -0,0 +1,23 @@ +/* atexit.c: atexit entry point + + Copyright 2014 Red Hat, Inc. + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#include + +/* Statically linked replacement for the former cygwin_atexit. We need + the function here to be able to access the correct __dso_handle of the + caller's DSO. */ +int +atexit (void (*fn) (void)) +{ + extern int __cxa_atexit(void (*)(void*), void*, void*); + extern void *__dso_handle; + + return __cxa_atexit ((void (*)(void*))fn, NULL, &__dso_handle); +}