xref: /freebsd/contrib/llvm-project/compiler-rt/lib/builtins/gcc_personality_v0.c (revision c66ec88fed842fbaad62c30d510644ceb7bd2d71)
1 //===-- gcc_personality_v0.c - Implement __gcc_personality_v0 -------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "int_lib.h"
10 
11 #include <unwind.h>
12 /*
13  * XXX On FreeBSD, this file is compiled into three libraries:
14  *   - libcompiler_rt
15  *   - libgcc_eh
16  *   - libgcc_s
17  *
18  * In the former, the include path points to the contrib/libcxxrt/unwind-arm.h
19  * copy of unwind.h.  In the latter, the include path points to the
20  * contrib/libunwind/include/unwind.h header (LLVM libunwind).
21  *
22  * Neither (seemingly redundant) variant of unwind.h needs the redefinitions
23  * provided in the "helpful" header below, and libcxxrt's unwind-arm.h provides
24  * *no* useful distinguishing macros, so just forcibly disable the helper
25  * header on FreeBSD.
26  */
27 #if defined(__arm__) && !defined(__ARM_DWARF_EH__) &&                          \
28     !defined(__USING_SJLJ_EXCEPTIONS__) && !defined(__FreeBSD__)
29 // When building with older compilers (e.g. clang <3.9), it is possible that we
30 // have a version of unwind.h which does not provide the EHABI declarations
31 // which are quired for the C personality to conform to the specification.  In
32 // order to provide forward compatibility for such compilers, we re-declare the
33 // necessary interfaces in the helper to permit a standalone compilation of the
34 // builtins (which contains the C unwinding personality for historical reasons).
35 #include "unwind-ehabi-helpers.h"
36 #endif
37 
38 // Pointer encodings documented at:
39 //   http://refspecs.freestandards.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.html
40 
41 #define DW_EH_PE_omit 0xff // no data follows
42 
43 #define DW_EH_PE_absptr 0x00
44 #define DW_EH_PE_uleb128 0x01
45 #define DW_EH_PE_udata2 0x02
46 #define DW_EH_PE_udata4 0x03
47 #define DW_EH_PE_udata8 0x04
48 #define DW_EH_PE_sleb128 0x09
49 #define DW_EH_PE_sdata2 0x0A
50 #define DW_EH_PE_sdata4 0x0B
51 #define DW_EH_PE_sdata8 0x0C
52 
53 #define DW_EH_PE_pcrel 0x10
54 #define DW_EH_PE_textrel 0x20
55 #define DW_EH_PE_datarel 0x30
56 #define DW_EH_PE_funcrel 0x40
57 #define DW_EH_PE_aligned 0x50
58 #define DW_EH_PE_indirect 0x80 // gcc extension
59 
60 // read a uleb128 encoded value and advance pointer
61 static uintptr_t readULEB128(const uint8_t **data) {
62   uintptr_t result = 0;
63   uintptr_t shift = 0;
64   unsigned char byte;
65   const uint8_t *p = *data;
66   do {
67     byte = *p++;
68     result |= (byte & 0x7f) << shift;
69     shift += 7;
70   } while (byte & 0x80);
71   *data = p;
72   return result;
73 }
74 
75 // read a pointer encoded value and advance pointer
76 static uintptr_t readEncodedPointer(const uint8_t **data, uint8_t encoding) {
77   const uint8_t *p = *data;
78   uintptr_t result = 0;
79 
80   if (encoding == DW_EH_PE_omit)
81     return 0;
82 
83   // first get value
84   switch (encoding & 0x0F) {
85   case DW_EH_PE_absptr:
86     result = *((const uintptr_t *)p);
87     p += sizeof(uintptr_t);
88     break;
89   case DW_EH_PE_uleb128:
90     result = readULEB128(&p);
91     break;
92   case DW_EH_PE_udata2:
93     result = *((const uint16_t *)p);
94     p += sizeof(uint16_t);
95     break;
96   case DW_EH_PE_udata4:
97     result = *((const uint32_t *)p);
98     p += sizeof(uint32_t);
99     break;
100   case DW_EH_PE_udata8:
101     result = *((const uint64_t *)p);
102     p += sizeof(uint64_t);
103     break;
104   case DW_EH_PE_sdata2:
105     result = *((const int16_t *)p);
106     p += sizeof(int16_t);
107     break;
108   case DW_EH_PE_sdata4:
109     result = *((const int32_t *)p);
110     p += sizeof(int32_t);
111     break;
112   case DW_EH_PE_sdata8:
113     result = *((const int64_t *)p);
114     p += sizeof(int64_t);
115     break;
116   case DW_EH_PE_sleb128:
117   default:
118     // not supported
119     compilerrt_abort();
120     break;
121   }
122 
123   // then add relative offset
124   switch (encoding & 0x70) {
125   case DW_EH_PE_absptr:
126     // do nothing
127     break;
128   case DW_EH_PE_pcrel:
129     result += (uintptr_t)(*data);
130     break;
131   case DW_EH_PE_textrel:
132   case DW_EH_PE_datarel:
133   case DW_EH_PE_funcrel:
134   case DW_EH_PE_aligned:
135   default:
136     // not supported
137     compilerrt_abort();
138     break;
139   }
140 
141   // then apply indirection
142   if (encoding & DW_EH_PE_indirect) {
143     result = *((const uintptr_t *)result);
144   }
145 
146   *data = p;
147   return result;
148 }
149 
150 #if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) &&                 \
151     !defined(__ARM_DWARF_EH__)
152 #define USING_ARM_EHABI 1
153 _Unwind_Reason_Code __gnu_unwind_frame(struct _Unwind_Exception *,
154                                        struct _Unwind_Context *);
155 #endif
156 
157 static inline _Unwind_Reason_Code
158 continueUnwind(struct _Unwind_Exception *exceptionObject,
159                struct _Unwind_Context *context) {
160 #if USING_ARM_EHABI
161   // On ARM EHABI the personality routine is responsible for actually
162   // unwinding a single stack frame before returning (ARM EHABI Sec. 6.1).
163   if (__gnu_unwind_frame(exceptionObject, context) != _URC_OK)
164     return _URC_FAILURE;
165 #endif
166   return _URC_CONTINUE_UNWIND;
167 }
168 
169 // The C compiler makes references to __gcc_personality_v0 in
170 // the dwarf unwind information for translation units that use
171 // __attribute__((cleanup(xx))) on local variables.
172 // This personality routine is called by the system unwinder
173 // on each frame as the stack is unwound during a C++ exception
174 // throw through a C function compiled with -fexceptions.
175 #if __USING_SJLJ_EXCEPTIONS__
176 // the setjump-longjump based exceptions personality routine has a
177 // different name
178 COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_sj0(
179     int version, _Unwind_Action actions, uint64_t exceptionClass,
180     struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context)
181 #elif USING_ARM_EHABI
182 // The ARM EHABI personality routine has a different signature.
183 COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0(
184     _Unwind_State state, struct _Unwind_Exception *exceptionObject,
185     struct _Unwind_Context *context)
186 #else
187 COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0(
188     int version, _Unwind_Action actions, uint64_t exceptionClass,
189     struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context)
190 #endif
191 {
192   // Since C does not have catch clauses, there is nothing to do during
193   // phase 1 (the search phase).
194 #if USING_ARM_EHABI
195   // After resuming from a cleanup we should also continue on to the next
196   // frame straight away.
197   if ((state & _US_ACTION_MASK) != _US_UNWIND_FRAME_STARTING)
198 #else
199   if (actions & _UA_SEARCH_PHASE)
200 #endif
201     return continueUnwind(exceptionObject, context);
202 
203   // There is nothing to do if there is no LSDA for this frame.
204   const uint8_t *lsda = (uint8_t *)_Unwind_GetLanguageSpecificData(context);
205   if (lsda == (uint8_t *)0)
206     return continueUnwind(exceptionObject, context);
207 
208   uintptr_t pc = (uintptr_t)_Unwind_GetIP(context) - 1;
209   uintptr_t funcStart = (uintptr_t)_Unwind_GetRegionStart(context);
210   uintptr_t pcOffset = pc - funcStart;
211 
212   // Parse LSDA header.
213   uint8_t lpStartEncoding = *lsda++;
214   if (lpStartEncoding != DW_EH_PE_omit) {
215     readEncodedPointer(&lsda, lpStartEncoding);
216   }
217   uint8_t ttypeEncoding = *lsda++;
218   if (ttypeEncoding != DW_EH_PE_omit) {
219     readULEB128(&lsda);
220   }
221   // Walk call-site table looking for range that includes current PC.
222   uint8_t callSiteEncoding = *lsda++;
223   uint32_t callSiteTableLength = readULEB128(&lsda);
224   const uint8_t *callSiteTableStart = lsda;
225   const uint8_t *callSiteTableEnd = callSiteTableStart + callSiteTableLength;
226   const uint8_t *p = callSiteTableStart;
227   while (p < callSiteTableEnd) {
228     uintptr_t start = readEncodedPointer(&p, callSiteEncoding);
229     uintptr_t length = readEncodedPointer(&p, callSiteEncoding);
230     uintptr_t landingPad = readEncodedPointer(&p, callSiteEncoding);
231     readULEB128(&p); // action value not used for C code
232     if (landingPad == 0)
233       continue; // no landing pad for this entry
234     if ((start <= pcOffset) && (pcOffset < (start + length))) {
235       // Found landing pad for the PC.
236       // Set Instruction Pointer to so we re-enter function
237       // at landing pad. The landing pad is created by the compiler
238       // to take two parameters in registers.
239       _Unwind_SetGR(context, __builtin_eh_return_data_regno(0),
240                     (uintptr_t)exceptionObject);
241       _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0);
242       _Unwind_SetIP(context, (funcStart + landingPad));
243       return _URC_INSTALL_CONTEXT;
244     }
245   }
246 
247   // No landing pad found, continue unwinding.
248   return continueUnwind(exceptionObject, context);
249 }
250