Пример #1
0
/* Run a CFI program to update the register state.  */
static int
run_cfi_program (struct dwarf_cursor *c, dwarf_state_record_t *sr,
                 unw_word_t ip, unw_word_t *addr, unw_word_t end_addr,
                 struct dwarf_cie_info *dci)
{
    unw_word_t curr_ip, operand = 0, regnum, val, len, fde_encoding;
    dwarf_reg_state_t *rs_stack = NULL, *new_rs, *old_rs;
    unw_addr_space_t as;
    unw_accessors_t *a;
    uint8_t u8, op;
    uint16_t u16;
    uint32_t u32;
    void *arg;
    int ret;

    as = c->as;
    arg = c->as_arg;
    if (c->pi.flags & UNW_PI_FLAG_DEBUG_FRAME)
    {
        /* .debug_frame CFI is stored in local address space.  */
        as = unw_local_addr_space;
        arg = NULL;
    }
    a = unw_get_accessors (as);
    curr_ip = c->pi.start_ip;

    /* Process everything up to and including the current 'ip',
       including all the DW_CFA_advance_loc instructions.  See
       'c->use_prev_instr' use in 'fetch_proc_info' for details. */
    while (curr_ip <= ip && *addr < end_addr)
    {
        if ((ret = dwarf_readu8 (as, a, addr, &op, arg)) < 0)
            return ret;

        if (op & DWARF_CFA_OPCODE_MASK)
        {
            operand = op & DWARF_CFA_OPERAND_MASK;
            op &= ~DWARF_CFA_OPERAND_MASK;
        }
        switch ((dwarf_cfa_t) op)
        {
        case DW_CFA_advance_loc:
            curr_ip += operand * dci->code_align;
            Debug (15, "CFA_advance_loc to 0x%lx\n", (long) curr_ip);
            break;

        case DW_CFA_advance_loc1:
            if ((ret = dwarf_readu8 (as, a, addr, &u8, arg)) < 0)
                goto fail;
            curr_ip += u8 * dci->code_align;
            Debug (15, "CFA_advance_loc1 to 0x%lx\n", (long) curr_ip);
            break;

        case DW_CFA_advance_loc2:
            if ((ret = dwarf_readu16 (as, a, addr, &u16, arg)) < 0)
                goto fail;
            curr_ip += u16 * dci->code_align;
            Debug (15, "CFA_advance_loc2 to 0x%lx\n", (long) curr_ip);
            break;

        case DW_CFA_advance_loc4:
            if ((ret = dwarf_readu32 (as, a, addr, &u32, arg)) < 0)
                goto fail;
            curr_ip += u32 * dci->code_align;
            Debug (15, "CFA_advance_loc4 to 0x%lx\n", (long) curr_ip);
            break;

        case DW_CFA_MIPS_advance_loc8:
#ifdef UNW_TARGET_MIPS
        {
            uint64_t u64;

            if ((ret = dwarf_readu64 (as, a, addr, &u64, arg)) < 0)
                goto fail;
            curr_ip += u64 * dci->code_align;
            Debug (15, "CFA_MIPS_advance_loc8\n");
            break;
        }
#else
        Debug (1, "DW_CFA_MIPS_advance_loc8 on non-MIPS target\n");
        ret = -UNW_EINVAL;
        goto fail;
#endif

        case DW_CFA_offset:
            regnum = operand;
            if (regnum >= DWARF_NUM_PRESERVED_REGS)
            {
                Debug (1, "Invalid register number %u in DW_cfa_OFFSET\n",
                       (unsigned int) regnum);
                ret = -UNW_EBADREG;
                goto fail;
            }
            if ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0)
                goto fail;
            set_reg (sr, regnum, DWARF_WHERE_CFAREL, val * dci->data_align);
            Debug (15, "CFA_offset r%lu at cfa+0x%lx\n",
                   (long) regnum, (long) (val * dci->data_align));
            break;

        case DW_CFA_offset_extended:
            if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
                    || ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0))
                goto fail;
            set_reg (sr, regnum, DWARF_WHERE_CFAREL, val * dci->data_align);
            Debug (15, "CFA_offset_extended r%lu at cf+0x%lx\n",
                   (long) regnum, (long) (val * dci->data_align));
            break;

        case DW_CFA_offset_extended_sf:
            if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
                    || ((ret = dwarf_read_sleb128 (as, a, addr, &val, arg)) < 0))
                goto fail;
            set_reg (sr, regnum, DWARF_WHERE_CFAREL, val * dci->data_align);
            Debug (15, "CFA_offset_extended_sf r%lu at cf+0x%lx\n",
                   (long) regnum, (long) (val * dci->data_align));
            break;

        case DW_CFA_restore:
            regnum = operand;
            if (regnum >= DWARF_NUM_PRESERVED_REGS)
            {
                Debug (1, "Invalid register number %u in DW_CFA_restore\n",
                       (unsigned int) regnum);
                ret = -UNW_EINVAL;
                goto fail;
            }
            sr->rs_current.reg[regnum] = sr->rs_initial.reg[regnum];
            Debug (15, "CFA_restore r%lu\n", (long) regnum);
            break;

        case DW_CFA_restore_extended:
            if ((ret = dwarf_read_uleb128 (as, a, addr, &regnum, arg)) < 0)
                goto fail;
            if (regnum >= DWARF_NUM_PRESERVED_REGS)
            {
                Debug (1, "Invalid register number %u in "
                       "DW_CFA_restore_extended\n", (unsigned int) regnum);
                ret = -UNW_EINVAL;
                goto fail;
            }
            sr->rs_current.reg[regnum] = sr->rs_initial.reg[regnum];
            Debug (15, "CFA_restore_extended r%lu\n", (long) regnum);
            break;

        case DW_CFA_nop:
            break;

        case DW_CFA_set_loc:
            fde_encoding = dci->fde_encoding;
            if ((ret = dwarf_read_encoded_pointer (as, a, addr, fde_encoding,
                                                   c->pi.gp, c->pi.start_ip, &curr_ip,
                                                   arg)) < 0)
                goto fail;
            Debug (15, "CFA_set_loc to 0x%lx\n", (long) curr_ip);
            break;

        case DW_CFA_undefined:
            if ((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
                goto fail;
            set_reg (sr, regnum, DWARF_WHERE_UNDEF, 0);
            Debug (15, "CFA_undefined r%lu\n", (long) regnum);
            break;

        case DW_CFA_same_value:
            if ((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
                goto fail;
            set_reg (sr, regnum, DWARF_WHERE_SAME, 0);
            Debug (15, "CFA_same_value r%lu\n", (long) regnum);
            break;

        case DW_CFA_register:
            if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
                    || ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0))
                goto fail;
            set_reg (sr, regnum, DWARF_WHERE_REG, val);
            Debug (15, "CFA_register r%lu to r%lu\n", (long) regnum, (long) val);
            break;

        case DW_CFA_remember_state:
            new_rs = alloc_reg_state ();
            if (!new_rs)
            {
                Debug (1, "Out of memory in DW_CFA_remember_state\n");
                ret = -UNW_ENOMEM;
                goto fail;
            }

            memcpy (new_rs->reg, sr->rs_current.reg, sizeof (new_rs->reg));
            new_rs->next = rs_stack;
            rs_stack = new_rs;
            Debug (15, "CFA_remember_state\n");
            break;

        case DW_CFA_restore_state:
            if (!rs_stack)
            {
                Debug (1, "register-state stack underflow\n");
                ret = -UNW_EINVAL;
                goto fail;
            }
            memcpy (&sr->rs_current.reg, &rs_stack->reg, sizeof (rs_stack->reg));
            old_rs = rs_stack;
            rs_stack = rs_stack->next;
            free_reg_state (old_rs);
            Debug (15, "CFA_restore_state\n");
            break;

        case DW_CFA_def_cfa:
            if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
                    || ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0))
                goto fail;
            set_reg (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_REG, regnum);
            set_reg (sr, DWARF_CFA_OFF_COLUMN, 0, val);   /* NOT factored! */
            Debug (15, "CFA_def_cfa r%lu+0x%lx\n", (long) regnum, (long) val);
            break;

        case DW_CFA_def_cfa_sf:
            if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
                    || ((ret = dwarf_read_sleb128 (as, a, addr, &val, arg)) < 0))
                goto fail;
            set_reg (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_REG, regnum);
            set_reg (sr, DWARF_CFA_OFF_COLUMN, 0,
                     val * dci->data_align);              /* factored! */
            Debug (15, "CFA_def_cfa_sf r%lu+0x%lx\n",
                   (long) regnum, (long) (val * dci->data_align));
            break;

        case DW_CFA_def_cfa_register:
            if ((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
                goto fail;
            set_reg (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_REG, regnum);
            Debug (15, "CFA_def_cfa_register r%lu\n", (long) regnum);
            break;

        case DW_CFA_def_cfa_offset:
            if ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0)
                goto fail;
            set_reg (sr, DWARF_CFA_OFF_COLUMN, 0, val);   /* NOT factored! */
            Debug (15, "CFA_def_cfa_offset 0x%lx\n", (long) val);
            break;

        case DW_CFA_def_cfa_offset_sf:
            if ((ret = dwarf_read_sleb128 (as, a, addr, &val, arg)) < 0)
                goto fail;
            set_reg (sr, DWARF_CFA_OFF_COLUMN, 0,
                     val * dci->data_align);      /* factored! */
            Debug (15, "CFA_def_cfa_offset_sf 0x%lx\n",
                   (long) (val * dci->data_align));
            break;

        case DW_CFA_def_cfa_expression:
            /* Save the address of the DW_FORM_block for later evaluation. */
            set_reg (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_EXPR, *addr);

            if ((ret = dwarf_read_uleb128 (as, a, addr, &len, arg)) < 0)
                goto fail;

            Debug (15, "CFA_def_cfa_expr @ 0x%lx [%lu bytes]\n",
                   (long) *addr, (long) len);
            *addr += len;
            break;

        case DW_CFA_expression:
            if ((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
                goto fail;

            /* Save the address of the DW_FORM_block for later evaluation. */
            set_reg (sr, regnum, DWARF_WHERE_EXPR, *addr);

            if ((ret = dwarf_read_uleb128 (as, a, addr, &len, arg)) < 0)
                goto fail;

            Debug (15, "CFA_expression r%lu @ 0x%lx [%lu bytes]\n",
                   (long) regnum, (long) addr, (long) len);
            *addr += len;
            break;

        case DW_CFA_val_expression:
            if ((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
                goto fail;

            /* Save the address of the DW_FORM_block for later evaluation. */
            set_reg (sr, regnum, DWARF_WHERE_VAL_EXPR, *addr);

            if ((ret = dwarf_read_uleb128 (as, a, addr, &len, arg)) < 0)
                goto fail;

            Debug (15, "CFA_val_expression r%lu @ 0x%lx [%lu bytes]\n",
                   (long) regnum, (long) addr, (long) len);
            *addr += len;
            break;

        case DW_CFA_GNU_args_size:
            if ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0)
                goto fail;
            sr->args_size = val;
            Debug (15, "CFA_GNU_args_size %lu\n", (long) val);
            break;

        case DW_CFA_GNU_negative_offset_extended:
            /* A comment in GCC says that this is obsoleted by
               DW_CFA_offset_extended_sf, but that it's used by older
               PowerPC code.  */
            if (((ret = read_regnum (as, a, addr, &regnum, arg)) < 0)
                    || ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0))
                goto fail;
            set_reg (sr, regnum, DWARF_WHERE_CFAREL, -(val * dci->data_align));
            Debug (15, "CFA_GNU_negative_offset_extended cfa+0x%lx\n",
                   (long) -(val * dci->data_align));
            break;

        case DW_CFA_GNU_window_save:
#ifdef UNW_TARGET_SPARC
            /* This is a special CFA to handle all 16 windowed registers
               on SPARC.  */
            for (regnum = 16; regnum < 32; ++regnum)
                set_reg (sr, regnum, DWARF_WHERE_CFAREL,
                         (regnum - 16) * sizeof (unw_word_t));
            Debug (15, "CFA_GNU_window_save\n");
            break;
#else
            /* FALL THROUGH */
#endif
        case DW_CFA_lo_user:
        case DW_CFA_hi_user:
            Debug (1, "Unexpected CFA opcode 0x%x\n", op);
            ret = -UNW_EINVAL;
            goto fail;
        }
    }
    ret = 0;

fail:
    /* Free the register-state stack, if not empty already.  */
    while (rs_stack)
    {
        old_rs = rs_stack;
        rs_stack = rs_stack->next;
        free_reg_state (old_rs);
    }
    return ret;
}
Пример #2
0
/* Note: we don't need to keep track of more than the first four
   characters of the augmentation string, because we (a) ignore any
   augmentation string contents once we find an unrecognized character
   and (b) those characters that we do recognize, can't be
   repeated.  */
