# HG changeset patch # Parent 0505fe166dc67837469fabb459671f7040ca0656 diff --git a/dcrt0.cc b/dcrt0.cc --- a/dcrt0.cc +++ b/dcrt0.cc @@ -792,6 +792,13 @@ main_vfork = vfork_storage.create (); #endif + /* Windows doesn't use the lower 4MB of address space consistently, + and those uses arise before cygwin1.dll loads. If a dll loads + there we risk being unable to fork later. To avoid the problem, + we just reserve everything that's left in that space -- windows + can still do what it wants since it got there first. */ + dlls.block_bad_address_space (); + cygbench ("pre-forkee"); if (in_forkee) { diff --git a/dll_init.cc b/dll_init.cc --- a/dll_init.cc +++ b/dll_init.cc @@ -340,6 +340,44 @@ #define A64K (64 * 1024) +void +dll_list::block_bad_address_space () +{ + /* For some reason VirtualQuery doesn't return consistent values of + RegionSize for free space, so we have to compute it manually by + looking for MEM_FREE followed by a not-free region. We ensure not + to leave a danging free region by allowing the loop to examine + 0x00400000, which is always the address of the application's + executable image. + */ + MEMORY_BASIC_INFORMATION mb; + DWORD here; + for (DWORD i=A64K; i <= 64*A64K; i += mb.RegionSize) + { + if ( !VirtualQuery ((void*)i, &mb, sizeof(mb))) + api_fatal ("-> unable to examine address space at %08lx, %E", i); + here = (DWORD) mb.BaseAddress; + + /* this should never happen. If it does we'll need to write some + code to compensate for it */ + if (here != i) + api_fatal ("VirtualQuery returned info for %lx instead of %lx", + here, i); + if (mb.State == MEM_FREE) + { + DWORD size = mb.RegionSize, end = here + size; + if (!VirtualAlloc ((void*) here, size, MEM_RESERVE, PAGE_NOACCESS)) + system_printf ("-> couldn't block out %08lx, %E", here); + } + else if (mb.RegionSize & (A64K-1)) + { + /* skip free space at the end of mapped slices -- they can't + be used by anything else */ + mb.RegionSize = (mb.RegionSize + A64K - 1) & -A64K; + } + } +} + /* Reserve the chunk of free address space starting _here_ and (usually) covering at least _dll_size_ bytes. However, we must take care not to clobber the dll's target address range because it often overlaps. diff --git a/dll_init.h b/dll_init.h --- a/dll_init.h +++ b/dll_init.h @@ -82,6 +82,7 @@ int tot; int loaded_dlls; int reload_on_fork; + void block_bad_address_space (); dll *operator [] (const PWCHAR name); dll *alloc (HINSTANCE, per_process *, dll_type); dll *find (void *);