* 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.
This commit is contained in:
Christopher Faylor
2000-07-15 02:48:11 +00:00
parent 44d2afed74
commit 2eb392bd77
13 changed files with 427 additions and 496 deletions

View File

@ -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;
}