diff options
Diffstat (limited to 'winsup/cygwin/dll_init.cc')
-rw-r--r-- | winsup/cygwin/dll_init.cc | 433 |
1 files changed, 0 insertions, 433 deletions
diff --git a/winsup/cygwin/dll_init.cc b/winsup/cygwin/dll_init.cc deleted file mode 100644 index ec1132434..000000000 --- a/winsup/cygwin/dll_init.cc +++ /dev/null @@ -1,433 +0,0 @@ -/* dll_init.cc - - Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, - 2007, 2008, 2009, 2010 Red Hat, Inc. - -This software is a copyrighted work licensed under the terms of the -Cygwin license. Please consult the file "CYGWIN_LICENSE" for -details. */ - -#include "winsup.h" -#include "cygerrno.h" -#include "perprocess.h" -#include "dll_init.h" -#include "environ.h" -#include "security.h" -#include "path.h" -#include "fhandler.h" -#include "dtable.h" -#include "cygheap.h" -#include "pinfo.h" -#include "cygtls.h" -#include "exception.h" -#include <wchar.h> -#include <sys/reent.h> -#include <assert.h> - -extern void __stdcall check_sanity_and_sync (per_process *); - -dll_list dlls; - -static bool dll_global_dtors_recorded; - -/* Run destructors for all DLLs on exit. */ -void -dll_global_dtors () -{ - int recorded = dll_global_dtors_recorded; - dll_global_dtors_recorded = false; - if (recorded && dlls.start.next) - for (dll *d = dlls.end; d != &dlls.start; d = d->prev) - d->run_dtors (); -} - -/* Run all constructors associated with a dll */ -void -per_module::run_ctors () -{ - void (**pfunc)() = ctors; - - /* Run ctors backwards, so skip the first entry and find how many - there are, then run them. */ - - if (pfunc) - { - int i; - for (i = 1; pfunc[i]; i++); - - for (int j = i - 1; j > 0; j--) - (pfunc[j]) (); - } -} - -/* Run all destructors associated with a dll */ -void -per_module::run_dtors () -{ - void (**pfunc)() = dtors; - while (*++pfunc) - (*pfunc) (); -} - -/* Initialize an individual DLL */ -int -dll::init () -{ - int ret = 1; - - /* This should be a no-op. Why didn't we just import this variable? */ - *(p.envptr) = __cygwin_environ; - - /* Don't run constructors or the "main" if we've forked. */ - if (!in_forkee) - { - /* global contructors */ - p.run_ctors (); - - /* entry point of dll (use main of per_process with null args...) */ - if (p.main) - ret = p.main (0, 0, 0); - } - - return ret; -} - -/* Look for a dll based on name */ -dll * -dll_list::operator[] (const PWCHAR name) -{ - dll *d = &start; - while ((d = d->next) != NULL) - if (!wcscasecmp (name, d->name)) - return d; - - return NULL; -} - -#define RETRIES 1000 - -/* Allocate space for a dll struct. */ -dll * -dll_list::alloc (HINSTANCE h, per_process *p, dll_type type) -{ - WCHAR name[NT_MAX_PATH]; - DWORD namelen = GetModuleFileNameW (h, name, sizeof (name)); - - /* Already loaded? */ - dll *d = dlls[name]; - if (d) - { - if (!in_forkee) - d->count++; /* Yes. Bump the usage count. */ - } - else - { - /* FIXME: Change this to new at some point. */ - d = (dll *) cmalloc (HEAP_2_DLL, sizeof (*d) + (namelen * sizeof (*name))); - - /* Now we've allocated a block of information. Fill it in with the supplied - info about this DLL. */ - d->count = 1; - wcscpy (d->name, name); - d->handle = h; - d->has_dtors = true; - d->p = p; - d->type = type; - if (end == NULL) - end = &start; /* Point to "end" of dll chain. */ - end->next = d; /* Standard linked list stuff. */ - d->next = NULL; - d->prev = end; - end = d; - tot++; - if (type == DLL_LOAD) - loaded_dlls++; - } - assert (p->envptr != NULL); - return d; -} - -dll * -dll_list::find (void *retaddr) -{ - MEMORY_BASIC_INFORMATION m; - if (!VirtualQuery (retaddr, &m, sizeof m)) - return NULL; - HMODULE h = (HMODULE) m.AllocationBase; - - dll *d = &start; - while ((d = d->next)) - if (d->handle == h) - break; - return d; -} - -/* Detach a DLL from the chain. */ -void -dll_list::detach (void *retaddr) -{ - dll *d; - if (!myself || !(d = find (retaddr))) - return; - if (d->count <= 0) - system_printf ("WARNING: trying to detach an already detached dll ..."); - if (--d->count == 0) - { - /* Ensure our exception handler is enabled for destructors */ - exception protect; - /* Call finalize function if we are not already exiting */ - if (!exit_state) - __cxa_finalize (d); - d->run_dtors (); - d->prev->next = d->next; - if (d->next) - d->next->prev = d->prev; - if (d->type == DLL_LOAD) - loaded_dlls--; - if (end == d) - end = d->prev; - cfree (d); - } -} - -/* Initialization for all linked DLLs, called by dll_crt0_1. */ -void -dll_list::init () -{ - /* Walk the dll chain, initializing each dll */ - dll *d = &start; - dll_global_dtors_recorded = d->next != NULL; - while ((d = d->next)) - d->init (); -} - -#define A64K (64 * 1024) - -/* Mark every memory address up to "here" as reserved. This may force - Windows NT to load a DLL in the next available, lowest slot. */ -static void -reserve_upto (const PWCHAR name, DWORD here) -{ - DWORD size; - MEMORY_BASIC_INFORMATION mb; - for (DWORD start = 0x10000; start < here; start += size) - if (!VirtualQuery ((void *) start, &mb, sizeof (mb))) - size = A64K; - else - { - size = A64K * ((mb.RegionSize + A64K - 1) / A64K); - start = A64K * (((DWORD) mb.BaseAddress + A64K - 1) / A64K); - - if (start + size > here) - size = here - start; - if (mb.State == MEM_FREE && - !VirtualAlloc ((void *) start, size, MEM_RESERVE, PAGE_NOACCESS)) - api_fatal ("couldn't allocate memory %p(%d) for '%W' alignment, %E\n", - start, size, name); - } -} - -/* Release all of the memory previously allocated by "upto" above. - Note that this may also free otherwise reserved memory. If that becomes - a problem, we'll have to keep track of the memory that we reserve above. */ -static void -release_upto (const PWCHAR name, DWORD here) -{ - DWORD size; - MEMORY_BASIC_INFORMATION mb; - for (DWORD start = 0x10000; start < here; start += size) - if (!VirtualQuery ((void *) start, &mb, sizeof (mb))) - size = 64 * 1024; - else - { - size = mb.RegionSize; - if (!(mb.State == MEM_RESERVE && mb.AllocationProtect == PAGE_NOACCESS - && (((void *) start < cygheap->user_heap.base - || (void *) start > cygheap->user_heap.top) - && ((void *) start < (void *) cygheap - || (void *) start - > (void *) ((char *) cygheap + CYGHEAPSIZE))))) - continue; - if (!VirtualFree ((void *) start, 0, MEM_RELEASE)) - api_fatal ("couldn't release memory %p(%d) for '%W' alignment, %E\n", - start, size, name); - } -} - -/* Reload DLLs after a fork. Iterates over the list of dynamically loaded - DLLs and attempts to load them in the same place as they were loaded in the - parent. */ -void -dll_list::load_after_fork (HANDLE parent) -{ - for (dll *d = &dlls.start; (d = d->next) != NULL; ) - if (d->type == DLL_LOAD) - for (int i = 0; i < 2; i++) - { - /* See if DLL will load in proper place. If so, free it and reload - it the right way. - It stinks that we can't invert the order of the initial LoadLibrary - and FreeLibrar since Microsoft documentation seems to imply that - should do what we want. However, once a library is loaded as - above, the second LoadLibrary will not execute its startup code - unless it is first unloaded. */ - HMODULE h = LoadLibraryExW (d->name, NULL, DONT_RESOLVE_DLL_REFERENCES); - - if (!h) - system_printf ("can't reload %W, %E", d->name); - else - { - FreeLibrary (h); - if (h == d->handle) - h = LoadLibraryW (d->name); - } - /* If we reached here on the second iteration of the for loop - then there is a lot of memory to release. */ - if (i > 0) - release_upto (d->name, (DWORD) d->handle); - if (h == d->handle) - break; /* Success */ - - if (i > 0) - /* We tried once to relocate the dll and it failed. */ - api_fatal ("unable to remap %W to same address as parent: %p != %p", - d->name, d->handle, h); - - /* Dll loaded in the wrong place. Dunno why this happens but it - always seems to happen when there are multiple DLLs attempting to - load into the same address space. In the "forked" process, the - second DLL always loads into a different location. So, block all - of the memory up to the new load address and try again. */ - reserve_upto (d->name, (DWORD) d->handle); - } - in_forkee = false; -} - -struct dllcrt0_info -{ - HMODULE h; - per_process *p; - int res; - dllcrt0_info (HMODULE h0, per_process *p0): h(h0), p(p0) {} -}; - -extern "C" int -dll_dllcrt0 (HMODULE h, per_process *p) -{ - dllcrt0_info x (h, p); - - if (_my_tls.isinitialized ()) - dll_dllcrt0_1 (&x); - else - _my_tls.call ((DWORD (*) (void *, void *)) dll_dllcrt0_1, &x); - return x.res; -} - -void -dll_dllcrt0_1 (VOID *x) -{ - HMODULE& h = ((dllcrt0_info *)x)->h; - per_process*& p = ((dllcrt0_info *)x)->p; - int& res = ((dllcrt0_info *)x)->res; - - if (p == NULL) - p = &__cygwin_user_data; - else - { - *(p->impure_ptr_ptr) = __cygwin_user_data.impure_ptr; - _pei386_runtime_relocator (p); - } - - 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); - else - check_sanity_and_sync (p); - - dll_type type; - - /* If this function is called before cygwin has finished - initializing, then the DLL must be a cygwin-aware DLL - that was explicitly linked into the program rather than - a dlopened DLL. */ - if (linked) - type = DLL_LINK; - else - { - type = DLL_LOAD; - dlls.reload_on_fork = 1; - } - - /* Allocate and initialize space for the DLL. */ - dll *d = dlls.alloc (h, p, type); - - /* If d == NULL, then something is broken. - Otherwise, if we've finished initializing, it's ok to - initialize the DLL. If we haven't finished initializing, - it may not be safe to call the dll's "main" since not - all of cygwin's internal structures may have been set up. */ - if (!d || (!linked && !d->init ())) - res = -1; - else - res = (DWORD) d; -} - -/* OBSOLETE: This function is obsolete and will go away in the - future. Cygwin can now handle being loaded from a noncygwin app - using the same entry point. */ - -extern "C" int -dll_noncygwin_dllcrt0 (HMODULE h, per_process *p) -{ - return dll_dllcrt0 (h, p); -} - -extern "C" void -cygwin_detach_dll (dll *) -{ - HANDLE retaddr; - if (_my_tls.isinitialized ()) - retaddr = (void *) _my_tls.retaddr (); - else - retaddr = __builtin_return_address (0); - dlls.detach (retaddr); -} - -extern "C" void -dlfork (int val) -{ - dlls.reload_on_fork = val; -} - -/* Called from various places to update all of the individual - ideas of the environ block. Explain to me again why we didn't - just import __cygwin_environ? */ -void __stdcall -update_envptrs () -{ - for (dll *d = dlls.istart (DLL_ANY); d; d = dlls.inext ()) - *(d->p.envptr) = __cygwin_environ; - *main_environ = __cygwin_environ; -} |