-/* Heap management routines for GNU Emacs on Windows NT.
+/* Heap management routines for GNU Emacs on the Microsoft W32 API.
Copyright (C) 1994 Free Software Foundation, Inc.
This file is part of GNU Emacs.
Geoff Voelker (voelker@cs.washington.edu) 7-29-94
*/
-#include "config.h"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <stdlib.h>
#include <stdio.h>
-#include "ntheap.h"
+#include "w32heap.h"
#include "lisp.h" /* for VALMASK */
+#undef RVA_TO_PTR
+#define RVA_TO_PTR(rva) ((unsigned char *)((DWORD)(rva) + (DWORD)GetModuleHandle (NULL)))
+
/* This gives us the page size and the size of the allocation unit on NT. */
SYSTEM_INFO sysinfo_cache;
+
+/* This gives us version, build, and platform identification. */
+OSVERSIONINFO osinfo_cache;
+
unsigned long syspage_mask = 0;
/* These are defined to get Emacs to compile, but are not used. */
int etext;
/* The major and minor versions of NT. */
-int nt_major_version;
-int nt_minor_version;
+int w32_major_version;
+int w32_minor_version;
+int w32_build_number;
+
+/* Distinguish between Windows NT and Windows 95. */
+int os_subtype;
/* Cache information describing the NT system for later use. */
void
cache_system_info (void)
{
- union
+ union
{
- struct info
+ struct info
{
char major;
char minor;
/* Cache the version of the operating system. */
version.data = GetVersion ();
- nt_major_version = version.info.major;
- nt_minor_version = version.info.minor;
+ w32_major_version = version.info.major;
+ w32_minor_version = version.info.minor;
+
+ if (version.info.platform & 0x8000)
+ os_subtype = OS_WIN95;
+ else
+ os_subtype = OS_NT;
/* Cache page size, allocation unit, processor type, etc. */
GetSystemInfo (&sysinfo_cache);
syspage_mask = sysinfo_cache.dwPageSize - 1;
-}
-/* Round ADDRESS up to be aligned with ALIGN. */
-unsigned char *
-round_to_next (unsigned char *address, unsigned long align)
-{
- unsigned long tmp;
+ /* Cache os info. */
+ osinfo_cache.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
+ GetVersionEx (&osinfo_cache);
- tmp = (unsigned long) address;
- tmp = (tmp + align - 1) / align;
+ w32_build_number = osinfo_cache.dwBuildNumber;
+ if (os_subtype == OS_WIN95)
+ w32_build_number &= 0xffff;
+}
- return (unsigned char *) (tmp * align);
+/* Emulate getpagesize. */
+int
+getpagesize (void)
+{
+ return sysinfo_cache.dwPageSize;
}
+/* Info for managing our preload heap, which is essentially a fixed size
+ data area in the executable. */
+PIMAGE_SECTION_HEADER preload_heap_section;
+
/* Info for keeping track of our heap. */
unsigned char *data_region_base = NULL;
unsigned char *data_region_end = NULL;
unsigned char *real_data_region_end = NULL;
-unsigned long data_region_size = 0;
unsigned long reserved_heap_size = 0;
/* The start of the data segment. */
static char *
allocate_heap (void)
{
- /* 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
- and Win95 arrange the virtual address space for a process (and on
- 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
- of the process address space on Win95 for the primary thread stack,
- 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.
-
- Thus we would like to set the malloc heap base to 20MB. However,
- Win95 refuses to allocate the heap starting at this address, so we
- set the base to 27MB to make it happy. Since Emacs now leaves
- 28 bits available for pointers, this lets us use the remainder of
- the region below the 256MB line for our malloc arena - 229MB is
- still a pretty decent arena to play in! */
-
- unsigned long base = 0x01B00000; /* 27MB */
+ /* 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 ());
unsigned long end = 1 << VALBITS; /* 256MB */
void *ptr = NULL;
-#ifdef NTHEAP_PROBE_BASE
while (!ptr && (base < end))
{
-#endif
reserved_heap_size = end - base;
ptr = VirtualAlloc ((void *) base,
get_reserved_heap_size (),
MEM_RESERVE,
PAGE_NOACCESS);
-#ifdef NTHEAP_PROBE_BASE
base += 0x00100000; /* 1MB increment */
}
-#endif
+
return ptr;
}
{
void *result;
long size = (long) increment;
-
- /* Allocate our heap if we haven't done so already. */
- if (!data_region_base)
- {
- data_region_base = allocate_heap ();
- if (!data_region_base)
- return NULL;
- /* 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;
- data_region_size = get_reserved_heap_size ();
- }
-
result = data_region_end;
-
+
/* If size is negative, shrink the heap by decommitting pages. */
- if (size < 0)
+ if (size < 0)
{
int new_size;
unsigned char *new_data_region_end;
if ((data_region_end - size) < data_region_base)
return NULL;
- /* We can only decommit full pages, so allow for
+ /* 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)
+ if (new_size > 0)
{
/* Decommit size bytes from the end of the heap. */
- if (!VirtualFree (real_data_region_end, new_size, MEM_DECOMMIT))
+ if (using_dynamic_heap
+ && !VirtualFree (real_data_region_end, new_size, MEM_DECOMMIT))
return NULL;
}
data_region_end -= size;
- }
+ }
/* If size is positive, grow the heap by committing reserved pages. */
- else if (size > 0)
+ else if (size > 0)
{
/* Sanity checks. */
if ((data_region_end + size) >
return NULL;
/* Commit more of our heap. */
- if (VirtualAlloc (data_region_end, size, MEM_COMMIT,
- PAGE_READWRITE) == NULL)
+ if (using_dynamic_heap
+ && VirtualAlloc (data_region_end, size, MEM_COMMIT,
+ PAGE_READWRITE) == NULL)
return NULL;
data_region_end += size;
real_data_region_end = (unsigned char *)
((long) (data_region_end + syspage_mask) & ~syspage_mask);
}
-
+
return result;
}
-/* Recreate the heap from the data that was dumped to the executable.
- EXECUTABLE_PATH tells us where to find the executable. */
+/* 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. */
void
-recreate_heap (char *executable_path)
+init_heap ()
{
- 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);
+ 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);
+ }
+
+#if defined (NO_UNION_TYPE) && !defined (USE_LSB_TAG)
+ /* 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);
+ }
+#endif
+ 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;
+ }
+
+ /* Update system version information to match current system. */
+ cache_system_info ();
}
/* Round the heap up to the given alignment. */
{
unsigned long needs_to_be;
unsigned long need_to_alloc;
-
- needs_to_be = (unsigned long) round_to_next (get_heap_end (), align);
+
+ needs_to_be = (unsigned long) ROUND_UP (get_heap_end (), align);
need_to_alloc = needs_to_be - (unsigned long) get_heap_end ();
-
- if (need_to_alloc)
+
+ if (need_to_alloc)
sbrk (need_to_alloc);
}
+
+#if (_MSC_VER >= 1000 && _MSC_VER < 1300 && !defined(USE_CRT_DLL))
+
+/* 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
+
+/* arch-tag: 9a6a9860-040d-422d-8905-450dd535cd9c
+ (do not change this comment) */