Exemple #1
0
static const unsigned char *
parse_lsda_header (struct _Unwind_Context *context, const unsigned char *p,
		   struct lsda_header_info *info)
{
  _uleb128_t tmp;
  unsigned char lpstart_encoding;

  info->Start = (context ? _Unwind_GetRegionStart (context) : 0);

  /* 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)
    {
#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;
    }
  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;
}
static const unsigned char *
parse_lsda_header (struct _Unwind_Context *context, const unsigned char *p,
		   struct lsda_header_info *info)
{
  _uleb128_t tmp;
  unsigned char lpstart_encoding;

  info->Start = (context ? _Unwind_GetRegionStart (context) : 0);

  /* 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;
}
Exemple #3
0
_Unwind_Reason_Code
PERSONALITY_FUNCTION (int version,
		      _Unwind_Action actions,
		      _Unwind_Exception_Class exception_class ATTRIBUTE_UNUSED,
		      struct _Unwind_Exception *ue_header,
		      struct _Unwind_Context *context)
/* APPLE LOCAL begin LLVM */
#endif
/* APPLE LOCAL end LLVM */
{
  lsda_header_info info;
  const unsigned char *language_specific_data, *p, *action_record;
  _Unwind_Ptr landing_pad, ip;

/* APPLE LOCAL begin LLVM */
#ifdef __ARM_EABI_UNWINDER__
  if (state != _US_UNWIND_FRAME_STARTING)
    CONTINUE_UNWINDING;

  /* 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
/* APPLE LOCAL end LLVM */
  if (version != 1)
    return _URC_FATAL_PHASE1_ERROR;

  /* Currently we only support cleanups for C.  */
  if ((actions & _UA_CLEANUP_PHASE) == 0)
/* APPLE LOCAL begin LLVM */
    CONTINUE_UNWINDING;
#endif
/* APPLE LOCAL end LLVM */

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

  /* If no LSDA, then there are no handlers or cleanups.  */
  if (! language_specific_data)
/* APPLE LOCAL begin LLVM */
    CONTINUE_UNWINDING;
/* APPLE LOCAL end LLVM */

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

#ifdef __USING_SJLJ_EXCEPTIONS__
  /* 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
    {
      _Unwind_Word cs_lp, cs_action;
      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;
    }
#else
  /* 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;
      _Unwind_Word cs_action;

      /* 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

  /* IP is not in table.  No associated cleanups.  */
  /* ??? This is where C++ calls std::terminate to catch throw
     from a destructor.  */
/* APPLE LOCAL begin LLVM */
  CONTINUE_UNWINDING;
/* APPLE LOCAL end LLVM */

 found_something:
  if (landing_pad == 0)
    {
      /* IP is present, but has a null landing pad.
	 No handler to be run.  */
/* APPLE LOCAL begin LLVM */
      CONTINUE_UNWINDING;
/* APPLE LOCAL end LLVM */
    }

  _Unwind_SetGR (context, __builtin_eh_return_data_regno (0),
		 (_Unwind_Ptr) ue_header);
  _Unwind_SetGR (context, __builtin_eh_return_data_regno (1), 0);
  _Unwind_SetIP (context, landing_pad);
  return _URC_INSTALL_CONTEXT;
}
Exemple #4
0
PERSONALITY_FUNCTION (int version,
		      _Unwind_Action actions,
		      _Unwind_Exception_Class exception_class,
		      struct _Unwind_Exception *ue_header,
		      struct _Unwind_Context *context)
#endif
{
  struct ObjcException *xh = (struct ObjcException *) ue_header;

  struct 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;
  int saw_cleanup = 0, saw_handler;
  void *return_object;
  int 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:
      abort();
    }
  actions |= state & _US_FORCE_UNWIND;

  foreign_exception = 0;

  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 == __objc_exception_class);
