diff options
Diffstat (limited to 'contrib/libcxxrt')
-rw-r--r-- | contrib/libcxxrt/FREEBSD-upgrade | 6 | ||||
-rw-r--r-- | contrib/libcxxrt/LICENSE | 14 | ||||
-rw-r--r-- | contrib/libcxxrt/abi_namespace.h | 5 | ||||
-rw-r--r-- | contrib/libcxxrt/atomic.h | 30 | ||||
-rw-r--r-- | contrib/libcxxrt/auxhelper.cc | 82 | ||||
-rw-r--r-- | contrib/libcxxrt/cxxabi.h | 244 | ||||
-rw-r--r-- | contrib/libcxxrt/dwarf_eh.h | 477 | ||||
-rw-r--r-- | contrib/libcxxrt/dynamic_cast.cc | 210 | ||||
-rw-r--r-- | contrib/libcxxrt/exception.cc | 1591 | ||||
-rw-r--r-- | contrib/libcxxrt/guard.cc | 193 | ||||
-rw-r--r-- | contrib/libcxxrt/libelftc_dem_gnu3.c | 3895 | ||||
-rw-r--r-- | contrib/libcxxrt/memory.cc | 171 | ||||
-rw-r--r-- | contrib/libcxxrt/stdexcept.cc | 99 | ||||
-rw-r--r-- | contrib/libcxxrt/stdexcept.h | 96 | ||||
-rw-r--r-- | contrib/libcxxrt/terminate.cc | 40 | ||||
-rw-r--r-- | contrib/libcxxrt/typeinfo.cc | 124 | ||||
-rw-r--r-- | contrib/libcxxrt/typeinfo.h | 313 | ||||
-rw-r--r-- | contrib/libcxxrt/unwind-arm.h | 227 | ||||
-rw-r--r-- | contrib/libcxxrt/unwind-itanium.h | 170 | ||||
-rw-r--r-- | contrib/libcxxrt/unwind.h | 40 |
20 files changed, 8027 insertions, 0 deletions
diff --git a/contrib/libcxxrt/FREEBSD-upgrade b/contrib/libcxxrt/FREEBSD-upgrade new file mode 100644 index 000000000000..bdd74f308a4f --- /dev/null +++ b/contrib/libcxxrt/FREEBSD-upgrade @@ -0,0 +1,6 @@ +$FreeBSD$ + +This is the FreeBSD copy of libcxxrt. It contains the src directory from the +upstream repository. + +When updating, copy *.{c,cc,h} from the upstream src/. diff --git a/contrib/libcxxrt/LICENSE b/contrib/libcxxrt/LICENSE new file mode 100644 index 000000000000..cda1ea2f7b74 --- /dev/null +++ b/contrib/libcxxrt/LICENSE @@ -0,0 +1,14 @@ +The BSD License + +Copyright 2010-2011 PathScale, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of PathScale, Inc. diff --git a/contrib/libcxxrt/abi_namespace.h b/contrib/libcxxrt/abi_namespace.h new file mode 100644 index 000000000000..dda788da1980 --- /dev/null +++ b/contrib/libcxxrt/abi_namespace.h @@ -0,0 +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/contrib/libcxxrt/atomic.h b/contrib/libcxxrt/atomic.h new file mode 100644 index 000000000000..131ca9f57798 --- /dev/null +++ b/contrib/libcxxrt/atomic.h @@ -0,0 +1,30 @@ + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif +#ifndef __has_feature +#define __has_feature(x) 0 +#endif +/** + * Swap macro that enforces a happens-before relationship with a corresponding + * ATOMIC_LOAD. + */ +#if __has_builtin(__c11_atomic_exchange) +#define ATOMIC_SWAP(addr, val)\ + __c11_atomic_exchange(reinterpret_cast<_Atomic(__typeof__(val))*>(addr), val, __ATOMIC_ACQ_REL) +#elif __has_builtin(__sync_swap) +#define ATOMIC_SWAP(addr, val)\ + __sync_swap(addr, val) +#else +#define ATOMIC_SWAP(addr, val)\ + __sync_lock_test_and_set(addr, val) +#endif + +#if __has_builtin(__c11_atomic_load) +#define ATOMIC_LOAD(addr)\ + __c11_atomic_load(reinterpret_cast<_Atomic(__typeof__(*addr))*>(addr), __ATOMIC_ACQUIRE) +#else +#define ATOMIC_LOAD(addr)\ + (__sync_synchronize(), *addr) +#endif + diff --git a/contrib/libcxxrt/auxhelper.cc b/contrib/libcxxrt/auxhelper.cc new file mode 100644 index 000000000000..3e98da036a11 --- /dev/null +++ b/contrib/libcxxrt/auxhelper.cc @@ -0,0 +1,82 @@ +/* + * Copyright 2010-2011 PathScale, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * 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 <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(); +} + +/** + * 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(); +} + +/** + * Compilers may (but are not required to) set any deleted-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 deleted-virtual function. + */ +extern "C" void __cxa_deleted_virtual() +{ + abort(); +} + +extern "C" void __cxa_throw_bad_array_new_length() +{ + throw std::bad_array_new_length(); +} diff --git a/contrib/libcxxrt/cxxabi.h b/contrib/libcxxrt/cxxabi.h new file mode 100644 index 000000000000..411c4c749ccf --- /dev/null +++ b/contrib/libcxxrt/cxxabi.h @@ -0,0 +1,244 @@ +/* + * Copyright 2012 David Chisnall. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __CXXABI_H_ +#define __CXXABI_H_ +#include <stddef.h> +#include <stdint.h> +#include "unwind.h" +namespace std +{ + class type_info; +} +/* + * The cxxabi.h header provides a set of public definitions for types and + * functions defined by the Itanium C++ ABI specification. For reference, see + * the ABI specification here: + * + * http://sourcery.mentor.com/public/cxx-abi/abi.html + * + * All deviations from this specification, unless otherwise noted, are + * accidental. + */ + +#ifdef __cplusplus +namespace __cxxabiv1 { +extern "C" { +#endif +/** + * Function type to call when an unexpected exception is encountered. + */ +typedef void (*unexpected_handler)(); +/** + * Function type to call when an unrecoverable condition is encountered. + */ +typedef void (*terminate_handler)(); + + +/** + * Structure used as a header on thrown exceptions. This is the same layout as + * 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. + */ +struct __cxa_exception +{ +#if __LP64__ + /** + * Reference count. Used to support the C++11 exception_ptr class. This + * is prepended to the structure in 64-bit mode and squeezed in to the + * padding left before the 64-bit aligned _Unwind_Exception at the end in + * 32-bit mode. + * + * Note that it is safe to extend this structure at the beginning, rather + * than the end, because the public API for creating it returns the address + * of the end (where the exception object can be stored). + */ + uintptr_t referenceCount; +#endif + /** Type info for the thrown object. */ + std::type_info *exceptionType; + /** Destructor for the object, if one exists. */ + void (*exceptionDestructor) (void *); + /** Handler called when an exception specification is violated. */ + unexpected_handler unexpectedHandler; + /** Hander called to terminate. */ + terminate_handler terminateHandler; + /** + * 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. + */ + __cxa_exception *nextException; + /** + * 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). + */ + int handlerCount; +#if defined(__arm__) && !defined(__ARM_DWARF_EH__) + /** + * The ARM EH ABI requires the unwind library to keep track of exceptions + * during cleanups. These support nesting, so we need to keep a list of + * them. + */ + _Unwind_Exception *nextCleanup; + /** + * The number of cleanups that are currently being run on this exception. + */ + int cleanupCount; +#endif + /** + * 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. + */ + 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 + * frame. This is unused in this implementation, but set for ABI + * 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 + * throwing a class and catching a virtual superclass (for example), we + * need to adjust the thrown pointer to make it all work correctly. + */ + void *adjustedPtr; +#if !__LP64__ + /** + * Reference count. Used to support the C++11 exception_ptr class. This + * is prepended to the structure in 64-bit mode and squeezed in to the + * padding left before the 64-bit aligned _Unwind_Exception at the end in + * 32-bit mode. + * + * Note that it is safe to extend this structure at the beginning, rather + * than the end, because the public API for creating it returns the address + * of the end (where the exception object can be stored) + */ + uintptr_t referenceCount; +#endif + /** 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. 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; +}; +/** + * ABI function returning the __cxa_eh_globals structure. + */ +__cxa_eh_globals *__cxa_get_globals(void); +/** + * Version of __cxa_get_globals() assuming that __cxa_get_globals() has already + * been called at least once by this thread. + */ +__cxa_eh_globals *__cxa_get_globals_fast(void); + +std::type_info * __cxa_current_exception_type(); + +/** + * Throws an exception returned by __cxa_current_primary_exception(). This + * exception may have been caught in another thread. + */ +void __cxa_rethrow_primary_exception(void* thrown_exception); +/** + * Returns the current exception in a form that can be stored in an + * exception_ptr object and then rethrown by a call to + * __cxa_rethrow_primary_exception(). + */ +void *__cxa_current_primary_exception(void); +/** + * Increments the reference count of an exception. Called when an + * exception_ptr is copied. + */ +void __cxa_increment_exception_refcount(void* thrown_exception); +/** + * Decrements the reference count of an exception. Called when an + * exception_ptr is deleted. + */ +void __cxa_decrement_exception_refcount(void* thrown_exception); +/** + * 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 + */ +char* __cxa_demangle(const char* mangled_name, + char* buf, + size_t* n, + int* status); +#ifdef __cplusplus +} // extern "C" +} // namespace + +namespace abi = __cxxabiv1; + +#endif /* __cplusplus */ +#endif /* __CXXABI_H_ */ diff --git a/contrib/libcxxrt/dwarf_eh.h b/contrib/libcxxrt/dwarf_eh.h new file mode 100644 index 000000000000..da86846abf97 --- /dev/null +++ b/contrib/libcxxrt/dwarf_eh.h @@ -0,0 +1,477 @@ +/* + * Copyright 2010-2011 PathScale, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/** + * 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> + +// 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 +# define _GNU_SOURCE 1 +# include "unwind.h" +# undef _GNU_SOURCE +#else +# include "unwind.h" +#endif + +#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. +enum dwarf_data_encoding +{ + /// Absolute pointer value + DW_EH_PE_absptr = 0x00, + /// Unsigned, little-endian, base 128-encoded (variable length). + DW_EH_PE_uleb128 = 0x01, + /// Unsigned 16-bit integer. + DW_EH_PE_udata2 = 0x02, + /// Unsigned 32-bit integer. + DW_EH_PE_udata4 = 0x03, + /// Unsigned 64-bit integer. + DW_EH_PE_udata8 = 0x04, + /// 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 static_cast<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 + DW_EH_PE_omit = 0xff, + /// Value relative to program counter + DW_EH_PE_pcrel = 0x10, + /// Value relative to the text segment + DW_EH_PE_textrel = 0x20, + /// Value relative to the data segment + DW_EH_PE_datarel = 0x30, + /// Value relative to the start of the function + DW_EH_PE_funcrel = 0x40, + /// Aligned pointer (Not supported yet - are they actually used?) + DW_EH_PE_aligned = 0x50, + /// 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 static_cast<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) == 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) +{ + 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*); + } +} + +/** + * Read an unsigned, little-endian, base-128, DWARF value. Updates *data to + * 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(dw_eh_ptr_t *data, int *b) +{ + uint64_t uleb = 0; + unsigned int bit = 0; + unsigned char digit = 0; + // We have to read at least one octet, and keep reading until we get to one + // with the high bit unset + do + { + // This check is a bit too strict - we should also check the highest + // bit of the digit. + assert(bit < sizeof(uint64_t) * 8); + // Get the base 128 digit + digit = (**data) & 0x7f; + // Add it to the current value + uleb += digit << bit; + // Increase the shift value + bit += 7; + // Proceed to the next octet + (*data)++; + // Terminate when we reach a value that does not have the high bit set + // (i.e. which was not modified when we mask it with 0x7f) + } while ((*(*data - 1)) != digit); + *b = bit; + + return uleb; +} + +/** + * 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); +} + +/** + * 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 + uint64_t uleb = read_leb128(data, &bits); + // If the most significant bit read is 1, then we need to sign extend it + if ((uleb >> (bits-1)) == 1) + { + // Sign extend by setting all bits in front of it to 1 + uleb |= static_cast<int64_t>(-1) << bits; + } + return static_cast<int64_t>(uleb); +} +/** + * 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); + switch (type) + { + // Read fixed-length types +#define READ(dwarf, type) \ + case dwarf:\ + {\ + type t;\ + memcpy(&t, *data, sizeof t);\ + *data += sizeof t;\ + return static_cast<uint64_t>(t);\ + } + READ(DW_EH_PE_udata2, uint16_t) + READ(DW_EH_PE_udata4, uint32_t) + READ(DW_EH_PE_udata8, uint64_t) + READ(DW_EH_PE_sdata2, int16_t) + READ(DW_EH_PE_sdata4, int32_t) + 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: + return read_sleb128(data); + case DW_EH_PE_uleb128: + return read_uleb128(data); + default: abort(); + } +} + +/** + * 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)) + { + case DW_EH_PE_pcrel: + v += reinterpret_cast<uint64_t>(start); + break; + case DW_EH_PE_textrel: + v += static_cast<uint64_t>(static_cast<uintptr_t>(_Unwind_GetTextRelBase(c))); + break; + case DW_EH_PE_datarel: + v += static_cast<uint64_t>(static_cast<uintptr_t>(_Unwind_GetDataRelBase(c))); + break; + case DW_EH_PE_funcrel: + v += static_cast<uint64_t>(static_cast<uintptr_t>(_Unwind_GetRegionStart(c))); + default: + break; + } + // If this is an indirect value, then it is really the address of the real + // value + // TODO: Check whether this should really always be a pointer - it seems to + // be a GCC extensions, so not properly documented... + if (is_indirect(encoding)) + { + v = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(*reinterpret_cast<void**>(v))); + } + return v; +} + + +/** + * 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) +{ + dw_eh_ptr_t start = *data; + unsigned char encoding = *((*data)++); + // If this value is omitted, skip it and don't touch the output value + if (encoding == DW_EH_PE_omit) { return; } + + *out = read_value(encoding, data); + *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; +}; + +/** + * 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; + + lsda.region_start = reinterpret_cast<dw_eh_ptr_t>(_Unwind_GetRegionStart(context)); + + // If the landing pads are relative to anything other than the start of + // this region, find out where. This is @LPStart in the spec, although the + // encoding that GCC uses does not quite match the spec. + uint64_t v = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(lsda.region_start)); + read_value_with_encoding(context, &data, &v); + lsda.landing_pads = reinterpret_cast<dw_eh_ptr_t>(static_cast<uintptr_t>(v)); + + // If there is a type table, find out where it is. This is @TTBase in the + // spec. Note: we find whether there is a type table pointer by checking + // whether the leading byte is DW_EH_PE_omit (0xff), which is not what the + // spec says, but does seem to be how G++ indicates this. + lsda.type_table = 0; + lsda.type_table_encoding = *data++; + if (lsda.type_table_encoding != DW_EH_PE_omit) + { + v = read_uleb128(&data); + dw_eh_ptr_t type_table = data; + type_table += v; + lsda.type_table = type_table; + //lsda.type_table = (uintptr_t*)(data + v); + } +#if defined(__arm__) && !defined(__ARM_DWARF_EH__) + lsda.type_table_encoding = (DW_EH_PE_pcrel | DW_EH_PE_indirect); +#endif + + lsda.callsite_encoding = static_cast<enum dwarf_data_encoding>(*(data++)); + + // Action table is immediately after the call site table + lsda.action_table = data; + uintptr_t callsite_size = static_cast<uintptr_t>(read_uleb128(&data)); + lsda.action_table = data + callsite_size; + // Call site table is immediately after the header + lsda.call_site_table = static_cast<dw_eh_ptr_t>(data); + + + 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. 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) +{ + 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 = static_cast<unsigned char*>(lsda->call_site_table); + + while (callsite_table <= lsda->action_table) + { + // Once again, the layout deviates from the spec. + uint64_t call_site_start, call_site_size, landing_pad, action; + call_site_start = read_value(lsda->callsite_encoding, &callsite_table); + call_site_size = read_value(lsda->callsite_encoding, &callsite_table); + + // 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 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) + { + // Action records are 1-biased so both no-record and zeroth + // record can be stored. + result->action_record = lsda->action_table + action - 1; + } + // No landing pad means keep unwinding. + if (landing_pad) + { + // Landing pad is the offset from the value in the header + result->landing_pad = lsda->landing_pads + landing_pad; + } + return true; + } + } + return false; +} + +/// Defines an exception class from 8 bytes (endian independent) +#define EXCEPTION_CLASS(a,b,c,d,e,f,g,h) \ + ((static_cast<uint64_t>(a) << 56) +\ + (static_cast<uint64_t>(b) << 48) +\ + (static_cast<uint64_t>(c) << 40) +\ + (static_cast<uint64_t>(d) << 32) +\ + (static_cast<uint64_t>(e) << 24) +\ + (static_cast<uint64_t>(f) << 16) +\ + (static_cast<uint64_t>(g) << 8) +\ + (static_cast<uint64_t>(h))) + +#define GENERIC_EXCEPTION_CLASS(e,f,g,h) \ + (static_cast<uint32_t>(e) << 24) +\ + (static_cast<uint32_t>(f) << 16) +\ + (static_cast<uint32_t>(g) << 8) +\ + (static_cast<uint32_t>(h)) diff --git a/contrib/libcxxrt/dynamic_cast.cc b/contrib/libcxxrt/dynamic_cast.cc new file mode 100644 index 000000000000..6ae3a4035554 --- /dev/null +++ b/contrib/libcxxrt/dynamic_cast.cc @@ -0,0 +1,210 @@ +/* + * Copyright 2010-2011 PathScale, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "typeinfo.h" +#include <stdio.h> + +using namespace ABI_NAMESPACE; + +/** + * Vtable header. + */ +struct vtable_header +{ + /** Offset of the leaf object. */ + ptrdiff_t leaf_offset; + /** Type of the object. */ + 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) reinterpret_cast<__typeof__(x)>(reinterpret_cast<char*>(x) + off) + +bool std::type_info::__do_catch(std::type_info const *ex_type, + void **exception_object, + unsigned int outer) const +{ + const type_info *type = this; + + if (type == ex_type) + { + return true; + } + if (const __class_type_info *cti = dynamic_cast<const __class_type_info *>(type)) + { + return ex_type->__do_upcast(cti, exception_object); + } + return false; +} + +bool __pbase_type_info::__do_catch(std::type_info const *ex_type, + void **exception_object, + unsigned int outer) const +{ + if (ex_type == this) + { + return true; + } + if (!ex_type->__is_pointer_p()) + { + // Can't catch a non-pointer type in a pointer catch + return false; + } + + if (!(outer & 1)) + { + // If the low bit is cleared on this means that we've gone + // through a pointer that is not const qualified. + return false; + } + // Clear the low bit on outer if we're not const qualified. + if (!(__flags & __const_mask)) + { + outer &= ~1; + } + + const __pbase_type_info *ptr_type = + static_cast<const __pbase_type_info*>(ex_type); + + if (ptr_type->__flags & ~__flags) + { + // Handler pointer is less qualified + return false; + } + + // Special case for void* handler. + if(*__pointee == typeid(void)) + { + return true; + } + + return __pointee->__do_catch(ptr_type->__pointee, exception_object, outer); +} + +void *__class_type_info::cast_to(void *obj, const struct __class_type_info *other) const +{ + if (this == other) + { + return obj; + } + return 0; +} + +void *__si_class_type_info::cast_to(void *obj, const struct __class_type_info *other) const +{ + if (this == other) + { + return obj; + } + return __base_type->cast_to(obj, other); +} +bool __si_class_type_info::__do_upcast(const __class_type_info *target, + void **thrown_object) const +{ + if (this == target) + { + return true; + } + return __base_type->__do_upcast(target, thrown_object); +} + +void *__vmi_class_type_info::cast_to(void *obj, const struct __class_type_info *other) const +{ + if (__do_upcast(other, &obj)) + { + return obj; + } + return 0; +} + +bool __vmi_class_type_info::__do_upcast(const __class_type_info *target, + void **thrown_object) const +{ + if (this == target) + { + return true; + } + for (unsigned int i=0 ; i<__base_count ; i++) + { + const __base_class_type_info *info = &__base_info[i]; + ptrdiff_t offset = info->offset(); + // If this is a virtual superclass, the offset is stored in the + // object's vtable at the offset requested; 2.9.5.6.c: + // + // 'For a non-virtual base, this is the offset in the object of the + // base subobject. For a virtual base, this is the offset in the + // virtual table of the virtual base offset for the virtual base + // referenced (negative).' + + void *obj = *thrown_object; + if (info->isVirtual()) + { + // Object's vtable + ptrdiff_t *off = *static_cast<ptrdiff_t**>(obj); + // Offset location in vtable + off = ADD_TO_PTR(off, offset); + offset = *off; + } + void *cast = ADD_TO_PTR(obj, offset); + + if (info->__base_type == target || + (info->__base_type->__do_upcast(target, &cast))) + { + *thrown_object = cast; + return true; + } + } + return 0; +} + + +/** + * 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, + const __class_type_info *dst, + ptrdiff_t src2dst_offset) +{ + const char *vtable_location = *static_cast<const char * const *>(sub); + const vtable_header *header = + reinterpret_cast<const vtable_header*>(vtable_location - sizeof(vtable_header)); + void *leaf = ADD_TO_PTR(const_cast<void *>(sub), header->leaf_offset); + return header->type->cast_to(leaf, dst); +} diff --git a/contrib/libcxxrt/exception.cc b/contrib/libcxxrt/exception.cc new file mode 100644 index 000000000000..0de878e9e6db --- /dev/null +++ b/contrib/libcxxrt/exception.cc @@ -0,0 +1,1591 @@ +/* + * Copyright 2010-2011 PathScale, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdlib.h> +#include <dlfcn.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <pthread.h> +#include "typeinfo.h" +#include "dwarf_eh.h" +#include "atomic.h" +#include "cxxabi.h" + +#pragma weak pthread_key_create +#pragma weak pthread_setspecific +#pragma weak pthread_getspecific +#pragma weak pthread_once +#ifdef LIBCXXRT_WEAK_LOCKS +#pragma weak pthread_mutex_lock +#define pthread_mutex_lock(mtx) do {\ + if (pthread_mutex_lock) pthread_mutex_lock(mtx);\ + } while(0) +#pragma weak pthread_mutex_unlock +#define pthread_mutex_unlock(mtx) do {\ + if (pthread_mutex_unlock) pthread_mutex_unlock(mtx);\ + } while(0) +#pragma weak pthread_cond_signal +#define pthread_cond_signal(cv) do {\ + if (pthread_cond_signal) pthread_cond_signal(cv);\ + } while(0) +#pragma weak pthread_cond_wait +#define pthread_cond_wait(cv, mtx) do {\ + if (pthread_cond_wait) pthread_cond_wait(cv, mtx);\ + } while(0) +#endif + +using namespace ABI_NAMESPACE; + +/** + * Saves the result of the landing pad that we have found. For ARM, this is + * stored in the generic unwind structure, while on other platforms it is + * stored in the C++ exception. + */ +static void saveLandingPad(struct _Unwind_Context *context, + struct _Unwind_Exception *ucb, + struct __cxa_exception *ex, + int selector, + dw_eh_ptr_t landingPad) +{ +#if defined(__arm__) && !defined(__ARM_DWARF_EH__) + // On ARM, we store the saved exception in the generic part of the structure + ucb->barrier_cache.sp = _Unwind_GetGR(context, 13); + ucb->barrier_cache.bitpattern[1] = static_cast<uint32_t>(selector); + ucb->barrier_cache.bitpattern[3] = reinterpret_cast<uint32_t>(landingPad); +#endif + // Cache the results for the phase 2 unwind, if we found a handler + // and this is not a foreign exception. + if (ex) + { + ex->handlerSwitchValue = selector; + ex->catchTemp = landingPad; + } +} + +/** + * Loads the saved landing pad. Returns 1 on success, 0 on failure. + */ +static int loadLandingPad(struct _Unwind_Context *context, + struct _Unwind_Exception *ucb, + struct __cxa_exception *ex, + unsigned long *selector, + dw_eh_ptr_t *landingPad) +{ +#if defined(__arm__) && !defined(__ARM_DWARF_EH__) + *selector = ucb->barrier_cache.bitpattern[1]; + *landingPad = reinterpret_cast<dw_eh_ptr_t>(ucb->barrier_cache.bitpattern[3]); + return 1; +#else + if (ex) + { + *selector = ex->handlerSwitchValue; + *landingPad = reinterpret_cast<dw_eh_ptr_t>(ex->catchTemp); + return 0; + } + return 0; +#endif +} + +static inline _Unwind_Reason_Code continueUnwinding(struct _Unwind_Exception *ex, + struct _Unwind_Context *context) +{ +#if defined(__arm__) && !defined(__ARM_DWARF_EH__) + if (__gnu_unwind_frame(ex, context) != _URC_OK) { return _URC_FAILURE; } +#endif + return _URC_CONTINUE_UNWIND; +} + + +extern "C" void __cxa_free_exception(void *thrown_exception); +extern "C" void __cxa_free_dependent_exception(void *thrown_exception); +extern "C" void* __dynamic_cast(const void *sub, + const __class_type_info *src, + const __class_type_info *dst, + ptrdiff_t src2dst_offset); + +/** + * 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; + +/** + * Per-thread info required by the runtime. We store a single structure + * pointer in thread-local storage, because this tends to be a scarce resource + * and it's impolite to steal all of it and not leave any for the rest of the + * program. + * + * Instances of this structure are allocated lazily - at most one per thread - + * and are destroyed on thread termination. + */ +struct __cxa_thread_info +{ + /** The termination handler for this thread. */ + 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 + * 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 [3.3.1]). + */ + int emergencyBuffersHeld; + /** + * The exception currently running in a cleanup. + */ + _Unwind_Exception *currentCleanup; + /** + * Our state with respect to foreign exceptions. Usually none, set to + * caught if we have just caught an exception and rethrown if we are + * rethrowing it. + */ + enum + { + none, + caught, + rethrown + } foreign_exception_state; + /** + * The public part of this structure, accessible from outside of this + * module. + */ + __cxa_eh_globals globals; +}; +/** + * Dependent exception. This + */ +struct __cxa_dependent_exception +{ +#if __LP64__ + void *primaryException; +#endif + std::type_info *exceptionType; + void (*exceptionDestructor) (void *); + unexpected_handler unexpectedHandler; + terminate_handler terminateHandler; + __cxa_exception *nextException; + int handlerCount; +#if defined(__arm__) && !defined(__ARM_DWARF_EH__) + _Unwind_Exception *nextCleanup; + int cleanupCount; +#endif + int handlerSwitchValue; + const char *actionRecord; + const char *languageSpecificData; + void *catchTemp; + void *adjustedPtr; +#if !__LP64__ + void *primaryException; +#endif + _Unwind_Exception unwindHeader; +}; + + +namespace std +{ + void unexpected(); + class exception + { + public: + virtual ~exception() throw(); + virtual const char* what() const throw(); + }; + +} + +/** + * Class of exceptions to distinguish between this and other exception types. + * + * The first four characters are the vendor ID. Currently, we use GNUC, + * 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 = + EXCEPTION_CLASS('G', 'N', 'U', 'C', 'C', '+', '+', '\0'); +/** + * Class used for dependent exceptions. + */ +static const uint64_t dependent_exception_class = + EXCEPTION_CLASS('G', 'N', 'U', 'C', 'C', '+', '+', '\x01'); +/** + * The low four bytes of the exception class, indicating that we conform to the + * 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 = + GENERIC_EXCEPTION_CLASS('C', '+', '+', '\0'); + +static bool isCXXException(uint64_t cls) +{ + return (cls == exception_class) || (cls == dependent_exception_class); +} + +static bool isDependentException(uint64_t cls) +{ + return cls == dependent_exception_class; +} + +static __cxa_exception *exceptionFromPointer(void *ex) +{ + return reinterpret_cast<__cxa_exception*>(static_cast<char*>(ex) - + offsetof(struct __cxa_exception, unwindHeader)); +} +static __cxa_exception *realExceptionFromException(__cxa_exception *ex) +{ + if (!isDependentException(ex->unwindHeader.exception_class)) { return ex; } + return reinterpret_cast<__cxa_exception*>((reinterpret_cast<__cxa_dependent_exception*>(ex))->primaryException)-1; +} + + +namespace std +{ + // Forward declaration of standard library terminate() function used to + // abort execution. + void terminate(void); +} + +using namespace ABI_NAMESPACE; + + + +/** The global termination handler. */ +static terminate_handler terminateHandler = abort; +/** The global unexpected exception handler. */ +static unexpected_handler unexpectedHandler = std::terminate; + +/** Key used for thread-local data. */ +static pthread_key_t eh_key; + + +/** + * Cleanup function, allowing foreign exception handlers to correctly destroy + * this exception if they catch it. + */ +static void exception_cleanup(_Unwind_Reason_Code reason, + struct _Unwind_Exception *ex) +{ + // Exception layout: + // [__cxa_exception [_Unwind_Exception]] [exception object] + // + // __cxa_free_exception expects a pointer to the exception object + __cxa_free_exception(static_cast<void*>(ex + 1)); +} +static void dependent_exception_cleanup(_Unwind_Reason_Code reason, + struct _Unwind_Exception *ex) +{ + + __cxa_free_dependent_exception(static_cast<void*>(ex + 1)); +} + +/** + * Recursively walk a list of exceptions and delete them all in post-order. + */ +static void free_exception_list(__cxa_exception *ex) +{ + if (0 != ex->nextException) + { + free_exception_list(ex->nextException); + } + // __cxa_free_exception() expects to be passed the thrown object, which + // immediately follows the exception, not the exception itself + __cxa_free_exception(ex+1); +} + +/** + * Cleanup function called when a thread exists to make certain that all of the + * per-thread data is deleted. + */ +static void thread_cleanup(void* thread_info) +{ + __cxa_thread_info *info = static_cast<__cxa_thread_info*>(thread_info); + if (info->globals.caughtExceptions) + { + // If this is a foreign exception, ask it to clean itself up. + if (info->foreign_exception_state != __cxa_thread_info::none) + { + _Unwind_Exception *e = reinterpret_cast<_Unwind_Exception*>(info->globals.caughtExceptions); + if (e->exception_cleanup) + e->exception_cleanup(_URC_FOREIGN_EXCEPTION_CAUGHT, e); + } + else + { + free_exception_list(info->globals.caughtExceptions); + } + } + free(thread_info); +} + + +/** + * Once control used to protect the key creation. + */ +static pthread_once_t once_control = PTHREAD_ONCE_INIT; + +/** + * We may not be linked against a full pthread implementation. If we're not, + * then we need to fake the thread-local storage by storing 'thread-local' + * things in a global. + */ +static bool fakeTLS; +/** + * Thread-local storage for a single-threaded program. + */ +static __cxa_thread_info singleThreadInfo; +/** + * Initialise eh_key. + */ +static void init_key(void) +{ + if ((0 == pthread_key_create) || + (0 == pthread_setspecific) || + (0 == pthread_getspecific)) + { + fakeTLS = true; + return; + } + pthread_key_create(&eh_key, thread_cleanup); + pthread_setspecific(eh_key, reinterpret_cast<void *>(0x42)); + fakeTLS = (pthread_getspecific(eh_key) != reinterpret_cast<void *>(0x42)); + pthread_setspecific(eh_key, 0); +} + +/** + * Returns the thread info structure, creating it if it is not already created. + */ +static __cxa_thread_info *thread_info() +{ + if ((0 == pthread_once) || pthread_once(&once_control, init_key)) + { + fakeTLS = true; + } + if (fakeTLS) { return &singleThreadInfo; } + __cxa_thread_info *info = static_cast<__cxa_thread_info*>(pthread_getspecific(eh_key)); + if (0 == info) + { + info = static_cast<__cxa_thread_info*>(calloc(1, sizeof(__cxa_thread_info))); + pthread_setspecific(eh_key, info); + } + return info; +} +/** + * Fast version of thread_info(). May fail if thread_info() is not called on + * this thread at least once already. + */ +static __cxa_thread_info *thread_info_fast() +{ + if (fakeTLS) { return &singleThreadInfo; } + return static_cast<__cxa_thread_info*>(pthread_getspecific(eh_key)); +} +/** + * ABI function returning the __cxa_eh_globals structure. + */ +extern "C" __cxa_eh_globals *ABI_NAMESPACE::__cxa_get_globals(void) +{ + return &(thread_info()->globals); +} +/** + * Version of __cxa_get_globals() assuming that __cxa_get_globals() has already + * been called at least once by this thread. + */ +extern "C" __cxa_eh_globals *ABI_NAMESPACE::__cxa_get_globals_fast(void) +{ + return &(thread_info_fast()->globals); +} + +/** + * An emergency allocation reserved for when malloc fails. This is treated as + * 16 buffers of 1KB each. + */ +static char emergency_buffer[16384]; +/** + * Flag indicating whether each buffer is allocated. + */ +static bool buffer_allocated[16]; +/** + * Lock used to protect emergency allocation. + */ +static pthread_mutex_t emergency_malloc_lock = PTHREAD_MUTEX_INITIALIZER; +/** + * Condition variable used to wait when two threads are both trying to use the + * emergency malloc() buffer at once. + */ +static pthread_cond_t emergency_malloc_wait = PTHREAD_COND_INITIALIZER; + +/** + * Allocates size bytes from the emergency allocation mechanism, if possible. + * This function will fail if size is over 1KB or if this thread already has 4 + * emergency buffers. If all emergency buffers are allocated, it will sleep + * until one becomes available. + */ +static char *emergency_malloc(size_t size) +{ + if (size > 1024) { return 0; } + + __cxa_thread_info *info = thread_info(); + // Only 4 emergency buffers allowed per thread! + if (info->emergencyBuffersHeld > 3) { return 0; } + + pthread_mutex_lock(&emergency_malloc_lock); + int buffer = -1; + while (buffer < 0) + { + // While we were sleeping on the lock, another thread might have free'd + // enough memory for us to use, so try the allocation again - no point + // using the emergency buffer if there is some real memory that we can + // use... + void *m = calloc(1, size); + if (0 != m) + { + pthread_mutex_unlock(&emergency_malloc_lock); + return static_cast<char*>(m); + } + for (int i=0 ; i<16 ; i++) + { + if (!buffer_allocated[i]) + { + buffer = i; + buffer_allocated[i] = true; + 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); + } + } + pthread_mutex_unlock(&emergency_malloc_lock); + info->emergencyBuffersHeld++; + return emergency_buffer + (1024 * buffer); +} + +/** + * Frees a buffer returned by emergency_malloc(). + * + * Note: Neither this nor emergency_malloc() is particularly efficient. This + * should not matter, because neither will be called in normal operation - they + * are only used when the program runs out of memory, which should not happen + * often. + */ +static void emergency_malloc_free(char *ptr) +{ + int buffer = -1; + // Find the buffer corresponding to this pointer. + for (int i=0 ; i<16 ; i++) + { + if (ptr == static_cast<void*>(emergency_buffer + (1024 * i))) + { + buffer = i; + break; + } + } + 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. + memset(ptr, 0, 1024); + // 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); + // 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); +} + +static char *alloc_or_die(size_t size) +{ + char *buffer = static_cast<char*>(calloc(1, size)); + + // If calloc() doesn't want to give us any memory, try using an emergency + // buffer. + 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"); + std::terminate(); + } + } + return buffer; +} +static void free_exception(char *e) +{ + // 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)))) + { + emergency_malloc_free(e); + } + else + { + free(e); + } +} + +#ifdef __LP64__ +/** + * There's an ABI bug in __cxa_exception: unwindHeader requires 16-byte + * alignment but it was broken by the addition of the referenceCount. + * The unwindHeader is at offset 0x58 in __cxa_exception. In order to keep + * compatibility with consumers of the broken __cxa_exception, explicitly add + * padding on allocation (and account for it on free). + */ +static const int exception_alignment_padding = 8; +#else +static const int exception_alignment_padding = 0; +#endif + +/** + * Allocates an exception structure. Returns a pointer to the space that can + * 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) +{ + size_t size = exception_alignment_padding + sizeof(__cxa_exception) + + thrown_size; + char *buffer = alloc_or_die(size); + return buffer + exception_alignment_padding + sizeof(__cxa_exception); +} + +extern "C" void *__cxa_allocate_dependent_exception(void) +{ + size_t size = exception_alignment_padding + + sizeof(__cxa_dependent_exception); + char *buffer = alloc_or_die(size); + return buffer + exception_alignment_padding + + sizeof(__cxa_dependent_exception); +} + +/** + * __cxa_free_exception() is called when an exception was thrown in between + * calling __cxa_allocate_exception() and actually throwing the exception. + * This happens when the object's copy constructor throws an exception. + * + * In this implementation, it is also called by __cxa_end_catch() and during + * thread cleanup. + */ +extern "C" void __cxa_free_exception(void *thrown_exception) +{ + __cxa_exception *ex = reinterpret_cast<__cxa_exception*>(thrown_exception) - 1; + // Free the object that was thrown, calling its destructor + if (0 != ex->exceptionDestructor) + { + try + { + ex->exceptionDestructor(thrown_exception); + } + catch(...) + { + // FIXME: Check that this is really what the spec says to do. + std::terminate(); + } + } + + free_exception(reinterpret_cast<char*>(ex) - + exception_alignment_padding); +} + +static void releaseException(__cxa_exception *exception) +{ + if (isDependentException(exception->unwindHeader.exception_class)) + { + __cxa_free_dependent_exception(exception+1); + return; + } + if (__sync_sub_and_fetch(&exception->referenceCount, 1) == 0) + { + // __cxa_free_exception() expects to be passed the thrown object, + // which immediately follows the exception, not the exception + // itself + __cxa_free_exception(exception+1); + } +} + +void __cxa_free_dependent_exception(void *thrown_exception) +{ + __cxa_dependent_exception *ex = reinterpret_cast<__cxa_dependent_exception*>(thrown_exception) - 1; + assert(isDependentException(ex->unwindHeader.exception_class)); + if (ex->primaryException) + { + releaseException(realExceptionFromException(reinterpret_cast<__cxa_exception*>(ex))); + } + free_exception(reinterpret_cast<char*>(ex) - + exception_alignment_padding); +} + +/** + * Callback function used with _Unwind_Backtrace(). + * + * Prints a stack trace. Used only for debugging help. + * + * Note: As of FreeBSD 8.1, dladd() still doesn't work properly, so this only + * correctly prints function names from public, relocatable, symbols. + */ +static _Unwind_Reason_Code trace(struct _Unwind_Context *context, void *c) +{ + Dl_info myinfo; + int mylookup = + dladdr(reinterpret_cast<void *>(__cxa_current_exception_type), &myinfo); + void *ip = reinterpret_cast<void*>(_Unwind_GetIP(context)); + Dl_info info; + if (dladdr(ip, &info) != 0) + { + if (mylookup == 0 || strcmp(info.dli_fname, myinfo.dli_fname) != 0) + { + printf("%p:%s() in %s\n", ip, info.dli_sname, info.dli_fname); + } + } + return _URC_CONTINUE_UNWIND; +} + +/** + * Report a failure that occurred when attempting to throw an exception. + * + * If the failure happened by falling off the end of the stack without finding + * a handler, prints a back trace before aborting. + */ +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) +extern "C" void *__cxa_begin_catch(void *e) throw(); +#else +extern "C" void *__cxa_begin_catch(void *e); +#endif +static void report_failure(_Unwind_Reason_Code err, __cxa_exception *thrown_exception) +{ + switch (err) + { + default: break; + case _URC_FATAL_PHASE1_ERROR: + fprintf(stderr, "Fatal error during phase 1 unwinding\n"); + break; +#if !defined(__arm__) || defined(__ARM_DWARF_EH__) + case _URC_FATAL_PHASE2_ERROR: + fprintf(stderr, "Fatal error during phase 2 unwinding\n"); + break; +#endif + case _URC_END_OF_STACK: + __cxa_begin_catch (&(thrown_exception->unwindHeader)); + std::terminate(); + fprintf(stderr, "Terminating due to uncaught exception %p", + static_cast<void*>(thrown_exception)); + thrown_exception = realExceptionFromException(thrown_exception); + static const __class_type_info *e_ti = + static_cast<const __class_type_info*>(&typeid(std::exception)); + const __class_type_info *throw_ti = + dynamic_cast<const __class_type_info*>(thrown_exception->exceptionType); + if (throw_ti) + { + std::exception *e = + static_cast<std::exception*>(e_ti->cast_to(static_cast<void*>(thrown_exception+1), + throw_ti)); + if (e) + { + fprintf(stderr, " '%s'", e->what()); + } + } + + size_t bufferSize = 128; + char *demangled = static_cast<char*>(malloc(bufferSize)); + const char *mangled = thrown_exception->exceptionType->name(); + int status; + demangled = __cxa_demangle(mangled, demangled, &bufferSize, &status); + fprintf(stderr, " of type %s\n", + status == 0 ? demangled : mangled); + if (status == 0) { free(demangled); } + // Print a back trace if no handler is found. + // TODO: Make this optional +#ifndef __arm__ + _Unwind_Backtrace(trace, 0); +#endif + + // Just abort. No need to call std::terminate for the second time + abort(); + break; + } + std::terminate(); +} + +static void throw_exception(__cxa_exception *ex) +{ + __cxa_thread_info *info = thread_info(); + ex->unexpectedHandler = info->unexpectedHandler; + if (0 == ex->unexpectedHandler) + { + ex->unexpectedHandler = unexpectedHandler; + } + ex->terminateHandler = info->terminateHandler; + if (0 == ex->terminateHandler) + { + ex->terminateHandler = terminateHandler; + } + 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); +} + + +/** + * ABI function for throwing an exception. Takes the object to be thrown (the + * pointer returned by __cxa_allocate_exception()), the type info for the + * pointee, and the destructor (if there is one) as arguments. + */ +extern "C" void __cxa_throw(void *thrown_exception, + std::type_info *tinfo, + void(*dest)(void*)) +{ + __cxa_exception *ex = reinterpret_cast<__cxa_exception*>(thrown_exception) - 1; + + ex->referenceCount = 1; + ex->exceptionType = tinfo; + + ex->exceptionDestructor = dest; + + ex->unwindHeader.exception_class = exception_class; + ex->unwindHeader.exception_cleanup = exception_cleanup; + + throw_exception(ex); +} + +extern "C" void __cxa_rethrow_primary_exception(void* thrown_exception) +{ + if (NULL == thrown_exception) { return; } + + __cxa_exception *original = exceptionFromPointer(thrown_exception); + __cxa_dependent_exception *ex = reinterpret_cast<__cxa_dependent_exception*>(__cxa_allocate_dependent_exception())-1; + + ex->primaryException = thrown_exception; + __cxa_increment_exception_refcount(thrown_exception); + + ex->exceptionType = original->exceptionType; + ex->unwindHeader.exception_class = dependent_exception_class; + ex->unwindHeader.exception_cleanup = dependent_exception_cleanup; + + throw_exception(reinterpret_cast<__cxa_exception*>(ex)); +} + +extern "C" void *__cxa_current_primary_exception(void) +{ + __cxa_eh_globals* globals = __cxa_get_globals(); + __cxa_exception *ex = globals->caughtExceptions; + + if (0 == ex) { return NULL; } + ex = realExceptionFromException(ex); + __sync_fetch_and_add(&ex->referenceCount, 1); + return ex + 1; +} + +extern "C" void __cxa_increment_exception_refcount(void* thrown_exception) +{ + if (NULL == thrown_exception) { return; } + __cxa_exception *ex = static_cast<__cxa_exception*>(thrown_exception) - 1; + if (isDependentException(ex->unwindHeader.exception_class)) { return; } + __sync_fetch_and_add(&ex->referenceCount, 1); +} +extern "C" void __cxa_decrement_exception_refcount(void* thrown_exception) +{ + if (NULL == thrown_exception) { return; } + __cxa_exception *ex = static_cast<__cxa_exception*>(thrown_exception) - 1; + releaseException(ex); +} + +/** + * ABI function. Rethrows the current exception. Does not remove the + * exception from the stack or decrement its handler count - the compiler is + * expected to set the landing pad for this function to the end of the catch + * block, and then call _Unwind_Resume() to continue unwinding once + * __cxa_end_catch() has been called and any cleanup code has been run. + */ +extern "C" void __cxa_rethrow() +{ + __cxa_thread_info *ti = thread_info(); + __cxa_eh_globals *globals = &ti->globals; + // Note: We don't remove this from the caught list here, because + // __cxa_end_catch will be called when we unwind out of the try block. We + // could probably make this faster by providing an alternative rethrow + // function and ensuring that all cleanup code is run before calling it, so + // we can skip the top stack frame when unwinding. + __cxa_exception *ex = globals->caughtExceptions; + + if (0 == ex) + { + fprintf(stderr, + "Attempting to rethrow an exception that doesn't exist!\n"); + std::terminate(); + } + + if (ti->foreign_exception_state != __cxa_thread_info::none) + { + ti->foreign_exception_state = __cxa_thread_info::rethrown; + _Unwind_Exception *e = reinterpret_cast<_Unwind_Exception*>(ex); + _Unwind_Reason_Code err = _Unwind_Resume_or_Rethrow(e); + report_failure(err, ex); + return; + } + + assert(ex->handlerCount > 0 && "Rethrowing uncaught exception!"); + + // `globals->uncaughtExceptions` was decremented by `__cxa_begin_catch`. + // It's normally incremented by `throw_exception`, but this path invokes + // `_Unwind_Resume_or_Rethrow` directly to rethrow the exception. + // This path is only reachable if we're rethrowing a C++ exception - + // foreign exceptions don't adjust any of this state. + globals->uncaughtExceptions++; + + // ex->handlerCount will be decremented in __cxa_end_catch in enclosing + // catch block + + // Make handler count negative. This will tell __cxa_end_catch that + // exception was rethrown and exception object should not be destroyed + // when handler count become zero + ex->handlerCount = -ex->handlerCount; + + // Continue unwinding the stack with this exception. This should unwind to + // the place in the caller where __cxa_end_catch() is called. The caller + // will then run cleanup code and bounce the exception back with + // _Unwind_Resume(). + _Unwind_Reason_Code err = _Unwind_Resume_or_Rethrow(&ex->unwindHeader); + report_failure(err, ex); +} + +/** + * Returns the type_info object corresponding to the filter. + */ +static std::type_info *get_type_info_entry(_Unwind_Context *context, + dwarf_eh_lsda *lsda, + int filter) +{ + // Get the address of the record in the table. + dw_eh_ptr_t record = lsda->type_table - + dwarf_size_of_fixed_size_field(lsda->type_table_encoding)*filter; + //record -= 4; + dw_eh_ptr_t start = record; + // 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 (offset == 0) { return 0; } + + // ...so we need to resolve it + return reinterpret_cast<std::type_info*>(resolve_indirect_value(context, + lsda->type_table_encoding, offset, start)); +} + + + +/** + * Checks the type signature found in a handler against the type of the thrown + * 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, + void *&adjustedPtr) +{ + void *exception_ptr = static_cast<void*>(ex+1); + const std::type_info *ex_type = ex ? ex->exceptionType : 0; + + bool is_ptr = ex ? ex_type->__is_pointer_p() : false; + if (is_ptr) + { + exception_ptr = *static_cast<void**>(exception_ptr); + } + // Always match a catchall, even with a foreign exception + // + // Note: A 0 here is a catchall, not a cleanup, so we return true to + // indicate that we found a catch. + if (0 == type) + { + if (ex) + { + adjustedPtr = exception_ptr; + } + return true; + } + + if (0 == ex) { return false; } + + // If the types are the same, no casting is needed. + if (*type == *ex_type) + { + adjustedPtr = exception_ptr; + return true; + } + + + if (type->__do_catch(ex_type, &exception_ptr, 1)) + { + adjustedPtr = exception_ptr; + return true; + } + + return false; +} +/** + * Checks whether the exception matches the type specifiers in this action + * record. If the exception only matches cleanups, then this returns false. + * If it matches a catch (including a catchall) then it returns true. + * + * The selector argument is used to return the selector that is passed in the + * second exception register when installing the context. + */ +static handler_type check_action_record(_Unwind_Context *context, + dwarf_eh_lsda *lsda, + dw_eh_ptr_t action_record, + __cxa_exception *ex, + unsigned long *selector, + void *&adjustedPtr) +{ + if (!action_record) { return handler_cleanup; } + handler_type found = handler_none; + while (action_record) + { + int filter = read_sleb128(&action_record); + dw_eh_ptr_t action_record_offset_base = action_record; + int displacement = read_sleb128(&action_record); + action_record = displacement ? + action_record_offset_base + displacement : 0; + // We only check handler types for C++ exceptions - foreign exceptions + // are only allowed for cleanups and catchalls. + if (filter > 0) + { + std::type_info *handler_type = get_type_info_entry(context, lsda, filter); + if (check_type_signature(ex, handler_type, adjustedPtr)) + { + *selector = filter; + return handler_catch; + } + } + else if (filter < 0 && 0 != ex) + { + bool matched = false; + *selector = filter; +#if defined(__arm__) && !defined(__ARM_DWARF_EH__) + filter++; + std::type_info *handler_type = get_type_info_entry(context, lsda, filter--); + while (handler_type) + { + if (check_type_signature(ex, handler_type, adjustedPtr)) + { + matched = true; + break; + } + handler_type = get_type_info_entry(context, lsda, filter--); + } +#else + unsigned char *type_index = reinterpret_cast<unsigned char*>(lsda->type_table) - filter - 1; + while (*type_index) + { + std::type_info *handler_type = get_type_info_entry(context, lsda, *(type_index++)); + // If the exception spec matches a permitted throw type for + // this function, don't report a handler - we are allowed to + // propagate this exception out. + if (check_type_signature(ex, handler_type, adjustedPtr)) + { + matched = true; + break; + } + } +#endif + if (matched) { continue; } + // If we don't find an allowed exception spec, we need to install + // the context for this action. The landing pad will then call the + // unexpected exception function. Treat this as a catch + return handler_catch; + } + else if (filter == 0) + { + *selector = filter; + found = handler_cleanup; + } + } + return found; +} + +static void pushCleanupException(_Unwind_Exception *exceptionObject, + __cxa_exception *ex) +{ +#if defined(__arm__) && !defined(__ARM_DWARF_EH__) + __cxa_thread_info *info = thread_info_fast(); + if (ex) + { + ex->cleanupCount++; + if (ex->cleanupCount > 1) + { + assert(exceptionObject == info->currentCleanup); + return; + } + ex->nextCleanup = info->currentCleanup; + } + info->currentCleanup = exceptionObject; +#endif +} + +/** + * The exception personality function. This is referenced in the unwinding + * DWARF metadata and is called by the unwind library for each C++ stack frame + * containing catch or cleanup code. + */ +extern "C" +BEGIN_PERSONALITY_FUNCTION(__gxx_personality_v0) + // This personality function is for version 1 of the ABI. If you use it + // with a future version of the ABI, it won't know what to do, so it + // reports a fatal error and give up before it breaks anything. + if (1 != version) + { + return _URC_FATAL_PHASE1_ERROR; + } + __cxa_exception *ex = 0; + __cxa_exception *realEx = 0; + + // If this exception is throw by something else then we can't make any + // assumptions about its layout beyond the fields declared in + // _Unwind_Exception. + bool foreignException = !isCXXException(exceptionClass); + + // If this isn't a foreign exception, then we have a C++ exception structure + if (!foreignException) + { + ex = exceptionFromPointer(exceptionObject); + realEx = realExceptionFromException(ex); + } + +#if defined(__arm__) && !defined(__ARM_DWARF_EH__) + unsigned char *lsda_addr = + static_cast<unsigned char*>(_Unwind_GetLanguageSpecificData(context)); +#else + unsigned char *lsda_addr = + reinterpret_cast<unsigned char*>(static_cast<uintptr_t>(_Unwind_GetLanguageSpecificData(context))); +#endif + + // No LSDA implies no landing pads - try the next frame + if (0 == lsda_addr) { return continueUnwinding(exceptionObject, context); } + + // These two variables define how the exception will be handled. + dwarf_eh_action action = {0}; + unsigned long selector = 0; + + // During the search phase, we do a complete lookup. If we return + // _URC_HANDLER_FOUND, then the phase 2 unwind will call this function with + // a _UA_HANDLER_FRAME action, telling us to install the handler frame. If + // we return _URC_CONTINUE_UNWIND, we may be called again later with a + // _UA_CLEANUP_PHASE action for this frame. + // + // The point of the two-stage unwind allows us to entirely avoid any stack + // unwinding if there is no handler. If there are just cleanups found, + // then we can just panic call an abort function. + // + // Matching a handler is much more expensive than matching a cleanup, + // because we don't need to bother doing type comparisons (or looking at + // the type table at all) for a cleanup. This means that there is no need + // to cache the result of finding a cleanup, because it's (quite) quick to + // look it up again from the action table. + 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 + // 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, realEx, &selector, ex->adjustedPtr); + // If there's no action record, we've only found a cleanup, so keep + // searching for something real + if (found_handler == handler_catch) + { + // Cache the results for the phase 2 unwind, if we found a handler + // and this is not a foreign exception. + if (ex) + { + saveLandingPad(context, exceptionObject, ex, selector, action.landing_pad); + ex->languageSpecificData = reinterpret_cast<const char*>(lsda_addr); + ex->actionRecord = reinterpret_cast<const char*>(action.action_record); + // ex->adjustedPtr is set when finding the action record. + } + return _URC_HANDLER_FOUND; + } + return continueUnwinding(exceptionObject, context); + } + + + // If this is a foreign exception, we didn't have anywhere to cache the + // lookup stuff, so we need to do it again. If this is either a forced + // unwind, a foreign exception, or a cleanup, then we just install the + // context for a cleanup. + if (!(actions & _UA_HANDLER_FRAME)) + { + // cleanup + struct dwarf_eh_lsda lsda = parse_lsda(context, lsda_addr); + dwarf_eh_find_callsite(context, &lsda, &action); + if (0 == action.landing_pad) { return continueUnwinding(exceptionObject, context); } + handler_type found_handler = check_action_record(context, &lsda, + action.action_record, realEx, &selector, ex->adjustedPtr); + // Ignore handlers this time. + if (found_handler != handler_cleanup) { return continueUnwinding(exceptionObject, context); } + pushCleanupException(exceptionObject, ex); + } + else if (foreignException) + { + struct dwarf_eh_lsda lsda = parse_lsda(context, lsda_addr); + dwarf_eh_find_callsite(context, &lsda, &action); + check_action_record(context, &lsda, action.action_record, realEx, + &selector, ex->adjustedPtr); + } + else if (ex->catchTemp == 0) + { + // Uncaught exception in cleanup, calling terminate + std::terminate(); + } + else + { + // Restore the saved info if we saved some last time. + loadLandingPad(context, exceptionObject, ex, &selector, &action.landing_pad); + ex->catchTemp = 0; + ex->handlerSwitchValue = 0; + } + + + _Unwind_SetIP(context, reinterpret_cast<unsigned long>(action.landing_pad)); + _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), + reinterpret_cast<unsigned long>(exceptionObject)); + _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), selector); + + return _URC_INSTALL_CONTEXT; +} + +/** + * ABI function called when entering a catch statement. The argument is the + * pointer passed out of the personality function. This is always the start of + * the _Unwind_Exception object. The return value for this function is the + * pointer to the caught exception, which is either the adjusted pointer (for + * C++ exceptions) of the unadjusted pointer (for foreign exceptions). + */ +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) +extern "C" void *__cxa_begin_catch(void *e) throw() +#else +extern "C" void *__cxa_begin_catch(void *e) +#endif +{ + // We can't call the fast version here, because if the first exception that + // we see is a foreign exception then we won't have called it yet. + __cxa_thread_info *ti = thread_info(); + __cxa_eh_globals *globals = &ti->globals; + _Unwind_Exception *exceptionObject = static_cast<_Unwind_Exception*>(e); + + if (isCXXException(exceptionObject->exception_class)) + { + // Only exceptions thrown with a C++ exception throwing function will + // increment this, so don't decrement it here. + globals->uncaughtExceptions--; + __cxa_exception *ex = exceptionFromPointer(exceptionObject); + + 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. + ex->nextException = globals->caughtExceptions; + globals->caughtExceptions = ex; + } + + if (ex->handlerCount < 0) + { + // Rethrown exception is catched before end of catch block. + // Clear the rethrow flag (make value positive) - we are allowed + // to delete this exception at the end of the catch block, as long + // as it isn't thrown again later. + + // Code pattern: + // + // try { + // throw x; + // } + // catch() { + // try { + // throw; + // } + // catch() { + // __cxa_begin_catch() <- we are here + // } + // } + ex->handlerCount = -ex->handlerCount + 1; + } + else + { + ex->handlerCount++; + } + ti->foreign_exception_state = __cxa_thread_info::none; + + return ex->adjustedPtr; + } + else + { + // If this is a foreign exception, then we need to be able to + // store it. We can't chain foreign exceptions, so we give up + // if there are already some outstanding ones. + if (globals->caughtExceptions != 0) + { + std::terminate(); + } + globals->caughtExceptions = reinterpret_cast<__cxa_exception*>(exceptionObject); + ti->foreign_exception_state = __cxa_thread_info::caught; + } + // exceptionObject is the pointer to the _Unwind_Exception within the + // __cxa_exception. The throw object is after this + return (reinterpret_cast<char*>(exceptionObject) + sizeof(_Unwind_Exception)); +} + + + +/** + * ABI function called when exiting a catch block. This will free the current + * exception if it is no longer referenced in other catch blocks. + */ +extern "C" void __cxa_end_catch() +{ + // We can call the fast version here because the slow version is called in + // __cxa_throw(), which must have been called before we end a catch block + __cxa_thread_info *ti = thread_info_fast(); + __cxa_eh_globals *globals = &ti->globals; + __cxa_exception *ex = globals->caughtExceptions; + + assert(0 != ex && "Ending catch when no exception is on the stack!"); + + if (ti->foreign_exception_state != __cxa_thread_info::none) + { + if (ti->foreign_exception_state != __cxa_thread_info::rethrown) + { + _Unwind_Exception *e = reinterpret_cast<_Unwind_Exception*>(ti->globals.caughtExceptions); + if (e->exception_cleanup) + e->exception_cleanup(_URC_FOREIGN_EXCEPTION_CAUGHT, e); + } + globals->caughtExceptions = 0; + ti->foreign_exception_state = __cxa_thread_info::none; + return; + } + + bool deleteException = true; + + if (ex->handlerCount < 0) + { + // exception was rethrown. Exception should not be deleted even if + // handlerCount become zero. + // Code pattern: + // try { + // throw x; + // } + // catch() { + // { + // throw; + // } + // cleanup { + // __cxa_end_catch(); <- we are here + // } + // } + // + + ex->handlerCount++; + deleteException = false; + } + else + { + ex->handlerCount--; + } + + if (ex->handlerCount == 0) + { + globals->caughtExceptions = ex->nextException; + if (deleteException) + { + releaseException(ex); + } + } +} + +/** + * ABI function. Returns the type of the current exception. + */ +extern "C" std::type_info *__cxa_current_exception_type() +{ + __cxa_eh_globals *globals = __cxa_get_globals(); + __cxa_exception *ex = globals->caughtExceptions; + return ex ? ex->exceptionType : 0; +} + +/** + * Cleanup, ensures that `__cxa_end_catch` is called to balance an explicit + * `__cxa_begin_catch` call. + */ +static void end_catch(char *) +{ + __cxa_end_catch(); +} +/** + * ABI function, called when an exception specification is violated. + * + * This function does not return. + */ +extern "C" void __cxa_call_unexpected(void*exception) +{ + _Unwind_Exception *exceptionObject = static_cast<_Unwind_Exception*>(exception); + // Wrap the call to the unexpected handler in calls to `__cxa_begin_catch` + // and `__cxa_end_catch` so that we correctly update exception counts if + // the unexpected handler throws an exception. + __cxa_begin_catch(exceptionObject); + __attribute__((cleanup(end_catch))) + char unused; + if (exceptionObject->exception_class == exception_class) + { + __cxa_exception *ex = exceptionFromPointer(exceptionObject); + if (ex->unexpectedHandler) + { + ex->unexpectedHandler(); + // Should not be reached. + abort(); + } + } + std::unexpected(); + // Should not be reached. + abort(); +} + +/** + * ABI function, returns the adjusted pointer to the exception object. + */ +extern "C" void *__cxa_get_exception_ptr(void *exceptionObject) +{ + return exceptionFromPointer(exceptionObject)->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 +{ + /** + * 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(); + unexpected_handler old = info->unexpectedHandler; + 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(); + terminate_handler old = info->terminateHandler; + info->terminateHandler = f; + return old; + } +} + +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 ATOMIC_SWAP(&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 ATOMIC_SWAP(&terminateHandler, f); + } + /** + * Terminates the program, calling a custom terminate implementation if + * required. + */ + void terminate() + { + static __cxa_thread_info *info = thread_info(); + if (0 != info && 0 != info->terminateHandler) + { + info->terminateHandler(); + // Should not be reached - a terminate handler is not expected to + // return. + abort(); + } + 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(); + if (0 != info && 0 != info->unexpectedHandler) + { + info->unexpectedHandler(); + // Should not be reached - a terminate handler is not expected to + // return. + abort(); + } + unexpectedHandler(); + } + /** + * 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; + } + /** + * Returns the number of exceptions currently being thrown that have not + * been caught. This can occur inside a nested catch statement. + */ + int uncaught_exceptions() throw() + { + __cxa_thread_info *info = thread_info(); + return info->globals.uncaughtExceptions; + } + /** + * Returns the current unexpected handler. + */ + unexpected_handler get_unexpected() throw() + { + __cxa_thread_info *info = thread_info(); + if (info->unexpectedHandler) + { + return info->unexpectedHandler; + } + return ATOMIC_LOAD(&unexpectedHandler); + } + /** + * Returns the current terminate handler. + */ + terminate_handler get_terminate() throw() + { + __cxa_thread_info *info = thread_info(); + if (info->terminateHandler) + { + return info->terminateHandler; + } + return ATOMIC_LOAD(&terminateHandler); + } +} +#if defined(__arm__) && !defined(__ARM_DWARF_EH__) +extern "C" _Unwind_Exception *__cxa_get_cleanup(void) +{ + __cxa_thread_info *info = thread_info_fast(); + _Unwind_Exception *exceptionObject = info->currentCleanup; + if (isCXXException(exceptionObject->exception_class)) + { + __cxa_exception *ex = exceptionFromPointer(exceptionObject); + ex->cleanupCount--; + if (ex->cleanupCount == 0) + { + info->currentCleanup = ex->nextCleanup; + ex->nextCleanup = 0; + } + } + else + { + info->currentCleanup = 0; + } + return exceptionObject; +} + +asm ( +".pushsection .text.__cxa_end_cleanup \n" +".global __cxa_end_cleanup \n" +".type __cxa_end_cleanup, \"function\" \n" +"__cxa_end_cleanup: \n" +" push {r1, r2, r3, r4} \n" +" bl __cxa_get_cleanup \n" +" push {r1, r2, r3, r4} \n" +" b _Unwind_Resume \n" +" bl abort \n" +".popsection \n" +); +#endif diff --git a/contrib/libcxxrt/guard.cc b/contrib/libcxxrt/guard.cc new file mode 100644 index 000000000000..34d294cf7432 --- /dev/null +++ b/contrib/libcxxrt/guard.cc @@ -0,0 +1,193 @@ +/* + * Copyright 2010-2012 PathScale, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * 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 <stdlib.h> +#include <stdio.h> +#include <pthread.h> +#include <assert.h> +#include "atomic.h" + +// Older GCC doesn't define __LITTLE_ENDIAN__ +#ifndef __LITTLE_ENDIAN__ + // If __BYTE_ORDER__ is defined, use that instead +# ifdef __BYTE_ORDER__ +# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define __LITTLE_ENDIAN__ +# endif + // x86 and ARM are the most common little-endian CPUs, so let's have a + // special case for them (ARM is already special cased). Assume everything + // else is big endian. +# elif defined(__x86_64) || defined(__i386) +# define __LITTLE_ENDIAN__ +# endif +#endif + + +/* + * The least significant bit of the guard variable indicates that the object + * has been initialised, the most significant bit is used for a spinlock. + */ +#ifdef __arm__ +// ARM ABI - 32-bit guards. +typedef uint32_t guard_t; +typedef uint32_t guard_lock_t; +static const uint32_t LOCKED = static_cast<guard_t>(1) << 31; +static const uint32_t INITIALISED = 1; +#define LOCK_PART(guard) (guard) +#define INIT_PART(guard) (guard) +#elif defined(_LP64) +typedef uint64_t guard_t; +typedef uint64_t guard_lock_t; +# if defined(__LITTLE_ENDIAN__) +static const guard_t LOCKED = static_cast<guard_t>(1) << 63; +static const guard_t INITIALISED = 1; +# else +static const guard_t LOCKED = 1; +static const guard_t INITIALISED = static_cast<guard_t>(1) << 56; +# endif +#define LOCK_PART(guard) (guard) +#define INIT_PART(guard) (guard) +#else +typedef uint32_t guard_lock_t; +# if defined(__LITTLE_ENDIAN__) +typedef struct { + uint32_t init_half; + uint32_t lock_half; +} guard_t; +static const uint32_t LOCKED = static_cast<guard_lock_t>(1) << 31; +static const uint32_t INITIALISED = 1; +# else +typedef struct { + uint32_t init_half; + uint32_t lock_half; +} guard_t; +static_assert(sizeof(guard_t) == sizeof(uint64_t), ""); +static const uint32_t LOCKED = 1; +static const uint32_t INITIALISED = static_cast<guard_lock_t>(1) << 24; +# endif +#define LOCK_PART(guard) (&(guard)->lock_half) +#define INIT_PART(guard) (&(guard)->init_half) +#endif +static const guard_lock_t INITIAL = 0; + +/** + * 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 guard_t *guard_object) +{ + guard_lock_t old; + // Not an atomic read, doesn't establish a happens-before relationship, but + // if one is already established and we end up seeing an initialised state + // then it's a fast path, otherwise we'll do something more expensive than + // this test anyway... + if (INITIALISED == *INIT_PART(guard_object)) + return 0; + // Spin trying to do the initialisation + for (;;) + { + // Loop trying to move the value of the guard from 0 (not + // locked, not initialised) to the locked-uninitialised + // position. + old = __sync_val_compare_and_swap(LOCK_PART(guard_object), + INITIAL, LOCKED); + if (old == INITIAL) { + // Lock obtained. If lock and init bit are + // in separate words, check for init race. + if (INIT_PART(guard_object) == LOCK_PART(guard_object)) + return 1; + if (INITIALISED != *INIT_PART(guard_object)) + return 1; + + // No need for a memory barrier here, + // see first comment. + *LOCK_PART(guard_object) = INITIAL; + return 0; + } + // If lock and init bit are in the same word, check again + // if we are done. + if (INIT_PART(guard_object) == LOCK_PART(guard_object) && + old == INITIALISED) + return 0; + + assert(old == LOCKED); + // Another thread holds the lock. + // If lock and init bit are in different words, check + // if we are done before yielding and looping. + if (INIT_PART(guard_object) != LOCK_PART(guard_object) && + INITIALISED == *INIT_PART(guard_object)) + return 0; + sched_yield(); + } +} + +/** + * 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(volatile guard_t *guard_object) +{ + __attribute__((unused)) + bool reset = __sync_bool_compare_and_swap(LOCK_PART(guard_object), + LOCKED, INITIAL); + assert(reset); +} +/** + * 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(volatile guard_t *guard_object) +{ + guard_lock_t old; + if (INIT_PART(guard_object) == LOCK_PART(guard_object)) + old = LOCKED; + else + old = INITIAL; + __attribute__((unused)) + bool reset = __sync_bool_compare_and_swap(INIT_PART(guard_object), + old, INITIALISED); + assert(reset); + if (INIT_PART(guard_object) != LOCK_PART(guard_object)) + *LOCK_PART(guard_object) = INITIAL; +} diff --git a/contrib/libcxxrt/libelftc_dem_gnu3.c b/contrib/libcxxrt/libelftc_dem_gnu3.c new file mode 100644 index 000000000000..14a9b7420095 --- /dev/null +++ b/contrib/libcxxrt/libelftc_dem_gnu3.c @@ -0,0 +1,3895 @@ +/*- + * Copyright (c) 2007, 2008 Hyogeol Lee <hyogeollee@gmail.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <sys/types.h> +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/** + * @file cpp_demangle.c + * @brief Decode IA-64 C++ ABI style implementation. + * + * IA-64 standard ABI(Itanium C++ ABI) references. + * + * http://www.codesourcery.com/cxx-abi/abi.html#mangling \n + * http://www.codesourcery.com/cxx-abi/abi-mangling.html + */ + +/** @brief Dynamic vector data for string. */ +struct vector_str { + /** Current size */ + size_t size; + /** Total capacity */ + size_t capacity; + /** String array */ + char **container; +}; + +#define BUFFER_GROWFACTOR 1.618 +#define VECTOR_DEF_CAPACITY 8 +#define ELFTC_ISDIGIT(C) (isdigit((C) & 0xFF)) + +enum type_qualifier { + TYPE_PTR, TYPE_REF, TYPE_CMX, TYPE_IMG, TYPE_EXT, TYPE_RST, TYPE_VAT, + TYPE_CST, TYPE_VEC +}; + +struct vector_type_qualifier { + size_t size, capacity; + enum type_qualifier *q_container; + struct vector_str ext_name; +}; + +enum read_cmd { + READ_FAIL, READ_NEST, READ_TMPL, READ_EXPR, READ_EXPL, READ_LOCAL, + READ_TYPE, READ_FUNC, READ_PTRMEM +}; + +struct vector_read_cmd { + size_t size, capacity; + enum read_cmd *r_container; +}; + +struct cpp_demangle_data { + struct vector_str output; /* output string vector */ + struct vector_str output_tmp; + struct vector_str subst; /* substitution string vector */ + struct vector_str tmpl; + struct vector_str class_type; + struct vector_read_cmd cmd; + bool paren; /* parenthesis opened */ + bool pfirst; /* first element of parameter */ + bool mem_rst; /* restrict member function */ + bool mem_vat; /* volatile member function */ + bool mem_cst; /* const member function */ + int func_type; + const char *cur; /* current mangled name ptr */ + const char *last_sname; /* last source name */ + int push_head; +}; + +#define CPP_DEMANGLE_TRY_LIMIT 128 +#define FLOAT_SPRINTF_TRY_LIMIT 5 +#define FLOAT_QUADRUPLE_BYTES 16 +#define FLOAT_EXTENED_BYTES 10 + +#define SIMPLE_HASH(x,y) (64 * x + y) + +static size_t get_strlen_sum(const struct vector_str *v); +static bool vector_str_grow(struct vector_str *v); + +static size_t +get_strlen_sum(const struct vector_str *v) +{ + size_t i, len = 0; + + if (v == NULL) + return (0); + + assert(v->size > 0); + + for (i = 0; i < v->size; ++i) + len += strlen(v->container[i]); + + return (len); +} + +/** + * @brief Deallocate resource in vector_str. + */ +static void +vector_str_dest(struct vector_str *v) +{ + size_t i; + + if (v == NULL) + return; + + for (i = 0; i < v->size; ++i) + free(v->container[i]); + + free(v->container); +} + +/** + * @brief Find string in vector_str. + * @param v Destination vector. + * @param o String to find. + * @param l Length of the string. + * @return -1 at failed, 0 at not found, 1 at found. + */ +static int +vector_str_find(const struct vector_str *v, const char *o, size_t l) +{ + size_t i; + + if (v == NULL || o == NULL) + return (-1); + + for (i = 0; i < v->size; ++i) + if (strncmp(v->container[i], o, l) == 0) + return (1); + + return (0); +} + +/** + * @brief Get new allocated flat string from vector. + * + * If l is not NULL, return length of the string. + * @param v Destination vector. + * @param l Length of the string. + * @return NULL at failed or NUL terminated new allocated string. + */ +static char * +vector_str_get_flat(const struct vector_str *v, size_t *l) +{ + ssize_t elem_pos, elem_size, rtn_size; + size_t i; + char *rtn; + + if (v == NULL || v->size == 0) + return (NULL); + + if ((rtn_size = get_strlen_sum(v)) == 0) + return (NULL); + + if ((rtn = malloc(sizeof(char) * (rtn_size + 1))) == NULL) + return (NULL); + + elem_pos = 0; + for (i = 0; i < v->size; ++i) { + elem_size = strlen(v->container[i]); + + memcpy(rtn + elem_pos, v->container[i], elem_size); + + elem_pos += elem_size; + } + + rtn[rtn_size] = '\0'; + + if (l != NULL) + *l = rtn_size; + + return (rtn); +} + +static bool +vector_str_grow(struct vector_str *v) +{ + size_t i, tmp_cap; + char **tmp_ctn; + + if (v == NULL) + return (false); + + assert(v->capacity > 0); + + tmp_cap = v->capacity * BUFFER_GROWFACTOR; + + assert(tmp_cap > v->capacity); + + if ((tmp_ctn = malloc(sizeof(char *) * tmp_cap)) == NULL) + return (false); + + for (i = 0; i < v->size; ++i) + tmp_ctn[i] = v->container[i]; + + free(v->container); + + v->container = tmp_ctn; + v->capacity = tmp_cap; + + return (true); +} + +/** + * @brief Initialize vector_str. + * @return false at failed, true at success. + */ +static bool +vector_str_init(struct vector_str *v) +{ + + if (v == NULL) + return (false); + + v->size = 0; + v->capacity = VECTOR_DEF_CAPACITY; + + assert(v->capacity > 0); + + if ((v->container = malloc(sizeof(char *) * v->capacity)) == NULL) + return (false); + + assert(v->container != NULL); + + return (true); +} + +/** + * @brief Remove last element in vector_str. + * @return false at failed, true at success. + */ +static bool +vector_str_pop(struct vector_str *v) +{ + + if (v == NULL) + return (false); + + if (v->size == 0) + return (true); + + --v->size; + + free(v->container[v->size]); + v->container[v->size] = NULL; + + return (true); +} + +/** + * @brief Push back string to vector. + * @return false at failed, true at success. + */ +static bool +vector_str_push(struct vector_str *v, const char *str, size_t len) +{ + + if (v == NULL || str == NULL) + return (false); + + if (v->size == v->capacity && vector_str_grow(v) == false) + return (false); + + if ((v->container[v->size] = malloc(sizeof(char) * (len + 1))) == NULL) + return (false); + + snprintf(v->container[v->size], len + 1, "%s", str); + + ++v->size; + + return (true); +} + +/** + * @brief Push front org vector to det vector. + * @return false at failed, true at success. + */ +static bool +vector_str_push_vector_head(struct vector_str *dst, struct vector_str *org) +{ + size_t i, j, tmp_cap; + char **tmp_ctn; + + if (dst == NULL || org == NULL) + return (false); + + tmp_cap = (dst->size + org->size) * BUFFER_GROWFACTOR; + + if ((tmp_ctn = malloc(sizeof(char *) * tmp_cap)) == NULL) + return (false); + + for (i = 0; i < org->size; ++i) + if ((tmp_ctn[i] = strdup(org->container[i])) == NULL) { + for (j = 0; j < i; ++j) + free(tmp_ctn[j]); + + free(tmp_ctn); + + return (false); + } + + for (i = 0; i < dst->size; ++i) + tmp_ctn[i + org->size] = dst->container[i]; + + free(dst->container); + + dst->container = tmp_ctn; + dst->capacity = tmp_cap; + dst->size += org->size; + + return (true); +} + +/** + * @brief Get new allocated flat string from vector between begin and end. + * + * If r_len is not NULL, string length will be returned. + * @return NULL at failed or NUL terminated new allocated string. + */ +static char * +vector_str_substr(const struct vector_str *v, size_t begin, size_t end, + size_t *r_len) +{ + size_t cur, i, len; + char *rtn; + + if (v == NULL || begin > end) + return (NULL); + + len = 0; + for (i = begin; i < end + 1; ++i) + len += strlen(v->container[i]); + + if ((rtn = malloc(sizeof(char) * (len + 1))) == NULL) + return (NULL); + + if (r_len != NULL) + *r_len = len; + + cur = 0; + for (i = begin; i < end + 1; ++i) { + len = strlen(v->container[i]); + memcpy(rtn + cur, v->container[i], len); + cur += len; + } + rtn[cur] = '\0'; + + return (rtn); +} + +static void cpp_demangle_data_dest(struct cpp_demangle_data *); +static int cpp_demangle_data_init(struct cpp_demangle_data *, + const char *); +static int cpp_demangle_get_subst(struct cpp_demangle_data *, size_t); +static int cpp_demangle_get_tmpl_param(struct cpp_demangle_data *, size_t); +static int cpp_demangle_push_fp(struct cpp_demangle_data *, + char *(*)(const char *, size_t)); +static int cpp_demangle_push_str(struct cpp_demangle_data *, const char *, + size_t); +static int cpp_demangle_push_subst(struct cpp_demangle_data *, + const char *, size_t); +static int cpp_demangle_push_subst_v(struct cpp_demangle_data *, + struct vector_str *); +static int cpp_demangle_push_type_qualifier(struct cpp_demangle_data *, + struct vector_type_qualifier *, const char *); +static int cpp_demangle_read_array(struct cpp_demangle_data *); +static int cpp_demangle_read_encoding(struct cpp_demangle_data *); +static int cpp_demangle_read_expr_primary(struct cpp_demangle_data *); +static int cpp_demangle_read_expression(struct cpp_demangle_data *); +static int cpp_demangle_read_expression_flat(struct cpp_demangle_data *, + char **); +static int cpp_demangle_read_expression_binary(struct cpp_demangle_data *, + const char *, size_t); +static int cpp_demangle_read_expression_unary(struct cpp_demangle_data *, + const char *, size_t); +static int cpp_demangle_read_expression_trinary(struct cpp_demangle_data *, + const char *, size_t, const char *, size_t); +static int cpp_demangle_read_function(struct cpp_demangle_data *, int *, + struct vector_type_qualifier *); +static int cpp_demangle_local_source_name(struct cpp_demangle_data *ddata); +static int cpp_demangle_read_local_name(struct cpp_demangle_data *); +static int cpp_demangle_read_name(struct cpp_demangle_data *); +static int cpp_demangle_read_name_flat(struct cpp_demangle_data *, + char**); +static int cpp_demangle_read_nested_name(struct cpp_demangle_data *); +static int cpp_demangle_read_number(struct cpp_demangle_data *, long *); +static int cpp_demangle_read_number_as_string(struct cpp_demangle_data *, + char **); +static int cpp_demangle_read_nv_offset(struct cpp_demangle_data *); +static int cpp_demangle_read_offset(struct cpp_demangle_data *); +static int cpp_demangle_read_offset_number(struct cpp_demangle_data *); +static int cpp_demangle_read_pointer_to_member(struct cpp_demangle_data *); +static int cpp_demangle_read_sname(struct cpp_demangle_data *); +static int cpp_demangle_read_subst(struct cpp_demangle_data *); +static int cpp_demangle_read_subst_std(struct cpp_demangle_data *); +static int cpp_demangle_read_subst_stdtmpl(struct cpp_demangle_data *, + const char *, size_t); +static int cpp_demangle_read_tmpl_arg(struct cpp_demangle_data *); +static int cpp_demangle_read_tmpl_args(struct cpp_demangle_data *); +static int cpp_demangle_read_tmpl_param(struct cpp_demangle_data *); +static int cpp_demangle_read_type(struct cpp_demangle_data *, int); +static int cpp_demangle_read_type_flat(struct cpp_demangle_data *, + char **); +static int cpp_demangle_read_uqname(struct cpp_demangle_data *); +static int cpp_demangle_read_v_offset(struct cpp_demangle_data *); +static char *decode_fp_to_double(const char *, size_t); +static char *decode_fp_to_float(const char *, size_t); +static char *decode_fp_to_float128(const char *, size_t); +static char *decode_fp_to_float80(const char *, size_t); +static char *decode_fp_to_long_double(const char *, size_t); +static int hex_to_dec(char); +static void vector_read_cmd_dest(struct vector_read_cmd *); +static int vector_read_cmd_find(struct vector_read_cmd *, enum read_cmd); +static int vector_read_cmd_init(struct vector_read_cmd *); +static int vector_read_cmd_pop(struct vector_read_cmd *); +static int vector_read_cmd_push(struct vector_read_cmd *, enum read_cmd); +static void vector_type_qualifier_dest(struct vector_type_qualifier *); +static int vector_type_qualifier_init(struct vector_type_qualifier *); +static int vector_type_qualifier_push(struct vector_type_qualifier *, + enum type_qualifier); + +/** + * @brief Decode the input string by IA-64 C++ ABI style. + * + * GNU GCC v3 use IA-64 standard ABI. + * @return New allocated demangled string or NULL if failed. + * @todo 1. Testing and more test case. 2. Code cleaning. + */ +char * +__cxa_demangle_gnu3(const char *org) +{ + struct cpp_demangle_data ddata; + ssize_t org_len; + unsigned int limit; + char *rtn = NULL; + + if (org == NULL) + return (NULL); + + org_len = strlen(org); + if (org_len > 11 && !strncmp(org, "_GLOBAL__I_", 11)) { + if ((rtn = malloc(org_len + 19)) == NULL) + return (NULL); + snprintf(rtn, org_len + 19, + "global constructors keyed to %s", org + 11); + return (rtn); + } + + // Try demangling as a type for short encodings + if ((org_len < 2) || (org[0] != '_' || org[1] != 'Z' )) { + if (!cpp_demangle_data_init(&ddata, org)) + return (NULL); + if (!cpp_demangle_read_type(&ddata, 0)) + goto clean; + rtn = vector_str_get_flat(&ddata.output, (size_t *) NULL); + goto clean; + } + + + if (!cpp_demangle_data_init(&ddata, org + 2)) + return (NULL); + + rtn = NULL; + + if (!cpp_demangle_read_encoding(&ddata)) + goto clean; + + limit = 0; + while (*ddata.cur != '\0') { + /* + * Breaking at some gcc info at tail. e.g) @@GLIBCXX_3.4 + */ + if (*ddata.cur == '@' && *(ddata.cur + 1) == '@') + break; + if (!cpp_demangle_read_type(&ddata, 1)) + goto clean; + if (limit++ > CPP_DEMANGLE_TRY_LIMIT) + goto clean; + } + + if (ddata.output.size == 0) + goto clean; + if (ddata.paren && !vector_str_push(&ddata.output, ")", 1)) + goto clean; + if (ddata.mem_vat && !vector_str_push(&ddata.output, " volatile", 9)) + goto clean; + if (ddata.mem_cst && !vector_str_push(&ddata.output, " const", 6)) + goto clean; + if (ddata.mem_rst && !vector_str_push(&ddata.output, " restrict", 9)) + goto clean; + + rtn = vector_str_get_flat(&ddata.output, (size_t *) NULL); + +clean: + cpp_demangle_data_dest(&ddata); + + return (rtn); +} + +static void +cpp_demangle_data_dest(struct cpp_demangle_data *d) +{ + + if (d == NULL) + return; + + vector_read_cmd_dest(&d->cmd); + vector_str_dest(&d->class_type); + vector_str_dest(&d->tmpl); + vector_str_dest(&d->subst); + vector_str_dest(&d->output_tmp); + vector_str_dest(&d->output); +} + +static int +cpp_demangle_data_init(struct cpp_demangle_data *d, const char *cur) +{ + + if (d == NULL || cur == NULL) + return (0); + + if (!vector_str_init(&d->output)) + return (0); + if (!vector_str_init(&d->output_tmp)) + goto clean1; + if (!vector_str_init(&d->subst)) + goto clean2; + if (!vector_str_init(&d->tmpl)) + goto clean3; + if (!vector_str_init(&d->class_type)) + goto clean4; + if (!vector_read_cmd_init(&d->cmd)) + goto clean5; + + assert(d->output.container != NULL); + assert(d->output_tmp.container != NULL); + assert(d->subst.container != NULL); + assert(d->tmpl.container != NULL); + assert(d->class_type.container != NULL); + + d->paren = false; + d->pfirst = false; + d->mem_rst = false; + d->mem_vat = false; + d->mem_cst = false; + d->func_type = 0; + d->cur = cur; + d->last_sname = NULL; + d->push_head = 0; + + return (1); + +clean5: + vector_str_dest(&d->class_type); +clean4: + vector_str_dest(&d->tmpl); +clean3: + vector_str_dest(&d->subst); +clean2: + vector_str_dest(&d->output_tmp); +clean1: + vector_str_dest(&d->output); + + return (0); +} + +static int +cpp_demangle_push_fp(struct cpp_demangle_data *ddata, + char *(*decoder)(const char *, size_t)) +{ + size_t len; + int rtn; + const char *fp; + char *f; + + if (ddata == NULL || decoder == NULL) + return (0); + + fp = ddata->cur; + while (*ddata->cur != 'E') + ++ddata->cur; + + if ((f = decoder(fp, ddata->cur - fp)) == NULL) + return (0); + + rtn = 0; + if ((len = strlen(f)) > 0) + rtn = cpp_demangle_push_str(ddata, f, len); + + free(f); + + ++ddata->cur; + + return (rtn); +} + +static int +cpp_demangle_push_str(struct cpp_demangle_data *ddata, const char *str, + size_t len) +{ + + if (ddata == NULL || str == NULL || len == 0) + return (0); + + if (ddata->push_head > 0) + return (vector_str_push(&ddata->output_tmp, str, len)); + + return (vector_str_push(&ddata->output, str, len)); +} + +static int +cpp_demangle_push_subst(struct cpp_demangle_data *ddata, const char *str, + size_t len) +{ + + if (ddata == NULL || str == NULL || len == 0) + return (0); + + if (!vector_str_find(&ddata->subst, str, len)) + return (vector_str_push(&ddata->subst, str, len)); + + return (1); +} + +static int +cpp_demangle_push_subst_v(struct cpp_demangle_data *ddata, struct vector_str *v) +{ + size_t str_len; + int rtn; + char *str; + + if (ddata == NULL || v == NULL) + return (0); + + if ((str = vector_str_get_flat(v, &str_len)) == NULL) + return (0); + + rtn = cpp_demangle_push_subst(ddata, str, str_len); + + free(str); + + return (rtn); +} + +static int +cpp_demangle_push_type_qualifier(struct cpp_demangle_data *ddata, + struct vector_type_qualifier *v, const char *type_str) +{ + struct vector_str subst_v; + size_t idx, e_idx, e_len; + int rtn; + char *buf; + + if (ddata == NULL || v == NULL) + return (0); + + if ((idx = v->size) == 0) + return (1); + + rtn = 0; + if (type_str != NULL) { + if (!vector_str_init(&subst_v)) + return (0); + if (!vector_str_push(&subst_v, type_str, strlen(type_str))) + goto clean; + } + + e_idx = 0; + while (idx > 0) { + switch (v->q_container[idx - 1]) { + case TYPE_PTR: + if (!cpp_demangle_push_str(ddata, "*", 1)) + goto clean; + if (type_str != NULL) { + if (!vector_str_push(&subst_v, "*", 1)) + goto clean; + if (!cpp_demangle_push_subst_v(ddata, + &subst_v)) + goto clean; + } + break; + + case TYPE_REF: + if (!cpp_demangle_push_str(ddata, "&", 1)) + goto clean; + if (type_str != NULL) { + if (!vector_str_push(&subst_v, "&", 1)) + goto clean; + if (!cpp_demangle_push_subst_v(ddata, + &subst_v)) + goto clean; + } + break; + + case TYPE_CMX: + if (!cpp_demangle_push_str(ddata, " complex", 8)) + goto clean; + if (type_str != NULL) { + if (!vector_str_push(&subst_v, " complex", 8)) + goto clean; + if (!cpp_demangle_push_subst_v(ddata, + &subst_v)) + goto clean; + } + break; + + case TYPE_IMG: + if (!cpp_demangle_push_str(ddata, " imaginary", 10)) + goto clean; + if (type_str != NULL) { + if (!vector_str_push(&subst_v, " imaginary", + 10)) + goto clean; + if (!cpp_demangle_push_subst_v(ddata, + &subst_v)) + goto clean; + } + break; + + case TYPE_EXT: + if (v->ext_name.size == 0 || + e_idx > v->ext_name.size - 1) + goto clean; + if ((e_len = strlen(v->ext_name.container[e_idx])) == + 0) + goto clean; + if ((buf = malloc(e_len + 2)) == NULL) + goto clean; + snprintf(buf, e_len + 2, " %s", + v->ext_name.container[e_idx]); + + if (!cpp_demangle_push_str(ddata, buf, e_len + 1)) { + free(buf); + goto clean; + } + + if (type_str != NULL) { + if (!vector_str_push(&subst_v, buf, + e_len + 1)) { + free(buf); + goto clean; + } + if (!cpp_demangle_push_subst_v(ddata, + &subst_v)) { + free(buf); + goto clean; + } + } + free(buf); + ++e_idx; + break; + + case TYPE_RST: + if (!cpp_demangle_push_str(ddata, " restrict", 9)) + goto clean; + if (type_str != NULL) { + if (!vector_str_push(&subst_v, " restrict", 9)) + goto clean; + if (!cpp_demangle_push_subst_v(ddata, + &subst_v)) + goto clean; + } + break; + + case TYPE_VAT: + if (!cpp_demangle_push_str(ddata, " volatile", 9)) + goto clean; + if (type_str != NULL) { + if (!vector_str_push(&subst_v, " volatile", 9)) + goto clean; + if (!cpp_demangle_push_subst_v(ddata, + &subst_v)) + goto clean; + } + break; + + case TYPE_CST: + if (!cpp_demangle_push_str(ddata, " const", 6)) + goto clean; + if (type_str != NULL) { + if (!vector_str_push(&subst_v, " const", 6)) + goto clean; + if (!cpp_demangle_push_subst_v(ddata, + &subst_v)) + goto clean; + } + break; + + case TYPE_VEC: + if (v->ext_name.size == 0 || + e_idx > v->ext_name.size - 1) + goto clean; + if ((e_len = strlen(v->ext_name.container[e_idx])) == + 0) + goto clean; + if ((buf = malloc(e_len + 12)) == NULL) + goto clean; + snprintf(buf, e_len + 12, " __vector(%s)", + v->ext_name.container[e_idx]); + if (!cpp_demangle_push_str(ddata, buf, e_len + 11)) { + free(buf); + goto clean; + } + if (type_str != NULL) { + if (!vector_str_push(&subst_v, buf, + e_len + 11)) { + free(buf); + goto clean; + } + if (!cpp_demangle_push_subst_v(ddata, + &subst_v)) { + free(buf); + goto clean; + } + } + free(buf); + ++e_idx; + break; + } + --idx; + } + + rtn = 1; +clean: + if (type_str != NULL) + vector_str_dest(&subst_v); + + return (rtn); +} + +static int +cpp_demangle_get_subst(struct cpp_demangle_data *ddata, size_t idx) +{ + size_t len; + + if (ddata == NULL || ddata->subst.size <= idx) + return (0); + if ((len = strlen(ddata->subst.container[idx])) == 0) + return (0); + if (!cpp_demangle_push_str(ddata, ddata->subst.container[idx], len)) + return (0); + + /* skip '_' */ + ++ddata->cur; + + return (1); +} + +static int +cpp_demangle_get_tmpl_param(struct cpp_demangle_data *ddata, size_t idx) +{ + size_t len; + + if (ddata == NULL || ddata->tmpl.size <= idx) + return (0); + if ((len = strlen(ddata->tmpl.container[idx])) == 0) + return (0); + if (!cpp_demangle_push_str(ddata, ddata->tmpl.container[idx], len)) + return (0); + + ++ddata->cur; + + return (1); +} + +static int +cpp_demangle_read_array(struct cpp_demangle_data *ddata) +{ + size_t i, num_len, exp_len, p_idx, idx; + const char *num; + char *exp; + + if (ddata == NULL || *(++ddata->cur) == '\0') + return (0); + + if (*ddata->cur == '_') { + if (*(++ddata->cur) == '\0') + return (0); + + if (!cpp_demangle_read_type(ddata, 0)) + return (0); + + if (!cpp_demangle_push_str(ddata, "[]", 2)) + return (0); + } else { + if (ELFTC_ISDIGIT(*ddata->cur) != 0) { + num = ddata->cur; + while (ELFTC_ISDIGIT(*ddata->cur) != 0) + ++ddata->cur; + if (*ddata->cur != '_') + return (0); + num_len = ddata->cur - num; + assert(num_len > 0); + if (*(++ddata->cur) == '\0') + return (0); + if (!cpp_demangle_read_type(ddata, 0)) + return (0); + if (!cpp_demangle_push_str(ddata, "[", 1)) + return (0); + if (!cpp_demangle_push_str(ddata, num, num_len)) + return (0); + if (!cpp_demangle_push_str(ddata, "]", 1)) + return (0); + } else { + p_idx = ddata->output.size; + if (!cpp_demangle_read_expression(ddata)) + return (0); + if ((exp = vector_str_substr(&ddata->output, p_idx, + ddata->output.size - 1, &exp_len)) == NULL) + return (0); + idx = ddata->output.size; + for (i = p_idx; i < idx; ++i) + if (!vector_str_pop(&ddata->output)) { + free(exp); + return (0); + } + if (*ddata->cur != '_') { + free(exp); + return (0); + } + ++ddata->cur; + if (*ddata->cur == '\0') { + free(exp); + return (0); + } + if (!cpp_demangle_read_type(ddata, 0)) { + free(exp); + return (0); + } + if (!cpp_demangle_push_str(ddata, "[", 1)) { + free(exp); + return (0); + } + if (!cpp_demangle_push_str(ddata, exp, exp_len)) { + free(exp); + return (0); + } + if (!cpp_demangle_push_str(ddata, "]", 1)) { + free(exp); + return (0); + } + free(exp); + } + } + + return (1); +} + +static int +cpp_demangle_read_expr_primary(struct cpp_demangle_data *ddata) +{ + const char *num; + + if (ddata == NULL || *(++ddata->cur) == '\0') + return (0); + + if (*ddata->cur == '_' && *(ddata->cur + 1) == 'Z') { + ddata->cur += 2; + if (*ddata->cur == '\0') + return (0); + if (!cpp_demangle_read_encoding(ddata)) + return (0); + ++ddata->cur; + return (1); + } + + switch (*ddata->cur) { + case 'b': + if (*(ddata->cur + 2) != 'E') + return (0); + switch (*(++ddata->cur)) { + case '0': + ddata->cur += 2; + return (cpp_demangle_push_str(ddata, "false", 5)); + case '1': + ddata->cur += 2; + return (cpp_demangle_push_str(ddata, "true", 4)); + default: + return (0); + } + + case 'd': + ++ddata->cur; + return (cpp_demangle_push_fp(ddata, decode_fp_to_double)); + + case 'e': + ++ddata->cur; + if (sizeof(long double) == 10) + return (cpp_demangle_push_fp(ddata, + decode_fp_to_double)); + return (cpp_demangle_push_fp(ddata, decode_fp_to_float80)); + + case 'f': + ++ddata->cur; + return (cpp_demangle_push_fp(ddata, decode_fp_to_float)); + + case 'g': + ++ddata->cur; + if (sizeof(long double) == 16) + return (cpp_demangle_push_fp(ddata, + decode_fp_to_double)); + return (cpp_demangle_push_fp(ddata, decode_fp_to_float128)); + + case 'i': + case 'j': + case 'l': + case 'm': + case 'n': + case 's': + case 't': + case 'x': + case 'y': + if (*(++ddata->cur) == 'n') { + if (!cpp_demangle_push_str(ddata, "-", 1)) + return (0); + ++ddata->cur; + } + num = ddata->cur; + while (*ddata->cur != 'E') { + if (!ELFTC_ISDIGIT(*ddata->cur)) + return (0); + ++ddata->cur; + } + ++ddata->cur; + return (cpp_demangle_push_str(ddata, num, + ddata->cur - num - 1)); + + default: + return (0); + } +} + +static int +cpp_demangle_read_expression(struct cpp_demangle_data *ddata) +{ + + if (ddata == NULL || *ddata->cur == '\0') + return (0); + + switch (SIMPLE_HASH(*ddata->cur, *(ddata->cur + 1))) { + case SIMPLE_HASH('s', 't'): + ddata->cur += 2; + return (cpp_demangle_read_type(ddata, 0)); + + case SIMPLE_HASH('s', 'r'): + ddata->cur += 2; + if (!cpp_demangle_read_type(ddata, 0)) + return (0); + if (!cpp_demangle_read_uqname(ddata)) + return (0); + if (*ddata->cur == 'I') + return (cpp_demangle_read_tmpl_args(ddata)); + return (1); + + case SIMPLE_HASH('a', 'a'): + /* operator && */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "&&", 2)); + + case SIMPLE_HASH('a', 'd'): + /* operator & (unary) */ + ddata->cur += 2; + return (cpp_demangle_read_expression_unary(ddata, "&", 1)); + + case SIMPLE_HASH('a', 'n'): + /* operator & */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "&", 1)); + + case SIMPLE_HASH('a', 'N'): + /* operator &= */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "&=", 2)); + + case SIMPLE_HASH('a', 'S'): + /* operator = */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "=", 1)); + + case SIMPLE_HASH('c', 'l'): + /* operator () */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "()", 2)); + + case SIMPLE_HASH('c', 'm'): + /* operator , */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, ",", 1)); + + case SIMPLE_HASH('c', 'o'): + /* operator ~ */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "~", 1)); + + case SIMPLE_HASH('c', 'v'): + /* operator (cast) */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "(cast)", 6)); + + case SIMPLE_HASH('d', 'a'): + /* operator delete [] */ + ddata->cur += 2; + return (cpp_demangle_read_expression_unary(ddata, "delete []", 9)); + + case SIMPLE_HASH('d', 'e'): + /* operator * (unary) */ + ddata->cur += 2; + return (cpp_demangle_read_expression_unary(ddata, "*", 1)); + + case SIMPLE_HASH('d', 'l'): + /* operator delete */ + ddata->cur += 2; + return (cpp_demangle_read_expression_unary(ddata, "delete", 6)); + + case SIMPLE_HASH('d', 'v'): + /* operator / */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "/", 1)); + + case SIMPLE_HASH('d', 'V'): + /* operator /= */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "/=", 2)); + + case SIMPLE_HASH('e', 'o'): + /* operator ^ */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "^", 1)); + + case SIMPLE_HASH('e', 'O'): + /* operator ^= */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "^=", 2)); + + case SIMPLE_HASH('e', 'q'): + /* operator == */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "==", 2)); + + case SIMPLE_HASH('g', 'e'): + /* operator >= */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, ">=", 2)); + + case SIMPLE_HASH('g', 't'): + /* operator > */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, ">", 1)); + + case SIMPLE_HASH('i', 'x'): + /* operator [] */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "[]", 2)); + + case SIMPLE_HASH('l', 'e'): + /* operator <= */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "<=", 2)); + + case SIMPLE_HASH('l', 's'): + /* operator << */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "<<", 2)); + + case SIMPLE_HASH('l', 'S'): + /* operator <<= */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "<<=", 3)); + + case SIMPLE_HASH('l', 't'): + /* operator < */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "<", 1)); + + case SIMPLE_HASH('m', 'i'): + /* operator - */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "-", 1)); + + case SIMPLE_HASH('m', 'I'): + /* operator -= */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "-=", 2)); + + case SIMPLE_HASH('m', 'l'): + /* operator * */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "*", 1)); + + case SIMPLE_HASH('m', 'L'): + /* operator *= */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "*=", 2)); + + case SIMPLE_HASH('m', 'm'): + /* operator -- */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "--", 2)); + + case SIMPLE_HASH('n', 'a'): + /* operator new[] */ + ddata->cur += 2; + return (cpp_demangle_read_expression_unary(ddata, "new []", 6)); + + case SIMPLE_HASH('n', 'e'): + /* operator != */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "!=", 2)); + + case SIMPLE_HASH('n', 'g'): + /* operator - (unary) */ + ddata->cur += 2; + return (cpp_demangle_read_expression_unary(ddata, "-", 1)); + + case SIMPLE_HASH('n', 't'): + /* operator ! */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "!", 1)); + + case SIMPLE_HASH('n', 'w'): + /* operator new */ + ddata->cur += 2; + return (cpp_demangle_read_expression_unary(ddata, "new", 3)); + + case SIMPLE_HASH('o', 'o'): + /* operator || */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "||", 2)); + + case SIMPLE_HASH('o', 'r'): + /* operator | */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "|", 1)); + + case SIMPLE_HASH('o', 'R'): + /* operator |= */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "|=", 2)); + + case SIMPLE_HASH('p', 'l'): + /* operator + */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "+", 1)); + + case SIMPLE_HASH('p', 'L'): + /* operator += */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "+=", 2)); + + case SIMPLE_HASH('p', 'm'): + /* operator ->* */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "->*", 3)); + + case SIMPLE_HASH('p', 'p'): + /* operator ++ */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "++", 2)); + + case SIMPLE_HASH('p', 's'): + /* operator + (unary) */ + ddata->cur += 2; + return (cpp_demangle_read_expression_unary(ddata, "+", 1)); + + case SIMPLE_HASH('p', 't'): + /* operator -> */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "->", 2)); + + case SIMPLE_HASH('q', 'u'): + /* operator ? */ + ddata->cur += 2; + return (cpp_demangle_read_expression_trinary(ddata, "?", 1, + ":", 1)); + + case SIMPLE_HASH('r', 'm'): + /* operator % */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "%", 1)); + + case SIMPLE_HASH('r', 'M'): + /* operator %= */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, "%=", 2)); + + case SIMPLE_HASH('r', 's'): + /* operator >> */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, ">>", 2)); + + case SIMPLE_HASH('r', 'S'): + /* operator >>= */ + ddata->cur += 2; + return (cpp_demangle_read_expression_binary(ddata, ">>=", 3)); + + case SIMPLE_HASH('r', 'z'): + /* operator sizeof */ + ddata->cur += 2; + return (cpp_demangle_read_expression_unary(ddata, "sizeof", 6)); + + case SIMPLE_HASH('s', 'v'): + /* operator sizeof */ + ddata->cur += 2; + return (cpp_demangle_read_expression_unary(ddata, "sizeof", 6)); + } + + switch (*ddata->cur) { + case 'L': + return (cpp_demangle_read_expr_primary(ddata)); + case 'T': + return (cpp_demangle_read_tmpl_param(ddata)); + } + + return (0); +} + +static int +cpp_demangle_read_expression_flat(struct cpp_demangle_data *ddata, char **str) +{ + struct vector_str *output; + size_t i, p_idx, idx, exp_len; + char *exp; + + output = ddata->push_head > 0 ? &ddata->output_tmp : + &ddata->output; + + p_idx = output->size; + + if (!cpp_demangle_read_expression(ddata)) + return (0); + + if ((exp = vector_str_substr(output, p_idx, output->size - 1, + &exp_len)) == NULL) + return (0); + + idx = output->size; + for (i = p_idx; i < idx; ++i) { + if (!vector_str_pop(output)) { + free(exp); + return (0); + } + } + + *str = exp; + + return (1); +} + +static int +cpp_demangle_read_expression_binary(struct cpp_demangle_data *ddata, + const char *name, size_t len) +{ + + if (ddata == NULL || name == NULL || len == 0) + return (0); + if (!cpp_demangle_read_expression(ddata)) + return (0); + if (!cpp_demangle_push_str(ddata, name, len)) + return (0); + + return (cpp_demangle_read_expression(ddata)); +} + +static int +cpp_demangle_read_expression_unary(struct cpp_demangle_data *ddata, + const char *name, size_t len) +{ + + if (ddata == NULL || name == NULL || len == 0) + return (0); + if (!cpp_demangle_read_expression(ddata)) + return (0); + + return (cpp_demangle_push_str(ddata, name, len)); +} + +static int +cpp_demangle_read_expression_trinary(struct cpp_demangle_data *ddata, + const char *name1, size_t len1, const char *name2, size_t len2) +{ + + if (ddata == NULL || name1 == NULL || len1 == 0 || name2 == NULL || + len2 == 0) + return (0); + + if (!cpp_demangle_read_expression(ddata)) + return (0); + if (!cpp_demangle_push_str(ddata, name1, len1)) + return (0); + if (!cpp_demangle_read_expression(ddata)) + return (0); + if (!cpp_demangle_push_str(ddata, name2, len2)) + return (0); + + return (cpp_demangle_read_expression(ddata)); +} + +static int +cpp_demangle_read_function(struct cpp_demangle_data *ddata, int *ext_c, + struct vector_type_qualifier *v) +{ + size_t class_type_size, class_type_len, limit; + const char *class_type; + + if (ddata == NULL || *ddata->cur != 'F' || v == NULL) + return (0); + + ++ddata->cur; + if (*ddata->cur == 'Y') { + if (ext_c != NULL) + *ext_c = 1; + ++ddata->cur; + } + if (!cpp_demangle_read_type(ddata, 0)) + return (0); + if (*ddata->cur != 'E') { + if (!cpp_demangle_push_str(ddata, "(", 1)) + return (0); + if (vector_read_cmd_find(&ddata->cmd, READ_PTRMEM)) { + if ((class_type_size = ddata->class_type.size) == 0) + return (0); + class_type = + ddata->class_type.container[class_type_size - 1]; + if (class_type == NULL) + return (0); + if ((class_type_len = strlen(class_type)) == 0) + return (0); + if (!cpp_demangle_push_str(ddata, class_type, + class_type_len)) + return (0); + if (!cpp_demangle_push_str(ddata, "::*", 3)) + return (0); + ++ddata->func_type; + } else { + if (!cpp_demangle_push_type_qualifier(ddata, v, + (const char *) NULL)) + return (0); + vector_type_qualifier_dest(v); + if (!vector_type_qualifier_init(v)) + return (0); + } + + if (!cpp_demangle_push_str(ddata, ")(", 2)) + return (0); + + limit = 0; + for (;;) { + if (!cpp_demangle_read_type(ddata, 0)) + return (0); + if (*ddata->cur == 'E') + break; + if (limit++ > CPP_DEMANGLE_TRY_LIMIT) + return (0); + } + + if (vector_read_cmd_find(&ddata->cmd, READ_PTRMEM) == 1) { + if (!cpp_demangle_push_type_qualifier(ddata, v, + (const char *) NULL)) + return (0); + vector_type_qualifier_dest(v); + if (!vector_type_qualifier_init(v)) + return (0); + } + + if (!cpp_demangle_push_str(ddata, ")", 1)) + return (0); + } + + ++ddata->cur; + + return (1); +} + +/* read encoding, encoding are function name, data name, special-name */ +static int +cpp_demangle_read_encoding(struct cpp_demangle_data *ddata) +{ + char *name, *type, *num_str; + long offset; + int rtn; + + if (ddata == NULL || *ddata->cur == '\0') + return (0); + + /* special name */ + switch (SIMPLE_HASH(*ddata->cur, *(ddata->cur + 1))) { + case SIMPLE_HASH('G', 'A'): + if (!cpp_demangle_push_str(ddata, "hidden alias for ", 17)) + return (0); + ddata->cur += 2; + if (*ddata->cur == '\0') + return (0); + return (cpp_demangle_read_encoding(ddata)); + + case SIMPLE_HASH('G', 'R'): + if (!cpp_demangle_push_str(ddata, "reference temporary #", 21)) + return (0); + ddata->cur += 2; + if (*ddata->cur == '\0') + return (0); + if (!cpp_demangle_read_name_flat(ddata, &name)) + return (0); + rtn = 0; + if (!cpp_demangle_read_number_as_string(ddata, &num_str)) + goto clean1; + if (!cpp_demangle_push_str(ddata, num_str, strlen(num_str))) + goto clean2; + if (!cpp_demangle_push_str(ddata, " for ", 5)) + goto clean2; + if (!cpp_demangle_push_str(ddata, name, strlen(name))) + goto clean2; + rtn = 1; + clean2: + free(num_str); + clean1: + free(name); + return (rtn); + + case SIMPLE_HASH('G', 'T'): + ddata->cur += 2; + if (*ddata->cur == '\0') + return (0); + switch (*ddata->cur) { + case 'n': + if (!cpp_demangle_push_str(ddata, + "non-transaction clone for ", 26)) + return (0); + break; + case 't': + default: + if (!cpp_demangle_push_str(ddata, + "transaction clone for ", 22)) + return (0); + break; + } + ++ddata->cur; + return (cpp_demangle_read_encoding(ddata)); + + case SIMPLE_HASH('G', 'V'): + /* sentry object for 1 time init */ + if (!cpp_demangle_push_str(ddata, "guard variable for ", 20)) + return (0); + ddata->cur += 2; + break; + + case SIMPLE_HASH('T', 'c'): + /* virtual function covariant override thunk */ + if (!cpp_demangle_push_str(ddata, + "virtual function covariant override ", 36)) + return (0); + ddata->cur += 2; + if (*ddata->cur == '\0') + return (0); + if (!cpp_demangle_read_offset(ddata)) + return (0); + if (!cpp_demangle_read_offset(ddata)) + return (0); + return (cpp_demangle_read_encoding(ddata)); + + case SIMPLE_HASH('T', 'C'): + /* construction vtable */ + if (!cpp_demangle_push_str(ddata, "construction vtable for ", + 24)) + return (0); + ddata->cur += 2; + if (*ddata->cur == '\0') + return (0); + if (!cpp_demangle_read_type_flat(ddata, &type)) + return (0); + rtn = 0; + if (!cpp_demangle_read_number(ddata, &offset)) + goto clean3; + if (*ddata->cur++ != '_') + goto clean3; + if (!cpp_demangle_read_type(ddata, 0)) + goto clean3; + if (!cpp_demangle_push_str(ddata, "-in-", 4)) + goto clean3; + if (!cpp_demangle_push_str(ddata, type, strlen(type))) + goto clean3; + rtn = 1; + clean3: + free(type); + return (rtn); + + case SIMPLE_HASH('T', 'D'): + /* typeinfo common proxy */ + break; + + case SIMPLE_HASH('T', 'F'): + /* typeinfo fn */ + if (!cpp_demangle_push_str(ddata, "typeinfo fn for ", 16)) + return (0); + ddata->cur += 2; + if (*ddata->cur == '\0') + return (0); + return (cpp_demangle_read_type(ddata, 0)); + + case SIMPLE_HASH('T', 'h'): + /* virtual function non-virtual override thunk */ + if (!cpp_demangle_push_str(ddata, + "virtual function non-virtual override ", 38)) + return (0); + ddata->cur += 2; + if (*ddata->cur == '\0') + return (0); + if (!cpp_demangle_read_nv_offset(ddata)) + return (0); + return (cpp_demangle_read_encoding(ddata)); + + case SIMPLE_HASH('T', 'H'): + /* TLS init function */ + if (!cpp_demangle_push_str(ddata, "TLS init function for ", + 22)) + return (0); + ddata->cur += 2; + if (*ddata->cur == '\0') + return (0); + break; + + case SIMPLE_HASH('T', 'I'): + /* typeinfo structure */ + if (!cpp_demangle_push_str(ddata, "typeinfo for ", 13)) + return (0); + ddata->cur += 2; + if (*ddata->cur == '\0') + return (0); + return (cpp_demangle_read_type(ddata, 0)); + + case SIMPLE_HASH('T', 'J'): + /* java class */ + if (!cpp_demangle_push_str(ddata, "java Class for ", 15)) + return (0); + ddata->cur += 2; + if (*ddata->cur == '\0') + return (0); + return (cpp_demangle_read_type(ddata, 0)); + + case SIMPLE_HASH('T', 'S'): + /* RTTI name (NTBS) */ + if (!cpp_demangle_push_str(ddata, "typeinfo name for ", 18)) + return (0); + ddata->cur += 2; + if (*ddata->cur == '\0') + return (0); + return (cpp_demangle_read_type(ddata, 0)); + + case SIMPLE_HASH('T', 'T'): + /* VTT table */ + if (!cpp_demangle_push_str(ddata, "VTT for ", 8)) + return (0); + ddata->cur += 2; + if (*ddata->cur == '\0') + return (0); + return (cpp_demangle_read_type(ddata, 0)); + + case SIMPLE_HASH('T', 'v'): + /* virtual function virtual override thunk */ + if (!cpp_demangle_push_str(ddata, + "virtual function virtual override ", 34)) + return (0); + ddata->cur += 2; + if (*ddata->cur == '\0') + return (0); + if (!cpp_demangle_read_v_offset(ddata)) + return (0); + return (cpp_demangle_read_encoding(ddata)); + + case SIMPLE_HASH('T', 'V'): + /* virtual table */ + if (!cpp_demangle_push_str(ddata, "vtable for ", 12)) + return (0); + ddata->cur += 2; + if (*ddata->cur == '\0') + return (0); + return (cpp_demangle_read_type(ddata, 0)); + + case SIMPLE_HASH('T', 'W'): + /* TLS wrapper function */ + if (!cpp_demangle_push_str(ddata, "TLS wrapper function for ", + 25)) + return (0); + ddata->cur += 2; + if (*ddata->cur == '\0') + return (0); + break; + } + + return (cpp_demangle_read_name(ddata)); +} + +static int +cpp_demangle_read_local_name(struct cpp_demangle_data *ddata) +{ + size_t limit; + + if (ddata == NULL) + return (0); + if (*(++ddata->cur) == '\0') + return (0); + if (!cpp_demangle_read_encoding(ddata)) + return (0); + + limit = 0; + for (;;) { + if (!cpp_demangle_read_type(ddata, 1)) + return (0); + if (*ddata->cur == 'E') + break; + if (limit++ > CPP_DEMANGLE_TRY_LIMIT) + return (0); + } + if (*(++ddata->cur) == '\0') + return (0); + if (ddata->paren == true) { + if (!cpp_demangle_push_str(ddata, ")", 1)) + return (0); + ddata->paren = false; + } + if (*ddata->cur == 's') + ++ddata->cur; + else { + if (!cpp_demangle_push_str(ddata, "::", 2)) + return (0); + if (!cpp_demangle_read_name(ddata)) + return (0); + } + if (*ddata->cur == '_') { + ++ddata->cur; + while (ELFTC_ISDIGIT(*ddata->cur) != 0) + ++ddata->cur; + } + + return (1); +} + +static int +cpp_demangle_read_name(struct cpp_demangle_data *ddata) +{ + struct vector_str *output, v; + size_t p_idx, subst_str_len; + int rtn; + char *subst_str; + + if (ddata == NULL || *ddata->cur == '\0') + return (0); + + output = ddata->push_head > 0 ? &ddata->output_tmp : &ddata->output; + + subst_str = NULL; + + switch (*ddata->cur) { + case 'S': + return (cpp_demangle_read_subst(ddata)); + case 'N': + return (cpp_demangle_read_nested_name(ddata)); + case 'Z': + return (cpp_demangle_read_local_name(ddata)); + } + + if (!vector_str_init(&v)) + return (0); + + p_idx = output->size; + rtn = 0; + if (!cpp_demangle_read_uqname(ddata)) + goto clean; + if ((subst_str = vector_str_substr(output, p_idx, output->size - 1, + &subst_str_len)) == NULL) + goto clean; + if (subst_str_len > 8 && strstr(subst_str, "operator") != NULL) { + rtn = 1; + goto clean; + } + if (!vector_str_push(&v, subst_str, subst_str_len)) + goto clean; + if (!cpp_demangle_push_subst_v(ddata, &v)) + goto clean; + + if (*ddata->cur == 'I') { + p_idx = output->size; + if (!cpp_demangle_read_tmpl_args(ddata)) + goto clean; + free(subst_str); + if ((subst_str = vector_str_substr(output, p_idx, + output->size - 1, &subst_str_len)) == NULL) + goto clean; + if (!vector_str_push(&v, subst_str, subst_str_len)) + goto clean; + if (!cpp_demangle_push_subst_v(ddata, &v)) + goto clean; + } + + rtn = 1; + +clean: + free(subst_str); + vector_str_dest(&v); + + return (rtn); +} + +static int +cpp_demangle_read_name_flat(struct cpp_demangle_data *ddata, char **str) +{ + struct vector_str *output; + size_t i, p_idx, idx, name_len; + char *name; + + output = ddata->push_head > 0 ? &ddata->output_tmp : + &ddata->output; + + p_idx = output->size; + + if (!cpp_demangle_read_name(ddata)) + return (0); + + if ((name = vector_str_substr(output, p_idx, output->size - 1, + &name_len)) == NULL) + return (0); + + idx = output->size; + for (i = p_idx; i < idx; ++i) { + if (!vector_str_pop(output)) { + free(name); + return (0); + } + } + + *str = name; + + return (1); +} + +static int +cpp_demangle_read_nested_name(struct cpp_demangle_data *ddata) +{ + struct vector_str *output, v; + size_t limit, p_idx, subst_str_len; + int rtn; + char *subst_str; + + if (ddata == NULL || *ddata->cur != 'N') + return (0); + if (*(++ddata->cur) == '\0') + return (0); + + while (*ddata->cur == 'r' || *ddata->cur == 'V' || + *ddata->cur == 'K') { + switch (*ddata->cur) { + case 'r': + ddata->mem_rst = true; + break; + case 'V': + ddata->mem_vat = true; + break; + case 'K': + ddata->mem_cst = true; + break; + } + ++ddata->cur; + } + + output = ddata->push_head > 0 ? &ddata->output_tmp : &ddata->output; + if (!vector_str_init(&v)) + return (0); + + rtn = 0; + limit = 0; + for (;;) { + p_idx = output->size; + switch (*ddata->cur) { + case 'I': + if (!cpp_demangle_read_tmpl_args(ddata)) + goto clean; + break; + case 'S': + if (!cpp_demangle_read_subst(ddata)) + goto clean; + break; + case 'T': + if (!cpp_demangle_read_tmpl_param(ddata)) + goto clean; + break; + default: + if (!cpp_demangle_read_uqname(ddata)) + goto clean; + } + + if ((subst_str = vector_str_substr(output, p_idx, + output->size - 1, &subst_str_len)) == NULL) + goto clean; + if (!vector_str_push(&v, subst_str, subst_str_len)) { + free(subst_str); + goto clean; + } + free(subst_str); + + if (!cpp_demangle_push_subst_v(ddata, &v)) + goto clean; + if (*ddata->cur == 'E') + break; + else if (*ddata->cur != 'I' && + *ddata->cur != 'C' && *ddata->cur != 'D') { + if (!cpp_demangle_push_str(ddata, "::", 2)) + goto clean; + if (!vector_str_push(&v, "::", 2)) + goto clean; + } + if (limit++ > CPP_DEMANGLE_TRY_LIMIT) + goto clean; + } + + ++ddata->cur; + rtn = 1; + +clean: + vector_str_dest(&v); + + return (rtn); +} + +/* + * read number + * number ::= [n] <decimal> + */ +static int +cpp_demangle_read_number(struct cpp_demangle_data *ddata, long *rtn) +{ + long len, negative_factor; + + if (ddata == NULL || rtn == NULL) + return (0); + + negative_factor = 1; + if (*ddata->cur == 'n') { + negative_factor = -1; + + ++ddata->cur; + } + if (ELFTC_ISDIGIT(*ddata->cur) == 0) + return (0); + + errno = 0; + if ((len = strtol(ddata->cur, (char **) NULL, 10)) == 0 && + errno != 0) + return (0); + + while (ELFTC_ISDIGIT(*ddata->cur) != 0) + ++ddata->cur; + + assert(len >= 0); + assert(negative_factor == 1 || negative_factor == -1); + + *rtn = len * negative_factor; + + return (1); +} + +static int +cpp_demangle_read_number_as_string(struct cpp_demangle_data *ddata, char **str) +{ + long n; + + if (!cpp_demangle_read_number(ddata, &n)) { + *str = NULL; + return (0); + } + + if (asprintf(str, "%ld", n) < 0) { + *str = NULL; + return (0); + } + + return (1); +} + +static int +cpp_demangle_read_nv_offset(struct cpp_demangle_data *ddata) +{ + + if (ddata == NULL) + return (0); + + if (!cpp_demangle_push_str(ddata, "offset : ", 9)) + return (0); + + return (cpp_demangle_read_offset_number(ddata)); +} + +/* read offset, offset are nv-offset, v-offset */ +static int +cpp_demangle_read_offset(struct cpp_demangle_data *ddata) +{ + + if (ddata == NULL) + return (0); + + if (*ddata->cur == 'h') { + ++ddata->cur; + return (cpp_demangle_read_nv_offset(ddata)); + } else if (*ddata->cur == 'v') { + ++ddata->cur; + return (cpp_demangle_read_v_offset(ddata)); + } + + return (0); +} + +static int +cpp_demangle_read_offset_number(struct cpp_demangle_data *ddata) +{ + bool negative; + const char *start; + + if (ddata == NULL || *ddata->cur == '\0') + return (0); + + /* offset could be negative */ + if (*ddata->cur == 'n') { + negative = true; + start = ddata->cur + 1; + } else { + negative = false; + start = ddata->cur; + } + + while (*ddata->cur != '_') + ++ddata->cur; + + if (negative && !cpp_demangle_push_str(ddata, "-", 1)) + return (0); + + assert(start != NULL); + + if (!cpp_demangle_push_str(ddata, start, ddata->cur - start)) + return (0); + if (!cpp_demangle_push_str(ddata, " ", 1)) + return (0); + + ++ddata->cur; + + return (1); +} + +static int +cpp_demangle_read_pointer_to_member(struct cpp_demangle_data *ddata) +{ + size_t class_type_len, i, idx, p_idx; + int p_func_type, rtn; + char *class_type; + + if (ddata == NULL || *ddata->cur != 'M' || *(++ddata->cur) == '\0') + return (0); + + p_idx = ddata->output.size; + if (!cpp_demangle_read_type(ddata, 0)) + return (0); + + if ((class_type = vector_str_substr(&ddata->output, p_idx, + ddata->output.size - 1, &class_type_len)) == NULL) + return (0); + + rtn = 0; + idx = ddata->output.size; + for (i = p_idx; i < idx; ++i) + if (!vector_str_pop(&ddata->output)) + goto clean1; + + if (!vector_read_cmd_push(&ddata->cmd, READ_PTRMEM)) + goto clean1; + + if (!vector_str_push(&ddata->class_type, class_type, class_type_len)) + goto clean2; + + p_func_type = ddata->func_type; + if (!cpp_demangle_read_type(ddata, 0)) + goto clean3; + + if (p_func_type == ddata->func_type) { + if (!cpp_demangle_push_str(ddata, " ", 1)) + goto clean3; + if (!cpp_demangle_push_str(ddata, class_type, class_type_len)) + goto clean3; + if (!cpp_demangle_push_str(ddata, "::*", 3)) + goto clean3; + } + + rtn = 1; +clean3: + if (!vector_str_pop(&ddata->class_type)) + rtn = 0; +clean2: + if (!vector_read_cmd_pop(&ddata->cmd)) + rtn = 0; +clean1: + free(class_type); + + return (rtn); +} + +/* read source-name, source-name is <len> <ID> */ +static int +cpp_demangle_read_sname(struct cpp_demangle_data *ddata) +{ + long len; + int err; + + if (ddata == NULL || cpp_demangle_read_number(ddata, &len) == 0 || + len <= 0) + return (0); + + if (len == 12 && (memcmp("_GLOBAL__N_1", ddata->cur, 12) == 0)) + err = cpp_demangle_push_str(ddata, "(anonymous namespace)", 21); + else + err = cpp_demangle_push_str(ddata, ddata->cur, len); + + if (err == 0) + return (0); + + assert(ddata->output.size > 0); + if (vector_read_cmd_find(&ddata->cmd, READ_TMPL) == 0) + ddata->last_sname = + ddata->output.container[ddata->output.size - 1]; + + ddata->cur += len; + + return (1); +} + +static int +cpp_demangle_read_subst(struct cpp_demangle_data *ddata) +{ + long nth; + + if (ddata == NULL || *ddata->cur == '\0') + return (0); + + /* abbreviations of the form Sx */ + switch (SIMPLE_HASH(*ddata->cur, *(ddata->cur + 1))) { + case SIMPLE_HASH('S', 'a'): + /* std::allocator */ + if (cpp_demangle_push_str(ddata, "std::allocator", 14) == 0) + return (0); + ddata->cur += 2; + if (*ddata->cur == 'I') + return (cpp_demangle_read_subst_stdtmpl(ddata, + "std::allocator", 14)); + return (1); + + case SIMPLE_HASH('S', 'b'): + /* std::basic_string */ + if (!cpp_demangle_push_str(ddata, "std::basic_string", 17)) + return (0); + ddata->cur += 2; + if (*ddata->cur == 'I') + return (cpp_demangle_read_subst_stdtmpl(ddata, + "std::basic_string", 17)); + return (1); + + case SIMPLE_HASH('S', 'd'): + /* std::basic_iostream<char, std::char_traits<char> > */ + if (!cpp_demangle_push_str(ddata, "std::basic_iostream", 19)) + return (0); + ddata->last_sname = "basic_iostream"; + ddata->cur += 2; + if (*ddata->cur == 'I') + return (cpp_demangle_read_subst_stdtmpl(ddata, + "std::basic_iostream", 19)); + return (1); + + case SIMPLE_HASH('S', 'i'): + /* std::basic_istream<char, std::char_traits<char> > */ + if (!cpp_demangle_push_str(ddata, "std::basic_istream", 18)) + return (0); + ddata->last_sname = "basic_istream"; + ddata->cur += 2; + if (*ddata->cur == 'I') + return (cpp_demangle_read_subst_stdtmpl(ddata, + "std::basic_istream", 18)); + return (1); + + case SIMPLE_HASH('S', 'o'): + /* std::basic_ostream<char, std::char_traits<char> > */ + if (!cpp_demangle_push_str(ddata, "std::basic_ostream", 18)) + return (0); + ddata->last_sname = "basic_ostream"; + ddata->cur += 2; + if (*ddata->cur == 'I') + return (cpp_demangle_read_subst_stdtmpl(ddata, + "std::basic_ostream", 18)); + return (1); + + case SIMPLE_HASH('S', 's'): + /* + * std::basic_string<char, std::char_traits<char>, + * std::allocator<char> > + * + * a.k.a std::string + */ + if (!cpp_demangle_push_str(ddata, "std::string", 11)) + return (0); + ddata->last_sname = "string"; + ddata->cur += 2; + if (*ddata->cur == 'I') + return (cpp_demangle_read_subst_stdtmpl(ddata, + "std::string", 11)); + return (1); + + case SIMPLE_HASH('S', 't'): + /* std:: */ + return (cpp_demangle_read_subst_std(ddata)); + } + + if (*(++ddata->cur) == '\0') + return (0); + + /* substitution */ + if (*ddata->cur == '_') + return (cpp_demangle_get_subst(ddata, 0)); + else { + errno = 0; + /* substitution number is base 36 */ + if ((nth = strtol(ddata->cur, (char **) NULL, 36)) == 0 && + errno != 0) + return (0); + + /* first was '_', so increase one */ + ++nth; + + while (*ddata->cur != '_') + ++ddata->cur; + + assert(nth > 0); + + return (cpp_demangle_get_subst(ddata, nth)); + } + + /* NOTREACHED */ + return (0); +} + +static int +cpp_demangle_read_subst_std(struct cpp_demangle_data *ddata) +{ + struct vector_str *output, v; + size_t p_idx, subst_str_len; + int rtn; + char *subst_str; + + if (ddata == NULL) + return (0); + + if (!vector_str_init(&v)) + return (0); + + subst_str = NULL; + rtn = 0; + if (!cpp_demangle_push_str(ddata, "std::", 5)) + goto clean; + + if (!vector_str_push(&v, "std::", 5)) + goto clean; + + ddata->cur += 2; + + output = ddata->push_head > 0 ? &ddata->output_tmp : &ddata->output; + + p_idx = output->size; + if (!cpp_demangle_read_uqname(ddata)) + goto clean; + + if ((subst_str = vector_str_substr(output, p_idx, output->size - 1, + &subst_str_len)) == NULL) + goto clean; + + if (!vector_str_push(&v, subst_str, subst_str_len)) + goto clean; + + if (!cpp_demangle_push_subst_v(ddata, &v)) + goto clean; + + if (*ddata->cur == 'I') { + p_idx = output->size; + if (!cpp_demangle_read_tmpl_args(ddata)) + goto clean; + free(subst_str); + if ((subst_str = vector_str_substr(output, p_idx, + output->size - 1, &subst_str_len)) == NULL) + goto clean; + if (!vector_str_push(&v, subst_str, subst_str_len)) + goto clean; + if (!cpp_demangle_push_subst_v(ddata, &v)) + goto clean; + } + + rtn = 1; +clean: + free(subst_str); + vector_str_dest(&v); + + return (rtn); +} + +static int +cpp_demangle_read_subst_stdtmpl(struct cpp_demangle_data *ddata, + const char *str, size_t len) +{ + struct vector_str *output; + size_t p_idx, substr_len; + int rtn; + char *subst_str, *substr; + + if (ddata == NULL || str == NULL || len == 0) + return (0); + + output = ddata->push_head > 0 ? &ddata->output_tmp : &ddata->output; + + p_idx = output->size; + substr = NULL; + subst_str = NULL; + + if (!cpp_demangle_read_tmpl_args(ddata)) + return (0); + if ((substr = vector_str_substr(output, p_idx, output->size - 1, + &substr_len)) == NULL) + return (0); + + rtn = 0; + if ((subst_str = malloc(sizeof(char) * (substr_len + len + 1))) == + NULL) + goto clean; + + memcpy(subst_str, str, len); + memcpy(subst_str + len, substr, substr_len); + subst_str[substr_len + len] = '\0'; + + if (!cpp_demangle_push_subst(ddata, subst_str, substr_len + len)) + goto clean; + + rtn = 1; +clean: + free(subst_str); + free(substr); + + return (rtn); +} + +static int +cpp_demangle_read_tmpl_arg(struct cpp_demangle_data *ddata) +{ + + if (ddata == NULL || *ddata->cur == '\0') + return (0); + + switch (*ddata->cur) { + case 'L': + return (cpp_demangle_read_expr_primary(ddata)); + case 'X': + return (cpp_demangle_read_expression(ddata)); + } + + return (cpp_demangle_read_type(ddata, 0)); +} + +static int +cpp_demangle_read_tmpl_args(struct cpp_demangle_data *ddata) +{ + struct vector_str *v; + size_t arg_len, idx, limit, size; + char *arg; + + if (ddata == NULL || *ddata->cur == '\0') + return (0); + + ++ddata->cur; + + if (!vector_read_cmd_push(&ddata->cmd, READ_TMPL)) + return (0); + + if (!cpp_demangle_push_str(ddata, "<", 1)) + return (0); + + limit = 0; + v = ddata->push_head > 0 ? &ddata->output_tmp : &ddata->output; + for (;;) { + idx = v->size; + if (!cpp_demangle_read_tmpl_arg(ddata)) + return (0); + if ((arg = vector_str_substr(v, idx, v->size - 1, &arg_len)) == + NULL) + return (0); + if (!vector_str_find(&ddata->tmpl, arg, arg_len) && + !vector_str_push(&ddata->tmpl, arg, arg_len)) { + free(arg); + return (0); + } + + free(arg); + + if (*ddata->cur == 'E') { + ++ddata->cur; + size = v->size; + assert(size > 0); + if (!strncmp(v->container[size - 1], ">", 1)) { + if (!cpp_demangle_push_str(ddata, " >", 2)) + return (0); + } else if (!cpp_demangle_push_str(ddata, ">", 1)) + return (0); + break; + } else if (*ddata->cur != 'I' && + !cpp_demangle_push_str(ddata, ", ", 2)) + return (0); + + if (limit++ > CPP_DEMANGLE_TRY_LIMIT) + return (0); + } + + return (vector_read_cmd_pop(&ddata->cmd)); +} + +/* + * Read template parameter that forms in 'T[number]_'. + * This function much like to read_subst but only for types. + */ +static int +cpp_demangle_read_tmpl_param(struct cpp_demangle_data *ddata) +{ + long nth; + + if (ddata == NULL || *ddata->cur != 'T') + return (0); + + ++ddata->cur; + + if (*ddata->cur == '_') + return (cpp_demangle_get_tmpl_param(ddata, 0)); + else { + + errno = 0; + if ((nth = strtol(ddata->cur, (char **) NULL, 36)) == 0 && + errno != 0) + return (0); + + /* T_ is first */ + ++nth; + + while (*ddata->cur != '_') + ++ddata->cur; + + assert(nth > 0); + + return (cpp_demangle_get_tmpl_param(ddata, nth)); + } + + /* NOTREACHED */ + return (0); +} + +static int +cpp_demangle_read_type(struct cpp_demangle_data *ddata, int delimit) +{ + struct vector_type_qualifier v; + struct vector_str *output; + size_t p_idx, type_str_len; + int extern_c, is_builtin; + long len; + char *type_str, *exp_str, *num_str; + + if (ddata == NULL) + return (0); + + output = &ddata->output; + if (ddata->output.size > 0 && !strncmp(ddata->output.container[ddata->output.size - 1], ">", 1)) { + ddata->push_head++; + output = &ddata->output_tmp; + } else if (delimit == 1) { + if (ddata->paren == false) { + if (!cpp_demangle_push_str(ddata, "(", 1)) + return (0); + if (ddata->output.size < 2) + return (0); + ddata->paren = true; + ddata->pfirst = true; + /* Need pop function name */ + if (ddata->subst.size == 1 && + !vector_str_pop(&ddata->subst)) + return (0); + } + + if (ddata->pfirst) + ddata->pfirst = false; + else if (*ddata->cur != 'I' && + !cpp_demangle_push_str(ddata, ", ", 2)) + return (0); + } + + assert(output != NULL); + /* + * [r, V, K] [P, R, C, G, U] builtin, function, class-enum, array + * pointer-to-member, template-param, template-template-param, subst + */ + + if (!vector_type_qualifier_init(&v)) + return (0); + + extern_c = 0; + is_builtin = 1; + p_idx = output->size; + type_str = exp_str = num_str = NULL; +again: + /* builtin type */ + switch (*ddata->cur) { + case 'a': + /* signed char */ + if (!cpp_demangle_push_str(ddata, "signed char", 11)) + goto clean; + ++ddata->cur; + goto rtn; + + case 'A': + /* array type */ + if (!cpp_demangle_read_array(ddata)) + goto clean; + is_builtin = 0; + goto rtn; + + case 'b': + /* bool */ + if (!cpp_demangle_push_str(ddata, "bool", 4)) + goto clean; + ++ddata->cur; + goto rtn; + + case 'C': + /* complex pair */ + if (!vector_type_qualifier_push(&v, TYPE_CMX)) + goto clean; + ++ddata->cur; + goto again; + + case 'c': + /* char */ + if (!cpp_demangle_push_str(ddata, "char", 4)) + goto clean; + ++ddata->cur; + goto rtn; + + case 'd': + /* double */ + if (!cpp_demangle_push_str(ddata, "double", 6)) + goto clean; + ++ddata->cur; + goto rtn; + + case 'D': + ++ddata->cur; + switch (*ddata->cur) { + case 'd': + /* IEEE 754r decimal floating point (64 bits) */ + if (!cpp_demangle_push_str(ddata, "decimal64", 9)) + goto clean; + ++ddata->cur; + break; + case 'e': + /* IEEE 754r decimal floating point (128 bits) */ + if (!cpp_demangle_push_str(ddata, "decimal128", 10)) + goto clean; + ++ddata->cur; + break; + case 'f': + /* IEEE 754r decimal floating point (32 bits) */ + if (!cpp_demangle_push_str(ddata, "decimal32", 9)) + goto clean; + ++ddata->cur; + break; + case 'h': + /* IEEE 754r half-precision floating point (16 bits) */ + if (!cpp_demangle_push_str(ddata, "half", 4)) + goto clean; + ++ddata->cur; + break; + case 'i': + /* char32_t */ + if (!cpp_demangle_push_str(ddata, "char32_t", 8)) + goto clean; + ++ddata->cur; + break; + case 'n': + /* std::nullptr_t (i.e., decltype(nullptr)) */ + if (!cpp_demangle_push_str(ddata, "decltype(nullptr)", + 17)) + goto clean; + ++ddata->cur; + break; + case 's': + /* char16_t */ + if (!cpp_demangle_push_str(ddata, "char16_t", 8)) + goto clean; + ++ddata->cur; + break; + case 'v': + /* gcc vector_size extension. */ + ++ddata->cur; + if (*ddata->cur == '_') { + ++ddata->cur; + if (!cpp_demangle_read_expression_flat(ddata, + &exp_str)) + goto clean; + if (!vector_str_push(&v.ext_name, exp_str, + strlen(exp_str))) + goto clean; + } else { + if (!cpp_demangle_read_number_as_string(ddata, + &num_str)) + goto clean; + if (!vector_str_push(&v.ext_name, num_str, + strlen(num_str))) + goto clean; + } + if (*ddata->cur != '_') + goto clean; + ++ddata->cur; + if (!vector_type_qualifier_push(&v, TYPE_VEC)) + goto clean; + goto again; + default: + goto clean; + } + goto rtn; + + case 'e': + /* long double */ + if (!cpp_demangle_push_str(ddata, "long double", 11)) + goto clean; + ++ddata->cur; + goto rtn; + + case 'f': + /* float */ + if (!cpp_demangle_push_str(ddata, "float", 5)) + goto clean; + ++ddata->cur; + goto rtn; + + case 'F': + /* function */ + if (!cpp_demangle_read_function(ddata, &extern_c, &v)) + goto clean; + is_builtin = 0; + goto rtn; + + case 'g': + /* __float128 */ + if (!cpp_demangle_push_str(ddata, "__float128", 10)) + goto clean; + ++ddata->cur; + goto rtn; + + case 'G': + /* imaginary */ + if (!vector_type_qualifier_push(&v, TYPE_IMG)) + goto clean; + ++ddata->cur; + goto again; + + case 'h': + /* unsigned char */ + if (!cpp_demangle_push_str(ddata, "unsigned char", 13)) + goto clean; + ++ddata->cur; + goto rtn; + + case 'i': + /* int */ + if (!cpp_demangle_push_str(ddata, "int", 3)) + goto clean; + ++ddata->cur; + goto rtn; + + case 'j': + /* unsigned int */ + if (!cpp_demangle_push_str(ddata, "unsigned int", 12)) + goto clean; + ++ddata->cur; + goto rtn; + + case 'K': + /* const */ + if (!vector_type_qualifier_push(&v, TYPE_CST)) + goto clean; + ++ddata->cur; + goto again; + + case 'l': + /* long */ + if (!cpp_demangle_push_str(ddata, "long", 4)) + goto clean; + ++ddata->cur; + goto rtn; + + case 'm': + /* unsigned long */ + if (!cpp_demangle_push_str(ddata, "unsigned long", 13)) + goto clean; + + ++ddata->cur; + + goto rtn; + case 'M': + /* pointer to member */ + if (!cpp_demangle_read_pointer_to_member(ddata)) + goto clean; + is_builtin = 0; + goto rtn; + + case 'n': + /* __int128 */ + if (!cpp_demangle_push_str(ddata, "__int128", 8)) + goto clean; + ++ddata->cur; + goto rtn; + + case 'o': + /* unsigned __int128 */ + if (!cpp_demangle_push_str(ddata, "unsigned __int128", 17)) + goto clean; + ++ddata->cur; + goto rtn; + + case 'P': + /* pointer */ + if (!vector_type_qualifier_push(&v, TYPE_PTR)) + goto clean; + ++ddata->cur; + goto again; + + case 'r': + /* restrict */ + if (!vector_type_qualifier_push(&v, TYPE_RST)) + goto clean; + ++ddata->cur; + goto again; + + case 'R': + /* reference */ + if (!vector_type_qualifier_push(&v, TYPE_REF)) + goto clean; + ++ddata->cur; + goto again; + + case 's': + /* short, local string */ + if (!cpp_demangle_push_str(ddata, "short", 5)) + goto clean; + ++ddata->cur; + goto rtn; + + case 'S': + /* substitution */ + if (!cpp_demangle_read_subst(ddata)) + goto clean; + is_builtin = 0; + goto rtn; + + case 't': + /* unsigned short */ + if (!cpp_demangle_push_str(ddata, "unsigned short", 14)) + goto clean; + ++ddata->cur; + goto rtn; + + case 'T': + /* template parameter */ + if (!cpp_demangle_read_tmpl_param(ddata)) + goto clean; + is_builtin = 0; + goto rtn; + + case 'u': + /* vendor extended builtin */ + ++ddata->cur; + if (!cpp_demangle_read_sname(ddata)) + goto clean; + is_builtin = 0; + goto rtn; + + case 'U': + /* vendor extended type qualifier */ + if (!cpp_demangle_read_number(ddata, &len)) + goto clean; + if (len <= 0) + goto clean; + if (!vector_str_push(&v.ext_name, ddata->cur, len)) + return (0); + ddata->cur += len; + if (!vector_type_qualifier_push(&v, TYPE_EXT)) + goto clean; + goto again; + + case 'v': + /* void */ + if (!cpp_demangle_push_str(ddata, "void", 4)) + goto clean; + ++ddata->cur; + goto rtn; + + case 'V': + /* volatile */ + if (!vector_type_qualifier_push(&v, TYPE_VAT)) + goto clean; + ++ddata->cur; + goto again; + + case 'w': + /* wchar_t */ + if (!cpp_demangle_push_str(ddata, "wchar_t", 7)) + goto clean; + ++ddata->cur; + goto rtn; + + case 'x': + /* long long */ + if (!cpp_demangle_push_str(ddata, "long long", 9)) + goto clean; + ++ddata->cur; + goto rtn; + + case 'y': + /* unsigned long long */ + if (!cpp_demangle_push_str(ddata, "unsigned long long", 18)) + goto clean; + ++ddata->cur; + goto rtn; + + case 'z': + /* ellipsis */ + if (!cpp_demangle_push_str(ddata, "...", 3)) + goto clean; + ++ddata->cur; + goto rtn; + } + + if (!cpp_demangle_read_name(ddata)) + goto clean; + + is_builtin = 0; +rtn: + if ((type_str = vector_str_substr(output, p_idx, output->size - 1, + &type_str_len)) == NULL) + goto clean; + + if (is_builtin == 0) { + if (!vector_str_find(&ddata->subst, type_str, type_str_len) && + !vector_str_push(&ddata->subst, type_str, type_str_len)) + goto clean; + } + + if (!cpp_demangle_push_type_qualifier(ddata, &v, type_str)) + goto clean; + + free(type_str); + free(exp_str); + free(num_str); + vector_type_qualifier_dest(&v); + + if (ddata->push_head > 0) { + if (*ddata->cur == 'I' && cpp_demangle_read_tmpl_args(ddata) + == 0) + return (0); + + if (--ddata->push_head > 0) + return (1); + + if (!vector_str_push(&ddata->output_tmp, " ", 1)) + return (0); + + if (!vector_str_push_vector_head(&ddata->output, + &ddata->output_tmp)) + return (0); + + vector_str_dest(&ddata->output_tmp); + if (!vector_str_init(&ddata->output_tmp)) + return (0); + + if (!cpp_demangle_push_str(ddata, "(", 1)) + return (0); + + ddata->paren = true; + ddata->pfirst = true; + } + + return (1); +clean: + free(type_str); + free(exp_str); + free(num_str); + vector_type_qualifier_dest(&v); + + return (0); +} + +static int +cpp_demangle_read_type_flat(struct cpp_demangle_data *ddata, char **str) +{ + struct vector_str *output; + size_t i, p_idx, idx, type_len; + char *type; + + output = ddata->push_head > 0 ? &ddata->output_tmp : + &ddata->output; + + p_idx = output->size; + + if (!cpp_demangle_read_type(ddata, 0)) + return (0); + + if ((type = vector_str_substr(output, p_idx, output->size - 1, + &type_len)) == NULL) + return (0); + + idx = output->size; + for (i = p_idx; i < idx; ++i) { + if (!vector_str_pop(output)) { + free(type); + return (0); + } + } + + *str = type; + + return (1); +} + +/* + * read unqualified-name, unqualified name are operator-name, ctor-dtor-name, + * source-name + */ +static int +cpp_demangle_read_uqname(struct cpp_demangle_data *ddata) +{ + size_t len; + + if (ddata == NULL || *ddata->cur == '\0') + return (0); + + /* operator name */ + switch (SIMPLE_HASH(*ddata->cur, *(ddata->cur + 1))) { + case SIMPLE_HASH('a', 'a'): + /* operator && */ + if (!cpp_demangle_push_str(ddata, "operator&&", 10)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('a', 'd'): + /* operator & (unary) */ + if (!cpp_demangle_push_str(ddata, "operator&", 9)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('a', 'n'): + /* operator & */ + if (!cpp_demangle_push_str(ddata, "operator&", 9)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('a', 'N'): + /* operator &= */ + if (!cpp_demangle_push_str(ddata, "operator&=", 10)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('a', 'S'): + /* operator = */ + if (!cpp_demangle_push_str(ddata, "operator=", 9)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('c', 'l'): + /* operator () */ + if (!cpp_demangle_push_str(ddata, "operator()", 10)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('c', 'm'): + /* operator , */ + if (!cpp_demangle_push_str(ddata, "operator,", 9)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('c', 'o'): + /* operator ~ */ + if (!cpp_demangle_push_str(ddata, "operator~", 9)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('c', 'v'): + /* operator (cast) */ + if (!cpp_demangle_push_str(ddata, "operator(cast)", 14)) + return (0); + ddata->cur += 2; + return (cpp_demangle_read_type(ddata, 1)); + + case SIMPLE_HASH('d', 'a'): + /* operator delete [] */ + if (!cpp_demangle_push_str(ddata, "operator delete []", 18)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('d', 'e'): + /* operator * (unary) */ + if (!cpp_demangle_push_str(ddata, "operator*", 9)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('d', 'l'): + /* operator delete */ + if (!cpp_demangle_push_str(ddata, "operator delete", 15)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('d', 'v'): + /* operator / */ + if (!cpp_demangle_push_str(ddata, "operator/", 9)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('d', 'V'): + /* operator /= */ + if (!cpp_demangle_push_str(ddata, "operator/=", 10)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('e', 'o'): + /* operator ^ */ + if (!cpp_demangle_push_str(ddata, "operator^", 9)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('e', 'O'): + /* operator ^= */ + if (!cpp_demangle_push_str(ddata, "operator^=", 10)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('e', 'q'): + /* operator == */ + if (!cpp_demangle_push_str(ddata, "operator==", 10)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('g', 'e'): + /* operator >= */ + if (!cpp_demangle_push_str(ddata, "operator>=", 10)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('g', 't'): + /* operator > */ + if (!cpp_demangle_push_str(ddata, "operator>", 9)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('i', 'x'): + /* operator [] */ + if (!cpp_demangle_push_str(ddata, "operator[]", 10)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('l', 'e'): + /* operator <= */ + if (!cpp_demangle_push_str(ddata, "operator<=", 10)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('l', 's'): + /* operator << */ + if (!cpp_demangle_push_str(ddata, "operator<<", 10)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('l', 'S'): + /* operator <<= */ + if (!cpp_demangle_push_str(ddata, "operator<<=", 11)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('l', 't'): + /* operator < */ + if (!cpp_demangle_push_str(ddata, "operator<", 9)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('m', 'i'): + /* operator - */ + if (!cpp_demangle_push_str(ddata, "operator-", 9)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('m', 'I'): + /* operator -= */ + if (!cpp_demangle_push_str(ddata, "operator-=", 10)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('m', 'l'): + /* operator * */ + if (!cpp_demangle_push_str(ddata, "operator*", 9)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('m', 'L'): + /* operator *= */ + if (!cpp_demangle_push_str(ddata, "operator*=", 10)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('m', 'm'): + /* operator -- */ + if (!cpp_demangle_push_str(ddata, "operator--", 10)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('n', 'a'): + /* operator new[] */ + if (!cpp_demangle_push_str(ddata, "operator new []", 15)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('n', 'e'): + /* operator != */ + if (!cpp_demangle_push_str(ddata, "operator!=", 10)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('n', 'g'): + /* operator - (unary) */ + if (!cpp_demangle_push_str(ddata, "operator-", 9)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('n', 't'): + /* operator ! */ + if (!cpp_demangle_push_str(ddata, "operator!", 9)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('n', 'w'): + /* operator new */ + if (!cpp_demangle_push_str(ddata, "operator new", 12)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('o', 'o'): + /* operator || */ + if (!cpp_demangle_push_str(ddata, "operator||", 10)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('o', 'r'): + /* operator | */ + if (!cpp_demangle_push_str(ddata, "operator|", 9)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('o', 'R'): + /* operator |= */ + if (!cpp_demangle_push_str(ddata, "operator|=", 10)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('p', 'l'): + /* operator + */ + if (!cpp_demangle_push_str(ddata, "operator+", 9)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('p', 'L'): + /* operator += */ + if (!cpp_demangle_push_str(ddata, "operator+=", 10)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('p', 'm'): + /* operator ->* */ + if (!cpp_demangle_push_str(ddata, "operator->*", 11)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('p', 'p'): + /* operator ++ */ + if (!cpp_demangle_push_str(ddata, "operator++", 10)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('p', 's'): + /* operator + (unary) */ + if (!cpp_demangle_push_str(ddata, "operator+", 9)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('p', 't'): + /* operator -> */ + if (!cpp_demangle_push_str(ddata, "operator->", 10)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('q', 'u'): + /* operator ? */ + if (!cpp_demangle_push_str(ddata, "operator?", 9)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('r', 'm'): + /* operator % */ + if (!cpp_demangle_push_str(ddata, "operator%", 9)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('r', 'M'): + /* operator %= */ + if (!cpp_demangle_push_str(ddata, "operator%=", 10)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('r', 's'): + /* operator >> */ + if (!cpp_demangle_push_str(ddata, "operator>>", 10)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('r', 'S'): + /* operator >>= */ + if (!cpp_demangle_push_str(ddata, "operator>>=", 11)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('r', 'z'): + /* operator sizeof */ + if (!cpp_demangle_push_str(ddata, "operator sizeof ", 16)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('s', 'r'): + /* scope resolution operator */ + if (!cpp_demangle_push_str(ddata, "scope resolution operator ", + 26)) + return (0); + ddata->cur += 2; + return (1); + + case SIMPLE_HASH('s', 'v'): + /* operator sizeof */ + if (!cpp_demangle_push_str(ddata, "operator sizeof ", 16)) + return (0); + ddata->cur += 2; + return (1); + } + + /* vendor extened operator */ + if (*ddata->cur == 'v' && ELFTC_ISDIGIT(*(ddata->cur + 1))) { + if (!cpp_demangle_push_str(ddata, "vendor extened operator ", + 24)) + return (0); + if (!cpp_demangle_push_str(ddata, ddata->cur + 1, 1)) + return (0); + ddata->cur += 2; + return (cpp_demangle_read_sname(ddata)); + } + + /* ctor-dtor-name */ + switch (SIMPLE_HASH(*ddata->cur, *(ddata->cur + 1))) { + case SIMPLE_HASH('C', '1'): + /* FALLTHROUGH */ + case SIMPLE_HASH('C', '2'): + /* FALLTHROUGH */ + case SIMPLE_HASH('C', '3'): + if (ddata->last_sname == NULL) + return (0); + if ((len = strlen(ddata->last_sname)) == 0) + return (0); + if (!cpp_demangle_push_str(ddata, "::", 2)) + return (0); + if (!cpp_demangle_push_str(ddata, ddata->last_sname, len)) + return (0); + ddata->cur +=2; + return (1); + + case SIMPLE_HASH('D', '0'): + /* FALLTHROUGH */ + case SIMPLE_HASH('D', '1'): + /* FALLTHROUGH */ + case SIMPLE_HASH('D', '2'): + if (ddata->last_sname == NULL) + return (0); + if ((len = strlen(ddata->last_sname)) == 0) + return (0); + if (!cpp_demangle_push_str(ddata, "::~", 3)) + return (0); + if (!cpp_demangle_push_str(ddata, ddata->last_sname, len)) + return (0); + ddata->cur +=2; + return (1); + } + + /* source name */ + if (ELFTC_ISDIGIT(*ddata->cur) != 0) + return (cpp_demangle_read_sname(ddata)); + + /* local source name */ + if (*ddata->cur == 'L') + return (cpp_demangle_local_source_name(ddata)); + + return (1); +} + +/* + * Read local source name. + * + * References: + * http://gcc.gnu.org/bugzilla/show_bug.cgi?id=31775 + * http://gcc.gnu.org/viewcvs?view=rev&revision=124467 + */ +static int +cpp_demangle_local_source_name(struct cpp_demangle_data *ddata) +{ + /* L */ + if (ddata == NULL || *ddata->cur != 'L') + return (0); + ++ddata->cur; + + /* source name */ + if (!cpp_demangle_read_sname(ddata)) + return (0); + + /* discriminator */ + if (*ddata->cur == '_') { + ++ddata->cur; + while (ELFTC_ISDIGIT(*ddata->cur) != 0) + ++ddata->cur; + } + + return (1); +} + +static int +cpp_demangle_read_v_offset(struct cpp_demangle_data *ddata) +{ + + if (ddata == NULL) + return (0); + + if (!cpp_demangle_push_str(ddata, "offset : ", 9)) + return (0); + + if (!cpp_demangle_read_offset_number(ddata)) + return (0); + + if (!cpp_demangle_push_str(ddata, "virtual offset : ", 17)) + return (0); + + return (!cpp_demangle_read_offset_number(ddata)); +} + +/* + * Decode floating point representation to string + * Return new allocated string or NULL + * + * Todo + * Replace these functions to macro. + */ +static char * +decode_fp_to_double(const char *p, size_t len) +{ + double f; + size_t rtn_len, limit, i; + int byte; + char *rtn; + + if (p == NULL || len == 0 || len % 2 != 0 || len / 2 > sizeof(double)) + return (NULL); + + memset(&f, 0, sizeof(double)); + + for (i = 0; i < len / 2; ++i) { + byte = hex_to_dec(p[len - i * 2 - 1]) + + hex_to_dec(p[len - i * 2 - 2]) * 16; + + if (byte < 0 || byte > 255) + return (NULL); + +#if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN + ((unsigned char *)&f)[i] = (unsigned char)(byte); +#else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ + ((unsigned char *)&f)[sizeof(double) - i - 1] = + (unsigned char)(byte); +#endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ + } + + rtn_len = 64; + limit = 0; +again: + if ((rtn = malloc(sizeof(char) * rtn_len)) == NULL) + return (NULL); + + if (snprintf(rtn, rtn_len, "%fld", f) >= (int)rtn_len) { + free(rtn); + if (limit++ > FLOAT_SPRINTF_TRY_LIMIT) + return (NULL); + rtn_len *= BUFFER_GROWFACTOR; + goto again; + } + + return rtn; +} + +static char * +decode_fp_to_float(const char *p, size_t len) +{ + size_t i, rtn_len, limit; + float f; + int byte; + char *rtn; + + if (p == NULL || len == 0 || len % 2 != 0 || len / 2 > sizeof(float)) + return (NULL); + + memset(&f, 0, sizeof(float)); + + for (i = 0; i < len / 2; ++i) { + byte = hex_to_dec(p[len - i * 2 - 1]) + + hex_to_dec(p[len - i * 2 - 2]) * 16; + if (byte < 0 || byte > 255) + return (NULL); +#if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN + ((unsigned char *)&f)[i] = (unsigned char)(byte); +#else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ + ((unsigned char *)&f)[sizeof(float) - i - 1] = + (unsigned char)(byte); +#endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ + } + + rtn_len = 64; + limit = 0; +again: + if ((rtn = malloc(sizeof(char) * rtn_len)) == NULL) + return (NULL); + + if (snprintf(rtn, rtn_len, "%ff", f) >= (int)rtn_len) { + free(rtn); + if (limit++ > FLOAT_SPRINTF_TRY_LIMIT) + return (NULL); + rtn_len *= BUFFER_GROWFACTOR; + goto again; + } + + return rtn; +} + +static char * +decode_fp_to_float128(const char *p, size_t len) +{ + long double f; + size_t rtn_len, limit, i; + int byte; + unsigned char buf[FLOAT_QUADRUPLE_BYTES]; + char *rtn; + + switch(sizeof(long double)) { + case FLOAT_QUADRUPLE_BYTES: + return (decode_fp_to_long_double(p, len)); + case FLOAT_EXTENED_BYTES: + if (p == NULL || len == 0 || len % 2 != 0 || + len / 2 > FLOAT_QUADRUPLE_BYTES) + return (NULL); + + memset(buf, 0, FLOAT_QUADRUPLE_BYTES); + + for (i = 0; i < len / 2; ++i) { + byte = hex_to_dec(p[len - i * 2 - 1]) + + hex_to_dec(p[len - i * 2 - 2]) * 16; + if (byte < 0 || byte > 255) + return (NULL); +#if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN + buf[i] = (unsigned char)(byte); +#else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ + buf[FLOAT_QUADRUPLE_BYTES - i -1] = + (unsigned char)(byte); +#endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ + } + memset(&f, 0, FLOAT_EXTENED_BYTES); + +#if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN + memcpy(&f, buf, FLOAT_EXTENED_BYTES); +#else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ + memcpy(&f, buf + 6, FLOAT_EXTENED_BYTES); +#endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ + + rtn_len = 256; + limit = 0; +again: + if ((rtn = malloc(sizeof(char) * rtn_len)) == NULL) + return (NULL); + + if (snprintf(rtn, rtn_len, "%Lfd", f) >= (int)rtn_len) { + free(rtn); + if (limit++ > FLOAT_SPRINTF_TRY_LIMIT) + return (NULL); + rtn_len *= BUFFER_GROWFACTOR; + goto again; + } + + return (rtn); + default: + return (NULL); + } +} + +static char * +decode_fp_to_float80(const char *p, size_t len) +{ + long double f; + size_t rtn_len, limit, i; + int byte; + unsigned char buf[FLOAT_EXTENED_BYTES]; + char *rtn; + + switch(sizeof(long double)) { + case FLOAT_QUADRUPLE_BYTES: + if (p == NULL || len == 0 || len % 2 != 0 || + len / 2 > FLOAT_EXTENED_BYTES) + return (NULL); + + memset(buf, 0, FLOAT_EXTENED_BYTES); + + for (i = 0; i < len / 2; ++i) { + byte = hex_to_dec(p[len - i * 2 - 1]) + + hex_to_dec(p[len - i * 2 - 2]) * 16; + + if (byte < 0 || byte > 255) + return (NULL); + +#if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN + buf[i] = (unsigned char)(byte); +#else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ + buf[FLOAT_EXTENED_BYTES - i -1] = + (unsigned char)(byte); +#endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ + } + + memset(&f, 0, FLOAT_QUADRUPLE_BYTES); + +#if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN + memcpy(&f, buf, FLOAT_EXTENED_BYTES); +#else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ + memcpy((unsigned char *)(&f) + 6, buf, FLOAT_EXTENED_BYTES); +#endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ + + rtn_len = 256; + limit = 0; +again: + if ((rtn = malloc(sizeof(char) * rtn_len)) == NULL) + return (NULL); + + if (snprintf(rtn, rtn_len, "%Lfd", f) >= (int)rtn_len) { + free(rtn); + if (limit++ > FLOAT_SPRINTF_TRY_LIMIT) + return (NULL); + rtn_len *= BUFFER_GROWFACTOR; + goto again; + } + + return (rtn); + case FLOAT_EXTENED_BYTES: + return (decode_fp_to_long_double(p, len)); + default: + return (NULL); + } +} + +static char * +decode_fp_to_long_double(const char *p, size_t len) +{ + long double f; + size_t rtn_len, limit, i; + int byte; + char *rtn; + + if (p == NULL || len == 0 || len % 2 != 0 || + len / 2 > sizeof(long double)) + return (NULL); + + memset(&f, 0, sizeof(long double)); + + for (i = 0; i < len / 2; ++i) { + byte = hex_to_dec(p[len - i * 2 - 1]) + + hex_to_dec(p[len - i * 2 - 2]) * 16; + + if (byte < 0 || byte > 255) + return (NULL); + +#if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN + ((unsigned char *)&f)[i] = (unsigned char)(byte); +#else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ + ((unsigned char *)&f)[sizeof(long double) - i - 1] = + (unsigned char)(byte); +#endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ + } + + rtn_len = 256; + limit = 0; +again: + if ((rtn = malloc(sizeof(char) * rtn_len)) == NULL) + return (NULL); + + if (snprintf(rtn, rtn_len, "%Lfd", f) >= (int)rtn_len) { + free(rtn); + if (limit++ > FLOAT_SPRINTF_TRY_LIMIT) + return (NULL); + rtn_len *= BUFFER_GROWFACTOR; + goto again; + } + + return (rtn); +} + +/* Simple hex to integer function used by decode_to_* function. */ +static int +hex_to_dec(char c) +{ + + switch (c) { + case '0': + return (0); + case '1': + return (1); + case '2': + return (2); + case '3': + return (3); + case '4': + return (4); + case '5': + return (5); + case '6': + return (6); + case '7': + return (7); + case '8': + return (8); + case '9': + return (9); + case 'a': + return (10); + case 'b': + return (11); + case 'c': + return (12); + case 'd': + return (13); + case 'e': + return (14); + case 'f': + return (15); + default: + return (-1); + } +} + +static void +vector_read_cmd_dest(struct vector_read_cmd *v) +{ + + if (v == NULL) + return; + + free(v->r_container); +} + +/* return -1 at failed, 0 at not found, 1 at found. */ +static int +vector_read_cmd_find(struct vector_read_cmd *v, enum read_cmd dst) +{ + size_t i; + + if (v == NULL || dst == READ_FAIL) + return (-1); + + for (i = 0; i < v->size; ++i) + if (v->r_container[i] == dst) + return (1); + + return (0); +} + +static int +vector_read_cmd_init(struct vector_read_cmd *v) +{ + + if (v == NULL) + return (0); + + v->size = 0; + v->capacity = VECTOR_DEF_CAPACITY; + + if ((v->r_container = malloc(sizeof(enum read_cmd) * v->capacity)) + == NULL) + return (0); + + return (1); +} + +static int +vector_read_cmd_pop(struct vector_read_cmd *v) +{ + + if (v == NULL || v->size == 0) + return (0); + + --v->size; + v->r_container[v->size] = READ_FAIL; + + return (1); +} + +static int +vector_read_cmd_push(struct vector_read_cmd *v, enum read_cmd cmd) +{ + enum read_cmd *tmp_r_ctn; + size_t tmp_cap; + size_t i; + + if (v == NULL) + return (0); + + if (v->size == v->capacity) { + tmp_cap = v->capacity * BUFFER_GROWFACTOR; + if ((tmp_r_ctn = malloc(sizeof(enum read_cmd) * tmp_cap)) + == NULL) + return (0); + for (i = 0; i < v->size; ++i) + tmp_r_ctn[i] = v->r_container[i]; + free(v->r_container); + v->r_container = tmp_r_ctn; + v->capacity = tmp_cap; + } + + v->r_container[v->size] = cmd; + ++v->size; + + return (1); +} + +static void +vector_type_qualifier_dest(struct vector_type_qualifier *v) +{ + + if (v == NULL) + return; + + free(v->q_container); + vector_str_dest(&v->ext_name); +} + +/* size, capacity, ext_name */ +static int +vector_type_qualifier_init(struct vector_type_qualifier *v) +{ + + if (v == NULL) + return (0); + + v->size = 0; + v->capacity = VECTOR_DEF_CAPACITY; + + if ((v->q_container = malloc(sizeof(enum type_qualifier) * v->capacity)) + == NULL) + return (0); + + assert(v->q_container != NULL); + + if (vector_str_init(&v->ext_name) == false) { + free(v->q_container); + return (0); + } + + return (1); +} + +static int +vector_type_qualifier_push(struct vector_type_qualifier *v, + enum type_qualifier t) +{ + enum type_qualifier *tmp_ctn; + size_t tmp_cap; + size_t i; + + if (v == NULL) + return (0); + + if (v->size == v->capacity) { + tmp_cap = v->capacity * BUFFER_GROWFACTOR; + if ((tmp_ctn = malloc(sizeof(enum type_qualifier) * tmp_cap)) + == NULL) + return (0); + for (i = 0; i < v->size; ++i) + tmp_ctn[i] = v->q_container[i]; + free(v->q_container); + v->q_container = tmp_ctn; + v->capacity = tmp_cap; + } + + v->q_container[v->size] = t; + ++v->size; + + return (1); +} diff --git a/contrib/libcxxrt/memory.cc b/contrib/libcxxrt/memory.cc new file mode 100644 index 000000000000..5f1aad76961f --- /dev/null +++ b/contrib/libcxxrt/memory.cc @@ -0,0 +1,171 @@ +/* + * Copyright 2010-2011 PathScale, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * 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" +#include "atomic.h" + + +namespace std +{ + struct nothrow_t {}; +} + + +/// 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 ATOMIC_SWAP(&new_handl, handler); + } + __attribute__((weak)) + new_handler get_new_handler(void) + { + return ATOMIC_LOAD(&new_handl); + } +} + + +#if __cplusplus < 201103L +#define NOEXCEPT throw() +#define BADALLOC throw(std::bad_alloc) +#else +#define NOEXCEPT noexcept +#define BADALLOC +#endif + + +__attribute__((weak)) +void* operator new(size_t size) BADALLOC +{ + if (0 == size) + { + size = 1; + } + void * mem = malloc(size); + while (0 == mem) + { + new_handler h = std::get_new_handler(); + if (0 != h) + { + h(); + } + else + { + throw std::bad_alloc(); + } + mem = malloc(size); + } + + return mem; +} + +__attribute__((weak)) +void* operator new(size_t size, const std::nothrow_t &) NOEXCEPT +{ + try { + return :: operator new(size); + } catch (...) { + // nothrow operator new should return NULL in case of + // std::bad_alloc exception in new handler + return NULL; + } +} + + +__attribute__((weak)) +void operator delete(void * ptr) NOEXCEPT +{ + free(ptr); +} + + +__attribute__((weak)) +void * operator new[](size_t size) BADALLOC +{ + return ::operator new(size); +} + + +__attribute__((weak)) +void * operator new[](size_t size, const std::nothrow_t &) NOEXCEPT +{ + try { + return ::operator new[](size); + } catch (...) { + // nothrow operator new should return NULL in case of + // std::bad_alloc exception in new handler + return NULL; + } +} + + +__attribute__((weak)) +void operator delete[](void * ptr) NOEXCEPT +{ + ::operator delete(ptr); +} + +// C++14 additional delete operators + +#if __cplusplus >= 201402L + +__attribute__((weak)) +void operator delete(void * ptr, size_t) NOEXCEPT +{ + ::operator delete(ptr); +} + + +__attribute__((weak)) +void operator delete[](void * ptr, size_t) NOEXCEPT +{ + ::operator delete(ptr); +} + +#endif diff --git a/contrib/libcxxrt/stdexcept.cc b/contrib/libcxxrt/stdexcept.cc new file mode 100644 index 000000000000..c1cea39f6aa6 --- /dev/null +++ b/contrib/libcxxrt/stdexcept.cc @@ -0,0 +1,99 @@ +/* + * Copyright 2010-2011 PathScale, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * stdexcept.cc - provides stub implementations of the exceptions required by the runtime. + */ +#include "stdexcept.h" + +namespace std { + +exception::exception() throw() {} +exception::~exception() {} +exception::exception(const exception&) throw() {} +exception& exception::operator=(const exception&) throw() +{ + return *this; +} +const char* exception::what() const throw() +{ + return "std::exception"; +} + +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_alloc::what() const throw() +{ + return "cxxrt::bad_alloc"; +} + + + +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; +} +const char* bad_cast::what() const throw() +{ + return "std::bad_cast"; +} + +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"; +} + +bad_array_new_length::bad_array_new_length() throw() {} +bad_array_new_length::~bad_array_new_length() {} +bad_array_new_length::bad_array_new_length(const bad_array_new_length&) throw() {} +bad_array_new_length& bad_array_new_length::operator=(const bad_array_new_length&) throw() +{ + return *this; +} + +const char* bad_array_new_length::what() const throw() +{ + return "std::bad_array_new_length"; +} + +} // namespace std + diff --git a/contrib/libcxxrt/stdexcept.h b/contrib/libcxxrt/stdexcept.h new file mode 100644 index 000000000000..892039357595 --- /dev/null +++ b/contrib/libcxxrt/stdexcept.h @@ -0,0 +1,96 @@ +/* + * Copyright 2010-2011 PathScale, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * 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(); + }; + + class bad_array_new_length: public bad_alloc + { + public: + bad_array_new_length() throw(); + bad_array_new_length(const bad_array_new_length&) throw(); + bad_array_new_length& operator=(const bad_array_new_length&) throw(); + virtual ~bad_array_new_length(); + virtual const char *what() const throw(); + }; + + +} // namespace std + diff --git a/contrib/libcxxrt/terminate.cc b/contrib/libcxxrt/terminate.cc new file mode 100644 index 000000000000..b7f50b474dc9 --- /dev/null +++ b/contrib/libcxxrt/terminate.cc @@ -0,0 +1,40 @@ +/* + * Copyright 2010-2011 PathScale, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdlib.h> + +namespace std +{ + /** + * Stub implementation of std::terminate. Used when the STL implementation + * doesn't provide one. + */ + __attribute__((weak)) + void terminate() + { + abort(); + } +} diff --git a/contrib/libcxxrt/typeinfo.cc b/contrib/libcxxrt/typeinfo.cc new file mode 100644 index 000000000000..5c44ef1fd2ca --- /dev/null +++ b/contrib/libcxxrt/typeinfo.cc @@ -0,0 +1,124 @@ +/* + * Copyright 2010-2012 PathScale, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "typeinfo.h" +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +using std::type_info; + +type_info::~type_info() {} + +bool type_info::operator==(const type_info &other) const +{ + return __type_name == other.__type_name; +} +bool type_info::operator!=(const type_info &other) const +{ + return __type_name != other.__type_name; +} +bool type_info::before(const type_info &other) const +{ + return __type_name < other.__type_name; +} +const char* type_info::name() const +{ + return __type_name; +} +type_info::type_info (const type_info& rhs) +{ + __type_name = rhs.__type_name; +} +type_info& type_info::operator= (const type_info& rhs) +{ + return *new type_info(rhs); +} + +ABI_NAMESPACE::__fundamental_type_info::~__fundamental_type_info() {} +ABI_NAMESPACE::__array_type_info::~__array_type_info() {} +ABI_NAMESPACE::__function_type_info::~__function_type_info() {} +ABI_NAMESPACE::__enum_type_info::~__enum_type_info() {} +ABI_NAMESPACE::__class_type_info::~__class_type_info() {} +ABI_NAMESPACE::__si_class_type_info::~__si_class_type_info() {} +ABI_NAMESPACE::__vmi_class_type_info::~__vmi_class_type_info() {} +ABI_NAMESPACE::__pbase_type_info::~__pbase_type_info() {} +ABI_NAMESPACE::__pointer_type_info::~__pointer_type_info() {} +ABI_NAMESPACE::__pointer_to_member_type_info::~__pointer_to_member_type_info() {} + +// From libelftc +extern "C" char *__cxa_demangle_gnu3(const char *); + +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 = __cxa_demangle_gnu3(mangled_name); + if (NULL != demangled) + { + size_t len = strlen(demangled); + if (!buf || (*n < len+1)) + { + buf = static_cast<char*>(realloc(buf, len+1)); + } + if (0 != buf) + { + memcpy(buf, demangled, len); + buf[len] = 0; + if (n) + { + *n = len; + } + if (status) + { + *status = 0; + } + } + else + { + if (status) + { + *status = -1; + } + } + free(demangled); + } + else + { + if (status) + { + *status = -2; + } + return NULL; + } + return buf; +} diff --git a/contrib/libcxxrt/typeinfo.h b/contrib/libcxxrt/typeinfo.h new file mode 100644 index 000000000000..a8a1a980e926 --- /dev/null +++ b/contrib/libcxxrt/typeinfo.h @@ -0,0 +1,313 @@ +/* + * Copyright 2010-2011 PathScale, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stddef.h> +#include "abi_namespace.h" + +namespace ABI_NAMESPACE +{ + struct __class_type_info; +} +namespace std +{ + /** + * Standard type info class. The layout of this class is specified by the + * ABI. The layout of the vtable is not, but is intended to be + * compatible with the GNU ABI. + * + * Unlike the GNU version, the vtable layout is considered semi-private. + */ + class type_info + { + public: + /** + * 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; + bool before(const type_info &) const; + const char* name() const; + type_info(); + private: + type_info(const type_info& rhs); + type_info& operator= (const type_info& rhs); + const char *__type_name; + /* + * The following functions are in this order to match the + * vtable layout of libsupc++. This allows libcxxrt to be used + * with libraries that depend on this. + * + * These functions are in the public headers for libstdc++, so + * we have to assume that someone will probably call them and + * expect them to work. Their names must also match the names used in + * libsupc++, so that code linking against this library can subclass + * type_info and correctly fill in the values in the vtables. + */ + public: + /** + * Returns true if this is some pointer type, false otherwise. + */ + virtual bool __is_pointer_p() const { return false; } + /** + * Returns true if this is some function type, false otherwise. + */ + virtual bool __is_function_p() const { return false; } + /** + * Catch function. Allows external libraries to implement + * their own basic types. This is used, for example, in the + * GNUstep Objective-C runtime to allow Objective-C types to be + * caught in G++ catch blocks. + * + * The outer parameter indicates the number of outer pointers + * in the high bits. The low bit indicates whether the + * pointers are const qualified. + */ + virtual bool __do_catch(const type_info *thrown_type, + void **thrown_object, + unsigned outer) const; + /** + * Performs an upcast. This is used in exception handling to + * cast from subclasses to superclasses. If the upcast is + * possible, it returns true and adjusts the pointer. If the + * upcast is not possible, it returns false and does not adjust + * the pointer. + */ + virtual bool __do_upcast( + const ABI_NAMESPACE::__class_type_info *target, + void **thrown_object) const + { + return false; + } + }; +} + + +namespace ABI_NAMESPACE +{ + /** + * 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(); + virtual bool __is_function_p() const { return true; } + }; + /** + * 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. + */ + struct __class_type_info : public std::type_info + { + virtual ~__class_type_info(); + /** + * Function implementing dynamic casts. + */ + virtual void *cast_to(void *obj, const struct __class_type_info *other) const; + virtual bool __do_upcast(const __class_type_info *target, + void **thrown_object) const + { + return this == target; + } + }; + + /** + * 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(); + const __class_type_info *__base_type; + virtual bool __do_upcast( + const ABI_NAMESPACE::__class_type_info *target, + void **thrown_object) const; + virtual void *cast_to(void *obj, 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 bool __do_upcast( + const ABI_NAMESPACE::__class_type_info *target, + void **thrown_object) const; + virtual void *cast_to(void *obj, 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 + }; + virtual bool __do_catch(const type_info *thrown_type, + void **thrown_object, + unsigned outer) const; + }; + + /** + * Pointer type info. + */ + struct __pointer_type_info : public __pbase_type_info + { + virtual ~__pointer_type_info(); + virtual bool __is_pointer_p() const { return true; } + }; + + /** + * 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/contrib/libcxxrt/unwind-arm.h b/contrib/libcxxrt/unwind-arm.h new file mode 100644 index 000000000000..4dc639882bca --- /dev/null +++ b/contrib/libcxxrt/unwind-arm.h @@ -0,0 +1,227 @@ +/* + * Copyright 2012 David Chisnall. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * ARM-specific unwind definitions. These are taken from the ARM EHABI + * specification. + */ + typedef enum +{ + _URC_OK = 0, /* operation completed successfully */ + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8, + _URC_FAILURE = 9, /* unspecified failure of some kind */ + _URC_FATAL_PHASE1_ERROR = _URC_FAILURE +} _Unwind_Reason_Code; + +typedef int _Unwind_Action; + +typedef uint32_t _Unwind_State; +#ifdef __clang__ +static const _Unwind_State _US_VIRTUAL_UNWIND_FRAME = 0; +static const _Unwind_State _US_UNWIND_FRAME_STARTING = 1; +static const _Unwind_State _US_UNWIND_FRAME_RESUME = 2; +static const _Unwind_State _US_ACTION_MASK = 3; +#else // GCC fails at knowing what a constant expression is +# define _US_VIRTUAL_UNWIND_FRAME 0 +# define _US_UNWIND_FRAME_STARTING 1 +# define _US_UNWIND_FRAME_RESUME 2 +# define _US_ACTION_MASK 3 +#endif + +typedef struct _Unwind_Context _Unwind_Context; + +typedef uint32_t _Unwind_EHT_Header; + +struct _Unwind_Exception +{ + uint64_t exception_class; + void (*exception_cleanup)(_Unwind_Reason_Code, struct _Unwind_Exception *); + /* Unwinder cache, private fields for the unwinder's use */ + struct + { + uint32_t reserved1; + uint32_t reserved2; + uint32_t reserved3; + uint32_t reserved4; + uint32_t reserved5; + /* init reserved1 to 0, then don't touch */ + } unwinder_cache; + /* Propagation barrier cache (valid after phase 1): */ + struct + { + uint32_t sp; + uint32_t bitpattern[5]; + } barrier_cache; + /* Cleanup cache (preserved over cleanup): */ + struct + { + uint32_t bitpattern[4]; + } cleanup_cache; + /* Pr cache (for pr's benefit): */ + struct + { + /** function start address */ + uint32_t fnstart; + /** pointer to EHT entry header word */ + _Unwind_EHT_Header *ehtp; + /** additional data */ + uint32_t additional; + uint32_t reserved1; + } pr_cache; + /** Force alignment of next item to 8-byte boundary */ + long long int :0; +}; + +/* Unwinding functions */ +_Unwind_Reason_Code _Unwind_RaiseException(struct _Unwind_Exception *ucbp); +void _Unwind_Resume(struct _Unwind_Exception *ucbp); +void _Unwind_Complete(struct _Unwind_Exception *ucbp); +void _Unwind_DeleteException(struct _Unwind_Exception *ucbp); +void *_Unwind_GetLanguageSpecificData(struct _Unwind_Context*); + +typedef enum +{ + _UVRSR_OK = 0, + _UVRSR_NOT_IMPLEMENTED = 1, + _UVRSR_FAILED = 2 +} _Unwind_VRS_Result; +typedef enum +{ + _UVRSC_CORE = 0, + _UVRSC_VFP = 1, + _UVRSC_WMMXD = 3, + _UVRSC_WMMXC = 4 +} _Unwind_VRS_RegClass; +typedef enum +{ + _UVRSD_UINT32 = 0, + _UVRSD_VFPX = 1, + _UVRSD_UINT64 = 3, + _UVRSD_FLOAT = 4, + _UVRSD_DOUBLE = 5 +} _Unwind_VRS_DataRepresentation; + +_Unwind_VRS_Result _Unwind_VRS_Get(_Unwind_Context *context, + _Unwind_VRS_RegClass regclass, + uint32_t regno, + _Unwind_VRS_DataRepresentation representation, + void *valuep); +_Unwind_VRS_Result _Unwind_VRS_Set(_Unwind_Context *context, + _Unwind_VRS_RegClass regclass, + uint32_t regno, + _Unwind_VRS_DataRepresentation representation, + void *valuep); + +/* Return the base-address for data references. */ +extern unsigned long _Unwind_GetDataRelBase(struct _Unwind_Context *); + +/* Return the base-address for text references. */ +extern unsigned long _Unwind_GetTextRelBase(struct _Unwind_Context *); +extern unsigned long _Unwind_GetRegionStart(struct _Unwind_Context *); + +typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn) (struct _Unwind_Context *, + void *); +extern _Unwind_Reason_Code _Unwind_Backtrace (_Unwind_Trace_Fn, void *); +extern _Unwind_Reason_Code + _Unwind_Resume_or_Rethrow (struct _Unwind_Exception *); + +/** + * The next set of functions are compatibility extensions, implementing Itanium + * ABI functions on top of ARM ones. + */ + +#define _UA_SEARCH_PHASE 1 +#define _UA_CLEANUP_PHASE 2 +#define _UA_HANDLER_FRAME 4 +#define _UA_FORCE_UNWIND 8 + +static inline unsigned long _Unwind_GetGR(struct _Unwind_Context *context, int reg) +{ + unsigned long val; + _Unwind_VRS_Get(context, _UVRSC_CORE, reg, _UVRSD_UINT32, &val); + return val; +} +static inline void _Unwind_SetGR(struct _Unwind_Context *context, int reg, unsigned long val) +{ + _Unwind_VRS_Set(context, _UVRSC_CORE, reg, _UVRSD_UINT32, &val); +} +static inline unsigned long _Unwind_GetIP(_Unwind_Context *context) +{ + // Low bit store the thumb state - discard it + return _Unwind_GetGR(context, 15) & ~1; +} +static inline void _Unwind_SetIP(_Unwind_Context *context, unsigned long val) +{ + // The lowest bit of the instruction pointer indicates whether we're in + // thumb or ARM mode. This is assumed to be fixed throughout a function, + // so must be propagated when setting the program counter. + unsigned long thumbState = _Unwind_GetGR(context, 15) & 1; + _Unwind_SetGR(context, 15, (val | thumbState)); +} + +/** GNU API function that unwinds the frame */ +_Unwind_Reason_Code __gnu_unwind_frame(struct _Unwind_Exception*, struct _Unwind_Context*); + + +#define DECLARE_PERSONALITY_FUNCTION(name) \ +_Unwind_Reason_Code name(_Unwind_State state,\ + struct _Unwind_Exception *exceptionObject,\ + struct _Unwind_Context *context); + +#define BEGIN_PERSONALITY_FUNCTION(name) \ +_Unwind_Reason_Code name(_Unwind_State state,\ + struct _Unwind_Exception *exceptionObject,\ + struct _Unwind_Context *context)\ +{\ + int version = 1;\ + uint64_t exceptionClass = exceptionObject->exception_class;\ + int actions;\ + switch (state)\ + {\ + default: return _URC_FAILURE;\ + case _US_VIRTUAL_UNWIND_FRAME:\ + {\ + actions = _UA_SEARCH_PHASE;\ + break;\ + }\ + case _US_UNWIND_FRAME_STARTING:\ + {\ + actions = _UA_CLEANUP_PHASE;\ + if (exceptionObject->barrier_cache.sp == _Unwind_GetGR(context, 13))\ + {\ + actions |= _UA_HANDLER_FRAME;\ + }\ + break;\ + }\ + case _US_UNWIND_FRAME_RESUME:\ + {\ + return continueUnwinding(exceptionObject, context);\ + break;\ + }\ + }\ + _Unwind_SetGR (context, 12, reinterpret_cast<unsigned long>(exceptionObject));\ + +#define CALL_PERSONALITY_FUNCTION(name) name(state,exceptionObject,context) diff --git a/contrib/libcxxrt/unwind-itanium.h b/contrib/libcxxrt/unwind-itanium.h new file mode 100644 index 000000000000..0ca9488605aa --- /dev/null +++ b/contrib/libcxxrt/unwind-itanium.h @@ -0,0 +1,170 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2003 Hewlett-Packard Co + Contributed by David Mosberger-Tang <davidm@hpl.hp.com> + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef _UNWIND_H +#define _UNWIND_H + +/* For uint64_t */ +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* Minimal interface as per C++ ABI draft standard: + + http://www.codesourcery.com/cxx-abi/abi-eh.html */ + +typedef enum + { + _URC_NO_REASON = 0, + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, + _URC_FATAL_PHASE2_ERROR = 2, + _URC_FATAL_PHASE1_ERROR = 3, + _URC_NORMAL_STOP = 4, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8 + } +_Unwind_Reason_Code; + +typedef int _Unwind_Action; + +#define _UA_SEARCH_PHASE 1 +#define _UA_CLEANUP_PHASE 2 +#define _UA_HANDLER_FRAME 4 +#define _UA_FORCE_UNWIND 8 + +struct _Unwind_Context; /* opaque data-structure */ +struct _Unwind_Exception; /* forward-declaration */ + +typedef void (*_Unwind_Exception_Cleanup_Fn) (_Unwind_Reason_Code, + struct _Unwind_Exception *); + +typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn) (int, _Unwind_Action, + uint64_t, + struct _Unwind_Exception *, + struct _Unwind_Context *, + void *); + +/* The C++ ABI requires exception_class, private_1, and private_2 to + be of type uint64 and the entire structure to be + double-word-aligned. Please note that exception_class stays 64-bit + even on 32-bit machines for gcc compatibility. */ +struct _Unwind_Exception + { + uint64_t exception_class; + _Unwind_Exception_Cleanup_Fn exception_cleanup; + unsigned long private_1; + unsigned long private_2; + } ; + +extern _Unwind_Reason_Code _Unwind_RaiseException (struct _Unwind_Exception *); +extern _Unwind_Reason_Code _Unwind_ForcedUnwind (struct _Unwind_Exception *, + _Unwind_Stop_Fn, void *); +extern void _Unwind_Resume (struct _Unwind_Exception *); +extern void _Unwind_DeleteException (struct _Unwind_Exception *); +extern unsigned long _Unwind_GetGR (struct _Unwind_Context *, int); +extern void _Unwind_SetGR (struct _Unwind_Context *, int, unsigned long); +extern unsigned long _Unwind_GetIP (struct _Unwind_Context *); +extern unsigned long _Unwind_GetIPInfo (struct _Unwind_Context *, int *); +extern void _Unwind_SetIP (struct _Unwind_Context *, unsigned long); +extern unsigned long _Unwind_GetLanguageSpecificData (struct _Unwind_Context*); +extern unsigned long _Unwind_GetRegionStart (struct _Unwind_Context *); + +#ifdef _GNU_SOURCE + +/* Callback for _Unwind_Backtrace(). The backtrace stops immediately + if the callback returns any value other than _URC_NO_REASON. */ +typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn) (struct _Unwind_Context *, + void *); + +/* See http://gcc.gnu.org/ml/gcc-patches/2001-09/msg00082.html for why + _UA_END_OF_STACK exists. */ +# define _UA_END_OF_STACK 16 + +/* If the unwind was initiated due to a forced unwind, resume that + operation, else re-raise the exception. This is used by + __cxa_rethrow(). */ +extern _Unwind_Reason_Code + _Unwind_Resume_or_Rethrow (struct _Unwind_Exception *); + +/* See http://gcc.gnu.org/ml/gcc-patches/2003-09/msg00154.html for why + _Unwind_GetBSP() exists. */ +extern unsigned long _Unwind_GetBSP (struct _Unwind_Context *); + +/* Return the "canonical frame address" for the given context. + This is used by NPTL... */ +extern unsigned long _Unwind_GetCFA (struct _Unwind_Context *); + +/* Return the base-address for data references. */ +extern unsigned long _Unwind_GetDataRelBase (struct _Unwind_Context *); + +/* Return the base-address for text references. */ +extern unsigned long _Unwind_GetTextRelBase (struct _Unwind_Context *); + +/* Call _Unwind_Trace_Fn once for each stack-frame, without doing any + cleanup. The first frame for which the callback is invoked is the + one for the caller of _Unwind_Backtrace(). _Unwind_Backtrace() + returns _URC_END_OF_STACK when the backtrace stopped due to + reaching the end of the call-chain or _URC_FATAL_PHASE1_ERROR if it + stops for any other reason. */ +extern _Unwind_Reason_Code _Unwind_Backtrace (_Unwind_Trace_Fn, void *); + +/* Find the start-address of the procedure containing the specified IP + or NULL if it cannot be found (e.g., because the function has no + unwind info). Note: there is not necessarily a one-to-one + correspondence between source-level functions and procedures: some + functions don't have unwind-info and others are split into multiple + procedures. */ +extern void *_Unwind_FindEnclosingFunction (void *); + +/* See also Linux Standard Base Spec: + http://www.linuxbase.org/spec/refspecs/LSB_1.3.0/gLSB/gLSB/libgcc-s.html */ + +#endif /* _GNU_SOURCE */ + +#define DECLARE_PERSONALITY_FUNCTION(name) \ +_Unwind_Reason_Code name(int version,\ + _Unwind_Action actions,\ + uint64_t exceptionClass,\ + struct _Unwind_Exception *exceptionObject,\ + struct _Unwind_Context *context); +#define BEGIN_PERSONALITY_FUNCTION(name) \ +_Unwind_Reason_Code name(int version,\ + _Unwind_Action actions,\ + uint64_t exceptionClass,\ + struct _Unwind_Exception *exceptionObject,\ + struct _Unwind_Context *context)\ +{ + +#define CALL_PERSONALITY_FUNCTION(name) name(version, actions, exceptionClass, exceptionObject, context) + +#ifdef __cplusplus +} +#endif + +#endif /* _UNWIND_H */ diff --git a/contrib/libcxxrt/unwind.h b/contrib/libcxxrt/unwind.h new file mode 100644 index 000000000000..130dd6d82fc4 --- /dev/null +++ b/contrib/libcxxrt/unwind.h @@ -0,0 +1,40 @@ +/* + * Copyright 2012 David Chisnall. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef UNWIND_H_INCLUDED +#define UNWIND_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__arm__) && !defined(__ARM_DWARF_EH__) +#include "unwind-arm.h" +#else +#include "unwind-itanium.h" +#endif + +#ifdef __cplusplus +} +#endif + +#endif |