* cygheap.h (mini_cygheap): New struct.

(init_cygheap): Inherit locale field via mini_cygheap.
* cygheap.cc (cygheap_at_start): Define new variable.
(cygheap): Initialize as cygheap_at_start so that locale information is always
available.
(cygheap_init): Initialize cygheap iff it is set to cygheap_at_start.
* shared_info.h (memory_init): Accommodate argument change.
* memory.cc (memory_init): Accept an argument indicating whether cygheap should
be initialized or not.
* dcrt0.cc (child_info_fork::handle_fork): Pass false to memory_init().
(child_info_spawn::handle_spawn): Ditto.
(dll_crt0_0): Pass true to memory_init when not forking or execing.
* cygheap.h (cygheap_types::HEAP_2_DLL): New enum.
* dll_init.h (dll): Remove unused namelen field.
(dll_list::load_after_fork): Accommodate change in arguments.
* dll_init.cc (dll_list::alloc): Allocate dll information in the cygwin heap.
(dll_list::detach): Free dll information from the cygwin heap.
(dll_list::load_after_fork): Use dll information in the cygwin heap directly
rather than querying parent.
* fork.cc (frok::first_dll): Delete.
(frok::child): Don't report on first_dll.  Don't pass it to load_on_fork.
(frok::parent): Don't set first_dll.
(fork): Ditto.
This commit is contained in:
Christopher Faylor 2009-06-08 03:53:40 +00:00
parent 1866410bb8
commit 71f53a2f62
9 changed files with 104 additions and 185 deletions

View File

@ -1,3 +1,32 @@
2009-06-07 Christopher Faylor <me+cygwin@cgf.cx>
* cygheap.h (mini_cygheap): New struct.
(init_cygheap): Inherit locale field via mini_cygheap.
* cygheap.cc (cygheap_at_start): Define new variable.
(cygheap): Initialize as cygheap_at_start so that locale information is
always available.
(cygheap_init): Initialize cygheap iff it is set to cygheap_at_start.
* shared_info.h (memory_init): Accommodate argument change.
* memory.cc (memory_init): Accept an argument indicating whether
cygheap should be initialized or not.
* dcrt0.cc (child_info_fork::handle_fork): Pass false to memory_init().
(child_info_spawn::handle_spawn): Ditto.
(dll_crt0_0): Pass true to memory_init when not forking or execing.
* cygheap.h (cygheap_types::HEAP_2_DLL): New enum.
* dll_init.h (dll): Remove unused namelen field.
(dll_list::load_after_fork): Accommodate change in arguments.
* dll_init.cc (dll_list::alloc): Allocate dll information in the cygwin
heap.
(dll_list::detach): Free dll information from the cygwin heap.
(dll_list::load_after_fork): Use dll information in the cygwin heap
directly rather than querying parent.
* fork.cc (frok::first_dll): Delete.
(frok::child): Don't report on first_dll. Don't pass it to
load_on_fork.
(frok::parent): Don't set first_dll.
(fork): Ditto.
2009-06-06 Corinna Vinschen <corinna@vinschen.de> 2009-06-06 Corinna Vinschen <corinna@vinschen.de>
* dll_init.cc (dll_list::alloc): Allocate memory using a section * dll_init.cc (dll_list::alloc): Allocate memory using a section

View File