#endif

  /* Shortcut for phase 2 found handler for domestic exception.  */
  if (actions == (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME)
      && !foreign_exception)
    {
#ifdef __ARM_EABI_UNWINDER__
      handler_switch_value = (int) ue_header->barrier_cache.bitpattern[1];
      landing_pad = (_Unwind_Ptr) ue_header->barrier_cache.bitpattern[3];
#else
      handler_switch_value = xh->handlerSwitchValue;
      landing_pad = xh->landingPad;
#endif
      goto install_context;
    }

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

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

  /* Parse the LSDA header.  */
  p = parse_lsda_header (context, language_specific_data, &info);
  info.ttype_base = base_of_encoded_value (info.ttype_encoding, context);
#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;

#ifdef SJLJ_EXCEPTIONS
  /* 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
    {
      _Unwind_Word cs_lp, cs_action;
      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;
    }
#else
  /* 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;
      _Unwind_Word cs_action;

      /* 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  */

  /* If ip is not present in the table, C++ would call terminate.  */
  /* ??? As with Java, it's perhaps better to tweek the LSDA to
     that no-action is mapped to no-entry.  */
  return _URC_CONTINUE_UNWIND;

 found_something:
  saw_cleanup = 0;
  saw_handler = 0;

  if (landing_pad == 0)
    {
      /* 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 = 1;
    }
  else
    {
      /* Otherwise we have a catch handler.  */
      _Unwind_Sword ar_filter, ar_disp;

      while (1)
	{
	  p = action_record;
	  p = read_sleb128 (p, &ar_filter);
	  read_sleb128 (p, &ar_disp);

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

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

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

	      Class catch_type = get_ttype_entry (&info, ar_filter);

	      if (isKindOf (xh->value, catch_type))
		{
		  handler_switch_value = ar_filter;
		  saw_handler = 1;
		  break;
		}
	    }
	  else
	    {
	      /* Negative filter values are exception specifications,
	         which Objective-C does not use.  */
	      abort ();
	    }

	  if (ar_disp == 0)
	    break;
	  action_record = p + ar_disp;
	}
    }

  if (! saw_handler && ! saw_cleanup)
    CONTINUE_UNWINDING;

  if (actions & _UA_SEARCH_PHASE)
    {
      if (!saw_handler)
	CONTINUE_UNWINDING;

      /* For domestic exceptions, we cache data from phase 1 for phase 2.  */
      if (!foreign_exception)
        {
#ifdef __ARM_EABI_UNWIDER__
	  ue_header->barrier_cache.sp = _Unwind_GetGR(context, 13);
	  ue_header->barrier_cache.bitpattern[1]
	    = (_uw) handler_switch_value;
	  ue_header->barrier_cache.bitpattern[3] = (_uw) landing_pad;
#else
          xh->handlerSwitchValue = handler_switch_value;
          xh->landingPad = landing_pad;
#endif
	}
      return _URC_HANDLER_FOUND;
    }

 install_context:
  if (saw_cleanup == 0)
    {
      return_object = xh->value;
      if (!(actions & _UA_SEARCH_PHASE))
	_Unwind_DeleteException(&xh->base);
    }
  
  _Unwind_SetGR (context, __builtin_eh_return_data_regno (0),
		 __builtin_extend_pointer (saw_cleanup ? xh : return_object));
  _Unwind_SetGR (context, __builtin_eh_return_data_regno (1),
		 handler_switch_value);
  _Unwind_SetIP (context, landing_pad);
  return _URC_INSTALL_CONTEXT;
}
Exemple #5
0
_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
{
  lsda_header_info info;
  const unsigned char *language_specific_data, *p, *action_record;
  _Unwind_Ptr landing_pad, ip;
  int ip_before_insn = 0;
  _Bool is_foreign;
  G *g;

#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:
      abort();
    }
  actions |= state & _US_FORCE_UNWIND;

  is_foreign = 0;

  /* 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
  if (version != 1)
    return _URC_FATAL_PHASE1_ERROR;

  is_foreign = exception_class != __go_exception_class;
#endif

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

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

  /* Parse the LSDA header.  */
  p = parse_lsda_header (context, language_specific_data, &info);
#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 = NULL;

