master
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/* zig patch: remove compiler-rt int_lib.h dependency */
10#if __ARM_EABI__
11#ifdef COMPILER_RT_ARMHF_TARGET
12#define COMPILER_RT_ABI
13#else
14#define COMPILER_RT_ABI __attribute__((__pcs__("aapcs")))
15#endif
16#else
17#define COMPILER_RT_ABI
18#endif
19
20#define compilerrt_abort() __builtin_unreachable()
21
22#include <unwind.h>
23/* zig patch: remove unwind-ehabi-helpers.h dependency */
24
25#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__)
26#include <windows.h>
27#include <winnt.h>
28
29EXCEPTION_DISPOSITION _GCC_specific_handler(PEXCEPTION_RECORD, void *, PCONTEXT,
30 PDISPATCHER_CONTEXT,
31 _Unwind_Personality_Fn);
32#endif
33
34// Pointer encodings documented at:
35// http://refspecs.freestandards.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.html
36
37#define DW_EH_PE_omit 0xff // no data follows
38
39#define DW_EH_PE_absptr 0x00
40#define DW_EH_PE_uleb128 0x01
41#define DW_EH_PE_udata2 0x02
42#define DW_EH_PE_udata4 0x03
43#define DW_EH_PE_udata8 0x04
44#define DW_EH_PE_sleb128 0x09
45#define DW_EH_PE_sdata2 0x0A
46#define DW_EH_PE_sdata4 0x0B
47#define DW_EH_PE_sdata8 0x0C
48
49#define DW_EH_PE_pcrel 0x10
50#define DW_EH_PE_textrel 0x20
51#define DW_EH_PE_datarel 0x30
52#define DW_EH_PE_funcrel 0x40
53#define DW_EH_PE_aligned 0x50
54#define DW_EH_PE_indirect 0x80 // gcc extension
55
56// read a uleb128 encoded value and advance pointer
57static size_t readULEB128(const uint8_t **data) {
58 size_t result = 0;
59 size_t shift = 0;
60 unsigned char byte;
61 const uint8_t *p = *data;
62 do {
63 byte = *p++;
64 result |= (byte & 0x7f) << shift;
65 shift += 7;
66 } while (byte & 0x80);
67 *data = p;
68 return result;
69}
70
71// read a pointer encoded value and advance pointer
72static uintptr_t readEncodedPointer(const uint8_t **data, uint8_t encoding) {
73 const uint8_t *p = *data;
74 uintptr_t result = 0;
75
76 if (encoding == DW_EH_PE_omit)
77 return 0;
78
79 // first get value
80 switch (encoding & 0x0F) {
81 case DW_EH_PE_absptr:
82 result = *((const uintptr_t *)p);
83 p += sizeof(uintptr_t);
84 break;
85 case DW_EH_PE_uleb128:
86 result = readULEB128(&p);
87 break;
88 case DW_EH_PE_udata2:
89 result = *((const uint16_t *)p);
90 p += sizeof(uint16_t);
91 break;
92 case DW_EH_PE_udata4:
93 result = *((const uint32_t *)p);
94 p += sizeof(uint32_t);
95 break;
96 case DW_EH_PE_udata8:
97 result = *((const uint64_t *)p);
98 p += sizeof(uint64_t);
99 break;
100 case DW_EH_PE_sdata2:
101 result = *((const int16_t *)p);
102 p += sizeof(int16_t);
103 break;
104 case DW_EH_PE_sdata4:
105 result = *((const int32_t *)p);
106 p += sizeof(int32_t);
107 break;
108 case DW_EH_PE_sdata8:
109 result = *((const int64_t *)p);
110 p += sizeof(int64_t);
111 break;
112 case DW_EH_PE_sleb128:
113 default:
114 // not supported
115 compilerrt_abort();
116 break;
117 }
118
119 // then add relative offset
120 switch (encoding & 0x70) {
121 case DW_EH_PE_absptr:
122 // do nothing
123 break;
124 case DW_EH_PE_pcrel:
125 result += (uintptr_t)(*data);
126 break;
127 case DW_EH_PE_textrel:
128 case DW_EH_PE_datarel:
129 case DW_EH_PE_funcrel:
130 case DW_EH_PE_aligned:
131 default:
132 // not supported
133 compilerrt_abort();
134 break;
135 }
136
137 // then apply indirection
138 if (encoding & DW_EH_PE_indirect) {
139 result = *((const uintptr_t *)result);
140 }
141
142 *data = p;
143 return result;
144}
145
146#if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) && \
147 !defined(__ARM_DWARF_EH__) && !defined(__SEH__)
148#define USING_ARM_EHABI 1
149_Unwind_Reason_Code __gnu_unwind_frame(struct _Unwind_Exception *,
150 struct _Unwind_Context *);
151#endif
152
153static inline _Unwind_Reason_Code
154continueUnwind(struct _Unwind_Exception *exceptionObject,
155 struct _Unwind_Context *context) {
156#if USING_ARM_EHABI
157 // On ARM EHABI the personality routine is responsible for actually
158 // unwinding a single stack frame before returning (ARM EHABI Sec. 6.1).
159 if (__gnu_unwind_frame(exceptionObject, context) != _URC_OK)
160 return _URC_FAILURE;
161#endif
162 return _URC_CONTINUE_UNWIND;
163}
164
165// The C compiler makes references to __gcc_personality_v0 in
166// the dwarf unwind information for translation units that use
167// __attribute__((cleanup(xx))) on local variables.
168// This personality routine is called by the system unwinder
169// on each frame as the stack is unwound during a C++ exception
170// throw through a C function compiled with -fexceptions.
171#if __USING_SJLJ_EXCEPTIONS__
172// the setjump-longjump based exceptions personality routine has a
173// different name
174COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_sj0(
175 int version, _Unwind_Action actions, uint64_t exceptionClass,
176 struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context)
177#elif USING_ARM_EHABI
178// The ARM EHABI personality routine has a different signature.
179COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0(
180 _Unwind_State state, struct _Unwind_Exception *exceptionObject,
181 struct _Unwind_Context *context)
182#elif defined(__SEH__)
183static _Unwind_Reason_Code __gcc_personality_imp(
184 int version, _Unwind_Action actions, uint64_t exceptionClass,
185 struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context)
186#else
187COMPILER_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 size_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 size_t length = readEncodedPointer(&p, callSiteEncoding);
230 size_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
251#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__)
252COMPILER_RT_ABI EXCEPTION_DISPOSITION
253__gcc_personality_seh0(PEXCEPTION_RECORD ms_exc, void *this_frame,
254 PCONTEXT ms_orig_context, PDISPATCHER_CONTEXT ms_disp) {
255 return _GCC_specific_handler(ms_exc, this_frame, ms_orig_context, ms_disp,
256 __gcc_personality_imp);
257}
258#endif