150
|
1 //===--------------------- UnwindLevel1-gcc-ext.c -------------------------===//
|
|
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 // Implements gcc extensions to the C++ ABI Exception Handling Level 1.
|
|
9 //
|
|
10 //===----------------------------------------------------------------------===//
|
|
11
|
|
12 #include <inttypes.h>
|
|
13 #include <stdbool.h>
|
|
14 #include <stdint.h>
|
|
15 #include <stdio.h>
|
|
16 #include <stdlib.h>
|
|
17 #include <string.h>
|
|
18
|
|
19 #include "config.h"
|
|
20 #include "libunwind_ext.h"
|
|
21 #include "libunwind.h"
|
|
22 #include "Unwind-EHABI.h"
|
|
23 #include "unwind.h"
|
|
24
|
|
25 #if defined(_LIBUNWIND_BUILD_ZERO_COST_APIS)
|
|
26
|
|
27 #if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND)
|
|
28 #define private_1 private_[0]
|
|
29 #endif
|
|
30
|
|
31 /// Called by __cxa_rethrow().
|
|
32 _LIBUNWIND_EXPORT _Unwind_Reason_Code
|
|
33 _Unwind_Resume_or_Rethrow(_Unwind_Exception *exception_object) {
|
|
34 #if defined(_LIBUNWIND_ARM_EHABI)
|
|
35 _LIBUNWIND_TRACE_API("_Unwind_Resume_or_Rethrow(ex_obj=%p), private_1=%ld",
|
|
36 (void *)exception_object,
|
|
37 (long)exception_object->unwinder_cache.reserved1);
|
|
38 #else
|
|
39 _LIBUNWIND_TRACE_API("_Unwind_Resume_or_Rethrow(ex_obj=%p), private_1=%" PRIdPTR,
|
|
40 (void *)exception_object,
|
|
41 (intptr_t)exception_object->private_1);
|
|
42 #endif
|
|
43
|
|
44 #if defined(_LIBUNWIND_ARM_EHABI)
|
|
45 // _Unwind_RaiseException on EHABI will always set the reserved1 field to 0,
|
|
46 // which is in the same position as private_1 below.
|
|
47 return _Unwind_RaiseException(exception_object);
|
|
48 #else
|
|
49 // If this is non-forced and a stopping place was found, then this is a
|
|
50 // re-throw.
|
|
51 // Call _Unwind_RaiseException() as if this was a new exception
|
|
52 if (exception_object->private_1 == 0) {
|
|
53 return _Unwind_RaiseException(exception_object);
|
|
54 // Will return if there is no catch clause, so that __cxa_rethrow can call
|
|
55 // std::terminate().
|
|
56 }
|
|
57
|
|
58 // Call through to _Unwind_Resume() which distiguishes between forced and
|
|
59 // regular exceptions.
|
|
60 _Unwind_Resume(exception_object);
|
|
61 _LIBUNWIND_ABORT("_Unwind_Resume_or_Rethrow() called _Unwind_RaiseException()"
|
|
62 " which unexpectedly returned");
|
|
63 #endif
|
|
64 }
|
|
65
|
|
66
|
|
67 /// Called by personality handler during phase 2 to get base address for data
|
|
68 /// relative encodings.
|
|
69 _LIBUNWIND_EXPORT uintptr_t
|
|
70 _Unwind_GetDataRelBase(struct _Unwind_Context *context) {
|
|
71 (void)context;
|
|
72 _LIBUNWIND_TRACE_API("_Unwind_GetDataRelBase(context=%p)", (void *)context);
|
|
73 _LIBUNWIND_ABORT("_Unwind_GetDataRelBase() not implemented");
|
|
74 }
|
|
75
|
|
76
|
|
77 /// Called by personality handler during phase 2 to get base address for text
|
|
78 /// relative encodings.
|
|
79 _LIBUNWIND_EXPORT uintptr_t
|
|
80 _Unwind_GetTextRelBase(struct _Unwind_Context *context) {
|
|
81 (void)context;
|
|
82 _LIBUNWIND_TRACE_API("_Unwind_GetTextRelBase(context=%p)", (void *)context);
|
|
83 _LIBUNWIND_ABORT("_Unwind_GetTextRelBase() not implemented");
|
|
84 }
|
|
85
|
|
86
|
|
87 /// Scans unwind information to find the function that contains the
|
|
88 /// specified code address "pc".
|
|
89 _LIBUNWIND_EXPORT void *_Unwind_FindEnclosingFunction(void *pc) {
|
|
90 _LIBUNWIND_TRACE_API("_Unwind_FindEnclosingFunction(pc=%p)", pc);
|
|
91 // This is slow, but works.
|
|
92 // We create an unwind cursor then alter the IP to be pc
|
|
93 unw_cursor_t cursor;
|
|
94 unw_context_t uc;
|
|
95 unw_proc_info_t info;
|
|
96 __unw_getcontext(&uc);
|
|
97 __unw_init_local(&cursor, &uc);
|
|
98 __unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)(intptr_t)pc);
|
|
99 if (__unw_get_proc_info(&cursor, &info) == UNW_ESUCCESS)
|
|
100 return (void *)(intptr_t) info.start_ip;
|
|
101 else
|
|
102 return NULL;
|
|
103 }
|
|
104
|
|
105 /// Walk every frame and call trace function at each one. If trace function
|
|
106 /// returns anything other than _URC_NO_REASON, then walk is terminated.
|
|
107 _LIBUNWIND_EXPORT _Unwind_Reason_Code
|
|
108 _Unwind_Backtrace(_Unwind_Trace_Fn callback, void *ref) {
|
|
109 unw_cursor_t cursor;
|
|
110 unw_context_t uc;
|
|
111 __unw_getcontext(&uc);
|
|
112 __unw_init_local(&cursor, &uc);
|
|
113
|
|
114 _LIBUNWIND_TRACE_API("_Unwind_Backtrace(callback=%p)",
|
|
115 (void *)(uintptr_t)callback);
|
|
116
|
|
117 #if defined(_LIBUNWIND_ARM_EHABI)
|
|
118 // Create a mock exception object for force unwinding.
|
|
119 _Unwind_Exception ex;
|
|
120 memset(&ex, '\0', sizeof(ex));
|
|
121 ex.exception_class = 0x434C4E47554E5700; // CLNGUNW\0
|
|
122 #endif
|
|
123
|
|
124 // walk each frame
|
|
125 while (true) {
|
|
126 _Unwind_Reason_Code result;
|
|
127
|
|
128 #if !defined(_LIBUNWIND_ARM_EHABI)
|
|
129 // ask libunwind to get next frame (skip over first frame which is
|
|
130 // _Unwind_Backtrace())
|
|
131 if (__unw_step(&cursor) <= 0) {
|
|
132 _LIBUNWIND_TRACE_UNWINDING(" _backtrace: ended because cursor reached "
|
|
133 "bottom of stack, returning %d",
|
|
134 _URC_END_OF_STACK);
|
|
135 return _URC_END_OF_STACK;
|
|
136 }
|
|
137 #else
|
|
138 // Get the information for this frame.
|
|
139 unw_proc_info_t frameInfo;
|
|
140 if (__unw_get_proc_info(&cursor, &frameInfo) != UNW_ESUCCESS) {
|
|
141 return _URC_END_OF_STACK;
|
|
142 }
|
|
143
|
|
144 // Update the pr_cache in the mock exception object.
|
|
145 const uint32_t* unwindInfo = (uint32_t *) frameInfo.unwind_info;
|
|
146 ex.pr_cache.fnstart = frameInfo.start_ip;
|
|
147 ex.pr_cache.ehtp = (_Unwind_EHT_Header *) unwindInfo;
|
|
148 ex.pr_cache.additional= frameInfo.flags;
|
|
149
|
|
150 struct _Unwind_Context *context = (struct _Unwind_Context *)&cursor;
|
|
151 // Get and call the personality function to unwind the frame.
|
|
152 _Unwind_Personality_Fn handler = (_Unwind_Personality_Fn)frameInfo.handler;
|
|
153 if (handler == NULL) {
|
|
154 return _URC_END_OF_STACK;
|
|
155 }
|
|
156 if (handler(_US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND, &ex, context) !=
|
|
157 _URC_CONTINUE_UNWIND) {
|
|
158 return _URC_END_OF_STACK;
|
|
159 }
|
|
160 #endif // defined(_LIBUNWIND_ARM_EHABI)
|
|
161
|
|
162 // debugging
|
|
163 if (_LIBUNWIND_TRACING_UNWINDING) {
|
|
164 char functionName[512];
|
|
165 unw_proc_info_t frame;
|
|
166 unw_word_t offset;
|
|
167 __unw_get_proc_name(&cursor, functionName, 512, &offset);
|
|
168 __unw_get_proc_info(&cursor, &frame);
|
|
169 _LIBUNWIND_TRACE_UNWINDING(
|
|
170 " _backtrace: start_ip=0x%" PRIxPTR ", func=%s, lsda=0x%" PRIxPTR ", context=%p",
|
|
171 frame.start_ip, functionName, frame.lsda,
|
|
172 (void *)&cursor);
|
|
173 }
|
|
174
|
|
175 // call trace function with this frame
|
|
176 result = (*callback)((struct _Unwind_Context *)(&cursor), ref);
|
|
177 if (result != _URC_NO_REASON) {
|
|
178 _LIBUNWIND_TRACE_UNWINDING(
|
|
179 " _backtrace: ended because callback returned %d", result);
|
|
180 return result;
|
|
181 }
|
|
182 }
|
|
183 }
|
|
184
|
|
185
|
|
186 /// Find DWARF unwind info for an address 'pc' in some function.
|
|
187 _LIBUNWIND_EXPORT const void *_Unwind_Find_FDE(const void *pc,
|
|
188 struct dwarf_eh_bases *bases) {
|
|
189 // This is slow, but works.
|
|
190 // We create an unwind cursor then alter the IP to be pc
|
|
191 unw_cursor_t cursor;
|
|
192 unw_context_t uc;
|
|
193 unw_proc_info_t info;
|
|
194 __unw_getcontext(&uc);
|
|
195 __unw_init_local(&cursor, &uc);
|
|
196 __unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)(intptr_t)pc);
|
|
197 __unw_get_proc_info(&cursor, &info);
|
|
198 bases->tbase = (uintptr_t)info.extra;
|
|
199 bases->dbase = 0; // dbase not used on Mac OS X
|
|
200 bases->func = (uintptr_t)info.start_ip;
|
|
201 _LIBUNWIND_TRACE_API("_Unwind_Find_FDE(pc=%p) => %p", pc,
|
|
202 (void *)(intptr_t) info.unwind_info);
|
|
203 return (void *)(intptr_t) info.unwind_info;
|
|
204 }
|
|
205
|
|
206 /// Returns the CFA (call frame area, or stack pointer at start of function)
|
|
207 /// for the current context.
|
|
208 _LIBUNWIND_EXPORT uintptr_t _Unwind_GetCFA(struct _Unwind_Context *context) {
|
|
209 unw_cursor_t *cursor = (unw_cursor_t *)context;
|
|
210 unw_word_t result;
|
|
211 __unw_get_reg(cursor, UNW_REG_SP, &result);
|
|
212 _LIBUNWIND_TRACE_API("_Unwind_GetCFA(context=%p) => 0x%" PRIxPTR,
|
|
213 (void *)context, result);
|
|
214 return (uintptr_t)result;
|
|
215 }
|
|
216
|
|
217
|
|
218 /// Called by personality handler during phase 2 to get instruction pointer.
|
|
219 /// ipBefore is a boolean that says if IP is already adjusted to be the call
|
|
220 /// site address. Normally IP is the return address.
|
|
221 _LIBUNWIND_EXPORT uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context *context,
|
|
222 int *ipBefore) {
|
|
223 _LIBUNWIND_TRACE_API("_Unwind_GetIPInfo(context=%p)", (void *)context);
|
|
224 int isSignalFrame = __unw_is_signal_frame((unw_cursor_t *)context);
|
|
225 // Negative means some kind of error (probably UNW_ENOINFO), but we have no
|
|
226 // good way to report that, and this maintains backward compatibility with the
|
|
227 // implementation that hard-coded zero in every case, even signal frames.
|
|
228 if (isSignalFrame <= 0)
|
|
229 *ipBefore = 0;
|
|
230 else
|
|
231 *ipBefore = 1;
|
|
232 return _Unwind_GetIP(context);
|
|
233 }
|
|
234
|
|
235 #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
|
|
236
|
|
237 /// Called by programs with dynamic code generators that want
|
|
238 /// to register a dynamically generated FDE.
|
|
239 /// This function has existed on Mac OS X since 10.4, but
|
|
240 /// was broken until 10.6.
|
|
241 _LIBUNWIND_EXPORT void __register_frame(const void *fde) {
|
|
242 _LIBUNWIND_TRACE_API("__register_frame(%p)", fde);
|
|
243 __unw_add_dynamic_fde((unw_word_t)(uintptr_t)fde);
|
|
244 }
|
|
245
|
|
246
|
|
247 /// Called by programs with dynamic code generators that want
|
|
248 /// to unregister a dynamically generated FDE.
|
|
249 /// This function has existed on Mac OS X since 10.4, but
|
|
250 /// was broken until 10.6.
|
|
251 _LIBUNWIND_EXPORT void __deregister_frame(const void *fde) {
|
|
252 _LIBUNWIND_TRACE_API("__deregister_frame(%p)", fde);
|
|
253 __unw_remove_dynamic_fde((unw_word_t)(uintptr_t)fde);
|
|
254 }
|
|
255
|
|
256
|
|
257 // The following register/deregister functions are gcc extensions.
|
|
258 // They have existed on Mac OS X, but have never worked because Mac OS X
|
|
259 // before 10.6 used keymgr to track known FDEs, but these functions
|
|
260 // never got updated to use keymgr.
|
|
261 // For now, we implement these as do-nothing functions to keep any existing
|
|
262 // applications working. We also add the not in 10.6 symbol so that nwe
|
|
263 // application won't be able to use them.
|
|
264
|
|
265 #if defined(_LIBUNWIND_SUPPORT_FRAME_APIS)
|
|
266 _LIBUNWIND_EXPORT void __register_frame_info_bases(const void *fde, void *ob,
|
|
267 void *tb, void *db) {
|
|
268 (void)fde;
|
|
269 (void)ob;
|
|
270 (void)tb;
|
|
271 (void)db;
|
|
272 _LIBUNWIND_TRACE_API("__register_frame_info_bases(%p,%p, %p, %p)",
|
|
273 fde, ob, tb, db);
|
|
274 // do nothing, this function never worked in Mac OS X
|
|
275 }
|
|
276
|
|
277 _LIBUNWIND_EXPORT void __register_frame_info(const void *fde, void *ob) {
|
|
278 (void)fde;
|
|
279 (void)ob;
|
|
280 _LIBUNWIND_TRACE_API("__register_frame_info(%p, %p)", fde, ob);
|
|
281 // do nothing, this function never worked in Mac OS X
|
|
282 }
|
|
283
|
|
284 _LIBUNWIND_EXPORT void __register_frame_info_table_bases(const void *fde,
|
|
285 void *ob, void *tb,
|
|
286 void *db) {
|
|
287 (void)fde;
|
|
288 (void)ob;
|
|
289 (void)tb;
|
|
290 (void)db;
|
|
291 _LIBUNWIND_TRACE_API("__register_frame_info_table_bases"
|
|
292 "(%p,%p, %p, %p)", fde, ob, tb, db);
|
|
293 // do nothing, this function never worked in Mac OS X
|
|
294 }
|
|
295
|
|
296 _LIBUNWIND_EXPORT void __register_frame_info_table(const void *fde, void *ob) {
|
|
297 (void)fde;
|
|
298 (void)ob;
|
|
299 _LIBUNWIND_TRACE_API("__register_frame_info_table(%p, %p)", fde, ob);
|
|
300 // do nothing, this function never worked in Mac OS X
|
|
301 }
|
|
302
|
|
303 _LIBUNWIND_EXPORT void __register_frame_table(const void *fde) {
|
|
304 (void)fde;
|
|
305 _LIBUNWIND_TRACE_API("__register_frame_table(%p)", fde);
|
|
306 // do nothing, this function never worked in Mac OS X
|
|
307 }
|
|
308
|
|
309 _LIBUNWIND_EXPORT void *__deregister_frame_info(const void *fde) {
|
|
310 (void)fde;
|
|
311 _LIBUNWIND_TRACE_API("__deregister_frame_info(%p)", fde);
|
|
312 // do nothing, this function never worked in Mac OS X
|
|
313 return NULL;
|
|
314 }
|
|
315
|
|
316 _LIBUNWIND_EXPORT void *__deregister_frame_info_bases(const void *fde) {
|
|
317 (void)fde;
|
|
318 _LIBUNWIND_TRACE_API("__deregister_frame_info_bases(%p)", fde);
|
|
319 // do nothing, this function never worked in Mac OS X
|
|
320 return NULL;
|
|
321 }
|
|
322 #endif // defined(_LIBUNWIND_SUPPORT_FRAME_APIS)
|
|
323
|
|
324 #endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
|
|
325
|
|
326 #endif // defined(_LIBUNWIND_BUILD_ZERO_COST_APIS)
|