*** empty log message ***
[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 31
30d2b1c2 32#undef RVA_TO_PTR
2e4f6477 33#define RVA_TO_PTR(rva) ((unsigned char *)((DWORD)(rva) + (DWORD)GetModuleHandle (NULL)))
30d2b1c2 34
95ed0025
RS
35/* This gives us the page size and the size of the allocation unit on NT. */
36SYSTEM_INFO sysinfo_cache;
b9cad9c1
GV
37
38/* This gives us version, build, and platform identification. */
39OSVERSIONINFO osinfo_cache;
40
3bbabc43 41unsigned long syspage_mask = 0;
95ed0025
RS
42
43/* These are defined to get Emacs to compile, but are not used. */
44int edata;
45int etext;
46
47/* The major and minor versions of NT. */
fbd6baed
GV
48int w32_major_version;
49int w32_minor_version;
39f1f652 50int w32_build_number;
95ed0025 51
801f68b9
GV
52/* Distinguish between Windows NT and Windows 95. */
53int os_subtype;
54
95ed0025
RS
55/* Cache information describing the NT system for later use. */
56void
57cache_system_info (void)
58{
59 union
60 {
61 struct info
62 {
63 char major;
64 char minor;
65 short platform;
66 } info;
67 DWORD data;
68 } version;
69
70 /* Cache the version of the operating system. */
71 version.data = GetVersion ();
fbd6baed
GV
72 w32_major_version = version.info.major;
73 w32_minor_version = version.info.minor;
95ed0025 74
801f68b9
GV
75 if (version.info.platform & 0x8000)
76 os_subtype = OS_WIN95;
77 else
78 os_subtype = OS_NT;
79
95ed0025
RS
80 /* Cache page size, allocation unit, processor type, etc. */
81 GetSystemInfo (&sysinfo_cache);
3bbabc43 82 syspage_mask = sysinfo_cache.dwPageSize - 1;
b9cad9c1
GV
83
84 /* Cache os info. */
85 osinfo_cache.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
86 GetVersionEx (&osinfo_cache);
39f1f652
AI
87
88 w32_build_number = osinfo_cache.dwBuildNumber;
89 if (os_subtype == OS_WIN95)
90 w32_build_number &= 0xffff;
95ed0025
RS
91}
92
e54c8cd1
GV
93/* Emulate getpagesize. */
94int
95getpagesize (void)
96{
97 return sysinfo_cache.dwPageSize;
98}
99
30d2b1c2
AI
100/* Info for managing our preload heap, which is essentially a fixed size
101 data area in the executable. */
102PIMAGE_SECTION_HEADER preload_heap_section;
95ed0025
RS
103
104/* Info for keeping track of our heap. */
105unsigned char *data_region_base = NULL;
106unsigned char *data_region_end = NULL;
3bbabc43 107unsigned char *real_data_region_end = NULL;
011db670 108unsigned long reserved_heap_size = 0;
95ed0025
RS
109
110/* The start of the data segment. */
111unsigned char *
112get_data_start (void)
113{
114 return data_region_base;
115}
116
117/* The end of the data segment. */
118unsigned char *
119get_data_end (void)
120{
121 return data_region_end;
122}
123
011db670
GV
124static char *
125allocate_heap (void)
126{
30d2b1c2
AI
127 /* Try to get as much as possible of the address range from the end of
128 the preload heap section up to the usable address limit. Since GNU
129 malloc can handle gaps in the memory it gets from sbrk, we can
130 simply set the sbrk pointer to the base of the new heap region. */
131 unsigned long base =
132 ROUND_UP ((RVA_TO_PTR (preload_heap_section->VirtualAddress)
133 + preload_heap_section->Misc.VirtualSize),
134 get_allocation_unit ());
8dfdd41f 135 unsigned long end = 1 << VALBITS; /* 256MB */
709fd16b 136 void *ptr = NULL;
011db670 137
709fd16b
GV
138 while (!ptr && (base < end))
139 {
709fd16b
GV
140 reserved_heap_size = end - base;
141 ptr = VirtualAlloc ((void *) base,
142 get_reserved_heap_size (),
143 MEM_RESERVE,
144 PAGE_NOACCESS);
709fd16b
GV
145 base += 0x00100000; /* 1MB increment */
146 }
0d05360d 147
709fd16b 148 return ptr;
011db670 149}
011db670
GV
150
151
95ed0025
RS
152/* Emulate Unix sbrk. */
153void *
154sbrk (unsigned long increment)
155{
156 void *result;
157 long size = (long) increment;
158
95ed0025
RS
159 result = data_region_end;
160
161 /* If size is negative, shrink the heap by decommitting pages. */
162 if (size < 0)
163 {
3bbabc43
GV
164 int new_size;
165 unsigned char *new_data_region_end;
166
95ed0025
RS
167 size = -size;
168
169 /* Sanity checks. */
95ed0025
RS
170 if ((data_region_end - size) < data_region_base)
171 return NULL;
172
3bbabc43
GV
173 /* We can only decommit full pages, so allow for
174 partial deallocation [cga]. */
175 new_data_region_end = (data_region_end - size);
176 new_data_region_end = (unsigned char *)
177 ((long) (new_data_region_end + syspage_mask) & ~syspage_mask);
178 new_size = real_data_region_end - new_data_region_end;
179 real_data_region_end = new_data_region_end;
30d2b1c2 180 if (new_size > 0)
3bbabc43
GV
181 {
182 /* Decommit size bytes from the end of the heap. */
30d2b1c2
AI
183 if (using_dynamic_heap
184 && !VirtualFree (real_data_region_end, new_size, MEM_DECOMMIT))
3bbabc43
GV
185 return NULL;
186 }
95ed0025
RS
187
188 data_region_end -= size;
189 }
190 /* If size is positive, grow the heap by committing reserved pages. */
191 else if (size > 0)
192 {
193 /* Sanity checks. */
95ed0025
RS
194 if ((data_region_end + size) >
195 (data_region_base + get_reserved_heap_size ()))
196 return NULL;
197
198 /* Commit more of our heap. */
30d2b1c2
AI
199 if (using_dynamic_heap
200 && VirtualAlloc (data_region_end, size, MEM_COMMIT,
201 PAGE_READWRITE) == NULL)
95ed0025
RS
202 return NULL;
203 data_region_end += size;
3bbabc43
GV
204
205 /* We really only commit full pages, so record where
206 the real end of committed memory is [cga]. */
207 real_data_region_end = (unsigned char *)
208 ((long) (data_region_end + syspage_mask) & ~syspage_mask);
95ed0025
RS
209 }
210
211 return result;
212}
213
30d2b1c2
AI
214/* Initialize the internal heap variables used by sbrk. When running in
215 preload phase (ie. in the undumped executable), we rely entirely on a
216 fixed size heap section included in the .exe itself; this is
217 preserved during dumping, and truncated to the size actually used.
218
219 When running in the dumped executable, we reserve as much as possible
220 of the address range that is addressable by Lisp object pointers, to
221 supplement what is left of the preload heap. Although we cannot rely
222 on the dynamically allocated arena being contiguous with the static
223 heap area, it is not a problem because sbrk can pretend that the gap
224 was allocated by something else; GNU malloc detects when there is a
225 jump in the sbrk values, and starts a new heap block. */
95ed0025 226void
30d2b1c2 227init_heap ()
95ed0025 228{
30d2b1c2
AI
229 PIMAGE_DOS_HEADER dos_header;
230 PIMAGE_NT_HEADERS nt_header;
231
232 dos_header = (PIMAGE_DOS_HEADER) RVA_TO_PTR (0);
233 nt_header = (PIMAGE_NT_HEADERS) (((unsigned long) dos_header) +
234 dos_header->e_lfanew);
235 preload_heap_section = find_section ("EMHEAP", nt_header);
236
237 if (using_dynamic_heap)
238 {
239 data_region_base = allocate_heap ();
240 if (!data_region_base)
241 {
242 printf ("Error: Could not reserve dynamic heap area.\n");
243 exit (1);
244 }
245
246 /* Ensure that the addresses don't use the upper tag bits since
247 the Lisp type goes there. */
248 if (((unsigned long) data_region_base & ~VALMASK) != 0)
249 {
250 printf ("Error: The heap was allocated in upper memory.\n");
251 exit (1);
252 }
253
254 data_region_end = data_region_base;
255 real_data_region_end = data_region_end;
256 }
257 else
258 {
259 data_region_base = RVA_TO_PTR (preload_heap_section->VirtualAddress);
260 data_region_end = data_region_base;
261 real_data_region_end = data_region_end;
262 reserved_heap_size = preload_heap_section->Misc.VirtualSize;
263 }
801f68b9
GV
264
265 /* Update system version information to match current system. */
266 cache_system_info ();
95ed0025
RS
267}
268
269/* Round the heap up to the given alignment. */
270void
271round_heap (unsigned long align)
272{
273 unsigned long needs_to_be;
274 unsigned long need_to_alloc;
275
30d2b1c2 276 needs_to_be = (unsigned long) ROUND_UP (get_heap_end (), align);
95ed0025
RS
277 need_to_alloc = needs_to_be - (unsigned long) get_heap_end ();
278
279 if (need_to_alloc)
280 sbrk (need_to_alloc);
281}
801f68b9 282
2e4f6477 283#if (_MSC_VER >= 1000 && !defined(USE_CRT_DLL))
801f68b9
GV
284
285/* MSVC 4.2 invokes these functions from mainCRTStartup to initialize
286 a heap via HeapCreate. They are normally defined by the runtime,
287 but we override them here so that the unnecessary HeapCreate call
288 is not performed. */
289
290int __cdecl
291_heap_init (void)
292{
293 /* Stepping through the assembly indicates that mainCRTStartup is
294 expecting a nonzero success return value. */
295 return 1;
296}
297
298void __cdecl
299_heap_term (void)
300{
301 return;
302}
303
304#endif