* heap.cc (heap_init): Rewrite initial heap allocation to use addresses

beyond 0x20000000.  Explain why and how.
	* shared.cc (shared_info::heap_slop_size): Remove.
	* shared_info.h (class shared_info): Remove heap_slop_inited and
	heap_slop members.  Remove heap_slop_size declaration.
	(CURR_SHARED_MAGIC): Update.
	* wincap.cc: Throughout, drop heapslop.
	* wincap.h (struct wincaps): Drop heapslop.
This commit is contained in:
Corinna Vinschen
2011-05-16 10:27:14 +00:00
parent 6d6cfa4840
commit 883ea27df0
6 changed files with 82 additions and 60 deletions

View File

@@ -17,6 +17,7 @@ details. */
#include "dtable.h"
#include "cygheap.h"
#include "child_info.h"
#include <sys/param.h>
#define assert(x)
@@ -30,41 +31,85 @@ heap_init ()
{
const DWORD alloctype = MEM_RESERVE;
/* If we're the forkee, we must allocate the heap at exactly the same place
as our parent. If not, we don't care where it ends up. */
as our parent. If not, we (almost) don't care where it ends up. */
page_const = wincap.page_size ();
if (!cygheap->user_heap.base)
{
/* Starting with Vista, Windows performs heap ASLR. This spoils
the entire region below 0x20000000 for us, because that region
is used by Windows to randomize heap and stack addresses.
Therefore we put our heap into a safe region starting at 0x20000000.
This should work right from the start in 99% of the cases. But,
there's always a but. Read on... */
uintptr_t start_address = 0x20000000L;
uintptr_t largest_found = 0;
size_t largest_found_size = 0;
SIZE_T ret;
MEMORY_BASIC_INFORMATION mbi;
cygheap->user_heap.chunk = cygwin_shared->heap_chunk_size ();
/* For some obscure reason Vista and 2003 sometimes reserve space after
calls to CreateProcess overlapping the spot where the heap has been
allocated. This apparently spoils fork. The behaviour looks quite
arbitrary. Experiments on Vista show a memory size of 0x37e000 or
0x1fd000 overlapping the usual heap by at most 0x1ed000. So what
we do here is to allocate the heap with an extra slop of (by default)
0x400000 and set the appropriate pointers to the start of the heap
area + slop. A forking child then creates its heap at the new start
address and without the slop factor. Since this is not entirely
foolproof we add a registry setting "heap_slop_in_mb" so the slop
factor can be influenced by the user if the need arises. */
cygheap->user_heap.slop = cygwin_shared->heap_slop_size ();
while (cygheap->user_heap.chunk >= MINHEAP_SIZE)
do
{
/* Initialize page mask and default heap size. Preallocate a heap
* to assure contiguous memory. */
cygheap->user_heap.base =
VirtualAlloc (NULL, cygheap->user_heap.chunk
+ cygheap->user_heap.slop,
alloctype, PAGE_NOACCESS);
cygheap->user_heap.base = VirtualAlloc ((LPVOID) start_address,
cygheap->user_heap.chunk,
alloctype, PAGE_NOACCESS);
if (cygheap->user_heap.base)
break;
cygheap->user_heap.chunk -= 1 * 1024 * 1024;
/* Ok, so we are at the 1% which didn't work with 0x20000000 out
of the box. What we do now is to search for the next free
region which matches our desired heap size. While doing that,
we keep track of the largest region we found. */
start_address += wincap.allocation_granularity ();
while ((ret = VirtualQuery ((LPCVOID) start_address, &mbi,
sizeof mbi)) != 0)
{
if (mbi.State == MEM_FREE)
{
if (mbi.RegionSize >= cygheap->user_heap.chunk)
break;
if (mbi.RegionSize > largest_found_size)
{
largest_found = (uintptr_t) mbi.BaseAddress;
largest_found_size = mbi.RegionSize;
}
}
/* Since VirtualAlloc only reserves at allocation granularity
boundaries, we round up here, too. Otherwise we might end
up at a bogus page-aligned address. */
start_address = roundup2 (start_address + mbi.RegionSize,
wincap.allocation_granularity ());
}
if (!ret)
{
/* In theory this should not happen. But if it happens, we have
collected the information about the largest available region
in the above loop. So, next we squeeze the heap into that
region, unless it's smaller than the minimum size. */
if (largest_found_size >= MINHEAP_SIZE)
{
cygheap->user_heap.chunk = largest_found_size;
cygheap->user_heap.base =
VirtualAlloc ((LPVOID) start_address,
cygheap->user_heap.chunk,
alloctype, PAGE_NOACCESS);
}
/* Last resort (but actually we are probably broken anyway):
Use the minimal heap size and let the system decide. */
if (!cygheap->user_heap.base)
{
cygheap->user_heap.chunk = MINHEAP_SIZE;
cygheap->user_heap.base =
VirtualAlloc (NULL, cygheap->user_heap.chunk,
alloctype, PAGE_NOACCESS);
}
}
}
while (!cygheap->user_heap.base && ret);
if (cygheap->user_heap.base == NULL)
api_fatal ("unable to allocate heap, heap_chunk_size %p, slop %p, %E",
cygheap->user_heap.chunk, cygheap->user_heap.slop);
cygheap->user_heap.base = (void *) ((char *) cygheap->user_heap.base
+ cygheap->user_heap.slop);
api_fatal ("unable to allocate heap, heap_chunk_size %p, %E",
cygheap->user_heap.chunk);
cygheap->user_heap.ptr = cygheap->user_heap.top = cygheap->user_heap.base;
cygheap->user_heap.max = (char *) cygheap->user_heap.base
+ cygheap->user_heap.chunk;