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

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

6 7 8 9
GNU Emacs is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, 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 17 18 19
You should have received a copy of the GNU General Public License
along with GNU Emacs; see the file COPYING.  If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
Richard M. Stallman's avatar
Richard M. Stallman committed
20 21 22 23

   Geoff Voelker (voelker@cs.washington.edu)			     7-29-94
*/

Geoff Voelker's avatar
Geoff Voelker committed
24 25
#include "config.h"

Richard M. Stallman's avatar
Richard M. Stallman committed
26 27 28
#include <stdlib.h>
#include <stdio.h>

Geoff Voelker's avatar
Geoff Voelker committed
29
#include "w32heap.h"
Geoff Voelker's avatar
Geoff Voelker committed
30
#include "lisp.h"  /* for VALMASK */
Richard M. Stallman's avatar
Richard M. Stallman committed
31 32 33

/* This gives us the page size and the size of the allocation unit on NT.  */
SYSTEM_INFO sysinfo_cache;
Geoff Voelker's avatar
Geoff Voelker committed
34
unsigned long syspage_mask = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
35 36 37 38 39 40

/* These are defined to get Emacs to compile, but are not used.  */
int edata;
int etext;

/* The major and minor versions of NT.  */
41 42
int w32_major_version;
int w32_minor_version;
Richard M. Stallman's avatar
Richard M. Stallman committed
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60

/* Cache information describing the NT system for later use.  */
void
cache_system_info (void)
{
  union 
    {
      struct info 
	{
	  char  major;
	  char  minor;
	  short platform;
	} info;
      DWORD data;
    } version;

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

  /* Cache page size, allocation unit, processor type, etc.  */
  GetSystemInfo (&sysinfo_cache);
Geoff Voelker's avatar
Geoff Voelker committed
66
  syspage_mask = sysinfo_cache.dwPageSize - 1;
Richard M. Stallman's avatar
Richard M. Stallman committed
67 68
}

69 70 71 72 73 74 75
/* Emulate getpagesize.  */
int
getpagesize (void)
{
  return sysinfo_cache.dwPageSize;
}

Richard M. Stallman's avatar
Richard M. Stallman committed
76 77 78 79 80 81 82 83 84 85 86 87
/* Round ADDRESS up to be aligned with ALIGN.  */
unsigned char *
round_to_next (unsigned char *address, unsigned long align)
{
  unsigned long tmp;

  tmp = (unsigned long) address;
  tmp = (tmp + align - 1) / align;

  return (unsigned char *) (tmp * align);
}

88 89 90 91
/* Force zero initialized variables to be placed in the .data segment;
   MSVC 5.0 otherwise places them in .bss, which breaks the dumping code.  */
#pragma data_seg(".data")

Richard M. Stallman's avatar
Richard M. Stallman committed
92 93 94
/* 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
95
unsigned char *real_data_region_end = NULL;
Richard M. Stallman's avatar
Richard M. Stallman committed
96
unsigned long  data_region_size = 0;
97
unsigned long  reserved_heap_size = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112

/* 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;
}

113 114 115
static char *
allocate_heap (void)
{
Geoff Voelker's avatar
Geoff Voelker committed
116 117 118 119 120
  /* The base address for our GNU malloc heap is chosen in conjuction
     with the link settings for temacs.exe which control the stack size,
     the initial default process heap size and the executable image base
     address.  The link settings and the malloc heap base below must all
     correspond; the relationship between these values depends on how NT
121
     and Windows 95 arrange the virtual address space for a process (and on
Geoff Voelker's avatar
Geoff Voelker committed
122 123 124 125
     the size of the code and data segments in temacs.exe).

     The most important thing is to make base address for the executable
     image high enough to leave enough room between it and the 4MB floor
126
     of the process address space on Windows 95 for the primary thread stack,
Geoff Voelker's avatar
Geoff Voelker committed
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
     the process default heap, and other assorted odds and ends
     (eg. environment strings, private system dll memory etc) that are
     allocated before temacs has a chance to grab its malloc arena.  The
     malloc heap base can then be set several MB higher than the
     executable image base, leaving enough room for the code and data
     segments.

     Because some parts of Emacs can use rather a lot of stack space
     (for instance, the regular expression routines can potentially
     allocate several MB of stack space) we allow 8MB for the stack.

     Allowing 1MB for the default process heap, and 1MB for odds and
     ends, we can base the executable at 16MB and still have a generous
     safety margin.  At the moment, the executable has about 810KB of
     code (for x86) and about 550KB of data - on RISC platforms the code
     size could be roughly double, so if we allow 4MB for the executable
     we will have plenty of room for expansion.

145
     Thus we would like to set the malloc heap base to 20MB.  However,
146
     Windows 95 refuses to allocate the heap starting at this address, so we
147
     set the base to 27MB to make it happy.  Since Emacs now leaves
Geoff Voelker's avatar
Geoff Voelker committed
148
     28 bits available for pointers, this lets us use the remainder of
149 150
     the region below the 256MB line for our malloc arena - 229MB is
     still a pretty decent arena to play in!  */
