OSDN Git Service

2012-04-09 Terry Guo <terry.guo@arm.com>
[pf3gnuchains/gcc-fork.git] / libjava / exception.cc
index 41f7676..56f25ad 100644 (file)
@@ -1,6 +1,7 @@
 // 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.
 
@@ -15,25 +16,23 @@ details.  */
 
 #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;
@@ -60,6 +59,21 @@ struct java_exception_header
   _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' 
@@ -77,6 +91,7 @@ get_exception_header_from_ue (_Unwind_Exception *exc)
 {
   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. */
@@ -84,206 +99,37 @@ get_exception_header_from_ue (_Unwind_Exception *exc)
 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
 {
@@ -299,7 +145,7 @@ static const unsigned char *
 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);
@@ -315,6 +161,11 @@ parse_lsda_header (_Unwind_Context *context, const unsigned char *p,
   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;
     }
@@ -330,7 +181,7 @@ parse_lsda_header (_Unwind_Context *context, const unsigned char *p,
   return p;
 }
 
-static jclass
+static void **
 get_ttype_entry (_Unwind_Context *context, lsda_header_info *info, long i)
 {
   _Unwind_Ptr ptr;
@@ -338,10 +189,9 @@ get_ttype_entry (_Unwind_Context *context, lsda_header_info *info, long i)
   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
@@ -351,12 +201,33 @@ get_ttype_entry (_Unwind_Context *context, lsda_header_info *info, long i)
 #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);
 
@@ -368,25 +239,66 @@ PERSONALITY_FUNCTION (int version,
   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
@@ -397,11 +309,17 @@ PERSONALITY_FUNCTION (int version,
 
   // 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;
@@ -415,7 +333,7 @@ PERSONALITY_FUNCTION (int version,
     return _URC_CONTINUE_UNWIND;
   else
     {
-      _Unwind_Ptr cs_lp, cs_action;
+      _uleb128_t cs_lp, cs_action;
       do
        {
          p = read_uleb128 (p, &cs_lp);
@@ -434,7 +352,8 @@ PERSONALITY_FUNCTION (int version,
   // 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);
@@ -459,7 +378,7 @@ PERSONALITY_FUNCTION (int version,
   // 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;
@@ -480,15 +399,13 @@ PERSONALITY_FUNCTION (int version,
   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)
            {
@@ -499,19 +416,21 @@ PERSONALITY_FUNCTION (int version,
          // 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))
                {
@@ -537,15 +456,15 @@ PERSONALITY_FUNCTION (int version,
     }
 
   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;
@@ -559,5 +478,9 @@ PERSONALITY_FUNCTION (int version,
   _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;
 }