w32heap.c 8.41 KB
Newer Older
1
/* Heap management routines for GNU Emacs on the Microsoft W32 API.
2
   Copyright (C) 1994, 2001, 2002, 2003, 2004, 2005,
Glenn Morris's avatar
Glenn Morris committed
3
                 2006, 2007, 2008  Free Software Foundation, Inc.
Richard M. Stallman's avatar
Richard M. Stallman committed
4

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

7 8
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
9
the Free Software Foundation; either version 3, or (at your option)
10
any later version.
Richard M. Stallman's avatar
Richard M. Stallman committed
11

12 13 14 15
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
16

17 18
You should have received a copy of the GNU General Public License
along with GNU Emacs; see the file COPYING.  If not, write to
Lute Kamstra's avatar
Lute Kamstra committed
19 20
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
Richard M. Stallman's avatar
Richard M. Stallman committed
21 22 23 24

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

Pavel Janík's avatar
Pavel Janík committed
25 26 27
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
Geoff Voelker's avatar
Geoff Voelker committed
28

Richard M. Stallman's avatar
Richard M. Stallman committed
29 30 31
#include <stdlib.h>
#include <stdio.h>

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

35
#define RVA_TO_PTR(rva) ((unsigned char *)((DWORD)(rva) + (DWORD)GetModuleHandle (NULL)))
36

Richard M. Stallman's avatar
Richard M. Stallman committed
37 38
/* This gives us the page size and the size of the allocation unit on NT.  */
SYSTEM_INFO sysinfo_cache;
39 40 41 42

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

Geoff Voelker's avatar
Geoff Voelker committed
43
unsigned long syspage_mask = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
44 45

/* The major and minor versions of NT.  */
46 47
int w32_major_version;
int w32_minor_version;
48
int w32_build_number;
Richard M. Stallman's avatar
Richard M. Stallman committed
49

Geoff Voelker's avatar
Geoff Voelker committed
50 51 52
/* Distinguish between Windows NT and Windows 95.  */
int os_subtype;

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

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

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

Richard M. Stallman's avatar
Richard M. Stallman committed
78 79
  /* Cache page size, allocation unit, processor type, etc.  */
  GetSystemInfo (&sysinfo_cache);
Geoff Voelker's avatar
Geoff Voelker committed
80
  syspage_mask = sysinfo_cache.dwPageSize - 1;
81 82 83 84

  /* Cache os info.  */
  osinfo_cache.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
  GetVersionEx (&osinfo_cache);
85 86 87 88

  w32_build_number = osinfo_cache.dwBuildNumber;
  if (os_subtype == OS_WIN95)
    w32_build_number &= 0xffff;
Richard M. Stallman's avatar
Richard M. Stallman committed
89 90
}

91 92 93 94 95 96 97
/* Emulate getpagesize.  */
int
getpagesize (void)
{
  return sysinfo_cache.dwPageSize;
}

98 99 100
/* 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
101 102 103 104

/* 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
105
unsigned char *real_data_region_end = NULL;
106
unsigned long  reserved_heap_size = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121

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

122 123 124
static char *
allocate_heap (void)
{
125 126 127 128 129 130 131 132
  /* 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
133
  unsigned long end  = 1 << VALBITS; /* 256MB */
134
  void *ptr = NULL;
135

136 137 138 139 140 141 142 143 144
  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 */
    }
145

146
  return ptr;
147 148 149
}


