]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm-project/compiler-rt/lib/builtins/gcc_personality_v0.c
Reapply r230021, r276851 and a few other commits to compiler-rt
[FreeBSD/FreeBSD.git] / contrib / llvm-project / compiler-rt / lib / builtins / gcc_personality_v0.c
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 #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"
21 #endif
22
23 // Pointer encodings documented at:
24 //   http://refspecs.freestandards.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.html
25
26 #define DW_EH_PE_omit 0xff // no data follows
27
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
37
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
44
45 // read a uleb128 encoded value and advance pointer
46 static uintptr_t readULEB128(const uint8_t **data) {
47   uintptr_t result = 0;
48   uintptr_t shift = 0;
49   unsigned char byte;
50   const uint8_t *p = *data;
51   do {
52     byte = *p++;
53     result |= (byte & 0x7f) << shift;
54     shift += 7;
55   } while (byte & 0x80);
56   *data = p;
57   return result;
58 }
59
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;
63   uintptr_t result = 0;
64
65   if (encoding == DW_EH_PE_omit)
66     return 0;
67
68   // first get value
69   switch (encoding & 0x0F) {
70   case DW_EH_PE_absptr:
71     result = *((const uintptr_t *)p);
72     p += sizeof(uintptr_t);
73     break;
74   case DW_EH_PE_uleb128:
75     result = readULEB128(&p);
76     break;
77   case DW_EH_PE_udata2:
78     result = *((const uint16_t *)p);
79     p += sizeof(uint16_t);
80     break;
81   case DW_EH_PE_udata4:
82     result = *((const uint32_t *)p);
83     p += sizeof(uint32_t);
84     break;
85   case DW_EH_PE_udata8:
86     result = *((const uint64_t *)p);
87     p += sizeof(uint64_t);
88     break;
89   case DW_EH_PE_sdata2:
90     result = *((const int16_t *)p);
91     p += sizeof(int16_t);
92     break;
93   case DW_EH_PE_sdata4:
94     result = *((const int32_t *)p);
95     p += sizeof(int32_t);
96     break;
97   case DW_EH_PE_sdata8:
98     result = *((const int64_t *)p);
99     p += sizeof(int64_t);
100     break;
101   case DW_EH_PE_sleb128:
102   default:
103     // not supported
104     compilerrt_abort();
105     break;
106   }
107
108   // then add relative offset
109   switch (encoding & 0x70) {
110   case DW_EH_PE_absptr:
111     // do nothing
112     break;
113   case DW_EH_PE_pcrel:
114     result += (uintptr_t)(*data);
115     break;
116   case DW_EH_PE_textrel:
117   case DW_EH_PE_datarel:
118   case DW_EH_PE_funcrel:
119   case DW_EH_PE_aligned:
120   default:
121     // not supported
122     compilerrt_abort();
123     break;
124   }
125
126   // then apply indirection
127   if (encoding & DW_EH_PE_indirect) {
128     result = *((const uintptr_t *)result);
129   }
130
131   *data = p;
132   return result;
133 }
134
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 *);
140 #endif
141
142 static inline _Unwind_Reason_Code
143 continueUnwind(struct _Unwind_Exception *exceptionObject,
144                struct _Unwind_Context *context) {
145 #if USING_ARM_EHABI
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)
149     return _URC_FAILURE;
150 #endif
151   return _URC_CONTINUE_UNWIND;
152 }
153
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
162 // different name
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)
171 #else
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)
175 #endif
176 {
177   // Since C does not have catch clauses, there is nothing to do during
178   // phase 1 (the search phase).
179 #if USING_ARM_EHABI
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)
183 #else
184   if (actions & _UA_SEARCH_PHASE)
185 #endif
186     return continueUnwind(exceptionObject, context);
187
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);
192
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;
196
197   // Parse LSDA header.
198   uint8_t lpStartEncoding = *lsda++;
199   if (lpStartEncoding != DW_EH_PE_omit) {
200     readEncodedPointer(&lsda, lpStartEncoding);
201   }
202   uint8_t ttypeEncoding = *lsda++;
203   if (ttypeEncoding != DW_EH_PE_omit) {
204     readULEB128(&lsda);
205   }
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
217     if (landingPad == 0)
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;
229     }
230   }
231
232   // No landing pad found, continue unwinding.
233   return continueUnwind(exceptionObject, context);
234 }