500 lines
11 KiB
C++
500 lines
11 KiB
C++
|
/* 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);
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
//-----------------------------------------------------------------------------
|