diff --git a/CMakeLists.txt b/CMakeLists.txt index 05e6001f9..1db3331b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,9 +91,17 @@ if(ABC_USE_NAMESPACE) set_source_files_properties(${ABC_SRC} PROPERTIES LANGUAGE CXX) endif() +# Allocation failures cross ABC's C frames on their way to STP's handler. +# C++ unwinding needs exception tables even in translation units with no +# cleanup of their own. +list(APPEND ABC_SRC src/misc/util/utilAlloc.cpp) + function(abc_properties target visibility) target_include_directories(${target} ${visibility} ${CMAKE_CURRENT_SOURCE_DIR}/src ) target_compile_options_filtered(${target} ${visibility} ${ABC_CFLAGS} ${ABC_CXXFLAGS} -Wno-unused-but-set-variable ) + if(NOT MSVC) + target_compile_options(${target} PRIVATE -fexceptions) + endif() target_link_libraries(${target} ${visibility} ${ABC_LIBS}) endfunction() diff --git a/src/misc/util/abc_global.h b/src/misc/util/abc_global.h index 4bc3c1c15..ed7f2c93f 100644 --- a/src/misc/util/abc_global.h +++ b/src/misc/util/abc_global.h @@ -260,13 +260,46 @@ typedef ABC_INT64_T iword; #define ABC_PRMn(a,f) (Abc_Print(1, "%s =", (a)), Abc_Print(1, "%10.3f MB ", 1.0*((double)(f))/(1<<20))) #define ABC_PRMP(a,f,F) (Abc_Print(1, "%s =", (a)), Abc_Print(1, "%10.3f MB (%6.2f %%)\n", (1.0*((double)(f))/(1<<20)), (((double)(F))? 100.0*((double)(f))/((double)(F)) : 0.0) ) ) -#define ABC_ALLOC(type, num) ((type *) malloc(sizeof(type) * (size_t)(num))) -#define ABC_CALLOC(type, num) ((type *) calloc((size_t)(num), sizeof(type))) -#define ABC_FALLOC(type, num) ((type *) memset(malloc(sizeof(type) * (size_t)(num)), 0xff, sizeof(type) * (size_t)(num))) +/* STP catches allocation failure at its C++ API boundary. Never publish a + * null buffer to ABC's callers, which assume allocation succeeded. */ +#define ABC_STP_CHECKED_ALLOCATIONS 1 +extern void Abc_ThrowBadAlloc( void ); + +static inline size_t Abc_AllocSize( size_t size, size_t count ) +{ + if ( count && size > (size_t)-1 / count ) + Abc_ThrowBadAlloc(); + return size * count; +} +static inline void * Abc_AllocMemory( size_t size ) +{ + void * result = malloc( size ? size : 1 ); + if ( !result ) + Abc_ThrowBadAlloc(); + return result; +} +static inline void * Abc_CallocMemory( size_t size, size_t count ) +{ + size_t bytes = Abc_AllocSize( size, count ); + void * result = calloc( bytes ? bytes : 1, 1 ); + if ( !result ) + Abc_ThrowBadAlloc(); + return result; +} +static inline void * Abc_ReallocMemory( void * ptr, size_t size ) +{ + void * result = realloc( ptr, size ? size : 1 ); + if ( !result ) + Abc_ThrowBadAlloc(); + return result; +} + +#define ABC_ALLOC(type, num) ((type *) Abc_AllocMemory(Abc_AllocSize(sizeof(type), (size_t)(num)))) +#define ABC_CALLOC(type, num) ((type *) Abc_CallocMemory(sizeof(type), (size_t)(num))) +#define ABC_FALLOC(type, num) ((type *) memset(ABC_ALLOC(type, num), 0xff, Abc_AllocSize(sizeof(type), (size_t)(num)))) #define ABC_FREE(obj) ((obj) ? (free((char *) (obj)), (obj) = 0) : 0) #define ABC_REALLOC(type, obj, num) \ - ((obj) ? ((type *) realloc((char *)(obj), sizeof(type) * (size_t)(num))) : \ - ((type *) malloc(sizeof(type) * (size_t)(num)))) + ((type *) Abc_ReallocMemory((void *)(obj), Abc_AllocSize(sizeof(type), (size_t)(num)))) static inline int Abc_AbsInt( int a ) { return a < 0 ? -a : a; } static inline int Abc_MaxInt( int a, int b ) { return a > b ? a : b; } diff --git a/src/misc/util/utilAlloc.cpp b/src/misc/util/utilAlloc.cpp new file mode 100644 index 000000000..a6396fa62 --- /dev/null +++ b/src/misc/util/utilAlloc.cpp @@ -0,0 +1,13 @@ +// ABC's checked C allocation helpers report exhaustion to the embedding +// C++ caller. Keep the throw here so ABC itself can still be compiled as C. +#include +#include "misc/util/abc_global.h" + +ABC_NAMESPACE_IMPL_START + +void Abc_ThrowBadAlloc( void ) +{ + throw std::bad_alloc(); +} + +ABC_NAMESPACE_IMPL_END