w32heap.c 8.23 KB
Newer Older
1
/* Heap management routines for GNU Emacs on the Microsoft Windows API.
2
   Copyright (C) 1994, 2001-2012  Free Software Foundation, Inc.
Richard M. Stallman's avatar
Richard M. Stallman committed
3

4
This file is part of GNU Emacs.
Richard M. Stallman's avatar
Richard M. Stallman committed
5

6
GNU Emacs is free software: you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8 9
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Richard M. Stallman's avatar
Richard M. Stallman committed
10

11 12 13 14
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
Richard M. Stallman's avatar
Richard M. Stallman committed
15

16
You should have received a copy of the GNU General Public License
17
along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
Richard M. Stallman's avatar
Richard M. Stallman committed
18

19
/*
Richard M. Stallman's avatar
Richard M. Stallman committed
20 21 22
   Geoff Voelker (voelker@cs.washington.edu)			     7-29-94
*/

Pavel Janík's avatar
Pavel Janík committed
23
#include <config.h>
Richard M. Stallman's avatar
Richard M. Stallman committed
24 25
#include <stdio.h>

Geoff Voelker's avatar
Geoff Voelker committed
26
#include "w32heap.h"
Geoff Voelker's avatar
Geoff Voelker committed
27
#include "lisp.h"  /* for VALMASK */
Richard M. Stallman's avatar
Richard M. Stallman committed
28

29
#define RVA_TO_PTR(rva) ((unsigned char *)((DWORD)(rva) + (DWORD)GetModuleHandle (NULL)))
30

Richard M. Stallman's avatar
Richard M. Stallman committed
31 32
/* This gives us the page size and the size of the allocation unit on NT.  */
SYSTEM_INFO sysinfo_cache;
33 34 35 36

/* This gives us version, build, and platform identification.  */
OSVERSIONINFO osinfo_cache;

Geoff Voelker's avatar
Geoff Voelker committed
37
unsigned long syspage_mask = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
38 39

/* The major and minor versions of NT.  */
40 41
int w32_major_version;
int w32_minor_version;
42
int w32_build_number;
Richard M. Stallman's avatar
Richard M. Stallman committed
43

Geoff Voelker's avatar
Geoff Voelker committed
44 45 46
/* Distinguish between Windows NT and Windows 95.  */
int os_subtype;

Richard M. Stallman's avatar
Richard M. Stallman committed
47 48 49 50
/* Cache information describing the NT system for later use.  */
void
cache_system_info (void)
{
51
  union
Richard M. Stallman's avatar
Richard M. Stallman committed
52
    {
53
      struct info
Richard M. Stallman's avatar
Richard M. Stallman committed
54 55 56 57 58 59 60 61 62 63
	{
	  char  major;
	  char  minor;
	  short platform;
	} info;
      DWORD data;
    } version;

  /* Cache the version of the operating system.  */
  version.data = GetVersion ();
64 65
  w32_major_version = version.info.major;
  w32_minor_version = version.info.minor;
Richard M. Stallman's avatar
Richard M. Stallman committed
66

Geoff Voelker's avatar
Geoff Voelker committed
67
  if (version.info.platform & 0x8000)
Eli Zaretskii's avatar
Eli Zaretskii committed
68
    os_subtype = OS_9X;
Geoff Voelker's avatar
Geoff Voelker committed
69 70 71
  else
    os_subtype = OS_NT;

Richard M. Stallman's avatar
Richard M. Stallman committed
72 73
  /* Cache page size, allocation unit, processor type, etc.  */
  GetSystemInfo (&sysinfo_cache);
Geoff Voelker's avatar
Geoff Voelker committed
74
  syspage_mask = sysinfo_cache.dwPageSize - 1;
75 76 77 78

  /* Cache os info.  */
  osinfo_cache.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
  GetVersionEx (&osinfo_cache);
79 80

  w32_build_number = osinfo_cache.dwBuildNumber;
Eli Zaretskii's avatar
Eli Zaretskii committed
81
  if (os_subtype == OS_9X)
82
    w32_build_number &= 0xffff;
Richard M. Stallman's avatar
Richard M. Stallman committed
83 84
}

85 86 87 88 89 90 91
/* Emulate getpagesize.  */
int
getpagesize (void)
{
  return sysinfo_cache.dwPageSize;
}

92 93 94
/* Info for managing our preload heap, which is essentially a fixed size
   data area in the executable.  */
