X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/95ed00257adf283a2740cb3e4feb83c075620ef8..1ede3eb64ba10005fd1b3fd9d7d5e81561c99c55:/src/w32heap.c diff --git a/src/w32heap.c b/src/w32heap.c index aac860e769..4298e6f70a 100644 --- a/src/w32heap.c +++ b/src/w32heap.c @@ -1,48 +1,61 @@ -/* Heap management routines for GNU Emacs on Windows NT. - Copyright (C) 1994 Free Software Foundation, Inc. +/* Heap management routines for GNU Emacs on the Microsoft W32 API. + Copyright (C) 1994, 2001, 2002, 2003, 2004, 2005, 2006, 2007, + 2008 Free Software Foundation, Inc. - This file is part of GNU Emacs. +This file is part of GNU Emacs. - 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. +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 3 of the License, or +(at your option) any later version. - 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. +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. - 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, 675 Mass Ave, Cambridge, MA 02139, USA. +You should have received a copy of the GNU General Public License +along with GNU Emacs. If not, see . */ +/* Geoff Voelker (voelker@cs.washington.edu) 7-29-94 */ +#ifdef HAVE_CONFIG_H +#include +#endif + #include #include -#include "ntheap.h" +#include "w32heap.h" +#include "lisp.h" /* for VALMASK */ + +#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; -/* These are defined to get Emacs to compile, but are not used. */ -int edata; -int etext; +/* This gives us version, build, and platform identification. */ +OSVERSIONINFO osinfo_cache; + +unsigned long syspage_mask = 0; /* 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; @@ -53,29 +66,43 @@ cache_system_info (void) /* 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 long data_region_size = 0; +unsigned char *real_data_region_end = NULL; +unsigned long reserved_heap_size = 0; /* The start of the data segment. */ unsigned char * @@ -91,96 +118,150 @@ get_data_end (void) return data_region_end; } +static char * +allocate_heap (void) +{ + /* 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; + + 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 */ + } + + return ptr; +} + + /* Emulate Unix sbrk. */ void * sbrk (unsigned long increment) { void *result; long size = (long) increment; - - /* Allocate our heap if we haven't done so already. */ - if (!data_region_base) - { - data_region_base = VirtualAlloc ((void *) get_data_region_base (), - get_reserved_heap_size (), - MEM_RESERVE, - PAGE_NOACCESS); - if (!data_region_base) - return NULL; - - /* Ensure that the addresses don't use the upper 8 bits since - the Lisp type goes there (yucko). */ - if (((unsigned long) data_region_base & 0xFF000000) != 0) - { - printf ("Error: The heap was allocated in upper memory.\n"); - exit (1); - } - data_region_end = data_region_base; - 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; + size = -size; /* Sanity checks. */ - if (size % get_page_size () != 0) - return NULL; if ((data_region_end - size) < data_region_base) return NULL; - /* Decommit size bytes from the end of the heap. */ - if (!VirtualFree (data_region_end - size, size, MEM_DECOMMIT)) - return NULL; + /* 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) + { + /* Decommit size bytes from the end of the heap. */ + 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 (size % get_page_size () != 0) - return NULL; if ((data_region_end + size) > (data_region_base + get_reserved_heap_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; + + /* 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); } - + 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 (USE_LISP_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. */ @@ -189,10 +270,36 @@ round_heap (unsigned long align) { 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) */