OSDN Git Service

* exceptions.cc (set_signal_mask): Remove useless debugging output.
[pf3gnuchains/pf3gnuchains4x.git] / winsup / cygwin / heap.cc
1 /* heap.cc: Cygwin heap manager.
2
3    Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
4    2006, 2007, 2008, 2009, 2010, 2011 Red Hat, Inc.
5
6 This file is part of Cygwin.
7
8 This software is a copyrighted work licensed under the terms of the
9 Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
10 details. */
11
12 #include "winsup.h"
13 #include "cygerrno.h"
14 #include "shared_info.h"
15 #include "path.h"
16 #include "fhandler.h"
17 #include "dtable.h"
18 #include "cygheap.h"
19 #include "child_info.h"
20 #include "ntdll.h"
21 #include <sys/param.h>
22
23 #define assert(x)
24
25 static unsigned page_const;
26
27 #define MINHEAP_SIZE (4 * 1024 * 1024)
28
29 static uintptr_t
30 eval_start_address ()
31 {
32   /* Starting with Vista, Windows performs heap ASLR.  This spoils the entire
33      region below 0x20000000 for us, because that region is used by Windows
34      to randomize heap and stack addresses.  Therefore we put our heap into a
35      safe region starting at 0x20000000.  This should work right from the start
36      in 99% of the cases. */
37   uintptr_t start_address = 0x20000000L;
38   if ((uintptr_t) NtCurrentTeb () >= 0xbf000000L)
39     {
40       /* However, if we're running on a /3GB enabled 32 bit system or on
41          a 64 bit system, and the executable is large address aware, then
42          we know that we have spare 1 Gig (32 bit) or even 2 Gigs (64 bit)
43          virtual address space.  This memory region is practically unused
44          by Windows, only PEB and TEBs are allocated top-down here.  We use
45          the current TEB address as very simple test that this is a large
46          address aware executable.
47          The above test for an address beyond 0xbf000000 is supposed to
48          make sure that we really have 3GB on a 32 bit system.  XP and
49          later support smaller large address regions, but then it's not
50          that interesting for us to use it for the heap.
51          If the region is big enough, the heap gets allocated at its
52          start.  What we get are 0.999 or 1.999 Gigs of free contiguous
53          memory for heap, thread stacks, and shared memory regions. */
54       start_address = 0x80000000L;
55     }
56   return start_address;
57 }
58
59 static unsigned
60 eval_initial_heap_size ()
61 {
62   PIMAGE_DOS_HEADER dosheader;
63   PIMAGE_NT_HEADERS32 ntheader;
64   unsigned size;
65
66   dosheader = (PIMAGE_DOS_HEADER) GetModuleHandle (NULL);
67   ntheader = (PIMAGE_NT_HEADERS32) ((PBYTE) dosheader + dosheader->e_lfanew);
68   /* LoaderFlags is an obsolete DWORD member of the PE/COFF file header.
69      It's value is ignored by the loader, so we're free to use it for
70      Cygwin.  If it's 0, we default to the usual 384 Megs.  Otherwise,
71      we use it as the default initial heap size in megabyte.  Valid values
72      are between 4 and 2048 Megs. */
73   size = ntheader->OptionalHeader.LoaderFlags;
74   if (size == 0)
75     size = 384;
76   else if (size < 4)
77     size = 4;
78   else if (size > 2048)
79     size = 2048;
80   return size << 20;
81 }
82
83 /* Initialize the heap at process start up.  */
84 void
85 heap_init ()
86 {
87   const DWORD alloctype = MEM_RESERVE;
88   /* If we're the forkee, we must allocate the heap at exactly the same place
89      as our parent.  If not, we (almost) don't care where it ends up.  */
90
91   page_const = wincap.page_size ();
92   if (!cygheap->user_heap.base)
93     {
94       uintptr_t start_address = eval_start_address ();
95       PVOID largest_found = NULL;
96       size_t largest_found_size = 0;
97       SIZE_T ret;
98       MEMORY_BASIC_INFORMATION mbi;
99
100       cygheap->user_heap.chunk = eval_initial_heap_size ();
101       do
102         {
103           cygheap->user_heap.base = VirtualAlloc ((LPVOID) start_address,
104                                                   cygheap->user_heap.chunk,
105                                                   alloctype, PAGE_NOACCESS);
106           if (cygheap->user_heap.base)
107             break;
108
109           /* Ok, so we are at the 1% which didn't work with 0x20000000 out
110              of the box.  What we do now is to search for the next free
111              region which matches our desired heap size.  While doing that,
112              we keep track of the largest region we found, including the
113              region starting at 0x20000000. */
114           while ((ret = VirtualQuery ((LPCVOID) start_address, &mbi,
115                                       sizeof mbi)) != 0)
116             {
117               if (mbi.State == MEM_FREE)
118                 {
119                   if (mbi.RegionSize >= cygheap->user_heap.chunk)
120                     break;
121                   if (mbi.RegionSize > largest_found_size)
122                     {
123                       largest_found = mbi.BaseAddress;
124                       largest_found_size = mbi.RegionSize;
125                     }
126                 }
127               /* Since VirtualAlloc only reserves at allocation granularity
128                  boundaries, we round up here, too.  Otherwise we might end
129                  up at a bogus page-aligned address. */
130               start_address = roundup2 (start_address + mbi.RegionSize,
131                                         wincap.allocation_granularity ());
132             }
133           if (!ret)
134             {
135               /* In theory this should not happen.  But if it happens, we have
136                  collected the information about the largest available region
137                  in the above loop.  So, next we squeeze the heap into that
138                  region, unless it's smaller than the minimum size. */
139               if (largest_found_size >= MINHEAP_SIZE)
140                 {
141                   cygheap->user_heap.chunk = largest_found_size;
142                   cygheap->user_heap.base =
143                         VirtualAlloc (largest_found, cygheap->user_heap.chunk,
144                                       alloctype, PAGE_NOACCESS);
145                 }
146               /* Last resort (but actually we are probably broken anyway):
147                  Use the minimal heap size and let the system decide. */
148               if (!cygheap->user_heap.base)
149                 {
150                   cygheap->user_heap.chunk = MINHEAP_SIZE;
151                   cygheap->user_heap.base =
152                         VirtualAlloc (NULL, cygheap->user_heap.chunk,
153                                       alloctype, PAGE_NOACCESS);
154                 }
155             }
156         }
157       while (!cygheap->user_heap.base && ret);
158       if (cygheap->user_heap.base == NULL)
159         api_fatal ("unable to allocate heap, heap_chunk_size %p, %E",
160                    cygheap->user_heap.chunk);
161       cygheap->user_heap.ptr = cygheap->user_heap.top = cygheap->user_heap.base;
162       cygheap->user_heap.max = (char *) cygheap->user_heap.base
163                                + cygheap->user_heap.chunk;
164     }
165   else
166     {
167       DWORD chunk = cygheap->user_heap.chunk;   /* allocation chunk */
168       /* total size commited in parent */
169       DWORD allocsize = (char *) cygheap->user_heap.top -
170                         (char *) cygheap->user_heap.base;
171
172       /* Loop until we've managed to reserve an adequate amount of memory. */
173       char *p;
174       DWORD reserve_size = chunk * ((allocsize + (chunk - 1)) / chunk);
175       while (1)
176         {
177           p = (char *) VirtualAlloc (cygheap->user_heap.base, reserve_size,
178                                      alloctype, PAGE_READWRITE);
179           if (p)
180             break;
181           if ((reserve_size -= page_const) < allocsize)
182             break;
183         }
184       if (!p && in_forkee && !fork_info->abort (NULL))
185         api_fatal ("couldn't allocate heap, %E, base %p, top %p, "
186                    "reserve_size %d, allocsize %d, page_const %d",
187                    cygheap->user_heap.base, cygheap->user_heap.top,
188                    reserve_size, allocsize, page_const);
189       if (p != cygheap->user_heap.base)
190         api_fatal ("heap allocated at wrong address %p (mapped) != %p (expected)", p, cygheap->user_heap.base);
191       if (allocsize && !VirtualAlloc (cygheap->user_heap.base, allocsize, MEM_COMMIT, PAGE_READWRITE))
192         api_fatal ("MEM_COMMIT failed, %E");
193     }
194
195   debug_printf ("heap base %p, heap top %p", cygheap->user_heap.base,
196                 cygheap->user_heap.top);
197   page_const--;
198   // malloc_init ();
199 }
200
201 #define pround(n) (((size_t)(n) + page_const) & ~page_const)
202
203 /* FIXME: This function no longer handles "split heaps". */
204
205 extern "C" void *
206 sbrk (int n)
207 {
208   char *newtop, *newbrk;
209   unsigned commitbytes, newbrksize;
210
211   if (n == 0)
212     return cygheap->user_heap.ptr;              /* Just wanted to find current cygheap->user_heap.ptr address */
213
214   newbrk = (char *) cygheap->user_heap.ptr + n; /* Where new cygheap->user_heap.ptr will be */
215   newtop = (char *) pround (newbrk);            /* Actual top of allocated memory -
216                                                    on page boundary */
217
218   if (newtop == cygheap->user_heap.top)
219     goto good;
220
221   if (n < 0)
222     {                                           /* Freeing memory */
223       assert (newtop < cygheap->user_heap.top);
224       n = (char *) cygheap->user_heap.top - newtop;
225       if (VirtualFree (newtop, n, MEM_DECOMMIT)) /* Give it back to OS */
226         goto good;                              /*  Didn't take */
227       else
228         goto err;
229     }
230
231   assert (newtop > cygheap->user_heap.top);
232
233   /* Find the number of bytes to commit, rounded up to the nearest page. */
234   commitbytes = pround (newtop - (char *) cygheap->user_heap.top);
235
236   /* Need to grab more pages from the OS.  If this fails it may be because
237      we have used up previously reserved memory.  Or, we're just plumb out
238      of memory.  Only attempt to commit memory that we know we've previously
239      reserved.  */
240   if (newtop <= cygheap->user_heap.max)
241     {
242       if (VirtualAlloc (cygheap->user_heap.top, commitbytes, MEM_COMMIT, PAGE_READWRITE) != NULL)
243         goto good;
244     }
245
246   /* Couldn't allocate memory.  Maybe we can reserve some more.
247      Reserve either the maximum of the standard cygwin_shared->heap_chunk_size ()
248      or the requested amount.  Then attempt to actually allocate it.  */
249   if ((newbrksize = cygheap->user_heap.chunk) < commitbytes)
250     newbrksize = commitbytes;
251
252    if ((VirtualAlloc (cygheap->user_heap.top, newbrksize, MEM_RESERVE, PAGE_NOACCESS)
253         || VirtualAlloc (cygheap->user_heap.top, newbrksize = commitbytes, MEM_RESERVE, PAGE_NOACCESS))
254        && VirtualAlloc (cygheap->user_heap.top, commitbytes, MEM_COMMIT, PAGE_READWRITE) != NULL)
255      {
256         cygheap->user_heap.max = (char *) cygheap->user_heap.max + pround (newbrksize);
257         goto good;
258      }
259
260 err:
261   set_errno (ENOMEM);
262   return (void *) -1;
263
264 good:
265   void *oldbrk = cygheap->user_heap.ptr;
266   cygheap->user_heap.ptr = newbrk;
267   cygheap->user_heap.top = newtop;
268   return oldbrk;
269 }