Include limits.h and errno.h.
[bpt/emacs.git] / src / w32heap.c
CommitLineData
e9e23e23 1/* Heap management routines for GNU Emacs on the Microsoft W32 API.
95ed0025
RS
2 Copyright (C) 1994 Free Software Foundation, Inc.
3
3b7ad313 4This file is part of GNU Emacs.
95ed0025 5
3b7ad313
EN
6GNU Emacs is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
95ed0025 10
3b7ad313
EN
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
95ed0025 15
3b7ad313
EN
16You should have received a copy of the GNU General Public License
17along with GNU Emacs; see the file COPYING. If not, write to
18the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA.
95ed0025
RS
20
21 Geoff Voelker (voelker@cs.washington.edu) 7-29-94
22*/
23
3bbabc43
GV
24#include "config.h"
25
95ed0025
RS
26#include <stdlib.h>
27#include <stdio.h>
28
489f9371 29#include "w32heap.h"
8dfdd41f 30#include "lisp.h" /* for VALMASK */
95ed0025
RS
31
32/* This gives us the page size and the size of the allocation unit on NT. */
33SYSTEM_INFO sysinfo_cache;
3bbabc43 34unsigned long syspage_mask = 0;
95ed0025
RS
35
36/* These are defined to get Emacs to compile, but are not used. */
37int edata;
38int etext;
39
40/* The major and minor versions of NT. */
fbd6baed
GV
41int w32_major_version;
42int w32_minor_version;
95ed0025
RS
43
44/* Cache information describing the NT system for later use. */
45void
46cache_system_info (void)
47{
48 union
49 {
50 struct info
51 {
52 char major;
53 char minor;
54 short platform;
55 } info;
56 DWORD data;
57 } version;
58
59 /* Cache the version of the operating system. */
60 version.data = GetVersion ();
fbd6baed
GV
61 w32_major_version = version.info.major;
62 w32_minor_version = version.info.minor;
95ed0025
RS
63
64 /* Cache page size, allocation unit, processor type, etc. */
65 GetSystemInfo (&sysinfo_cache);
3bbabc43 66 syspage_mask = sysinfo_cache.dwPageSize - 1;
95ed0025
RS
67}
68
e54c8cd1
GV
69/* Emulate getpagesize. */
70int
71getpagesize (void)
72{
73 return sysinfo_cache.dwPageSize;
74}
75
95ed0025
RS
76/* Round ADDRESS up to be aligned with ALIGN. */
77unsigned char *
78round_to_next (unsigned char *address, unsigned long align)
79{
80 unsigned long tmp;
81
82 tmp = (unsigned long) address;
83 tmp = (tmp + align - 1) / align;
84
85 return (unsigned char *) (tmp * align);
86}
87
e54c8cd1
GV
88/* Force zero initialized variables to be placed in the .data segment;
89 MSVC 5.0 otherwise places them in .bss, which breaks the dumping code. */
90#pragma data_seg(".data")
91
95ed0025
RS
92/* Info for keeping track of our heap. */
93unsigned char *data_region_base = NULL;
94unsigned char *data_region_end = NULL;
3bbabc43 95unsigned char *real_data_region_end = NULL;
95ed0025 96unsigned long data_region_size = 0;
011db670 97unsigned long reserved_heap_size = 0;
95ed0025
RS
98
99/* The start of the data segment. */
100unsigned char *
101get_data_start (void)
102{
103 return data_region_base;
104}
105
106/* The end of the data segment. */
107unsigned char *
108get_data_end (void)
109{
110 return data_region_end;
111}
112
011db670
GV
113static char *
114allocate_heap (void)
115{
8dfdd41f
GV
116 /* The base address for our GNU malloc heap is chosen in conjuction
117 with the link settings for temacs.exe which control the stack size,
118 the initial default process heap size and the executable image base
119 address. The link settings and the malloc heap base below must all
120 correspond; the relationship between these values depends on how NT
e9e23e23 121 and Windows 95 arrange the virtual address space for a process (and on
8dfdd41f
GV
122 the size of the code and data segments in temacs.exe).
123
124 The most important thing is to make base address for the executable
125 image high enough to leave enough room between it and the 4MB floor
e9e23e23 126 of the process address space on Windows 95 for the primary thread stack,
8dfdd41f
GV
127 the process default heap, and other assorted odds and ends
128 (eg. environment strings, private system dll memory etc) that are
129 allocated before temacs has a chance to grab its malloc arena. The
130 malloc heap base can then be set several MB higher than the
131 executable image base, leaving enough room for the code and data
132 segments.
133
134 Because some parts of Emacs can use rather a lot of stack space
135 (for instance, the regular expression routines can potentially
136 allocate several MB of stack space) we allow 8MB for the stack.
137
138 Allowing 1MB for the default process heap, and 1MB for odds and
139 ends, we can base the executable at 16MB and still have a generous
140 safety margin. At the moment, the executable has about 810KB of
141 code (for x86) and about 550KB of data - on RISC platforms the code
142 size could be roughly double, so if we allow 4MB for the executable
143 we will have plenty of room for expansion.
144
709fd16b 145 Thus we would like to set the malloc heap base to 20MB. However,
e9e23e23 146 Windows 95 refuses to allocate the heap starting at this address, so we
709fd16b 147 set the base to 27MB to make it happy. Since Emacs now leaves
8dfdd41f 148 28 bits available for pointers, this lets us use the remainder of
709fd16b
GV
149 the region below the 256MB line for our malloc arena - 229MB is
150 still a pretty decent arena to play in! */
8dfdd41f 151
709fd16b 152 unsigned long base = 0x01B00000; /* 27MB */
8dfdd41f 153 unsigned long end = 1 << VALBITS; /* 256MB */
709fd16b 154 void *ptr = NULL;
011db670 155
0d05360d
RS
156#if NTHEAP_PROBE_BASE /* This is never normally defined */
157 /* Try various addresses looking for one the kernel will let us have. */
709fd16b
GV
158 while (!ptr && (base < end))
159 {
709fd16b
GV
160 reserved_heap_size = end - base;
161 ptr = VirtualAlloc ((void *) base,
162 get_reserved_heap_size (),
163 MEM_RESERVE,
164 PAGE_NOACCESS);
709fd16b
GV
165 base += 0x00100000; /* 1MB increment */
166 }
0d05360d
RS
167#else
168 reserved_heap_size = end - base;
169 ptr = VirtualAlloc ((void *) base,
170 get_reserved_heap_size (),
171 MEM_RESERVE,
172 PAGE_NOACCESS);
709fd16b 173#endif
0d05360d 174
709fd16b 175 return ptr;
011db670 176}
011db670
GV
177
178
95ed0025
RS
179/* Emulate Unix sbrk. */
180void *
181sbrk (unsigned long increment)
182{
183 void *result;
184 long size = (long) increment;
185
186 /* Allocate our heap if we haven't done so already. */
187 if (!data_region_base)
188 {
011db670 189 data_region_base = allocate_heap ();
95ed0025
RS
190 if (!data_region_base)
191 return NULL;
192
8dfdd41f
GV
193 /* Ensure that the addresses don't use the upper tag bits since
194 the Lisp type goes there. */
195 if (((unsigned long) data_region_base & ~VALMASK) != 0)
95ed0025
RS
196 {
197 printf ("Error: The heap was allocated in upper memory.\n");
198 exit (1);
199 }
200
201 data_region_end = data_region_base;
3bbabc43 202 real_data_region_end = data_region_end;
95ed0025
RS
203 data_region_size = get_reserved_heap_size ();
204 }
205
206 result = data_region_end;
207
208 /* If size is negative, shrink the heap by decommitting pages. */
209 if (size < 0)
210 {
3bbabc43
GV
211 int new_size;
212 unsigned char *new_data_region_end;
213
95ed0025
RS
214 size = -size;
215
216 /* Sanity checks. */
95ed0025
RS
217 if ((data_region_end - size) < data_region_base)
218 return NULL;
219
3bbabc43
GV
220 /* We can only decommit full pages, so allow for
221 partial deallocation [cga]. */
222 new_data_region_end = (data_region_end - size);
223 new_data_region_end = (unsigned char *)
224 ((long) (new_data_region_end + syspage_mask) & ~syspage_mask);
225 new_size = real_data_region_end - new_data_region_end;
226 real_data_region_end = new_data_region_end;
227 if (new_size > 0)
228 {
229 /* Decommit size bytes from the end of the heap. */
230 if (!VirtualFree (real_data_region_end, new_size, MEM_DECOMMIT))
231 return NULL;
232 }
95ed0025
RS
233
234 data_region_end -= size;
235 }
236 /* If size is positive, grow the heap by committing reserved pages. */
237 else if (size > 0)
238 {
239 /* Sanity checks. */
95ed0025
RS
240 if ((data_region_end + size) >
241 (data_region_base + get_reserved_heap_size ()))
242 return NULL;
243
244 /* Commit more of our heap. */
245 if (VirtualAlloc (data_region_end, size, MEM_COMMIT,
246 PAGE_READWRITE) == NULL)
247 return NULL;
248 data_region_end += size;
3bbabc43
GV
249
250 /* We really only commit full pages, so record where
251 the real end of committed memory is [cga]. */
252 real_data_region_end = (unsigned char *)
253 ((long) (data_region_end + syspage_mask) & ~syspage_mask);
95ed0025
RS
254 }
255
256 return result;
257}
258
259/* Recreate the heap from the data that was dumped to the executable.
260 EXECUTABLE_PATH tells us where to find the executable. */
261void
262recreate_heap (char *executable_path)
263{
264 unsigned char *tmp;
265
266 /* First reserve the upper part of our heap. (We reserve first
267 because there have been problems in the past where doing the
268 mapping first has loaded DLLs into the VA space of our heap.) */
269 tmp = VirtualAlloc ((void *) get_heap_end (),
270 get_reserved_heap_size () - get_committed_heap_size (),
271 MEM_RESERVE,
272 PAGE_NOACCESS);
273 if (!tmp)
274 exit (1);
275
276 /* We read in the data for the .bss section from the executable
277 first and map in the heap from the executable second to prevent
278 any funny interactions between file I/O and file mapping. */
279 read_in_bss (executable_path);
280 map_in_heap (executable_path);
281}
282
283/* Round the heap up to the given alignment. */
284void
285round_heap (unsigned long align)
286{
287 unsigned long needs_to_be;
288 unsigned long need_to_alloc;
289
290 needs_to_be = (unsigned long) round_to_next (get_heap_end (), align);
291 need_to_alloc = needs_to_be - (unsigned long) get_heap_end ();
292
293 if (need_to_alloc)
294 sbrk (need_to_alloc);
295}