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

Side by Side Diff: libstdc++-v3/libsupc++/eh_pnacl.cc

Issue 27341005: libstdc++: Add support for exception handling using the PNaClSjLjEH LLVM pass (Closed) Base URL: http://git.chromium.org/native_client/pnacl-gcc.git@pnacl
Patch Set: Add comments Created 7 years, 1 month 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 | « libstdc++-v3/libsupc++/Makefile.in ('k') | 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 // -*- C++ -*- Exception handling routines for PNaCl.
2 // Copyright (C) 2013
3 // Free Software Foundation, Inc.
4 //
5 // This file is part of GCC.
6 //
7 // GCC is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 3, or (at your option)
10 // any later version.
11 //
12 // GCC is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 // Under Section 7 of GPL version 3, you are granted additional
18 // permissions described in the GCC Runtime Library Exception, version
19 // 3.1, as published by the Free Software Foundation.
20
21 // You should have received a copy of the GNU General Public License and
22 // a copy of the GCC Runtime Library Exception along with this program;
23 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
24 // <http://www.gnu.org/licenses/>.
25
26 #include <setjmp.h>
27 #include <stdint.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <typeinfo>
31 #include "unwind-cxx.h"
32
33 using namespace __cxxabiv1;
34
35
36 // Exception info written by ExceptionInfoWriter.cpp.
37
38 struct action_table_entry
39 {
40 int32_t clause_id;
41 uint32_t next_clause_list_id;
42 };
43
44 extern const struct action_table_entry __pnacl_eh_action_table[];
45 extern const std::type_info *const __pnacl_eh_type_table[];
46 extern const int32_t __pnacl_eh_filter_table[];
47
48 // Data structures used by PNaClSjLjEH.cpp.
49
50 struct landing_pad_result
51 {
52 void *exception_obj;
53 uint32_t matched_clause_id;
54 };
55
56 struct exception_frame
57 {
58 union
59 {
60 jmp_buf jmpbuf;
61 struct landing_pad_result result;
62 };
63 struct exception_frame *next;
64 uint32_t clause_list_id;
65 };
66
67 __thread struct exception_frame *__pnacl_eh_stack;
68
69
70 // This is based on get_adjusted_ptr() in eh_personality.cc.
71 //
72 // Given the thrown type THROW_TYPE, pointer to a variable containing a
73 // pointer to the exception object THROWN_PTR_P and a type CATCH_TYPE to
74 // compare against, return whether or not there is a match and if so,
75 // update *THROWN_PTR_P.
76 static bool
77 get_adjusted_ptr (const std::type_info *catch_type,
78 const std::type_info *throw_type,
79 void **thrown_ptr_p)
80 {
81 if (catch_type == NULL)
82 return true;
83
84 void *thrown_ptr = *thrown_ptr_p;
85
86 // Pointer types need to adjust the actual pointer, not
87 // the pointer to pointer that is the exception object.
88 // This also has the effect of passing pointer types
89 // "by value" through the __cxa_begin_catch return value.
90 if (throw_type->__is_pointer_p ())
91 thrown_ptr = *(void **) thrown_ptr;
92
93 if (catch_type->__do_catch (throw_type, &thrown_ptr, 1))
94 {
95 *thrown_ptr_p = thrown_ptr;
96 return true;
97 }
98
99 return false;
100 }
101
102 // Returns whether the thrown exception (specified by THROW_TYPE and
103 // OBJ) matches one of the exception types in a C++ exception
104 // specification (specified by FILTER_ID).
105 static bool
106 check_exception_spec (const std::type_info *throw_type, void *obj,
107 int32_t filter_id)
108 {
109 const int32_t *filter_ptr =
110 &__pnacl_eh_filter_table[-filter_id - 1];
111 for (; *filter_ptr != -1; ++filter_ptr)
112 {
113 const std::type_info *catch_type = __pnacl_eh_type_table[*filter_ptr];
114 // We ignore the modified value of obj here.
115 if (get_adjusted_ptr (catch_type, throw_type, &obj))
116 return true;
117 }
118 // No type matched, so we have an exception specification error.
119 return false;
120 }
121
122 // Returns whether the thrown exception (specified by THROW_TYPE and
123 // OBJ) matches the given landingpad clause (CLAUSE_ID). If the
124 // exception matches, this adjusts *OBJ.
125 static bool
126 does_clause_match (const std::type_info *throw_type, void **obj,
127 int32_t clause_id)
128 {
129 if (clause_id < 0)
130 return !check_exception_spec (throw_type, obj, clause_id);
131
132 const std::type_info *catch_type = __pnacl_eh_type_table[clause_id];
133 return get_adjusted_ptr (catch_type, throw_type, obj);
134 }
135
136 // Search for a stack frame that will handle the given exception.
Derek Schuff 2013/10/28 22:14:45 add the description of what the arguments mean her
137 static bool
138 find_match (const std::type_info *throw_type, void **obj,
139 struct exception_frame **result_frame, int32_t *result_clause_id)
140 {
141 while (__pnacl_eh_stack != NULL)
142 {
143 struct exception_frame *frame = __pnacl_eh_stack;
144 __pnacl_eh_stack = frame->next;
145
146 for (uint32_t clause_list_id = frame->clause_list_id;
147 clause_list_id != 0; )
148 {
149 const struct action_table_entry *list_node =
150 &__pnacl_eh_action_table[clause_list_id - 1];
151 if (does_clause_match (throw_type, obj, list_node->clause_id))
152 {
153 // Match found.
154 *result_frame = frame;
155 *result_clause_id = list_node->clause_id;
156 return true;
157 }
158 clause_list_id = list_node->next_clause_list_id;
159 }
160 }
161 return false;
162 }
163
164
165 // Each __pnacl_eh_sjlj_Unwind_*() function below provides the
166 // definition of _Unwind_*().
167 //
168 // The "__pnacl_eh_sjlj" prefix is added so that PNaCl's SJLJ
169 // (setjmp()/longjmp()-based) implementation of C++ exception handling
170 // can coexist with other implementations in the same build of
171 // libstdc++/libsupc++. When SJLJ EH is enabled, each
172 // __pnacl_eh_sjlj_Unwind_*() symbol will get renamed to _Unwind_*()
173 // when linking a PNaCl pexe.
174
175
176 // This implements _Unwind_RaiseException(). This is called when
177 // raising an exception for the first time, i.e. for the statement
178 // "throw EXPR;". The compiler lowers "throw EXPR;" to:
179 // * a call to __cxa_allocate_exception() to allocate memory;
180 // * a call to __cxa_throw() which throws the exception by calling
181 // _Unwind_RaiseException().
182 //
183 // _Unwind_RaiseException() unwinds the stack, looking for the first
184 // C++ destructor or catch() block to pass control to. In LLVM terms,
185 // it searches for the first matching invoke/landingpad instruction.
186 // When it finds a match, this implementation passes control to the
187 // landingpad block by longjmp()'ing to it.
188 //
189 // In a traditional implementation (based on the Itanium ABI),
190 // _Unwind_RaiseException() is implemented in a library (libgcc_eh)
191 // that is separate from libsupc++. It calls back to libsupc++'s
192 // personality function (__gxx_personality_v0()) to determine whether
193 // a call on the stack has a handler for the exception.
194 // __gxx_personality_v0() knows that UE_HEADER was allocated by
195 // libsupc++'s __cxa_allocate_exception() and can downcast it to
196 // libsupc++'s __cxa_exception type.
197 //
198 // In contrast, in the implementation below, the functionality of the
199 // personality function is folded into _Unwind_RaiseException(), so
200 // this implements C++-specific matching of exceptions and downcasts
201 // UE_HEADER to __cxa_exception immediately.
202 //
203 // This function returns if stack unwinding did not find any stack
204 // frames that match the exception being thrown.
205 extern "C" _Unwind_Reason_Code
206 __pnacl_eh_sjlj_Unwind_RaiseException (struct _Unwind_Exception *ue_header)
207 {
208 __cxa_exception *xh = __get_exception_header_from_ue (ue_header);
209
210 void *obj = __get_object_from_ue (ue_header);
211 struct exception_frame *frame;
212 int32_t clause_id;
213 if (find_match (xh->exceptionType, &obj, &frame, &clause_id))
214 {
215 // Save adjusted exception pointer so that it can be returned by
216 // __cxa_begin_catch() when entering a catch() block.
217 xh->adjustedPtr = obj;
218
219 // Save the clause ID so that if the landingpad block calls
220 // __cxa_call_unexpected() and the std::set_unexpected() handler
221 // throws an exception, we can re-check that exception against
222 // the exception specification.
223 xh->handlerSwitchValue = clause_id;
224
225 // exception_frame uses the same location for storing the
226 // jmp_buf and the landing_pad_result, so we must make a copy of
227 // the jmp_buf first.
228 jmp_buf jmpbuf_copy;
229 memcpy (&jmpbuf_copy, &frame->jmpbuf, sizeof (jmpbuf_copy));
230
231 // Return to the landingpad block, passing it two values.
232 frame->result.exception_obj = ue_header;
233 frame->result.matched_clause_id = clause_id;
234 longjmp (jmpbuf_copy, 1);
235 }
236
237 return _URC_END_OF_STACK;
238 }
239
240 // This is the equivalent of _Unwind_Resume() from libgcc_eh, but we
241 // use a different name for PNaCl SJLJ to avoid accidental collisions
242 // with libgcc_eh.
243 //
244 // This is called by a landingpad block as a final step after it has
245 // run C++ destructors. This is only called by a landingpad if it did
246 // not enter a catch() block.
247 //
248 // This function never returns.
249 extern "C" void
250 __pnacl_eh_resume (struct _Unwind_Exception *ue_header)
251 {
252 __pnacl_eh_sjlj_Unwind_RaiseException (ue_header);
253
254 // We've run C++ destructors (cleanup handlers), but no further
255 // handlers were found, so abort.
256 //
257 // Note that this situation does not normally happen with GNU C++,
258 // which scans the stack to check for a matching catch or filter
259 // clause, and calls std::terminate() if none is found, before
260 // calling any cleanup handlers.
261 //
262 // Both behaviours are allowed by the C++ standard, which says "If
263 // no matching handler is found, the function std::terminate() is
264 // called; whether or not the stack is unwound before this call to
265 // std::terminate() is implementation-defined".
266 __cxa_begin_catch (ue_header);
267 std::terminate ();
268 }
269
270 // _Unwind_Resume_or_Rethrow() is called when rethrowing an
271 // exception, i.e. for the statement "throw;" (with no arguments).
272 // The compiler lowers "throw;" to a call to __cxa_rethrow(), which
273 // calls this function.
274 extern "C" _Unwind_Reason_Code
275 __pnacl_eh_sjlj_Unwind_Resume_or_Rethrow (struct _Unwind_Exception *ue_header)
276 {
277 return __pnacl_eh_sjlj_Unwind_RaiseException (ue_header);
278 }
279
280 // A convenience function that calls the exception_cleanup field.
281 // Based on the definition in libgcc_eh's unwind.inc.
282 //
283 // This is called when a catch() block that handles an exception exits
284 // without rethrowing the exception. This is called by
285 // __cxa_end_catch(). The compiler generates a call to
286 // __cxa_end_catch() at the end of a catch() block.
287 extern "C" void
288 __pnacl_eh_sjlj_Unwind_DeleteException (struct _Unwind_Exception *exc)
289 {
290 if (exc->exception_cleanup)
291 (*exc->exception_cleanup) (_URC_FOREIGN_EXCEPTION_CAUGHT, exc);
292 }
293
294 // This is based on __cxa_call_unexpected() in eh_personality.cc,
295 // adapted to use PNaCl's exception info instead of the non-portable
296 // LSDA.
297 //
298 // __cxa_call_unexpected() is called by landingpad blocks when an
299 // exception has been thrown that doesn't match a function's exception
300 // specification. Calls to __cxa_call_unexpected() are generated by
301 // the compiler.
302 //
303 // __cxa_call_unexpected() calls the handler registered via
304 // std::set_unexpected(). This handler is allowed to throw, in which
305 // case we must re-check the resulting exception against the original
306 // exception specification.
307 //
308 // The reason that __cxa_call_unexpected() is called by landingpad
309 // code rather than by the personality function is so that the
310 // landingpad code can run destructors first.
311 //
312 // This function never returns.
313 extern "C" void
314 __pnacl_eh_sjlj_cxa_call_unexpected (_Unwind_Exception *ue_header)
315 {
316 __cxa_exception *xh = __get_exception_header_from_ue (ue_header);
317 int xh_switch_value = xh->handlerSwitchValue;
318 std::terminate_handler xh_terminate_handler = xh->terminateHandler;
319
320 __cxa_begin_catch (ue_header);
321
322 // This function is a handler for our exception argument. If we exit
323 // by throwing a different exception, we'll need the original cleaned up.
324 struct end_catch_protect
325 {
326 end_catch_protect() { }
327 ~end_catch_protect() { __cxa_end_catch(); }
328 } end_catch_protect_obj;
329
330 // We use the set_unexpected() handler function that was cached by
331 // __cxa_throw().
332 try
333 { __unexpected (xh->unexpectedHandler); }
334 catch (...)
335 {
336 __cxa_eh_globals *globals = __cxa_get_globals_fast ();
337 __cxa_exception *new_xh = globals->caughtExceptions;
338 void *new_ptr = __get_object_from_ambiguous_exception (new_xh);
339
340 // If this new exception meets the exception spec, allow it.
341 if (check_exception_spec (
342 __get_exception_header_from_obj (new_ptr)->exceptionType,
343 new_ptr, xh_switch_value))
344 throw;
345
346 // If the exception spec allows std::bad_exception, throw that.
347 // We don't have a thrown object to compare against, but since
348 // bad_exception doesn't have virtual bases, that's OK; just pass 0.
349 const std::type_info *bad_exc = &typeid (std::bad_exception);
350 if (check_exception_spec (bad_exc, NULL, xh_switch_value))
351 throw std::bad_exception ();
352
353 __terminate (xh_terminate_handler);
354 }
355 }
OLDNEW
« no previous file with comments | « libstdc++-v3/libsupc++/Makefile.in ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698