Richard M. Stallman's avatar
Richard M. Stallman committed
150 151 152 153 154 155
/* Emulate Unix sbrk.  */
void *
sbrk (unsigned long increment)
{
  void *result;
  long size = (long) increment;
156

Richard M. Stallman's avatar
Richard M. Stallman committed
157
  result = data_region_end;
158

Richard M. Stallman's avatar
Richard M. Stallman committed
159
  /* If size is negative, shrink the heap by decommitting pages.  */
160
  if (size < 0)
Richard M. Stallman's avatar
Richard M. Stallman committed
161
    {
Geoff Voelker's avatar
Geoff Voelker committed
162 163 164
      int new_size;
      unsigned char *new_data_region_end;

Richard M. Stallman's avatar
Richard M. Stallman committed
165 166 167 168 169 170
      size = -size;

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

171
      /* We can only decommit full pages, so allow for
Geoff Voelker's avatar
Geoff Voelker committed
172 173 174 175 176 177
	 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;
178
      if (new_size > 0)
Geoff Voelker's avatar
Geoff Voelker committed
179 180
	{
	  /* Decommit size bytes from the end of the heap.  */
181 182
	  if (using_dynamic_heap
	      && !VirtualFree (real_data_region_end, new_size, MEM_DECOMMIT))
Geoff Voelker's avatar
Geoff Voelker committed
183 184
	    return NULL;
 	}
Richard M. Stallman's avatar
Richard M. Stallman committed
185 186

      data_region_end -= size;
187
    }
Richard M. Stallman's avatar
Richard M. Stallman committed
188
  /* If size is positive, grow the heap by committing reserved pages.  */
189
  else if (size > 0)
Richard M. Stallman's avatar
Richard M. Stallman committed
190 191 192 193 194 195 196
    {
      /* Sanity checks.  */
      if ((data_region_end + size) >
	  (data_region_base + get_reserved_heap_size ()))
	return NULL;

      /* Commit more of our heap. */
197 198 199
      if (using_dynamic_heap
	  && VirtualAlloc (data_region_end, size, MEM_COMMIT,
			   PAGE_READWRITE) == NULL)
Richard M. Stallman's avatar
Richard M. Stallman committed
200 201
	return NULL;
      data_region_end += size;
Geoff Voelker's avatar
Geoff Voelker committed
202 203 204 205 206

      /* 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
207
    }
208

Richard M. Stallman's avatar
Richard M. Stallman committed
209 210 211
  return result;
}

212 213 214 215 216 217 218 219 220 221 222 223
/* 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
224
void
225
init_heap ()
Richard M. Stallman's avatar
Richard M. Stallman committed
226
{
227 228 229 230
  PIMAGE_DOS_HEADER dos_header;
  PIMAGE_NT_HEADERS nt_header;

  dos_header = (PIMAGE_DOS_HEADER) RVA_TO_PTR (0);
231
  nt_header = (PIMAGE_NT_HEADERS) (((unsigned long) dos_header) +
232 233 234 235 236 237 238 239 240 241 242 243
				   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);
	}

244
#if defined (NO_UNION_TYPE) && !defined (USE_LSB_TAG)
245 246
      /* Ensure that the addresses don't use the upper tag bits since
	 the Lisp type goes there.  */
247
      if (((unsigned long) data_region_base & ~VALMASK) != 0)
248 249 250 251
	{
	  printf ("Error: The heap was allocated in upper memory.\n");
	  exit (1);
	}
252
#endif
253 254 255 256 257 258 259 260 261 262
      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
263 264 265

  /* Update system version information to match current system.  */
  cache_system_info ();
Richard M. Stallman's avatar
Richard M. Stallman committed
266 267 268 269 270 271 272 273
}

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

275
  needs_to_be = (unsigned long) ROUND_UP (get_heap_end (), align);
Richard M. Stallman's avatar
Richard M. Stallman committed
276
  need_to_alloc = needs_to_be - (unsigned long) get_heap_end ();
277 278

  if (need_to_alloc)
Richard M. Stallman's avatar
Richard M. Stallman committed
279 280
    sbrk (need_to_alloc);
}
Geoff Voelker's avatar
Geoff Voelker committed
281

282
#if (_MSC_VER >= 1000 && _MSC_VER < 1300 && !defined(USE_CRT_DLL))
Geoff Voelker's avatar
Geoff Voelker committed
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303

/* 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
Miles Bader's avatar
Miles Bader committed
304 305 306

/* arch-tag: 9a6a9860-040d-422d-8905-450dd535cd9c
   (do not change this comment) */