exception.cc 11.3 KB
Newer Older
Tom Tromey committed
1 2
// Functions for Exception Support for Java.

3
/* Copyright (C) 1998, 1999, 2001, 2002  Free Software Foundation
Tom Tromey committed
4 5 6 7 8 9 10 11 12 13

   This file is part of libgcj.

This software is copyrighted work licensed under the terms of the
Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
details.  */

#include <config.h>

#include <stddef.h>
14
#include <stdlib.h>
Tom Tromey committed
15 16 17

#include <java/lang/Class.h>
#include <java/lang/NullPointerException.h>
Andrew Haley committed
18 19 20
#include <gnu/gcj/runtime/StackTrace.h> 
#include <gnu/gcj/runtime/MethodRef.h> 
#include <gnu/gcj/RawData.h> 
Tom Tromey committed
21
#include <gcj/cni.h>
Tom Tromey committed
22 23
#include <jvm.h>

24 25 26 27 28
// 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
{
29 30
  static __attribute__ ((__noreturn__)) void
  abort ()
31 32 33 34
  {
    ::abort ();
  }
}
35
#include "unwind.h"
Tom Tromey committed
36

37 38 39 40 41
struct alignment_test_struct
{
  char space;
  char end[0] __attribute__((aligned));
};
Tom Tromey committed
42

43
struct java_exception_header
Tom Tromey committed
44
{
45 46 47
  /* Cache handler details between Phase 1 and Phase 2.  */
  _Unwind_Ptr landingPad;
  int handlerSwitchValue;
Tom Tromey committed
48

49 50 51 52 53 54 55 56 57
  /* The object being thrown.  Compiled code expects this to be immediately
     before the generic exception header.  Which is complicated by the fact
     that _Unwind_Exception is ((aligned)).  */

  char pad[sizeof(jthrowable) < sizeof(alignment_test_struct)
	   ? sizeof(alignment_test_struct) - sizeof(jthrowable) : 0]
    __attribute__((aligned));

  jthrowable value;
Tom Tromey committed
58

59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
  /* The generic exception header.  */
  _Unwind_Exception unwindHeader;
};

// This is the exception class we report -- "GNUCJAVA".
const _Unwind_Exception_Class __gcj_exception_class
= ((((((((_Unwind_Exception_Class) 'G' 
         << 8 | (_Unwind_Exception_Class) 'N')
        << 8 | (_Unwind_Exception_Class) 'U')
       << 8 | (_Unwind_Exception_Class) 'C')
      << 8 | (_Unwind_Exception_Class) 'J')
     << 8 | (_Unwind_Exception_Class) 'A')
    << 8 | (_Unwind_Exception_Class) 'V')
   << 8 | (_Unwind_Exception_Class) 'A');


static inline java_exception_header *
get_exception_header_from_ue (_Unwind_Exception *exc)
{
  return reinterpret_cast<java_exception_header *>(exc + 1) - 1;
Tom Tromey committed
79 80
}

81 82
/* Perform a throw, Java style. Throw will unwind through this call,
   so there better not be any handlers or exception thrown here. */
Tom Tromey committed
83

84 85
extern "C" void
_Jv_Throw (jthrowable value)
Tom Tromey committed
86
{
87
  java_exception_header *xh
88
    = static_cast<java_exception_header *>(_Jv_AllocRawObj (sizeof (*xh)));
89 90 91 92

  if (value == NULL)
    value = new java::lang::NullPointerException ();
  xh->value = value;
Tom Tromey committed
93

94 95
  xh->unwindHeader.exception_class = __gcj_exception_class;
  xh->unwindHeader.exception_cleanup = NULL;
Tom Tromey committed
96

97 98 99 100 101 102 103 104
  /* 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);
#else
  code = _Unwind_RaiseException (&xh->unwindHeader);
#endif
Tom Tromey committed
105

106 107 108 109 110 111 112
  /* 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.  */
113
  abort();
Tom Tromey committed
114 115
}

116

117
#include "unwind-pe.h"
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132

struct lsda_header_info
{
  _Unwind_Ptr Start;
  _Unwind_Ptr LPStart;
  const unsigned char *TType;
  const unsigned char *action_table;
  unsigned char ttype_encoding;
  unsigned char call_site_encoding;
};