@ -24,7 +24,12 @@
#include <unistd.h> #include <unistd.h>
#include <wchar.h> #include <wchar.h>
init_cygheap NO_COPY *cygheap; static mini_cygheap NO_COPY cygheap_at_start =
{
{__utf8_mbtowc, __utf8_wctomb}
};
init_cygheap NO_COPY *cygheap = (init_cygheap *) &cygheap_at_start;
void NO_COPY *cygheap_max; void NO_COPY *cygheap_max;
extern "C" char _cygheap_mid[] __attribute__((section(".cygheap"))); extern "C" char _cygheap_mid[] __attribute__((section(".cygheap")));
@ -33,11 +38,11 @@ extern "C" char _cygheap_end[];
static NO_COPY muto cygheap_protect; static NO_COPY muto cygheap_protect;
struct cygheap_entry struct cygheap_entry
{ {
int type; int type;
struct cygheap_entry *next; struct cygheap_entry *next;
char data[0]; char data[0];
}; };
#define NBUCKETS (sizeof (cygheap->buckets) / sizeof (cygheap->buckets[0])) #define NBUCKETS (sizeof (cygheap->buckets) / sizeof (cygheap->buckets[0]))
#define N0 ((_cmalloc_entry *) NULL) #define N0 ((_cmalloc_entry *) NULL)
@ -150,7 +155,7 @@ extern "C" void __stdcall
cygheap_init () cygheap_init ()
{ {
cygheap_protect.init ("cygheap_protect"); cygheap_protect.init ("cygheap_protect");
if (!cygheap) if (cygheap == &cygheap_at_start)
{ {
cygheap = (init_cygheap *) memset (_cygheap_start, 0, cygheap = (init_cygheap *) memset (_cygheap_start, 0,
_cygheap_mid - _cygheap_start); _cygheap_mid - _cygheap_start);

View File

@ -31,6 +31,7 @@ enum cygheap_types
HEAP_1_EXEC, HEAP_1_EXEC,
HEAP_1_MAX = 100, HEAP_1_MAX = 100,
HEAP_2_STR, HEAP_2_STR,
HEAP_2_DLL,
HEAP_MMAP = 200 HEAP_MMAP = 200
}; };
@ -282,13 +283,17 @@ struct hook_chain
struct hook_chain *next; struct hook_chain *next;
}; };
struct init_cygheap struct mini_cygheap
{
cygheap_locale locale;
};
struct init_cygheap: public mini_cygheap
{ {
_cmalloc_entry *chain; _cmalloc_entry *chain;
char *buckets[32]; char *buckets[32];
cygheap_root root; cygheap_root root;
cygheap_user user; cygheap_user user;
cygheap_locale locale;
user_heap_info user_heap; user_heap_info user_heap;
mode_t umask; mode_t umask;
HANDLE console_h; HANDLE console_h;

View File

@ -571,7 +571,7 @@ void
child_info_fork::handle_fork () child_info_fork::handle_fork ()
{ {
cygheap_fixup_in_child (false); cygheap_fixup_in_child (false);
memory_init (); memory_init (false);
myself.thisproc (NULL); myself.thisproc (NULL);
myself->uid = cygheap->user.real_uid; myself->uid = cygheap->user.real_uid;
myself->gid = cygheap->user.real_gid; myself->gid = cygheap->user.real_gid;
@ -598,7 +598,7 @@ child_info_spawn::handle_spawn ()
extern void fixup_lockf_after_exec (); extern void fixup_lockf_after_exec ();
HANDLE h; HANDLE h;
cygheap_fixup_in_child (true); cygheap_fixup_in_child (true);
memory_init (); memory_init (false);
if (!moreinfo->myself_pinfo || if (!moreinfo->myself_pinfo ||
!DuplicateHandle (hMainProc, moreinfo->myself_pinfo, hMainProc, &h, 0, !DuplicateHandle (hMainProc, moreinfo->myself_pinfo, hMainProc, &h, 0,
FALSE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE)) FALSE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
@ -702,7 +702,7 @@ dll_crt0_0 ()
child_proc_info = get_cygwin_startup_info (); child_proc_info = get_cygwin_startup_info ();
if (!child_proc_info) if (!child_proc_info)
memory_init (); memory_init (true);
else else
{ {
cygwin_user_h = child_proc_info->user_h; cygwin_user_h = child_proc_info->user_h;

View File

@ -20,14 +20,10 @@ details. */
#include "pinfo.h" #include "pinfo.h"
#include "cygtls.h" #include "cygtls.h"
#include <wchar.h> #include <wchar.h>
#include <alloca.h>
#include <unistd.h>
#include <sys/param.h>
#include "ntdll.h"
extern void __stdcall check_sanity_and_sync (per_process *); extern void __stdcall check_sanity_and_sync (per_process *);
dll_list NO_COPY dlls; dll_list dlls;
static bool dll_global_dtors_recorded; static bool dll_global_dtors_recorded;
@ -107,7 +103,7 @@ dll_list::operator[] (const PWCHAR name)
#define RETRIES 1000 #define RETRIES 1000
/* Allocate space for a dll struct contiguous with the just-loaded dll. */ /* Allocate space for a dll struct. */
dll * dll *
dll_list::alloc (HINSTANCE h, per_process *p, dll_type type) dll_list::alloc (HINSTANCE h, per_process *p, dll_type type)
{ {
@ -118,96 +114,16 @@ dll_list::alloc (HINSTANCE h, per_process *p, dll_type type)
dll *d = dlls[name]; dll *d = dlls[name];
if (d) if (d)
{ {
d->count++; /* Yes. Bump the usage count. */ if (!in_forkee)
d->count++; /* Yes. Bump the usage count. */
return d; /* Return previously allocated pointer. */ return d; /* Return previously allocated pointer. */
} }
void *s = p->bss_end; d = (dll *) cmalloc (HEAP_2_DLL, sizeof (*d) + (namelen * sizeof (*name)));
size_t d_size = sizeof (dll) + namelen * sizeof (WCHAR);
MEMORY_BASIC_INFORMATION m;
NTSTATUS status = 0;
HANDLE sect_h;
OBJECT_ATTRIBUTES oa;
InitializeObjectAttributes (&oa, NULL, 0, NULL,
sec_none.lpSecurityDescriptor);
/* Search for space after the DLL */
for (int 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. */
d = (dll *) m.BaseAddress;
/* Instead of calling VirtualAlloc, which always allocates memory
on a 64K boundary, we allocate the memory using a section
object. The disadvantage of the 64K boundary in this case is
the fact that that boundary could be easily the start address
of another DLL yet to load into memory.
On x86, using a section object allows us to allocate the struct
dll into a memory slot in the remainder of the last 64K slot of
the DLL. This memory slot will never be used for anything
else. Given that the struct dll will fit into a single page
99.99% of the time anyway, this is a neat way to avoid DLL load
address collisions in most cases.
Of course, this doesn't help if the DLL needs all of the 64K
memory slot but there's only a 1 in 16 chance for that.
And, alas, it won't work on 64 bit systems because the
AT_ROUND_TO_PAGE flag required to make a page-aligned allocation
isn't supported under WOW64. So, as with VirtualAlloc, ensure
that address is rounded up to next 64K allocation boundary if
running under WOW64. */
if (wincap.is_wow64 ())
d = (dll *) roundup2 ((uintptr_t) d, getpagesize ());
LARGE_INTEGER so = { QuadPart: d_size };
status = NtCreateSection (&sect_h, SECTION_ALL_ACCESS, &oa, &so,
PAGE_READWRITE, SEC_COMMIT, NULL);
if (NT_SUCCESS (status))
{
ULONG viewsize = 0;
so.QuadPart = 0;
status = NtMapViewOfSection (sect_h, GetCurrentProcess (),
(void **) &d, 0, d_size, &so,
&viewsize, ViewUnmap,
wincap.is_wow64 ()
? 0 : AT_ROUND_TO_PAGE,
PAGE_READWRITE);
#ifdef DEBUGGING
if (!NT_SUCCESS (status))
system_printf ("NtMapViewOfSection failed, %p", status);
#endif
NtClose (sect_h);
}
#ifdef DEBUGGING
else
system_printf ("NtCreateSection failed, %p", status);
#endif
if (NT_SUCCESS (status))
break;
}
}
/* Did we succeed? */
if (!NT_SUCCESS (status))
{ /* Nope. */
__seterrno_from_nt_status (status);
return NULL;
}
/* Now we've allocated a block of information. Fill it in with the supplied /* Now we've allocated a block of information. Fill it in with the supplied
info about this DLL. */ info about this DLL. */
d->count = 1; d->count = 1;
d->namelen = namelen;
wcscpy (d->name, name); wcscpy (d->name, name);
d->handle = h; d->handle = h;
d->p = p; d->p = p;
@ -251,7 +167,7 @@ dll_list::detach (void *retaddr)
loaded_dlls--; loaded_dlls--;
if (end == d) if (end == d)
end = d->prev; end = d->prev;
NtUnmapViewOfSection (GetCurrentProcess (), d); cfree (d);
break; break;
} }
} }
@ -321,82 +237,51 @@ release_upto (const PWCHAR name, DWORD here)
} }
/* Reload DLLs after a fork. Iterates over the list of dynamically loaded /* 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 DLLs and attempts to load them in the same place as they were loaded in the
the parent. */ parent. */
void void
dll_list::load_after_fork (HANDLE parent, dll *first) dll_list::load_after_fork (HANDLE parent)
{ {
int try2 = 0; for (dll *d = &dlls.start; (d = d->next) != NULL; )
dll *d = (dll *) alloca (sizeof (dll) + (NT_MAX_PATH - 1) * sizeof (WCHAR)); if (d->type == DLL_LOAD)
for (int i = 0; i < 2; i++)
void *next = first;
while (next)
{
DWORD nb;
/* Read 4K of the dll structure from the parent. A full page has
been allocated anyway and this covers most, if not all DLL paths.
Only if d->namelen indicates that more than 4K are required,
read them in a second step. */
if (!ReadProcessMemory (parent, next, d, getsystempagesize (), &nb)
|| nb != getsystempagesize ())
return;
size_t namelen = d->namelen * sizeof (WCHAR);
if (namelen >= getsystempagesize () - sizeof (dll)
&& (!ReadProcessMemory (parent, next, d->name, namelen, &nb)
|| nb != namelen))
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 = LoadLibraryExW (d->name, NULL,
DONT_RESOLVE_DLL_REFERENCES);
if (!h)
system_printf ("can't reload %W", d->name);
/* See if DLL will load in proper place. If so, free it and reload /* See if DLL will load in proper place. If so, free it and reload
it the right way. it the right way.
It sort of stinks that we can't invert the order of the It stinks that we can't invert the order of the initial LoadLibrary
FreeLibrary and LoadLibrary since Microsoft documentation seems and FreeLibrar since Microsoft documentation seems to imply that
to imply that that should do what we want. However, since the should do what we want. However, once a library is loaded as
library was loaded above, the second LoadLibrary does not execute above, the second LoadLibrary will not execute its startup code
it's startup code unless it is first unloaded. */ unless it is first unloaded. */
else if (h == d->handle) HMODULE h = LoadLibraryExW (d->name, NULL, DONT_RESOLVE_DLL_REFERENCES);
{
if (unload) if (!h)
{ system_printf ("can't reload %W, %E", d->name);
FreeLibrary (h);
LoadLibraryW (d->name);
}
}
else if (try2)
api_fatal ("unable to remap %W to same address as parent(%p) != %p",
d->name, d->handle, h);
else 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); FreeLibrary (h);
/* Block all of the memory up to the new load address. */ if (h == d->handle)
reserve_upto (d->name, (DWORD) d->handle); h = LoadLibraryW (d->name);
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;
} }
/* If we reached here on the second iteration of the for loop
then there is a lot of memory to release. */
if (i > 0)
release_upto (d->name, (DWORD) d->handle);
if (h == d->handle)
break; /* Success */
if (i > 0)
/* We tried once to relocate the dll and it failed. */
api_fatal ("unable to remap %W to same address as parent: %p != %p",
d->name, d->handle, h);
/* Dll 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. So, block all
of the memory up to the new load address and try again. */
reserve_upto (d->name, (DWORD) d->handle);
} }
next = d->next; /* Get the address of the next DLL. */
}
in_forkee = false; in_forkee = false;
} }