#ifdef __USING_SJLJ_EXCEPTIONS__
  /* 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
    {
      _uleb128_t cs_lp, cs_action;
      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 = (_Unwind_Ptr)cs_lp + 1;
      if (cs_action)
	action_record = info.action_table + cs_action - 1;
      goto found_something;
    }
#else
  /* 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;
      _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);
      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

  /* IP is not in table.  No associated cleanups.  */
  CONTINUE_UNWINDING;

 found_something:
  if (landing_pad == 0)
    {
      /* IP is present, but has a null landing pad.
	 No handler to be run.  */
      CONTINUE_UNWINDING;
    }

  if (actions & _UA_SEARCH_PHASE)
    {
      if (action_record == 0)
	{
	  /* This indicates a cleanup rather than an exception
	     handler.  */
	  CONTINUE_UNWINDING;
	}

      return _URC_HANDLER_FOUND;
    }

  /* It's possible for g to be NULL here for an exception thrown by a
     language other than Go.  */
  g = runtime_g ();
  if (g == NULL)
    {
      if (!is_foreign)
	abort ();
    }
  else
    {
      g->exception = ue_header;
      g->isforeign = is_foreign;
    }

  _Unwind_SetGR (context, __builtin_eh_return_data_regno (0),
		 (_Unwind_Ptr) ue_header);
  _Unwind_SetGR (context, __builtin_eh_return_data_regno (1), 0);
  _Unwind_SetIP (context, landing_pad);
  return _URC_INSTALL_CONTEXT;
}
Exemple #6
0
static const uint8_t *
parse_eh_frame_hdr (const uint8_t *hdr, size_t hdr_size, GElf_Addr hdr_vaddr,
		    const GElf_Ehdr *ehdr, GElf_Addr *eh_frame_vaddr,
		    size_t *table_entries, uint8_t *table_encoding)
{
  const uint8_t *h = hdr;

  if (*h++ != 1)		/* version */
    return (void *) -1l;

  uint8_t eh_frame_ptr_encoding = *h++;
  uint8_t fde_count_encoding = *h++;
  uint8_t fde_table_encoding = *h++;

  if (eh_frame_ptr_encoding == DW_EH_PE_omit)
    return (void *) -1l;

  /* Dummy used by read_encoded_value.  */
  Elf_Data_Scn dummy_cfi_hdr_data =
    {
      .d = { .d_buf = (void *) hdr, .d_size = hdr_size }
    };
  Dwarf_CFI dummy_cfi =
    {
      .e_ident = ehdr->e_ident,
      .datarel = hdr_vaddr,
      .frame_vaddr = hdr_vaddr,
      .data = &dummy_cfi_hdr_data,
    };

  if (unlikely (read_encoded_value (&dummy_cfi, eh_frame_ptr_encoding, &h,
				    eh_frame_vaddr)))
    return (void *) -1l;

  if (fde_count_encoding != DW_EH_PE_omit)
    {
      Dwarf_Word fde_count;
      if (unlikely (read_encoded_value (&dummy_cfi, fde_count_encoding, &h,
					&fde_count)))
	return (void *) -1l;
      if (fde_count != 0 && (size_t) fde_count == fde_count
	  && fde_table_encoding != DW_EH_PE_omit
	  && (fde_table_encoding &~ DW_EH_PE_signed) != DW_EH_PE_uleb128)
	{
	  *table_entries = fde_count;
	  *table_encoding = fde_table_encoding;
	  return h;
	}
    }

  return NULL;
}

