/* dll_init.cc Copyright 1998, 1999, 2000 Cygnus Solutions. This software is a copyrighted work licensed under the terms of the Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ #include #include "winsup.h" #include "exceptions.h" #include "dll_init.h" 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*/ /* 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; }; //----------------------------------------------------------------------------- #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 void __dll_global_dtors() { _the.doGlobalDestructorsOfDlls(); } static void doGlobalCTORS (per_process *p) { void (**pfunc)() = p->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]) (); } } static void doGlobalDTORS (per_process *p) { if (!p) return; void (**pfunc)() = p->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) { /* global variable user_data must be initialized */ if (user_data == NULL) { small_printf ("WARNING: process not inited while trying to init a DLL!\n"); return 0; } /* init impure_ptr */ *(p->impure_ptr_ptr) = *(user_data->impure_ptr_ptr); /* FIXME: init environment (useful?) */ *(p->envptr) = *(user_data->envptr); /* FIXME: need other initializations? */ int ret = 1; if (!_in_forkee) { /* global contructors */ doGlobalCTORS (p); /* entry point of dll (use main of per_process with null args...) */ if (p->main) ret = (*(p->main)) (0, 0, 0); } return ret; } DllList& DllList::the () { return _the; } 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; } 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; } void DllList::detachDll (int dll_index) { if (dll_index != -1) { 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; } 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; } #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 reserve_upto (const char *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 = 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 '%s' 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. */ void release_upto (const char *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 < user_data->heapbase || (void *) start > user_data->heaptop))) continue; if (!VirtualFree ((void *) start, 0, MEM_RELEASE)) api_fatal ("couldn't release memory %p(%d) for '%s' 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 DllList::forkeeLoadDlls () { _initCalled = 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 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 () { } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // the extern symbols extern "C" { /* This is an exported copy of environ which can be used by DLLs which use cygwin.dll. */ extern struct _reent reent_data; }; extern "C" int dll_dllcrt0 (HMODULE h, per_process *p) { /* Partially initialize Cygwin guts for non-cygwin apps. */ if (dynamically_loaded && (! user_data || user_data->magic_biscuit == 0)) { dll_crt0 (p); } return _the.recordDll (h, p); } /* 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 dll_noncygwin_dllcrt0 (HMODULE h, per_process *p) { return dll_dllcrt0 (h, p); } extern "C" void cygwin_detach_dll (int dll_index) { _the.detachDll (dll_index); } extern "C" void dlfork (int val) { _the.forkeeMustReloadDlls (val); } //----------------------------------------------------------------------------- //-----------------------------------------------------------------------------