View File

@ -51,8 +51,7 @@ struct dll
HMODULE handle; HMODULE handle;
int count; int count;
dll_type type; dll_type type;
int namelen; WCHAR name[1];
WCHAR name[ANYSIZE_ARRAY];
void detach (); void detach ();
int init (); int init ();
}; };
@ -73,7 +72,7 @@ public:
dll *alloc (HINSTANCE, per_process *, dll_type); dll *alloc (HINSTANCE, per_process *, dll_type);
void detach (void *); void detach (void *);
void init (); void init ();
void load_after_fork (HANDLE, dll *); void load_after_fork (HANDLE);
dll *inext () dll *inext ()
{ {
while ((hold = hold->next)) while ((hold = hold->next))

View File

@ -34,7 +34,6 @@ details. */
class frok class frok
{ {
dll *first_dll;
bool load_dlls; bool load_dlls;
child_info_fork ch; child_info_fork ch;
const char *error; const char *error;
@ -174,8 +173,7 @@ frok::child (volatile char * volatile here)
myself->pid, myself->ppid, __builtin_frame_address (0)); myself->pid, myself->ppid, __builtin_frame_address (0));
sync_with_parent ("after longjmp", true); sync_with_parent ("after longjmp", true);
sigproc_printf ("hParent %p, child 1 first_dll %p, load_dlls %d", hParent, sigproc_printf ("hParent %p, load_dlls %d", hParent, load_dlls);
first_dll, load_dlls);
/* If we've played with the stack, stacksize != 0. That means that /* If we've played with the stack, stacksize != 0. That means that
fork() was invoked from other than the main thread. Make sure that fork() was invoked from other than the main thread. Make sure that
@ -230,7 +228,7 @@ frok::child (volatile char * volatile here)
} }
else else
{ {
dlls.load_after_fork (hParent, first_dll); dlls.load_after_fork (hParent);
cygheap->fdtab.fixup_after_fork (hParent); cygheap->fdtab.fixup_after_fork (hParent);
sync_with_parent ("loaded dlls", true); sync_with_parent ("loaded dlls", true);
} }
@ -304,14 +302,13 @@ frok::parent (volatile char * volatile stack_here)
else else
c_flags |= DETACHED_PROCESS; c_flags |= DETACHED_PROCESS;
/* Remember the address of the first loaded dll and decide /* Remember if we need to load dynamically linked dlls.
if we need to load dlls. We do this here so that this We do this here so that this information will be available
information will be available in the parent and, when in the parent and, when the stack is copied, in the child. */
the stack is copied, in the child. */
first_dll = dlls.start.next;
load_dlls = dlls.reload_on_fork && dlls.loaded_dlls; load_dlls = dlls.reload_on_fork && dlls.loaded_dlls;
/* This will help some of the confusion. */ /* This will help some of the confusion. */
/* FIXME: Is this really appropriate? What if stdout is closed? */
fflush (stdout); fflush (stdout);
forker_finished = CreateEvent (&sec_all, FALSE, FALSE, NULL); forker_finished = CreateEvent (&sec_all, FALSE, FALSE, NULL);
@ -570,7 +567,6 @@ fork ()
frok grouped; frok grouped;
debug_printf ("entering"); debug_printf ("entering");
grouped.first_dll = NULL;
grouped.load_dlls = 0; grouped.load_dlls = 0;
int res; int res;

View File

@ -361,13 +361,13 @@ shared_info::initialize ()
SHARED_INFO_CB, cb); SHARED_INFO_CB, cb);
} }
void __stdcall void
memory_init () memory_init (bool init_cygheap)
{ {
getpagesize (); getpagesize ();
/* Initialize the Cygwin heap, if necessary */ /* Initialize the Cygwin heap, if necessary */
if (!cygheap) if (init_cygheap)
{ {
cygheap_init (); cygheap_init ();
cygheap->user.init (); cygheap->user.init ();

View File

@ -79,7 +79,7 @@ enum shared_locations
}; };
void __stdcall memory_init (); void memory_init (bool) __attribute__ ((regparm(1)));
void __stdcall shared_destroy (); void __stdcall shared_destroy ();
#define shared_align_past(p) \ #define shared_align_past(p) \