static Dwarf_CFI *
getcfi_gnu_eh_frame (Elf *elf, const GElf_Ehdr *ehdr, const GElf_Phdr *phdr)
{
  if (unlikely (phdr->p_filesz < 4))
    goto invalid;

  Elf_Data *data = elf_getdata_rawchunk (elf, phdr->p_offset, phdr->p_filesz,
					 ELF_T_BYTE);
  if (data == NULL)
    {
    invalid_hdr:
    invalid:
      /* XXX might be read error or corrupt phdr */
      __libdw_seterrno (DWARF_E_INVALID_CFI);
      return NULL;
    }

  Dwarf_Addr eh_frame_ptr;
  size_t search_table_entries;
  uint8_t search_table_encoding;
  const uint8_t *search_table = parse_eh_frame_hdr (data->d_buf, phdr->p_filesz,
						    phdr->p_vaddr, ehdr,
						    &eh_frame_ptr,
						    &search_table_entries,
						    &search_table_encoding);
  if (search_table == (void *) -1l)
    goto invalid_hdr;

  Dwarf_Off eh_frame_offset = eh_frame_ptr - phdr->p_vaddr + phdr->p_offset;
  Dwarf_Word eh_frame_size = 0;

  /* XXX we have no way without section headers to know the size
     of the .eh_frame data.  Calculate the largest it might possibly be.
     This won't be wasteful if the file is already mmap'd, but if it isn't
     it might be quite excessive.  */
  size_t filesize;
  if (elf_rawfile (elf, &filesize) != NULL)
    eh_frame_size = filesize - eh_frame_offset;

  data = elf_getdata_rawchunk (elf, eh_frame_offset, eh_frame_size, ELF_T_BYTE);
  if (data == NULL)
    {
      __libdw_seterrno (DWARF_E_INVALID_ELF); /* XXX might be read error */
      return NULL;
    }
  Dwarf_CFI *cfi = allocate_cfi (elf, eh_frame_ptr);
  if (cfi != NULL)
    {
      cfi->data = (Elf_Data_Scn *) data;

      if (search_table != NULL)
	{
	  cfi->search_table = search_table;
	  cfi->search_table_vaddr = phdr->p_vaddr;
	  cfi->search_table_encoding = search_table_encoding;
	  cfi->search_table_entries = search_table_entries;
	}
    }
  return cfi;
}

