From: Fabrice Popineau Date: Tue, 27 May 2014 17:31:17 +0000 (+0300) Subject: Use mmap(2) emulation for buffer text on MS-Windows. X-Git-Url: https://git.hcoop.net/bpt/emacs.git/commitdiff_plain/587fd086a045f715932f886ecf31015932464ce6 Use mmap(2) emulation for buffer text on MS-Windows. src/Makefile.in (C_HEAP_SWITCH): Get the predefined heap size from configure. (ADDSECTION, MINGW_TEMACS_POST_LINK): Remove, no longer used. src/lisp.h (NONPOINTER_BITS): Modify the condition to define to zero for MinGW, since it no longer uses gmalloc. src/buffer.c: Do not define mmap allocations functions for Windows. Remove mmap_find which is unused. Remove mmap_set_vars which does nothing useful. [WINDOWSNT]: Include w32heap.h. (init_buffer): Always allocate new memory for buffers. src/emacs.c: Remove mmap_set_vars calls. src/image.c (free_image): Undef free for Windows because it is redirected to our private version. src/unexw32.c (COPY_PROC_CHUNK): Use %p format for 64bits compatibility. (copy_executable_and_dump_data): Remove dumping the heap section. (unexec): Restore using_dynamic_heap after dumping. src/w32heap.c (dumped_data_commit, malloc_after_dump) (malloc_before_dump, realloc_after_dump, realloc_before_dump) (free_after_dump, free_before_dump, mmap_alloc, mmap_realloc) (mmap_free): New functions. src/w32heap.h: Declare dumped_data and mmap_* function prototypes. nt/inc/ms-w32.h: Switch to the system heap allocation scheme instead of GNU malloc and ralloc. nt/inc/sys/mman.h: New file. nt/INSTALL: Update for the new build requirements. etc/NEWS: Mention build changes on MS-Windows. configure.ac (C_HEAP_SWITCH) define for different values of dumped heap size depending on 32/64bits arch on Windows. Don't check for pthreads.h on MinGW32/64, it gets in the way. Use mmap(2) for buffers and system malloc for MinGW32/64. --- diff --git a/ChangeLog b/ChangeLog index 28c877a18b..09f0618113 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2014-05-27 Fabrice Popineau + + * configure.ac (C_HEAP_SWITCH) define for different values of + dumped heap size depending on 32/64bits arch on Windows. + Don't check for pthreads.h on MinGW32/64, it gets in the way. + Use mmap(2) for buffers and system malloc for MinGW32/64. + 2014-05-27 Paul Eggert Merge from gnulib, incorporating: diff --git a/configure.ac b/configure.ac index d727c0253f..84f989f7f9 100644 --- a/configure.ac +++ b/configure.ac @@ -1973,7 +1973,7 @@ doug_lea_malloc=$emacs_cv_var_doug_lea_malloc system_malloc=$emacs_cv_sanitize_address case "$opsys" in ## darwin ld insists on the use of malloc routines in the System framework. - darwin|sol2-10) system_malloc=yes ;; + darwin|mingw32|sol2-10) system_malloc=yes ;; esac GMALLOC_OBJ= @@ -2020,7 +2020,7 @@ if test "$doug_lea_malloc" = "yes" ; then ## #ifdef DOUG_LEA_MALLOC; #undef REL_ALLOC; #endif ## Does the AC_FUNC_MMAP test below make this check unnecessary? case "$opsys" in - gnu*) REL_ALLOC=no ;; + mingw32|gnu*) REL_ALLOC=no ;; esac fi @@ -2030,7 +2030,7 @@ fi use_mmap_for_buffers=no case "$opsys" in - cygwin|freebsd|irix6-5) use_mmap_for_buffers=yes ;; + cygwin|mingw32|freebsd|irix6-5) use_mmap_for_buffers=yes ;; esac AC_FUNC_MMAP @@ -2046,6 +2046,7 @@ AC_CHECK_LIB(Xbsd, main, LD_SWITCH_X_SITE="$LD_SWITCH_X_SITE -lXbsd") dnl Check for the POSIX thread library. LIB_PTHREAD= +if test "$opsys" != "mingw32"; then AC_CHECK_HEADERS_ONCE(pthread.h) if test "$ac_cv_header_pthread_h"; then dnl gmalloc.c uses pthread_atfork, which is not available on older-style @@ -2066,6 +2067,7 @@ if test "$ac_cv_header_pthread_h"; then LIBS=$OLD_LIBS fi AC_SUBST([LIB_PTHREAD]) +fi dnl Check for need for bigtoc support on IBM AIX @@ -4817,11 +4819,9 @@ case "$opsys" in gnu*) LD_SWITCH_SYSTEM_TEMACS="\$(LD_SWITCH_X_SITE_RPATH)" ;; mingw32) - ## MinGW64 does not prepend an underscore to symbols, so we must - ## pass a different -entry switch to linker. FIXME: It is better - ## to make the entry points the same by changing unexw32.c. + ## Is it any better under MinGW64 to relocate emacs into higher addresses? case "$canonical" in - x86_64-*-*) LD_SWITCH_SYSTEM_TEMACS="-Wl,-stack,0x00800000 -Wl,-heap,0x00100000 -Wl,-image-base,0x01000000 -Wl,-entry,__start -Wl,-Map,./temacs.map" ;; + x86_64-*-*) LD_SWITCH_SYSTEM_TEMACS="-Wl,-stack,0x00800000 -Wl,-heap,0x00100000 -Wl,-image-base,0x400000000 -Wl,-entry,__start -Wl,-Map,./temacs.map" ;; *) LD_SWITCH_SYSTEM_TEMACS="-Wl,-stack,0x00800000 -Wl,-heap,0x00100000 -Wl,-image-base,0x01000000 -Wl,-entry,__start -Wl,-Map,./temacs.map" ;; esac ;; @@ -4845,20 +4845,20 @@ AC_SUBST(LD_SWITCH_SYSTEM_TEMACS) ## MinGW-specific post-link processing of temacs. TEMACS_POST_LINK=":" ADDSECTION= -EMACS_HEAPSIZE= +C_HEAP_SWITCH= if test "${opsys}" = "mingw32"; then TEMACS_POST_LINK="\$(MINGW_TEMACS_POST_LINK)" ADDSECTION="../nt/addsection\$(EXEEXT)" ## Preload heap size of temacs.exe in MB. case "$canonical" in - x86_64-*-*) EMACS_HEAPSIZE=42 ;; - *) EMACS_HEAPSIZE=27 ;; + x86_64-*-*) C_HEAP_SWITCH="-DHEAPSIZE=18" ;; + *) C_HEAP_SWITCH="-DHEAPSIZE=10" ;; esac fi AC_SUBST(ADDSECTION) AC_SUBST(TEMACS_POST_LINK) -AC_SUBST(EMACS_HEAPSIZE) +AC_SUBST(C_HEAP_SWITCH) ## Common for all window systems if test "$window_system" != "none"; then diff --git a/etc/ChangeLog b/etc/ChangeLog index 49be20a5f7..27ee804400 100644 --- a/etc/ChangeLog +++ b/etc/ChangeLog @@ -1,3 +1,7 @@ +2014-05-27 Fabrice Popineau + + * NEWS: Mention build changes on MS-Windows. + 2014-05-26 Paul Eggert Specify coding if Latin-1 Emacs would misinterpret (Bug#17575). diff --git a/etc/NEWS b/etc/NEWS index 3d76642fe5..20112451a3 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -40,6 +40,11 @@ or by sticking with Emacs 24.4. ** The configure option `--with-pkg-config-prog' has been removed. Use './configure PKG_CONFIG=/full/name/of/pkg-config' if you need to. +--- +** Building Emacs for MS-Windows requires at least Windows XP +or Windows Server 2003. The built binaries still run on all versions +of Windows starting with Windows 9X. + * Startup Changes in Emacs 24.5 diff --git a/nt/ChangeLog b/nt/ChangeLog index a3cb5e62da..961de329ad 100644 --- a/nt/ChangeLog +++ b/nt/ChangeLog @@ -1,3 +1,12 @@ +2014-05-27 Fabrice Popineau + + * inc/ms-w32.h: Switch to the system heap allocation scheme + instead of GNU malloc and ralloc. + + * inc/sys/mman.h: New file. + + * INSTALL: Update for the new build requirements. + 2014-05-17 Paul Eggert Assume C99 or later (Bug#17487). diff --git a/nt/INSTALL b/nt/INSTALL index 949a4e452c..4ecf4da318 100644 --- a/nt/INSTALL +++ b/nt/INSTALL @@ -5,9 +5,9 @@ See the end of the file for license conditions. The MSYS/MinGW build described here is supported on versions of -Windows starting with Windows 2000 and newer. Windows 9X are not -supported (but the Emacs binary produced by this build will run on -Windows 9X as well). +Windows starting with Windows XP and newer. Building on Windows 2000 +and Windows 9X is not supported (but the Emacs binary produced by this +build will run on Windows 9X and newer systems). Do not use this recipe with Cygwin. For building on Cygwin, use the normal installation instructions, ../INSTALL. @@ -389,9 +389,10 @@ Windows 9X as well). Where should the build process find the source code? /path/to/emacs/sources What compiler should emacs be built with? gcc -std=gnu99 -O0 -g3 - Should Emacs use the GNU version of malloc? yes - Should Emacs use a relocating allocator for buffers? yes - Should Emacs use mmap(2) for buffer allocation? no + Should Emacs use the GNU version of malloc? no + (The GNU allocators don't work with this system configuration.) + Should Emacs use a relocating allocator for buffers? no + Should Emacs use mmap(2) for buffer allocation? yes What window system should Emacs use? w32 What toolkit should Emacs use? none Where do we find X Windows header files? NONE @@ -401,13 +402,16 @@ Windows 9X as well). Does Emacs use -ljpeg? yes Does Emacs use -ltiff? yes Does Emacs use a gif library? yes - Does Emacs use -lpng? yes - Does Emacs use -lrsvg-2? no + Does Emacs use a png library? yes + Does Emacs use -lrsvg-2? yes Does Emacs use imagemagick? no + Does Emacs support sound? no Does Emacs use -lgpm? no Does Emacs use -ldbus? no Does Emacs use -lgconf? no Does Emacs use GSettings? no + Does Emacs use a file notification library? yes (w32) + Does Emacs use access control lists? yes Does Emacs use -lselinux? no Does Emacs use -lgnutls? yes Does Emacs use -lxml2? yes @@ -415,6 +419,7 @@ Windows 9X as well). Does Emacs use -lm17n-flt? no Does Emacs use -lotf? no Does Emacs use -lxft? no + Does Emacs directly use zlib? yes Does Emacs use toolkit scroll bars? yes You are almost there, hang on. diff --git a/nt/inc/ms-w32.h b/nt/inc/ms-w32.h index 8f7c36ab1e..1cf78bac83 100644 --- a/nt/inc/ms-w32.h +++ b/nt/inc/ms-w32.h @@ -140,6 +140,7 @@ extern char *getenv (); in its system headers, and is not really compatible with values lower than 0x0500, so leave it alone. */ #ifndef _W64 +# undef _WIN32_WINNT # define _WIN32_WINNT 0x0400 #endif @@ -427,20 +428,36 @@ extern char *get_emacs_configuration_options (void); #define _WINSOCK_H /* Defines size_t and alloca (). */ -#ifdef emacs -#define malloc e_malloc -#define free e_free -#define realloc e_realloc -#define calloc e_calloc -#endif +#include +#include #ifdef _MSC_VER #define alloca _alloca #else #include #endif -#include -#include +#ifdef emacs + +typedef void * (* malloc_fn)(size_t); +typedef void * (* realloc_fn)(void *, size_t); +typedef void (* free_fn)(void *); + +extern void *malloc_before_dump(size_t); +extern void *realloc_before_dump(void *, size_t); +extern void free_before_dump(void *); +extern void *malloc_after_dump(size_t); +extern void *realloc_after_dump(void *, size_t); +extern void free_after_dump(void *); + +extern malloc_fn the_malloc_fn; +extern realloc_fn the_realloc_fn; +extern free_fn the_free_fn; + +#define malloc(size) (*the_malloc_fn)(size) +#define free(ptr) (*the_free_fn)(ptr) +#define realloc(ptr, size) (*the_realloc_fn)(ptr, size) + +#endif /* Define for those source files that do not include enough NT system files. */ #ifndef NULL diff --git a/nt/inc/sys/mman.h b/nt/inc/sys/mman.h new file mode 100644 index 0000000000..6990edcb59 --- /dev/null +++ b/nt/inc/sys/mman.h @@ -0,0 +1,31 @@ +/* + * sys/mman.h + * mman-win32 + */ + +#ifndef _SYS_MMAN_H_ +#define _SYS_MMAN_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* We need MAP_ANON in src/buffer.c */ + +#define MAP_FILE 0 +#define MAP_SHARED 1 +#define MAP_PRIVATE 2 +#define MAP_TYPE 0xf +#define MAP_FIXED 0x10 +#define MAP_ANONYMOUS 0x20 +#define MAP_ANON MAP_ANONYMOUS + +#define MAP_FAILED ((void *)-1) + +#ifdef __cplusplus +}; +#endif + +#endif /* _SYS_MMAN_H_ */ diff --git a/src/ChangeLog b/src/ChangeLog index 4378cfce5d..959f29a267 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,35 @@ +2014-05-27 Fabrice Popineau + + * Makefile.in (C_HEAP_SWITCH): Get the predefined heap size from + configure. + (ADDSECTION, MINGW_TEMACS_POST_LINK): Remove, no longer used. + + * lisp.h (NONPOINTER_BITS): Modify the condition to define to zero + for MinGW, since it no longer uses gmalloc. + + * buffer.c: Do not define mmap allocations functions for Windows. + Remove mmap_find which is unused. Remove mmap_set_vars which does + nothing useful. + [WINDOWSNT]: Include w32heap.h. + (init_buffer): Always allocate new memory for buffers. + + * emacs.c: Remove mmap_set_vars calls. + + * image.c (free_image): Undef free for Windows because it is + redirected to our private version. + + * unexw32.c (COPY_PROC_CHUNK): Use %p format for 64bits + compatibility. + (copy_executable_and_dump_data): Remove dumping the heap section. + (unexec): Restore using_dynamic_heap after dumping. + + * w32heap.c (dumped_data_commit, malloc_after_dump) + (malloc_before_dump, realloc_after_dump, realloc_before_dump) + (free_after_dump, free_before_dump, mmap_alloc, mmap_realloc) + (mmap_free): New functions. + + * w32heap.h: Declare dumped_data and mmap_* function prototypes. + 2014-05-27 Paul Eggert * image.c (imagemagick_load_image): Use MagickRealType for local diff --git a/src/Makefile.in b/src/Makefile.in index b4e9eae330..0f4130b4da 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -86,6 +86,9 @@ PNG_CFLAGS=@PNG_CFLAGS@ ## something similar. This is normally set by configure. C_SWITCH_X_SITE=@C_SWITCH_X_SITE@ +## Set Emacs dumped heap size for Windows NT +C_HEAP_SWITCH=@C_HEAP_SWITCH@ + ## Define LD_SWITCH_X_SITE to contain any special flags your loader ## may need to deal with X Windows. For instance, if your X libraries ## aren't in a place that your loader can find on its own, you might @@ -300,11 +303,7 @@ RUN_TEMACS = ./temacs ## Invoke ../nt/addsection for MinGW, ":" elsewhere. TEMACS_POST_LINK = @TEMACS_POST_LINK@ -ADDSECTION = @ADDSECTION@ EMACS_HEAPSIZE = @EMACS_HEAPSIZE@ -MINGW_TEMACS_POST_LINK = \ - mv temacs$(EXEEXT) temacs.tmp; \ - ../nt/addsection temacs.tmp temacs$(EXEEXT) EMHEAP $(EMACS_HEAPSIZE) UNEXEC_OBJ = @UNEXEC_OBJ@ @@ -326,7 +325,7 @@ MKDEPDIR=@MKDEPDIR@ ## ## FIXME? MYCPPFLAGS only referenced in etc/DEBUG. ALL_CFLAGS=-Demacs $(MYCPPFLAGS) -I. -I$(srcdir) \ - -I$(lib) -I$(srcdir)/../lib \ + -I$(lib) -I$(srcdir)/../lib $(C_HEAP_SWITCH) \ $(C_SWITCH_MACHINE) $(C_SWITCH_SYSTEM) $(C_SWITCH_X_SITE) \ $(GNUSTEP_CFLAGS) $(CFLAGS_SOUND) $(RSVG_CFLAGS) $(IMAGEMAGICK_CFLAGS) \ $(PNG_CFLAGS) $(LIBXML2_CFLAGS) $(DBUS_CFLAGS) \ diff --git a/src/buffer.c b/src/buffer.c index 1f0bd3f097..3cbb8153bc 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -41,6 +41,10 @@ along with GNU Emacs. If not, see . */ #include "keymap.h" #include "frame.h" +#ifdef WINDOWSNT +#include "w32heap.h" /* for mmap_* */ +#endif + struct buffer *current_buffer; /* The current buffer. */ /* First buffer in chain of all buffers (in reverse order of creation). @@ -4632,7 +4636,8 @@ evaporate_overlays (ptrdiff_t pos) Allocation with mmap ***********************************************************************/ -#ifdef USE_MMAP_FOR_BUFFERS +/* Note: WINDOWSNT implements this stuff on w32heap.c. */ +#if defined USE_MMAP_FOR_BUFFERS && !defined WINDOWSNT #include @@ -4774,36 +4779,6 @@ mmap_init (void) mmap_page_size = getpagesize (); } -/* Return a region overlapping address range START...END, or null if - none. END is not including, i.e. the last byte in the range - is at END - 1. */ - -static struct mmap_region * -mmap_find (void *start, void *end) -{ - struct mmap_region *r; - char *s = start, *e = end; - - for (r = mmap_regions; r; r = r->next) - { - char *rstart = (char *) r; - char *rend = rstart + r->nbytes_mapped; - - if (/* First byte of range, i.e. START, in this region? */ - (s >= rstart && s < rend) - /* Last byte of range, i.e. END - 1, in this region? */ - || (e > rstart && e <= rend) - /* First byte of this region in the range? */ - || (rstart >= s && rstart < e) - /* Last byte of this region in the range? */ - || (rend > s && rend <= e)) - break; - } - - return r; -} - - /* Unmap a region. P is a pointer to the start of the user-araa of the region. */ @@ -4880,38 +4855,6 @@ mmap_enlarge (struct mmap_region *r, int npages) } -/* Set or reset variables holding references to mapped regions. - If not RESTORE_P, set all variables to null. If RESTORE_P, set all - variables to the start of the user-areas of mapped regions. - - This function is called from Fdump_emacs to ensure that the dumped - Emacs doesn't contain references to memory that won't be mapped - when Emacs starts. */ - -void -mmap_set_vars (bool restore_p) -{ - struct mmap_region *r; - - if (restore_p) - { - mmap_regions = mmap_regions_1; - mmap_fd = mmap_fd_1; - for (r = mmap_regions; r; r = r->next) - *r->var = MMAP_USER_AREA (r); - } - else - { - for (r = mmap_regions; r; r = r->next) - *r->var = NULL; - mmap_regions_1 = mmap_regions; - mmap_regions = NULL; - mmap_fd_1 = mmap_fd; - mmap_fd = -1; - } -} - - /* Allocate a block of storage large enough to hold NBYTES bytes of data. A pointer to the data is returned in *VAR. VAR is thus the address of some variable which will use the data area. diff --git a/src/emacs.c b/src/emacs.c index 16d91de84a..fabea11a3b 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -2155,13 +2155,8 @@ You must run Emacs in batch mode in order to dump it. */) malloc_state_ptr = malloc_get_state (); #endif -#ifdef USE_MMAP_FOR_BUFFERS - mmap_set_vars (0); -#endif unexec (SSDATA (filename), !NILP (symfile) ? SSDATA (symfile) : 0); -#ifdef USE_MMAP_FOR_BUFFERS - mmap_set_vars (1); -#endif + #ifdef DOUG_LEA_MALLOC free (malloc_state_ptr); #endif diff --git a/src/image.c b/src/image.c index 07cf9f9af4..304603e738 100644 --- a/src/image.c +++ b/src/image.c @@ -998,6 +998,11 @@ free_image (struct frame *f, struct image *img) c->images[img->id] = NULL; + /* Windows NT redefines 'free', but in this file, we need to + avoid the redefinition. */ +#ifdef WINDOWSNT +#undef free +#endif /* Free resources, then free IMG. */ img->type->free (f, img); xfree (img); @@ -6453,7 +6458,6 @@ jpeg_file_src (j_decompress_ptr cinfo, FILE *fp) src->mgr.next_input_byte = NULL; } - /* Load image IMG for use on frame F. Patterned after example.c from the JPEG lib. */ diff --git a/src/lisp.h b/src/lisp.h index 2fd2835986..62fca16ec3 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -72,7 +72,7 @@ DEFINE_GDB_SYMBOL_END (GCTYPEBITS) 2. We know malloc returns a multiple of 8. */ #if (defined alignas \ && (defined GNU_MALLOC || defined DOUG_LEA_MALLOC || defined __GLIBC__ \ - || defined DARWIN_OS || defined __sun)) + || defined DARWIN_OS || defined __sun || defined __MINGW32__)) # define NONPOINTER_BITS 0 #else # define NONPOINTER_BITS GCTYPEBITS diff --git a/src/unexw32.c b/src/unexw32.c index f70cdd7947..60b926b249 100644 --- a/src/unexw32.c +++ b/src/unexw32.c @@ -83,8 +83,6 @@ PCHAR bss_start_static = 0; DWORD_PTR bss_size_static = 0; DWORD_PTR extra_bss_size_static = 0; -PIMAGE_SECTION_HEADER heap_section; - /* MinGW64 doesn't add a leading underscore to external symbols, whereas configure.ac sets up LD_SWITCH_SYSTEM_TEMACS to force the entry point at __start, with two underscores. */ @@ -475,8 +473,6 @@ get_section_info (file_data *p_infile) bss_section_static = 0; extra_bss_size_static = 0; } - - heap_section = rva_to_section (PTR_TO_RVA (get_heap_start ()), nt_header); } @@ -518,9 +514,11 @@ copy_executable_and_dump_data (file_data *p_infile, if (verbose) \ { \ printf ("%s\n", (message)); \ - printf ("\t0x%08x Address in process.\n", s); \ - printf ("\t0x%08x Offset in output file.\n", dst - p_outfile->file_base); \ - printf ("\t0x%08x Size in bytes.\n", count); \ + printf ("\t0x%p Address in process.\n", s); \ + printf ("\t0x%p Base output file.\n", p_outfile->file_base); \ + printf ("\t0x%p Offset in output file.\n", dst - p_outfile->file_base); \ + printf ("\t0x%p Address in output file.\n", dst); \ + printf ("\t0x%p Size in bytes.\n", count); \ } \ memcpy (dst, s, count); \ dst += count; \ @@ -629,34 +627,6 @@ copy_executable_and_dump_data (file_data *p_infile, dst_section->Characteristics &= ~IMAGE_SCN_CNT_UNINITIALIZED_DATA; dst_section->Characteristics |= IMAGE_SCN_CNT_INITIALIZED_DATA; } - if (section == heap_section) - { - DWORD_PTR heap_start = (DWORD_PTR) get_heap_start (); - DWORD_PTR heap_size = get_committed_heap_size (); - - /* Dump the used portion of the predump heap, adjusting the - section's size to the appropriate size. */ - dst = dst_save - + RVA_TO_SECTION_OFFSET (PTR_TO_RVA (heap_start), dst_section); - COPY_PROC_CHUNK ("Dumping heap...", heap_start, heap_size, - be_verbose); - ROUND_UP_DST (dst_nt_header->OptionalHeader.FileAlignment); - dst_section->PointerToRawData = PTR_TO_OFFSET (dst_save, p_outfile); - /* Determine new size of raw data area. */ - dst = max (dst, dst_save + dst_section->SizeOfRawData); - dst_section->SizeOfRawData = dst - dst_save; - /* Reduce the size of the heap section to fit (must be last - section). */ - dst_nt_header->OptionalHeader.SizeOfImage -= - dst_section->Misc.VirtualSize - - ROUND_UP (dst_section->SizeOfRawData, - dst_nt_header->OptionalHeader.SectionAlignment); - dst_section->Misc.VirtualSize = - ROUND_UP (dst_section->SizeOfRawData, - dst_nt_header->OptionalHeader.SectionAlignment); - dst_section->Characteristics &= ~IMAGE_SCN_CNT_UNINITIALIZED_DATA; - dst_section->Characteristics |= IMAGE_SCN_CNT_INITIALIZED_DATA; - } /* Align the section's raw data area. */ ROUND_UP_DST (dst_nt_header->OptionalHeader.FileAlignment); @@ -767,9 +737,6 @@ unexec (const char *new_name, const char *old_name) printf ("Dumping from %s\n", in_filename); printf (" to %s\n", out_filename); - /* We need to round off our heap to NT's page size. */ - round_heap (get_page_size ()); - /* Open the undumped executable file. */ if (!open_input_file (&in_file, in_filename)) { @@ -784,7 +751,6 @@ unexec (const char *new_name, const char *old_name) /* The size of the dumped executable is the size of the original executable plus the size of the heap and the size of the .bss section. */ size = in_file.size + - get_committed_heap_size () + extra_bss_size + extra_bss_size_static; if (!open_output_file (&out_file, out_filename, size)) @@ -799,6 +765,10 @@ unexec (const char *new_name, const char *old_name) copy_executable_and_dump_data (&in_file, &out_file); + /* Unset it because it is plain wrong to keep it after dumping. + Malloc can still occur! */ + using_dynamic_heap = FALSE; + /* Patch up header fields; profiler is picky about this. */ { PIMAGE_DOS_HEADER dos_header; diff --git a/src/w32heap.c b/src/w32heap.c dissimilarity index 88% index 8ab2f58c6e..7cce7c5031 100644 --- a/src/w32heap.c +++ b/src/w32heap.c @@ -1,256 +1,611 @@ -/* Heap management routines for GNU Emacs on the Microsoft Windows API. - Copyright (C) 1994, 2001-2014 Free Software Foundation, Inc. - -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 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. - -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 -*/ - -#include -#include - -#include "w32common.h" -#include "w32heap.h" -#include "lisp.h" /* for VALMASK */ - -#define RVA_TO_PTR(rva) ((unsigned char *)((DWORD_PTR)(rva) + (DWORD_PTR)GetModuleHandle (NULL))) - -/* 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; -size_t reserved_heap_size = 0; - -/* 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; -} - -#if !USE_LSB_TAG -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. */ - DWORD_PTR base = - ROUND_UP ((RVA_TO_PTR (preload_heap_section->VirtualAddress) - + preload_heap_section->Misc.VirtualSize), - get_allocation_unit ()); - DWORD_PTR end = ((unsigned __int64)1) << VALBITS; /* 256MB */ - void *ptr = NULL; - - while (!ptr && (base < end)) - { -#ifdef _WIN64 - reserved_heap_size = min(end - base, 0x4000000000ull); /* Limit to 256Gb */ -#else - reserved_heap_size = end - base; -#endif - ptr = VirtualAlloc ((void *) base, - get_reserved_heap_size (), - MEM_RESERVE, - PAGE_NOACCESS); - base += 0x00100000; /* 1MB increment */ - } - - return ptr; -} -#else /* USE_LSB_TAG */ -static char * -allocate_heap (void) -{ -#ifdef _WIN64 - size_t size = 0x4000000000ull; /* start by asking for 32GB */ -#else - /* We used to start with 2GB here, but on Windows 7 that would leave - too little room in the address space for threads started by - Windows on our behalf, e.g. when we pop up the file selection - dialog. */ - size_t size = 0x68000000; /* start by asking for 1.7GB */ -#endif - 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; -} -#endif /* USE_LSB_TAG */ - - -/* 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. */ -void * -sbrk (ptrdiff_t increment) -{ - void *result; - ptrdiff_t size = increment; - - result = data_region_end; - - /* If size is negative, shrink the heap by decommitting pages. */ - if (size < 0) - { - ptrdiff_t new_size; - unsigned char *new_data_region_end; - - size = -size; - - /* Sanity checks. */ - if ((data_region_end - size) < data_region_base) - 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 *) - ((DWORD_PTR) (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) - { - /* Sanity checks. */ - if ((data_region_end + size) > - (data_region_base + get_reserved_heap_size ())) - return NULL; - - /* Commit more of our heap. */ - 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 *) - ((DWORD_PTR) (data_region_end + syspage_mask) & ~syspage_mask); - } - - return result; -} - -/* 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 -init_heap (void) -{ - PIMAGE_DOS_HEADER dos_header; - PIMAGE_NT_HEADERS nt_header; - - dos_header = (PIMAGE_DOS_HEADER) RVA_TO_PTR (0); - nt_header = (PIMAGE_NT_HEADERS) (((DWORD_PTR) 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 !USE_LSB_TAG - /* Ensure that the addresses don't use the upper tag bits since - the Lisp type goes there. */ - if (((DWORD_PTR) 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. */ -void -round_heap (size_t align) -{ - DWORD_PTR needs_to_be; - DWORD_PTR need_to_alloc; - - needs_to_be = (DWORD_PTR) ROUND_UP (get_heap_end (), align); - need_to_alloc = needs_to_be - (DWORD_PTR) get_heap_end (); - - if (need_to_alloc) - sbrk (need_to_alloc); -} +/* Heap management routines for GNU Emacs on the Microsoft Windows + API. Copyright (C) 1994, 2001-2014 Free Software Foundation, Inc. + + 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 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. + + 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 +*/ + +/* + Heavily modified by Fabrice Popineau (fabrice.popineau@gmail.com) 28-02-2014 +*/ + +/* + Memory allocation scheme for w32/w64: + + - Buffers are mmap'ed using a very simple emulation of mmap/munmap + - During the temacs phase: + * we use a private heap declared to be stored into the `dumped_data' + * unfortunately, this heap cannot be made growable, so the size of + blocks it can allocate is limited to (0x80000 - pagesize) + * the blocks that are larger than this are allocated from the end + of the `dumped_data' array; there are not so many of them. + We use a very simple first-fit scheme to reuse those blocks. + * we check that the private heap does not cross the area used + by the bigger chunks. + - During the emacs phase: + * we create a private heap for new memory blocks + * we make sure that we never free a block that has been dumped. + Freeing a dumped block could work in principle, but may prove + unreliable if we distribute binaries of emacs.exe: MS does not + guarantee that the heap data structures are the same across all + versions of their OS, even though the API is available since XP. */ + +#include +#include + +#include +#include "w32common.h" +#include "w32heap.h" +#include "lisp.h" /* for VALMASK */ + +/* We chose to leave those declarations here. They are used only in + this file. The RtlCreateHeap is available since XP. It is located + in ntdll.dll and is available with the DDK. People often + complained that HeapCreate doesn't offer the ability to create a + heap at a given place, which we need here, and which RtlCreateHeap + provides. We reproduce here the definitions available with the + DDK. */ + +typedef PVOID (WINAPI * RtlCreateHeap_Proc) ( + /* _In_ */ ULONG Flags, + /* _In_opt_ */ PVOID HeapBase, + /* _In_opt_ */ SIZE_T ReserveSize, + /* _In_opt_ */ SIZE_T CommitSize, + /* _In_opt_ */ PVOID Lock, + /* _In_opt_ */ PVOID Parameters + ); + +typedef LONG NTSTATUS; + +typedef NTSTATUS +(NTAPI * PRTL_HEAP_COMMIT_ROUTINE)( + IN PVOID Base, + IN OUT PVOID *CommitAddress, + IN OUT PSIZE_T CommitSize + ); + +typedef struct _RTL_HEAP_PARAMETERS { + ULONG Length; + SIZE_T SegmentReserve; + SIZE_T SegmentCommit; + SIZE_T DeCommitFreeBlockThreshold; + SIZE_T DeCommitTotalFreeThreshold; + SIZE_T MaximumAllocationSize; + SIZE_T VirtualMemoryThreshold; + SIZE_T InitialCommit; + SIZE_T InitialReserve; + PRTL_HEAP_COMMIT_ROUTINE CommitRoutine; + SIZE_T Reserved[ 2 ]; +} RTL_HEAP_PARAMETERS, *PRTL_HEAP_PARAMETERS; + +/* We reserve space for dumping emacs lisp byte-code inside a static + array. By storing it in an array, the generic mechanism in + unexecw32.c will be able to dump it without the need to add a + special segment to the executable. In order to be able to do this + without losing too much space, we need to create a Windows heap at + the specific address of the static array. The RtlCreateHeap + available inside the NT kernel since XP will do this. It allows to + create a non-growable heap at a specific address. So before + dumping, we create a non-growable heap at the address of the + dumped_data[] array. After dumping, we reuse memory allocated + there without being able to free it (but most of it is not meant to + be freed anyway), and we use a new private heap for all new + allocations. */ + +unsigned char dumped_data[DUMPED_HEAP_SIZE]; + +/* Info for managing our preload heap, which is essentially a fixed size + data area in the executable. */ +/* Info for keeping track of our heap. */ +unsigned char *data_region_base = NULL; +unsigned char *data_region_end = NULL; +static DWORD_PTR committed = 0; + +/* The maximum block size that can be handled by a non-growable w32 + heap is limited by the MaxBlockSize value below. + + This point deserves and explanation. + + The W32 heap allocator can be used for a growable + heap or a non-growable one. + + A growable heap is not compatible with a fixed base address for the + heap. Only a non-growable one is. One drawback of non-growable + heaps is that they can hold only objects smaller than a certain + size (the one defined below). Most of the largest blocks are GC'ed + before dumping. In any case and to be safe, we implement a simple + first-fit allocation algorithm starting at the end of the + dumped_data[] array like depicted below: + + ---------------------------------------------- + | | | | + | Private heap |-> <-| Big chunks | + | | | | + ---------------------------------------------- + ^ ^ ^ + dumped_data dumped_data bc_limit + + committed + +*/ +#define HEAP_ENTRY_SHIFT 3 +#define PAGE_SIZE 0x1000 +#define MaxBlockSize (0x80000 - PAGE_SIZE) + +#define MAX_BLOCKS 0x40 + +static struct +{ + unsigned char *address; + size_t size; + DWORD occupied; +} blocks[MAX_BLOCKS]; + +static DWORD blocks_number = 0; +static unsigned char *bc_limit; + +/* Handle for the private heap: + - inside the dumped_data[] array before dump, + - outside of it after dump. +*/ +HANDLE heap = NULL; + +/* We redirect the standard allocation functions. */ +malloc_fn the_malloc_fn; +realloc_fn the_realloc_fn; +free_fn the_free_fn; + +/* It doesn't seem to be useful to allocate from a file mapping. + It would be if the memory was shared. + http://stackoverflow.com/questions/307060/what-is-the-purpose-of-allocating-pages-in-the-pagefile-with-createfilemapping */ + +/* This is the function to commit memory when the heap allocator + claims for new memory. Before dumping, we allocate space + from the fixed size dumped_data[] array. +*/ +NTSTATUS NTAPI +dumped_data_commit (PVOID Base, PVOID *CommitAddress, PSIZE_T CommitSize) +{ + /* This is used before dumping. + + The private heap is stored at dumped_data[] address. + We commit contiguous areas of the dumped_data array + as requests arrive. */ + *CommitAddress = data_region_base + committed; + committed += *CommitSize; + if (((unsigned char *)(*CommitAddress)) + *CommitSize >= bc_limit) + { + /* Check that the private heap area does not overlap the big + chunks area. */ + fprintf(stderr, + "dumped_data_commit: memory exhausted.\nEnlarge dumped_data[]!\n"); + exit (-1); + } + return 0; +} + +/* Heap creation. */ + +/* Under MinGW32, we want to turn on Low Fragmentation Heap for XP. + MinGW32 lacks those definitions. */ +#ifndef _W64 +typedef enum _HEAP_INFORMATION_CLASS { + HeapCompatibilityInformation +} HEAP_INFORMATION_CLASS; + +typedef WINBASEAPI BOOL (WINAPI * HeapSetInformation_Proc)(HANDLE,HEAP_INFORMATION_CLASS,PVOID,SIZE_T); +#endif + +void +init_heap (void) +{ + if (using_dynamic_heap) + { + unsigned long enable_lfh = 2; + + /* After dumping, use a new private heap. We explicitly enable + the low fragmentation heap here, for the sake of pre Vista + versions. Note: this will harnlessly fail on Vista and + later, whyere the low fragmentation heap is enabled by + default. It will also fail on pre-Vista versions when Emacs + is run under a debugger; set _NO_DEBUG_HEAP=1 in the + environment before starting GDB to get low fragmentation heap + on XP and older systems, for the price of losing "certain + heap debug options"; for the details see + http://msdn.microsoft.com/en-us/library/windows/desktop/aa366705%28v=vs.85%29.aspx. */ + data_region_end = data_region_base; + + /* Create the private heap. */ + heap = HeapCreate(0, 0, 0); + +#ifndef _W64 + /* Set the low-fragmentation heap for OS before XP and Windows + Server 2003. */ + HMODULE hm_kernel32dll = LoadLibrary("kernel32.dll"); + HeapSetInformation_Proc s_pfn_Heap_Set_Information = (HeapSetInformation_Proc) GetProcAddress(hm_kernel32dll, "HeapSetInformation"); + if (s_pfn_Heap_Set_Information != NULL) + if (s_pfn_Heap_Set_Information ((PVOID) heap, + HeapCompatibilityInformation, + &enable_lfh, sizeof(enable_lfh)) == 0) + DebPrint (("Enabling Low Fragmentation Heap failed\n")); +#endif + + the_malloc_fn = malloc_after_dump; + the_realloc_fn = realloc_after_dump; + the_free_fn = free_after_dump; + } + else + { + /* Find the RtlCreateHeap function. Headers for this function + are provided with the w32 ddk, but the function is available + in ntdll.dll since XP. */ + HMODULE hm_ntdll = LoadLibrary ("ntdll.dll"); + RtlCreateHeap_Proc s_pfn_Rtl_Create_Heap + = (RtlCreateHeap_Proc) GetProcAddress (hm_ntdll, "RtlCreateHeap"); + /* Specific parameters for the private heap. */ + RTL_HEAP_PARAMETERS params; + ZeroMemory(¶ms, sizeof(params)); + params.Length = sizeof(RTL_HEAP_PARAMETERS); + + data_region_base = (unsigned char *)ROUND_UP (dumped_data, 0x1000); + data_region_end = bc_limit = dumped_data + DUMPED_HEAP_SIZE; + + params.InitialCommit = committed = 0x1000; + params.InitialReserve = sizeof(dumped_data); + /* Use our own routine to commit memory from the dumped_data + array. */ + params.CommitRoutine = &dumped_data_commit; + + /* Create the private heap. */ + heap = s_pfn_Rtl_Create_Heap (0, data_region_base, 0, 0, NULL, ¶ms); + the_malloc_fn = malloc_before_dump; + the_realloc_fn = realloc_before_dump; + the_free_fn = free_before_dump; + } + + /* Update system version information to match current system. */ + cache_system_info (); +} + +#undef malloc +#undef realloc +#undef calloc +#undef free + +/* FREEABLE_P checks if the block can be safely freed. */ +#define FREEABLE_P(addr) \ + ((unsigned char *)(addr) < dumped_data \ + || (unsigned char *)(addr) >= dumped_data + DUMPED_HEAP_SIZE) + +void * +malloc_after_dump (size_t size) +{ + /* Use the new private heap. */ + void *p = HeapAlloc (heap, 0, size); + + /* After dump, keep track of the last allocated byte for sbrk(0). */ + data_region_end = p + size - 1; + return p; +} + +void * +malloc_before_dump (size_t size) +{ + void *p; + + /* Before dumping. The private heap can handle only requests for + less than MaxBlockSize. */ + if (size < MaxBlockSize) + { + /* Use the private heap if possible. */ + p = HeapAlloc (heap, 0, size); + } + else + { + /* Find the first big chunk that can hold the requested size. */ + int i = 0; + + for (i = 0; i < blocks_number; i++) + { + if (blocks[i].occupied == 0 && blocks[i].size >= size) + break; + } + if (i < blocks_number) + { + /* If found, use it. */ + p = blocks[i].address; + blocks[i].occupied = TRUE; + } + else + { + /* Allocate a new big chunk from the end of the dumped_data + array. */ + if (blocks_number >= MAX_BLOCKS) + { + fprintf(stderr, + "malloc_before_dump: no more big chunks available.\nEnlarge MAX_BLOCKS!\n"); + exit (-1); + } + bc_limit -= size; + bc_limit = (unsigned char *)ROUND_DOWN (bc_limit, 0x10); + p = bc_limit; + blocks[blocks_number].address = p; + blocks[blocks_number].size = size; + blocks[blocks_number].occupied = TRUE; + blocks_number++; + if (bc_limit < dumped_data + committed) + { + /* Check that areas do not overlap. */ + fprintf(stderr, + "malloc_before_dump: memory exhausted.\nEnlarge dumped_data[]!\n"); + exit (-1); + } + } + } + return p; +} + +/* Re-allocate the previously allocated block in ptr, making the new + block SIZE bytes long. */ +void * +realloc_after_dump (void *ptr, size_t size) +{ + void *p; + + /* After dumping. */ + if (FREEABLE_P (ptr)) + { + /* Reallocate the block since it lies in the new heap. */ + p = HeapReAlloc (heap, 0, ptr, size); + } + else + { + /* If the block lies in the dumped data, do not free it. Only + allocate a new one. */ + p = HeapAlloc (heap, 0, size); + CopyMemory (p, ptr, size); + } + /* After dump, keep track of the last allocated byte for sbrk(0). */ + data_region_end = p + size - 1; + return p; +} + +void * +realloc_before_dump (void *ptr, size_t size) +{ + void *p; + + /* Before dumping. */ + if (dumped_data < (unsigned char *)ptr + && (unsigned char *)ptr < bc_limit && size <= MaxBlockSize) + p = HeapReAlloc (heap, 0, ptr, size); + else + { + /* In this case, either the new block is too large for the heap, + or the old block was already too large. In both cases, + malloc_before_dump() and free_before_dump() will take care of + reallocation. */ + p = malloc_before_dump (size); + CopyMemory (p, ptr, size); + free_before_dump (ptr); + } + return p; +} + +/* Free a block allocated by `malloc', `realloc' or `calloc'. */ +void +free_after_dump (void *ptr) +{ + /* After dumping. */ + if (FREEABLE_P (ptr)) + { + /* Free the block if it is in the new private heap. */ + HeapFree (heap, 0, ptr); + } +} + +void +free_before_dump (void *ptr) +{ + /* Before dumping. */ + if (dumped_data < (unsigned char *)ptr + && (unsigned char *)ptr < bc_limit) + { + /* Free the block if it is allocated in the private heap. */ + HeapFree (heap, 0, ptr); + } + else + { + /* Look for the big chunk. */ + int i; + + for(i = 0; i < blocks_number; i++) + { + if (blocks[i].address == ptr) + { + /* Reset block occupation if found. */ + blocks[i].occupied = 0; + break; + } + /* What if the block is not found? We should trigger an + error here. */ + eassert (i < blocks_number); + } + } +} + +/* Emulate getpagesize. */ +int +getpagesize (void) +{ + return sysinfo_cache.dwPageSize; +} + +void * +sbrk (ptrdiff_t increment) +{ + /* The data_region_end address is the one of the last byte + allocated. The sbrk() function is not emulated at all, except + for a 0 value of its parameter. This is needed by the emacs lisp + function `memory-limit'. */ + return data_region_end; +} + +#define MAX_BUFFER_SIZE (512 * 1024 * 1024) + +/* MMAP allocation for buffers. */ +void * +mmap_alloc (void **var, size_t nbytes) +{ + void *p = NULL; + + /* We implement amortized allocation. We start by reserving twice + the size requested and commit only the size requested. Then + realloc could proceed and use the reserved pages, reallocating + only if needed. Buffer shrink would happen only so that we stay + in the 2x range. This is a big win when visiting compressed + files, where the final size of the buffer is not known in + advance, and the buffer is enlarged several times as the data is + decompressed on the fly. */ + if (nbytes < MAX_BUFFER_SIZE) + p = VirtualAlloc (NULL, (nbytes * 2), MEM_RESERVE, PAGE_READWRITE); + + /* If it fails, or if the request is above 512MB, try with the + requested size. */ + if (p == NULL) + p = VirtualAlloc (NULL, nbytes, MEM_RESERVE, PAGE_READWRITE); + + if (p != NULL) + { + /* Now, commit pages for NBYTES. */ + *var = VirtualAlloc (p, nbytes, MEM_COMMIT, PAGE_READWRITE); + } + + if (!p && GetLastError () != ERROR_NOT_ENOUGH_MEMORY) + DebPrint (("mmap_alloc: error %ld\n", GetLastError())); + + return *var = p; +} + +void +mmap_free (void **var) +{ + if (*var) + { + if (VirtualFree (*var, 0, MEM_RELEASE) == 0) + DebPrint (("mmap_free: error %ld\n", GetLastError())); + *var = NULL; + } +} + +void * +mmap_realloc (void **var, size_t nbytes) +{ + MEMORY_BASIC_INFORMATION memInfo, m2; + + if (*var == NULL) + return mmap_alloc (var, nbytes); + + /* This case happens in init_buffer(). */ + if (nbytes == 0) + { + mmap_free (var); + return mmap_alloc (var, nbytes); + } + + if (VirtualQuery (*var, &memInfo, sizeof (memInfo)) == 0) + DebPrint (("mmap_realloc: VirtualQuery error = %ld\n", GetLastError())); + + /* We need to enlarge the block. */ + if (memInfo.RegionSize < nbytes) + { + if (VirtualQuery (*var + memInfo.RegionSize, &m2, sizeof(m2)) == 0) + DebPrint (("mmap_realloc: VirtualQuery error = %ld\n", GetLastError())); + /* If there is enough room in the current reserved area, then + commit more pages as needed. */ + if (m2.State == MEM_RESERVE + && nbytes <= memInfo.RegionSize + m2.RegionSize) + { + void *p; + + p = VirtualAlloc (*var + memInfo.RegionSize, + nbytes - memInfo.RegionSize, + MEM_COMMIT, PAGE_READWRITE); + if (!p /* && GetLastError() != ERROR_NOT_ENOUGH_MEMORY */) + DebPrint (("realloc enlarge: VirtualAlloc error %ld\n", + GetLastError())); + return *var; + } + else + { + /* Else we must actually enlarge the block by allocating a + new one and copying previous contents from the old to the + new one. */ + void *old_ptr = *var; + + if (mmap_alloc (var, nbytes)) + { + CopyMemory (*var, old_ptr, memInfo.RegionSize); + mmap_free (&old_ptr); + return *var; + } + else + { + /* We failed to enlarge the buffer. */ + *var = old_ptr; + return NULL; + } + } + } + + /* If we are shrinking by more than one page... */ + if (memInfo.RegionSize > nbytes + getpagesize()) + { + /* If we are shrinking a lot... */ + if ((memInfo.RegionSize / 2) > nbytes) + { + /* Let's give some memory back to the system and release + some pages. */ + void *old_ptr = *var; + + if (mmap_alloc (var, nbytes)) + { + CopyMemory (*var, old_ptr, nbytes); + mmap_free (&old_ptr); + return *var; + } + else + { + /* In case we fail to shrink, try to go on with the old block. + But that means there is a lot of memory pressure. + We could also decommit pages. */ + *var = old_ptr; + return *var; + } + } + + /* We still can decommit pages. */ + if (VirtualFree (*var + nbytes + get_page_size(), + memInfo.RegionSize - nbytes - get_page_size(), + MEM_DECOMMIT) == 0) + DebPrint (("mmap_realloc: VirtualFree error %ld\n", GetLastError())); + return *var; + } + + /* Not enlarging, not shrinking by more than one page. */ + return *var; +} diff --git a/src/w32heap.h b/src/w32heap.h index d5791a3a23..84a26f958d 100644 --- a/src/w32heap.h +++ b/src/w32heap.h @@ -27,15 +27,20 @@ along with GNU Emacs. If not, see . /* * Heap related stuff. */ -#define get_reserved_heap_size() reserved_heap_size -#define get_committed_heap_size() (get_data_end () - get_data_start ()) -#define get_heap_start() get_data_start () -#define get_heap_end() get_data_end () + +#define DUMPED_HEAP_SIZE (HEAPSIZE*1024*1024) + +extern unsigned char dumped_data[]; extern unsigned char *get_data_start (void); extern unsigned char *get_data_end (void); extern size_t reserved_heap_size; -extern BOOL using_dynamic_heap; +extern BOOL using_dynamic_heap; + +extern void *mmap_realloc (void **, size_t); +extern void mmap_free (void **); +extern void *mmap_alloc (void **, size_t); + /* Emulation of Unix sbrk(). */ extern void *sbrk (ptrdiff_t size); @@ -43,11 +48,8 @@ extern void *sbrk (ptrdiff_t size); /* Initialize heap structures for sbrk on startup. */ extern void init_heap (void); -/* Round the heap to this size. */ -extern void round_heap (size_t size); - /* ----------------------------------------------------------------- */ -/* Useful routines for manipulating memory-mapped files. */ +/* Useful routines for manipulating memory-mapped files. */ typedef struct file_data { char *name; @@ -61,11 +63,11 @@ int open_input_file (file_data *p_file, char *name); int open_output_file (file_data *p_file, char *name, unsigned long size); void close_file_data (file_data *p_file); -/* Return pointer to section header for named section. */ +/* Return pointer to section header for named section. */ IMAGE_SECTION_HEADER * find_section (char * name, IMAGE_NT_HEADERS * nt_header); /* Return pointer to section header for section containing the given - relative virtual address. */ + relative virtual address. */ IMAGE_SECTION_HEADER * rva_to_section (DWORD_PTR rva, IMAGE_NT_HEADERS * nt_header); #endif /* NTHEAP_H_ */