static const unsigned char *
parse_lsda_header (_Unwind_Context *context, const unsigned char *p,
		   lsda_header_info *info)
{
133
  _Unwind_Word tmp;
134 135 136
  unsigned char lpstart_encoding;

  info->Start = (context ? _Unwind_GetRegionStart (context) : 0);
Tom Tromey committed
137

138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
  // Find @LPStart, the base to which landing pad offsets are relative.
  lpstart_encoding = *p++;
  if (lpstart_encoding != DW_EH_PE_omit)
    p = read_encoded_value (context, lpstart_encoding, p, &info->LPStart);
  else
    info->LPStart = info->Start;

  // Find @TType, the base of the handler and exception spec type data.
  info->ttype_encoding = *p++;
  if (info->ttype_encoding != DW_EH_PE_omit)
    {
      p = read_uleb128 (p, &tmp);
      info->TType = p + tmp;
    }
  else
    info->TType = 0;

  // The encoding and length of the call-site table; the action table
  // immediately follows.
  info->call_site_encoding = *p++;
  p = read_uleb128 (p, &tmp);
  info->action_table = p + tmp;

  return p;
}

164
static void **
165
get_ttype_entry (_Unwind_Context *context, lsda_header_info *info, long i)
Tom Tromey committed
166
{
167 168 169 170 171
  _Unwind_Ptr ptr;

  i *= size_of_encoded_value (info->ttype_encoding);
  read_encoded_value (context, info->ttype_encoding, info->TType - i, &ptr);

172
  return reinterpret_cast<void **>(ptr);
Tom Tromey committed
173 174
}

Tom Tromey committed
175

176 177
// Using a different personality function name causes link failures
// when trying to mix code using different exception handling models.
178
#ifdef SJLJ_EXCEPTIONS
179 180 181 182
#define PERSONALITY_FUNCTION	__gcj_personality_sj0
#define __builtin_eh_return_data_regno(x) x
#else
#define PERSONALITY_FUNCTION	__gcj_personality_v0
183 184
#endif

