]>
Commit | Line | Data |
---|---|---|
ee9dd372 TT |
1 | // Functions for Exception Support for Java. |
2 | ||
52a11cbf | 3 | /* Copyright (C) 1998, 1999, 2001 Free Software Foundation |
ee9dd372 TT |
4 | |
5 | This file is part of libgcj. | |
6 | ||
7 | This software is copyrighted work licensed under the terms of the | |
8 | Libgcj License. Please consult the file "LIBGCJ_LICENSE" for | |
9 | details. */ | |
10 | ||
11 | #include <config.h> | |
12 | ||
ee9dd372 | 13 | #include <stddef.h> |
9b811d11 | 14 | #include <stdlib.h> |
ee9dd372 TT |
15 | |
16 | #include <java/lang/Class.h> | |
17 | #include <java/lang/NullPointerException.h> | |
27e934d8 | 18 | #include <gcj/cni.h> |
ee9dd372 TT |
19 | #include <jvm.h> |
20 | ||
52a11cbf | 21 | #include "unwind.h" |
ee9dd372 | 22 | |
52a11cbf RH |
23 | \f |
24 | // More nastiness: the GC wants to define TRUE and FALSE. We don't | |
25 | // need the Java definitions (themselves a hack), so we undefine them. | |
26 | #undef TRUE | |
27 | #undef FALSE | |
ee9dd372 | 28 | |
52a11cbf RH |
29 | extern "C" |
30 | { | |
31 | #include <gc_priv.h> | |
32 | #include <gc_mark.h> | |
33 | #include <include/gc_gcj.h> | |
34 | }; | |
ee9dd372 | 35 | |
52a11cbf RH |
36 | \f |
37 | struct alignment_test_struct | |
38 | { | |
39 | char space; | |
40 | char end[0] __attribute__((aligned)); | |
41 | }; | |
ee9dd372 | 42 | |
52a11cbf | 43 | struct java_exception_header |
ee9dd372 | 44 | { |
52a11cbf RH |
45 | /* Cache handler details between Phase 1 and Phase 2. */ |
46 | _Unwind_Ptr landingPad; | |
47 | int handlerSwitchValue; | |
ee9dd372 | 48 | |
52a11cbf RH |
49 | /* The object being thrown. Compiled code expects this to be immediately |
50 | before the generic exception header. Which is complicated by the fact | |
51 | that _Unwind_Exception is ((aligned)). */ | |
52 | ||
53 | char pad[sizeof(jthrowable) < sizeof(alignment_test_struct) | |
54 | ? sizeof(alignment_test_struct) - sizeof(jthrowable) : 0] | |
55 | __attribute__((aligned)); | |
56 | ||
57 | jthrowable value; | |
ee9dd372 | 58 | |
52a11cbf RH |
59 | /* The generic exception header. */ |
60 | _Unwind_Exception unwindHeader; | |
61 | }; | |
62 | ||
63 | // This is the exception class we report -- "GNUCJAVA". | |
64 | const _Unwind_Exception_Class __gcj_exception_class | |
65 | = ((((((((_Unwind_Exception_Class) 'G' | |
66 | << 8 | (_Unwind_Exception_Class) 'N') | |
67 | << 8 | (_Unwind_Exception_Class) 'U') | |
68 | << 8 | (_Unwind_Exception_Class) 'C') | |
69 | << 8 | (_Unwind_Exception_Class) 'J') | |
70 | << 8 | (_Unwind_Exception_Class) 'A') | |
71 | << 8 | (_Unwind_Exception_Class) 'V') | |
72 | << 8 | (_Unwind_Exception_Class) 'A'); | |
73 | ||
74 | ||
75 | static inline java_exception_header * | |
76 | get_exception_header_from_ue (_Unwind_Exception *exc) | |
77 | { | |
78 | return reinterpret_cast<java_exception_header *>(exc + 1) - 1; | |
ee9dd372 TT |
79 | } |
80 | ||
52a11cbf RH |
81 | /* Perform a throw, Java style. Throw will unwind through this call, |
82 | so there better not be any handlers or exception thrown here. */ | |
ee9dd372 | 83 | |
52a11cbf RH |
84 | extern "C" void |
85 | _Jv_Throw (jthrowable value) | |
ee9dd372 | 86 | { |
52a11cbf RH |
87 | /* FIXME: Use the proper API to the collector. */ |
88 | java_exception_header *xh | |
89 | = static_cast<java_exception_header *>(GC_malloc (sizeof (*xh))); | |
90 | ||
91 | if (value == NULL) | |
92 | value = new java::lang::NullPointerException (); | |
93 | xh->value = value; | |
ee9dd372 | 94 | |
52a11cbf RH |
95 | xh->unwindHeader.exception_class = __gcj_exception_class; |
96 | xh->unwindHeader.exception_cleanup = NULL; | |
ee9dd372 | 97 | |
52a11cbf RH |
98 | /* We're happy with setjmp/longjmp exceptions or region-based |
99 | exception handlers: entry points are provided here for both. */ | |
100 | _Unwind_Reason_Code code; | |
101 | #ifdef SJLJ_EXCEPTIONS | |
102 | code = _Unwind_SjLj_RaiseException (&xh->unwindHeader); | |
103 | #else | |
104 | code = _Unwind_RaiseException (&xh->unwindHeader); | |
105 | #endif | |
ee9dd372 | 106 | |
212a2676 RH |
107 | /* If code == _URC_END_OF_STACK, then we reached top of stack without |
108 | finding a handler for the exception. Since each thread is run in | |
109 | a try/catch, this oughtn't happen. If code is something else, we | |
110 | encountered some sort of heinous lossage from which we could not | |
111 | recover. As is the way of such things, almost certainly we will have | |
112 | crashed before now, rather than actually being able to diagnose the | |
113 | problem. */ | |
52a11cbf | 114 | abort (); |
ee9dd372 TT |
115 | } |
116 | ||
52a11cbf RH |
117 | \f |
118 | // ??? These ought to go somewhere else dwarf2 or dwarf2eh related. | |
119 | ||
120 | // Pointer encodings. | |
121 | #define DW_EH_PE_absptr 0x00 | |
122 | #define DW_EH_PE_omit 0xff | |
123 | ||
124 | #define DW_EH_PE_uleb128 0x01 | |
125 | #define DW_EH_PE_udata2 0x02 | |
126 | #define DW_EH_PE_udata4 0x03 | |
127 | #define DW_EH_PE_udata8 0x04 | |
128 | #define DW_EH_PE_sleb128 0x09 | |
129 | #define DW_EH_PE_sdata2 0x0A | |
130 | #define DW_EH_PE_sdata4 0x0B | |
131 | #define DW_EH_PE_sdata8 0x0C | |
132 | #define DW_EH_PE_signed 0x08 | |
133 | ||
134 | #define DW_EH_PE_pcrel 0x10 | |
135 | #define DW_EH_PE_textrel 0x20 | |
136 | #define DW_EH_PE_datarel 0x30 | |
137 | #define DW_EH_PE_funcrel 0x40 | |
138 | ||
139 | static unsigned int | |
140 | size_of_encoded_value (unsigned char encoding) | |
141 | { | |
142 | switch (encoding & 0x07) | |
143 | { | |
144 | case DW_EH_PE_absptr: | |
145 | return sizeof (void *); | |
146 | case DW_EH_PE_udata2: | |
147 | return 2; | |
148 | case DW_EH_PE_udata4: | |
149 | return 4; | |
150 | case DW_EH_PE_udata8: | |
151 | return 8; | |
152 | } | |
153 | abort (); | |
154 | } | |
ee9dd372 | 155 | |
52a11cbf RH |
156 | static const unsigned char * |
157 | read_encoded_value (_Unwind_Context *context, unsigned char encoding, | |
158 | const unsigned char *p, _Unwind_Ptr *val) | |
159 | { | |
160 | union unaligned | |
161 | { | |
162 | void *ptr; | |
163 | unsigned u2 __attribute__ ((mode (HI))); | |
164 | unsigned u4 __attribute__ ((mode (SI))); | |
165 | unsigned u8 __attribute__ ((mode (DI))); | |
166 | signed s2 __attribute__ ((mode (HI))); | |
167 | signed s4 __attribute__ ((mode (SI))); | |
168 | signed s8 __attribute__ ((mode (DI))); | |
169 | } __attribute__((__packed__)); | |
170 | ||
171 | union unaligned *u = (union unaligned *) p; | |
172 | _Unwind_Ptr result; | |
173 | ||
174 | switch (encoding & 0x0f) | |
175 | { | |
176 | case DW_EH_PE_absptr: | |
177 | result = (_Unwind_Ptr) u->ptr; | |
178 | p += sizeof (void *); | |
179 | break; | |
180 | ||
181 | case DW_EH_PE_uleb128: | |
182 | { | |
183 | unsigned int shift = 0; | |
184 | unsigned char byte; | |
185 | ||
186 | result = 0; | |
187 | do | |
188 | { | |
189 | byte = *p++; | |
190 | result |= (_Unwind_Ptr)(byte & 0x7f) << shift; | |
191 | shift += 7; | |
192 | } | |
193 | while (byte & 0x80); | |
194 | } | |
195 | break; | |
196 | ||
197 | case DW_EH_PE_sleb128: | |
198 | { | |
199 | unsigned int shift = 0; | |
200 | unsigned char byte; | |
201 | ||
202 | result = 0; | |
203 | do | |
204 | { | |
205 | byte = *p++; | |
206 | result |= (_Unwind_Ptr)(byte & 0x7f) << shift; | |
207 | shift += 7; | |
208 | } | |
209 | while (byte & 0x80); | |
210 | ||
211 | if (shift < 8 * sizeof(result) && (byte & 0x40) != 0) | |
212 | result |= -(1L << shift); | |
213 | } | |
214 | break; | |
215 | ||
216 | case DW_EH_PE_udata2: | |
217 | result = u->u2; | |
218 | p += 2; | |
219 | break; | |
220 | case DW_EH_PE_udata4: | |
221 | result = u->u4; | |
222 | p += 4; | |
223 | break; | |
224 | case DW_EH_PE_udata8: | |
225 | result = u->u8; | |
226 | p += 8; | |
227 | break; | |
228 | ||
229 | case DW_EH_PE_sdata2: | |
230 | result = u->s2; | |
231 | p += 2; | |
232 | break; | |
233 | case DW_EH_PE_sdata4: | |
234 | result = u->s4; | |
235 | p += 4; | |
236 | break; | |
237 | case DW_EH_PE_sdata8: | |
238 | result = u->s8; | |
239 | p += 8; | |
240 | break; | |
241 | ||
242 | default: | |
243 | abort (); | |
244 | } | |
ee9dd372 | 245 | |
52a11cbf RH |
246 | if (result != 0) |
247 | switch (encoding & 0xf0) | |
248 | { | |
249 | case DW_EH_PE_absptr: | |
250 | break; | |
ee9dd372 | 251 | |
52a11cbf RH |
252 | case DW_EH_PE_pcrel: |
253 | // Define as relative to the beginning of the pointer. | |
254 | result += (_Unwind_Ptr) u; | |
255 | break; | |
256 | ||
257 | case DW_EH_PE_textrel: | |
258 | case DW_EH_PE_datarel: | |
259 | // FIXME. | |
260 | abort (); | |
ee9dd372 | 261 | |
52a11cbf RH |
262 | case DW_EH_PE_funcrel: |
263 | result += _Unwind_GetRegionStart (context); | |
264 | break; | |
ee9dd372 | 265 | |
52a11cbf RH |
266 | default: |
267 | abort (); | |
268 | } | |
ee9dd372 | 269 | |
52a11cbf RH |
270 | *val = result; |
271 | return p; | |
ee9dd372 TT |
272 | } |
273 | ||
52a11cbf RH |
274 | static inline const unsigned char * |
275 | read_uleb128 (const unsigned char *p, _Unwind_Ptr *val) | |
276 | { | |
277 | return read_encoded_value (0, DW_EH_PE_uleb128, p, val); | |
278 | } | |
ee9dd372 | 279 | |
52a11cbf RH |
280 | static inline const unsigned char * |
281 | read_sleb128 (const unsigned char *p, _Unwind_Ptr *val) | |
ee9dd372 | 282 | { |
52a11cbf | 283 | return read_encoded_value (0, DW_EH_PE_sleb128, p, val); |
ee9dd372 TT |
284 | } |
285 | ||
52a11cbf RH |
286 | \f |
287 | struct lsda_header_info | |
288 | { | |
289 | _Unwind_Ptr Start; | |
290 | _Unwind_Ptr LPStart; | |
291 | const unsigned char *TType; | |
292 | const unsigned char *action_table; | |
293 | unsigned char ttype_encoding; | |
294 | unsigned char call_site_encoding; | |
295 | }; | |
296 | ||
297 | static const unsigned char * | |
298 | parse_lsda_header (_Unwind_Context *context, const unsigned char *p, | |
299 | lsda_header_info *info) | |
300 | { | |
301 | _Unwind_Ptr tmp; | |
302 | unsigned char lpstart_encoding; | |
303 | ||
304 | info->Start = (context ? _Unwind_GetRegionStart (context) : 0); | |
ee9dd372 | 305 | |
52a11cbf RH |
306 | // Find @LPStart, the base to which landing pad offsets are relative. |
307 | lpstart_encoding = *p++; | |
308 | if (lpstart_encoding != DW_EH_PE_omit) | |
309 | p = read_encoded_value (context, lpstart_encoding, p, &info->LPStart); | |
310 | else | |
311 | info->LPStart = info->Start; | |
312 | ||
313 | // Find @TType, the base of the handler and exception spec type data. | |
314 | info->ttype_encoding = *p++; | |
315 | if (info->ttype_encoding != DW_EH_PE_omit) | |
316 | { | |
317 | p = read_uleb128 (p, &tmp); | |
318 | info->TType = p + tmp; | |
319 | } | |
320 | else | |
321 | info->TType = 0; | |
322 | ||
323 | // The encoding and length of the call-site table; the action table | |
324 | // immediately follows. | |
325 | info->call_site_encoding = *p++; | |
326 | p = read_uleb128 (p, &tmp); | |
327 | info->action_table = p + tmp; | |
328 | ||
329 | return p; | |
330 | } | |
331 | ||
332 | static jclass | |
333 | get_ttype_entry (_Unwind_Context *context, lsda_header_info *info, long i) | |
ee9dd372 | 334 | { |
52a11cbf RH |
335 | _Unwind_Ptr ptr; |
336 | ||
337 | i *= size_of_encoded_value (info->ttype_encoding); | |
338 | read_encoded_value (context, info->ttype_encoding, info->TType - i, &ptr); | |
339 | ||
340 | return reinterpret_cast<jclass>(ptr); | |
ee9dd372 TT |
341 | } |
342 | ||
3cf88fb4 | 343 | |
52a11cbf RH |
344 | // Using a different personality function name causes link failures |
345 | // when trying to mix code using different exception handling models. | |
b3208f56 | 346 | #ifdef SJLJ_EXCEPTIONS |
52a11cbf RH |
347 | #define PERSONALITY_FUNCTION __gcj_personality_sj0 |
348 | #define __builtin_eh_return_data_regno(x) x | |
349 | #else | |
350 | #define PERSONALITY_FUNCTION __gcj_personality_v0 | |
b3208f56 RH |
351 | #endif |
352 | ||
52a11cbf RH |
353 | extern "C" _Unwind_Reason_Code |
354 | PERSONALITY_FUNCTION (int version, | |
355 | _Unwind_Action actions, | |
356 | _Unwind_Exception_Class exception_class, | |
357 | struct _Unwind_Exception *ue_header, | |
358 | struct _Unwind_Context *context) | |
ee9dd372 | 359 | { |
52a11cbf RH |
360 | java_exception_header *xh = get_exception_header_from_ue (ue_header); |
361 | ||
362 | lsda_header_info info; | |
363 | const unsigned char *language_specific_data; | |
364 | const unsigned char *action_record; | |
365 | const unsigned char *p; | |
366 | _Unwind_Ptr landing_pad, ip; | |
367 | int handler_switch_value; | |
368 | bool saw_cleanup; | |
369 | bool saw_handler; | |
370 | ||
371 | ||
372 | // Interface version check. | |
373 | if (version != 1) | |
374 | return _URC_FATAL_PHASE1_ERROR; | |
375 | ||
376 | // Shortcut for phase 2 found handler for domestic exception. | |
377 | if (actions == (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME) | |
378 | && exception_class == __gcj_exception_class) | |
ee9dd372 | 379 | { |
52a11cbf RH |
380 | handler_switch_value = xh->handlerSwitchValue; |
381 | landing_pad = xh->landingPad; | |
382 | goto install_context; | |
ee9dd372 | 383 | } |
3cf88fb4 | 384 | |
52a11cbf RH |
385 | // FIXME: In Phase 1, record _Unwind_GetIP in xh->obj as a part of |
386 | // the stack trace for this exception. This will only collect Java | |
387 | // frames, but perhaps that is acceptable. | |
388 | // FIXME2: _Unwind_GetIP is nonsensical for SJLJ, being a call-site | |
389 | // index instead of a PC value. We could perhaps arrange for | |
390 | // _Unwind_GetRegionStart to return context->fc->jbuf[1], which | |
391 | // is the address of the handler label for __builtin_longjmp, but | |
392 | // there is no solution for DONT_USE_BUILTIN_SETJMP. | |
393 | ||
394 | language_specific_data = (const unsigned char *) | |
395 | _Unwind_GetLanguageSpecificData (context); | |
396 | ||
397 | // If no LSDA, then there are no handlers or cleanups. | |
398 | if (! language_specific_data) | |
399 | return _URC_CONTINUE_UNWIND; | |
400 | ||
401 | // Parse the LSDA header. | |
402 | p = parse_lsda_header (context, language_specific_data, &info); | |
403 | ip = _Unwind_GetIP (context) - 1; | |
404 | landing_pad = 0; | |
405 | action_record = 0; | |
406 | handler_switch_value = 0; | |
407 | ||
3cf88fb4 | 408 | #ifdef SJLJ_EXCEPTIONS |
52a11cbf RH |
409 | // The given "IP" is an index into the call-site table, with two |
410 | // exceptions -- -1 means no-action, and 0 means terminate. But | |
411 | // since we're using uleb128 values, we've not got random access | |
412 | // to the array. | |
413 | if ((int) ip <= 0) | |
414 | return _URC_CONTINUE_UNWIND; | |
415 | else | |
416 | { | |
417 | _Unwind_Ptr cs_lp, cs_action; | |
418 | do | |
419 | { | |
420 | p = read_uleb128 (p, &cs_lp); | |
421 | p = read_uleb128 (p, &cs_action); | |
422 | } | |
423 | while (--ip); | |
424 | ||
425 | // Can never have null landing pad for sjlj -- that would have | |
426 | // been indicated by a -1 call site index. | |
427 | landing_pad = cs_lp + 1; | |
428 | if (cs_action) | |
429 | action_record = info.action_table + cs_action - 1; | |
430 | goto found_something; | |
431 | } | |
3cf88fb4 | 432 | #else |
52a11cbf RH |
433 | // Search the call-site table for the action associated with this IP. |
434 | while (p < info.action_table) | |
435 | { | |
436 | _Unwind_Ptr cs_start, cs_len, cs_lp, cs_action; | |
437 | ||
438 | // Note that all call-site encodings are "absolute" displacements. | |
439 | p = read_encoded_value (0, info.call_site_encoding, p, &cs_start); | |
440 | p = read_encoded_value (0, info.call_site_encoding, p, &cs_len); | |
441 | p = read_encoded_value (0, info.call_site_encoding, p, &cs_lp); | |
442 | p = read_uleb128 (p, &cs_action); | |
443 | ||
444 | // The table is sorted, so if we've passed the ip, stop. | |
445 | if (ip < info.Start + cs_start) | |
446 | p = info.action_table; | |
447 | else if (ip < info.Start + cs_start + cs_len) | |
448 | { | |
449 | if (cs_lp) | |
450 | landing_pad = info.LPStart + cs_lp; | |
451 | if (cs_action) | |
452 | action_record = info.action_table + cs_action - 1; | |
453 | goto found_something; | |
454 | } | |
455 | } | |
456 | #endif // SJLJ_EXCEPTIONS | |
878885b4 | 457 | |
52a11cbf RH |
458 | // If ip is not present in the table, C++ would call terminate. |
459 | // ??? It is perhaps better to tweek the LSDA so that no-action | |
460 | // is mapped to no-entry for Java. | |
461 | return _URC_CONTINUE_UNWIND; | |
878885b4 | 462 | |
52a11cbf RH |
463 | found_something: |
464 | saw_cleanup = false; | |
465 | saw_handler = false; | |
878885b4 | 466 | |
52a11cbf | 467 | if (landing_pad == 0) |
878885b4 | 468 | { |
52a11cbf RH |
469 | // If ip is present, and has a null landing pad, there are |
470 | // no cleanups or handlers to be run. | |
471 | } | |
472 | else if (action_record == 0) | |
473 | { | |
474 | // If ip is present, has a non-null landing pad, and a null | |
475 | // action table offset, then there are only cleanups present. | |
476 | // Cleanups use a zero switch value, as set above. | |
477 | saw_cleanup = true; | |
478 | } | |
479 | else | |
480 | { | |
481 | // Otherwise we have a catch handler. | |
482 | signed long ar_filter, ar_disp; | |
483 | ||
484 | while (1) | |
485 | { | |
486 | _Unwind_Ptr tmp; | |
487 | ||
488 | p = action_record; | |
489 | p = read_sleb128 (p, &tmp); ar_filter = tmp; | |
490 | read_sleb128 (p, &tmp); ar_disp = tmp; | |
491 | ||
492 | if (ar_filter == 0) | |
493 | { | |
494 | // Zero filter values are cleanups. | |
495 | saw_cleanup = true; | |
496 | } | |
497 | ||
498 | // During forced unwinding, we only run cleanups. With a | |
499 | // foreign exception class, we have no class info to match. | |
500 | else if ((actions & _UA_FORCE_UNWIND) | |
501 | || exception_class != __gcj_exception_class) | |
502 | ; | |
503 | ||
504 | else if (ar_filter > 0) | |
505 | { | |
506 | // Positive filter values are handlers. | |
507 | ||
508 | jclass catch_type = get_ttype_entry (context, &info, ar_filter); | |
509 | ||
510 | // The catch_type is either a (java::lang::Class*) or | |
511 | // is one more than a (Utf8Const*). | |
512 | if ((size_t)catch_type & 1) | |
513 | catch_type = _Jv_FindClass ((Utf8Const*)catch_type - 1, NULL); | |
514 | ||
515 | if (_Jv_IsInstanceOf (xh->value, catch_type)) | |
516 | { | |
517 | handler_switch_value = ar_filter; | |
518 | saw_handler = true; | |
519 | break; | |
520 | } | |
521 | } | |
522 | else | |
523 | { | |
524 | // Negative filter values are exception specifications, | |
525 | // which Java does not use. | |
526 | // ??? Perhaps better to make them an index into a table | |
527 | // of null-terminated strings instead of playing games | |
528 | // with Utf8Const+1 as above. | |
529 | abort (); | |
530 | } | |
531 | ||
532 | if (ar_disp == 0) | |
533 | break; | |
534 | action_record = p + ar_disp; | |
535 | } | |
878885b4 | 536 | } |
878885b4 | 537 | |
52a11cbf RH |
538 | if (! saw_handler && ! saw_cleanup) |
539 | return _URC_CONTINUE_UNWIND; | |
878885b4 | 540 | |
52a11cbf RH |
541 | if (actions & _UA_SEARCH_PHASE) |
542 | { | |
543 | if (! saw_handler) | |
544 | return _URC_CONTINUE_UNWIND; | |
545 | ||
546 | // For domestic exceptions, we cache data from phase 1 for phase 2. | |
547 | if (exception_class == __gcj_exception_class) | |
548 | { | |
549 | xh->handlerSwitchValue = handler_switch_value; | |
550 | xh->landingPad = landing_pad; | |
551 | } | |
552 | return _URC_HANDLER_FOUND; | |
553 | } | |
878885b4 | 554 | |
52a11cbf RH |
555 | install_context: |
556 | _Unwind_SetGR (context, __builtin_eh_return_data_regno (0), | |
557 | (_Unwind_Ptr) &xh->unwindHeader); | |
558 | _Unwind_SetGR (context, __builtin_eh_return_data_regno (1), | |
559 | handler_switch_value); | |
560 | _Unwind_SetIP (context, landing_pad); | |
561 | return _URC_INSTALL_CONTEXT; | |
878885b4 | 562 | } |