PIMAGE_SECTION_HEADER preload_heap_section;
Richard M. Stallman's avatar
Richard M. Stallman committed
95 96 97 98

/* Info for keeping track of our heap.  */
unsigned char *data_region_base = NULL;
unsigned char *data_region_end = NULL;
Geoff Voelker's avatar
Geoff Voelker committed
99
unsigned char *real_data_region_end = NULL;
100
unsigned long  reserved_heap_size = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115

/* The start of the data segment.  */
unsigned char *
get_data_start (void)
{
  return data_region_base;
}

/* The end of the data segment.  */
unsigned char *
get_data_end (void)
{
  return data_region_end;
}

116
#if !USE_LSB_TAG
117 118 119
static char *
allocate_heap (void)
{
120 121 122 123 124 125 126 127
  /* Try to get as much as possible of the address range from the end of
     the preload heap section up to the usable address limit.  Since GNU
     malloc can handle gaps in the memory it gets from sbrk, we can
     simply set the sbrk pointer to the base of the new heap region.  */
  unsigned long base =
    ROUND_UP ((RVA_TO_PTR (preload_heap_section->VirtualAddress)
	       + preload_heap_section->Misc.VirtualSize),
	      get_allocation_unit ());
Geoff Voelker's avatar
Geoff Voelker committed
128
  unsigned long end  = 1 << VALBITS; /* 256MB */
129
  void *ptr = NULL;
130

131 132 133 134 135 136 137 138 139
  while (!ptr && (base < end))
    {
      reserved_heap_size = end - base;
      ptr = VirtualAlloc ((void *) base,
			  get_reserved_heap_size (),
			  MEM_RESERVE,
			  PAGE_NOACCESS);
      base += 0x00100000;  /* 1MB increment */
    }
140

141
  return ptr;
142
}
143
#else  /* USE_LSB_TAG */
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
static char *
allocate_heap (void)
{
  unsigned long size = 0x80000000; /* start by asking for 2GB */
  void *ptr = NULL;

  while (!ptr && size > 0x00100000)
    {
      reserved_heap_size = size;
      ptr = VirtualAlloc (NULL,
			  get_reserved_heap_size (),
			  MEM_RESERVE,
			  PAGE_NOACCESS);
      size -= 0x00800000; /* if failed, decrease request by 8MB */
    }

  return ptr;
}
162
#endif /* USE_LSB_TAG */
163 164


165 166 167
/* Emulate Unix sbrk.  Note that ralloc.c expects the return value to
   be the address of the _start_ (not end) of the new block in case of
   success, and zero (not -1) in case of failure.  */
Richard M. Stallman's avatar
Richard M. Stallman committed
168 169 170 171 172
void *
sbrk (unsigned long increment)
{
  void *result;
  long size = (long) increment;
173

Richard M. Stallman's avatar
Richard M. Stallman committed
174
  result = data_region_end;
175

Richard M. Stallman's avatar
Richard M. Stallman committed
176
  /* If size is negative, shrink the heap by decommitting pages.  */
177
  if (size < 0)
Richard M. Stallman's avatar
Richard M. Stallman committed
178
    {
Geoff Voelker's avatar
Geoff Voelker committed
179 180 181
      int new_size;
      unsigned char *new_data_region_end;

Richard M. Stallman's avatar
Richard M. Stallman committed
182 183 184 185 186 187
      size = -size;

      /* Sanity checks.  */
      if ((data_region_end - size) < data_region_base)
	return NULL;

188
      /* We can only decommit full pages, so allow for
Geoff Voelker's avatar
Geoff Voelker committed
189 190 191 192 193 194
	 partial deallocation [cga].  */
      new_data_region_end = (data_region_end - size);
      new_data_region_end = (unsigned char *)
	((long) (new_data_region_end + syspage_mask) & ~syspage_mask);
      new_size = real_data_region_end - new_data_region_end;
      real_data_region_end = new_data_region_end;
195
      if (new_size > 0)
Geoff Voelker's avatar
Geoff Voelker committed
196 197
	{
	  /* Decommit size bytes from the end of the heap.  */
198 199
	  if (using_dynamic_heap
	      && !VirtualFree (real_data_region_end, new_size, MEM_DECOMMIT))
Geoff Voelker's avatar
Geoff Voelker committed
200 201
	    return NULL;
 	}
Richard M. Stallman's avatar
Richard M. Stallman committed
202 203

      data_region_end -= size;
204
    }
Richard M. Stallman's avatar
Richard M. Stallman committed
205
  /* If size is positive, grow the heap by committing reserved pages.  */
206
  else if (size > 0)
Richard M. Stallman's avatar
Richard M. Stallman committed
207 208 209 210 211 212 213
    {
      /* Sanity checks.  */
      if ((data_region_end + size) >
	  (data_region_base + get_reserved_heap_size ()))
	return NULL;

      /* Commit more of our heap. */
214 215 216
      if (using_dynamic_heap
	  && VirtualAlloc (data_region_end, size, MEM_COMMIT,
			   PAGE_READWRITE) == NULL)
Richard M. Stallman's avatar
Richard M. Stallman committed
217 218
	return NULL;
      data_region_end += size;
Geoff Voelker's avatar
Geoff Voelker committed
219 220 221 222 223

      /* We really only commit full pages, so record where
	 the real end of committed memory is [cga].  */
      real_data_region_end = (unsigned char *)
	  ((long) (data_region_end + syspage_mask) & ~syspage_mask);
Richard M. Stallman's avatar
Richard M. Stallman committed
224
    }
225

Richard M. Stallman's avatar
Richard M. Stallman committed
226 227 228
  return result;
}

