// Functions for Exception Support for Java.
-/* Copyright (C) 1998, 1999, 2001 Free Software Foundation
+/* Copyright (C) 1998, 1999, 2001, 2002, 2006, 2010, 2011
+ Free Software Foundation
This file is part of libgcj.
#include <java/lang/Class.h>
#include <java/lang/NullPointerException.h>
+#include <gnu/gcj/RawData.h>
#include <gcj/cni.h>
#include <jvm.h>
-#include "unwind.h"
-
-\f
-// More nastiness: the GC wants to define TRUE and FALSE. We don't
-// need the Java definitions (themselves a hack), so we undefine them.
-#undef TRUE
-#undef FALSE
-
-extern "C"
+// unwind-pe.h uses std::abort(), but sometimes we compile libjava
+// without libstdc++-v3. The following hack forces it to use
+// stdlib.h's abort().
+namespace std
{
-#include <gc_priv.h>
-#include <gc_mark.h>
-#include <include/gc_gcj.h>
-};
+ __attribute__ ((__noreturn__)) void
+ abort ()
+ {
+ ::abort ();
+ }
+}
+#include "unwind.h"
-\f
struct alignment_test_struct
{
char space;
_Unwind_Exception unwindHeader;
};
+#ifdef __ARM_EABI_UNWINDER__
+// This is the exception class we report -- "GNUCJAVA".
+
+const _Unwind_Exception_Class __gcj_exception_class
+ = {'G', 'N', 'U', 'C', 'J', 'A', 'V', 'A'};
+
+static inline java_exception_header *
+get_exception_header_from_ue (_Unwind_Exception *exc)
+{
+ return reinterpret_cast<java_exception_header *>(exc + 1) - 1;
+}
+
+extern "C" void __cxa_begin_cleanup (_Unwind_Exception*);
+
+#else // !__ARM_EABI_UNWINDER__
// This is the exception class we report -- "GNUCJAVA".
const _Unwind_Exception_Class __gcj_exception_class
= ((((((((_Unwind_Exception_Class) 'G'
{
return reinterpret_cast<java_exception_header *>(exc + 1) - 1;
}
+#endif // !__ARM_EABI_UNWINDER__
/* Perform a throw, Java style. Throw will unwind through this call,
so there better not be any handlers or exception thrown here. */
extern "C" void
_Jv_Throw (jthrowable value)
{
- /* FIXME: Use the proper API to the collector. */
java_exception_header *xh
- = static_cast<java_exception_header *>(GC_malloc (sizeof (*xh)));
+ = static_cast<java_exception_header *>(_Jv_AllocRawObj (sizeof (*xh)));
if (value == NULL)
value = new java::lang::NullPointerException ();
xh->value = value;
- xh->unwindHeader.exception_class = __gcj_exception_class;
+ memcpy (&xh->unwindHeader.exception_class, &__gcj_exception_class,
+ sizeof xh->unwindHeader.exception_class);
xh->unwindHeader.exception_cleanup = NULL;
/* We're happy with setjmp/longjmp exceptions or region-based
exception handlers: entry points are provided here for both. */
- _Unwind_Reason_Code code;
#ifdef SJLJ_EXCEPTIONS
- code = _Unwind_SjLj_RaiseException (&xh->unwindHeader);
+ _Unwind_SjLj_RaiseException (&xh->unwindHeader);
#else
- code = _Unwind_RaiseException (&xh->unwindHeader);
+ _Unwind_RaiseException (&xh->unwindHeader);
#endif
- /* FIXME: If code == _URC_END_OF_STACK, then we reached top of
- stack without finding a handler for the exception. I seem to
- recall that Java has specific rules to handle this.
-
- If code is something else, we encountered some sort of heinous
- lossage, from which we could not recover. As is the way of such
- things we'll almost certainly have crashed before now, rather
- than actually being able to diagnose the problem. */
- abort ();
+ /* If code == _URC_END_OF_STACK, then we reached top of stack without
+ finding a handler for the exception. Since each thread is run in
+ a try/catch, this oughtn't happen. If code is something else, we
+ encountered some sort of heinous lossage from which we could not
+ recover. As is the way of such things, almost certainly we will have
+ crashed before now, rather than actually being able to diagnose the
+ problem. */
+ abort();
}
\f
-// ??? These ought to go somewhere else dwarf2 or dwarf2eh related.
-
-// Pointer encodings.
-#define DW_EH_PE_absptr 0x00
-#define DW_EH_PE_omit 0xff
-
-#define DW_EH_PE_uleb128 0x01
-#define DW_EH_PE_udata2 0x02
-#define DW_EH_PE_udata4 0x03
-#define DW_EH_PE_udata8 0x04
-#define DW_EH_PE_sleb128 0x09
-#define DW_EH_PE_sdata2 0x0A
-#define DW_EH_PE_sdata4 0x0B
-#define DW_EH_PE_sdata8 0x0C
-#define DW_EH_PE_signed 0x08
-
-#define DW_EH_PE_pcrel 0x10
-#define DW_EH_PE_textrel 0x20
-#define DW_EH_PE_datarel 0x30
-#define DW_EH_PE_funcrel 0x40
-
-static unsigned int
-size_of_encoded_value (unsigned char encoding)
-{
- switch (encoding & 0x07)
- {
- case DW_EH_PE_absptr:
- return sizeof (void *);
- case DW_EH_PE_udata2:
- return 2;
- case DW_EH_PE_udata4:
- return 4;
- case DW_EH_PE_udata8:
- return 8;
- }
- abort ();
-}
-
-static const unsigned char *
-read_encoded_value (_Unwind_Context *context, unsigned char encoding,
- const unsigned char *p, _Unwind_Ptr *val)
-{
- union unaligned
- {
- void *ptr;
- unsigned u2 __attribute__ ((mode (HI)));
- unsigned u4 __attribute__ ((mode (SI)));
- unsigned u8 __attribute__ ((mode (DI)));
- signed s2 __attribute__ ((mode (HI)));
- signed s4 __attribute__ ((mode (SI)));
- signed s8 __attribute__ ((mode (DI)));
- } __attribute__((__packed__));
-
- union unaligned *u = (union unaligned *) p;
- _Unwind_Ptr result;
-
- switch (encoding & 0x0f)
- {
- case DW_EH_PE_absptr:
- result = (_Unwind_Ptr) u->ptr;
- p += sizeof (void *);
- break;
-
- case DW_EH_PE_uleb128:
- {
- unsigned int shift = 0;
- unsigned char byte;
-
- result = 0;
- do
- {
- byte = *p++;
- result |= (_Unwind_Ptr)(byte & 0x7f) << shift;
- shift += 7;
- }
- while (byte & 0x80);
- }
- break;
-
- case DW_EH_PE_sleb128:
- {
- unsigned int shift = 0;
- unsigned char byte;
-
- result = 0;
- do
- {
- byte = *p++;
- result |= (_Unwind_Ptr)(byte & 0x7f) << shift;
- shift += 7;
- }
- while (byte & 0x80);
-
- if (shift < 8 * sizeof(result) && (byte & 0x40) != 0)
- result |= -(1L << shift);
- }
- break;
-
- case DW_EH_PE_udata2:
- result = u->u2;
- p += 2;
- break;
- case DW_EH_PE_udata4:
- result = u->u4;
- p += 4;
- break;
- case DW_EH_PE_udata8:
- result = u->u8;
- p += 8;
- break;
-
- case DW_EH_PE_sdata2:
- result = u->s2;
- p += 2;
- break;
- case DW_EH_PE_sdata4:
- result = u->s4;
- p += 4;
- break;
- case DW_EH_PE_sdata8:
- result = u->s8;
- p += 8;
- break;
-
- default:
- abort ();
- }
-
- if (result != 0)
- switch (encoding & 0xf0)
- {
- case DW_EH_PE_absptr:
- break;
-
- case DW_EH_PE_pcrel:
- // Define as relative to the beginning of the pointer.
- result += (_Unwind_Ptr) u;
- break;
-
- case DW_EH_PE_textrel:
- case DW_EH_PE_datarel:
- // FIXME.
- abort ();
-
- case DW_EH_PE_funcrel:
- result += _Unwind_GetRegionStart (context);
- break;
-
- default:
- abort ();
- }
-
- *val = result;
- return p;
-}
-
-static inline const unsigned char *
-read_uleb128 (const unsigned char *p, _Unwind_Ptr *val)
-{
- return read_encoded_value (0, DW_EH_PE_uleb128, p, val);
-}
-
-static inline const unsigned char *
-read_sleb128 (const unsigned char *p, _Unwind_Ptr *val)
-{
- return read_encoded_value (0, DW_EH_PE_sleb128, p, val);
-}
-
+#include "unwind-pe.h"
\f
struct lsda_header_info
{
parse_lsda_header (_Unwind_Context *context, const unsigned char *p,
lsda_header_info *info)
{
- _Unwind_Ptr tmp;
+ _uleb128_t tmp;
unsigned char lpstart_encoding;
info->Start = (context ? _Unwind_GetRegionStart (context) : 0);
info->ttype_encoding = *p++;
if (info->ttype_encoding != DW_EH_PE_omit)
{
+#if _GLIBCXX_OVERRIDE_TTYPE_ENCODING
+ /* Older ARM EABI toolchains set this value incorrectly, so use a
+ hardcoded OS-specific format. */
+ info->ttype_encoding = _GLIBCXX_OVERRIDE_TTYPE_ENCODING;
+#endif
p = read_uleb128 (p, &tmp);
info->TType = p + tmp;
}
return p;
}
-static jclass
+static void **
get_ttype_entry (_Unwind_Context *context, lsda_header_info *info, long i)
{
_Unwind_Ptr ptr;
i *= size_of_encoded_value (info->ttype_encoding);
read_encoded_value (context, info->ttype_encoding, info->TType - i, &ptr);
- return reinterpret_cast<jclass>(ptr);
+ return reinterpret_cast<void **>(ptr);
}
-
// Using a different personality function name causes link failures
// when trying to mix code using different exception handling models.
#ifdef SJLJ_EXCEPTIONS
#define PERSONALITY_FUNCTION __gcj_personality_v0
#endif
+#ifdef __ARM_EABI_UNWINDER__
+
+#define CONTINUE_UNWINDING \
+ do \
+ { \
+ if (__gnu_unwind_frame(ue_header, context) != _URC_OK) \
+ return _URC_FAILURE; \
+ return _URC_CONTINUE_UNWIND; \
+ } \
+ while (0)
+
+extern "C" _Unwind_Reason_Code
+PERSONALITY_FUNCTION (_Unwind_State state,
+ struct _Unwind_Exception* ue_header,
+ struct _Unwind_Context* context)
+#else
+
+#define CONTINUE_UNWINDING return _URC_CONTINUE_UNWIND
+
extern "C" _Unwind_Reason_Code
PERSONALITY_FUNCTION (int version,
_Unwind_Action actions,
_Unwind_Exception_Class exception_class,
struct _Unwind_Exception *ue_header,
struct _Unwind_Context *context)
+
+#endif
{
java_exception_header *xh = get_exception_header_from_ue (ue_header);
int handler_switch_value;
bool saw_cleanup;
bool saw_handler;
+ bool foreign_exception;
+ int ip_before_insn = 0;
+
+#ifdef __ARM_EABI_UNWINDER__
+ _Unwind_Action actions;
+
+ switch (state & _US_ACTION_MASK)
+ {
+ case _US_VIRTUAL_UNWIND_FRAME:
+ actions = _UA_SEARCH_PHASE;
+ break;
+
+ case _US_UNWIND_FRAME_STARTING:
+ actions = _UA_CLEANUP_PHASE;
+ if (!(state & _US_FORCE_UNWIND)
+ && ue_header->barrier_cache.sp == _Unwind_GetGR(context, 13))
+ actions |= _UA_HANDLER_FRAME;
+ break;
+
+ case _US_UNWIND_FRAME_RESUME:
+ CONTINUE_UNWINDING;
+ break;
+
+ default:
+ std::abort();
+ }
+ actions |= state & _US_FORCE_UNWIND;
+
+ // We don't know which runtime we're working with, so can't check this.
+ // However the ABI routines hide this from us, and we don't actually need
+ // to know.
+ foreign_exception = false;
+ // The dwarf unwinder assumes the context structure holds things like the
+ // function and LSDA pointers. The ARM implementation caches these in
+ // the exception header (UCB). To avoid rewriting everything we make the
+ // virtual IP register point at the UCB.
+ ip = (_Unwind_Ptr) ue_header;
+ _Unwind_SetGR(context, 12, ip);
+#else
// Interface version check.
if (version != 1)
return _URC_FATAL_PHASE1_ERROR;
+ foreign_exception = exception_class != __gcj_exception_class;
+#endif
// Shortcut for phase 2 found handler for domestic exception.
if (actions == (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME)
- && exception_class == __gcj_exception_class)
+ && !foreign_exception)
{
handler_switch_value = xh->handlerSwitchValue;
landing_pad = xh->landingPad;
goto install_context;
}
- // FIXME: In Phase 1, record _Unwind_GetIP in xh->obj as a part of
+ // FIXME: In Phase 1, record _Unwind_GetIPInfo in xh->obj as a part of
// the stack trace for this exception. This will only collect Java
// frames, but perhaps that is acceptable.
- // FIXME2: _Unwind_GetIP is nonsensical for SJLJ, being a call-site
+ // FIXME2: _Unwind_GetIPInfo is nonsensical for SJLJ, being a call-site
// index instead of a PC value. We could perhaps arrange for
// _Unwind_GetRegionStart to return context->fc->jbuf[1], which
// is the address of the handler label for __builtin_longjmp, but
// If no LSDA, then there are no handlers or cleanups.
if (! language_specific_data)
- return _URC_CONTINUE_UNWIND;
+ CONTINUE_UNWINDING;
// Parse the LSDA header.
p = parse_lsda_header (context, language_specific_data, &info);
- ip = _Unwind_GetIP (context) - 1;
+#ifdef HAVE_GETIPINFO
+ ip = _Unwind_GetIPInfo (context, &ip_before_insn);
+#else
+ ip = _Unwind_GetIP (context);
+#endif
+ if (! ip_before_insn)
+ --ip;
landing_pad = 0;
action_record = 0;
handler_switch_value = 0;
return _URC_CONTINUE_UNWIND;
else
{
- _Unwind_Ptr cs_lp, cs_action;
+ _uleb128_t cs_lp, cs_action;
do
{
p = read_uleb128 (p, &cs_lp);
// Search the call-site table for the action associated with this IP.
while (p < info.action_table)
{
- _Unwind_Ptr cs_start, cs_len, cs_lp, cs_action;
+ _Unwind_Ptr cs_start, cs_len, cs_lp;
+ _uleb128_t cs_action;
// Note that all call-site encodings are "absolute" displacements.
p = read_encoded_value (0, info.call_site_encoding, p, &cs_start);
// If ip is not present in the table, C++ would call terminate.
// ??? It is perhaps better to tweek the LSDA so that no-action
// is mapped to no-entry for Java.
- return _URC_CONTINUE_UNWIND;
+ CONTINUE_UNWINDING;
found_something:
saw_cleanup = false;
else
{
// Otherwise we have a catch handler.
- signed long ar_filter, ar_disp;
+ _sleb128_t ar_filter, ar_disp;
while (1)
{
- _Unwind_Ptr tmp;
-
p = action_record;
- p = read_sleb128 (p, &tmp); ar_filter = tmp;
- read_sleb128 (p, &tmp); ar_disp = tmp;
+ p = read_sleb128 (p, &ar_filter);
+ read_sleb128 (p, &ar_disp);
if (ar_filter == 0)
{
// During forced unwinding, we only run cleanups. With a
// foreign exception class, we have no class info to match.
else if ((actions & _UA_FORCE_UNWIND)
- || exception_class != __gcj_exception_class)
+ || foreign_exception)
;
else if (ar_filter > 0)
{
// Positive filter values are handlers.
- jclass catch_type = get_ttype_entry (context, &info, ar_filter);
+ void **catch_word = get_ttype_entry (context, &info, ar_filter);
+ jclass catch_type = (jclass)*catch_word;
- // The catch_type is either a (java::lang::Class*) or
- // is one more than a (Utf8Const*).
- if ((size_t)catch_type & 1)
- catch_type = _Jv_FindClass ((Utf8Const*)catch_type - 1, NULL);
+ // FIXME: This line is a kludge to work around exception
+ // handlers written in C++, which don't yet use indirect
+ // dispatch.
+ if (catch_type == *(void **)&java::lang::Class::class$)
+ catch_type = (jclass)catch_word;
if (_Jv_IsInstanceOf (xh->value, catch_type))
{
}
if (! saw_handler && ! saw_cleanup)
- return _URC_CONTINUE_UNWIND;
+ CONTINUE_UNWINDING;
if (actions & _UA_SEARCH_PHASE)
{
if (! saw_handler)
- return _URC_CONTINUE_UNWIND;
+ CONTINUE_UNWINDING;
// For domestic exceptions, we cache data from phase 1 for phase 2.
- if (exception_class == __gcj_exception_class)
+ if (! foreign_exception)
{
xh->handlerSwitchValue = handler_switch_value;
xh->landingPad = landing_pad;
_Unwind_SetGR (context, __builtin_eh_return_data_regno (1),
handler_switch_value);
_Unwind_SetIP (context, landing_pad);
+#ifdef __ARM_EABI_UNWINDER__
+ if (saw_cleanup)
+ __cxa_begin_cleanup(ue_header);
+#endif
return _URC_INSTALL_CONTEXT;
}