static inline int
parse_cie (unw_addr_space_t as, unw_accessors_t *a, unw_word_t addr,
	   const unw_proc_info_t *pi, struct dwarf_cie_info *dci,
	   unw_word_t base, void *arg)
{
  uint8_t version, ch, augstr[5], fde_encoding, handler_encoding;
  unw_word_t len, cie_end_addr, aug_size;
  uint32_t u32val;
  uint64_t u64val;
  size_t i;
  int ret;
# define STR2(x)	#x
# define STR(x)		STR2(x)

  /* Pick appropriate default for FDE-encoding.  DWARF spec says
     start-IP (initial_location) and the code-size (address_range) are
     "address-unit sized constants".  The `R' augmentation can be used
     to override this, but by default, we pick an address-sized unit
     for fde_encoding.  */
  switch (dwarf_addr_size (as))
    {
    case 4:	fde_encoding = DW_EH_PE_udata4; break;
    case 8:	fde_encoding = DW_EH_PE_udata8; break;
    default:	fde_encoding = DW_EH_PE_omit; break;
    }

  dci->lsda_encoding = DW_EH_PE_omit;
  dci->handler = 0;

  if ((ret = dwarf_readu32 (as, a, &addr, &u32val, arg)) < 0)
    return ret;

  if (u32val != 0xffffffff)
    {
      /* the CIE is in the 32-bit DWARF format */
      uint32_t cie_id;
      /* DWARF says CIE id should be 0xffffffff, but in .eh_frame, it's 0 */
      const uint32_t expected_id = (base) ? 0xffffffff : 0;

      len = u32val;
      cie_end_addr = addr + len;
      if ((ret = dwarf_readu32 (as, a, &addr, &cie_id, arg)) < 0)
	return ret;
      if (cie_id != expected_id)
	{
	  Debug (1, "Unexpected CIE id %x\n", cie_id);
	  return -UNW_EINVAL;
	}
    }
  else
    {
      /* the CIE is in the 64-bit DWARF format */
      uint64_t cie_id;
      /* DWARF says CIE id should be 0xffffffffffffffff, but in
	 .eh_frame, it's 0 */
      const uint64_t expected_id = (base) ? 0xffffffffffffffffull : 0;

      if ((ret = dwarf_readu64 (as, a, &addr, &u64val, arg)) < 0)
	return ret;
      len = u64val;
      cie_end_addr = addr + len;
      if ((ret = dwarf_readu64 (as, a, &addr, &cie_id, arg)) < 0)
	return ret;
      if (cie_id != expected_id)
	{
	  Debug (1, "Unexpected CIE id %llx\n", (long long) cie_id);
	  return -UNW_EINVAL;
	}
    }
  dci->cie_instr_end = cie_end_addr;

  if ((ret = dwarf_readu8 (as, a, &addr, &version, arg)) < 0)
    return ret;

  if (version != 1 && version != DWARF_CIE_VERSION)
    {
      Debug (1, "Got CIE version %u, expected version 1 or "
	     STR (DWARF_CIE_VERSION) "\n", version);
      return -UNW_EBADVERSION;
    }

  /* read and parse the augmentation string: */
  memset (augstr, 0, sizeof (augstr));
  for (i = 0;;)
    {
      if ((ret = dwarf_readu8 (as, a, &addr, &ch, arg)) < 0)
	return ret;

      if (!ch)
	break;	/* end of augmentation string */

      if (i < sizeof (augstr) - 1)
	augstr[i++] = ch;
    }

  if ((ret = dwarf_read_uleb128 (as, a, &addr, &dci->code_align, arg)) < 0
      || (ret = dwarf_read_sleb128 (as, a, &addr, &dci->data_align, arg)) < 0)
    return ret;

  /* Read the return-address column either as a u8 or as a uleb128.  */
  if (version == 1)
    {
      if ((ret = dwarf_readu8 (as, a, &addr, &ch, arg)) < 0)
	return ret;
      dci->ret_addr_column = ch;
    }
  else if ((ret = dwarf_read_uleb128 (as, a, &addr, &dci->ret_addr_column,
				      arg)) < 0)
    return ret;

  i = 0;
  if (augstr[0] == 'z')
    {
      dci->sized_augmentation = 1;
      if ((ret = dwarf_read_uleb128 (as, a, &addr, &aug_size, arg)) < 0)
	return ret;
      i++;
    }

  for (; i < sizeof (augstr) && augstr[i]; ++i)
    switch (augstr[i])
      {
      case 'L':
	/* read the LSDA pointer-encoding format.  */
	if ((ret = dwarf_readu8 (as, a, &addr, &ch, arg)) < 0)
	  return ret;
	dci->lsda_encoding = ch;
	break;

      case 'R':
	/* read the FDE pointer-encoding format.  */
	if ((ret = dwarf_readu8 (as, a, &addr, &fde_encoding, arg)) < 0)
	  return ret;
	break;

      case 'P':
	/* read the personality-routine pointer-encoding format.  */
	if ((ret = dwarf_readu8 (as, a, &addr, &handler_encoding, arg)) < 0)
	  return ret;
	if ((ret = dwarf_read_encoded_pointer (as, a, &addr, handler_encoding,
					       pi->gp, pi->start_ip, &dci->handler, arg)) < 0)
	  return ret;
	break;

      case 'S':
	/* This is a signal frame. */
	dci->signal_frame = 1;

	/* Temporarily set it to one so dwarf_parse_fde() knows that
	   it should fetch the actual ABI/TAG pair from the FDE.  */
	dci->have_abi_marker = 1;
	break;

      default:
	Debug (1, "Unexpected augmentation string `%s'\n", augstr);
	if (dci->sized_augmentation)
	  /* If we have the size of the augmentation body, we can skip
	     over the parts that we don't understand, so we're OK. */
	  goto done;
	else
	  return -UNW_EINVAL;
      }
 done:
  dci->fde_encoding = fde_encoding;
  dci->cie_instr_start = addr;
  Debug (15, "CIE parsed OK, augmentation = \"%s\", handler=0x%lx\n",
	 augstr, (long) dci->handler);
  return 0;
}