diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index bea140e8a..36bb97659 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,14 @@ +2009-08-13 Corinna Vinschen + Dave Korn + + * cxx.cc (default_cygwin_cxx_malloc): Enhance commenting. + * dll_init.cc (dll_dllcrt0_1): Likewise. + * dlfcn.cc (dlopen): Prevent dlopen()'d DLL from installing any + cxx malloc overrides. + * include/cygwin/cygwin_dll.h (__dynamically_loaded): New variable. + * lib/_cygwin_crt0_common.cc (_cygwin_crt0_common): Check it and only + install cxx malloc overrides when statically loaded. Extend comments. + 2009-08-12 Corinna Vinschen * fhandler_socket.cc (fhandler_socket::accept): Always use local diff --git a/winsup/cygwin/cxx.cc b/winsup/cygwin/cxx.cc index 523fb4268..0faeaf7ee 100644 --- a/winsup/cygwin/cxx.cc +++ b/winsup/cygwin/cxx.cc @@ -87,7 +87,10 @@ __cxa_guard_release () } /* These routines are made available as last-resort fallbacks - for the application. Should not be used in practice. */ + for the application. Should not be used in practice; the + entries in this struct get overwritten by each DLL as it + is loaded, and libstdc++ will override the whole lot first + thing of all. */ struct per_process_cxx_malloc default_cygwin_cxx_malloc = { diff --git a/winsup/cygwin/dlfcn.cc b/winsup/cygwin/dlfcn.cc index a9e3295da..bc95e221c 100644 --- a/winsup/cygwin/dlfcn.cc +++ b/winsup/cygwin/dlfcn.cc @@ -93,7 +93,28 @@ dlopen (const char *name, int) wchar_t *path = tp.w_get (); pc.get_wide_win32_path (path); + + /* Workaround for broken DLLs built against Cygwin versions 1.7.0-49 + up to 1.7.0-57. They override the cxx_malloc pointer in their + DLL initialization code even if loaded dynamically. This is a + no-no since a later dlclose lets cxx_malloc point into nirvana. + The below kludge "fixes" that by reverting the original cxx_malloc + pointer after LoadLibrary. This implies that their overrides + won't be applied; that's OK. All overrides should be present at + final link time, as Windows doesn't allow undefined references; + it would actually be wrong for a dlopen'd DLL to opportunistically + override functions in a way that wasn't known then. We're not + going to try and reproduce the full ELF dynamic loader here! */ + + /* Store original cxx_malloc pointer. */ + struct per_process_cxx_malloc *tmp_malloc; + tmp_malloc = __cygwin_user_data.cxx_malloc; + ret = (void *) LoadLibraryW (path); + + /* Restore original cxx_malloc pointer. */ + __cygwin_user_data.cxx_malloc = tmp_malloc; + if (ret == NULL) __seterrno (); } diff --git a/winsup/cygwin/dll_init.cc b/winsup/cygwin/dll_init.cc index 1f83fb9a9..39a8f505b 100644 --- a/winsup/cygwin/dll_init.cc +++ b/winsup/cygwin/dll_init.cc @@ -328,6 +328,26 @@ dll_dllcrt0_1 (VOID *x) bool linked = !in_forkee && !cygwin_finished_initializing; + /* Broken DLLs built against Cygwin versions 1.7.0-49 up to 1.7.0-57 + override the cxx_malloc pointer in their DLL initialization code, + when loaded either statically or dynamically. Because this leaves + a stale pointer into demapped memory space if the DLL is unloaded + by a call to dlclose, we prevent this happening for dynamically + loaded DLLS in dlopen by saving and restoring cxx_malloc around + the call to LoadLibrary, which invokes the DLL's startup sequence. + Modern DLLs won't even attempt to override the pointer when loaded + statically, but will write their overrides directly into the + struct it points to. With all modern DLLs, this will remain the + default_cygwin_cxx_malloc struct in cxx.cc, but if any broken DLLs + are in the mix they will have overridden the pointer and subsequent + overrides will go into their embedded cxx_malloc structs. This is + almost certainly not a problem as they can never be unloaded, but + if we ever did want to do anything about it, we could check here to + see if the pointer had been altered in the early parts of the DLL's + startup, and if so copy back the new overrides and reset it here. + However, that's just a note for the record; at the moment, we can't + see any need to worry about this happening. */ + /* Partially initialize Cygwin guts for non-cygwin apps. */ if (dynamically_loaded && user_data->magic_biscuit == 0) dll_crt0 (p); diff --git a/winsup/cygwin/include/cygwin/cygwin_dll.h b/winsup/cygwin/include/cygwin/cygwin_dll.h index 80c9d13c6..7b1f1b883 100644 --- a/winsup/cygwin/include/cygwin/cygwin_dll.h +++ b/winsup/cygwin/include/cygwin/cygwin_dll.h @@ -33,6 +33,7 @@ CDECL_END \ static HINSTANCE storedHandle; \ static DWORD storedReason; \ static void* storedPtr; \ +int __dynamically_loaded; \ \ static int __dllMain (int a, char **b, char **c) \ { \ @@ -53,6 +54,7 @@ int WINAPI _cygwin_dll_entry (HINSTANCE h, DWORD reason, void *ptr) \ storedHandle = h; \ storedReason = reason; \ storedPtr = ptr; \ + __dynamically_loaded = (ptr == NULL); \ dll_index = cygwin_attach_dll (h, &__dllMain); \ if (dll_index == (DWORD) -1) \ ret = 0; \ diff --git a/winsup/cygwin/lib/_cygwin_crt0_common.cc b/winsup/cygwin/lib/_cygwin_crt0_common.cc index 280c50ef4..23cedfaaa 100644 --- a/winsup/cygwin/lib/_cygwin_crt0_common.cc +++ b/winsup/cygwin/lib/_cygwin_crt0_common.cc @@ -40,6 +40,9 @@ extern WEAK void operator delete[](void *p, const std::nothrow_t &nt) throw() /* Avoid an info message from linker when linking applications. */ extern __declspec(dllimport) struct _reent *_impure_ptr; +/* Initialised in _cygwin_dll_entry. */ +extern int __dynamically_loaded; + #undef environ extern "C" @@ -70,11 +73,13 @@ _cygwin_crt0_common (MainFunc f, per_process *u) per_process *newu = (per_process *) cygwin_internal (CW_USER_DATA); int uwasnull; + /* u is non-NULL if we are in a DLL, and NULL in the main exe. + newu is the Cygwin DLL's internal per_process and never NULL. */ if (u != NULL) uwasnull = 0; /* Caller allocated space for per_process structure. */ else { - u = newu; /* Using DLL built-in per_process. */ + u = newu; /* Using DLL built-in per_process. */ uwasnull = 1; /* Remember for later. */ } @@ -114,8 +119,10 @@ _cygwin_crt0_common (MainFunc f, per_process *u) u->realloc = &realloc; u->calloc = &calloc; - /* Likewise for the C++ memory operators - if any. */ - if (newu && newu->cxx_malloc) + /* Likewise for the C++ memory operators, if any, but not if we + were dlopen()'d, as we might get dlclose()'d and that would + leave stale function pointers behind. */ + if (newu && newu->cxx_malloc && !__dynamically_loaded) { /* Inherit what we don't override. */ #define CONDITIONALLY_OVERRIDE(MEMBER) \ @@ -129,12 +136,10 @@ _cygwin_crt0_common (MainFunc f, per_process *u) CONDITIONALLY_OVERRIDE(oper_new___nt); CONDITIONALLY_OVERRIDE(oper_delete_nt); CONDITIONALLY_OVERRIDE(oper_delete___nt); + /* Now update the resulting set into the global redirectors. */ + *newu->cxx_malloc = __cygwin_cxx_malloc; } - /* Now update the resulting set into the global redirectors. */ - if (newu) - newu->cxx_malloc = &__cygwin_cxx_malloc; - /* Setup the module handle so fork can get the path name. */ u->hmodule = GetModuleHandle (0);