w32heap.c 7.57 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_PTR)(rva) + (DWORD_PTR)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

/* This gives us version, build, and platform identification.  */
Daniel Colascione's avatar
Daniel Colascione committed
35
extern unsigned long syspage_mask;
36 37
OSVERSIONINFO osinfo_cache;

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

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

46 47 48 49 50 51 52
/* Emulate getpagesize.  */
int
getpagesize (void)
{
  return sysinfo_cache.dwPageSize;
}

53 54 55
/* 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
56 57 58 59

/* 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
60
unsigned char *real_data_region_end = NULL;
61
size_t  reserved_heap_size = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76

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

77
#if !USE_LSB_TAG
78 79 80
static char *
allocate_heap (void)
{
81 82 83 84
  /* 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.  */
85
  DWORD_PTR base =
86 87 88
    ROUND_UP ((RVA_TO_PTR (preload_heap_section->VirtualAddress)
	       + preload_heap_section->Misc.VirtualSize),
	      get_allocation_unit ());
89
  DWORD_PTR end  = ((unsigned __int64)1) << VALBITS; /* 256MB */
90
  void *ptr = NULL;
91

92 93
  while (!ptr && (base < end))
    {
94 95 96
#ifdef _WIN64
      reserved_heap_size = min(end - base, 0x4000000000i64); /* Limit to 256Gb */
#else
97
      reserved_heap_size = end - base;
98
#endif
99 100 101 102 103 104
      ptr = VirtualAlloc ((void *) base,
			  get_reserved_heap_size (),
			  MEM_RESERVE,
			  PAGE_NOACCESS);
      base += 0x00100000;  /* 1MB increment */
    }
105

106
  return ptr;
107
}
108
#else  /* USE_LSB_TAG */
109 110 111
static char *
allocate_heap (void)
{
112 113 114 115 116
#ifdef _WIN64
  size_t size = 0x4000000000i64; /* start by asking for 32GB */
#else
  size_t size = 0x80000000; /* start by asking for 2GB */
#endif
117 118 119 120 121 122 123 124 125 126 127 128 129 130
  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;
}
131
#endif /* USE_LSB_TAG */
132 133


134 135 136
/* 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
137
void *
138
sbrk (ptrdiff_t increment)
Richard M. Stallman's avatar
Richard M. Stallman committed
139 140
{
  void *result;
141
  ptrdiff_t size = increment;
142

Richard M. Stallman's avatar
Richard M. Stallman committed
143
  result = data_region_end;
144

Richard M. Stallman's avatar
Richard M. Stallman committed
145
  /* If size is negative, shrink the heap by decommitting pages.  */
146
  if (size < 0)
Richard M. Stallman's avatar
Richard M. Stallman committed
147
    {
148
      ptrdiff_t new_size;
Geoff Voelker's avatar
Geoff Voelker committed
149 150
      unsigned char *new_data_region_end;

Richard M. Stallman's avatar
Richard M. Stallman committed
151 152 153 154 155 156
      size = -size;

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

157
      /* We can only decommit full pages, so allow for
Geoff Voelker's avatar
Geoff Voelker committed
158 159 160
	 partial deallocation [cga].  */
      new_data_region_end = (data_region_end - size);
      new_data_region_end = (unsigned char *)
161
	((DWORD_PTR) (new_data_region_end + syspage_mask) & ~syspage_mask);
Geoff Voelker's avatar
Geoff Voelker committed
162 163
      new_size = real_data_region_end - new_data_region_end;
      real_data_region_end = new_data_region_end;
164
      if (new_size > 0)
Geoff Voelker's avatar
Geoff Voelker committed
165 166
	{
	  /* Decommit size bytes from the end of the heap.  */
167 168
	  if (using_dynamic_heap
	      && !VirtualFree (real_data_region_end, new_size, MEM_DECOMMIT))
Geoff Voelker's avatar
Geoff Voelker committed
169 170
	    return NULL;
 	}
Richard M. Stallman's avatar
Richard M. Stallman committed
171 172

      data_region_end -= size;
173
    }
Richard M. Stallman's avatar
Richard M. Stallman committed
174
  /* If size is positive, grow the heap by committing reserved pages.  */
175
  else if (size > 0)
Richard M. Stallman's avatar
Richard M. Stallman committed
176 177 178 179 180 181 182
    {
      /* Sanity checks.  */
      if ((data_region_end + size) >
	  (data_region_base + get_reserved_heap_size ()))
	return NULL;

      /* Commit more of our heap. */
183 184 185
      if (using_dynamic_heap
	  && VirtualAlloc (data_region_end, size, MEM_COMMIT,
			   PAGE_READWRITE) == NULL)
Richard M. Stallman's avatar
Richard M. Stallman committed
186 187
	return NULL;
      data_region_end += size;
Geoff Voelker's avatar
Geoff Voelker committed
188 189 190 191

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

Richard M. Stallman's avatar
Richard M. Stallman committed
195 196 197
  return result;
}

198 199 200 201 202 203 204 205 206 207 208 209
/* 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
210
void
211
init_heap (void)
Richard M. Stallman's avatar
Richard M. Stallman committed
212
{
213 214 215 216
  PIMAGE_DOS_HEADER dos_header;
  PIMAGE_NT_HEADERS nt_header;

  dos_header = (PIMAGE_DOS_HEADER) RVA_TO_PTR (0);
217
  nt_header = (PIMAGE_NT_HEADERS) (((DWORD_PTR) dos_header) +
218 219 220 221 222 223 224 225 226 227 228 229
				   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);
	}

230
#if !USE_LSB_TAG
231 232
      /* Ensure that the addresses don't use the upper tag bits since
	 the Lisp type goes there.  */
233
      if (((DWORD_PTR) data_region_base & ~VALMASK) != 0)
234 235 236 237
	{
	  printf ("Error: The heap was allocated in upper memory.\n");
	  exit (1);
	}
238
#endif
239 240 241 242 243 244 245 246 247 248
      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
249 250 251

  /* Update system version information to match current system.  */
  cache_system_info ();
Richard M. Stallman's avatar
Richard M. Stallman committed
252 253 254 255
}

/* Round the heap up to the given alignment.  */
void
256
round_heap (size_t align)
Richard M. Stallman's avatar
Richard M. Stallman committed
257
{
258 259
  DWORD_PTR needs_to_be;
  DWORD_PTR need_to_alloc;
260

261 262
  needs_to_be = (DWORD_PTR) ROUND_UP (get_heap_end (), align);
  need_to_alloc = needs_to_be - (DWORD_PTR) get_heap_end ();
263 264

  if (need_to_alloc)
Richard M. Stallman's avatar
Richard M. Stallman committed
265 266
    sbrk (need_to_alloc);
}