1/* Heap management routines for GNU Emacs on the Microsoft W32 API.
2   Copyright (C) 1994, 2001, 2002, 2003, 2004, 2005,
3                 2006, 2007  Free Software Foundation, Inc.
4
5This file is part of GNU Emacs.
6
7GNU Emacs is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2, or (at your option)
10any later version.
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Emacs; see the file COPYING.  If not, write to
19the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20Boston, MA 02110-1301, USA.
21
22   Geoff Voelker (voelker@cs.washington.edu)			     7-29-94
23*/
24
25#ifdef HAVE_CONFIG_H
26#include <config.h>
27#endif
28
29#include <stdlib.h>
30#include <stdio.h>
31
32#include "w32heap.h"
33#include "lisp.h"  /* for VALMASK */
34
35#define RVA_TO_PTR(rva) ((unsigned char *)((DWORD)(rva) + (DWORD)GetModuleHandle (NULL)))
36
37/* This gives us the page size and the size of the allocation unit on NT.  */
38SYSTEM_INFO sysinfo_cache;
39
40/* This gives us version, build, and platform identification.  */
41OSVERSIONINFO osinfo_cache;
42
43unsigned long syspage_mask = 0;
44
45/* The major and minor versions of NT.  */
46int w32_major_version;
47int w32_minor_version;
48int w32_build_number;
49
50/* Distinguish between Windows NT and Windows 95.  */
51int os_subtype;
52
53/* Cache information describing the NT system for later use.  */
54void
55cache_system_info (void)
56{
57  union
58    {
59      struct info
60	{
61	  char  major;
62	  char  minor;
63	  short platform;
64	} info;
65      DWORD data;
66    } version;
67
68  /* Cache the version of the operating system.  */
69  version.data = GetVersion ();
70  w32_major_version = version.info.major;
71  w32_minor_version = version.info.minor;
72
73  if (version.info.platform & 0x8000)
74    os_subtype = OS_WIN95;
75  else
76    os_subtype = OS_NT;
77
78  /* Cache page size, allocation unit, processor type, etc.  */
79  GetSystemInfo (&sysinfo_cache);
80  syspage_mask = sysinfo_cache.dwPageSize - 1;
81
82  /* Cache os info.  */
83  osinfo_cache.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
84  GetVersionEx (&osinfo_cache);
85
86  w32_build_number = osinfo_cache.dwBuildNumber;
87  if (os_subtype == OS_WIN95)
88    w32_build_number &= 0xffff;
89}
90
91/* Emulate getpagesize.  */
92int
93getpagesize (void)
94{
95  return sysinfo_cache.dwPageSize;
96}
97
98/* Info for managing our preload heap, which is essentially a fixed size
99   data area in the executable.  */
100PIMAGE_SECTION_HEADER preload_heap_section;
101
102/* Info for keeping track of our heap.  */
103unsigned char *data_region_base = NULL;
104unsigned char *data_region_end = NULL;
105unsigned char *real_data_region_end = NULL;
106unsigned long  reserved_heap_size = 0;
107
108/* The start of the data segment.  */
109unsigned char *
110get_data_start (void)
111{
112  return data_region_base;
113}
114
115/* The end of the data segment.  */
116unsigned char *
117get_data_end (void)
118{
119  return data_region_end;
120}
121
122static char *
123allocate_heap (void)
124{
125  /* Try to get as much as possible of the address range from the end of
126     the preload heap section up to the usable address limit.  Since GNU
127     malloc can handle gaps in the memory it gets from sbrk, we can
128     simply set the sbrk pointer to the base of the new heap region.  */
129  unsigned long base =
130    ROUND_UP ((RVA_TO_PTR (preload_heap_section->VirtualAddress)
131	       + preload_heap_section->Misc.VirtualSize),
132	      get_allocation_unit ());
133  unsigned long end  = 1 << VALBITS; /* 256MB */
134  void *ptr = NULL;
135
136  while (!ptr && (base < end))
137    {
138      reserved_heap_size = end - base;
139      ptr = VirtualAlloc ((void *) base,
140			  get_reserved_heap_size (),
141			  MEM_RESERVE,
142			  PAGE_NOACCESS);
143      base += 0x00100000;  /* 1MB increment */
144    }
145
146  return ptr;
147}
148
149
150/* Emulate Unix sbrk.  */
151void *
152sbrk (unsigned long increment)
153{
154  void *result;
155  long size = (long) increment;
156
157  result = data_region_end;
158
159  /* If size is negative, shrink the heap by decommitting pages.  */
160  if (size < 0)
161    {
162      int new_size;
163      unsigned char *new_data_region_end;
164
165      size = -size;
166
167      /* Sanity checks.  */
168      if ((data_region_end - size) < data_region_base)
169	return NULL;
170
171      /* We can only decommit full pages, so allow for
172	 partial deallocation [cga].  */
173      new_data_region_end = (data_region_end - size);
174      new_data_region_end = (unsigned char *)
175	((long) (new_data_region_end + syspage_mask) & ~syspage_mask);
176      new_size = real_data_region_end - new_data_region_end;
177      real_data_region_end = new_data_region_end;
178      if (new_size > 0)
179	{
180	  /* Decommit size bytes from the end of the heap.  */
181	  if (using_dynamic_heap
182	      && !VirtualFree (real_data_region_end, new_size, MEM_DECOMMIT))
183	    return NULL;
184 	}
185
186      data_region_end -= size;
187    }
188  /* If size is positive, grow the heap by committing reserved pages.  */
189  else if (size > 0)
190    {
191      /* Sanity checks.  */
192      if ((data_region_end + size) >
193	  (data_region_base + get_reserved_heap_size ()))
194	return NULL;
195
196      /* Commit more of our heap. */
197      if (using_dynamic_heap
198	  && VirtualAlloc (data_region_end, size, MEM_COMMIT,
199			   PAGE_READWRITE) == NULL)
200	return NULL;
201      data_region_end += size;
202
203      /* We really only commit full pages, so record where
204	 the real end of committed memory is [cga].  */
205      real_data_region_end = (unsigned char *)
206	  ((long) (data_region_end + syspage_mask) & ~syspage_mask);
207    }
208
209  return result;
210}
211
212/* Initialize the internal heap variables used by sbrk.  When running in
213   preload phase (ie. in the undumped executable), we rely entirely on a
214   fixed size heap section included in the .exe itself; this is
215   preserved during dumping, and truncated to the size actually used.
216
217   When running in the dumped executable, we reserve as much as possible
218   of the address range that is addressable by Lisp object pointers, to
219   supplement what is left of the preload heap.  Although we cannot rely
220   on the dynamically allocated arena being contiguous with the static
221   heap area, it is not a problem because sbrk can pretend that the gap
222   was allocated by something else; GNU malloc detects when there is a
223   jump in the sbrk values, and starts a new heap block.  */
224void
225init_heap ()
226{
227  PIMAGE_DOS_HEADER dos_header;
228  PIMAGE_NT_HEADERS nt_header;
229
230  dos_header = (PIMAGE_DOS_HEADER) RVA_TO_PTR (0);
231  nt_header = (PIMAGE_NT_HEADERS) (((unsigned long) dos_header) +
232				   dos_header->e_lfanew);
233  preload_heap_section = find_section ("EMHEAP", nt_header);
234
235  if (using_dynamic_heap)
236    {
237      data_region_base = allocate_heap ();
238      if (!data_region_base)
239	{
240	  printf ("Error: Could not reserve dynamic heap area.\n");
241	  exit (1);
242	}
243
244#if defined (NO_UNION_TYPE) && !defined (USE_LSB_TAG)
245      /* Ensure that the addresses don't use the upper tag bits since
246	 the Lisp type goes there.  */
247      if (((unsigned long) data_region_base & ~VALMASK) != 0)
248	{
249	  printf ("Error: The heap was allocated in upper memory.\n");
250	  exit (1);
251	}
252#endif
253      data_region_end = data_region_base;
254      real_data_region_end = data_region_end;
255    }
256  else
257    {
258      data_region_base = RVA_TO_PTR (preload_heap_section->VirtualAddress);
259      data_region_end = data_region_base;
260      real_data_region_end = data_region_end;
261      reserved_heap_size = preload_heap_section->Misc.VirtualSize;
262    }
263
264  /* Update system version information to match current system.  */
265  cache_system_info ();
266}
267
268/* Round the heap up to the given alignment.  */
269void
270round_heap (unsigned long align)
271{
272  unsigned long needs_to_be;
273  unsigned long need_to_alloc;
274
275  needs_to_be = (unsigned long) ROUND_UP (get_heap_end (), align);
276  need_to_alloc = needs_to_be - (unsigned long) get_heap_end ();
277
278  if (need_to_alloc)
279    sbrk (need_to_alloc);
280}
281
282#if (_MSC_VER >= 1000 && _MSC_VER < 1300 && !defined(USE_CRT_DLL))
283
284/* MSVC 4.2 invokes these functions from mainCRTStartup to initialize
285   a heap via HeapCreate.  They are normally defined by the runtime,
286   but we override them here so that the unnecessary HeapCreate call
287   is not performed.  */
288
289int __cdecl
290_heap_init (void)
291{
292  /* Stepping through the assembly indicates that mainCRTStartup is
293     expecting a nonzero success return value.  */
294  return 1;
295}
296
297void __cdecl
298_heap_term (void)
299{
300  return;
301}
302
303#endif
304
305/* arch-tag: 9a6a9860-040d-422d-8905-450dd535cd9c
306   (do not change this comment) */
307