Index: src/cxa_exception.cpp =================================================================== --- src/cxa_exception.cpp +++ src/cxa_exception.cpp @@ -17,6 +17,7 @@ #include // for std::terminate #include // for malloc, free #include // for memset +#include // for std::max_align_t #if !LIBCXXABI_HAS_NO_THREADS # include // for fallback_malloc.ipp's mutexes #endif @@ -68,6 +69,16 @@ return cxa_exception_from_thrown_object(unwind_exception + 1 ); } +// Return true if the alignment requirements of __cxa_exception are greater +// than the fundamental alignment requirements. In this case we have to manually +// align pointers returned from malloc. +static +inline +bool +cxa_exception_must_be_overaligned() { + return alignof(__cxa_exception) > alignof(std::max_align_t); +} + static inline size_t @@ -106,16 +117,52 @@ #include "fallback_malloc.ipp" +// Same as std::align. This implementation is cribbed from libc++. +void* +align(size_t alignment, size_t size, void*& ptr, size_t& space) +{ + void* r = nullptr; + if (size <= space) + { + char* p1 = static_cast(ptr); + char* p2 = reinterpret_cast(reinterpret_cast(p1 + (alignment - 1)) & -alignment); + size_t d = static_cast(p2 - p1); + if (d <= space - size) + { + r = p2; + ptr = r; + space -= d; + } + } + return r; +} + // Allocate some memory from _somewhere_ static void *do_malloc(size_t size) { - void *ptr = std::malloc(size); + size_t aligned_size = size; + if (cxa_exception_must_be_overaligned()) { + aligned_size = (size + alignof(__cxa_exception) - alignof(void*)) + sizeof(void*); + } + + void *ptr = std::malloc(aligned_size); if (NULL == ptr) // if malloc fails, fall back to emergency stash - ptr = fallback_malloc(size); + ptr = fallback_malloc(aligned_size); + + if (cxa_exception_must_be_overaligned()) { + void *orig_ptr = ptr; + ptr = static_cast(ptr) + sizeof(void*); + align(alignof(__cxa_exception), size, ptr, aligned_size); + *(static_cast(ptr) - 1) = orig_ptr; + } + return ptr; } static void do_free(void *ptr) { - is_fallback_ptr(ptr) ? fallback_free(ptr) : std::free(ptr); + void* p = ptr; + if (cxa_exception_must_be_overaligned()) + p = *(static_cast(ptr) - 1); + is_fallback_ptr(p) ? fallback_free(p) : std::free(p); } /* @@ -156,7 +203,7 @@ // object. Zero-fill the object. If memory can't be allocated, call // std::terminate. Return a pointer to the memory to be used for the // user's exception object. -void * __cxa_allocate_exception (size_t thrown_size) throw() { +void * __cxa_allocate_exception(size_t thrown_size) throw() { size_t actual_size = cxa_exception_size_from_exception_thrown_size(thrown_size); __cxa_exception* exception_header = static_cast<__cxa_exception*>(do_malloc(actual_size)); if (NULL == exception_header) Index: test/test_cxa_allocate_exception.pass.cpp =================================================================== --- /dev/null +++ test/test_cxa_allocate_exception.pass.cpp @@ -0,0 +1,65 @@ +//===--------------------- test_fallback_malloc.cpp -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: c++98, c++03 + +// void* __cxa_allocate_exception(size_t); +// void __cxa_free_exception(void*); +// void* __cxa_allocate_dependent_exception(); +// void __cxa_free_dependent_exception(); + +// __cxa_exception is specified with __attribute__((aligned)) for GNU unwind. +// This means that during 32 bit builds __cxa_exception is "over aligned". This +// test checks that __cxa_allocate_exception return correctly overaligned memory. +// See PR24604 - https://llvm.org/bugs/show_bug.cgi?id=24604 + +#include "../src/cxa_exception.hpp" +#include // for std::max_align_t +#include + +#if defined(__GNUC__) && !defined(_LP64) +#define SHOULD_BE_OVERALIGNED 1 +#endif + +using namespace __cxxabiv1; + +const std::size_t max_alignment = alignof(std::max_align_t); +const std::size_t required_alignment = alignof(__cxa_exception); +const bool requires_over_alignment = max_alignment < required_alignment; + +static_assert(alignof(__cxa_exception) == alignof(__cxa_dependent_exception), + "They should have the same alignment requirements"); + +#if defined(__GNUC__) && !defined(_LP64) +static_assert(alignof(__cxa_exception) > alignof(std::max_align_t), + "On 32 bit platforms __cxa_exception is expected to be over aligned."); +#endif + +void test_cxa_allocate_exception() { + for (int i=0; i < 4096; ++i) { + void* ptr = __cxa_allocate_exception(i); + assert(ptr); + assert(reinterpret_cast(ptr) % required_alignment == 0); + __cxa_free_exception(ptr); + } +} + +void test_cxa_allocate_dependent_exception() { + for (int i=0; i < 100; ++i) { + void* ptr = __cxa_allocate_dependent_exception(); + assert(ptr); + assert(reinterpret_cast(ptr) % required_alignment == 0); + __cxa_free_dependent_exception(ptr); + } +} + +int main() { + test_cxa_allocate_exception(); + test_cxa_allocate_dependent_exception(); +}