185 186 187 188 189 190
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)
Tom Tromey committed
191
{
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
  java_exception_header *xh = get_exception_header_from_ue (ue_header);

  lsda_header_info info;
  const unsigned char *language_specific_data;
  const unsigned char *action_record;
  const unsigned char *p;
  _Unwind_Ptr landing_pad, ip;
  int handler_switch_value;
  bool saw_cleanup;
  bool saw_handler;


  // Interface version check.
  if (version != 1)
    return _URC_FATAL_PHASE1_ERROR;

  // Shortcut for phase 2 found handler for domestic exception.
  if (actions == (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME)
      && exception_class == __gcj_exception_class)
Tom Tromey committed
211
    {
212 213 214
      handler_switch_value = xh->handlerSwitchValue;
      landing_pad = xh->landingPad;
      goto install_context;
Tom Tromey committed
215
    }
Tom Tromey committed
216

217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
  // FIXME: In Phase 1, record _Unwind_GetIP 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
  // 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
  // there is no solution for DONT_USE_BUILTIN_SETJMP.

  language_specific_data = (const unsigned char *)
    _Unwind_GetLanguageSpecificData (context);

  // If no LSDA, then there are no handlers or cleanups.
  if (! language_specific_data)
    return _URC_CONTINUE_UNWIND;

  // Parse the LSDA header.
  p = parse_lsda_header (context, language_specific_data, &info);
  ip = _Unwind_GetIP (context) - 1;
  landing_pad = 0;
  action_record = 0;
  handler_switch_value = 0;

Tom Tromey committed
240
#ifdef SJLJ_EXCEPTIONS
241 242 243 244 245 246 247 248
  // The given "IP" is an index into the call-site table, with two
  // exceptions -- -1 means no-action, and 0 means terminate.  But
  // since we're using uleb128 values, we've not got random access
  // to the array.
  if ((int) ip <= 0)
    return _URC_CONTINUE_UNWIND;
  else
    {
249
      _Unwind_Word cs_lp, cs_action;
250 251 252 253 254 255 256 257 258 259 260 261 262 263
      do
	{
	  p = read_uleb128 (p, &cs_lp);
	  p = read_uleb128 (p, &cs_action);
	}
      while (--ip);

      // Can never have null landing pad for sjlj -- that would have
      // been indicated by a -1 call site index.
      landing_pad = cs_lp + 1;
      if (cs_action)
	action_record = info.action_table + cs_action - 1;
      goto found_something;
    }
Tom Tromey committed
264
#else
265 266 267
  // Search the call-site table for the action associated with this IP.
  while (p < info.action_table)
    {
268 269
      _Unwind_Ptr cs_start, cs_len, cs_lp;
      _Unwind_Word cs_action;
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289

      // Note that all call-site encodings are "absolute" displacements.
      p = read_encoded_value (0, info.call_site_encoding, p, &cs_start);
      p = read_encoded_value (0, info.call_site_encoding, p, &cs_len);
      p = read_encoded_value (0, info.call_site_encoding, p, &cs_lp);
      p = read_uleb128 (p, &cs_action);

      // The table is sorted, so if we've passed the ip, stop.
      if (ip < info.Start + cs_start)
	p = info.action_table;
      else if (ip < info.Start + cs_start + cs_len)
	{
	  if (cs_lp)
	    landing_pad = info.LPStart + cs_lp;
	  if (cs_action)
	    action_record = info.action_table + cs_action - 1;
	  goto found_something;
	}
    }
#endif // SJLJ_EXCEPTIONS
Tom Tromey committed
290

291 292 293 294
  // 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;
Tom Tromey committed
295

296 297 298
 found_something:
  saw_cleanup = false;
  saw_handler = false;
Tom Tromey committed
299

300
  if (landing_pad == 0)
Tom Tromey committed
301
    {
302 303 304 305 306 307 308 309 310 311 312 313 314
      // If ip is present, and has a null landing pad, there are
      // no cleanups or handlers to be run.
    }
  else if (action_record == 0)
    {
      // If ip is present, has a non-null landing pad, and a null
      // action table offset, then there are only cleanups present.
      // Cleanups use a zero switch value, as set above.
      saw_cleanup = true;
    }
  else
    {
      // Otherwise we have a catch handler.
315
      _Unwind_Sword ar_filter, ar_disp;
316 317 318 319

      while (1)
	{
	  p = action_record;
320 321
	  p = read_sleb128 (p, &ar_filter);
	  read_sleb128 (p, &ar_disp);
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338

	  if (ar_filter == 0)
	    {
	      // Zero filter values are cleanups.
	      saw_cleanup = true;
	    }

	  // 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)
	    ;

	  else if (ar_filter > 0)
	    {
	      // Positive filter values are handlers.

339 340 341 342 343 344 345 346
	      void **catch_word = get_ttype_entry (context, &info, ar_filter);
	      jclass catch_type = (jclass)*catch_word;

	      // 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;
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361

	      if (_Jv_IsInstanceOf (xh->value, catch_type))
		{
		  handler_switch_value = ar_filter;
		  saw_handler = true;
		  break;
		}
	    }
	  else
	    {
	      // Negative filter values are exception specifications,
	      // which Java does not use.
	      // ??? Perhaps better to make them an index into a table
	      // of null-terminated strings instead of playing games
	      // with Utf8Const+1 as above.
362
	      abort ();
363 364 365 366 367 368
	    }

	  if (ar_disp == 0)
	    break;
	  action_record = p + ar_disp;
	}
Tom Tromey committed
369 370
    }

371 372
  if (! saw_handler && ! saw_cleanup)
    return _URC_CONTINUE_UNWIND;
Tom Tromey committed
373

374 375 376 377 378 379 380 381 382 383 384 385 386
  if (actions & _UA_SEARCH_PHASE)
    {
      if (! saw_handler)
	return _URC_CONTINUE_UNWIND;

      // For domestic exceptions, we cache data from phase 1 for phase 2.
      if (exception_class == __gcj_exception_class)
        {
          xh->handlerSwitchValue = handler_switch_value;
          xh->landingPad = landing_pad;
	}
      return _URC_HANDLER_FOUND;
    }
Tom Tromey committed
387

388 389 390 391 392 393 394
 install_context:
  _Unwind_SetGR (context, __builtin_eh_return_data_regno (0),
		 (_Unwind_Ptr) &xh->unwindHeader);
  _Unwind_SetGR (context, __builtin_eh_return_data_regno (1),
		 handler_switch_value);
  _Unwind_SetIP (context, landing_pad);
  return _URC_INSTALL_CONTEXT;
Tom Tromey committed
395
}