From 2eb392bd77de1535823daeae04c83fae0e331ee8 Mon Sep 17 00:00:00 2001 From: Christopher Faylor Date: Sat, 15 Jul 2000 02:48:11 +0000 Subject: [PATCH] * 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/ChangeLog | 24 + winsup/cygwin/dcrt0.cc | 36 +- winsup/cygwin/dlfcn.cc | 3 +- winsup/cygwin/dll_init.cc | 563 ++++++++++------------- winsup/cygwin/dll_init.h | 149 +++--- winsup/cygwin/dtable.cc | 6 +- winsup/cygwin/environ.cc | 10 +- winsup/cygwin/exec.cc | 13 +- winsup/cygwin/fork.cc | 91 ++-- winsup/cygwin/lib/_cygwin_crt0_common.cc | 5 +- winsup/cygwin/lib/cygwin_attach_dll.c | 12 +- winsup/cygwin/spawn.cc | 8 +- winsup/cygwin/winsup.h | 3 + 13 files changed, 427 insertions(+), 496 deletions(-) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 82d841125..d934eea49 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,27 @@ +Fri Jul 14 22:40:22 2000 Christopher Faylor + + * 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. + Wed Jul 12 00:46:00 2000 Christopher Faylor * debug.cc (thread_stub): Use impure_ptr in place of reent_data. diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc index a42e84e1b..4dddd31b3 100644 --- a/winsup/cygwin/dcrt0.cc +++ b/winsup/cygwin/dcrt0.cc @@ -40,6 +40,7 @@ BOOL strip_title_path = FALSE; BOOL allow_glob = TRUE; HANDLE NO_COPY parent_alive = NULL; +int cygwin_finished_initializing = 0; /* Used in SIGTOMASK for generating a bit for insertion into a sigset_t. This is subtracted from the signal number prior to shifting the bit. @@ -49,10 +50,8 @@ HANDLE NO_COPY parent_alive = NULL; measure to allow an orderly transfer to the new, correct sigmask method. */ unsigned int signal_shift_subtract = 1; -#ifdef _MT_SAFE ResourceLocks _reslock NO_COPY; MTinterface _mtinterf NO_COPY; -#endif extern "C" { @@ -64,6 +63,7 @@ extern "C" /* This is an exported copy of environ which can be used by DLLs which use cygwin.dll. */ char **__cygwin_environ; + char ***main_environ; /* __progname used in getopt error message */ char *__progname = NULL; struct _reent reent_data; @@ -192,7 +192,7 @@ host_dependent_constants::init () { case winNT: win32_upper = 0xffffffff; - shared = FILE_SHARE_READ | FILE_SHARE_WRITE; + shared = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; break; case win98: @@ -699,16 +699,8 @@ dll_crt0_1 () of the calls below (eg. uinfo_init) do stdio calls - this area must be set to zero before then. */ -#ifdef _MT_SAFE user_data->threadinterface->ClearReent(); user_data->threadinterface->Init1(); -#else - memset (&reent_data, 0, sizeof (reent_data)); - reent_data._errno = 0; - reent_data._stdin = reent_data.__sf + 0; - reent_data._stdout = reent_data.__sf + 1; - reent_data._stderr = reent_data.__sf + 2; -#endif char *line = GetCommandLineA (); @@ -734,7 +726,10 @@ dll_crt0_1 () /* beyond this we only do for cygwin apps or dlls */ if (dynamically_loaded) - return; + { + cygwin_finished_initializing = 1; + return; + } /* Initialize signal/subprocess handling. */ sigproc_init (); @@ -766,15 +761,14 @@ dll_crt0_1 () /* Set up __progname for getopt error call. */ __progname = argv[0]; - /* Call init of loaded dlls. */ - DllList::the().initAll(); - - set_errno (0); - /* Flush signals and ensure that signal thread is up and running. Can't do this for noncygwin case since the signal thread is blocked due to LoadLibrary serialization. */ - sig_send (NULL, __SIGFLUSH); /* also initializes uid, gid */ + sig_send (NULL, __SIGFLUSH); + + cygwin_finished_initializing = 1; + /* Call init of loaded dlls. */ + dlls.init (); /* Execute any specified "premain" functions */ if (user_data->premain[PREMAIN_LEN / 2]) @@ -782,6 +776,9 @@ dll_crt0_1 () user_data->premain[i] (argc, argv); debug_printf ("user_data->main %p", user_data->main); + + set_errno (0); + if (user_data->main) exit (user_data->main (argc, argv, *user_data->envptr)); } @@ -806,6 +803,7 @@ _dll_crt0 () } #endif + main_environ = user_data->envptr; user_data->heapbase = user_data->heapptr = user_data->heaptop = NULL; set_console_handler (); @@ -874,7 +872,7 @@ void dll_crt0 (per_process *uptr) { /* Set the local copy of the pointer into the user space. */ - if (uptr) + if (uptr && uptr != user_data) { memcpy (user_data, uptr, per_process_overwrite); *(user_data->impure_ptr_ptr) = &reent_data; diff --git a/winsup/cygwin/dlfcn.cc b/winsup/cygwin/dlfcn.cc index cf74d4696..dc6fec321 100644 --- a/winsup/cygwin/dlfcn.cc +++ b/winsup/cygwin/dlfcn.cc @@ -183,13 +183,12 @@ dlopen (const char *name, int) if (!name) { // handle for the current module - ret = (void *) GetModuleHandle (0); + ret = (void *) GetModuleHandle (NULL); } else { // handle for the named library const char *fullpath = get_full_path_of_dll (name); - DllList::the().currentDlOpenedLib (fullpath); ret = (void *) LoadLibrary (fullpath); } 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 !!!! */ - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -// the private structure - -typedef enum { NONE, LINK, LOAD } dllType; - -struct dll -{ - per_process p; - HMODULE handle; - const char *name; - dllType type; -}; +static NO_COPY int in_forkee = 0; +/* local variables */ //----------------------------------------------------------------------------- -#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) +/* Initialize an individual DLL */ +int +dll::init () { - 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) -{ - /* FIXME: init environment (useful?) */ - *(p->envptr) = *(user_data->envptr); - - /* FIXME: need other initializations? */ - int ret = 1; - if (!_in_forkee) + + /* 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 */ - 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; + + return NULL; } +#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) +{ + char name[MAX_PATH + 1]; + DWORD namelen = GetModuleFileName (h, name, sizeof (name)); + + /* Already loaded? */ + dll *d = dlls[name]; + if (d) + { + d->count++; /* Yes. Bump the usage count. */ + return d; /* Return previously allocated pointer. */ + } + + int i; + void *s = p->bss_end; + MEMORY_BASIC_INFORMATION m; + /* Search for space after the DLL */ + for (i = 0; i <= RETRIES; i++) + { + if (!VirtualQuery (s, &m, sizeof (m))) + return NULL; /* Can't do it. */ + if (m.State == MEM_FREE) + break; + s = (char *) m.BaseAddress + m.RegionSize; + } + + /* 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::currentDlOpenedLib (const char *name) +dll_list::detach (dll *d) { - if (_dlopenedLib != 0) - small_printf ("WARNING: previous dlopen of %s wasn't correctly performed\n", _dlopenedLib); - _dlopenedLib = name; - _dlopenIndex = -1; -} - -int -DllList::recordDll (HMODULE h, per_process *p) -{ - int ret = -1; - - /* debug_printf ("Record a dll p=%p\n", p); see WARNING */ - dllType type = LINK; - if (_initCalled) - { - type = LOAD; - _numberOfOpenedDlls++; - forkeeMustReloadDlls (1); - } - - if (_in_forkee) - { - ret = 0; // Just a flag - goto out; - } - - 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; + if (d->count <= 0) + system_printf ("WARNING: try to detach an already detached dll ...\n"); + else if (--d->count == 0) + { + 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); + } } +/* Initialization called by dll_crt0_1. */ void -DllList::detachDll (int dll_index) +dll_list::init () { - if (dll_index != -1) + debug_printf ("here"); + /* Make sure that destructors are called on exit. */ + if (!dll_global_dtors_recorded) { - dll *aDll = &(_list[dll_index]); - doGlobalDTORS (&aDll->p); - if (aDll->type == LOAD) - _numberOfOpenedDlls--; - aDll->type = NONE; - } - else - small_printf ("WARNING: try to detach an already detached dll ...\n"); -} - -void -DllList::initAll () -{ - // init for destructors - // because initAll isn't called in forked process, this exit function will - // be recorded only once - 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); + dll d; - 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; -} + 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); -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -// iterators - -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 () -{ + /* 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; +} diff --git a/winsup/cygwin/dll_init.h b/winsup/cygwin/dll_init.h index ca2cc2c20..ac1466803 100644 --- a/winsup/cygwin/dll_init.h +++ b/winsup/cygwin/dll_init.h @@ -1,6 +1,6 @@ /* dll_init.h - Copyright 1998 Cygnus Solutions + Copyright 1998, 1999, 2000 Cygnus Solutions This file is part of Cygwin. @@ -8,95 +8,84 @@ This software is a copyrighted work licensed under the terms of the Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ -//----------------------------------------------------------------------------- -// list of loaded DLL (used by fork & init) -class DllList +struct per_module { -public: - static DllList& the (); - - // return dll index used for freeDll - int recordDll (HMODULE, per_process*); - void detachDll (int dll_index); - - // called after initialization of main module in dll_crt0 - void initAll (); - - // global destructors of loaded dlls - void doGlobalDestructorsOfDlls (); - - // number of dlls dlopened - int numberOfOpenedDlls (); - - // boolean to determine if forked process must reload dlls opened with - // LoadLibrary or dlopen ... - // default = 0 (FALSE) - int forkeeMustReloadDlls (); - void forkeeMustReloadDlls (int); - - void forkeeLoadDlls (); - - // set name of current library opened with dlopen - void currentDlOpenedLib (const char*); + char ***envptr; + void (**ctors)(void); + void (**dtors)(void); + void *data_start; + void *data_end; + void *bss_start; + void *bss_end; + int (*main)(int, char **, char **); + per_module &operator = (per_process *p) + { + envptr = p->envptr; + ctors = p->ctors; + dtors = p->dtors; + data_start = p->data_start; + data_end = p->data_end; + bss_start = p->bss_start; + bss_end = p->bss_end; + main = p->main; + return *this; + } + void run_ctors (); + void run_dtors (); }; -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -class DllListIterator +typedef enum { - int _type; - int _index; + DLL_NONE, + DLL_LINK, + DLL_LOAD, + DLL_ANY +} dll_type; -protected: - DllListIterator (int type); - int index () const { return _index; } - -public: - virtual ~DllListIterator(); - - int ok() { return _index!=-1; } - void operator++ (); - void operator++ (int) { operator++ (); } - operator per_process* (); +struct dll +{ + struct dll *next, *prev; + per_module p; + HMODULE handle; + int count; + dll_type type; + int namelen; + char name[MAX_PATH + 1]; + void detach (); + int init (); }; -//----------------------------------------------------------------------------- +#define MAX_DLL_BEFORE_INIT 100 -class LinkedDllIterator : public DllListIterator +class dll_list { + dll *end; + dll *hold; + dll_type hold_type; public: - LinkedDllIterator (); - ~LinkedDllIterator (); + dll start; + int tot; + int loaded_dlls; + int reload_on_fork; + dll *operator [] (const char *name); + dll *alloc (HINSTANCE, per_process *, dll_type); + void detach (dll *); + void init (); + void load_after_fork (HANDLE, dll *); + dll *istart (dll_type t) + { + hold_type = t; + hold = &start; + return inext (); + } + dll *inext () + { + while ((hold = hold->next)) + if (hold_type == DLL_ANY || hold->type == hold_type) + break; + return hold; + } }; -//----------------------------------------------------------------------------- - -class LoadedDllIterator : public DllListIterator -{ -public: - LoadedDllIterator (); - ~LoadedDllIterator (); -}; - -//----------------------------------------------------------------------------- - -#define DO_LINKED_DLL(var) \ -{ \ -LinkedDllIterator iterator; \ -while (iterator.ok ()) \ -{ \ - per_process *var = (per_process *) iterator; - -#define DO_LOADED_DLL(var) \ -{ \ -LoadedDllIterator iterator; \ -while (iterator.ok ()) \ -{ \ - per_process *var = (per_process *) iterator; - -#define DLL_DONE \ - iterator++; \ -} \ -} - +extern dll_list dlls; diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc index 13167ccd1..4fb488d16 100644 --- a/winsup/cygwin/dtable.cc +++ b/winsup/cygwin/dtable.cc @@ -497,8 +497,8 @@ hinfo::linearize_fd_array (unsigned char *in_buf, int buflen) LPBYTE hinfo::de_linearize_fd_array (LPBYTE buf) { - int len; - size_t max_used_fd, inc_size; + int len, max_used_fd; + size_t inc_size; debug_printf ("buf %x", buf); @@ -518,7 +518,7 @@ hinfo::de_linearize_fd_array (LPBYTE buf) return NULL; } - for (size_t i = 0; i <= max_used_fd; i++) + for (int i = 0; i <= max_used_fd; i++) { /* 0xFF means closed */ if (*buf == 0xff) diff --git a/winsup/cygwin/environ.cc b/winsup/cygwin/environ.cc index 719a1712b..716af8547 100644 --- a/winsup/cygwin/environ.cc +++ b/winsup/cygwin/environ.cc @@ -13,7 +13,7 @@ details. */ #include #include -#define environ (*user_data->envptr) +#define environ __cygwin_environ extern BOOL allow_glob; extern BOOL allow_ntea; @@ -228,11 +228,12 @@ setenv (const char *name, const char *value, int rewrite) for (P = environ, cnt = 0; *P; ++P, ++cnt) ; - __cygwin_environ = environ = (char **) realloc ((char *) environ, - (size_t) (sizeof (char *) * (cnt + 2))); + environ = (char **) realloc ((char *) environ, + (size_t) (sizeof (char *) * (cnt + 2))); if (!environ) return -1; environ[cnt + 1] = NULL; + update_envptrs (); offset = cnt; } @@ -502,7 +503,8 @@ environ_init (int already_posix) if (!sawTERM) envp[i++] = strdup ("TERM=cygwin"); envp[i] = NULL; - __cygwin_environ = environ = envp; + environ = envp; + update_envptrs (); FreeEnvironmentStringsA ((char *) rawenv); parse_options (NULL); MALLOC_CHECK; diff --git a/winsup/cygwin/exec.cc b/winsup/cygwin/exec.cc index b83a9b905..0ba3ead6f 100644 --- a/winsup/cygwin/exec.cc +++ b/winsup/cygwin/exec.cc @@ -44,7 +44,7 @@ execl (const char *path, const char *arg0, ...) while (argv[i++] != NULL); va_end (args); MALLOC_CHECK; - return _execve (path, (char * const *) argv, *user_data->envptr); + return _execve (path, (char * const *) argv, __cygwin_environ); } extern "C" @@ -52,7 +52,7 @@ int execv (const char *path, char * const *argv) { MALLOC_CHECK; - return _execve (path, (char * const *) argv, *user_data->envptr); + return _execve (path, (char * const *) argv, __cygwin_environ); } /* the same as a standard exec() calls family, but with NT security support */ @@ -85,7 +85,7 @@ sexecl (HANDLE hToken, const char *path, const char *arg0, ...) va_end (args); MALLOC_CHECK; - return sexecve (hToken, path, (char * const *) argv, *user_data->envptr); + return sexecve (hToken, path, (char * const *) argv, __cygwin_environ); } extern "C" @@ -131,8 +131,7 @@ sexeclp (HANDLE hToken, const char *path, const char *arg0, ...) va_end (args); MALLOC_CHECK; - return sexecvpe (hToken, path, (const char * const *) argv, - *user_data->envptr); + return sexecvpe (hToken, path, (const char * const *) argv, __cygwin_environ); } extern "C" @@ -164,7 +163,7 @@ int sexecv (HANDLE hToken, const char *path, const char * const *argv) { MALLOC_CHECK; - return sexecve (hToken, path, argv, *user_data->envptr); + return sexecve (hToken, path, argv, __cygwin_environ); } extern "C" @@ -172,7 +171,7 @@ int sexecp (HANDLE hToken, const char *path, const char * const *argv) { MALLOC_CHECK; - return sexecvpe (hToken, path, argv, *user_data->envptr); + return sexecvpe (hToken, path, argv, __cygwin_environ); } /* diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc index 54c668137..fdd63c6e3 100644 --- a/winsup/cygwin/fork.cc +++ b/winsup/cygwin/fork.cc @@ -280,6 +280,13 @@ fork () return -1; } + /* Remember the address of the first loaded dll and decide + if we need to load dlls. We do this here so that this + information will be available in the parent and, when + the stack is copied, in the child. */ + dll *first_dll = dlls.start.next; + int load_dlls = dlls.reload_on_fork && dlls.loaded_dlls; + static child_info_fork ch; x = setjmp (ch.jmp); @@ -457,43 +464,41 @@ fork () if (!rc) goto cleanup; - /* Now fill data/bss of linked dll */ - DO_LINKED_DLL (p) - { - debug_printf ("copying data/bss of a linked dll"); - if (!fork_copy (pi, "linked dll data/bss", p->data_start, p->data_end, - p->bss_start, p->bss_end, - NULL)) - goto cleanup; - } - DLL_DONE; - - proc_register (child); - int load_dll = DllList::the().forkeeMustReloadDlls() && - DllList::the().numberOfOpenedDlls(); - - /* Start thread, and wait for it to reload dlls. */ - if (!resume_child (pi, forker_finished) || - !sync_with_child (pi, subproc_ready, load_dll, "child loading dlls")) - goto cleanup; - - /* child reload dlls & then write their data and bss */ - if (load_dll) - { - /* CHILD IS STOPPED */ - /* write memory of reloaded dlls */ - DO_LOADED_DLL (p) + /* Now fill data/bss of any DLLs that were linked into the program. */ + for (dll *d = dlls.istart (DLL_LINK); d; d = dlls.inext ()) { - debug_printf ("copying data/bss for a loaded dll"); - if (!fork_copy (pi, "loaded dll data/bss", p->data_start, p->data_end, - p->bss_start, p->bss_end, + debug_printf ("copying data/bss of a linked dll"); + if (!fork_copy (pi, "linked dll data/bss", d->p.data_start, d->p.data_end, + d->p.bss_start, d->p.bss_end, NULL)) goto cleanup; } - DLL_DONE; - /* Start the child up again. */ - (void) resume_child (pi, forker_finished); - } + + proc_register (child); + + /* Start thread, and wait for it to reload dlls. */ + if (!resume_child (pi, forker_finished) || + !sync_with_child (pi, subproc_ready, load_dlls, "child loading dlls")) + goto cleanup; + + /* If DLLs were loaded in the parent, then the child has reloaded all + of them and is now waiting to have all of the individual data and + bss sections filled in. */ + if (load_dlls) + { + /* CHILD IS STOPPED */ + /* write memory of reloaded dlls */ + for (dll *d = dlls.istart (DLL_LOAD); d; d = dlls.inext ()) + { + debug_printf ("copying data/bss for a loaded dll"); + if (!fork_copy (pi, "loaded dll data/bss", d->p.data_start, d->p.data_end, + d->p.bss_start, d->p.bss_end, + NULL)) + goto cleanup; + } + /* Start the child up again. */ + (void) resume_child (pi, forker_finished); + } ForceCloseHandle (subproc_ready); ForceCloseHandle (pi.hThread); @@ -532,6 +537,14 @@ fork () char c; if (GetEnvironmentVariable ("FORKDEBUG", &c, 1)) try_to_debug (); + char buf[80]; + /* This is useful for debugging fork problems. Use gdb to attach to + the pid reported here. */ + if (GetEnvironmentVariable ("CYGWIN_FORK_SLEEP", buf, sizeof (buf))) + { + small_printf ("Sleeping %d after fork, pid %u\n", atoi (buf), GetCurrentProcessId ()); + Sleep (atoi(buf)); + } #endif /* If we've played with the stack, stacksize != 0. That means that @@ -548,20 +561,22 @@ fork () dtable.fixup_after_fork (hParent); signal_fixup_after_fork (); - ForceCloseHandle (hParent); MALLOC_CHECK; - /* reload dlls if necessary */ - if (!DllList::the().forkeeMustReloadDlls() || - !DllList::the().numberOfOpenedDlls()) + /* If we haven't dynamically loaded any dlls, just signal + the parent. Otherwise, load all the dlls, tell the parent + that we're done, and wait for the parent to fill in the. + loaded dlls' data/bss. */ + if (!load_dlls) sync_with_parent ("performed fork fixup.", FALSE); else { - DllList::the().forkeeLoadDlls(); + dlls.load_after_fork (hParent, first_dll); sync_with_parent ("loaded dlls", TRUE); } + ForceCloseHandle (hParent); (void) ForceCloseHandle (child_proc_info->subproc_ready); (void) ForceCloseHandle (child_proc_info->forker_finished); diff --git a/winsup/cygwin/lib/_cygwin_crt0_common.cc b/winsup/cygwin/lib/_cygwin_crt0_common.cc index fcca1694f..4ca6dd74e 100644 --- a/winsup/cygwin/lib/_cygwin_crt0_common.cc +++ b/winsup/cygwin/lib/_cygwin_crt0_common.cc @@ -56,10 +56,7 @@ _cygwin_crt0_common (MainFunc f, per_process *u) u->ctors = &__CTOR_LIST__; u->dtors = &__DTOR_LIST__; - if (!u->envptr) - u->envptr = &environ; - else - environ = *(u->envptr); + u->envptr = &environ; if (uwasnull) _impure_ptr = u->impure_ptr; /* Use field initialized in newer DLLs. */ else diff --git a/winsup/cygwin/lib/cygwin_attach_dll.c b/winsup/cygwin/lib/cygwin_attach_dll.c index 09b898bdf..440cace57 100644 --- a/winsup/cygwin/lib/cygwin_attach_dll.c +++ b/winsup/cygwin/lib/cygwin_attach_dll.c @@ -17,15 +17,9 @@ details. */ int cygwin_attach_dll (HMODULE h, MainFunc f) { - struct per_process *u; - if (_cygwin_crt0_common (f, NULL)) - u = NULL; /* Newer DLL. Use DLL internal per_process. */ - else /* Older DLL. Provide a per_process */ - { - u = (struct per_process *) alloca (sizeof (*u)); - (void) _cygwin_crt0_common (f, u); - } + struct per_process u; + (void) _cygwin_crt0_common (f, &u); /* jump into the dll. */ - return dll_dllcrt0 (h, u); + return dll_dllcrt0 (h, &u); } diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc index f6da86385..6ca82e413 100644 --- a/winsup/cygwin/spawn.cc +++ b/winsup/cygwin/spawn.cc @@ -894,7 +894,7 @@ spawnl (int mode, const char *path, const char *arg0, ...) va_end (args); return _spawnve (NULL, mode, path, (char * const *) argv, - *user_data->envptr); + __cygwin_environ); } extern "C" @@ -939,7 +939,7 @@ spawnlp (int mode, const char *path, const char *arg0, ...) va_end (args); - return spawnvpe (mode, path, (char * const *) argv, *user_data->envptr); + return spawnvpe (mode, path, (char * const *) argv, __cygwin_environ); } extern "C" @@ -969,7 +969,7 @@ extern "C" int spawnv (int mode, const char *path, const char * const *argv) { - return _spawnve (NULL, mode, path, argv, *user_data->envptr); + return _spawnve (NULL, mode, path, argv, __cygwin_environ); } extern "C" @@ -984,7 +984,7 @@ extern "C" int spawnvp (int mode, const char *path, const char * const *argv) { - return spawnvpe (mode, path, argv, *user_data->envptr); + return spawnvpe (mode, path, argv, __cygwin_environ); } extern "C" diff --git a/winsup/cygwin/winsup.h b/winsup/cygwin/winsup.h index 61dd62786..5a1ff2ff1 100644 --- a/winsup/cygwin/winsup.h +++ b/winsup/cygwin/winsup.h @@ -323,6 +323,8 @@ extern HANDLE netapi32_handle; extern "C" void error_start_init (const char*); extern "C" int try_to_debug (); +extern int cygwin_finished_initializing; + /**************************** Miscellaneous ******************************/ const char * __stdcall find_exec (const char *name, path_conv& buf, const char *winenv = "PATH=", @@ -473,6 +475,7 @@ struct win_env win_env * __stdcall getwinenv (const char *name, const char *posix = NULL); +void __stdcall update_envptrs (); char * __stdcall winenv (const char * const *, int); extern char **__cygwin_environ;