OLD | NEW |
---|---|
(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. | |
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 // Each __pnacl_eh_sjlj_Unwind_*() function below provides the | |
165 // definition of _Unwind_*(). | |
166 // | |
167 // The "__pnacl_eh_sjlj" prefix is added so that PNaCl's SJLJ | |
168 // (setjmp()/longjmp()-based) implementation of C++ exception handling | |
169 // can coexist with other implementations in the same build of | |
170 // libstdc++/libsupc++. When SJLJ EH is enabled, each | |
171 // __pnacl_eh_sjlj_Unwind_*() symbol will get renamed to _Unwind_*() | |
172 // when linking a PNaCl pexe. | |
173 | |
174 extern "C" _Unwind_Reason_Code | |
175 __pnacl_eh_sjlj_Unwind_RaiseException (struct _Unwind_Exception *ue_header) | |
176 { | |
177 __cxa_exception *xh = __get_exception_header_from_ue (ue_header); | |
178 | |
179 void *obj = __get_object_from_ue (ue_header); | |
180 struct exception_frame *frame; | |
181 int32_t clause_id; | |
182 if (find_match (xh->exceptionType, &obj, &frame, &clause_id)) | |
183 { | |
184 xh->adjustedPtr = obj; | |
185 xh->handlerSwitchValue = clause_id; | |
186 | |
187 // exception_frame uses the same location for storing the | |
188 // jmp_buf and the landing_pad_result, so we must make a copy of | |
189 // the jmp_buf first. | |
190 jmp_buf jmpbuf_copy; | |
191 memcpy (&jmpbuf_copy, &frame->jmpbuf, sizeof (jmpbuf_copy)); | |
192 | |
193 frame->result.exception_obj = ue_header; | |
194 frame->result.matched_clause_id = clause_id; | |
195 longjmp (jmpbuf_copy, 1); | |
196 } | |
197 | |
198 return _URC_END_OF_STACK; | |
199 } | |
200 | |
201 // This is the equivalent of _Unwind_Resume() from libgcc_eh, but we | |
202 // use a different name for PNaCl SJLJ to avoid accidental collisions | |
203 // with libgcc_eh. | |
204 extern "C" void | |
205 __pnacl_eh_resume (struct _Unwind_Exception *ue_header) | |
Derek Schuff
2013/10/24 20:41:17
why __pnacl_eh_resume and not __pnacl_eh_sjlj_Unwi
Mark Seaborn
2013/10/24 21:43:54
My thinking here was that there is a distinction b
| |
206 { | |
207 __pnacl_eh_sjlj_Unwind_RaiseException (ue_header); | |
208 | |
209 // We've run C++ destructors (cleanup handlers), but no further | |
210 // handlers were found, so abort. | |
211 // | |
212 // Note that this situation does not normally happen with GNU C++, | |
213 // which scans the stack to check for a matching catch or filter | |
214 // clause, and calls std::terminate() if none is found, before | |
215 // calling any cleanup handlers. | |
216 // | |
217 // Both behaviours are allowed by the C++ standard, which says "If | |
218 // no matching handler is found, the function std::terminate() is | |
219 // called; whether or not the stack is unwound before this call to | |
220 // std::terminate() is implementation-defined". | |
221 __cxa_begin_catch (ue_header); | |
222 std::terminate (); | |
223 } | |
224 | |
225 extern "C" _Unwind_Reason_Code | |
226 __pnacl_eh_sjlj_Unwind_Resume_or_Rethrow (struct _Unwind_Exception *ue_header) | |
227 { | |
228 return __pnacl_eh_sjlj_Unwind_RaiseException (ue_header); | |
229 } | |
230 | |
231 // A convenience function that calls the exception_cleanup field. | |
232 // Based on the definition in libgcc_eh's unwind.inc. | |
233 extern "C" void | |
234 __pnacl_eh_sjlj_Unwind_DeleteException (struct _Unwind_Exception *exc) | |
235 { | |
236 if (exc->exception_cleanup) | |
237 (*exc->exception_cleanup) (_URC_FOREIGN_EXCEPTION_CAUGHT, exc); | |
238 } | |
239 | |
240 // This is based on __cxa_call_unexpected in eh_personality.cc, | |
241 // adapted to use PNaCl's exception info instead of the non-portable | |
242 // LSDA. | |
243 extern "C" void | |
244 __pnacl_eh_sjlj_cxa_call_unexpected (_Unwind_Exception *ue_header) | |
245 { | |
246 __cxa_exception *xh = __get_exception_header_from_ue (ue_header); | |
247 int xh_switch_value = xh->handlerSwitchValue; | |
248 std::terminate_handler xh_terminate_handler = xh->terminateHandler; | |
249 | |
250 __cxa_begin_catch (ue_header); | |
251 | |
252 // This function is a handler for our exception argument. If we exit | |
253 // by throwing a different exception, we'll need the original cleaned up. | |
254 struct end_catch_protect | |
255 { | |
256 end_catch_protect() { } | |
257 ~end_catch_protect() { __cxa_end_catch(); } | |
258 } end_catch_protect_obj; | |
259 | |
260 // We use the handler function that was cached by __cxa_throw(). | |
261 // libstdc++'s unwind-cxx.h says this is cached because "The C++ | |
262 // standard has entertaining rules wrt calling set_terminate and | |
263 // set_unexpected in the middle of the exception cleanup process." | |
264 try | |
265 {__unexpected (xh->unexpectedHandler); } | |
266 catch (...) | |
267 { | |
268 __cxa_eh_globals *globals = __cxa_get_globals_fast (); | |
269 __cxa_exception *new_xh = globals->caughtExceptions; | |
270 void *new_ptr = __get_object_from_ambiguous_exception (new_xh); | |
271 | |
272 // If this new exception meets the exception spec, allow it. | |
273 if (check_exception_spec ( | |
274 __get_exception_header_from_obj (new_ptr)->exceptionType, | |
275 new_ptr, xh_switch_value)) | |
276 throw; | |
277 | |
278 // If the exception spec allows std::bad_exception, throw that. | |
279 // We don't have a thrown object to compare against, but since | |
280 // bad_exception doesn't have virtual bases, that's OK; just pass 0. | |
281 const std::type_info *bad_exc = &typeid (std::bad_exception); | |
282 if (check_exception_spec (bad_exc, NULL, xh_switch_value)) | |
283 throw std::bad_exception (); | |
284 | |
285 __terminate (xh_terminate_handler); | |
286 } | |
287 } | |
OLD | NEW |