newlib/winsup/cygwin/dll_init.cc
Christopher Faylor 2d1d1eb1e4 * cygtls.h (_threadinfo::call): Remove regparm declaration to work around
compiler bug.
* autoload.cc (TryEnterCriticalSection): Remove.
* dcrt0.cc (dll_crt0_0): Delete inappropriate setting of _my_tls.stackptr to
NULL since it has really bad consequences.  Make 'si' an automatic variable.
* cygtls.cc (_threadinfo::init_thread): Correct thinko which caused thread list
to be allocated every time.
* cygtls.h (CYGTLS_PADSIZE): Define as const int.
* sync.h: Make multiple inclusion safe.
(muto::next): Eliminate.
(muto::exiting_thread): New variable.
(muto::set_exiting_thread): New function.
(new_muto): Change to use different section for mutos since c++ give
inexplicable warning in some cases otherwise.
(new_muto1): Ditto.
* dcrt0.cc (do_exit): Call muto::set_exiting_thread here.
* sync.cc (muto_start): Eliminate.
(muto::acquire): Always give exiting thread a lock.  Never give thread a lock
if exiting.
(muto::release): Ditto for releasing.
* dtable.cc (dtable::init_lock): Unline function and define here.
* dtable.h (lock_cs): Define as a muto since critical sections seem to work
oddly on Windows Me.
(lock): Accommodate switch to muto.
(unlock): Ditto.
* exceptions.cc (setup_handler): Don't worry about acquiring mutos since that
hasn't mattered for a long time.
(signal_exit): Ditto: muto stuff will be handled automatically on exit now.
* Makefile.in (DLL_IMPORTS): Link advapi32 to ensure proper DLL initialization.
* autoload.cc (RegCloseKey): Arbitrarily choose this function as a "seed" to
pull the advapi32 link library in.  So, comment out the autoloading.
* cygtls.cc (_threadinfo::init_thread): Just clear CYGTLS_PADSIZE.
(_threadinfo::remove): Add debugging.
(_threadinfo::find_tls): Ditto.
* cygtls.h (_threadinfo::padding): Make zero length (for now?).
* dcrt0.cc (dll_crt0_0): Move more initialization here from dll_crt0_1.
(dll_crt0_1): See above.
* dtable.h (dtable::lock): Remove commented out critical section locking.
* dtable.h (dtable::init_lock): Remove commented out critical section locking.
* dtable.h (dtable::unlock): Remove commented out critical section locking.
* exceptions.cc (interruptible): bool'ize.
* init.cc (threadfunc_fe): Revert to storing threadfunc at stack bottom.
(munge_threadfunc): Ditto.  Avoid adding overhead to calibration_thread.
(prime_threads): Don't initialize tls stuff.
(dll_entry): Make minor change to initialization order.
* tlsoffsets.h: Regenerate.
* sigproc.cc (wait_sig): Add sanity check for end of process thread exit.
* select.h: Make minor formatting change.
* Makefile.in: Add still more -fomit-frame-pointer functions.
* dtable.h (dtable::lock): New function.
(dtable::unlock): New function.
(dtable::init_lock): New function.
* cygheap.h (HEAP_TLS): Declare new enum value.
(init_cygheap::threadlist): Declare new array.
(init_cygheap::sthreads): Declare new variable.
(cygheap_fdmanip::~cygheap_fdmanip): Use new dtable lock/unlock functions.
(cygheap_fdnew::cygheap_fdnew): Ditto.
(cygheap_fdget::cygheap_fdget): Ditto.
* dtable.cc (dtable_init): Initialize fdtab critical section.
(dtable::fixup_after_fork): Ditto.
(dtable::fixup_after_exec): Ditto.
(dtable::dup2): Use lock/unlock calls to protect access to fdtab.
(dtable::find_fifo): Ditto.
(dtable::fixup_before_fork): Ditto.
(dtable::fixup_before_exec): Ditto.
(dtable::set_file_pointers_for_exec): Ditto.
(dtable::vfork_child_dup): Ditto.
(dtable::vfork_parent_restore): Ditto.
* syscalls.cc (close_all_files): Ditto.
* sync.h (muto::acquired): Declare new function.
(new_muto1): Declare new macro used to specify name of muto storage.
* sync.cc (muto::acquired): Define new function.
* cygthread.cc (cygthread::stub): Remove signal chain removal call since it is
handled during initialization now.
* cygthread.cc (cygthread::simplestub): Remove signal chain removal call since
it is handled during initialization now.
* cygtls.cc (sentry): New class used for locking.  Use throughout.
(_threadinfo::reset_exception): Don't pop stack.
(_threadinfo::find_tls): Move from exceptions.cc.
(_threadinfo::init_thread): Initialize array of threads rather than linked
list.  Take second argument indicating thread function for this thread.
(_threadinfo::remove): Search thread array rather than linked list.  Use sentry
to lock.  Only unlock if we got the lock.
(_threadinfo::find_tls): Ditto for first two.
(handle_threadlist_exception): Handle exceptions when manipulating the thread
list in case of premature thread termination.
(_threadinfo::init_threadlist_exceptions): Ditto.
* cygtls.h (TLS_STACK_SIZE): Decrease size.
(_threadinfo::padding): Add element to avoid overwriting lower part of stack.
(_threadinfo::remove): Add a "wait" argument to control how long we wait for a
lock before removing.
* exceptions.cc (init_exception_handler): Make global.  Take argument to
control exception handler being set.
(ctrl_c_handler): Wait forever when removing self from signal chain.
(_threadinfo::find_tls): Move to cygtls.cc.
(sig_handle): Reorganize detection for thread-specific signals.
* heap.cc (heap_init): Rework slightly.  Make fatal error more verbose.  Remove
malloc initialization since it can't happen during dll attach.
* init.cc (search_for): Move address to search for on stack here.
(threadfunc_ix): Ditto for stack offset.  Make shared so that stack walk
potentially only has to be done once when cygwin processes are running.
(threadfunc_fe): Use standard tls to store thread function (may change back
later).
(calibration_thread): New function.  Potentially called to find threadfunc_ix.
(munge_threadfunc): Search for "search_for" value on stack.  Output warning
when thread func not found on stack.  Use standard tls to store thread
function.
(prime_threads): New function.  Called to prime thread front end.
(dll_entry): Call dll_crt0_0 here when DLL_PROCESS_ATTACH.  Call prime_threads
here.  Try to remove thread from signal list here.
* sigproc.cc (wait_sig): Initialize threadlist exception stuff here.
* thread.cc (pthread::exit): Pass argument to signal list remove function.
* thread.h: Remove obsolete *ResourceLock defines.
* tlsoffsets.h: Regenerate.
* winsup.h (spf): Define temporary debug macro to be deleted later.
* dcrt0.cc (dll_crt0_0): New function, called during DLL initialization.
Mainly consists of code pulled from dll_crt0_1.
(dll_crt0_1): See above.
(_dll_crt0): Wait for initial calibration thread to complete, if appropriate.
Move some stuff to dll_crt0_0.
(initialize_main_tls): Accommodate argument change to
_thread_info::init_thread.
* fork.cc (fork_child): Ditto.
(sync_with_child): Fix debug message.
* external.cc (cygwin_internal): Remove special considerations for
uninitialized dll since initialization happens during dll attach now.
* dlfcn.cc (dlopen): Remove obsolete *ResourceLock calls.
(dlclose): Ditto.
* cygheap.h (init_cygheap::close_ctty): Declare new function.
* cygheap.cc (init_cygheap::close_ctty): Define new function.
* syscalls.cc (close_all_files): Use close_ctty.
(setsid): Ditto.
* cygthread.cc (cygthread::stub): Remove exception initialization.
* cygthread.cc (cygthread::stub): Remove exception initialization.
(cygthread::simplestub): Ditto.
* thread.cc (pthread::thread_init_wrapper): Ditto.
* cygtls.cc (_last_thread): Make static.
(_threadinfo::call2): Initialize exception handler here.
(_threadinfo::find_tls): Move here.
* exceptions.cc (_threadinfo::find_tls): Move.
* dcrt0.cc (__api_fatal): Add prefix info to message here rather than including
it in every call to function.
* winsup.h (api_fatal): Accommodate above change.
* debug.cc (add_handle): Don't do anything if cygheap not around.
(mark_closed): Ditto.
* dll_init.cc (dll_list::detach): Fix debug output.
* fork.cc (sync_with_child): Ditto.
(vfork): Improve debug output.
* heap.cc (heap_init): Ditto.
* exceptions.cc (try_to_debug): Clarify message when debugger attaches.
2004-01-14 15:45:37 +00:00