/* Search the phdrs for PT_GNU_EH_FRAME.  */
static Dwarf_CFI *
getcfi_phdr (Elf *elf, const GElf_Ehdr *ehdr)
{
  size_t phnum;
  if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
    return NULL;

  for (size_t i = 0; i < phnum; ++i)
    {
      GElf_Phdr phdr_mem;
      GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
      if (unlikely (phdr == NULL))
	return NULL;
      if (phdr->p_type == PT_GNU_EH_FRAME)
	return getcfi_gnu_eh_frame (elf, ehdr, phdr);
    }

  __libdw_seterrno (DWARF_E_NO_DWARF);
  return NULL;
}
Exemple #7
0
_Unwind_Reason_Code __runa_personality(int version,
  _Unwind_Action actions, _Unwind_Exception_Class exception_class,
  struct _Unwind_Exception *ue_header, struct _Unwind_Context *context) {

  enum found_handler_type {
    found_nothing, found_terminate, found_cleanup, found_handler
  } found_type;

  struct 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;
  void* thrown_ptr = 0;
  bool foreign_exception;
  int ip_before_insn = 0;

  //__cxa_exception* xh = __get_exception_header_from_ue(ue_header);

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

  foreign_exception = exception_class != RUNA_CLASS;

  // Shortcut for phase 2 found handler for domestic exception.
  if (actions == (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME) && !foreign_exception) {
    restore_caught_exception(ue_header, &handler_switch_value,
                             &language_specific_data, &landing_pad);
    found_type = (landing_pad == 0 ? found_terminate : found_handler);
    goto install_context;
  }

  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);
  info.ttype_base = base_of_encoded_value(info.ttype_encoding, context);
  ip = _Unwind_GetIPInfo(context, &ip_before_insn);
  //ip = _Unwind_GetIP(context);
  if (!ip_before_insn)
    --ip;

  landing_pad = 0;
  action_record = 0;
  handler_switch_value = 0;

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

  }

  // If ip is not present in the table, call terminate. This is for
  // a destructor inside a cleanup, or a library routine the compiler
  // was not expecting to throw.
  found_type = found_terminate;
  goto do_something;

 found_something:
  if (landing_pad == 0) {
    // If ip is present, and has a null landing pad, there are
    // no cleanups or handlers to be run.
    found_type = found_nothing;
  } 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.
    found_type = found_cleanup;
  } else {

    // Otherwise we have a catch handler or exception specification.
    _sleb128_t ar_filter, ar_disp;
    //const std::type_info* catch_type;
    //_throw_typet* throw_type;
    bool saw_cleanup = false;
    bool saw_handler = false;

    {
      //thrown_ptr = __get_object_from_ue(ue_header);
      //throw_type = __get_exception_header_from_obj(thrown_ptr)->exceptionType;
    }

    while (1) {

      p = action_record;
      p = read_sleb128(p, &ar_filter);
      read_sleb128(p, &ar_disp);

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

        // Positive filter values are handlers.
        //catch_type = get_ttype_entry(&info, ar_filter);
        //printf("positive filter value -- handler found\n");
        saw_handler = true;
        break;

        // Null catch type is a catch-all handler; we can catch foreign
        // exceptions with this. Otherwise we must match types.

        //if (!catch_type || (throw_type &&
        //                    get_adjusted_ptr(catch_type, throw_type, &thrown_ptr))) {
        //  saw_handler = true;
        //  break;
        //}

      } else {

        printf("negative filter value -- exception spec\n");
        // Negative filter values are exception specifications.
        // ??? How do foreign exceptions fit in?  As far as I can
        // see we can't match because there's no __cxa_exception
        // object to stuff bits in for __cxa_call_unexpected to use.
        // Allow them iff the exception spec is non-empty.  I.e.
        // a throw() specification results in __unexpected.

        //if ((throw_type && !(actions & _UA_FORCE_UNWIND) && !foreign_exception) ?
        //    !check_exception_spec(&info, throw_type, thrown_ptr, ar_filter) :
        //    empty_exception_spec(&info, ar_filter)) {
        //  saw_handler = true;
        //  break;
        //}

      }

      if (ar_disp == 0)
        break;
      action_record = p + ar_disp;

    }

    if (saw_handler) {
      handler_switch_value = ar_filter;
      found_type = found_handler;
    } else {
      found_type = (saw_cleanup ? found_cleanup : found_nothing);
    }

  }

 do_something:

   if (found_type == found_nothing)
     return _URC_CONTINUE_UNWIND;

  if (actions & _UA_SEARCH_PHASE) {

    if (found_type == found_cleanup)
      return _URC_CONTINUE_UNWIND;

    // For domestic exceptions, we cache data from phase 1 for phase 2.
    if (!foreign_exception) {
      save_caught_exception(ue_header, handler_switch_value,
                            language_specific_data, landing_pad);
    }

    return _URC_HANDLER_FOUND;
  }

 install_context:

  // We can't use any of the cxa routines with foreign exceptions,
  // because they all expect ue_header to be a struct __cxa_exception.
  // So in that case, call terminate or unexpected directly.
  if ((actions & _UA_FORCE_UNWIND) || foreign_exception) {

    if (found_type == found_terminate)
      abort(); // std::terminate();
    else if (handler_switch_value < 0) {
      printf("WTF\n");
      //__try
      //  { std::unexpected (); }
      //__catch(...)
      //  { std::terminate (); }
    }

  } else {

    if (found_type == found_terminate)
      abort(); //__cxa_call_terminate(ue_header);

    // Cache the TType base value for __cxa_call_unexpected, as we won't
    // have an _Unwind_Context then.
    if (handler_switch_value < 0) {
      parse_lsda_header(context, language_specific_data, &info);
      info.ttype_base = base_of_encoded_value(info.ttype_encoding, context);
      //xh->catchTemp = base_of_encoded_value (info.ttype_encoding, context);
    }

  }

  /* For targets with pointers smaller than the word size, we must extend the
     pointer, and this extension is target dependent.  */
  _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), __builtin_extend_pointer(ue_header));
  _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), handler_switch_value);
  _Unwind_SetIP(context, landing_pad);
  return _URC_INSTALL_CONTEXT;

}
static void
get_call_site_action_for (_Unwind_Context *uw_context,
                          region_descriptor *region,
                          action_descriptor *action)
{
  _Unwind_Ptr ip
    = _Unwind_GetIP (uw_context) - 1;
  /* Subtract 1 because GetIP yields a call return address while we are
     interested in information for the call point. This does not always yield
     the exact call instruction address but always brings the ip back within
     the corresponding region.

     ??? When unwinding up from a signal handler triggered by a trap on some
     instruction, we usually have the faulting instruction address here and
     subtracting 1 might get us into the wrong region.  */

  const unsigned char * p
    = region->call_site_table;

  /* Unless we are able to determine otherwise ... */
  action->kind = nothing;

  db (DB_CSITE, "\n");

  while (p < region->action_table)
    {
      _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, region->call_site_encoding, p, &cs_start);
      p = read_encoded_value (0, region->call_site_encoding, p, &cs_len);
      p = read_encoded_value (0, region->call_site_encoding, p, &cs_lp);
      p = read_uleb128 (p, &cs_action);

      db (DB_CSITE,
	  "c_site @ 0x%08x (+0x%03x), len = %3d, lpad @ 0x%08x (+0x%03x)\n",
	  region->base+cs_start, cs_start, cs_len,
	  region->lp_base+cs_lp, cs_lp);

      /* The table is sorted, so if we've passed the ip, stop.  */
      if (ip < region->base + cs_start)
 	break;

      /* If we have a match, fill the ACTION fields accordingly.  */
      else if (ip < region->base + cs_start + cs_len)
	{
	  /* Let the caller know there may be an action to take, but let it
	     determine the kind.  */
	  action->kind = unknown;

	  if (cs_lp)
	    action->landing_pad = region->lp_base + cs_lp;
	  else
	    action->landing_pad = 0;

	  if (cs_action)
	    action->table_entry = region->action_table + cs_action - 1;
	  else
	    action->table_entry = 0;

	  db (DB_CSITE, "+++\n");
	  return;
	}
    }

  db (DB_CSITE, "---\n");
}
Exemple #9
0
static void
get_call_site_action_for (_Unwind_Context *uw_context,
                          region_descriptor *region,
                          action_descriptor *action)
{
  const unsigned char *p = region->call_site_table;
  int ip_before_insn = 0;
#ifdef HAVE_GETIPINFO
  _Unwind_Ptr ip = _Unwind_GetIPInfo (uw_context, &ip_before_insn);
#else
  _Unwind_Ptr ip = _Unwind_GetIP (uw_context);
#endif
  /* Subtract 1 if necessary because GetIPInfo yields a call return address
     in this case, while we are interested in information for the call point.
     This does not always yield the exact call instruction address but always
     brings the IP back within the corresponding region.  */
  if (!ip_before_insn)
    ip--;

  /* Unless we are able to determine otherwise...  */
  action->kind = nothing;

  db (DB_CSITE, "\n");

  while (p < region->action_table)
    {
      _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, region->call_site_encoding, p, &cs_start);
      p = read_encoded_value (0, region->call_site_encoding, p, &cs_len);
      p = read_encoded_value (0, region->call_site_encoding, p, &cs_lp);
      p = read_uleb128 (p, &cs_action);

      db (DB_CSITE,
	  "c_site @ 0x%08x (+0x%03x), len = %3d, lpad @ 0x%08x (+0x%03x)\n",
	  region->base+cs_start, cs_start, cs_len,
	  region->lp_base+cs_lp, cs_lp);

      /* The table is sorted, so if we've passed the IP, stop.  */
      if (ip < region->base + cs_start)
 	break;

      /* If we have a match, fill the ACTION fields accordingly.  */
      else if (ip < region->base + cs_start + cs_len)
	{
	  /* Let the caller know there may be an action to take, but let it
	     determine the kind.  */
	  action->kind = unknown;

	  if (cs_lp)
	    action->landing_pad = region->lp_base + cs_lp;
	  else
	    action->landing_pad = 0;

	  if (cs_action)
	    action->table_entry = region->action_table + cs_action - 1;
	  else
	    action->table_entry = 0;

	  db (DB_CSITE, "+++\n");
	  return;
	}
    }

  db (DB_CSITE, "---\n");
}
/* Use a binary search table in .eh_frame_hdr format, yield an FDE offset.  */
static Dwarf_Off
binary_search_fde (Dwarf_CFI *cache, Dwarf_Addr address)
{
    const size_t size = 2 * encoded_value_size (&cache->data->d, cache->e_ident,
                        cache->search_table_encoding,
                        NULL);

    /* Dummy used by read_encoded_value.  */
    Dwarf_CFI dummy_cfi =
    {
        .e_ident = cache->e_ident,
        .datarel = cache->search_table_vaddr,
        .frame_vaddr = cache->search_table_vaddr,
    };

    size_t l = 0, u = cache->search_table_entries;
    while (l < u)
    {
        size_t idx = (l + u) / 2;

        const uint8_t *p = &cache->search_table[idx * size];
        Dwarf_Addr start;
        if (unlikely (read_encoded_value (&dummy_cfi,
                                          cache->search_table_encoding, &p,
                                          &start)))
            break;
        if (address < start)
            u = idx;
        else
        {
            Dwarf_Addr fde;
            if (unlikely (read_encoded_value (&dummy_cfi,
                                              cache->search_table_encoding, &p,
                                              &fde)))
                break;
            if (address >= start)
            {
                l = idx + 1;

                /* If this is the last entry, its upper bound is assumed to be
                the end of the module.
                 XXX really should be end of containing PT_LOAD segment */
                if (l < cache->search_table_entries)
                {
                    /* Look at the start address in the following entry.  */
                    Dwarf_Addr end;
                    if (unlikely (read_encoded_value
                                  (&dummy_cfi, cache->search_table_encoding, &p,
                                   &end)))
                        break;
                    if (address >= end)
                        continue;
                }

                return fde - cache->frame_vaddr;
            }
        }
    }

    return (Dwarf_Off) -1l;
}

