From cce93c76bac0718cd084410797c38734b5649156 Mon Sep 17 00:00:00 2001 From: Richard Biener Date: Thu, 22 Jan 2015 09:21:48 +0000 Subject: [PATCH] re PR libstdc++/64535 (Emergency buffer for exception allocation too small) 2015-01-22 Richard Biener PR libstdc++/64535 * libsupc++/eh_alloc.cc: Include new. (bitmask_type): Remove. (one_buffer): Likewise. (emergency_buffer): Likewise. (emergency_used): Likewise. (dependents_buffer): Likewise. (dependents_used): Likewise. (class pool): New custom fixed-size arena, variable size object allocator. (emergency_pool): New global. (__cxxabiv1::__cxa_allocate_exception): Use new emergency_pool. (__cxxabiv1::__cxa_free_exception): Likewise. (__cxxabiv1::__cxa_allocate_dependent_exception): Likewise. (__cxxabiv1::__cxa_free_dependent_exception): Likewise. * g++.old-deja/g++.eh/badalloc1.C: Adjust. From-SVN: r219988 --- gcc/testsuite/ChangeLog | 5 + gcc/testsuite/g++.old-deja/g++.eh/badalloc1.C | 9 +- libstdc++-v3/ChangeLog | 18 ++ libstdc++-v3/libsupc++/eh_alloc.cc | 263 ++++++++++++------ 4 files changed, 208 insertions(+), 87 deletions(-) diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 2a54aff7fca..8246787f8aa 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2015-01-22 Richard Biener + + PR libstdc++/64535 + * g++.old-deja/g++.eh/badalloc1.C: Adjust. + 2015-01-21 Segher Boessenkool PR rtl-optimization/64682 diff --git a/gcc/testsuite/g++.old-deja/g++.eh/badalloc1.C b/gcc/testsuite/g++.old-deja/g++.eh/badalloc1.C index 5cde10ee272..f4f443bd325 100644 --- a/gcc/testsuite/g++.old-deja/g++.eh/badalloc1.C +++ b/gcc/testsuite/g++.old-deja/g++.eh/badalloc1.C @@ -12,21 +12,24 @@ typedef __SIZE_TYPE__ size_t; extern "C" void abort(); extern "C" void *memcpy(void *, const void *, size_t); +// libstdc++ requires a large initialization time allocation for the +// emergency EH allocation pool. Add that to the arena size. + // Assume that STACK_SIZE defined implies a system that does not have a // large data space either, and additionally that we're not linking against // a shared libstdc++ (which requires quite a bit more initialization space). #ifdef STACK_SIZE -const int arena_size = 256; +const int arena_size = 256 + 8 * 128; #else #if defined(__FreeBSD__) || defined(__sun__) || defined(__hpux__) // FreeBSD, Solaris and HP-UX require even more space at initialization time. // FreeBSD 5 now requires over 131072 bytes. -const int arena_size = 262144; +const int arena_size = 262144 + 72 * 1024; #else // Because pointers make up the bulk of our exception-initialization // allocations, we scale by the pointer size from the original // 32-bit-systems-based estimate. -const int arena_size = 32768 * ((sizeof (void *) + 3)/4); +const int arena_size = 32768 * ((sizeof (void *) + 3)/4) + 72 * 1024; #endif #endif diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index e4e9f4937c6..ffa74961b80 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,21 @@ +2015-01-22 Richard Biener + + PR libstdc++/64535 + * libsupc++/eh_alloc.cc: Include new. + (bitmask_type): Remove. + (one_buffer): Likewise. + (emergency_buffer): Likewise. + (emergency_used): Likewise. + (dependents_buffer): Likewise. + (dependents_used): Likewise. + (class pool): New custom fixed-size arena, variable size object + allocator. + (emergency_pool): New global. + (__cxxabiv1::__cxa_allocate_exception): Use new emergency_pool. + (__cxxabiv1::__cxa_free_exception): Likewise. + (__cxxabiv1::__cxa_allocate_dependent_exception): Likewise. + (__cxxabiv1::__cxa_free_dependent_exception): Likewise. + 2015-01-22 Tim Shen PR libstdc++/64680 diff --git a/libstdc++-v3/libsupc++/eh_alloc.cc b/libstdc++-v3/libsupc++/eh_alloc.cc index 9e8c247d3ab..c77f53fdf02 100644 --- a/libstdc++-v3/libsupc++/eh_alloc.cc +++ b/libstdc++-v3/libsupc++/eh_alloc.cc @@ -34,6 +34,7 @@ #include #include "unwind-cxx.h" #include +#include #if _GLIBCXX_HOSTED using std::free; @@ -72,28 +73,174 @@ using namespace __cxxabiv1; # define EMERGENCY_OBJ_COUNT 4 #endif -#if INT_MAX == 32767 || EMERGENCY_OBJ_COUNT <= 32 -typedef unsigned int bitmask_type; -#else -#if defined (_GLIBCXX_LLP64) -typedef unsigned long long bitmask_type; -#else -typedef unsigned long bitmask_type; -#endif -#endif +namespace +{ + // A fixed-size heap, variable size object allocator + class pool + { + public: + pool(); + + void *allocate (std::size_t); + void free (void *); + + bool in_pool (void *); + + private: + struct free_entry { + std::size_t size; + free_entry *next; + }; + struct allocated_entry { + std::size_t size; + char data[]; + }; + + // A single mutex controlling emergency allocations. + __gnu_cxx::__mutex emergency_mutex; + + // The free-list + free_entry *first_free_entry; + // The arena itself - we need to keep track of these only + // to implement in_pool. + char *arena; + std::size_t arena_size; + }; + + pool::pool() + { + // Allocate the arena - we could add a GLIBCXX_EH_ARENA_SIZE environment + // to make this tunable. + arena_size = (EMERGENCY_OBJ_SIZE * EMERGENCY_OBJ_COUNT + + EMERGENCY_OBJ_COUNT * sizeof (__cxa_dependent_exception)); + arena = (char *)malloc (arena_size); + if (!arena) + { + // If the allocation failed go without an emergency pool. + arena_size = 0; + first_free_entry = NULL; + return; + } -typedef char one_buffer[EMERGENCY_OBJ_SIZE] __attribute__((aligned)); -static one_buffer emergency_buffer[EMERGENCY_OBJ_COUNT]; -static bitmask_type emergency_used; + // Populate the free-list with a single entry covering the whole arena + first_free_entry = reinterpret_cast (arena); + new (first_free_entry) free_entry; + first_free_entry->size = arena_size; + first_free_entry->next = NULL; + } -static __cxa_dependent_exception dependents_buffer[EMERGENCY_OBJ_COUNT]; -static bitmask_type dependents_used; + void *pool::allocate (std::size_t size) + { + __gnu_cxx::__scoped_lock sentry(emergency_mutex); + // We need an additional size_t member. + size += sizeof (std::size_t); + // And we need to at least hand out objects of the size of + // a freelist entry. + if (size < sizeof (free_entry)) + size = sizeof (free_entry); + // And we need to align objects we hand out to the required + // alignment of a freelist entry (this really aligns the + // tail which will become a new freelist entry). + size = ((size + __alignof__(free_entry) - 1) + & ~(__alignof__(free_entry) - 1)); + // Search for an entry of proper size on the freelist. + free_entry **e; + for (e = &first_free_entry; + *e && (*e)->size < size; + e = &(*e)->next) + ; + if (!*e) + return NULL; + allocated_entry *x; + if ((*e)->size - size >= sizeof (free_entry)) + { + // Slit block if it is too large. + free_entry *f = reinterpret_cast + (reinterpret_cast (*e) + size); + std::size_t sz = (*e)->size; + free_entry *next = (*e)->next; + new (f) free_entry; + f->next = next; + f->size = sz - size; + x = reinterpret_cast (*e); + new (x) allocated_entry; + x->size = size; + *e = f; + } + else + { + // Exact size match or too small overhead for a free entry. + std::size_t sz = (*e)->size; + free_entry *next = (*e)->next; + x = reinterpret_cast (*e); + new (x) allocated_entry; + x->size = sz; + *e = next; + } + return &x->data; + } -namespace -{ - // A single mutex controlling emergency allocations. - __gnu_cxx::__mutex emergency_mutex; + void pool::free (void *data) + { + __gnu_cxx::__scoped_lock sentry(emergency_mutex); + allocated_entry *e = reinterpret_cast + (reinterpret_cast (data) - sizeof (std::size_t)); + std::size_t sz = e->size; + if (!first_free_entry) + { + // If the free list is empty just put the entry there. + free_entry *f = reinterpret_cast (e); + new (f) free_entry; + f->size = sz; + f->next = NULL; + first_free_entry = f; + } + else if (reinterpret_cast (e) + sz + == reinterpret_cast (first_free_entry)) + { + // Check if we can merge with the first free entry being right + // after us. + free_entry *f = reinterpret_cast (e); + new (f) free_entry; + f->size = sz + first_free_entry->size; + f->next = first_free_entry->next; + first_free_entry = f; + } + else + { + // Else search for a free item we can merge with at its end. + free_entry **fe; + for (fe = &first_free_entry; + (*fe)->next + && (reinterpret_cast ((*fe)->next) + > reinterpret_cast (e) + sz); + fe = &(*fe)->next) + ; + if (reinterpret_cast (*fe) + (*fe)->size + == reinterpret_cast (e)) + /* Merge with the freelist entry. */ + (*fe)->size += sz; + else + { + // Else put it after it which keeps the freelist sorted. + free_entry *f = reinterpret_cast (e); + new (f) free_entry; + f->size = sz; + f->next = (*fe)->next; + (*fe)->next = f; + } + } + } + + bool pool::in_pool (void *ptr) + { + char *p = reinterpret_cast (ptr); + return (p > arena + && p < arena + arena_size); + } + + pool emergency_pool; } extern "C" void * @@ -104,30 +251,11 @@ __cxxabiv1::__cxa_allocate_exception(std::size_t thrown_size) _GLIBCXX_NOTHROW thrown_size += sizeof (__cxa_refcounted_exception); ret = malloc (thrown_size); - if (! ret) - { - __gnu_cxx::__scoped_lock sentry(emergency_mutex); - - bitmask_type used = emergency_used; - unsigned int which = 0; - - if (thrown_size > EMERGENCY_OBJ_SIZE) - goto failed; - while (used & 1) - { - used >>= 1; - if (++which >= EMERGENCY_OBJ_COUNT) - goto failed; - } - - emergency_used |= (bitmask_type)1 << which; - ret = &emergency_buffer[which][0]; - - failed:; + if (!ret) + ret = emergency_pool.allocate (thrown_size); - if (!ret) - std::terminate (); - } + if (!ret) + std::terminate (); memset (ret, 0, sizeof (__cxa_refcounted_exception)); @@ -138,19 +266,11 @@ __cxxabiv1::__cxa_allocate_exception(std::size_t thrown_size) _GLIBCXX_NOTHROW extern "C" void __cxxabiv1::__cxa_free_exception(void *vptr) _GLIBCXX_NOTHROW { - char *base = (char *) emergency_buffer; - char *ptr = (char *) vptr; - if (ptr >= base - && ptr < base + sizeof (emergency_buffer)) - { - const unsigned int which - = (unsigned) (ptr - base) / EMERGENCY_OBJ_SIZE; - - __gnu_cxx::__scoped_lock sentry(emergency_mutex); - emergency_used &= ~((bitmask_type)1 << which); - } + char *ptr = (char *) vptr - sizeof (__cxa_refcounted_exception); + if (emergency_pool.in_pool (ptr)) + emergency_pool.free (ptr); else - free (ptr - sizeof (__cxa_refcounted_exception)); + free (ptr); } @@ -163,27 +283,11 @@ __cxxabiv1::__cxa_allocate_dependent_exception() _GLIBCXX_NOTHROW (malloc (sizeof (__cxa_dependent_exception))); if (!ret) - { - __gnu_cxx::__scoped_lock sentry(emergency_mutex); - - bitmask_type used = dependents_used; - unsigned int which = 0; - - while (used & 1) - { - used >>= 1; - if (++which >= EMERGENCY_OBJ_COUNT) - goto failed; - } - - dependents_used |= (bitmask_type)1 << which; - ret = &dependents_buffer[which]; + ret = static_cast <__cxa_dependent_exception*> + (emergency_pool.allocate (sizeof (__cxa_dependent_exception))); - failed:; - - if (!ret) - std::terminate (); - } + if (!ret) + std::terminate (); memset (ret, 0, sizeof (__cxa_dependent_exception)); @@ -195,17 +299,8 @@ extern "C" void __cxxabiv1::__cxa_free_dependent_exception (__cxa_dependent_exception *vptr) _GLIBCXX_NOTHROW { - char *base = (char *) dependents_buffer; - char *ptr = (char *) vptr; - if (ptr >= base - && ptr < base + sizeof (dependents_buffer)) - { - const unsigned int which - = (unsigned) (ptr - base) / sizeof (__cxa_dependent_exception); - - __gnu_cxx::__scoped_lock sentry(emergency_mutex); - dependents_used &= ~((bitmask_type)1 << which); - } + if (emergency_pool.in_pool (vptr)) + emergency_pool.free (vptr); else free (vptr); } -- 2.30.2