From cf2e7e9cbcda91783b81de4ecec07116e8c2e641 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Sun, 7 Jun 2009 07:26:07 +0000 Subject: [PATCH] * dll_init.cc (dll_list::alloc): Allocate memory using a section object. Explain why. Drop call to GetSystemInfo, rather call getpagesize to get allocation granularity. Only align to allocation granularity under WOW64. Use roundup2 to align. (dll_list::detach): Call NtUnmapViewOfSection instead of VirtualFree. --- winsup/cygwin/ChangeLog | 8 ++++ winsup/cygwin/dll_init.cc | 87 ++++++++++++++++++++++++++++----------- 2 files changed, 71 insertions(+), 24 deletions(-) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index e11466cbc..07389daf8 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,11 @@ +2009-06-06 Corinna Vinschen + + * dll_init.cc (dll_list::alloc): Allocate memory using a section + object. Explain why. Drop call to GetSystemInfo, rather call + getpagesize to get allocation granularity. Only align to allocation + granularity under WOW64. Use roundup2 to align. + (dll_list::detach): Call NtUnmapViewOfSection instead of VirtualFree. + 2009-06-06 Corinna Vinschen * mmap.cc: Use NtUnmapViewOfSection instead of UnmapViewOfFile diff --git a/winsup/cygwin/dll_init.cc b/winsup/cygwin/dll_init.cc index e42718de6..5b026c772 100644 --- a/winsup/cygwin/dll_init.cc +++ b/winsup/cygwin/dll_init.cc @@ -21,6 +21,9 @@ details. */ #include "cygtls.h" #include #include +#include +#include +#include "ntdll.h" extern void __stdcall check_sanity_and_sync (per_process *); @@ -110,7 +113,6 @@ dll_list::alloc (HINSTANCE h, per_process *p, dll_type type) { WCHAR name[NT_MAX_PATH]; DWORD namelen = GetModuleFileNameW (h, name, sizeof (name)); - size_t d_size = sizeof (dll) + namelen * sizeof (WCHAR); /* Already loaded? */ dll *d = dlls[name]; @@ -120,15 +122,18 @@ dll_list::alloc (HINSTANCE h, per_process *p, dll_type type) return d; /* Return previously allocated pointer. */ } - SYSTEM_INFO s1; - GetSystemInfo (&s1); - - int i; void *s = p->bss_end; - DWORD n; + 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 (i = 0; i <= RETRIES; i++, s = (char *) m.BaseAddress + m.RegionSize) + 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. */ @@ -138,30 +143,64 @@ dll_list::alloc (HINSTANCE h, per_process *p, dll_type type) 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; + 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. - if (r) - n = ((n - r) + s1.dwAllocationGranularity); + 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. - /* First reserve the area of memory, then commit it. */ - if (VirtualAlloc ((void *) n, d_size, MEM_RESERVE, PAGE_READWRITE)) - d = (dll *) VirtualAlloc ((void *) n, d_size, MEM_COMMIT, - PAGE_READWRITE); - if (d) + 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 (d == NULL) + if (!NT_SUCCESS (status)) { /* Nope. */ -#ifdef DEBUGGING - system_printf ("VirtualAlloc failed, %E"); -#endif - __seterrno (); + __seterrno_from_nt_status (status); return NULL; } @@ -212,7 +251,7 @@ dll_list::detach (void *retaddr) loaded_dlls--; if (end == d) end = d->prev; - VirtualFree (d, 0, MEM_RELEASE); + NtUnmapViewOfSection (GetCurrentProcess (), d); break; } }