diff options
-rw-r--r-- | src/CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/Makefile | 29 | ||||
-rw-r--r-- | src/abi_namespace.h | 4 | ||||
-rw-r--r-- | src/aux.cc | 27 | ||||
-rw-r--r-- | src/dwarf_eh.h | 219 | ||||
-rw-r--r-- | src/dynamic_cast.cc | 19 | ||||
-rw-r--r-- | src/exception.cc | 310 | ||||
-rw-r--r-- | src/guard.cc | 50 | ||||
-rw-r--r-- | src/memory.cc | 140 | ||||
-rw-r--r-- | src/stdexcept.cc | 100 | ||||
-rw-r--r-- | src/stdexcept.h | 107 | ||||
-rw-r--r-- | src/typeinfo | 6 | ||||
-rw-r--r-- | src/typeinfo.cc | 18 | ||||
-rw-r--r-- | src/typeinfo.h | 103 | ||||
-rw-r--r-- | test/Makefile | 3 | ||||
-rw-r--r-- | test/test_exception.cc | 17 |
16 files changed, 818 insertions, 337 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7633f03..7b79434 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,9 +3,8 @@ set(CXXRT_SOURCES dynamic_cast.cc exception.cc guard.cc - terminate.cc - typeinfo.cc stdexcept.cc + typeinfo.cc memory.cc aux.cc libelftc_dem_gnu3.c diff --git a/src/Makefile b/src/Makefile index f2ab4fc..4d8049d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,5 +1,13 @@ -OBJECTS = typeinfo.o exception.o dynamic_cast.o terminate.o guard.o \ - stdexcept.o memory.o aux.o libelftc_dem_gnu3.o libelftc_vstr.o +OBJECTS = \ + aux.o \ + dynamic_cast.o \ + exception.o \ + guard.o \ + libelftc_vstr.o \ + libelftc_dem_gnu3.o \ + memory.o \ + stdexcept.o \ + typeinfo.o # Needed for building the shared library CXXFLAGS = -fPIC @@ -35,16 +43,25 @@ libcxxabi.a: $(OBJECTS) @echo Compiling $<... @$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $< +.c.o: + @echo Compiling $<... + @$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + clean: @echo Cleaning... @rm -f $(OBJECTS) $(PRODUCTS) $(TEST_OBJECTS) vgcore* *.core +# Make seems to think that typeinfo should be created by compiling typeinfo.cc +# into an executable. This is bad because it introduces a circular dependency, +# and if you're unlucky tries to do some crazy things and deletes typeinfo. We +# add this rule as a little fudge to tell Make to behave like a good little +# program. +typeinfo: typeinfo.h + @touch typeinfo dynamic_cast.o: dynamic_cast.cc typeinfo.h abi_namespace.h typeinfo exception.o: exception.cc typeinfo.h abi_namespace.h typeinfo dwarf_eh.h guard.o: guard.cc -terminate.o: terminate.cc typeinfo.o: typeinfo.cc typeinfo.h abi_namespace.h typeinfo -stdexcept.o: stdexcept.cc stdexcept.h -memory.o: memory.cc stdexcept.h -aux.o: aux.cc stdexcept.h +memory.o: memory.cc +aux.o: aux.cc diff --git a/src/abi_namespace.h b/src/abi_namespace.h index 25627c8..dda788d 100644 --- a/src/abi_namespace.h +++ b/src/abi_namespace.h @@ -1 +1,5 @@ +/** + * The namespace used for the ABI declarations. This is currently defined to + * be the same as GNU libsupc++, however this may change in the future. + */ #define ABI_NAMESPACE __cxxabiv1 @@ -1,18 +1,41 @@ +/** + * aux.cc - Compiler helper functions. + * + * The functions declared in this file are intended to be called only by code + * that is automatically generated by C++ compilers for some common cases. + */ -#include "stdexcept.h" #include <stdlib.h> +#include "stdexcept.h" +/** + * Called to generate a bad cast exception. This function is intended to allow + * compilers to insert code generating this exception without needing to + * duplicate the code for throwing the exception in every call site. + */ extern "C" void __cxa_bad_cast() { throw std::bad_cast(); } +/** + * Called to generate a bad typeid exception. This function is intended to + * allow compilers to insert code generating this exception without needing to + * duplicate the code for throwing the exception in every call site. + */ extern "C" void __cxa_bad_typeid() { throw std::bad_typeid(); } -extern "C" void __cxa_pure_virtual() { +/** + * Compilers may (but are not required to) set any pure-virtual function's + * vtable entry to this function. This makes debugging slightly easier, as + * users can add a breakpoint on this function to tell if they've accidentally + * called a pure-virtual function. + */ +extern "C" void __cxa_pure_virtual() +{ abort(); } diff --git a/src/dwarf_eh.h b/src/dwarf_eh.h index 6d2e504..0901751 100644 --- a/src/dwarf_eh.h +++ b/src/dwarf_eh.h @@ -1,6 +1,17 @@ +/** + * dwarf_eh.h - Defines some helper functions for parsing DWARF exception + * handling tables. + * + * This file contains various helper functions that are independent of the + * language-specific code. It can be used in any personality function for the + * Itanium ABI. + */ #include <assert.h> -// _GNU_SOURCE must be defiend for unwind.h to expose some of the functions +// TODO: Factor out Itanium / ARM differences. We probably want an itanium.h +// and arm.h that can be included by this file depending on the target ABI. + +// _GNU_SOURCE must be defined for unwind.h to expose some of the functions // that we want. If it isn't, then we define it and undefine it to make sure // that it doesn't impact the rest of the program. #ifndef _GNU_SOURCE @@ -13,78 +24,111 @@ #include <stdint.h> +/// Type used for pointers into DWARF data typedef unsigned char *dw_eh_ptr_t; + // Flag indicating a signed quantity #define DW_EH_PE_signed 0x08 -/// DWARF data encoding types +/// DWARF data encoding types. enum dwarf_data_encoding { - // Unsigned, little-endian, base 128-encoded (variable length) + /// Unsigned, little-endian, base 128-encoded (variable length). DW_EH_PE_uleb128 = 0x01, - // uint16 + /// Unsigned 16-bit integer. DW_EH_PE_udata2 = 0x02, - // uint32 + /// Unsigned 32-bit integer. DW_EH_PE_udata4 = 0x03, - // uint64 + /// Unsigned 64-bit integer. DW_EH_PE_udata8 = 0x04, - // Signed versions of the above: + /// Signed, little-endian, base 128-encoded (variable length) DW_EH_PE_sleb128 = DW_EH_PE_uleb128 | DW_EH_PE_signed, + /// Signed 16-bit integer. DW_EH_PE_sdata2 = DW_EH_PE_udata2 | DW_EH_PE_signed, + /// Signed 32-bit integer. DW_EH_PE_sdata4 = DW_EH_PE_udata4 | DW_EH_PE_signed, + /// Signed 32-bit integer. DW_EH_PE_sdata8 = DW_EH_PE_udata8 | DW_EH_PE_signed }; +/** + * Returns the encoding for a DWARF EH table entry. The encoding is stored in + * the low four of an octet. The high four bits store the addressing mode. + */ static inline enum dwarf_data_encoding get_encoding(unsigned char x) { return (enum dwarf_data_encoding)(x & 0xf); } +/** + * DWARF addressing mode constants. When reading a pointer value from a DWARF + * exception table, you must know how it is stored and what the addressing mode + * is. The low four bits tell you the encoding, allowing you to decode a + * number. The high four bits tell you the addressing mode, allowing you to + * turn that number into an address in memory. + */ enum dwarf_data_relative { - // Value is omitted + /// Value is omitted DW_EH_PE_omit = 0xff, - // Absolute pointer value + /// Absolute pointer value DW_EH_PE_absptr = 0x00, - // Value relative to program counter + /// Value relative to program counter DW_EH_PE_pcrel = 0x10, - // Value relative to the text segment + /// Value relative to the text segment DW_EH_PE_textrel = 0x20, - // Value relative to the data segment + /// Value relative to the data segment DW_EH_PE_datarel = 0x30, - // Value relative to the start of the function + /// Value relative to the start of the function DW_EH_PE_funcrel = 0x40, - // Aligned pointer (Not supported yet - are they actually used?) + /// Aligned pointer (Not supported yet - are they actually used?) DW_EH_PE_aligned = 0x50, - // Pointer points to address of real value + /// Pointer points to address of real value DW_EH_PE_indirect = 0x80 }; +/** + * Returns the addressing mode component of this encoding. + */ static inline enum dwarf_data_relative get_base(unsigned char x) { return (enum dwarf_data_relative)(x & 0x70); } +/** + * Returns whether an encoding represents an indirect address. + */ static int is_indirect(unsigned char x) { - return (x & DW_EH_PE_indirect); + return ((x & DW_EH_PE_indirect) == DW_EH_PE_indirect); } +/** + * Returns the size of a fixed-size encoding. This function will abort if + * called with a value that is not a fixed-size encoding. + */ static inline int dwarf_size_of_fixed_size_field(unsigned char type) { - // Low three bits indicate size... - switch (type & 7) + switch (get_encoding(type)) { + default: abort(); + case DW_EH_PE_sdata2: case DW_EH_PE_udata2: return 2; + case DW_EH_PE_sdata4: case DW_EH_PE_udata4: return 4; + case DW_EH_PE_sdata8: case DW_EH_PE_udata8: return 8; case DW_EH_PE_absptr: return sizeof(void*); } - abort(); } /** * Read an unsigned, little-endian, base-128, DWARF value. Updates *data to - * point to the end of the value. + * point to the end of the value. Stores the number of bits read in the value + * pointed to by b, allowing you to determine the value of the highest bit, and + * therefore the sign of a signed value. + * + * This function is not intended to be called directly. Use read_sleb128() or + * read_uleb128() for reading signed and unsigned versions, respectively. */ -static uint64_t read_leb128(unsigned char** data, int *b) +static uint64_t read_leb128(dw_eh_ptr_t *data, int *b) { uint64_t uleb = 0; unsigned int bit = 0; @@ -112,14 +156,23 @@ static uint64_t read_leb128(unsigned char** data, int *b) return uleb; } -static int64_t read_uleb128(unsigned char** data) +/** + * Reads an unsigned little-endian base-128 value starting at the address + * pointed to by *data. Updates *data to point to the next byte after the end + * of the variable-length value. + */ +static int64_t read_uleb128(dw_eh_ptr_t *data) { int b; return read_leb128(data, &b); } - -static int64_t read_sleb128(unsigned char** data) +/** + * Reads a signed little-endian base-128 value starting at the address pointed + * to by *data. Updates *data to point to the next byte after the end of the + * variable-length value. + */ +static int64_t read_sleb128(dw_eh_ptr_t *data) { int bits; // Read as if it's signed @@ -132,8 +185,12 @@ static int64_t read_sleb128(unsigned char** data) } return (int64_t)uleb; } - -static uint64_t read_value(char encoding, unsigned char **data) +/** + * Reads a value using the specified encoding from the address pointed to by + * *data. Updates the value of *data to point to the next byte after the end + * of the data. + */ +static uint64_t read_value(char encoding, dw_eh_ptr_t *data) { enum dwarf_data_encoding type = get_encoding(encoding); uint64_t v; @@ -153,6 +210,7 @@ static uint64_t read_value(char encoding, unsigned char **data) READ(DW_EH_PE_sdata8, int64_t) READ(DW_EH_PE_absptr, intptr_t) #undef READ + // Read variable-length types case DW_EH_PE_sleb128: v = read_sleb128(data); break; @@ -165,7 +223,17 @@ static uint64_t read_value(char encoding, unsigned char **data) return v; } -static uint64_t resolve_indirect_value(_Unwind_Context *c, unsigned char encoding, int64_t v, dw_eh_ptr_t start) +/** + * Resolves an indirect value. This expects an unwind context, an encoding, a + * decoded value, and the start of the region as arguments. The returned value + * is a pointer to the address identified by the encoded value. + * + * If the encoding does not specify an indirect value, then this returns v. + */ +static uint64_t resolve_indirect_value(_Unwind_Context *c, + unsigned char encoding, + int64_t v, + dw_eh_ptr_t start) { switch (get_base(encoding)) { @@ -195,6 +263,9 @@ static uint64_t resolve_indirect_value(_Unwind_Context *c, unsigned char encodin } +/** + * Reads an encoding and a value, updating *data to point to the next byte. + */ static inline void read_value_with_encoding(_Unwind_Context *context, dw_eh_ptr_t *data, uint64_t *out) @@ -208,19 +279,41 @@ static inline void read_value_with_encoding(_Unwind_Context *context, *out = resolve_indirect_value(context, encoding, *out, start); } - +/** + * Structure storing a decoded language-specific data area. Use parse_lsda() + * to generate an instance of this structure from the address returned by the + * generic unwind library. + * + * You should not need to inspect the fields of this structure directly if you + * are just using this header. The structure stores the locations of the + * various tables used for unwinding exceptions and is used by the functions + * for reading values from these tables. + */ struct dwarf_eh_lsda { + /// The start of the region. This is a cache of the value returned by + /// _Unwind_GetRegionStart(). dw_eh_ptr_t region_start; + /// The start of the landing pads table. dw_eh_ptr_t landing_pads; + /// The start of the type table. dw_eh_ptr_t type_table; + /// The encoding used for entries in the type tables. unsigned char type_table_encoding; + /// The location of the call-site table. dw_eh_ptr_t call_site_table; + /// The location of the action table. dw_eh_ptr_t action_table; + /// The encoding used for entries in the call-site table. unsigned char callsite_encoding; }; -static inline struct dwarf_eh_lsda parse_lsda(_Unwind_Context *context, unsigned char *data) +/** + * Parse the header on the language-specific data area and return a structure + * containing the addresses and encodings of the various tables. + */ +static inline struct dwarf_eh_lsda parse_lsda(_Unwind_Context *context, + unsigned char *data) { struct dwarf_eh_lsda lsda; @@ -261,25 +354,39 @@ static inline struct dwarf_eh_lsda parse_lsda(_Unwind_Context *context, unsigned return lsda; } +/** + * Structure representing an action to be performed while unwinding. This + * contains the address that should be unwound to and the action record that + * provoked this action. + */ struct dwarf_eh_action { + /** + * The address that this action directs should be the new program counter + * value after unwinding. + */ dw_eh_ptr_t landing_pad; + /// The address of the action record. dw_eh_ptr_t action_record; }; /** * Look up the landing pad that corresponds to the current invoke. - * Returns true if record exists. + * Returns true if record exists. The context is provided by the generic + * unwind library and the lsda should be the result of a call to parse_lsda(). + * + * The action record is returned via the result parameter. */ -static bool - dwarf_eh_find_callsite(struct _Unwind_Context *context, - struct dwarf_eh_lsda *lsda, - struct dwarf_eh_action *result) +static bool dwarf_eh_find_callsite(struct _Unwind_Context *context, + struct dwarf_eh_lsda *lsda, + struct dwarf_eh_action *result) { - result->action_record = 0; - result->landing_pad = 0; + result->action_record = 0; + result->landing_pad = 0; + // The current instruction pointer offset within the region uint64_t ip = _Unwind_GetIP(context) - _Unwind_GetRegionStart(context); unsigned char *callsite_table = (unsigned char*)lsda->call_site_table; + while (callsite_table <= lsda->action_table) { // Once again, the layout deviates from the spec. @@ -287,20 +394,25 @@ static bool call_site_start = read_value(lsda->callsite_encoding, &callsite_table); call_site_size = read_value(lsda->callsite_encoding, &callsite_table); - // Call site entries are started + // Call site entries are sorted, so if we find a call site that's after + // the current instruction pointer then there is no action associated + // with this call and we should unwind straight through this frame + // without doing anything. if (call_site_start > ip) { break; } + // Read the address of the landing pad and the action from the call + // site table. landing_pad = read_value(lsda->callsite_encoding, &callsite_table); action = read_uleb128(&callsite_table); - // we shold not include call_site_start (begin of the region) - // address in ip range. For each call site - // - // address1: call proc - // address2: next instruction - // - // call stack contains address2 and not address1. - // address1 can be at the end of another EH region. + // We should not include the call_site_start (beginning of the region) + // address in the ip range. For each call site: + // + // address1: call proc + // address2: next instruction + // + // The call stack contains address2 and not address1, address1 can be + // at the end of another EH region. if (call_site_start < ip && ip <= call_site_start + call_site_size) { if (action) @@ -320,3 +432,20 @@ static bool } return false; } + +/// Defines an exception class from 8 bytes (endian independent) +#define EXCEPTION_CLASS(a,b,c,d,e,f,g,h) \ + (((uint64_t)a << 56) +\ + ((uint64_t)b << 48) +\ + ((uint64_t)c << 40) +\ + ((uint64_t)d << 32) +\ + ((uint64_t)e << 24) +\ + ((uint64_t)f << 16) +\ + ((uint64_t)g << 8) +\ + ((uint64_t)h)) + +#define GENERIC_EXCEPTION_CLASS(e,f,g,h) \ + ((uint32_t)e << 24) +\ + ((uint32_t)f << 16) +\ + ((uint32_t)g << 8) +\ + ((uint32_t)h) diff --git a/src/dynamic_cast.cc b/src/dynamic_cast.cc index a68248b..043796f 100644 --- a/src/dynamic_cast.cc +++ b/src/dynamic_cast.cc @@ -14,6 +14,10 @@ struct vtable_header const __class_type_info *type; }; +/** + * Simple macro that does pointer arithmetic in bytes but returns a value of + * the same type as the original. + */ #define ADD_TO_PTR(x, off) (__typeof__(x))(((char*)x) + off) bool __class_type_info::can_cast_to(const struct __class_type_info *other) const @@ -104,7 +108,17 @@ void *__vmi_class_type_info::cast_to(void *obj, const struct __class_type_info * } /** - * ABI function used to implement the dynamic_cast<> operator. + * ABI function used to implement the dynamic_cast<> operator. Some cases of + * this operator are implemented entirely in the compiler (e.g. to void*). + * This function implements the dynamic casts of the form dynamic_cast<T>(v). + * This will be translated to a call to this function with the value v as the + * first argument. The type id of the static type of v is the second argument + * and the type id of the destination type (T) is the third argument. + * + * The third argument is a hint about the compiler's guess at the correct + * pointer offset. If this value is negative, then -1 indicates no hint, -2 + * that src is not a public base of dst, and -3 that src is a multiple public + * base type but never a virtual base type */ extern "C" void* __dynamic_cast(const void *sub, const __class_type_info *src, @@ -112,7 +126,8 @@ extern "C" void* __dynamic_cast(const void *sub, ptrdiff_t src2dst_offset) { char *vtable_location = *(char**)sub; - const vtable_header *header = (const vtable_header*)(vtable_location - sizeof(vtable_header)); + const vtable_header *header = + (const vtable_header*)(vtable_location - sizeof(vtable_header)); void *leaf = ADD_TO_PTR((void*)sub, header->leaf_offset); return header->type->cast_to(leaf, dst); } diff --git a/src/exception.cc b/src/exception.cc index cb8e858..1b801d7 100644 --- a/src/exception.cc +++ b/src/exception.cc @@ -7,10 +7,13 @@ #include "dwarf_eh.h" -namespace std { - void unexpected(); +namespace std +{ + void unexpected(); } + extern "C" std::type_info *__cxa_current_exception_type(); + extern "C" char* __cxa_demangle(const char* mangled_name, char* buf, size_t* n, @@ -24,12 +27,16 @@ extern "C" char* __cxa_demangle(const char* mangled_name, * because we aim for ABI-compatibility with the GNU implementation, and * various checks may test for equality of the class, which is incorrect. */ -static const uint64_t exception_class = *(int64_t*)"GNUCC++\0"; +static const uint64_t exception_class = + EXCEPTION_CLASS('G', 'N', 'U', 'C', 'C', '+', '+', '\0'); /** * The low four bytes of the exception class, indicating that we conform to the - * Itanium C++ ABI. + * Itanium C++ ABI. This is currently unused, but should be used in the future + * if we change our exception class, to allow this library and libsupc++ to be + * linked to the same executable and both to interoperate. */ -static const uint32_t abi_exception_class = *(int32_t*)"C++\0"; +static const uint32_t abi_exception_class = + GENERIC_EXCEPTION_CLASS('C', '+', '+', '\0'); namespace std { @@ -54,6 +61,17 @@ typedef void (*terminate_handler)(); * defined by the Itanium ABI spec, so should be interoperable with any other * implementation of this spec, such as GNU libsupc++. * + * This structure is allocated when an exception is thrown. Unwinding happens + * in two phases, the first looks for a handler and the second installs the + * context. This structure stores a cache of the handler location between + * phase 1 and phase 2. Unfortunately, cleanup information is not cached, so + * must be looked up in both phases. This happens for two reasons. The first + * is that we don't know how many frames containing cleanups there will be, and + * we should avoid dynamic allocation during unwinding (the exception may be + * reporting that we've run out of memory). The second is that finding + * cleanups is much cheaper than finding handlers, because we don't have to + * look at the type table at all. + * * Note: Several fields of this structure have not-very-informative names. * These are taken from the ABI spec and have not been changed to make it * easier for people referring to to the spec while reading this code. @@ -68,43 +86,66 @@ struct __cxa_exception unexpected_handler unexpectedHandler; /** Hander called to terminate. */ terminate_handler terminateHandler; - /** Next exception in the list. If an exception is thrown inside a catch + /** + * Next exception in the list. If an exception is thrown inside a catch * block and caught in a nested catch, this points to the exception that - * will be handled after the inner catch block completes. */ + * will be handled after the inner catch block completes. + */ __cxa_exception *nextException; - - /** The number of handlers that currently have references to this + /** + * The number of handlers that currently have references to this * exception. The top (non-sign) bit of this is used as a flag to indicate * that the exception is being rethrown, so should not be deleted when its - * handler count reaches 0 (which it doesn't with the top bit set).*/ + * handler count reaches 0 (which it doesn't with the top bit set). + */ int handlerCount; - /** The selector value to be returned when installing the catch handler. + /** + * The selector value to be returned when installing the catch handler. * Used at the call site to determine which catch() block should execute. - * This is found in phase 1 of unwinding then installed in phase 2.*/ + * This is found in phase 1 of unwinding then installed in phase 2. + */ int handlerSwitchValue; + /** + * The action record for the catch. This is cached during phase 1 + * unwinding. + */ const char *actionRecord; - /** Pointer to the language-specific data area (LSDA) for the handler + /** + * Pointer to the language-specific data area (LSDA) for the handler * frame. This is unused in this implementation, but set for ABI - * compatibility in case we want to mix code in very weird ways. */ + * compatibility in case we want to mix code in very weird ways. + */ const char *languageSpecificData; /** The cached landing pad for the catch handler.*/ void *catchTemp; - /** The pointer that will be returned as the pointer to the object. When + /** + * The pointer that will be returned as the pointer to the object. When * throwing a class and catching a virtual superclass (for example), we - * need to adjust the thrown pointer to make it all work correctly. */ + * need to adjust the thrown pointer to make it all work correctly. + */ void *adjustedPtr; - /** The language-agnostic part of the exception header. */ _Unwind_Exception unwindHeader; }; /** * ABI-specified globals structure. Returned by the __cxa_get_globals() - * function and its fast variant. + * function and its fast variant. This is a per-thread structure - every + * thread will have one lazily allocated. + * + * This structure is defined by the ABI, so may be used outside of this + * library. */ struct __cxa_eh_globals { + /** + * A linked list of exceptions that are currently caught. There may be + * several of these in nested catch() blocks. + */ __cxa_exception *caughtExceptions; + /** + * The number of uncaught exceptions. + */ unsigned int uncaughtExceptions; }; @@ -123,11 +164,13 @@ struct __cxa_thread_info terminate_handler terminateHandler; /** The unexpected exception handler for this thread. */ unexpected_handler unexpectedHandler; - /** The number of emergency buffers held by this thread. This is 0 in + /** + * The number of emergency buffers held by this thread. This is 0 in * normal operation - the emergency buffers are only used when malloc() * fails to return memory for allocating an exception. Threads are not * permitted to hold more than 4 emergency buffers (as per recommendation - * in ABI spec).*/ + * in ABI spec [3.3.1]). + */ int emergencyBuffersHeld; /** * The public part of this structure, accessible from outside of this @@ -144,10 +187,22 @@ static unexpected_handler unexpectedHandler = abort; /** Key used for thread-local data. */ static pthread_key_t eh_key; +/** + * The type of a handler that has been found. + */ typedef enum { + /** No handler. */ handler_none, + /** + * A cleanup - the exception will propagate through this frame, but code + * must be run when this happens. + */ handler_cleanup, + /** + * A catch statement. The exception will not propagate past this frame + * (without an explicit rethrow). + */ handler_catch } handler_type; @@ -301,6 +356,9 @@ static char *emergency_malloc(size_t size) break; } } + // If there still isn't a buffer available, then sleep on the condition + // variable. This will be signalled when another thread releases one + // of the emergency buffers. if (buffer < 0) { pthread_cond_wait(&emergency_malloc_wait, &emergency_malloc_lock); @@ -331,7 +389,8 @@ static void emergency_malloc_free(char *ptr) break; } } - assert(buffer > 0 && "Trying to free something that is not an emergency buffer!"); + assert(buffer > 0 && + "Trying to free something that is not an emergency buffer!"); // emergency_malloc() is expected to return 0-initialized data. We don't // zero the buffer when allocating it, because the static buffers will // begin life containing 0 values. @@ -339,14 +398,19 @@ static void emergency_malloc_free(char *ptr) // Signal the condition variable to wake up any threads that are blocking // waiting for some space in the emergency buffer pthread_mutex_lock(&emergency_malloc_lock); - buffer_allocated[buffer] = true; + // In theory, we don't need to do this with the lock held. In practice, + // our array of bools will probably be updated using 32-bit or 64-bit + // memory operations, so this update may clobber adjacent values. + buffer_allocated[buffer] = false; pthread_cond_signal(&emergency_malloc_wait); pthread_mutex_unlock(&emergency_malloc_lock); } /** * Allocates an exception structure. Returns a pointer to the space that can - * be used to store an object of thrown_size bytes. + * be used to store an object of thrown_size bytes. This function will use an + * emergency buffer if malloc() fails, and may block if there are no such + * buffers available. */ extern "C" void *__cxa_allocate_exception(size_t thrown_size) { @@ -358,6 +422,8 @@ extern "C" void *__cxa_allocate_exception(size_t thrown_size) if (0 == buffer) { buffer = emergency_malloc(size); + // This is only reached if the allocation is greater than 1KB, and + // anyone throwing objects that big really should know better. if (0 == buffer) { fprintf(stderr, "Out of memory attempting to allocate exception\n"); @@ -379,7 +445,7 @@ extern "C" void __cxa_free_exception(void *thrown_exception) { __cxa_exception *ex = ((__cxa_exception*)thrown_exception) - 1; - // Free the + // Free the object that was thrown, calling its destructor if (0 != ex->exceptionDestructor) { try @@ -396,7 +462,8 @@ extern "C" void __cxa_free_exception(void *thrown_exception) char *e = (char*)ex; // If this allocation is within the address range of the emergency buffer, // don't call free() because it was not allocated with malloc() - if ((e > emergency_buffer) && (e < (emergency_buffer + sizeof(emergency_buffer)))) + if ((e > emergency_buffer) && + (e < (emergency_buffer + sizeof(emergency_buffer)))) { emergency_malloc_free(e); } @@ -417,7 +484,8 @@ extern "C" void __cxa_free_exception(void *thrown_exception) static _Unwind_Reason_Code trace(struct _Unwind_Context *context, void *c) { Dl_info myinfo; - int mylookup = dladdr((void*)(uintptr_t)__cxa_current_exception_type, &myinfo); + int mylookup = + dladdr((void*)(uintptr_t)__cxa_current_exception_type, &myinfo); void *ip = (void*)_Unwind_GetIP(context); Dl_info info; if (dladdr(ip, &info) != 0) @@ -449,7 +517,7 @@ static void report_failure(_Unwind_Reason_Code err, __cxa_exception *thrown_exce break; case _URC_END_OF_STACK: fprintf(stderr, "Terminating due to uncaught exception %p", - thrown_exception); + (void*)thrown_exception); size_t bufferSize = 128; char *demangled = (char*)malloc(bufferSize); @@ -480,8 +548,19 @@ extern "C" void __cxa_throw(void *thrown_exception, __cxa_thread_info *info = thread_info(); + // Cache the handlers, the thread-local versions if they are set, the + // global ones if not. This is required so that calls to set_unexpected() + // or set_terminate() do not affect exceptions currently being propagated. ex->unexpectedHandler = info->unexpectedHandler; + if (0 == ex->unexpectedHandler) + { + ex->unexpectedHandler = unexpectedHandler; + } ex->terminateHandler = info->terminateHandler; + if (0 == ex->terminateHandler) + { + ex->terminateHandler = terminateHandler; + } ex->exceptionType = tinfo; @@ -493,6 +572,9 @@ extern "C" void __cxa_throw(void *thrown_exception, info->globals.uncaughtExceptions++; _Unwind_Reason_Code err = _Unwind_RaiseException(&ex->unwindHeader); + // The _Unwind_RaiseException() function should not return, it should + // unwind the stack past this function. If it does return, then something + // has gone wrong. report_failure(err, ex); } @@ -516,7 +598,8 @@ extern "C" void __cxa_rethrow() if (0 == ex) { - fprintf(stderr, "Attempting to rethrow an exception that doesn't exist!\n"); + fprintf(stderr, + "Attempting to rethrow an exception that doesn't exist!\n"); std::terminate(); } @@ -553,7 +636,7 @@ static std::type_info *get_type_info_entry(_Unwind_Context *context, // Read the value, but it's probably an indirect reference... int64_t offset = read_value(lsda->type_table_encoding, &record); - // If the entry is 0, don't try to dereference it. That would be bad. + // (If the entry is 0, don't try to dereference it. That would be bad.) if (offset == 0) { return 0; } // ...so we need to resolve it @@ -567,13 +650,16 @@ static std::type_info *get_type_info_entry(_Unwind_Context *context, * object. If ex is 0 then it is assumed to be a foreign exception and only * matches cleanups. */ -static bool check_type_signature(__cxa_exception *ex, const std::type_info *type) +static bool check_type_signature(__cxa_exception *ex, + const std::type_info *type) { + // TODO: For compatibility with the GNU implementation, we should move this + // out into a __do_catch() virtual function in std::type_info void *exception_ptr = (void*)(ex+1); const std::type_info *ex_type = ex->exceptionType; const __pointer_type_info *ptr_type = - dynamic_cast<const __pointer_type_info*>(ex_type); + dynamic_cast<const __pointer_type_info*>(ex_type); if (0 != ptr_type) { exception_ptr = *(void**)exception_ptr; @@ -583,7 +669,10 @@ static bool check_type_signature(__cxa_exception *ex, const std::type_info *type // Note: A 0 here is a catchall, not a cleanup, so we return true to // indicate that we found a catch. // - // TODO: Provide a class for matching against foreign exceptions. + // TODO: Provide a class for matching against foreign exceptions. This is + // already done in libobjc2, allowing C++ exceptions to be boxed as + // Objective-C objects. We should do something similar, allowing foreign + // exceptions to be wrapped in a C++ exception and delivered. if (0 == type) { if (ex) @@ -595,25 +684,27 @@ static bool check_type_signature(__cxa_exception *ex, const std::type_info *type if (0 == ex) { return false; } - const __pointer_type_info *target_ptr_type = - dynamic_cast<const __pointer_type_info*>(type); + const __pointer_type_info *target_ptr_type = + dynamic_cast<const __pointer_type_info*>(type); - if (0 != ptr_type && 0 != target_ptr_type) - { - if (ptr_type->__flags & ~target_ptr_type->__flags) { - // handler pointer is less qualified - return false; - } + if (0 != ptr_type && 0 != target_ptr_type) + { + if (ptr_type->__flags & ~target_ptr_type->__flags) + { + // Handler pointer is less qualified + return false; + } - // special case for void* handler - if(*target_ptr_type->__pointee == typeid(void)) { - ex->adjustedPtr = exception_ptr; - return true; - } + // Special case for void* handler. + if(*target_ptr_type->__pointee == typeid(void)) + { + ex->adjustedPtr = exception_ptr; + return true; + } - ex_type = ptr_type->__pointee; - type = target_ptr_type->__pointee; - } + ex_type = ptr_type->__pointee; + type = target_ptr_type->__pointee; + } // If the types are the same, no casting is needed. if (*type == *ex_type) @@ -623,16 +714,16 @@ static bool check_type_signature(__cxa_exception *ex, const std::type_info *type } const __class_type_info *cls_type = - dynamic_cast<const __class_type_info*>(ex_type); + dynamic_cast<const __class_type_info*>(ex_type); const __class_type_info *target_cls_type = - dynamic_cast<const __class_type_info*>(type); + dynamic_cast<const __class_type_info*>(type); if (0 != cls_type && - 0 != target_cls_type && - cls_type->can_cast_to(target_cls_type)) + 0 != target_cls_type && + cls_type->can_cast_to(target_cls_type)) { ex->adjustedPtr = cls_type->cast_to(exception_ptr, target_cls_type); - return true; + return true; } return false; } @@ -727,13 +818,15 @@ extern "C" _Unwind_Reason_Code __gxx_personality_v0(int version, // _Unwind_Exception. bool foreignException = exceptionClass != exception_class; + // If this isn't a foreign exception, then we have a C++ exception structure if (!foreignException) { - ex = (__cxa_exception*) ((char*)exceptionObject - offsetof(struct - __cxa_exception, unwindHeader)); + ex = (__cxa_exception*)((char*)exceptionObject - + offsetof(struct __cxa_exception, unwindHeader)); } - unsigned char *lsda_addr = (unsigned char*)_Unwind_GetLanguageSpecificData(context); + unsigned char *lsda_addr = + (unsigned char*)_Unwind_GetLanguageSpecificData(context); // No LSDA implies no landing pads - try the next frame if (0 == lsda_addr) { return _URC_CONTINUE_UNWIND; } @@ -760,16 +853,16 @@ extern "C" _Unwind_Reason_Code __gxx_personality_v0(int version, if (actions & _UA_SEARCH_PHASE) { struct dwarf_eh_lsda lsda = parse_lsda(context, lsda_addr); + if (!dwarf_eh_find_callsite(context, &lsda, &action)) - { - // EH range not found. This happens if exception is thrown - // and not catched inside cleanup (destructor). - // We should call terminate() in this case - // catchTemp (landing pad) field of exception object will - // contain null when personality function will be called - // with _UA_HANDLER_FRAME action - return _URC_HANDLER_FOUND; - } + { + // EH range not found. This happens if exception is thrown and not + // caught inside a cleanup (destructor). We should call + // terminate() in this case. The catchTemp (landing pad) field of + // exception object will contain null when personality function is + // called with _UA_HANDLER_FRAME action for phase 2 unwinding. + return _URC_HANDLER_FOUND; + } handler_type found_handler = check_action_record(context, &lsda, action.action_record, ex, &selector); @@ -799,8 +892,7 @@ extern "C" _Unwind_Reason_Code __gxx_personality_v0(int version, // context for a cleanup. if (!(actions & _UA_HANDLER_FRAME)) { - // cleanup - + // cleanup struct dwarf_eh_lsda lsda = parse_lsda(context, lsda_addr); dwarf_eh_find_callsite(context, &lsda, &action); if (0 == action.landing_pad) { return _URC_CONTINUE_UNWIND; } @@ -809,18 +901,18 @@ extern "C" _Unwind_Reason_Code __gxx_personality_v0(int version, // Ignore handlers this time. if (found_handler != handler_cleanup) { return _URC_CONTINUE_UNWIND; } } - else if (ex->catchTemp == 0) - { - // uncaught exception in claenup, calling terminate - std::terminate(); - } else if (foreignException) { struct dwarf_eh_lsda lsda = parse_lsda(context, lsda_addr); - dwarf_eh_find_callsite(context, &lsda, &action); + dwarf_eh_find_callsite(context, &lsda, &action); check_action_record(context, &lsda, action.action_record, ex, &selector); } + else if (ex->catchTemp == 0) + { + // Uncaught exception in cleanup, calling terminate + std::terminate(); + } else { // Restore the saved info if we saved some last time. @@ -832,13 +924,11 @@ extern "C" _Unwind_Reason_Code __gxx_personality_v0(int version, _Unwind_SetIP(context, (unsigned long)action.landing_pad); - _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), (unsigned long)exceptionObject); + _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), + (unsigned long)exceptionObject); _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), selector); return _URC_INSTALL_CONTEXT; - - - exit(0); } /** @@ -853,7 +943,7 @@ extern "C" void *__cxa_begin_catch(void *e) throw() #else extern "C" void *__cxa_begin_catch(void *e) #endif -{ +{ // Decrement the uncaught exceptions count __cxa_eh_globals *globals = __cxa_get_globals(); globals->uncaughtExceptions--; @@ -861,13 +951,14 @@ extern "C" void *__cxa_begin_catch(void *e) if (exceptionObject->exception_class == exception_class) { - __cxa_exception *ex = (__cxa_exception*) - ((char*)exceptionObject - offsetof(struct __cxa_exception, unwindHeader)); + __cxa_exception *ex = (__cxa_exception*) ((char*)exceptionObject - + offsetof(struct __cxa_exception, unwindHeader)); if (ex->handlerCount == 0) { - // Add this to the front of the list of exceptions being handled and - // increment its handler count so that it won't be deleted prematurely. + // Add this to the front of the list of exceptions being handled + // and increment its handler count so that it won't be deleted + // prematurely. ex->nextException = globals->caughtExceptions; globals->caughtExceptions = ex; } @@ -952,8 +1043,9 @@ extern "C" void __cxa_end_catch() globals->caughtExceptions = ex->nextException; if (deleteException) { - // __cxa_free_exception() expects to be passed the thrown object, which - // immediately follows the exception, not the exception itself + // __cxa_free_exception() expects to be passed the thrown object, + // which immediately follows the exception, not the exception + // itself __cxa_free_exception(ex+1); } } @@ -979,8 +1071,8 @@ extern "C" void __cxa_call_unexpected(void*exception) _Unwind_Exception *exceptionObject = (_Unwind_Exception*)exception; if (exceptionObject->exception_class == exception_class) { - __cxa_exception *ex = (__cxa_exception*) - ((char*)exceptionObject - offsetof(struct __cxa_exception, unwindHeader)); + __cxa_exception *ex = (__cxa_exception*) ((char*)exceptionObject - + offsetof(struct __cxa_exception, unwindHeader)); if (ex->unexpectedHandler) { ex->unexpectedHandler(); @@ -1002,15 +1094,25 @@ extern "C" void *__cxa_get_exception_ptr(void *exceptionObject) offsetof(struct __cxa_exception, unwindHeader)))->adjustedPtr; } - +/** + * As an extension, we provide the ability for the unexpected and terminate + * handlers to be thread-local. We default to the standards-compliant + * behaviour where they are global. + */ static bool thread_local_handlers = false; namespace pathscale { - void set_use_thread_local_handlers(bool flag) + /** + * Sets whether unexpected and terminate handlers should be thread-local. + */ + void set_use_thread_local_handlers(bool flag) throw() { thread_local_handlers = flag; } + /** + * Sets a thread-local unexpected handler. + */ unexpected_handler set_unexpected(unexpected_handler f) throw() { static __cxa_thread_info *info = thread_info(); @@ -1018,6 +1120,9 @@ namespace pathscale info->unexpectedHandler = f; return old; } + /** + * Sets a thread-local terminate handler. + */ terminate_handler set_terminate(terminate_handler f) throw() { static __cxa_thread_info *info = thread_info(); @@ -1029,17 +1134,28 @@ namespace pathscale namespace std { + /** + * Sets the function that will be called when an exception specification is + * violated. + */ unexpected_handler set_unexpected(unexpected_handler f) throw() { if (thread_local_handlers) { return pathscale::set_unexpected(f); } return __sync_lock_test_and_set(&unexpectedHandler, f); } + /** + * Sets the function that is called to terminate the program. + */ terminate_handler set_terminate(terminate_handler f) throw() { if (thread_local_handlers) { return pathscale::set_terminate(f); } return __sync_lock_test_and_set(&terminateHandler, f); } + /** + * Terminates the program, calling a custom terminate implementation if + * required. + */ void terminate() { static __cxa_thread_info *info = thread_info_fast(); @@ -1052,6 +1168,11 @@ namespace std } terminateHandler(); } + /** + * Called when an unexpected exception is encountered (i.e. an exception + * violates an exception specification). This calls abort() unless a + * custom handler has been set.. + */ void unexpected() { static __cxa_thread_info *info = thread_info_fast(); @@ -1064,9 +1185,14 @@ namespace std } unexpectedHandler(); } - bool uncaught_exception() throw() { - __cxa_thread_info *info = thread_info(); - return info->globals.uncaughtExceptions != 0; - } + /** + * Returns whether there are any exceptions currently being thrown that + * have not been caught. This can occur inside a nested catch statement. + */ + bool uncaught_exception() throw() + { + __cxa_thread_info *info = thread_info(); + return info->globals.uncaughtExceptions != 0; + } } diff --git a/src/guard.cc b/src/guard.cc index a41be8f..23a4bae 100644 --- a/src/guard.cc +++ b/src/guard.cc @@ -1,7 +1,27 @@ +/** + * guard.cc: Functions for thread-safe static initialisation. + * + * Static values in C++ can be initialised lazily their first use. This file + * contains functions that are used to ensure that two threads attempting to + * initialize the same static do not call the constructor twice. This is + * important because constructors can have side effects, so calling the + * constructor twice may be very bad. + * + * Statics that require initialisation are protected by a 64-bit value. Any + * platform that can do 32-bit atomic test and set operations can use this + * value as a low-overhead lock. Because statics (in most sane code) are + * accessed far more times than they are initialised, this lock implementation + * is heavily optimised towards the case where the static has already been + * initialised. + */ #include <stdint.h> #include <pthread.h> -static int32_t *low_32_bits(int64_t *ptr) +/** + * Returns a pointer to the low 32 bits in a 64-bit value, respecting the + * platform's byte order. + */ +static int32_t *low_32_bits(volatile int64_t *ptr) { int32_t *low= (int32_t*)ptr; // Test if the machine is big endian - constant propagation at compile time @@ -14,26 +34,43 @@ static int32_t *low_32_bits(int64_t *ptr) return low; } -extern "C" int __cxa_guard_acquire(int64_t *guard_object) +/** + * Acquires a lock on a guard, returning 0 if the object has already been + * initialised, and 1 if it has not. If the object is already constructed then + * this function just needs to read a byte from memory and return. + */ +extern "C" int __cxa_guard_acquire(volatile int64_t *guard_object) { char first_byte = (*guard_object) >> 56; if (1 == first_byte) { return 0; } int32_t *lock = low_32_bits(guard_object); + // Simple spin lock using the low 32 bits. We assume that concurrent + // attempts to initialize statics are very rare, so we don't need to + // optimise for the case where we have lots of threads trying to acquire + // the lock at the same time. while (!__sync_bool_compare_and_swap_4(lock, 0, 1)) { - // TODO: Use some of the remaining 24 bits to define a mutex to sleep - // on. Whether this is actually worth bothering with depends on - // whether there is likely to be any contention. sched_yield(); } - return 1; + // We have to test the guard again, in case another thread has performed + // the initialisation while we were trying to acquire the lock. + first_byte = (*guard_object) >> 56; + return (1 != first_byte); } +/** + * Releases the lock without marking the object as initialised. This function + * is called if initialising a static causes an exception to be thrown. + */ extern "C" void __cxa_guard_abort(int64_t *guard_object) { int32_t *lock = low_32_bits(guard_object); *lock = 0; } +/** + * Releases the guard and marks the object as initialised. This function is + * called after successful initialisation of a static. + */ extern "C" void __cxa_guard_release(int64_t *guard_object) { // Set the first byte to 1 @@ -41,4 +78,3 @@ extern "C" void __cxa_guard_release(int64_t *guard_object) __cxa_guard_abort(guard_object); } - diff --git a/src/memory.cc b/src/memory.cc index 7a7fb3c..5e09d65 100644 --- a/src/memory.cc +++ b/src/memory.cc @@ -1,89 +1,111 @@ - - -// This file contains definition of C++ new/delete operators - +/** + * memory.cc - Contains stub definition of C++ new/delete operators. + * + * These definitions are intended to be used for testing and are weak symbols + * to allow them to be replaced by definitions from a STL implementation. + * These versions simply wrap malloc() and free(), they do not provide a + * C++-specific allocator. + */ #include <stddef.h> #include <stdlib.h> #include "stdexcept.h" -namespace std { - void terminate(); +namespace std +{ + struct nothrow_t {}; } -typedef void (*new_handler)(); -static new_handler new_handl = &std::terminate; - -namespace std { - new_handler set_new_handler(new_handler handl) { - new_handler old = new_handl; - new_handl = handl; - return old; - } +/// The type of the function called when allocation fails. +typedef void (*new_handler)(); +/** + * The function to call when allocation fails. By default, there is no + * handler and a bad allocation exception is thrown if an allocation fails. + */ +static new_handler new_handl; + +namespace std +{ + /** + * Sets a function to be called when there is a failure in new. + */ + __attribute__((weak)) + new_handler set_new_handler(new_handler handler) + { + return __sync_lock_test_and_set(&new_handl, handler); + } } __attribute__((weak)) -void * operator new(size_t size) { - - void * mem = malloc(size); - while(mem == NULL) { - if(new_handl != NULL) { - new_handl(); - } else { - throw std::bad_alloc(); - } - mem = malloc(size); - } - - return mem; -} - - -namespace std { - struct nothrow_t {}; +void* operator new(size_t size) +{ + void * mem = malloc(size); + while (0 == mem) + { + if (0 != new_handl) + { + new_handl(); + } + else + { + throw std::bad_alloc(); + } + mem = malloc(size); + } + + return mem; } __attribute__((weak)) -void * operator new(size_t size, const std::nothrow_t &) throw() { - - void * mem = malloc(size); - while(mem == NULL) { - if(new_handl != NULL) { - try { - new_handl(); - } - catch(std::bad_alloc &) { - // nothrow operator new should return NULL in case of - // std::bad_alloc exception in new handler - return NULL; - } - } else { - return NULL; - } - mem = malloc(size); - } - - return mem; +void* operator new(size_t size, const std::nothrow_t &) throw() +{ + void *mem = malloc(size); + while (0 == mem) + { + if (0 == new_handl) + { + try + { + new_handl(); + } + catch (...) + { + // nothrow operator new should return NULL in case of + // std::bad_alloc exception in new handler + return NULL; + } + } + else + { + return NULL; + } + mem = malloc(size); + } + + return mem; } __attribute__((weak)) -void operator delete(void * ptr) { - free(ptr); +void operator delete(void * ptr) +{ + free(ptr); } __attribute__((weak)) -void * operator new[](size_t size) { - return ::operator new(size); +void * operator new[](size_t size) +{ + return ::operator new(size); } __attribute__((weak)) -void operator delete[](void * ptr) { - ::operator delete(ptr); +void operator delete[](void * ptr) +{ + ::operator delete(ptr); } diff --git a/src/stdexcept.cc b/src/stdexcept.cc index e5ee812..ba74240 100644 --- a/src/stdexcept.cc +++ b/src/stdexcept.cc @@ -1,80 +1,60 @@ - +/** + * stdexcept.cc - provides stub implementations of the exceptions required by the runtime. + */ #include "stdexcept.h" namespace std { - -exception::exception() throw() { -} - -exception::exception(const exception&) throw() { -} - -exception& exception::operator=(const exception&) throw() { - return *this; -} - -exception::~exception() { -} - -const char* exception::what() const throw() { - return "std::exception"; -} - - -bad_alloc::bad_alloc() throw() { -} - -bad_alloc::bad_alloc(const bad_alloc&) throw() { -} - -bad_alloc& bad_alloc::operator=(const bad_alloc&) throw() { - return *this; -} - -bad_alloc::~bad_alloc() { -} - -const char* bad_alloc::what() const throw() { - return "cxxrt::bad_alloc"; -} - - - -bad_cast::bad_cast() throw() { -} - -bad_cast::bad_cast(const bad_cast&) throw() { +exception::exception() throw() {} +exception::~exception() {} +exception::exception(const exception&) throw() {} +exception& exception::operator=(const exception&) throw() +{ + return *this; } - -bad_cast& bad_cast::operator=(const bad_cast&) throw() { - return *this; +const char* exception::what() const throw() +{ + return "std::exception"; } -bad_cast::~bad_cast() { +bad_alloc::bad_alloc() throw() {} +bad_alloc::~bad_alloc() {} +bad_alloc::bad_alloc(const bad_alloc&) throw() {} +bad_alloc& bad_alloc::operator=(const bad_alloc&) throw() +{ + return *this; } - -const char* bad_cast::what() const throw() { - return "std::bad_cast"; +const char* bad_alloc::what() const throw() +{ + return "cxxrt::bad_alloc"; } -bad_typeid::bad_typeid() throw() { -} -bad_typeid::bad_typeid(const bad_typeid &__rhs) throw() { +bad_cast::bad_cast() throw() {} +bad_cast::~bad_cast() {} +bad_cast::bad_cast(const bad_cast&) throw() {} +bad_cast& bad_cast::operator=(const bad_cast&) throw() +{ + return *this; } - -bad_typeid::~bad_typeid() { +const char* bad_cast::what() const throw() +{ + return "std::bad_cast"; } -bad_typeid& bad_typeid::operator=(const bad_typeid &__rhs) throw() { - return *this; +bad_typeid::bad_typeid() throw() {} +bad_typeid::~bad_typeid() {} +bad_typeid::bad_typeid(const bad_typeid &__rhs) throw() {} +bad_typeid& bad_typeid::operator=(const bad_typeid &__rhs) throw() +{ + return *this; } -const char* bad_typeid::what() const throw() { - return "std::bad_typeid"; +const char* bad_typeid::what() const throw() +{ + return "std::bad_typeid"; } -} +} // namespace std diff --git a/src/stdexcept.h b/src/stdexcept.h index af06bda..718f810 100644 --- a/src/stdexcept.h +++ b/src/stdexcept.h @@ -1,56 +1,61 @@ - - -// This header defines standard exception classes which are needed -// for implementing new/delete operators - - -#ifndef __LIBCXXSUP_STDEXCEPT_H__ -#define __LIBCXXSUP_STDEXCEPT_H__ - -namespace std { - -class exception { -public: - exception() throw(); - exception(const exception&) throw(); - exception& operator=(const exception&) throw(); - virtual ~exception(); - virtual const char* what() const throw(); -}; - - -class bad_alloc: public exception { -public: - bad_alloc() throw(); - bad_alloc(const bad_alloc&) throw(); - bad_alloc& operator=(const bad_alloc&) throw(); - ~bad_alloc(); - virtual const char* what() const throw(); -}; - - -class bad_cast: public exception { -public: - bad_cast() throw(); - bad_cast(const bad_cast&) throw(); - bad_cast& operator=(const bad_cast&) throw(); - virtual ~bad_cast(); - virtual const char* what() const throw(); -}; - - -class bad_typeid: public exception { -public: - bad_typeid() throw(); - bad_typeid(const bad_typeid &__rhs) throw(); - virtual ~bad_typeid(); - bad_typeid& operator=(const bad_typeid &__rhs) throw(); - virtual const char* what() const throw(); -}; +/** + * stdexcept.h - provides a stub version of <stdexcept>, which defines enough + * of the exceptions for the runtime to use. + */ + +namespace std +{ + + class exception + { + public: + exception() throw(); + exception(const exception&) throw(); + exception& operator=(const exception&) throw(); + virtual ~exception(); + virtual const char* what() const throw(); + }; + + + /** + * Bad allocation exception. Thrown by ::operator new() if it fails. + */ + class bad_alloc: public exception + { + public: + bad_alloc() throw(); + bad_alloc(const bad_alloc&) throw(); + bad_alloc& operator=(const bad_alloc&) throw(); + ~bad_alloc(); + virtual const char* what() const throw(); + }; + + /** + * Bad cast exception. Thrown by the __cxa_bad_cast() helper function. + */ + class bad_cast: public exception { + public: + bad_cast() throw(); + bad_cast(const bad_cast&) throw(); + bad_cast& operator=(const bad_cast&) throw(); + virtual ~bad_cast(); + virtual const char* what() const throw(); + }; + + /** + * Bad typeidexception. Thrown by the __cxa_bad_typeid() helper function. + */ + class bad_typeid: public exception + { + public: + bad_typeid() throw(); + bad_typeid(const bad_typeid &__rhs) throw(); + virtual ~bad_typeid(); + bad_typeid& operator=(const bad_typeid &__rhs) throw(); + virtual const char* what() const throw(); + }; } // namespace std -#endif // __LIBCXXSUP_STDEXCEPT_H__ - diff --git a/src/typeinfo b/src/typeinfo index cc064c2..74e77ae 100644 --- a/src/typeinfo +++ b/src/typeinfo @@ -7,8 +7,10 @@ namespace std class type_info { public: - /** Virtual destructor. This class must have one virtual function to - * ensure that it has a vtable. */ + /** + * Virtual destructor. This class must have one virtual function to + * ensure that it has a vtable. + */ virtual ~type_info(); bool operator==(const type_info &) const; bool operator!=(const type_info &) const; diff --git a/src/typeinfo.cc b/src/typeinfo.cc index 6d16668..28af5c9 100644 --- a/src/typeinfo.cc +++ b/src/typeinfo.cc @@ -47,11 +47,29 @@ ABI_NAMESPACE::__pointer_to_member_type_info::~__pointer_to_member_type_info() { extern "C" char *cpp_demangle_gnu3(const char *); extern "C" bool is_cpp_mangled_gnu3(const char *); +/** + * Demangles a C++ symbol or type name. The buffer, if non-NULL, must be + * allocated with malloc() and must be *n bytes or more long. This function + * may call realloc() on the value pointed to by buf, and will return the + * length of the string via *n. + * + * The value pointed to by status is set to one of the following: + * + * 0: success + * -1: memory allocation failure + * -2: invalid mangled name + * -3: invalid arguments + */ extern "C" char* __cxa_demangle(const char* mangled_name, char* buf, size_t* n, int* status) { + // TODO: We should probably just be linking against libelf-tc, rather than + // copying their code. This requires them to do an actual release, + // however, and for our changes to be pushed upstream. We also need to + // call a different demangling function here depending on the ABI (e.g. + // ARM). char *demangled = cpp_demangle_gnu3(mangled_name); if (NULL != demangled) { diff --git a/src/typeinfo.h b/src/typeinfo.h index 6bc4b60..1ee1fe5 100644 --- a/src/typeinfo.h +++ b/src/typeinfo.h @@ -4,33 +4,57 @@ namespace ABI_NAMESPACE { - // Primitive type info + /** + * Primitive type info, for intrinsic types. + */ struct __fundamental_type_info : public std::type_info { virtual ~__fundamental_type_info(); }; + /** + * Type info for arrays. + */ struct __array_type_info : public std::type_info { virtual ~__array_type_info(); }; + /** + * Type info for functions. + */ struct __function_type_info : public std::type_info { virtual ~__function_type_info(); }; + /** + * Type info for enums. + */ struct __enum_type_info : public std::type_info { virtual ~__enum_type_info(); }; - // Base class for class type info. Used only for tentative definitions. + /** + * Base class for class type info. Used only for tentative definitions. + */ struct __class_type_info : public std::type_info { virtual ~__class_type_info(); - virtual void *cast_to(void *obj, const struct __class_type_info *other) const; - virtual bool can_cast_to(const struct __class_type_info *other) const; + /** + * Function implementing dynamic casts. + */ + virtual void *cast_to(void *obj, + const struct __class_type_info *other) const; + /** + * Function returning whether a cast from this type to another type is + * possible. + */ + virtual bool can_cast_to(const struct __class_type_info *other) const; }; - // Single-inheritance class. + /** + * Single-inheritance class type info. This is used for classes containing + * a single non-virtual base class at offset 0. + */ struct __si_class_type_info : public __class_type_info { virtual ~__si_class_type_info(); @@ -39,72 +63,137 @@ namespace ABI_NAMESPACE virtual bool can_cast_to(const struct __class_type_info *other) const; }; + /** + * Type info for base classes. Classes with multiple bases store an array + * of these, one for each superclass. + */ struct __base_class_type_info { const __class_type_info *__base_type; private: + /** + * The high __offset_shift bits of this store the (signed) offset + * of the base class. The low bits store flags from + * __offset_flags_masks. + */ long __offset_flags; - + /** + * Flags used in the low bits of __offset_flags. + */ enum __offset_flags_masks { + /** This base class is virtual. */ __virtual_mask = 0x1, + /** This base class is public. */ __public_mask = 0x2, + /** The number of bits reserved for flags. */ __offset_shift = 8 }; public: + /** + * Returns the offset of the base class. + */ long offset() const { return __offset_flags >> __offset_shift; } + /** + * Returns the flags. + */ long flags() const { return __offset_flags & ((1 << __offset_shift) - 1); } + /** + * Returns whether this is a public base class. + */ bool isPublic() const { return flags() & __public_mask; } + /** + * Returns whether this is a virtual base class. + */ bool isVirtual() const { return flags() & __virtual_mask; } }; - + /** + * Type info for classes with virtual bases or multiple superclasses. + */ struct __vmi_class_type_info : public __class_type_info { virtual ~__vmi_class_type_info(); + /** Flags describing this class. Contains values from __flags_masks. */ unsigned int __flags; + /** The number of base classes. */ unsigned int __base_count; + /** + * Array of base classes - this actually has __base_count elements, not + * 1. + */ __base_class_type_info __base_info[1]; + /** + * Flags used in the __flags field. + */ enum __flags_masks { + /** The class has non-diamond repeated inheritance. */ __non_diamond_repeat_mask = 0x1, + /** The class is diamond shaped. */ __diamond_shaped_mask = 0x2 }; virtual void *cast_to(void *obj, const struct __class_type_info *other) const; virtual bool can_cast_to(const struct __class_type_info *other) const; }; + /** + * Base class used for both pointer and pointer-to-member type info. + */ struct __pbase_type_info : public std::type_info { virtual ~__pbase_type_info(); + /** + * Flags. Values from __masks. + */ unsigned int __flags; + /** + * The type info for the pointee. + */ const std::type_info *__pointee; + /** + * Masks used for qualifiers on the pointer. + */ enum __masks { + /** Pointer has const qualifier. */ __const_mask = 0x1, + /** Pointer has volatile qualifier. */ __volatile_mask = 0x2, + /** Pointer has restrict qualifier. */ __restrict_mask = 0x4, + /** Pointer points to an incomplete type. */ __incomplete_mask = 0x8, + /** Pointer is a pointer to a member of an incomplete class. */ __incomplete_class_mask = 0x10 }; }; + /** + * Pointer type info. + */ struct __pointer_type_info : public __pbase_type_info { virtual ~__pointer_type_info(); }; + /** + * Pointer to member type info. + */ struct __pointer_to_member_type_info : public __pbase_type_info { virtual ~__pointer_to_member_type_info(); + /** + * Pointer to the class containing this member. + */ const __class_type_info *__context; }; diff --git a/test/Makefile b/test/Makefile index dfb04d7..ec4c62e 100644 --- a/test/Makefile +++ b/test/Makefile @@ -17,7 +17,7 @@ LDFLAGS += -L/usr/local/lib -L. -lpthread -fexceptions PRODUCTS = test libcxxabi.so.1 system_test test: $(TEST_OBJECTS) libcxxabi.so library - @gcc $(CPPFLAGS) $(LDFLAGS) -o test $(TEST_OBJECTS) -lcxxabi + @gcc $(CPPFLAGS) $(LDFLAGS) -o test $(TEST_OBJECTS) -lcxxabi -lstdc++ library: @cd ../src && $(MAKE) @@ -57,3 +57,4 @@ compare: test clean: @echo Cleaning... @rm -f $(OBJECTS) $(PRODUCTS) $(TEST_OBJECTS) vgcore* *.core + @cd ../src && $(MAKE) clean diff --git a/test/test_exception.cc b/test/test_exception.cc index 44fe5d0..6e16ca9 100644 --- a/test/test_exception.cc +++ b/test/test_exception.cc @@ -5,7 +5,7 @@ #include <exception> -//#define fprintf(...) +#define fprintf(...) void log(void* ignored) { @@ -162,6 +162,8 @@ fprintf(stderr, "Throwing 0\n"); throw 0; } +extern "C" void __cxa_bad_cast(); + void test_exceptions(void) { std::set_unexpected(throw_zero); @@ -193,6 +195,19 @@ void test_exceptions(void) { TEST(&a==b, "Caught int from thrown int"); } + try + { + __cxa_bad_cast(); + } + catch (std::exception b) + { + TEST(1, "Caught bad cast"); + } + catch (...) + { + TEST(0, "Bad cast was not caught correctly"); + } + //printf("Test: %s\n", } |