/* Block-relocating memory allocator.
- Copyright (C) 1993, 1995, 2000-2011 Free Software Foundation, Inc.
+ Copyright (C) 1993, 1995, 2000-2012 Free Software Foundation, Inc.
This file is part of GNU Emacs.
#include <unistd.h>
-typedef POINTER_TYPE *POINTER;
-typedef size_t SIZE;
-
#ifdef DOUG_LEA_MALLOC
#define M_TOP_PAD -2
extern int mallopt (int, int);
#include <stddef.h>
-typedef size_t SIZE;
-typedef void *POINTER;
-
#include <unistd.h>
#include <malloc.h>
#include "getpagesize.h"
+typedef size_t SIZE;
+typedef void *POINTER;
#define NIL ((POINTER) 0)
/* A flag to indicate whether we have initialized ralloc yet. For
/* Macros for rounding. Note that rounding to any value is possible
by changing the definition of PAGE. */
#define PAGE (getpagesize ())
-#define ALIGNED(addr) (((unsigned long int) (addr) & (page_size - 1)) == 0)
#define ROUNDUP(size) (((unsigned long int) (size) + page_size - 1) \
& ~(page_size - 1))
-#define ROUND_TO_PAGE(addr) (addr & (~(page_size - 1)))
-#define MEM_ALIGN sizeof(double)
+#define MEM_ALIGN sizeof (double)
#define MEM_ROUNDUP(addr) (((unsigned long int)(addr) + MEM_ALIGN - 1) \
& ~(MEM_ALIGN - 1))
} *heap_ptr;
#define NIL_HEAP ((heap_ptr) 0)
-#define HEAP_PTR_SIZE (sizeof (struct heap))
/* This is the first heap object.
If we need additional heap objects, each one resides at the beginning of
If enough space is not presently available in our reserve, this means
getting more page-aligned space from the system. If the returned space
is not contiguous to the last heap, allocate a new heap, and append it
+ to the heap list.
- obtain does not try to keep track of whether space is in use
- or not in use. It just returns the address of SIZE bytes that
- fall within a single heap. If you call obtain twice in a row
- with the same arguments, you typically get the same value.
- to the heap list. It's the caller's responsibility to keep
- track of what space is in use.
+ obtain does not try to keep track of whether space is in use or not
+ in use. It just returns the address of SIZE bytes that fall within a
+ single heap. If you call obtain twice in a row with the same arguments,
+ you typically get the same value. It's the caller's responsibility to
+ keep track of what space is in use.
Return the address of the space if all went well, or zero if we couldn't
allocate the memory. */
}
if (! heap)
- abort ();
+ emacs_abort ();
/* If we can't fit SIZE bytes in that heap,
try successive later heaps. */
/* This heap should have no blocs in it. */
if (last_heap->first_bloc != NIL_BLOC
|| last_heap->last_bloc != NIL_BLOC)
- abort ();
+ emacs_abort ();
/* Return the last heap, with its header, to the system. */
excess = (char *)last_heap->end - (char *)last_heap->start;
which returns the entire last heap to the system, seems
unlikely to trigger this mode of failure. */
if (last_heap->end != (*real_morecore) (0))
- abort ();
+ emacs_abort ();
}
}
}
-
-/* Return the total size in use by relocating allocator,
- above where malloc gets space. */
-
-long
-r_alloc_size_in_use (void)
-{
- return (char *) break_value - (char *) virtual_break_value;
-}
\f
/* The meat - allocating, freeing, and relocating blocs. */
while (p != NIL_BLOC)
{
/* Consistency check. Don't return inconsistent blocs.
- Don't abort here, as callers might be expecting this, but
+ Don't abort here, as callers might be expecting this, but
callers that always expect a bloc to be returned should abort
if one isn't to avoid a memory corruption bug that is
difficult to track down. */
register bloc_ptr new_bloc;
register heap_ptr heap;
- if (! (new_bloc = (bloc_ptr) malloc (BLOC_PTR_SIZE))
+ if (! (new_bloc = malloc (BLOC_PTR_SIZE))
|| ! (new_bloc->data = obtain (break_value, size)))
{
free (new_bloc);
/* No need to ever call this if arena is frozen, bug somewhere! */
if (r_alloc_freeze_level)
- abort();
+ emacs_abort ();
while (b)
{
/* No need to ever call this if arena is frozen, bug somewhere! */
if (r_alloc_freeze_level)
- abort();
+ emacs_abort ();
if (bloc == NIL_BLOC || size == bloc->size)
return 1;
}
if (heap == NIL_HEAP)
- abort ();
+ emacs_abort ();
old_size = bloc->size;
bloc->size = size;
}
else
{
- memmove (b->new_data, b->data, b->size);
+ if (b->new_data != b->data)
+ memmove (b->new_data, b->data, b->size);
*b->variable = b->data = b->new_data;
}
}
}
else
{
- memmove (bloc->new_data, bloc->data, old_size);
+ if (bloc->new_data != bloc->data)
+ memmove (bloc->new_data, bloc->data, old_size);
memset ((char *) bloc->new_data + old_size, 0, size - old_size);
*bloc->variable = bloc->data = bloc->new_data;
}
}
else
{
- memmove (b->new_data, b->data, b->size);
+ if (b->new_data != b->data)
+ memmove (b->new_data, b->data, b->size);
*b->variable = b->data = b->new_data;
}
}
free_bloc (bloc_ptr bloc)
{
heap_ptr heap = bloc->heap;
+ heap_ptr h;
if (r_alloc_freeze_level)
{
bloc->prev->next = bloc->next;
}
- /* Update the records of which blocs are in HEAP. */
- if (heap->first_bloc == bloc)
- {
- if (bloc->next != 0 && bloc->next->heap == heap)
- heap->first_bloc = bloc->next;
- else
- heap->first_bloc = heap->last_bloc = NIL_BLOC;
- }
- if (heap->last_bloc == bloc)
+ /* Sometimes, 'heap' obtained from bloc->heap above is not really a
+ 'heap' structure. It can even be beyond the current break point,
+ which will cause crashes when we dereference it below (see
+ bug#12242). Evidently, the reason is bloc allocations done while
+ use_relocatable_buffers was non-positive, because additional
+ memory we get then is not recorded in the heaps we manage. If
+ bloc->heap records such a "heap", we cannot (and don't need to)
+ update its records. So we validate the 'heap' value by making
+ sure it is one of the heaps we manage via the heaps linked list,
+ and don't touch a 'heap' that isn't found there. This avoids
+ accessing memory we know nothing about. */
+ for (h = first_heap; h != NIL_HEAP; h = h->next)
+ if (heap == h)
+ break;
+
+ if (h)
{
- if (bloc->prev != 0 && bloc->prev->heap == heap)
- heap->last_bloc = bloc->prev;
- else
- heap->first_bloc = heap->last_bloc = NIL_BLOC;
+ /* Update the records of which blocs are in HEAP. */
+ if (heap->first_bloc == bloc)
+ {
+ if (bloc->next != 0 && bloc->next->heap == heap)
+ heap->first_bloc = bloc->next;
+ else
+ heap->first_bloc = heap->last_bloc = NIL_BLOC;
+ }
+ if (heap->last_bloc == bloc)
+ {
+ if (bloc->prev != 0 && bloc->prev->heap == heap)
+ heap->last_bloc = bloc->prev;
+ else
+ heap->first_bloc = heap->last_bloc = NIL_BLOC;
+ }
}
relinquish ();
__morecore hook values - in particular, __default_morecore in the
GNU malloc package. */
-POINTER
+static POINTER
r_alloc_sbrk (long int size)
{
register bloc_ptr b;
if (! r_alloc_initialized)
r_alloc_init ();
- if (! use_relocatable_buffers)
+ if (use_relocatable_buffers <= 0)
return (*real_morecore) (size);
if (size == 0)
header. */
for (b = last_bloc; b != NIL_BLOC; b = b->prev)
{
- memmove (b->new_data, b->data, b->size);
+ if (b->new_data != b->data)
+ memmove (b->new_data, b->data, b->size);
*b->variable = b->data = b->new_data;
}
for (b = first_bloc; b != NIL_BLOC; b = b->next)
{
- memmove (b->new_data, b->data, b->size);
+ if (b->new_data != b->data)
+ memmove (b->new_data, b->data, b->size);
*b->variable = b->data = b->new_data;
}
}
dead_bloc = find_bloc (ptr);
if (dead_bloc == NIL_BLOC)
- abort (); /* Double free? PTR not originally used to allocate? */
+ emacs_abort (); /* Double free? PTR not originally used to allocate? */
free_bloc (dead_bloc);
*ptr = 0;
bloc = find_bloc (ptr);
if (bloc == NIL_BLOC)
- abort (); /* Already freed? PTR not originally used to allocate? */
+ emacs_abort (); /* Already freed? PTR not originally used to allocate? */
if (size < bloc->size)
{
return *ptr;
}
-/* Disable relocations, after making room for at least SIZE bytes
- of non-relocatable heap if possible. The relocatable blocs are
- guaranteed to hold still until thawed, even if this means that
- malloc must return a null pointer. */
-
-void
-r_alloc_freeze (long int size)
-{
- if (! r_alloc_initialized)
- r_alloc_init ();
-
- /* If already frozen, we can't make any more room, so don't try. */
- if (r_alloc_freeze_level > 0)
- size = 0;
- /* If we can't get the amount requested, half is better than nothing. */
- while (size > 0 && r_alloc_sbrk (size) == 0)
- size /= 2;
- ++r_alloc_freeze_level;
- if (size > 0)
- r_alloc_sbrk (-size);
-}
-
-void
-r_alloc_thaw (void)
-{
-
- if (! r_alloc_initialized)
- r_alloc_init ();
-
- if (--r_alloc_freeze_level < 0)
- abort ();
-
- /* This frees all unused blocs. It is not too inefficient, as the resize
- and memcpy is done only once. Afterwards, all unreferenced blocs are
- already shrunk to zero size. */
- if (!r_alloc_freeze_level)
- {
- bloc_ptr *b = &first_bloc;
- while (*b)
- if (!(*b)->variable)
- free_bloc (*b);
- else
- b = &(*b)->next;
- }
-}
-
#if defined (emacs) && defined (DOUG_LEA_MALLOC)
/* Find the bloc that corresponds to the data pointed to by pointer.
find_bloc cannot be used, as it has internal consistency checks
- which fail when the variable needs reseting. */
+ which fail when the variable needs resetting. */
while (bloc != NIL_BLOC)
{
if (bloc->data == *new)
}
if (bloc == NIL_BLOC || bloc->variable != old)
- abort (); /* Already freed? OLD not originally used to allocate? */
+ emacs_abort (); /* Already freed? OLD not originally used to allocate? */
/* Update variable to point to the new location. */
bloc->variable = new;
}
+void
+r_alloc_inhibit_buffer_relocation (int inhibit)
+{
+ if (use_relocatable_buffers > 1)
+ use_relocatable_buffers = 1;
+ if (inhibit)
+ use_relocatable_buffers--;
+ else if (use_relocatable_buffers < 1)
+ use_relocatable_buffers++;
+}
+
\f
/***********************************************************************
Initialization
first_heap->start = first_heap->bloc_start
= virtual_break_value = break_value = (*real_morecore) (0);
if (break_value == NIL)
- abort ();
+ emacs_abort ();
extra_bytes = ROUNDUP (50000);
#endif