Don't use the abbreviation "win" to refer to Windows (Bug#10421).
[bpt/emacs.git] / src / w32heap.c
CommitLineData
b46a6a83 1/* Heap management routines for GNU Emacs on the Microsoft Windows API.
acaf905b 2 Copyright (C) 1994, 2001-2012 Free Software Foundation, Inc.
95ed0025 3
3b7ad313 4This file is part of GNU Emacs.
95ed0025 5
9ec0b715 6GNU Emacs is free software: you can redistribute it and/or modify
3b7ad313 7it under the terms of the GNU General Public License as published by
9ec0b715
GM
8the Free Software Foundation, either version 3 of the License, or
9(at your option) any 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 16You should have received a copy of the GNU General Public License
9ec0b715 17along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
95ed0025 18
9ec0b715 19/*
95ed0025
RS
20 Geoff Voelker (voelker@cs.washington.edu) 7-29-94
21*/
22
4838e624 23#include <config.h>
95ed0025 24#include <stdio.h>
d7306fe6 25#include <setjmp.h>
95ed0025 26
489f9371 27#include "w32heap.h"
8dfdd41f 28#include "lisp.h" /* for VALMASK */
95ed0025 29
2e4f6477 30#define RVA_TO_PTR(rva) ((unsigned char *)((DWORD)(rva) + (DWORD)GetModuleHandle (NULL)))
30d2b1c2 31
95ed0025
RS
32/* This gives us the page size and the size of the allocation unit on NT. */
33SYSTEM_INFO sysinfo_cache;
b9cad9c1
GV
34
35/* This gives us version, build, and platform identification. */
36OSVERSIONINFO osinfo_cache;
37
3bbabc43 38unsigned long syspage_mask = 0;
95ed0025 39
95ed0025 40/* The major and minor versions of NT. */
fbd6baed
GV
41int w32_major_version;
42int w32_minor_version;
39f1f652 43int w32_build_number;
95ed0025 44
801f68b9
GV
45/* Distinguish between Windows NT and Windows 95. */
46int os_subtype;
47
95ed0025
RS
48/* Cache information describing the NT system for later use. */
49void
50cache_system_info (void)
51{
ce20e03e 52 union
95ed0025 53 {
ce20e03e 54 struct info
95ed0025
RS
55 {
56 char major;
57 char minor;
58 short platform;
59 } info;
60 DWORD data;
61 } version;
62
63 /* Cache the version of the operating system. */
64 version.data = GetVersion ();
fbd6baed
GV
65 w32_major_version = version.info.major;
66 w32_minor_version = version.info.minor;
95ed0025 67
801f68b9 68 if (version.info.platform & 0x8000)
b46a6a83 69 os_subtype = OS_WINDOWS_95;
801f68b9
GV
70 else
71 os_subtype = OS_NT;
72
95ed0025
RS
73 /* Cache page size, allocation unit, processor type, etc. */
74 GetSystemInfo (&sysinfo_cache);
3bbabc43 75 syspage_mask = sysinfo_cache.dwPageSize - 1;
b9cad9c1
GV
76
77 /* Cache os info. */
78 osinfo_cache.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
79 GetVersionEx (&osinfo_cache);
39f1f652
AI
80
81 w32_build_number = osinfo_cache.dwBuildNumber;
b46a6a83 82 if (os_subtype == OS_WINDOWS_95)
39f1f652 83 w32_build_number &= 0xffff;
95ed0025
RS
84}
85
e54c8cd1
GV
86/* Emulate getpagesize. */
87int
88getpagesize (void)
89{
90 return sysinfo_cache.dwPageSize;
91}
92
30d2b1c2
AI
93/* Info for managing our preload heap, which is essentially a fixed size
94 data area in the executable. */
95PIMAGE_SECTION_HEADER preload_heap_section;
95ed0025
RS
96
97/* Info for keeping track of our heap. */
98unsigned char *data_region_base = NULL;
99unsigned char *data_region_end = NULL;
3bbabc43 100unsigned char *real_data_region_end = NULL;
011db670 101unsigned long reserved_heap_size = 0;
95ed0025
RS
102
103/* The start of the data segment. */
104unsigned char *
105get_data_start (void)
106{
107 return data_region_base;
108}
109
110/* The end of the data segment. */
111unsigned char *
112get_data_end (void)
113{
114 return data_region_end;
115}
116
646b5f55 117#if !USE_LSB_TAG
011db670
GV
118static char *
119allocate_heap (void)
120{
30d2b1c2
AI
121 /* Try to get as much as possible of the address range from the end of
122 the preload heap section up to the usable address limit. Since GNU
123 malloc can handle gaps in the memory it gets from sbrk, we can
124 simply set the sbrk pointer to the base of the new heap region. */
125 unsigned long base =
126 ROUND_UP ((RVA_TO_PTR (preload_heap_section->VirtualAddress)
127 + preload_heap_section->Misc.VirtualSize),
128 get_allocation_unit ());
8dfdd41f 129 unsigned long end = 1 << VALBITS; /* 256MB */
709fd16b 130 void *ptr = NULL;
011db670 131
709fd16b
GV
132 while (!ptr && (base < end))
133 {
709fd16b
GV
134 reserved_heap_size = end - base;
135 ptr = VirtualAlloc ((void *) base,
136 get_reserved_heap_size (),
137 MEM_RESERVE,
138 PAGE_NOACCESS);
709fd16b
GV
139 base += 0x00100000; /* 1MB increment */
140 }
0d05360d 141
709fd16b 142 return ptr;
011db670 143}
646b5f55 144#else /* USE_LSB_TAG */
fab624aa
EZ
145static char *
146allocate_heap (void)
147{
148 unsigned long size = 0x80000000; /* start by asking for 2GB */
149 void *ptr = NULL;
150
151 while (!ptr && size > 0x00100000)
152 {
153 reserved_heap_size = size;
154 ptr = VirtualAlloc (NULL,
155 get_reserved_heap_size (),
156 MEM_RESERVE,
157 PAGE_NOACCESS);
158 size -= 0x00800000; /* if failed, decrease request by 8MB */
159 }
160
161 return ptr;
162}
646b5f55 163#endif /* USE_LSB_TAG */
011db670
GV
164
165
fab624aa
EZ
166/* Emulate Unix sbrk. Note that ralloc.c expects the return value to
167 be the address of the _start_ (not end) of the new block in case of
168 success, and zero (not -1) in case of failure. */
95ed0025
RS
169void *
170sbrk (unsigned long increment)
171{
172 void *result;
173 long size = (long) increment;
ce20e03e 174
95ed0025 175 result = data_region_end;
ce20e03e 176
95ed0025 177 /* If size is negative, shrink the heap by decommitting pages. */
ce20e03e 178 if (size < 0)
95ed0025 179 {
3bbabc43
GV
180 int new_size;
181 unsigned char *new_data_region_end;
182
95ed0025
RS
183 size = -size;
184
185 /* Sanity checks. */
95ed0025
RS
186 if ((data_region_end - size) < data_region_base)
187 return NULL;
188
ce20e03e 189 /* We can only decommit full pages, so allow for
3bbabc43
GV
190 partial deallocation [cga]. */
191 new_data_region_end = (data_region_end - size);
192 new_data_region_end = (unsigned char *)
193 ((long) (new_data_region_end + syspage_mask) & ~syspage_mask);
194 new_size = real_data_region_end - new_data_region_end;
195 real_data_region_end = new_data_region_end;
30d2b1c2 196 if (new_size > 0)
3bbabc43
GV
197 {
198 /* Decommit size bytes from the end of the heap. */
30d2b1c2
AI
199 if (using_dynamic_heap
200 && !VirtualFree (real_data_region_end, new_size, MEM_DECOMMIT))
3bbabc43
GV
201 return NULL;
202 }
95ed0025
RS
203
204 data_region_end -= size;
ce20e03e 205 }
95ed0025 206 /* If size is positive, grow the heap by committing reserved pages. */
ce20e03e 207 else if (size > 0)
95ed0025
RS
208 {
209 /* Sanity checks. */
95ed0025
RS
210 if ((data_region_end + size) >
211 (data_region_base + get_reserved_heap_size ()))
212 return NULL;
213
214 /* Commit more of our heap. */
30d2b1c2
AI
215 if (using_dynamic_heap
216 && VirtualAlloc (data_region_end, size, MEM_COMMIT,
217 PAGE_READWRITE) == NULL)
95ed0025
RS
218 return NULL;
219 data_region_end += size;
3bbabc43
GV
220
221 /* We really only commit full pages, so record where
222 the real end of committed memory is [cga]. */
223 real_data_region_end = (unsigned char *)
224 ((long) (data_region_end + syspage_mask) & ~syspage_mask);
95ed0025 225 }
ce20e03e 226
95ed0025
RS
227 return result;
228}
229
30d2b1c2
AI
230/* Initialize the internal heap variables used by sbrk. When running in
231 preload phase (ie. in the undumped executable), we rely entirely on a
232 fixed size heap section included in the .exe itself; this is
233 preserved during dumping, and truncated to the size actually used.
234
235 When running in the dumped executable, we reserve as much as possible
236 of the address range that is addressable by Lisp object pointers, to
237 supplement what is left of the preload heap. Although we cannot rely
238 on the dynamically allocated arena being contiguous with the static
239 heap area, it is not a problem because sbrk can pretend that the gap
240 was allocated by something else; GNU malloc detects when there is a
241 jump in the sbrk values, and starts a new heap block. */
95ed0025 242void
b56ceb92 243init_heap (void)
95ed0025 244{
30d2b1c2
AI
245 PIMAGE_DOS_HEADER dos_header;
246 PIMAGE_NT_HEADERS nt_header;
247
248 dos_header = (PIMAGE_DOS_HEADER) RVA_TO_PTR (0);
ce20e03e 249 nt_header = (PIMAGE_NT_HEADERS) (((unsigned long) dos_header) +
30d2b1c2
AI
250 dos_header->e_lfanew);
251 preload_heap_section = find_section ("EMHEAP", nt_header);
252
253 if (using_dynamic_heap)
254 {
255 data_region_base = allocate_heap ();
256 if (!data_region_base)
257 {
258 printf ("Error: Could not reserve dynamic heap area.\n");
259 exit (1);
260 }
261
646b5f55 262#if !USE_LSB_TAG
30d2b1c2
AI
263 /* Ensure that the addresses don't use the upper tag bits since
264 the Lisp type goes there. */
ce20e03e 265 if (((unsigned long) data_region_base & ~VALMASK) != 0)
30d2b1c2
AI
266 {
267 printf ("Error: The heap was allocated in upper memory.\n");
268 exit (1);
269 }
3ae12c8d 270#endif
30d2b1c2
AI
271 data_region_end = data_region_base;
272 real_data_region_end = data_region_end;
273 }
274 else
275 {
276 data_region_base = RVA_TO_PTR (preload_heap_section->VirtualAddress);
277 data_region_end = data_region_base;
278 real_data_region_end = data_region_end;
279 reserved_heap_size = preload_heap_section->Misc.VirtualSize;
280 }
801f68b9
GV
281
282 /* Update system version information to match current system. */
283 cache_system_info ();
95ed0025
RS
284}
285
286/* Round the heap up to the given alignment. */
287void
288round_heap (unsigned long align)
289{
290 unsigned long needs_to_be;
291 unsigned long need_to_alloc;
ce20e03e 292
30d2b1c2 293 needs_to_be = (unsigned long) ROUND_UP (get_heap_end (), align);
95ed0025 294 need_to_alloc = needs_to_be - (unsigned long) get_heap_end ();
ce20e03e
JB
295
296 if (need_to_alloc)
95ed0025
RS
297 sbrk (need_to_alloc);
298}