w32heap.c 7.9 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 34
#undef RVA_TO_PTR
#define RVA_TO_PTR(rva) ((DWORD)(rva) + (DWORD)GetModuleHandle (NULL))

Richard M. Stallman's avatar
Richard M. Stallman committed
35 36
/* 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
37
unsigned long syspage_mask = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
38 39 40 41 42 43

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

/* The major and minor versions of NT.  */
44 45
int w32_major_version;
int w32_minor_version;
Richard M. Stallman's avatar
Richard M. Stallman committed
46

Geoff Voelker's avatar
Geoff Voelker committed
47 48 49
/* Distinguish between Windows NT and Windows 95.  */
int os_subtype;

Richard M. Stallman's avatar
Richard M. Stallman committed
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
/* 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 ();
67 68
  w32_major_version = version.info.major;
  w32_minor_version = version.info.minor;
Richard M. Stallman's avatar
Richard M. Stallman committed
69

Geoff Voelker's avatar
Geoff Voelker committed
70 71 72 73 74
  if (version.info.platform & 0x8000)
    os_subtype = OS_WIN95;
  else
    os_subtype = OS_NT;

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

80 81 82 83 84 85 86
/* Emulate getpagesize.  */
int
getpagesize (void)
{
  return sysinfo_cache.dwPageSize;
}

87 88 89
/* 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
90 91 92 93

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

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

111 112 113
static char *
allocate_heap (void)
{
114 115 116 117 118 119 120 121
  /* 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
122
  unsigned long end  = 1 << VALBITS; /* 256MB */
123
  void *ptr = NULL;
124

125 126 127 128 129 130 131 132 133
  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 */
    }
134

135
  return ptr;
136 137 138
}


Richard M. Stallman's avatar
Richard M. Stallman committed
139 140 141 142 143 144 145 146 147 148 149 150
/* Emulate Unix sbrk.  */
void *
sbrk (unsigned long increment)
{
  void *result;
  long size = (long) increment;
  
  result = data_region_end;
  
  /* If size is negative, shrink the heap by decommitting pages.  */
  if (size < 0) 
    {
Geoff Voelker's avatar
Geoff Voelker committed
151 152 153
      int new_size;
      unsigned char *new_data_region_end;

Richard M. Stallman's avatar
Richard M. Stallman committed
154 155 156 157 158 159
      size = -size;

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

Geoff Voelker's avatar
Geoff Voelker committed
160 161 162 163 164 165 166
      /* 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;
167
      if (new_size > 0)
Geoff Voelker's avatar
Geoff Voelker committed
168 169
	{
	  /* Decommit size bytes from the end of the heap.  */
170 171
	  if (using_dynamic_heap
	      && !VirtualFree (real_data_region_end, new_size, MEM_DECOMMIT))
Geoff Voelker's avatar
Geoff Voelker committed
172 173
	    return NULL;
 	}
Richard M. Stallman's avatar
Richard M. Stallman committed
174 175 176 177 178 179 180 181 182 183 184 185

      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. */
186 187 188
      if (using_dynamic_heap
	  && VirtualAlloc (data_region_end, size, MEM_COMMIT,
			   PAGE_READWRITE) == NULL)
Richard M. Stallman's avatar
Richard M. Stallman committed
189 190
	return NULL;
      data_region_end += size;
Geoff Voelker's avatar
Geoff Voelker committed
191 192 193 194 195

      /* 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
196 197 198 199 200
    }
  
  return result;
}

201 202 203 204 205 206 207 208 209 210 211 212
/* 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
213
void
214
init_heap ()
Richard M. Stallman's avatar
Richard M. Stallman committed
215
{
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
  PIMAGE_DOS_HEADER dos_header;
  PIMAGE_NT_HEADERS nt_header;

  dos_header = (PIMAGE_DOS_HEADER) RVA_TO_PTR (0);
  nt_header = (PIMAGE_NT_HEADERS) (((unsigned long) dos_header) + 
				   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);
	}

      /* 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) 
	{
	  printf ("Error: The heap was allocated in upper memory.\n");
	  exit (1);
	}

      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
251 252 253

  /* Update system version information to match current system.  */
  cache_system_info ();
Richard M. Stallman's avatar
Richard M. Stallman committed
254 255 256 257 258 259 260 261 262
}

/* Round the heap up to the given alignment.  */
void
round_heap (unsigned long align)
{
  unsigned long needs_to_be;
  unsigned long need_to_alloc;
  
263
  needs_to_be = (unsigned long) ROUND_UP (get_heap_end (), align);
Richard M. Stallman's avatar
Richard M. Stallman committed
264 265 266 267 268
  need_to_alloc = needs_to_be - (unsigned long) get_heap_end ();
  
  if (need_to_alloc) 
    sbrk (need_to_alloc);
}
Geoff Voelker's avatar
Geoff Voelker committed
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291

#if (_MSC_VER >= 1000)

/* MSVC 4.2 invokes these functions from mainCRTStartup to initialize
   a heap via HeapCreate.  They are normally defined by the runtime,
   but we override them here so that the unnecessary HeapCreate call
   is not performed.  */

int __cdecl
_heap_init (void)
{
  /* Stepping through the assembly indicates that mainCRTStartup is
     expecting a nonzero success return value.  */
  return 1;
}

void __cdecl
_heap_term (void)
{
  return;
}

#endif