struct dwarf_fde *
    internal_function
__libdw_find_fde (Dwarf_CFI *cache, Dwarf_Addr address)
{
    /* Look for a cached FDE covering this address.  */

    const struct dwarf_fde fde_key = { .start = address, .end = 0 };
    struct dwarf_fde **found = tfind (&fde_key, &cache->fde_tree, &compare_fde);
    if (found != NULL)
        return *found;

    /* Use .eh_frame_hdr binary search table if possible.  */
    if (cache->search_table != NULL)
    {
        Dwarf_Off offset = binary_search_fde (cache, address);
        if (offset == (Dwarf_Off) -1l)
            goto no_match;
        struct dwarf_fde *fde = __libdw_fde_by_offset (cache, offset);
        if (unlikely (fde != NULL)
                /* Sanity check the address range.  */
                && unlikely (address < fde->start || address >= fde->end))
        {
            __libdw_seterrno (DWARF_E_INVALID_DWARF);
            return NULL;
        }
        return fde;
    }

    /* It's not there.  Read more CFI entries until we find it.  */
    while (1)
    {
        Dwarf_Off last_offset = cache->next_offset;
        Dwarf_CFI_Entry entry;
        int result = INTUSE(dwarf_next_cfi) (cache->e_ident,
                                             &cache->data->d, CFI_IS_EH (cache),
                                             last_offset, &cache->next_offset,
                                             &entry);
        if (result > 0)
            break;
        if (result < 0)
        {
            if (cache->next_offset == last_offset)
                /* We couldn't progress past the bogus FDE.  */
                break;
            /* Skip the loser and look at the next entry.  */
            continue;
        }

        if (dwarf_cfi_cie_p (&entry))
        {
            /* This is a CIE, not an FDE.  We eagerly intern these
               because the next FDE will usually refer to this CIE.  */
            __libdw_intern_cie (cache, last_offset, &entry.cie);
            continue;
        }

        /* We have a new FDE to consider.  */
        struct dwarf_fde *fde = intern_fde (cache, &entry.fde);

        if (fde == (void *) -1l)	/* Bad FDE, but we can keep looking.  */
            continue;

        if (fde == NULL)		/* Bad data.  */
            return NULL;

        /* Is this the one we're looking for?  */
        if (fde->start <= address && fde->end > address)
            return fde;
    }

no_match:
    /* We found no FDE covering this address.  */
    __libdw_seterrno (DWARF_E_NO_MATCH);
    return NULL;
}