diff options
author | Christopher Faylor <me@cgf.cx> | 2009-06-08 03:53:40 +0000 |
---|---|---|
committer | Christopher Faylor <me@cgf.cx> | 2009-06-08 03:53:40 +0000 |
commit | 71f53a2f6254e4f47891cd58ab562220547d01a2 (patch) | |
tree | 871e046c427914f0adc28b6d42e02a8caebc3243 /winsup/cygwin/dll_init.cc | |
parent | 1866410bb88b2d2733185e2a9aaaf48bd5134dd1 (diff) | |
download | cygnal-71f53a2f6254e4f47891cd58ab562220547d01a2.tar.gz cygnal-71f53a2f6254e4f47891cd58ab562220547d01a2.tar.bz2 cygnal-71f53a2f6254e4f47891cd58ab562220547d01a2.zip |
* 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.
Diffstat (limited to 'winsup/cygwin/dll_init.cc')
-rw-r--r-- | winsup/cygwin/dll_init.cc | 197 |
1 files changed, 41 insertions, 156 deletions
diff --git a/winsup/cygwin/dll_init.cc b/winsup/cygwin/dll_init.cc index 5b026c772..6743d2601 100644 --- a/winsup/cygwin/dll_init.cc +++ b/winsup/cygwin/dll_init.cc @@ -20,14 +20,10 @@ details. */ #include "pinfo.h" #include "cygtls.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 *); -dll_list NO_COPY dlls; +dll_list dlls; static bool dll_global_dtors_recorded; @@ -107,7 +103,7 @@ dll_list::operator[] (const PWCHAR name) #define RETRIES 1000 -/* Allocate space for a dll struct contiguous with the just-loaded dll. */ +/* Allocate space for a dll struct. */ dll * 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]; 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. */ } - void *s = p->bss_end; - 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 (§_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; - } + d = (dll *) cmalloc (HEAP_2_DLL, sizeof (*d) + (namelen * sizeof (*name))); /* Now we've allocated a block of information. Fill it in with the supplied info about this DLL. */ d->count = 1; - d->namelen = namelen; wcscpy (d->name, name); d->handle = h; d->p = p; @@ -251,7 +167,7 @@ dll_list::detach (void *retaddr) loaded_dlls--; if (end == d) end = d->prev; - NtUnmapViewOfSection (GetCurrentProcess (), d); + cfree (d); break; } } @@ -321,82 +237,51 @@ release_upto (const PWCHAR name, DWORD here) } /* 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. */ + 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) +dll_list::load_after_fork (HANDLE parent) { - int try2 = 0; - dll *d = (dll *) alloca (sizeof (dll) + (NT_MAX_PATH - 1) * sizeof (WCHAR)); - - 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) + for (dll *d = &dlls.start; (d = d->next) != NULL; ) + if (d->type == DLL_LOAD) + for (int i = 0; i < 2; i++) { - 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 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); - LoadLibraryW (d->name); - } - } - else if (try2) - api_fatal ("unable to remap %W to same address as parent(%p) != %p", - d->name, d->handle, h); + It stinks that we can't invert the order of the initial LoadLibrary + and FreeLibrar since Microsoft documentation seems to imply that + should do what we want. However, once a library is loaded as + above, the second LoadLibrary will not execute its startup code + unless it is first unloaded. */ + HMODULE h = LoadLibraryExW (d->name, NULL, DONT_RESOLVE_DLL_REFERENCES); + + if (!h) + system_printf ("can't reload %W, %E", d->name); 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; + if (h == d->handle) + h = LoadLibraryW (d->name); } + /* 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; } |