1/* Heap management routines for GNU Emacs on the Microsoft W32 API. 2 Copyright (C) 1994, 2001, 2002, 2003, 2004, 2005, 3 2006, 2007 Free Software Foundation, Inc. 4 5This file is part of GNU Emacs. 6 7GNU Emacs is free software; you can redistribute it and/or modify 8it under the terms of the GNU General Public License as published by 9the Free Software Foundation; either version 2, or (at your option) 10any later version. 11 12GNU Emacs is distributed in the hope that it will be useful, 13but WITHOUT ANY WARRANTY; without even the implied warranty of 14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15GNU General Public License for more details. 16 17You should have received a copy of the GNU General Public License 18along with GNU Emacs; see the file COPYING. If not, write to 19the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20Boston, MA 02110-1301, USA. 21 22 Geoff Voelker (voelker@cs.washington.edu) 7-29-94 23*/ 24 25#ifdef HAVE_CONFIG_H 26#include <config.h> 27#endif 28 29#include <stdlib.h> 30#include <stdio.h> 31 32#include "w32heap.h" 33#include "lisp.h" /* for VALMASK */ 34 35#define RVA_TO_PTR(rva) ((unsigned char *)((DWORD)(rva) + (DWORD)GetModuleHandle (NULL))) 36 37/* This gives us the page size and the size of the allocation unit on NT. */ 38SYSTEM_INFO sysinfo_cache; 39 40/* This gives us version, build, and platform identification. */ 41OSVERSIONINFO osinfo_cache; 42 43unsigned long syspage_mask = 0; 44 45/* The major and minor versions of NT. */ 46int w32_major_version; 47int w32_minor_version; 48int w32_build_number; 49 50/* Distinguish between Windows NT and Windows 95. */ 51int os_subtype; 52 53/* Cache information describing the NT system for later use. */ 54void 55cache_system_info (void) 56{ 57 union 58 { 59 struct info 60 { 61 char major; 62 char minor; 63 short platform; 64 } info; 65 DWORD data; 66 } version; 67 68 /* Cache the version of the operating system. */ 69 version.data = GetVersion (); 70 w32_major_version = version.info.major; 71 w32_minor_version = version.info.minor; 72 73 if (version.info.platform & 0x8000) 74 os_subtype = OS_WIN95; 75 else 76 os_subtype = OS_NT; 77 78 /* Cache page size, allocation unit, processor type, etc. */ 79 GetSystemInfo (&sysinfo_cache); 80 syspage_mask = sysinfo_cache.dwPageSize - 1; 81 82 /* Cache os info. */ 83 osinfo_cache.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); 84 GetVersionEx (&osinfo_cache); 85 86 w32_build_number = osinfo_cache.dwBuildNumber; 87 if (os_subtype == OS_WIN95) 88 w32_build_number &= 0xffff; 89} 90 91/* Emulate getpagesize. */ 92int 93getpagesize (void) 94{ 95 return sysinfo_cache.dwPageSize; 96} 97 98/* Info for managing our preload heap, which is essentially a fixed size 99 data area in the executable. */ 100PIMAGE_SECTION_HEADER preload_heap_section; 101 102/* Info for keeping track of our heap. */ 103unsigned char *data_region_base = NULL; 104unsigned char *data_region_end = NULL; 105unsigned char *real_data_region_end = NULL; 106unsigned long reserved_heap_size = 0; 107 108/* The start of the data segment. */ 109unsigned char * 110get_data_start (void) 111{ 112 return data_region_base; 113} 114 115/* The end of the data segment. */ 116unsigned char * 117get_data_end (void) 118{ 119 return data_region_end; 120} 121 122static char * 123allocate_heap (void) 124{ 125 /* Try to get as much as possible of the address range from the end of 126 the preload heap section up to the usable address limit. Since GNU 127 malloc can handle gaps in the memory it gets from sbrk, we can 128 simply set the sbrk pointer to the base of the new heap region. */ 129 unsigned long base = 130 ROUND_UP ((RVA_TO_PTR (preload_heap_section->VirtualAddress) 131 + preload_heap_section->Misc.VirtualSize), 132 get_allocation_unit ()); 133 unsigned long end = 1 << VALBITS; /* 256MB */ 134 void *ptr = NULL; 135 136 while (!ptr && (base < end)) 137 { 138 reserved_heap_size = end - base; 139 ptr = VirtualAlloc ((void *) base, 140 get_reserved_heap_size (), 141 MEM_RESERVE, 142 PAGE_NOACCESS); 143 base += 0x00100000; /* 1MB increment */ 144 } 145 146 return ptr; 147} 148 149 150/* Emulate Unix sbrk. */ 151void * 152sbrk (unsigned long increment) 153{ 154 void *result; 155 long size = (long) increment; 156 157 result = data_region_end; 158 159 /* If size is negative, shrink the heap by decommitting pages. */ 160 if (size < 0) 161 { 162 int new_size; 163 unsigned char *new_data_region_end; 164 165 size = -size; 166 167 /* Sanity checks. */ 168 if ((data_region_end - size) < data_region_base) 169 return NULL; 170 171 /* We can only decommit full pages, so allow for 172 partial deallocation [cga]. */ 173 new_data_region_end = (data_region_end - size); 174 new_data_region_end = (unsigned char *) 175 ((long) (new_data_region_end + syspage_mask) & ~syspage_mask); 176 new_size = real_data_region_end - new_data_region_end; 177 real_data_region_end = new_data_region_end; 178 if (new_size > 0) 179 { 180 /* Decommit size bytes from the end of the heap. */ 181 if (using_dynamic_heap 182 && !VirtualFree (real_data_region_end, new_size, MEM_DECOMMIT)) 183 return NULL; 184 } 185 186 data_region_end -= size; 187 } 188 /* If size is positive, grow the heap by committing reserved pages. */ 189 else if (size > 0) 190 { 191 /* Sanity checks. */ 192 if ((data_region_end + size) > 193 (data_region_base + get_reserved_heap_size ())) 194 return NULL; 195 196 /* Commit more of our heap. */ 197 if (using_dynamic_heap 198 && VirtualAlloc (data_region_end, size, MEM_COMMIT, 199 PAGE_READWRITE) == NULL) 200 return NULL; 201 data_region_end += size; 202 203 /* We really only commit full pages, so record where 204 the real end of committed memory is [cga]. */ 205 real_data_region_end = (unsigned char *) 206 ((long) (data_region_end + syspage_mask) & ~syspage_mask); 207 } 208 209 return result; 210} 211 212/* Initialize the internal heap variables used by sbrk. When running in 213 preload phase (ie. in the undumped executable), we rely entirely on a 214 fixed size heap section included in the .exe itself; this is 215 preserved during dumping, and truncated to the size actually used. 216 217 When running in the dumped executable, we reserve as much as possible 218 of the address range that is addressable by Lisp object pointers, to 219 supplement what is left of the preload heap. Although we cannot rely 220 on the dynamically allocated arena being contiguous with the static 221 heap area, it is not a problem because sbrk can pretend that the gap 222 was allocated by something else; GNU malloc detects when there is a 223 jump in the sbrk values, and starts a new heap block. */ 224void 225init_heap () 226{ 227 PIMAGE_DOS_HEADER dos_header; 228 PIMAGE_NT_HEADERS nt_header; 229 230 dos_header = (PIMAGE_DOS_HEADER) RVA_TO_PTR (0); 231 nt_header = (PIMAGE_NT_HEADERS) (((unsigned long) dos_header) + 232 dos_header->e_lfanew); 233 preload_heap_section = find_section ("EMHEAP", nt_header); 234 235 if (using_dynamic_heap) 236 { 237 data_region_base = allocate_heap (); 238 if (!data_region_base) 239 { 240 printf ("Error: Could not reserve dynamic heap area.\n"); 241 exit (1); 242 } 243 244#if defined (NO_UNION_TYPE) && !defined (USE_LSB_TAG) 245 /* Ensure that the addresses don't use the upper tag bits since 246 the Lisp type goes there. */ 247 if (((unsigned long) data_region_base & ~VALMASK) != 0) 248 { 249 printf ("Error: The heap was allocated in upper memory.\n"); 250 exit (1); 251 } 252#endif 253 data_region_end = data_region_base; 254 real_data_region_end = data_region_end; 255 } 256 else 257 { 258 data_region_base = RVA_TO_PTR (preload_heap_section->VirtualAddress); 259 data_region_end = data_region_base; 260 real_data_region_end = data_region_end; 261 reserved_heap_size = preload_heap_section->Misc.VirtualSize; 262 } 263 264 /* Update system version information to match current system. */ 265 cache_system_info (); 266} 267 268/* Round the heap up to the given alignment. */ 269void 270round_heap (unsigned long align) 271{ 272 unsigned long needs_to_be; 273 unsigned long need_to_alloc; 274 275 needs_to_be = (unsigned long) ROUND_UP (get_heap_end (), align); 276 need_to_alloc = needs_to_be - (unsigned long) get_heap_end (); 277 278 if (need_to_alloc) 279 sbrk (need_to_alloc); 280} 281 282#if (_MSC_VER >= 1000 && _MSC_VER < 1300 && !defined(USE_CRT_DLL)) 283 284/* MSVC 4.2 invokes these functions from mainCRTStartup to initialize 285 a heap via HeapCreate. They are normally defined by the runtime, 286 but we override them here so that the unnecessary HeapCreate call 287 is not performed. */ 288 289int __cdecl 290_heap_init (void) 291{ 292 /* Stepping through the assembly indicates that mainCRTStartup is 293 expecting a nonzero success return value. */ 294 return 1; 295} 296 297void __cdecl 298_heap_term (void) 299{ 300 return; 301} 302 303#endif 304 305/* arch-tag: 9a6a9860-040d-422d-8905-450dd535cd9c 306 (do not change this comment) */ 307