import winsup-2000-02-17 snapshot
This commit is contained in:
499
winsup/cygwin/dll_init.cc
Normal file
499
winsup/cygwin/dll_init.cc
Normal file
@@ -0,0 +1,499 @@
|
||||
/* 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 <stdlib.h>
|
||||
#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);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
Reference in New Issue
Block a user