1 //===-- gcc_personality_v0.c - Implement __gcc_personality_v0 -------------===//
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
7 //===----------------------------------------------------------------------===//
12 #if defined(__arm__) && !defined(__ARM_DWARF_EH__) && \
13 !defined(__USING_SJLJ_EXCEPTIONS__)
14 // When building with older compilers (e.g. clang <3.9), it is possible that we
15 // have a version of unwind.h which does not provide the EHABI declarations
16 // which are quired for the C personality to conform to the specification. In
17 // order to provide forward compatibility for such compilers, we re-declare the
18 // necessary interfaces in the helper to permit a standalone compilation of the
19 // builtins (which contains the C unwinding personality for historical reasons).
20 #include "unwind-ehabi-helpers.h"
23 // Pointer encodings documented at:
24 // http://refspecs.freestandards.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.html
26 #define DW_EH_PE_omit 0xff // no data follows
28 #define DW_EH_PE_absptr 0x00
29 #define DW_EH_PE_uleb128 0x01
30 #define DW_EH_PE_udata2 0x02
31 #define DW_EH_PE_udata4 0x03
32 #define DW_EH_PE_udata8 0x04
33 #define DW_EH_PE_sleb128 0x09
34 #define DW_EH_PE_sdata2 0x0A
35 #define DW_EH_PE_sdata4 0x0B
36 #define DW_EH_PE_sdata8 0x0C
38 #define DW_EH_PE_pcrel 0x10
39 #define DW_EH_PE_textrel 0x20
40 #define DW_EH_PE_datarel 0x30
41 #define DW_EH_PE_funcrel 0x40
42 #define DW_EH_PE_aligned 0x50
43 #define DW_EH_PE_indirect 0x80 // gcc extension
45 // read a uleb128 encoded value and advance pointer
46 static uintptr_t readULEB128(const uint8_t **data) {
50 const uint8_t *p = *data;
53 result |= (byte & 0x7f) << shift;
55 } while (byte & 0x80);
60 // read a pointer encoded value and advance pointer
61 static uintptr_t readEncodedPointer(const uint8_t **data, uint8_t encoding) {
62 const uint8_t *p = *data;
65 if (encoding == DW_EH_PE_omit)
69 switch (encoding & 0x0F) {
71 result = *((const uintptr_t *)p);
72 p += sizeof(uintptr_t);
74 case DW_EH_PE_uleb128:
75 result = readULEB128(&p);
78 result = *((const uint16_t *)p);
79 p += sizeof(uint16_t);
82 result = *((const uint32_t *)p);
83 p += sizeof(uint32_t);
86 result = *((const uint64_t *)p);
87 p += sizeof(uint64_t);
90 result = *((const int16_t *)p);
94 result = *((const int32_t *)p);
98 result = *((const int64_t *)p);
101 case DW_EH_PE_sleb128:
108 // then add relative offset
109 switch (encoding & 0x70) {
110 case DW_EH_PE_absptr:
114 result += (uintptr_t)(*data);
116 case DW_EH_PE_textrel:
117 case DW_EH_PE_datarel:
118 case DW_EH_PE_funcrel:
119 case DW_EH_PE_aligned:
126 // then apply indirection
127 if (encoding & DW_EH_PE_indirect) {
128 result = *((const uintptr_t *)result);
135 #if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) && \
136 !defined(__ARM_DWARF_EH__)
137 #define USING_ARM_EHABI 1
138 _Unwind_Reason_Code __gnu_unwind_frame(struct _Unwind_Exception *,
139 struct _Unwind_Context *);
142 static inline _Unwind_Reason_Code
143 continueUnwind(struct _Unwind_Exception *exceptionObject,
144 struct _Unwind_Context *context) {
146 // On ARM EHABI the personality routine is responsible for actually
147 // unwinding a single stack frame before returning (ARM EHABI Sec. 6.1).
148 if (__gnu_unwind_frame(exceptionObject, context) != _URC_OK)
151 return _URC_CONTINUE_UNWIND;
154 // The C compiler makes references to __gcc_personality_v0 in
155 // the dwarf unwind information for translation units that use
156 // __attribute__((cleanup(xx))) on local variables.
157 // This personality routine is called by the system unwinder
158 // on each frame as the stack is unwound during a C++ exception
159 // throw through a C function compiled with -fexceptions.
160 #if __USING_SJLJ_EXCEPTIONS__
161 // the setjump-longjump based exceptions personality routine has a
163 COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_sj0(
164 int version, _Unwind_Action actions, uint64_t exceptionClass,
165 struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context)
166 #elif USING_ARM_EHABI
167 // The ARM EHABI personality routine has a different signature.
168 COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0(
169 _Unwind_State state, struct _Unwind_Exception *exceptionObject,
170 struct _Unwind_Context *context)
172 COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0(
173 int version, _Unwind_Action actions, uint64_t exceptionClass,
174 struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context)
177 // Since C does not have catch clauses, there is nothing to do during
178 // phase 1 (the search phase).
180 // After resuming from a cleanup we should also continue on to the next
181 // frame straight away.
182 if ((state & _US_ACTION_MASK) != _US_UNWIND_FRAME_STARTING)
184 if (actions & _UA_SEARCH_PHASE)
186 return continueUnwind(exceptionObject, context);
188 // There is nothing to do if there is no LSDA for this frame.
189 const uint8_t *lsda = (uint8_t *)_Unwind_GetLanguageSpecificData(context);
190 if (lsda == (uint8_t *)0)
191 return continueUnwind(exceptionObject, context);
193 uintptr_t pc = (uintptr_t)_Unwind_GetIP(context) - 1;
194 uintptr_t funcStart = (uintptr_t)_Unwind_GetRegionStart(context);
195 uintptr_t pcOffset = pc - funcStart;
197 // Parse LSDA header.
198 uint8_t lpStartEncoding = *lsda++;
199 if (lpStartEncoding != DW_EH_PE_omit) {
200 readEncodedPointer(&lsda, lpStartEncoding);
202 uint8_t ttypeEncoding = *lsda++;
203 if (ttypeEncoding != DW_EH_PE_omit) {
206 // Walk call-site table looking for range that includes current PC.
207 uint8_t callSiteEncoding = *lsda++;
208 uint32_t callSiteTableLength = readULEB128(&lsda);
209 const uint8_t *callSiteTableStart = lsda;
210 const uint8_t *callSiteTableEnd = callSiteTableStart + callSiteTableLength;
211 const uint8_t *p = callSiteTableStart;
212 while (p < callSiteTableEnd) {
213 uintptr_t start = readEncodedPointer(&p, callSiteEncoding);
214 uintptr_t length = readEncodedPointer(&p, callSiteEncoding);
215 uintptr_t landingPad = readEncodedPointer(&p, callSiteEncoding);
216 readULEB128(&p); // action value not used for C code
218 continue; // no landing pad for this entry
219 if ((start <= pcOffset) && (pcOffset < (start + length))) {
220 // Found landing pad for the PC.
221 // Set Instruction Pointer to so we re-enter function
222 // at landing pad. The landing pad is created by the compiler
223 // to take two parameters in registers.
224 _Unwind_SetGR(context, __builtin_eh_return_data_regno(0),
225 (uintptr_t)exceptionObject);
226 _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0);
227 _Unwind_SetIP(context, (funcStart + landingPad));
228 return _URC_INSTALL_CONTEXT;
232 // No landing pad found, continue unwinding.
233 return continueUnwind(exceptionObject, context);