431 lines
11 KiB
C++

/* dll_init.cc
Copyright 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include "winsup.h"
#include <stdlib.h>
#include "cygerrno.h"
#include "perprocess.h"
#include "dll_init.h"
#include "environ.h"
#include "security.h"
#include "path.h"
#include "fhandler.h"
#include "dtable.h"
#include "cygheap.h"
#include "pinfo.h"
extern void __stdcall check_sanity_and_sync (per_process *);
dll_list NO_COPY dlls;
static NO_COPY int in_forkee = 0;
static int dll_global_dtors_recorded;
/* Run destructors for all DLLs on exit. */
static void
dll_global_dtors ()
{
for (dll *d = dlls.istart (DLL_ANY); d; d = dlls.inext ())
d->p.run_dtors ();
}
/* Run all constructors associated with a dll */
void
per_module::run_ctors ()
{
void (**pfunc)() = 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]) ();
}
}
/* Run all destructors associated with a dll */
void
per_module::run_dtors ()
{
void (**pfunc)() = dtors;
for (int i = 1; pfunc[i]; i++)
(pfunc[i]) ();
}
/* Initialize an individual DLL */
int
dll::init ()
{
int ret = 1;
/* 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 */
p.run_ctors ();
/* entry point of dll (use main of per_process with null args...) */
if (p.main)
ret = (*(p.main)) (0, 0, 0);
}
return ret;
}
/* Look for a dll based on name */
dll *
dll_list::operator[] (const char *name)
{
dll *d = &start;
while ((d = d->next) != NULL)
if (strcasematch (name, d->name))
return d;
return NULL;
}
#define RETRIES 1000
/* Allocate space for a dll struct contiguous with the just-loaded dll. */
dll *
dll_list::alloc (HINSTANCE h, per_process *p, dll_type type)
{
char name[CYG_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. */
}
SYSTEM_INFO s1;
GetSystemInfo (&s1);
int i;
void *s = p->bss_end;
DWORD n;
MEMORY_BASIC_INFORMATION m;
/* Search for space after the DLL */
for (i = 0; i <= RETRIES; i++, s = (char *) m.BaseAddress + m.RegionSize)
{
if (!VirtualQuery (s, &m, sizeof (m)))
return NULL; /* Can't do it. */
if (m.State == MEM_FREE)
{
/* Couldn't find any. Uh oh. FIXME: Issue an error? */
if (i == RETRIES)
return NULL; /* Oh well. Couldn't locate free space. */
/* Ensure that this is rounded to the nearest page boundary.
FIXME: Should this be ensured by VirtualQuery? */
n = (DWORD) m.BaseAddress;
DWORD r = n % s1.dwAllocationGranularity;
if (r)
n = ((n - r) + s1.dwAllocationGranularity);
/* First reserve the area of memory, then commit it. */
if (VirtualAlloc ((void *) n, sizeof (dll), MEM_RESERVE, PAGE_READWRITE))
d = (dll *) VirtualAlloc ((void *) n, sizeof (dll), MEM_COMMIT,
PAGE_READWRITE);
if (d)
break;
}
}
/* Did we succeed? */
if (d == NULL)
{ /* Nope. */
#ifdef DEBUGGING
system_printf ("VirtualAlloc failed, %E");
#endif
__seterrno ();
return NULL;
}
/* 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
dll_list::detach (void *retaddr)
{
if (!myself || myself->process_state == PID_EXITED)
return;
MEMORY_BASIC_INFORMATION m;
if (!VirtualQuery (retaddr, &m, sizeof m))
return;
HMODULE h = (HMODULE) m.AllocationBase;
dll *d = &start;
while ((d = d->next))
if (d->handle != h)
continue;
else if (d->count <= 0)
system_printf ("WARNING: trying to detach an already detached dll ...");
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;
VirtualFree (d, 0, MEM_RELEASE);
break;
}
}
/* Initialization for all linked DLLs, called by dll_crt0_1. */
void
dll_list::init ()
{
/* Make sure that destructors are called on exit. */
if (!dll_global_dtors_recorded)
{
atexit (dll_global_dtors);
dll_global_dtors_recorded = 1;
}
/* 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. */
static 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 = A64K;
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. */
static 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 < cygheap->user_heap.base
|| (void *) start > cygheap->user_heap.top) &&
((void *) start < (void *) cygheap
| (void *) start > (void *) ((char *) cygheap + CYGHEAPSIZE)))))
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
dll_list::load_after_fork (HANDLE parent, dll *first)
{
in_forkee = 1;
int try2 = 0;
dll d;
void *next = first;
while (next)
{
DWORD nb;
/* Read the dll structure from the parent. */
if (!ReadProcessMemory (parent, next, &d, sizeof (dll), &nb) ||
nb != sizeof (dll))
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)
{
bool unload = true;
HMODULE h = LoadLibraryEx (d.name, NULL, DONT_RESOLVE_DLL_REFERENCES);
if (!h)
system_printf ("can't reload %s", d.name);
/* 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. */
else if (h == d.handle)
{
if (unload)
{
FreeLibrary (h);
LoadLibrary (d.name);
}
}
else if (try2)
api_fatal ("unable to remap %s to same address as parent(%p) != %p",
d.name, d.handle, 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
dll_dllcrt0 (HMODULE h, per_process *p)
{
if (p == NULL)
p = &__cygwin_user_data;
else
*(p->impure_ptr_ptr) = __cygwin_user_data.impure_ptr;
/* Partially initialize Cygwin guts for non-cygwin apps. */
if (dynamically_loaded && user_data->magic_biscuit == 0)
dll_crt0 (p);
else
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
dll_noncygwin_dllcrt0 (HMODULE h, per_process *p)
{
return dll_dllcrt0 (h, p);
}
extern "C" void
cygwin_detach_dll (dll *)
{
dlls.detach (__builtin_return_address (0));
}
extern "C" void
dlfork (int 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;
}