229 230 231 232 233 234 235 236 237 238 239 240
/* Initialize the internal heap variables used by sbrk.  When running in
   preload phase (ie. in the undumped executable), we rely entirely on a
   fixed size heap section included in the .exe itself; this is
   preserved during dumping, and truncated to the size actually used.

   When running in the dumped executable, we reserve as much as possible
   of the address range that is addressable by Lisp object pointers, to
   supplement what is left of the preload heap.  Although we cannot rely
   on the dynamically allocated arena being contiguous with the static
   heap area, it is not a problem because sbrk can pretend that the gap
   was allocated by something else; GNU malloc detects when there is a
   jump in the sbrk values, and starts a new heap block.  */
Richard M. Stallman's avatar
Richard M. Stallman committed
241
void
242
init_heap (void)
Richard M. Stallman's avatar
Richard M. Stallman committed
243
{
244 245 246 247
  PIMAGE_DOS_HEADER dos_header;
  PIMAGE_NT_HEADERS nt_header;

  dos_header = (PIMAGE_DOS_HEADER) RVA_TO_PTR (0);
248
  nt_header = (PIMAGE_NT_HEADERS) (((unsigned long) dos_header) +
249 250 251 252 253 254 255 256 257 258 259 260
				   dos_header->e_lfanew);
  preload_heap_section = find_section ("EMHEAP", nt_header);

  if (using_dynamic_heap)
    {
      data_region_base = allocate_heap ();
      if (!data_region_base)
	{
	  printf ("Error: Could not reserve dynamic heap area.\n");
	  exit (1);
	}

261
#if !USE_LSB_TAG
262 263
      /* Ensure that the addresses don't use the upper tag bits since
	 the Lisp type goes there.  */
264
      if (((unsigned long) data_region_base & ~VALMASK) != 0)
265 266 267 268
	{
	  printf ("Error: The heap was allocated in upper memory.\n");
	  exit (1);
	}
269
#endif
270 271 272 273 274 275 276 277 278 279
      data_region_end = data_region_base;
      real_data_region_end = data_region_end;
    }
  else
    {
      data_region_base = RVA_TO_PTR (preload_heap_section->VirtualAddress);
      data_region_end = data_region_base;
      real_data_region_end = data_region_end;
      reserved_heap_size = preload_heap_section->Misc.VirtualSize;
    }
Geoff Voelker's avatar
Geoff Voelker committed
280 281 282

  /* Update system version information to match current system.  */
  cache_system_info ();
Richard M. Stallman's avatar
Richard M. Stallman committed
283 284 285 286 287 288 289 290
}

/* Round the heap up to the given alignment.  */
void
round_heap (unsigned long align)
{
  unsigned long needs_to_be;
  unsigned long need_to_alloc;
291

292
  needs_to_be = (unsigned long) ROUND_UP (get_heap_end (), align);
Richard M. Stallman's avatar
Richard M. Stallman committed
293
  need_to_alloc = needs_to_be - (unsigned long) get_heap_end ();
294 295

  if (need_to_alloc)
Richard M. Stallman's avatar
Richard M. Stallman committed
296 297
    sbrk (need_to_alloc);
}