From 2eb392bd77de1535823daeae04c83fae0e331ee8 Mon Sep 17 00:00:00 2001 From: Christopher Faylor Date: Sat, 15 Jul 2000 02:48:11 +0000 Subject: * hinfo.cc (hinfo::linearize_fd_array): Make max_used_fd an int so that we can detect when there are no fds to pass. * dcrt0.cc (host_dependent_constants::init): Revert Sat Mar 18 01:32:04 2000 change. (dll_crt0_1): Set "cygwin_finished_initializing" flag. (dll_crt0): Don't perform memcpy if uptr is already set to internal structure. (_dll_crt0): Remember location of programs envptr. * dll_init.h (per_module, dll, dll_list): Revamp. * dll_init.cc: Revamp. Use new classes. * fork.cc (fork): Use new revamped dll, dll_list, and per_module stuff. * environ.cc: Use __cygwin_environ throughout rather than the user_data->envptr. * exec.cc: Ditto. * spawn.cc: Ditto. * winsup.h: Declare update_envptrs, cygwin_finished_initializing. * lib/_cygwin_crt0_common.cc (_cygwin_crt0_common): Revert previous change. * lib/cygwin_attach_dll.cc (cygwin_attach_dll): Always pass in own per_process structure or we end up overwriting information from the main program. --- winsup/cygwin/dll_init.cc | 537 +++++++++++++++++++--------------------------- 1 file changed, 224 insertions(+), 313 deletions(-) (limited to 'winsup/cygwin/dll_init.cc') diff --git a/winsup/cygwin/dll_init.cc b/winsup/cygwin/dll_init.cc index 0bf405fa4..94cc8e646 100644 --- a/winsup/cygwin/dll_init.cc +++ b/winsup/cygwin/dll_init.cc @@ -13,65 +13,28 @@ details. */ extern void __stdcall check_sanity_and_sync (per_process *); -#ifdef _MT_SAFE -extern ResourceLocks _reslock NO_COPY; -extern MTinterface _mtinterf NO_COPY; -#endif /*_MT_SAFE*/ +dll_list NO_COPY dlls; -/* WARNING: debug can't be called before init !!!! */ +static NO_COPY int in_forkee = 0; +/* local variables */ //----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -// the private structure - -typedef enum { NONE, LINK, LOAD } dllType; - -struct dll -{ - per_process p; - HMODULE handle; - const char *name; - dllType type; -}; - -//----------------------------------------------------------------------------- - -#define MAX_DLL_BEFORE_INIT 100 // FIXME: enough ??? -static dll _list_before_init[MAX_DLL_BEFORE_INIT]; - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -// local variables - -static DllList _the; -static int _last = 0; -static int _max = MAX_DLL_BEFORE_INIT; -static dll *_list = _list_before_init; -static int _initCalled = 0; -static int _numberOfOpenedDlls = 0; -static int _forkeeMustReloadDlls = 0; -static int _in_forkee = 0; -static const char *_dlopenedLib = 0; -static int _dlopenIndex = -1; -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - -static int __dll_global_dtors_recorded = 0; +static int dll_global_dtors_recorded = 0; +/* Run destructors for all DLLs on exit. */ static void -__dll_global_dtors() +dll_global_dtors() { - _the.doGlobalDestructorsOfDlls(); + for (dll *d = dlls.istart (DLL_ANY); d; d = dlls.inext ()) + d->p.run_dtors (); } -static void -doGlobalCTORS (per_process *p) +/* Run all constructors associated with a dll */ +void +per_module::run_ctors () { - void (**pfunc)() = p->ctors; + void (**pfunc)() = ctors; /* Run ctors backwards, so skip the first entry and find how many there are, then run them. */ @@ -86,230 +49,160 @@ doGlobalCTORS (per_process *p) } } -static void -doGlobalDTORS (per_process *p) +/* Run all destructors associated with a dll */ +void +per_module::run_dtors () { - if (!p) - return; - void (**pfunc)() = p->dtors; + void (**pfunc)() = dtors; for (int i = 1; pfunc[i]; i++) (pfunc[i]) (); } -#define INC 500 - -static int -add (HMODULE h, char *name, per_process *p, dllType type) -{ - int ret = -1; - - if (p) - check_sanity_and_sync (p); - - if (_last == _max) - { - if (!_initCalled) // we try to load more than MAX_DLL_BEFORE_INIT - { - small_printf ("try to load more dll than max allowed=%d\n", - MAX_DLL_BEFORE_INIT); - ExitProcess (1); - } - - dll* newArray = new dll[_max+INC]; - if (_list) - { - memcpy (newArray, _list, _max * sizeof (dll)); - if (_list != _list_before_init) - delete []_list; - } - _list = newArray; - _max += INC; - } - - _list[_last].name = name && type == LOAD ? strdup (name) : NULL; - _list[_last].handle = h; - _list[_last].p = *p; - _list[_last].type = type; - - ret = _last++; - return ret; -} - -static int -initOneDll (per_process *p) +/* Initialize an individual DLL */ +int +dll::init () { - /* FIXME: init environment (useful?) */ - *(p->envptr) = *(user_data->envptr); + int ret = 1; - /* FIXME: need other initializations? */ + /* Why didn't we just import this variable? */ + *(p.envptr) = __cygwin_environ; - int ret = 1; - if (!_in_forkee) + /* Don't run constructors or the "main" if we've forked. */ + if (!in_forkee) { /* global contructors */ - doGlobalCTORS (p); + p.run_ctors (); /* entry point of dll (use main of per_process with null args...) */ - if (p->main) - ret = (*(p->main)) (0, 0, 0); + if (p.main) + ret = (*(p.main)) (0, 0, 0); } return ret; } -DllList& -DllList::the () +/* Look for a dll based on name */ +dll * +dll_list::operator[] (const char *name) { - return _the; -} + dll *d = &start; + while ((d = d->next) != NULL) + if (strcasematch (name, d->name)) + return d; -void -DllList::currentDlOpenedLib (const char *name) -{ - if (_dlopenedLib != 0) - small_printf ("WARNING: previous dlopen of %s wasn't correctly performed\n", _dlopenedLib); - _dlopenedLib = name; - _dlopenIndex = -1; + return NULL; } -int -DllList::recordDll (HMODULE h, per_process *p) +#define RETRIES 100 + +/* Allocate space for a dll struct after the just-loaded dll. */ +dll * +dll_list::alloc (HINSTANCE h, per_process *p, dll_type type) { - int ret = -1; + char name[MAX_PATH + 1]; + DWORD namelen = GetModuleFileName (h, name, sizeof (name)); - /* debug_printf ("Record a dll p=%p\n", p); see WARNING */ - dllType type = LINK; - if (_initCalled) + /* Already loaded? */ + dll *d = dlls[name]; + if (d) { - type = LOAD; - _numberOfOpenedDlls++; - forkeeMustReloadDlls (1); + d->count++; /* Yes. Bump the usage count. */ + return d; /* Return previously allocated pointer. */ } - if (_in_forkee) + int i; + void *s = p->bss_end; + MEMORY_BASIC_INFORMATION m; + /* Search for space after the DLL */ + for (i = 0; i <= RETRIES; i++) { - ret = 0; // Just a flag - goto out; + if (!VirtualQuery (s, &m, sizeof (m))) + return NULL; /* Can't do it. */ + if (m.State == MEM_FREE) + break; + s = (char *) m.BaseAddress + m.RegionSize; } - char buf[MAX_PATH]; - GetModuleFileName (h, buf, MAX_PATH); - - if (type == LOAD && _dlopenedLib !=0) - { - // it is not the current dlopened lib - // so we insert one empty lib to preserve place for current dlopened lib - if (!strcasematch (_dlopenedLib, buf)) - { - if (_dlopenIndex == -1) - _dlopenIndex = add (0, 0, 0, NONE); - ret = add (h, buf, p, type); - } - else // it is the current dlopened lib - { - if (_dlopenIndex != -1) - { - _list[_dlopenIndex].handle = h; - _list[_dlopenIndex].p = *p; - _list[_dlopenIndex].type = type; - ret = _dlopenIndex; - _dlopenIndex = -1; - } - else // it this case the dlopened lib doesn't need other lib - ret = add (h, buf, p, type); - _dlopenedLib = 0; - } - } - else - ret = add (h, buf, p, type); - -out: - if (_initCalled) // main module is already initialized - { - if (!initOneDll (p)) - ret = -1; - } - return ret; + /* Couldn't find any. Uh oh. FIXME: Issue an error? */ + if (i == RETRIES) + return NULL; /* Oh well */ + + SYSTEM_INFO s1; + GetSystemInfo (&s1); + + /* Need to do the shared memory thing since W95 can't allocate in + the shared memory region otherwise. */ + HANDLE h1 = CreateFileMapping (INVALID_HANDLE_VALUE, &sec_none_nih, + PAGE_READWRITE, 0, sizeof (dll), NULL); + + DWORD n = (DWORD) m.BaseAddress; + n = ((n - (n % s1.dwAllocationGranularity)) + s1.dwAllocationGranularity); + d = (dll *) MapViewOfFileEx (h1, FILE_MAP_WRITE, 0, 0, 0, (void *) n); + CloseHandle (h1); + + /* Now we've allocated a block of information. Fill it in with the supplied + info about this DLL. */ + d->count = 1; + d->namelen = namelen; + strcpy (d->name, name); + d->handle = h; + 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++; + return d; } +/* Detach a DLL from the chain. */ void -DllList::detachDll (int dll_index) +dll_list::detach (dll *d) { - if (dll_index != -1) + if (d->count <= 0) + system_printf ("WARNING: try to detach an already detached dll ...\n"); + else if (--d->count == 0) { - dll *aDll = &(_list[dll_index]); - doGlobalDTORS (&aDll->p); - if (aDll->type == LOAD) - _numberOfOpenedDlls--; - aDll->type = NONE; + d->p.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; + UnmapViewOfFile (d); } - else - small_printf ("WARNING: try to detach an already detached dll ...\n"); } +/* Initialization called by dll_crt0_1. */ void -DllList::initAll () +dll_list::init () { - // init for destructors - // because initAll isn't called in forked process, this exit function will - // be recorded only once - if (!__dll_global_dtors_recorded) + debug_printf ("here"); + /* Make sure that destructors are called on exit. */ + if (!dll_global_dtors_recorded) { - atexit (__dll_global_dtors); - __dll_global_dtors_recorded = 1; + atexit (dll_global_dtors); + dll_global_dtors_recorded = 1; } - if (!_initCalled) - { - debug_printf ("call to DllList::initAll"); - for (int i = 0; i < _last; i++) - { - per_process *p = &_list[i].p; - if (p) - initOneDll (p); - } - _initCalled = 1; - } -} - -void -DllList::doGlobalDestructorsOfDlls () -{ - // global destructors in reverse order - for (int i = _last - 1; i >= 0; i--) - { - if (_list[i].type != NONE) - { - per_process *p = &_list[i].p; - if (p) - doGlobalDTORS (p); - } - } -} - -int -DllList::numberOfOpenedDlls () -{ - return _numberOfOpenedDlls; -} - -int -DllList::forkeeMustReloadDlls () -{ - return _forkeeMustReloadDlls; -} - -void -DllList::forkeeMustReloadDlls (int i) -{ - _forkeeMustReloadDlls = i; + /* Walk the dll chain, initializing each dll */ + dll *d = &start; + 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. */ -void +static void reserve_upto (const char *name, DWORD here) { DWORD size; @@ -334,7 +227,7 @@ reserve_upto (const char *name, DWORD here) /* 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. */ -void +static void release_upto (const char *name, DWORD here) { DWORD size; @@ -354,87 +247,68 @@ release_upto (const char *name, DWORD here) } } +#define MAX_DLL_SIZE (sizeof (dll)) /* 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 -DllList::forkeeLoadDlls () +dll_list::load_after_fork (HANDLE parent, dll *first) { - _initCalled = 1; - _in_forkee = 1; + in_forkee = 1; int try2 = 0; - for (int i = 0; i < _last; i++) - if (_list[i].type == LOAD) - { - const char *name = _list[i].name; - HMODULE handle = _list[i].handle; - HMODULE h = LoadLibraryEx (name, NULL, DONT_RESOLVE_DLL_REFERENCES); - - if (h == handle) - { - FreeLibrary (h); - LoadLibrary (name); - } - else if (try2) - api_fatal ("unable to remap %s to same address as parent -- %p", name, h); - else - { - FreeLibrary (h); - reserve_upto (name, (DWORD) handle); - try2 = 1; - i--; - continue; - } - if (try2) - { - release_upto (name, (DWORD) handle); - try2 = 0; - } - } - _in_forkee = 0; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -// iterators + dll d; -DllListIterator::DllListIterator (int type) : _type (type), _index (-1) -{ - operator++ (); -} - -DllListIterator::~DllListIterator () -{ -} - -DllListIterator::operator per_process* () -{ - return &_list[index ()].p; -} - -void -DllListIterator::operator++ () -{ - _index++; - while (_index < _last && (int) (_list[_index].type) != _type) - _index++; - if (_index == _last) - _index = -1; -} - -LinkedDllIterator::LinkedDllIterator () : DllListIterator ((int) LINK) -{ -} - -LinkedDllIterator::~LinkedDllIterator () -{ -} - -LoadedDllIterator::LoadedDllIterator () : DllListIterator ((int) LOAD) -{ -} - -LoadedDllIterator::~LoadedDllIterator () -{ + void *next = first; + while (next) + { + DWORD nb; + /* Read the dll structure from the parent. */ + if (!ReadProcessMemory (parent, next, &d, MAX_DLL_SIZE, &nb) || + nb != MAX_DLL_SIZE) + return; + /* We're only interested in dynamically loaded dlls. + Hopefully, this function wouldn't even have been called unless + the parent had some of those. */ + if (d.type == DLL_LOAD) + { + HMODULE h = LoadLibraryEx (d.name, NULL, DONT_RESOLVE_DLL_REFERENCES); + + /* See if DLL will load in proper place. If so, free it and reload + it the right way. + It sort of stinks that we can't invert the order of the FreeLibrary + and LoadLibrary since Microsoft documentation seems to imply that that + should do what we want. However, since the library was loaded above, + The second LoadLibrary does not execute it's startup code unless it + is first unloaded. */ + if (h == d.handle) + { + FreeLibrary (h); + LoadLibrary (d.name); + } + else if (try2) + api_fatal ("unable to remap %s to same address as parent -- %p", d.name, h); + else + { + /* It 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. */ + FreeLibrary (h); + /* Block all of the memory up to the new load address. */ + reserve_upto (d.name, (DWORD) d.handle); + try2 = 1; /* And try */ + continue; /* again. */ + } + /* If we reached here, and try2 is set, then there is a lot of memory to + release. */ + if (try2) + { + release_upto (d.name, (DWORD) d.handle); + try2 = 0; + } + } + next = d.next; /* Get the address of the next DLL. */ + } + in_forkee = 0; } extern "C" int @@ -448,33 +322,70 @@ dll_dllcrt0 (HMODULE h, per_process *p) /* Partially initialize Cygwin guts for non-cygwin apps. */ if (dynamically_loaded && user_data->magic_biscuit == 0) dll_crt0 (p); - return _the.recordDll (h, p); + + if (p) + 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 (!cygwin_finished_initializing) + 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 || (cygwin_finished_initializing && !d->init ())) + return -1; + + return (DWORD) d; } /* OBSOLETE: This function is obsolescent 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 +extern "C" int dll_noncygwin_dllcrt0 (HMODULE h, per_process *p) { return dll_dllcrt0 (h, p); } -extern "C" -void -cygwin_detach_dll (int dll_index) +extern "C" void +cygwin_detach_dll (dll *d) { - _the.detachDll (dll_index); + dlls.detach (d); } -extern "C" -void +extern "C" void dlfork (int val) { - _the.forkeeMustReloadDlls (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 () +{ + extern char ***main_environ; + for (dll *d = dlls.istart (DLL_ANY); d; d = dlls.inext ()) + { + *(d->p.envptr) = __cygwin_environ; + } + *main_environ = __cygwin_environ; +} -- cgit v1.2.3