X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/e54c8cd1194ff791d17c7932a6080dda0838d7c8..0925c80cd3d8f9a973d699fc1dbdbe79cca62988:/src/w32heap.c diff --git a/src/w32heap.c b/src/w32heap.c index 8565999b45..a0c50da550 100644 --- a/src/w32heap.c +++ b/src/w32heap.c @@ -1,5 +1,5 @@ /* Heap management routines for GNU Emacs on the Microsoft W32 API. - Copyright (C) 1994 Free Software Foundation, Inc. + Copyright (C) 1994, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -15,13 +15,15 @@ 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, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. Geoff Voelker (voelker@cs.washington.edu) 7-29-94 */ -#include "config.h" +#ifdef HAVE_CONFIG_H +#include +#endif #include #include @@ -29,8 +31,14 @@ Boston, MA 02111-1307, USA. #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; + +/* 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. */ @@ -40,14 +48,18 @@ int etext; /* The major and minor versions of NT. */ 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; @@ -61,9 +73,22 @@ cache_system_info (void) 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; + + /* Cache os info. */ + osinfo_cache.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); + GetVersionEx (&osinfo_cache); + + w32_build_number = osinfo_cache.dwBuildNumber; + if (os_subtype == OS_WIN95) + w32_build_number &= 0xffff; } /* Emulate getpagesize. */ @@ -73,27 +98,14 @@ getpagesize (void) return sysinfo_cache.dwPageSize; } -/* Round ADDRESS up to be aligned with ALIGN. */ -unsigned char * -round_to_next (unsigned char *address, unsigned long align) -{ - unsigned long tmp; - - tmp = (unsigned long) address; - tmp = (tmp + align - 1) / align; - - return (unsigned char *) (tmp * align); -} - -/* Force zero initialized variables to be placed in the .data segment; - MSVC 5.0 otherwise places them in .bss, which breaks the dumping code. */ -#pragma data_seg(".data") +/* 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. */ @@ -113,48 +125,17 @@ get_data_end (void) 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 Windows 95 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 Windows 95 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, - Windows 95 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; -#if NTHEAP_PROBE_BASE /* This is never normally defined */ - /* Try various addresses looking for one the kernel will let us have. */ while (!ptr && (base < end)) { reserved_heap_size = end - base; @@ -164,13 +145,6 @@ allocate_heap (void) PAGE_NOACCESS); base += 0x00100000; /* 1MB increment */ } -#else - reserved_heap_size = end - base; - ptr = VirtualAlloc ((void *) base, - get_reserved_heap_size (), - MEM_RESERVE, - PAGE_NOACCESS); -#endif return ptr; } @@ -182,31 +156,11 @@ 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 = 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; @@ -217,24 +171,25 @@ sbrk (unsigned long increment) 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) > @@ -242,8 +197,9 @@ sbrk (unsigned long increment) 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; @@ -252,32 +208,64 @@ sbrk (unsigned long increment) 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. */ @@ -286,10 +274,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) */