* 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.
This commit is contained in:
Corinna Vinschen 2014-11-05 09:48:00 +00:00
parent a2ba36a67d
commit 4d67bb4936
4 changed files with 51 additions and 17 deletions

View File

@ -1,3 +1,12 @@
2014-11-05 Corinna Vinschen <corinna@vinschen.de>
* 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 <corinna@vinschen.de>
* Makefile.in (NEW_FUNCTIONS): Define target-independent. Add target

View File

@ -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= \

View File

@ -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;

View File

@ -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 <stddef.h>
/* 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);
}