xref: /openbsd-src/gnu/llvm/compiler-rt/lib/builtins/gcc_personality_v0.c (revision 810390e339a5425391477d5d41c78d7cab2424ac)
13cab2bb3Spatrick //===-- gcc_personality_v0.c - Implement __gcc_personality_v0 -------------===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick 
93cab2bb3Spatrick #include "int_lib.h"
10d89ec533Spatrick #include <stddef.h>
113cab2bb3Spatrick 
123cab2bb3Spatrick #include <unwind.h>
133cab2bb3Spatrick #if defined(__arm__) && !defined(__ARM_DWARF_EH__) &&                          \
143cab2bb3Spatrick     !defined(__USING_SJLJ_EXCEPTIONS__)
153cab2bb3Spatrick // When building with older compilers (e.g. clang <3.9), it is possible that we
163cab2bb3Spatrick // have a version of unwind.h which does not provide the EHABI declarations
173cab2bb3Spatrick // which are quired for the C personality to conform to the specification.  In
183cab2bb3Spatrick // order to provide forward compatibility for such compilers, we re-declare the
193cab2bb3Spatrick // necessary interfaces in the helper to permit a standalone compilation of the
203cab2bb3Spatrick // builtins (which contains the C unwinding personality for historical reasons).
213cab2bb3Spatrick #include "unwind-ehabi-helpers.h"
223cab2bb3Spatrick #endif
233cab2bb3Spatrick 
24d89ec533Spatrick #if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__)
25d89ec533Spatrick #include <windows.h>
26d89ec533Spatrick #include <winnt.h>
27d89ec533Spatrick 
28d89ec533Spatrick EXCEPTION_DISPOSITION _GCC_specific_handler(PEXCEPTION_RECORD, void *, PCONTEXT,
29d89ec533Spatrick                                             PDISPATCHER_CONTEXT,
30d89ec533Spatrick                                             _Unwind_Personality_Fn);
31d89ec533Spatrick #endif
32d89ec533Spatrick 
333cab2bb3Spatrick // Pointer encodings documented at:
343cab2bb3Spatrick //   http://refspecs.freestandards.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.html
353cab2bb3Spatrick 
363cab2bb3Spatrick #define DW_EH_PE_omit 0xff // no data follows
373cab2bb3Spatrick 
383cab2bb3Spatrick #define DW_EH_PE_absptr 0x00
393cab2bb3Spatrick #define DW_EH_PE_uleb128 0x01
403cab2bb3Spatrick #define DW_EH_PE_udata2 0x02
413cab2bb3Spatrick #define DW_EH_PE_udata4 0x03
423cab2bb3Spatrick #define DW_EH_PE_udata8 0x04
433cab2bb3Spatrick #define DW_EH_PE_sleb128 0x09
443cab2bb3Spatrick #define DW_EH_PE_sdata2 0x0A
453cab2bb3Spatrick #define DW_EH_PE_sdata4 0x0B
463cab2bb3Spatrick #define DW_EH_PE_sdata8 0x0C
473cab2bb3Spatrick 
483cab2bb3Spatrick #define DW_EH_PE_pcrel 0x10
493cab2bb3Spatrick #define DW_EH_PE_textrel 0x20
503cab2bb3Spatrick #define DW_EH_PE_datarel 0x30
513cab2bb3Spatrick #define DW_EH_PE_funcrel 0x40
523cab2bb3Spatrick #define DW_EH_PE_aligned 0x50
533cab2bb3Spatrick #define DW_EH_PE_indirect 0x80 // gcc extension
543cab2bb3Spatrick 
553cab2bb3Spatrick // read a uleb128 encoded value and advance pointer
readULEB128(const uint8_t ** data)56d89ec533Spatrick static size_t readULEB128(const uint8_t **data) {
57d89ec533Spatrick   size_t result = 0;
58d89ec533Spatrick   size_t shift = 0;
593cab2bb3Spatrick   unsigned char byte;
603cab2bb3Spatrick   const uint8_t *p = *data;
613cab2bb3Spatrick   do {
623cab2bb3Spatrick     byte = *p++;
633cab2bb3Spatrick     result |= (byte & 0x7f) << shift;
643cab2bb3Spatrick     shift += 7;
653cab2bb3Spatrick   } while (byte & 0x80);
663cab2bb3Spatrick   *data = p;
673cab2bb3Spatrick   return result;
683cab2bb3Spatrick }
693cab2bb3Spatrick 
703cab2bb3Spatrick // read a pointer encoded value and advance pointer
readEncodedPointer(const uint8_t ** data,uint8_t encoding)713cab2bb3Spatrick static uintptr_t readEncodedPointer(const uint8_t **data, uint8_t encoding) {
723cab2bb3Spatrick   const uint8_t *p = *data;
733cab2bb3Spatrick   uintptr_t result = 0;
743cab2bb3Spatrick 
753cab2bb3Spatrick   if (encoding == DW_EH_PE_omit)
763cab2bb3Spatrick     return 0;
773cab2bb3Spatrick 
783cab2bb3Spatrick   // first get value
793cab2bb3Spatrick   switch (encoding & 0x0F) {
803cab2bb3Spatrick   case DW_EH_PE_absptr:
813cab2bb3Spatrick     result = *((const uintptr_t *)p);
823cab2bb3Spatrick     p += sizeof(uintptr_t);
833cab2bb3Spatrick     break;
843cab2bb3Spatrick   case DW_EH_PE_uleb128:
853cab2bb3Spatrick     result = readULEB128(&p);
863cab2bb3Spatrick     break;
873cab2bb3Spatrick   case DW_EH_PE_udata2:
883cab2bb3Spatrick     result = *((const uint16_t *)p);
893cab2bb3Spatrick     p += sizeof(uint16_t);
903cab2bb3Spatrick     break;
913cab2bb3Spatrick   case DW_EH_PE_udata4:
923cab2bb3Spatrick     result = *((const uint32_t *)p);
933cab2bb3Spatrick     p += sizeof(uint32_t);
943cab2bb3Spatrick     break;
953cab2bb3Spatrick   case DW_EH_PE_udata8:
963cab2bb3Spatrick     result = *((const uint64_t *)p);
973cab2bb3Spatrick     p += sizeof(uint64_t);
983cab2bb3Spatrick     break;
993cab2bb3Spatrick   case DW_EH_PE_sdata2:
1003cab2bb3Spatrick     result = *((const int16_t *)p);
1013cab2bb3Spatrick     p += sizeof(int16_t);
1023cab2bb3Spatrick     break;
1033cab2bb3Spatrick   case DW_EH_PE_sdata4:
1043cab2bb3Spatrick     result = *((const int32_t *)p);
1053cab2bb3Spatrick     p += sizeof(int32_t);
1063cab2bb3Spatrick     break;
1073cab2bb3Spatrick   case DW_EH_PE_sdata8:
1083cab2bb3Spatrick     result = *((const int64_t *)p);
1093cab2bb3Spatrick     p += sizeof(int64_t);
1103cab2bb3Spatrick     break;
1113cab2bb3Spatrick   case DW_EH_PE_sleb128:
1123cab2bb3Spatrick   default:
1133cab2bb3Spatrick     // not supported
1143cab2bb3Spatrick     compilerrt_abort();
1153cab2bb3Spatrick     break;
1163cab2bb3Spatrick   }
1173cab2bb3Spatrick 
1183cab2bb3Spatrick   // then add relative offset
1193cab2bb3Spatrick   switch (encoding & 0x70) {
1203cab2bb3Spatrick   case DW_EH_PE_absptr:
1213cab2bb3Spatrick     // do nothing
1223cab2bb3Spatrick     break;
1233cab2bb3Spatrick   case DW_EH_PE_pcrel:
1243cab2bb3Spatrick     result += (uintptr_t)(*data);
1253cab2bb3Spatrick     break;
1263cab2bb3Spatrick   case DW_EH_PE_textrel:
1273cab2bb3Spatrick   case DW_EH_PE_datarel:
1283cab2bb3Spatrick   case DW_EH_PE_funcrel:
1293cab2bb3Spatrick   case DW_EH_PE_aligned:
1303cab2bb3Spatrick   default:
1313cab2bb3Spatrick     // not supported
1323cab2bb3Spatrick     compilerrt_abort();
1333cab2bb3Spatrick     break;
1343cab2bb3Spatrick   }
1353cab2bb3Spatrick 
1363cab2bb3Spatrick   // then apply indirection
1373cab2bb3Spatrick   if (encoding & DW_EH_PE_indirect) {
1383cab2bb3Spatrick     result = *((const uintptr_t *)result);
1393cab2bb3Spatrick   }
1403cab2bb3Spatrick 
1413cab2bb3Spatrick   *data = p;
1423cab2bb3Spatrick   return result;
1433cab2bb3Spatrick }
1443cab2bb3Spatrick 
1453cab2bb3Spatrick #if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) &&                 \
146*810390e3Srobert     !defined(__ARM_DWARF_EH__) && !defined(__SEH__)
1473cab2bb3Spatrick #define USING_ARM_EHABI 1
1483cab2bb3Spatrick _Unwind_Reason_Code __gnu_unwind_frame(struct _Unwind_Exception *,
1493cab2bb3Spatrick                                        struct _Unwind_Context *);
1503cab2bb3Spatrick #endif
1513cab2bb3Spatrick 
1523cab2bb3Spatrick static inline _Unwind_Reason_Code
continueUnwind(struct _Unwind_Exception * exceptionObject,struct _Unwind_Context * context)1533cab2bb3Spatrick continueUnwind(struct _Unwind_Exception *exceptionObject,
1543cab2bb3Spatrick                struct _Unwind_Context *context) {
1553cab2bb3Spatrick #if USING_ARM_EHABI
1563cab2bb3Spatrick   // On ARM EHABI the personality routine is responsible for actually
1573cab2bb3Spatrick   // unwinding a single stack frame before returning (ARM EHABI Sec. 6.1).
1583cab2bb3Spatrick   if (__gnu_unwind_frame(exceptionObject, context) != _URC_OK)
1593cab2bb3Spatrick     return _URC_FAILURE;
1603cab2bb3Spatrick #endif
1613cab2bb3Spatrick   return _URC_CONTINUE_UNWIND;
1623cab2bb3Spatrick }
1633cab2bb3Spatrick 
1643cab2bb3Spatrick // The C compiler makes references to __gcc_personality_v0 in
1653cab2bb3Spatrick // the dwarf unwind information for translation units that use
1663cab2bb3Spatrick // __attribute__((cleanup(xx))) on local variables.
1673cab2bb3Spatrick // This personality routine is called by the system unwinder
1683cab2bb3Spatrick // on each frame as the stack is unwound during a C++ exception
1693cab2bb3Spatrick // throw through a C function compiled with -fexceptions.
1703cab2bb3Spatrick #if __USING_SJLJ_EXCEPTIONS__
1713cab2bb3Spatrick // the setjump-longjump based exceptions personality routine has a
1723cab2bb3Spatrick // different name
__gcc_personality_sj0(int version,_Unwind_Action actions,uint64_t exceptionClass,struct _Unwind_Exception * exceptionObject,struct _Unwind_Context * context)1733cab2bb3Spatrick COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_sj0(
1743cab2bb3Spatrick     int version, _Unwind_Action actions, uint64_t exceptionClass,
1753cab2bb3Spatrick     struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context)
1763cab2bb3Spatrick #elif USING_ARM_EHABI
1773cab2bb3Spatrick // The ARM EHABI personality routine has a different signature.
1783cab2bb3Spatrick COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0(
1793cab2bb3Spatrick     _Unwind_State state, struct _Unwind_Exception *exceptionObject,
1803cab2bb3Spatrick     struct _Unwind_Context *context)
181d89ec533Spatrick #elif defined(__SEH__)
182d89ec533Spatrick static _Unwind_Reason_Code __gcc_personality_imp(
183d89ec533Spatrick     int version, _Unwind_Action actions, uint64_t exceptionClass,
184d89ec533Spatrick     struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context)
1853cab2bb3Spatrick #else
1863cab2bb3Spatrick COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0(
1873cab2bb3Spatrick     int version, _Unwind_Action actions, uint64_t exceptionClass,
1883cab2bb3Spatrick     struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context)
1893cab2bb3Spatrick #endif
1903cab2bb3Spatrick {
1913cab2bb3Spatrick   // Since C does not have catch clauses, there is nothing to do during
1923cab2bb3Spatrick   // phase 1 (the search phase).
1933cab2bb3Spatrick #if USING_ARM_EHABI
1943cab2bb3Spatrick   // After resuming from a cleanup we should also continue on to the next
1953cab2bb3Spatrick   // frame straight away.
1963cab2bb3Spatrick   if ((state & _US_ACTION_MASK) != _US_UNWIND_FRAME_STARTING)
1973cab2bb3Spatrick #else
1983cab2bb3Spatrick   if (actions & _UA_SEARCH_PHASE)
1993cab2bb3Spatrick #endif
2003cab2bb3Spatrick     return continueUnwind(exceptionObject, context);
2013cab2bb3Spatrick 
2023cab2bb3Spatrick   // There is nothing to do if there is no LSDA for this frame.
2033cab2bb3Spatrick   const uint8_t *lsda = (uint8_t *)_Unwind_GetLanguageSpecificData(context);
2043cab2bb3Spatrick   if (lsda == (uint8_t *)0)
2053cab2bb3Spatrick     return continueUnwind(exceptionObject, context);
2063cab2bb3Spatrick 
2073cab2bb3Spatrick   uintptr_t pc = (uintptr_t)_Unwind_GetIP(context) - 1;
2083cab2bb3Spatrick   uintptr_t funcStart = (uintptr_t)_Unwind_GetRegionStart(context);
2093cab2bb3Spatrick   uintptr_t pcOffset = pc - funcStart;
2103cab2bb3Spatrick 
2113cab2bb3Spatrick   // Parse LSDA header.
2123cab2bb3Spatrick   uint8_t lpStartEncoding = *lsda++;
2133cab2bb3Spatrick   if (lpStartEncoding != DW_EH_PE_omit) {
2143cab2bb3Spatrick     readEncodedPointer(&lsda, lpStartEncoding);
2153cab2bb3Spatrick   }
2163cab2bb3Spatrick   uint8_t ttypeEncoding = *lsda++;
2173cab2bb3Spatrick   if (ttypeEncoding != DW_EH_PE_omit) {
2183cab2bb3Spatrick     readULEB128(&lsda);
2193cab2bb3Spatrick   }
2203cab2bb3Spatrick   // Walk call-site table looking for range that includes current PC.
2213cab2bb3Spatrick   uint8_t callSiteEncoding = *lsda++;
2223cab2bb3Spatrick   uint32_t callSiteTableLength = readULEB128(&lsda);
2233cab2bb3Spatrick   const uint8_t *callSiteTableStart = lsda;
2243cab2bb3Spatrick   const uint8_t *callSiteTableEnd = callSiteTableStart + callSiteTableLength;
2253cab2bb3Spatrick   const uint8_t *p = callSiteTableStart;
2263cab2bb3Spatrick   while (p < callSiteTableEnd) {
2273cab2bb3Spatrick     uintptr_t start = readEncodedPointer(&p, callSiteEncoding);
228d89ec533Spatrick     size_t length = readEncodedPointer(&p, callSiteEncoding);
229d89ec533Spatrick     size_t landingPad = readEncodedPointer(&p, callSiteEncoding);
2303cab2bb3Spatrick     readULEB128(&p); // action value not used for C code
2313cab2bb3Spatrick     if (landingPad == 0)
2323cab2bb3Spatrick       continue; // no landing pad for this entry
2333cab2bb3Spatrick     if ((start <= pcOffset) && (pcOffset < (start + length))) {
2343cab2bb3Spatrick       // Found landing pad for the PC.
2353cab2bb3Spatrick       // Set Instruction Pointer to so we re-enter function
2363cab2bb3Spatrick       // at landing pad. The landing pad is created by the compiler
2373cab2bb3Spatrick       // to take two parameters in registers.
2383cab2bb3Spatrick       _Unwind_SetGR(context, __builtin_eh_return_data_regno(0),
2393cab2bb3Spatrick                     (uintptr_t)exceptionObject);
2403cab2bb3Spatrick       _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0);
2413cab2bb3Spatrick       _Unwind_SetIP(context, (funcStart + landingPad));
2423cab2bb3Spatrick       return _URC_INSTALL_CONTEXT;
2433cab2bb3Spatrick     }
2443cab2bb3Spatrick   }
2453cab2bb3Spatrick 
2463cab2bb3Spatrick   // No landing pad found, continue unwinding.
2473cab2bb3Spatrick   return continueUnwind(exceptionObject, context);
2483cab2bb3Spatrick }
249d89ec533Spatrick 
250d89ec533Spatrick #if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__)
251d89ec533Spatrick COMPILER_RT_ABI EXCEPTION_DISPOSITION
__gcc_personality_seh0(PEXCEPTION_RECORD ms_exc,void * this_frame,PCONTEXT ms_orig_context,PDISPATCHER_CONTEXT ms_disp)252d89ec533Spatrick __gcc_personality_seh0(PEXCEPTION_RECORD ms_exc, void *this_frame,
253d89ec533Spatrick                        PCONTEXT ms_orig_context, PDISPATCHER_CONTEXT ms_disp) {
254d89ec533Spatrick   return _GCC_specific_handler(ms_exc, this_frame, ms_orig_context, ms_disp,
255d89ec533Spatrick                                __gcc_personality_imp);
256d89ec533Spatrick }
257d89ec533Spatrick #endif
258