Geoff Voelker's avatar
Geoff Voelker committed
151

152
  unsigned long base = 0x01B00000;   /*  27MB */
Geoff Voelker's avatar
Geoff Voelker committed
153
  unsigned long end  = 1 << VALBITS; /* 256MB */
154
  void *ptr = NULL;
155

156 157
#if NTHEAP_PROBE_BASE /* This is never normally defined */
  /* Try various addresses looking for one the kernel will let us have.  */
158 159 160 161 162 163 164 165 166
  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 */
    }
167 168 169 170 171 172
#else
  reserved_heap_size = end - base;
  ptr = VirtualAlloc ((void *) base,
		      get_reserved_heap_size (),
		      MEM_RESERVE,
		      PAGE_NOACCESS);
173
#endif
174

175
  return ptr;
176 177 178
}


Richard M. Stallman's avatar
Richard M. Stallman committed
179 180 181 182 183 184 185 186 187 188
/* Emulate Unix sbrk.  */
void *
sbrk (unsigned long increment)
{
  void *result;
  long size = (long) increment;
  
  /* Allocate our heap if we haven't done so already.  */
  if (!data_region_base) 
    {
189
      data_region_base = allocate_heap ();
Richard M. Stallman's avatar
Richard M. Stallman committed
190 191 192
      if (!data_region_base)
	return NULL;

Geoff Voelker's avatar
Geoff Voelker committed
193 194 195
      /* Ensure that the addresses don't use the upper tag bits since
	 the Lisp type goes there.  */
      if (((unsigned long) data_region_base & ~VALMASK) != 0) 
Richard M. Stallman's avatar
Richard M. Stallman committed
196 197 198 199 200 201
	{
	  printf ("Error: The heap was allocated in upper memory.\n");
	  exit (1);
	}

      data_region_end = data_region_base;
Geoff Voelker's avatar
Geoff Voelker committed
202
      real_data_region_end = data_region_end;
Richard M. Stallman's avatar
Richard M. Stallman committed
203 204 205 206 207 208 209 210
      data_region_size = get_reserved_heap_size ();
    }
  
  result = data_region_end;
  
  /* If size is negative, shrink the heap by decommitting pages.  */
  if (size < 0) 
    {
Geoff Voelker's avatar
Geoff Voelker committed
211 212 213
      int new_size;
      unsigned char *new_data_region_end;

Richard M. Stallman's avatar
Richard M. Stallman committed
214 215 216 217 218 219
      size = -size;

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

Geoff Voelker's avatar
Geoff Voelker committed
220 221 222 223 224 225 226 227 228 229 230 231 232
      /* We can only decommit full pages, so allow for 
	 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;
      if (new_size > 0) 
	{
	  /* Decommit size bytes from the end of the heap.  */
	  if (!VirtualFree (real_data_region_end, new_size, MEM_DECOMMIT))
	    return NULL;
 	}
Richard M. Stallman's avatar
Richard M. Stallman committed
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248

      data_region_end -= size;
    } 
  /* If size is positive, grow the heap by committing reserved pages.  */
  else if (size > 0) 
    {
      /* Sanity checks.  */
      if ((data_region_end + size) >
	  (data_region_base + get_reserved_heap_size ()))
	return NULL;

      /* Commit more of our heap. */
      if (VirtualAlloc (data_region_end, size, MEM_COMMIT,
			PAGE_READWRITE) == NULL)
	return NULL;
      data_region_end += size;
Geoff Voelker's avatar
Geoff Voelker committed
249 250 251 252 253

      /* 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
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
    }
  
  return result;
}

/* Recreate the heap from the data that was dumped to the executable.
   EXECUTABLE_PATH tells us where to find the executable.  */
void
recreate_heap (char *executable_path)
{
  unsigned char *tmp;

  /* First reserve the upper part of our heap.  (We reserve first
     because there have been problems in the past where doing the
     mapping first has loaded DLLs into the VA space of our heap.)  */
  tmp = VirtualAlloc ((void *) get_heap_end (),
		      get_reserved_heap_size () - get_committed_heap_size (),
		      MEM_RESERVE,
		      PAGE_NOACCESS);
  if (!tmp)
    exit (1);

  /* We read in the data for the .bss section from the executable
     first and map in the heap from the executable second to prevent
     any funny interactions between file I/O and file mapping.  */
  read_in_bss (executable_path);
  map_in_heap (executable_path);
}

/* Round the heap up to the given alignment.  */
void
round_heap (unsigned long align)
{
  unsigned long needs_to_be;
  unsigned long need_to_alloc;
  
  needs_to_be = (unsigned long) round_to_next (get_heap_end (), align);
  need_to_alloc = needs_to_be - (unsigned long) get_heap_end ();
  
  if (need_to_alloc) 
    sbrk (need_to_alloc);
}