10b57cec5SDimitry Andric //===-- gcc_personality_v0.c - Implement __gcc_personality_v0 -------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "int_lib.h"
10fe6060f1SDimitry Andric #include <stddef.h>
110b57cec5SDimitry Andric 
120b57cec5SDimitry Andric #include <unwind.h>
130b57cec5SDimitry Andric /*
140b57cec5SDimitry Andric  * XXX On FreeBSD, this file is compiled into three libraries:
150b57cec5SDimitry Andric  *   - libcompiler_rt
160b57cec5SDimitry Andric  *   - libgcc_eh
170b57cec5SDimitry Andric  *   - libgcc_s
180b57cec5SDimitry Andric  *
190b57cec5SDimitry Andric  * In the former, the include path points to the contrib/libcxxrt/unwind-arm.h
200b57cec5SDimitry Andric  * copy of unwind.h.  In the latter, the include path points to the
210b57cec5SDimitry Andric  * contrib/libunwind/include/unwind.h header (LLVM libunwind).
220b57cec5SDimitry Andric  *
230b57cec5SDimitry Andric  * Neither (seemingly redundant) variant of unwind.h needs the redefinitions
240b57cec5SDimitry Andric  * provided in the "helpful" header below, and libcxxrt's unwind-arm.h provides
250b57cec5SDimitry Andric  * *no* useful distinguishing macros, so just forcibly disable the helper
260b57cec5SDimitry Andric  * header on FreeBSD.
270b57cec5SDimitry Andric  */
280b57cec5SDimitry Andric #if defined(__arm__) && !defined(__ARM_DWARF_EH__) &&                          \
290b57cec5SDimitry Andric     !defined(__USING_SJLJ_EXCEPTIONS__) && !defined(__FreeBSD__)
300b57cec5SDimitry Andric // When building with older compilers (e.g. clang <3.9), it is possible that we
310b57cec5SDimitry Andric // have a version of unwind.h which does not provide the EHABI declarations
320b57cec5SDimitry Andric // which are quired for the C personality to conform to the specification.  In
330b57cec5SDimitry Andric // order to provide forward compatibility for such compilers, we re-declare the
340b57cec5SDimitry Andric // necessary interfaces in the helper to permit a standalone compilation of the
350b57cec5SDimitry Andric // builtins (which contains the C unwinding personality for historical reasons).
360b57cec5SDimitry Andric #include "unwind-ehabi-helpers.h"
370b57cec5SDimitry Andric #endif
380b57cec5SDimitry Andric 
39fe6060f1SDimitry Andric #if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__)
40fe6060f1SDimitry Andric #include <windows.h>
41fe6060f1SDimitry Andric #include <winnt.h>
42fe6060f1SDimitry Andric 
43fe6060f1SDimitry Andric EXCEPTION_DISPOSITION _GCC_specific_handler(PEXCEPTION_RECORD, void *, PCONTEXT,
44fe6060f1SDimitry Andric                                             PDISPATCHER_CONTEXT,
45fe6060f1SDimitry Andric                                             _Unwind_Personality_Fn);
46fe6060f1SDimitry Andric #endif
47fe6060f1SDimitry Andric 
480b57cec5SDimitry Andric // Pointer encodings documented at:
490b57cec5SDimitry Andric //   http://refspecs.freestandards.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.html
500b57cec5SDimitry Andric 
510b57cec5SDimitry Andric #define DW_EH_PE_omit 0xff // no data follows
520b57cec5SDimitry Andric 
530b57cec5SDimitry Andric #define DW_EH_PE_absptr 0x00
540b57cec5SDimitry Andric #define DW_EH_PE_uleb128 0x01
550b57cec5SDimitry Andric #define DW_EH_PE_udata2 0x02
560b57cec5SDimitry Andric #define DW_EH_PE_udata4 0x03
570b57cec5SDimitry Andric #define DW_EH_PE_udata8 0x04
580b57cec5SDimitry Andric #define DW_EH_PE_sleb128 0x09
590b57cec5SDimitry Andric #define DW_EH_PE_sdata2 0x0A
600b57cec5SDimitry Andric #define DW_EH_PE_sdata4 0x0B
610b57cec5SDimitry Andric #define DW_EH_PE_sdata8 0x0C
620b57cec5SDimitry Andric 
630b57cec5SDimitry Andric #define DW_EH_PE_pcrel 0x10
640b57cec5SDimitry Andric #define DW_EH_PE_textrel 0x20
650b57cec5SDimitry Andric #define DW_EH_PE_datarel 0x30
660b57cec5SDimitry Andric #define DW_EH_PE_funcrel 0x40
670b57cec5SDimitry Andric #define DW_EH_PE_aligned 0x50
680b57cec5SDimitry Andric #define DW_EH_PE_indirect 0x80 // gcc extension
690b57cec5SDimitry Andric 
700b57cec5SDimitry Andric // read a uleb128 encoded value and advance pointer
readULEB128(const uint8_t ** data)71fe6060f1SDimitry Andric static size_t readULEB128(const uint8_t **data) {
72fe6060f1SDimitry Andric   size_t result = 0;
73fe6060f1SDimitry Andric   size_t shift = 0;
740b57cec5SDimitry Andric   unsigned char byte;
750b57cec5SDimitry Andric   const uint8_t *p = *data;
760b57cec5SDimitry Andric   do {
770b57cec5SDimitry Andric     byte = *p++;
780b57cec5SDimitry Andric     result |= (byte & 0x7f) << shift;
790b57cec5SDimitry Andric     shift += 7;
800b57cec5SDimitry Andric   } while (byte & 0x80);
810b57cec5SDimitry Andric   *data = p;
820b57cec5SDimitry Andric   return result;
830b57cec5SDimitry Andric }
840b57cec5SDimitry Andric 
850b57cec5SDimitry Andric // read a pointer encoded value and advance pointer
readEncodedPointer(const uint8_t ** data,uint8_t encoding)860b57cec5SDimitry Andric static uintptr_t readEncodedPointer(const uint8_t **data, uint8_t encoding) {
870b57cec5SDimitry Andric   const uint8_t *p = *data;
880b57cec5SDimitry Andric   uintptr_t result = 0;
890b57cec5SDimitry Andric 
900b57cec5SDimitry Andric   if (encoding == DW_EH_PE_omit)
910b57cec5SDimitry Andric     return 0;
920b57cec5SDimitry Andric 
930b57cec5SDimitry Andric   // first get value
940b57cec5SDimitry Andric   switch (encoding & 0x0F) {
950b57cec5SDimitry Andric   case DW_EH_PE_absptr:
960b57cec5SDimitry Andric     result = *((const uintptr_t *)p);
970b57cec5SDimitry Andric     p += sizeof(uintptr_t);
980b57cec5SDimitry Andric     break;
990b57cec5SDimitry Andric   case DW_EH_PE_uleb128:
1000b57cec5SDimitry Andric     result = readULEB128(&p);
1010b57cec5SDimitry Andric     break;
1020b57cec5SDimitry Andric   case DW_EH_PE_udata2:
1030b57cec5SDimitry Andric     result = *((const uint16_t *)p);
1040b57cec5SDimitry Andric     p += sizeof(uint16_t);
1050b57cec5SDimitry Andric     break;
1060b57cec5SDimitry Andric   case DW_EH_PE_udata4:
1070b57cec5SDimitry Andric     result = *((const uint32_t *)p);
1080b57cec5SDimitry Andric     p += sizeof(uint32_t);
1090b57cec5SDimitry Andric     break;
1100b57cec5SDimitry Andric   case DW_EH_PE_udata8:
1110b57cec5SDimitry Andric     result = *((const uint64_t *)p);
1120b57cec5SDimitry Andric     p += sizeof(uint64_t);
1130b57cec5SDimitry Andric     break;
1140b57cec5SDimitry Andric   case DW_EH_PE_sdata2:
1150b57cec5SDimitry Andric     result = *((const int16_t *)p);
1160b57cec5SDimitry Andric     p += sizeof(int16_t);
1170b57cec5SDimitry Andric     break;
1180b57cec5SDimitry Andric   case DW_EH_PE_sdata4:
1190b57cec5SDimitry Andric     result = *((const int32_t *)p);
1200b57cec5SDimitry Andric     p += sizeof(int32_t);
1210b57cec5SDimitry Andric     break;
1220b57cec5SDimitry Andric   case DW_EH_PE_sdata8:
1230b57cec5SDimitry Andric     result = *((const int64_t *)p);
1240b57cec5SDimitry Andric     p += sizeof(int64_t);
1250b57cec5SDimitry Andric     break;
1260b57cec5SDimitry Andric   case DW_EH_PE_sleb128:
1270b57cec5SDimitry Andric   default:
1280b57cec5SDimitry Andric     // not supported
1290b57cec5SDimitry Andric     compilerrt_abort();
1300b57cec5SDimitry Andric     break;
1310b57cec5SDimitry Andric   }
1320b57cec5SDimitry Andric 
1330b57cec5SDimitry Andric   // then add relative offset
1340b57cec5SDimitry Andric   switch (encoding & 0x70) {
1350b57cec5SDimitry Andric   case DW_EH_PE_absptr:
1360b57cec5SDimitry Andric     // do nothing
1370b57cec5SDimitry Andric     break;
1380b57cec5SDimitry Andric   case DW_EH_PE_pcrel:
1390b57cec5SDimitry Andric     result += (uintptr_t)(*data);
1400b57cec5SDimitry Andric     break;
1410b57cec5SDimitry Andric   case DW_EH_PE_textrel:
1420b57cec5SDimitry Andric   case DW_EH_PE_datarel:
1430b57cec5SDimitry Andric   case DW_EH_PE_funcrel:
1440b57cec5SDimitry Andric   case DW_EH_PE_aligned:
1450b57cec5SDimitry Andric   default:
1460b57cec5SDimitry Andric     // not supported
1470b57cec5SDimitry Andric     compilerrt_abort();
1480b57cec5SDimitry Andric     break;
1490b57cec5SDimitry Andric   }
1500b57cec5SDimitry Andric 
1510b57cec5SDimitry Andric   // then apply indirection
1520b57cec5SDimitry Andric   if (encoding & DW_EH_PE_indirect) {
1530b57cec5SDimitry Andric     result = *((const uintptr_t *)result);
1540b57cec5SDimitry Andric   }
1550b57cec5SDimitry Andric 
1560b57cec5SDimitry Andric   *data = p;
1570b57cec5SDimitry Andric   return result;
1580b57cec5SDimitry Andric }
1590b57cec5SDimitry Andric 
1600b57cec5SDimitry Andric #if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) &&                 \
16181ad6265SDimitry Andric     !defined(__ARM_DWARF_EH__) && !defined(__SEH__)
1620b57cec5SDimitry Andric #define USING_ARM_EHABI 1
1630b57cec5SDimitry Andric _Unwind_Reason_Code __gnu_unwind_frame(struct _Unwind_Exception *,
1640b57cec5SDimitry Andric                                        struct _Unwind_Context *);
1650b57cec5SDimitry Andric #endif
1660b57cec5SDimitry Andric 
1670b57cec5SDimitry Andric static inline _Unwind_Reason_Code
continueUnwind(struct _Unwind_Exception * exceptionObject,struct _Unwind_Context * context)1680b57cec5SDimitry Andric continueUnwind(struct _Unwind_Exception *exceptionObject,
1690b57cec5SDimitry Andric                struct _Unwind_Context *context) {
1700b57cec5SDimitry Andric #if USING_ARM_EHABI
1710b57cec5SDimitry Andric   // On ARM EHABI the personality routine is responsible for actually
1720b57cec5SDimitry Andric   // unwinding a single stack frame before returning (ARM EHABI Sec. 6.1).
1730b57cec5SDimitry Andric   if (__gnu_unwind_frame(exceptionObject, context) != _URC_OK)
1740b57cec5SDimitry Andric     return _URC_FAILURE;
1750b57cec5SDimitry Andric #endif
1760b57cec5SDimitry Andric   return _URC_CONTINUE_UNWIND;
1770b57cec5SDimitry Andric }
1780b57cec5SDimitry Andric 
1790b57cec5SDimitry Andric // The C compiler makes references to __gcc_personality_v0 in
1800b57cec5SDimitry Andric // the dwarf unwind information for translation units that use
1810b57cec5SDimitry Andric // __attribute__((cleanup(xx))) on local variables.
1820b57cec5SDimitry Andric // This personality routine is called by the system unwinder
1830b57cec5SDimitry Andric // on each frame as the stack is unwound during a C++ exception
1840b57cec5SDimitry Andric // throw through a C function compiled with -fexceptions.
1850b57cec5SDimitry Andric #if __USING_SJLJ_EXCEPTIONS__
1860b57cec5SDimitry Andric // the setjump-longjump based exceptions personality routine has a
1870b57cec5SDimitry Andric // different name
__gcc_personality_sj0(int version,_Unwind_Action actions,uint64_t exceptionClass,struct _Unwind_Exception * exceptionObject,struct _Unwind_Context * context)1880b57cec5SDimitry Andric COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_sj0(
1890b57cec5SDimitry Andric     int version, _Unwind_Action actions, uint64_t exceptionClass,
1900b57cec5SDimitry Andric     struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context)
1910b57cec5SDimitry Andric #elif USING_ARM_EHABI
1920b57cec5SDimitry Andric // The ARM EHABI personality routine has a different signature.
1930b57cec5SDimitry Andric COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0(
1940b57cec5SDimitry Andric     _Unwind_State state, struct _Unwind_Exception *exceptionObject,
1950b57cec5SDimitry Andric     struct _Unwind_Context *context)
196fe6060f1SDimitry Andric #elif defined(__SEH__)
197fe6060f1SDimitry Andric static _Unwind_Reason_Code __gcc_personality_imp(
198fe6060f1SDimitry Andric     int version, _Unwind_Action actions, uint64_t exceptionClass,
199fe6060f1SDimitry Andric     struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context)
2000b57cec5SDimitry Andric #else
2010b57cec5SDimitry Andric COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0(
2020b57cec5SDimitry Andric     int version, _Unwind_Action actions, uint64_t exceptionClass,
2030b57cec5SDimitry Andric     struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context)
2040b57cec5SDimitry Andric #endif
2050b57cec5SDimitry Andric {
2060b57cec5SDimitry Andric   // Since C does not have catch clauses, there is nothing to do during
2070b57cec5SDimitry Andric   // phase 1 (the search phase).
2080b57cec5SDimitry Andric #if USING_ARM_EHABI
2090b57cec5SDimitry Andric   // After resuming from a cleanup we should also continue on to the next
2100b57cec5SDimitry Andric   // frame straight away.
2110b57cec5SDimitry Andric   if ((state & _US_ACTION_MASK) != _US_UNWIND_FRAME_STARTING)
2120b57cec5SDimitry Andric #else
2130b57cec5SDimitry Andric   if (actions & _UA_SEARCH_PHASE)
2140b57cec5SDimitry Andric #endif
2150b57cec5SDimitry Andric     return continueUnwind(exceptionObject, context);
2160b57cec5SDimitry Andric 
2170b57cec5SDimitry Andric   // There is nothing to do if there is no LSDA for this frame.
2180b57cec5SDimitry Andric   const uint8_t *lsda = (uint8_t *)_Unwind_GetLanguageSpecificData(context);
2190b57cec5SDimitry Andric   if (lsda == (uint8_t *)0)
2200b57cec5SDimitry Andric     return continueUnwind(exceptionObject, context);
2210b57cec5SDimitry Andric 
2220b57cec5SDimitry Andric   uintptr_t pc = (uintptr_t)_Unwind_GetIP(context) - 1;
2230b57cec5SDimitry Andric   uintptr_t funcStart = (uintptr_t)_Unwind_GetRegionStart(context);
2240b57cec5SDimitry Andric   uintptr_t pcOffset = pc - funcStart;
2250b57cec5SDimitry Andric 
2260b57cec5SDimitry Andric   // Parse LSDA header.
2270b57cec5SDimitry Andric   uint8_t lpStartEncoding = *lsda++;
2280b57cec5SDimitry Andric   if (lpStartEncoding != DW_EH_PE_omit) {
2290b57cec5SDimitry Andric     readEncodedPointer(&lsda, lpStartEncoding);
2300b57cec5SDimitry Andric   }
2310b57cec5SDimitry Andric   uint8_t ttypeEncoding = *lsda++;
2320b57cec5SDimitry Andric   if (ttypeEncoding != DW_EH_PE_omit) {
2330b57cec5SDimitry Andric     readULEB128(&lsda);
2340b57cec5SDimitry Andric   }
2350b57cec5SDimitry Andric   // Walk call-site table looking for range that includes current PC.
2360b57cec5SDimitry Andric   uint8_t callSiteEncoding = *lsda++;
23706c3fb27SDimitry Andric   size_t callSiteTableLength = readULEB128(&lsda);
2380b57cec5SDimitry Andric   const uint8_t *callSiteTableStart = lsda;
2390b57cec5SDimitry Andric   const uint8_t *callSiteTableEnd = callSiteTableStart + callSiteTableLength;
2400b57cec5SDimitry Andric   const uint8_t *p = callSiteTableStart;
2410b57cec5SDimitry Andric   while (p < callSiteTableEnd) {
2420b57cec5SDimitry Andric     uintptr_t start = readEncodedPointer(&p, callSiteEncoding);
243fe6060f1SDimitry Andric     size_t length = readEncodedPointer(&p, callSiteEncoding);
244fe6060f1SDimitry Andric     size_t landingPad = readEncodedPointer(&p, callSiteEncoding);
2450b57cec5SDimitry Andric     readULEB128(&p); // action value not used for C code
2460b57cec5SDimitry Andric     if (landingPad == 0)
2470b57cec5SDimitry Andric       continue; // no landing pad for this entry
2480b57cec5SDimitry Andric     if ((start <= pcOffset) && (pcOffset < (start + length))) {
2490b57cec5SDimitry Andric       // Found landing pad for the PC.
2500b57cec5SDimitry Andric       // Set Instruction Pointer to so we re-enter function
2510b57cec5SDimitry Andric       // at landing pad. The landing pad is created by the compiler
2520b57cec5SDimitry Andric       // to take two parameters in registers.
2530b57cec5SDimitry Andric       _Unwind_SetGR(context, __builtin_eh_return_data_regno(0),
2540b57cec5SDimitry Andric                     (uintptr_t)exceptionObject);
2550b57cec5SDimitry Andric       _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0);
2560b57cec5SDimitry Andric       _Unwind_SetIP(context, (funcStart + landingPad));
2570b57cec5SDimitry Andric       return _URC_INSTALL_CONTEXT;
2580b57cec5SDimitry Andric     }
2590b57cec5SDimitry Andric   }
2600b57cec5SDimitry Andric 
2610b57cec5SDimitry Andric   // No landing pad found, continue unwinding.
2620b57cec5SDimitry Andric   return continueUnwind(exceptionObject, context);
2630b57cec5SDimitry Andric }
264fe6060f1SDimitry Andric 
265fe6060f1SDimitry Andric #if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__)
266fe6060f1SDimitry Andric COMPILER_RT_ABI EXCEPTION_DISPOSITION
__gcc_personality_seh0(PEXCEPTION_RECORD ms_exc,void * this_frame,PCONTEXT ms_orig_context,PDISPATCHER_CONTEXT ms_disp)267fe6060f1SDimitry Andric __gcc_personality_seh0(PEXCEPTION_RECORD ms_exc, void *this_frame,
268fe6060f1SDimitry Andric                        PCONTEXT ms_orig_context, PDISPATCHER_CONTEXT ms_disp) {
269fe6060f1SDimitry Andric   return _GCC_specific_handler(ms_exc, this_frame, ms_orig_context, ms_disp,
270fe6060f1SDimitry Andric                                __gcc_personality_imp);
271fe6060f1SDimitry Andric }
272fe6060f1SDimitry Andric #endif
273