2d1d1eb1e4
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.
431 lines
11 KiB
C++
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;
|
|
}
|