summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoranonymous <local@localhost>2011-05-02 12:51:25 +0000
committeranonymous <local@localhost>2011-05-09 15:05:54 +0700
commit6f9032852fb6c55846c4b4a23f8b8976442cce51 (patch)
tree8b5222c3778ee59173bb753f5e7b446a20706b2f
parentb12153bb8ad75cf2bf51d486895fce2769fe66d9 (diff)
downloadlibcxxrt-6f9032852fb6c55846c4b4a23f8b8976442cce51.tar.gz
libcxxrt-6f9032852fb6c55846c4b4a23f8b8976442cce51.tar.bz2
libcxxrt-6f9032852fb6c55846c4b4a23f8b8976442cce51.tar.xz
Tidy up of the libcxxrt sources. Added missing comments, fixed some inconsistent indenting.
-rw-r--r--src/CMakeLists.txt3
-rw-r--r--src/Makefile29
-rw-r--r--src/abi_namespace.h4
-rw-r--r--src/aux.cc27
-rw-r--r--src/dwarf_eh.h219
-rw-r--r--src/dynamic_cast.cc19
-rw-r--r--src/exception.cc310
-rw-r--r--src/guard.cc50
-rw-r--r--src/memory.cc140
-rw-r--r--src/stdexcept.cc100
-rw-r--r--src/stdexcept.h107
-rw-r--r--src/typeinfo6
-rw-r--r--src/typeinfo.cc18
-rw-r--r--src/typeinfo.h103
-rw-r--r--test/Makefile3
-rw-r--r--test/test_exception.cc17
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
diff --git a/src/aux.cc b/src/aux.cc
index 382cc73..e8ce6e7 100644
--- a/src/aux.cc
+++ b/src/aux.cc
@@ -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",
}