Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(961)

Side by Side Diff: src/cxa_pnacl_sjlj_exception.cpp

Issue 95613004: Add support for exception handling using the PNaClSjLjEH LLVM pass (Closed) Base URL: https://llvm.org/svn/llvm-project/libcxxabi/trunk
Patch Set: Fix handling of exceptions with virtual base classes Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 //===- cxa_pnacl_sjlj_exception.cpp - PNaCl SJLJ-based exception handling--===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is dual licensed under the MIT and the University of Illinois Open
6 // Source Licenses. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This implements setjmp()/longjmp()-based (SJLJ) C++ exception
11 // handling for PNaCl. This uses the C++ exception info tables
12 // generated by the PNaClSjLjEH LLVM pass.
13 //
14 // Each __pnacl_eh_sjlj_Unwind_*() function below provides the
15 // definition of _Unwind_*().
16 //
17 // The "__pnacl_eh_sjlj" prefix is added so that PNaCl's SJLJ
18 // (setjmp()/longjmp()-based) implementation of C++ exception handling
19 // can coexist with other implementations in the same build of
20 // libc++/libcxxabi. When SJLJ EH is enabled, each
21 // __pnacl_eh_sjlj_Unwind_*() symbol will get renamed to _Unwind_*()
22 // when linking a PNaCl pexe.
23 //
24 //===----------------------------------------------------------------------===//
25
26 #include <setjmp.h>
27 #include <stdint.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <typeinfo>
31
32 #include "cxa_exception.hpp"
33 #include "cxa_handlers.hpp"
34 #include "private_typeinfo.h"
35
36 using namespace __cxxabiv1;
37
38
39 // Exception info written by ExceptionInfoWriter.cpp.
40
41 struct action_table_entry {
42 int32_t clause_id;
43 uint32_t next_clause_list_id;
44 };
45
46 extern const struct action_table_entry __pnacl_eh_action_table[];
47 extern const __shim_type_info *const __pnacl_eh_type_table[];
48 extern const int32_t __pnacl_eh_filter_table[];
49
50 // Data structures used by PNaClSjLjEH.cpp.
51
52 struct landing_pad_result {
53 void *exception_obj;
54 uint32_t matched_clause_id;
55 };
56
57 struct exception_frame {
58 union {
59 jmp_buf jmpbuf;
60 struct landing_pad_result result;
61 };
62 struct exception_frame *next;
63 uint32_t clause_list_id;
64 };
65
66 __thread struct exception_frame *__pnacl_eh_stack;
67
68
69 // Returns whether the thrown exception (specified by throw_type and
70 // obj) matches none of the exception types in a C++ exception
71 // specification (specified by filter_id).
72 static bool
73 exception_spec_can_catch(const __shim_type_info *throw_type, void *obj,
74 int32_t filter_id)
75 {
76 const int32_t *filter_ptr =
77 &__pnacl_eh_filter_table[-filter_id - 1];
78 for (; *filter_ptr != 0; ++filter_ptr)
79 {
80 const __shim_type_info *catch_type =
81 __pnacl_eh_type_table[*filter_ptr - 1];
82 // We ignore the modified value of obj here.
83 if (catch_type->can_catch(throw_type, obj))
84 return false;
85 }
86 // No type matched, so we have an exception specification error.
87 return true;
88 }
89
90 // Returns whether the thrown exception (specified by throw_type and
91 // obj) matches the given landingpad clause (clause_id).
92 //
93 // If the exception matches and the clause is a "catch" clause, this
94 // adjusts *obj to upcast it to the type specified in the "catch"
95 // clause. (For example, if throw_type uses multiple inheritance and
96 // derives from multiple base classes, this might involve adding a
97 // constant offset to *obj.)
98 static bool
99 does_clause_match(const __shim_type_info *throw_type, void **obj,
100 int32_t clause_id)
101 {
102 // Handle "cleanup" clause.
103 if (clause_id == 0)
104 return true;
105
106 // Handle "filter" clause.
107 if (clause_id < 0)
108 return exception_spec_can_catch(throw_type, obj, clause_id);
109
110 // Handle "catch" clause.
111 const __shim_type_info *catch_type = __pnacl_eh_type_table[clause_id - 1];
112 if (catch_type == NULL)
113 return true;
114 return catch_type->can_catch(throw_type, *obj);
115 }
116
117 // Returns whether the given frame should be entered in order to
118 // handle the thrown exception (specified by throw_type and *obj). If
119 // so, this adjusts *obj (see does_clause_match()) and sets
120 // *result_clause_id.
121 static bool
122 does_frame_match(const __shim_type_info *throw_type, void **obj,
123 struct exception_frame *frame, int32_t *result_clause_id)
124 {
125 for (int32_t clause_list_id = frame->clause_list_id; clause_list_id != 0; )
126 {
127 const struct action_table_entry *list_node =
128 &__pnacl_eh_action_table[clause_list_id - 1];
129 if (does_clause_match(throw_type, obj, list_node->clause_id))
130 {
131 *result_clause_id = list_node->clause_id;
132 return true;
133 }
134 clause_list_id = list_node->next_clause_list_id;
135 }
136 return false;
137 }
138
139 // Search for a stack frame that will handle the given exception,
140 // starting from frame. The exception is specified by throw_type and
141 // *obj.
142 //
143 // If a frame is found that will handle the exception, this adjusts
144 // *obj (to upcast it to the "catch" type, if there is one), sets
145 // *result_frame and *result_clause_id to the frame and clause ID that
146 // matched the exception, and returns true.
147 static bool
148 find_match(const __shim_type_info *throw_type, void **obj,
149 struct exception_frame *frame,
150 struct exception_frame **result_frame, int32_t *result_clause_id)
151 {
152 for (; frame != NULL; frame = frame->next)
153 {
154 if (does_frame_match(throw_type, obj, frame, result_clause_id))
155 {
156 *result_frame = frame;
157 return true;
158 }
159 }
160 return false;
161 }
162
163 // Search for a non-cleanup stack frame that will handle the given
164 // exception, starting from frame. Returns whether a matching frame
165 // was found.
166 static bool
167 is_exception_caught(const __shim_type_info *throw_type, void *obj,
168 struct exception_frame *frame)
169 {
170 for (; frame != NULL; frame = frame->next)
171 {
172 int32_t clause_id;
173 if (does_frame_match(throw_type, &obj, frame, &clause_id)
174 && clause_id != 0)
175 return true;
176 }
177 return false;
178 }
179
180 static __cxa_exception *
181 get_exception_header_from_ue(struct _Unwind_Exception *ue_header)
182 {
183 return (__cxa_exception *) (ue_header + 1) - 1;
184 }
185
186 static __cxa_dependent_exception *
187 get_dependent_exception_from_ue(struct _Unwind_Exception *ue_header)
188 {
189 return (__cxa_dependent_exception *) (ue_header + 1) - 1;
190 }
191
192 static void *
193 get_object_from_ue(struct _Unwind_Exception *ue_header)
194 {
195 if (ue_header->exception_class == kOurDependentExceptionClass)
196 {
197 return get_dependent_exception_from_ue(ue_header)->primaryException;
198 }
199 return ue_header + 1;
200 }
201
202 // handle_exception() is called by _Unwind_RaiseException(). It
203 // unwinds the stack, looking for the first C++ destructor or catch()
204 // block to pass control to. In LLVM terms, it searches for the first
205 // matching invoke/landingpad instruction. When it finds a match,
206 // this implementation passes control to the landingpad block by
207 // longjmp()'ing to it.
208 //
209 // In a traditional implementation (based on the Itanium ABI),
210 // _Unwind_RaiseException() is implemented in a library (libgcc_eh)
211 // that is separate from libcxxabi. It calls back to libcxxabi's
212 // personality function (__gxx_personality_v0()) to determine whether
213 // a call on the stack has a handler for the exception.
214 // __gxx_personality_v0() knows that ue_header was allocated by
215 // libcxxabi's __cxa_allocate_exception() and can downcast it to
216 // libcxxabi's __cxa_exception type.
217 //
218 // In contrast, in the implementation below, the functionality of the
219 // personality function is folded into _Unwind_RaiseException(), so
220 // this implements C++-specific matching of exceptions and downcasts
221 // ue_header to __cxa_exception immediately.
222 //
223 // This function returns if stack unwinding did not find any stack
224 // frames that match the exception being thrown.
225 static void
226 handle_exception(struct _Unwind_Exception *ue_header, bool check_for_catch)
227 {
228 __cxa_exception *xh = get_exception_header_from_ue(ue_header);
229
230 void *obj = get_object_from_ue(ue_header);
231 struct exception_frame *frame;
232 int32_t clause_id;
233 if (!find_match((__shim_type_info *) xh->exceptionType, &obj,
234 __pnacl_eh_stack, &frame, &clause_id))
235 return;
236
237 // Check that there is a non-cleanup handler for the exception.
238 // If not, we should abort before running cleanup handlers
239 // (i.e. destructors).
240 //
241 // This is mainly a convenience for debugging. It means that if
242 // the program throws an uncaught exception, the location of the
243 // "throw" will be on the stack when the program aborts. If we
244 // ran cleanup handlers before aborting, this context would be
245 // lost.
246 //
247 // This is optional in the C++ standard, which says "If no
248 // matching handler is found, the function std::terminate() is
249 // called; whether or not the stack is unwound before this call to
250 // std::terminate() is implementation-defined".
251 if (check_for_catch && clause_id == 0 &&
252 !is_exception_caught((__shim_type_info *) xh->exceptionType, obj,
253 frame->next))
254 return;
255
256 __pnacl_eh_stack = frame->next;
257
258 // Save adjusted exception pointer so that it can be returned by
259 // __cxa_begin_catch() when entering a catch() block.
260 xh->adjustedPtr = obj;
261
262 // Save the clause ID so that if the landingpad block calls
263 // __cxa_call_unexpected() and the std::set_unexpected() handler
264 // throws an exception, we can re-check that exception against the
265 // exception specification.
266 xh->handlerSwitchValue = clause_id;
267
268 // exception_frame uses the same location for storing the jmp_buf
269 // and the landing_pad_result, so we must make a copy of the
270 // jmp_buf first.
271 jmp_buf jmpbuf_copy;
272 memcpy(&jmpbuf_copy, &frame->jmpbuf, sizeof(jmpbuf_copy));
273
274 // Return to the landingpad block, passing it two values.
275 frame->result.exception_obj = ue_header;
276 frame->result.matched_clause_id = clause_id;
277 longjmp(jmpbuf_copy, 1);
278 }
279
280
281 // This implements _Unwind_RaiseException(). This is called when
282 // raising an exception for the first time, i.e. for the statement
283 // "throw EXPR;". The compiler lowers "throw EXPR;" to:
284 // * a call to __cxa_allocate_exception() to allocate memory;
285 // * a call to __cxa_throw() which throws the exception by calling
286 // _Unwind_RaiseException().
287 extern "C" _Unwind_Reason_Code
288 __pnacl_eh_sjlj_Unwind_RaiseException(struct _Unwind_Exception *ue_header)
289 {
290 handle_exception(ue_header, true);
291 return _URC_END_OF_STACK;
292 }
293
294 // This is the equivalent of _Unwind_Resume() from libgcc_eh, but we
295 // use a different name for PNaCl SJLJ to avoid accidental collisions
296 // with libgcc_eh.
297 //
298 // This is called by a landingpad block as a final step after it has
299 // run C++ destructors. This is only called by a landingpad if it did
300 // not enter a catch() block.
301 //
302 // This function never returns.
303 extern "C" void
304 __pnacl_eh_resume(struct _Unwind_Exception *ue_header)
305 {
306 // Pass check_for_catch=false so that unwinding does not take O(n^2)
307 // time in the number of cleanup landingpads entered before entering
308 // the catch() block.
309 handle_exception(ue_header, false);
310
311 // We've run C++ destructors (cleanup handlers), but no further
312 // handlers were found, so abort. We should not reach here, because
313 // __pnacl_eh_sjlj_Unwind_RaiseException() already checked that
314 // there was a handler for this exception other than cleanup
315 // handlers.
316 __cxa_begin_catch(ue_header);
317 std::terminate();
318 }
319
320 // _Unwind_Resume_or_Rethrow() is called when rethrowing an
321 // exception, i.e. for the statement "throw;" (with no arguments).
322 // The compiler lowers "throw;" to a call to __cxa_rethrow(), which
323 // calls this function.
324 extern "C" _Unwind_Reason_Code
325 __pnacl_eh_sjlj_Unwind_Resume_or_Rethrow(struct _Unwind_Exception *ue_header)
326 {
327 return __pnacl_eh_sjlj_Unwind_RaiseException(ue_header);
328 }
329
330 // A convenience function that calls the exception_cleanup field.
331 // Based on the definition in libgcc_eh's unwind.inc.
332 //
333 // This is called when a catch() block that handles an exception exits
334 // without rethrowing the exception. This is called by
335 // __cxa_end_catch(). The compiler generates a call to
336 // __cxa_end_catch() at the end of a catch() block.
337 extern "C" void
338 __pnacl_eh_sjlj_Unwind_DeleteException(struct _Unwind_Exception *exc)
339 {
340 if (exc->exception_cleanup)
341 (*exc->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT, exc);
342 }
343
344 // This function implements __cxa_call_unexpected(), which is called
345 // by a landingpad block when an exception is thrown that doesn't
346 // match a function's exception spec (i.e. a "throw(...)" attribute on
347 // a function). Calls to __cxa_call_unexpected() are generated by the
348 // C++ front end.
349 //
350 // This calls the handler registered with std::set_unexpected(). This
351 // handler is allowed to throw, in which case we must re-check the
352 // resulting exception against the original exception specification.
353 //
354 // The reason that __cxa_call_unexpected() is called by landingpad
355 // code rather than by the personality function is so that the
356 // landingpad code can run destructors first.
357 //
358 // This is loosely based on the __cxa_call_unexpected() implementation
359 // in cxa_personality.cpp.
360 //
361 // This function never returns.
362 extern "C" void
363 __pnacl_eh_sjlj_cxa_call_unexpected(struct _Unwind_Exception *ue_header)
364 {
365 // Mark the exception as being handled, so that the
366 // set_unexpected() handler can rethrow it.
367 __cxa_begin_catch(ue_header);
368
369 // Ensure that the corresponding __cxa_end_catch() call happens on
370 // all paths out of this function.
371 struct do_end_catch
372 {
373 ~do_end_catch() { __cxa_end_catch(); }
374 } do_end_catch_obj;
375
376 __cxa_exception *old_exception_header =
377 get_exception_header_from_ue(ue_header);
378 int32_t filter_id = old_exception_header->handlerSwitchValue;
379 std::unexpected_handler u_handler = old_exception_header->unexpectedHandler;
380 std::terminate_handler t_handler = old_exception_header->terminateHandler;
381
382 try
383 {
384 std::__unexpected(u_handler);
385 }
386 catch (...)
387 {
388 __cxa_eh_globals *globals = __cxa_get_globals_fast();
389 __cxa_exception *new_exception_header = globals->caughtExceptions;
390
391 // If the handler threw an exception that is allowed by the
392 // original exception spec, allow this exception to propagate.
393 if (!exception_spec_can_catch(
394 (const __shim_type_info *) new_exception_header->exceptionType,
395 get_object_from_ue(&new_exception_header->unwindHeader),
396 filter_id))
397 throw;
398
399 // Otherwise, if the original exception spec allows
400 // std::bad_exception, throw an exception of that type.
401 std::bad_exception be;
402 const __shim_type_info *be_type =
403 (const __shim_type_info *) &typeid(be);
404 if (!exception_spec_can_catch(be_type, &be, filter_id))
405 throw be;
406 }
407 std::__terminate(t_handler);
408 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698