Initial import of compiler-rt.
 -


git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@74292 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/gcc_personality_v0.c b/lib/gcc_personality_v0.c
new file mode 100644
index 0000000..55d1166
--- /dev/null
+++ b/lib/gcc_personality_v0.c
@@ -0,0 +1,237 @@
+//===-- gcc_personality_v0.c - Implement __gcc_personality_v0 -------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+//
+// _Unwind_* stuff based on C++ ABI public documentation
+// http://refspecs.freestandards.org/abi-eh-1.21.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 enum {
+    _UA_SEARCH_PHASE = 1,
+    _UA_CLEANUP_PHASE = 2,
+    _UA_HANDLER_FRAME = 4,
+    _UA_FORCE_UNWIND = 8,
+    _UA_END_OF_STACK = 16
+} _Unwind_Action;
+
+typedef struct _Unwind_Context* _Unwind_Context_t;
+
+struct _Unwind_Exception {
+    uint64_t                exception_class;
+    _Unwind_Reason_Code        (*exception_cleanup)(_Unwind_Reason_Code reason, 
+                                                struct _Unwind_Exception* exc);
+    uintptr_t                private_1;    
+    uintptr_t                private_2;    
+};
+
+extern const uint8_t*    _Unwind_GetLanguageSpecificData(_Unwind_Context_t c);
+extern void              _Unwind_SetGR(_Unwind_Context_t c, int i, uintptr_t n);
+extern void              _Unwind_SetIP(_Unwind_Context_t, uintptr_t new_value);
+extern uintptr_t         _Unwind_GetIP(_Unwind_Context_t context);
+extern uintptr_t         _Unwind_GetRegionStart(_Unwind_Context_t context);
+
+
+// 
+// Pointer encodings documented at:
+//   http://refspecs.freestandards.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.html
+//
+#define DW_EH_PE_omit      0xff  // no data follows
+
+#define DW_EH_PE_absptr    0x00
+#define DW_EH_PE_uleb128   0x01
+#define DW_EH_PE_udata2    0x02
+#define DW_EH_PE_udata4    0x03
+#define DW_EH_PE_udata8    0x04
+#define DW_EH_PE_sleb128   0x09
+#define DW_EH_PE_sdata2    0x0A
+#define DW_EH_PE_sdata4    0x0B
+#define DW_EH_PE_sdata8    0x0C
+
+#define DW_EH_PE_pcrel     0x10
+#define DW_EH_PE_textrel   0x20
+#define DW_EH_PE_datarel   0x30
+#define DW_EH_PE_funcrel   0x40
+#define DW_EH_PE_aligned   0x50  
+#define DW_EH_PE_indirect  0x80 // gcc extension
+
+
+
+// read a uleb128 encoded value and advance pointer
+static uintptr_t readULEB128(const uint8_t** data)
+{
+    uintptr_t result = 0;
+    uintptr_t shift = 0;
+    unsigned char byte;
+    const uint8_t* p = *data;
+    do {
+        byte = *p++;
+        result |= (byte & 0x7f) << shift;
+        shift += 7;
+    } while (byte & 0x80);
+    *data = p;
+    return result;
+}
+
+// read a pointer encoded value and advance pointer
+static uintptr_t readEncodedPointer(const uint8_t** data, uint8_t encoding)
+{
+    const uint8_t* p = *data;
+    uintptr_t result = 0;
+
+    if ( encoding == DW_EH_PE_omit ) 
+        return 0;
+
+    // first get value
+    switch (encoding & 0x0F) {
+        case DW_EH_PE_absptr:
+            result = *((uintptr_t*)p);
+            p += sizeof(uintptr_t);
+            break;
+        case DW_EH_PE_uleb128:
+            result = readULEB128(&p);
+            break;
+        case DW_EH_PE_udata2:
+            result = *((uint16_t*)p);
+            p += sizeof(uint16_t);
+            break;
+        case DW_EH_PE_udata4:
+            result = *((uint32_t*)p);
+            p += sizeof(uint32_t);
+            break;
+        case DW_EH_PE_udata8:
+            result = *((uint64_t*)p);
+            p += sizeof(uint64_t);
+            break;
+        case DW_EH_PE_sdata2:
+            result = *((int16_t*)p);
+            p += sizeof(int16_t);
+            break;
+        case DW_EH_PE_sdata4:
+            result = *((int32_t*)p);
+            p += sizeof(int32_t);
+            break;
+        case DW_EH_PE_sdata8:
+            result = *((int64_t*)p);
+            p += sizeof(int64_t);
+            break;
+        case DW_EH_PE_sleb128:
+        default:
+            // not supported
+            abort();
+            break;
+    }
+
+    // then add relative offset
+    switch ( encoding & 0x70 ) {
+        case DW_EH_PE_absptr:
+            // do nothing
+            break;
+        case DW_EH_PE_pcrel:
+            result += (uintptr_t)(*data);
+            break;
+        case DW_EH_PE_textrel:
+        case DW_EH_PE_datarel:
+        case DW_EH_PE_funcrel:
+        case DW_EH_PE_aligned:
+        default:
+            // not supported
+            abort();
+            break;
+    }
+
+    // then apply indirection
+    if (encoding & DW_EH_PE_indirect) {
+        result = *((uintptr_t*)result);
+    }
+
+    *data = p;
+    return result;
+}
+
+
+//
+// The C compiler makes references to __gcc_personality_v0 in
+// the dwarf unwind information for translation units that use
+// __attribute__((cleanup(xx))) on local variables.
+// This personality routine is called by the system unwinder
+// on each frame as the stack is unwound during a C++ exception
+// throw through a C function compiled with -fexceptions.
+//
+_Unwind_Reason_Code __gcc_personality_v0(int version, _Unwind_Action actions,
+         uint64_t exceptionClass, struct _Unwind_Exception* exceptionObject,
+         _Unwind_Context_t context)
+{
+    // Since C does not have catch clauses, there is nothing to do during
+    // phase 1 (the search phase).
+    if ( actions & _UA_SEARCH_PHASE ) 
+        return _URC_CONTINUE_UNWIND;
+        
+    // There is nothing to do if there is no LSDA for this frame.
+    const uint8_t* lsda = _Unwind_GetLanguageSpecificData(context);
+    if ( lsda == NULL )
+        return _URC_CONTINUE_UNWIND;
+
+    uintptr_t pc = _Unwind_GetIP(context)-1;
+    uintptr_t funcStart = _Unwind_GetRegionStart(context);
+    uintptr_t pcOffset = pc - funcStart;
+
+    // Parse LSDA header.
+    uint8_t lpStartEncoding = *lsda++;
+    if (lpStartEncoding != DW_EH_PE_omit) {
+        readEncodedPointer(&lsda, lpStartEncoding); 
+    }
+    uint8_t ttypeEncoding = *lsda++;
+    if (ttypeEncoding != DW_EH_PE_omit) {
+        readULEB128(&lsda);  
+    }
+    // Walk call-site table looking for range that includes current PC.
+    uint8_t         callSiteEncoding = *lsda++;
+    uint32_t        callSiteTableLength = readULEB128(&lsda);
+    const uint8_t*  callSiteTableStart = lsda;
+    const uint8_t*  callSiteTableEnd = callSiteTableStart + callSiteTableLength;
+    const uint8_t* p=callSiteTableStart;
+    while (p < callSiteTableEnd) {
+        uintptr_t start = readEncodedPointer(&p, callSiteEncoding);
+        uintptr_t length = readEncodedPointer(&p, callSiteEncoding);
+        uintptr_t landingPad = readEncodedPointer(&p, callSiteEncoding);
+        readULEB128(&p); // action value not used for C code
+        if ( landingPad == 0 )
+            continue; // no landing pad for this entry
+        if ( (start <= pcOffset) && (pcOffset < (start+length)) ) {
+            // Found landing pad for the PC.
+            // Set Instruction Pointer to so we re-enter function 
+            // at landing pad. The landing pad is created by the compiler
+            // to take two parameters in registers.
+            _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), 
+                                                (uintptr_t)exceptionObject);
+            _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0);
+            _Unwind_SetIP(context, funcStart+landingPad);
+            return _URC_INSTALL_CONTEXT;
+        }
+    }
+    
+    // No landing pad found, continue unwinding.
+    return _URC_CONTINUE_UNWIND;
+}
+