Esempio n. 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;
}
Esempio n. 2
0
HIDDEN int
dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_accessors_t *a,
				  unw_word_t *addrp, unw_proc_info_t *pi,
				  int need_unwind_info, unw_word_t base,
				  void *arg)
{
  unw_word_t fde_end_addr, cie_addr, cie_offset_addr, aug_end_addr = 0;
  unw_word_t start_ip, ip_range, aug_size, addr = *addrp;
  int ret, ip_range_encoding;
  struct dwarf_cie_info dci;
  uint64_t u64val;
  uint32_t u32val;

  Debug (12, "FDE @ 0x%lx\n", (long) addr);

  memset (&dci, 0, sizeof (dci));

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

  if (u32val != 0xffffffff)
    {
      int32_t cie_offset;

      /* In some configurations, an FDE with a 0 length indicates the
	 end of the FDE-table.  */
      if (u32val == 0)
	return -UNW_ENOINFO;

      /* the FDE is in the 32-bit DWARF format */

      *addrp = fde_end_addr = addr + u32val;
      cie_offset_addr = addr;

      if ((ret = dwarf_reads32 (as, a, &addr, &cie_offset, arg)) < 0)
	return ret;

      if (is_cie_id (cie_offset, base != 0))
	/* ignore CIEs (happens during linear searches) */
	return 0;

      if (base != 0)
        cie_addr = base + cie_offset;
      else
	/* DWARF says that the CIE_pointer in the FDE is a
	   .debug_frame-relative offset, but the GCC-generated .eh_frame
	   sections instead store a "pcrelative" offset, which is just
	   as fine as it's self-contained.  */
	cie_addr = cie_offset_addr - cie_offset;
    }
  else
    {
      int64_t cie_offset;

      /* the FDE is in the 64-bit DWARF format */

      if ((ret = dwarf_readu64 (as, a, &addr, &u64val, arg)) < 0)
	return ret;

      *addrp = fde_end_addr = addr + u64val;
      cie_offset_addr = addr;

      if ((ret = dwarf_reads64 (as, a, &addr, &cie_offset, arg)) < 0)
	return ret;

      if (is_cie_id (cie_offset, base != 0))
	/* ignore CIEs (happens during linear searches) */
	return 0;

      if (base != 0)
	cie_addr = base + cie_offset;
      else
	/* DWARF says that the CIE_pointer in the FDE is a
	   .debug_frame-relative offset, but the GCC-generated .eh_frame
	   sections instead store a "pcrelative" offset, which is just
	   as fine as it's self-contained.  */
	cie_addr = (unw_word_t) ((uint64_t) cie_offset_addr - cie_offset);
    }

  Debug (15, "looking for CIE at address %lx\n", (long) cie_addr);

  if ((ret = parse_cie (as, a, cie_addr, pi, &dci, base, arg)) < 0)
    return ret;

  /* IP-range has same encoding as FDE pointers, except that it's
     always an absolute value: */
  ip_range_encoding = dci.fde_encoding & DW_EH_PE_FORMAT_MASK;

  if ((ret = dwarf_read_encoded_pointer (as, a, &addr, dci.fde_encoding,
					 pi->gp, pi->start_ip, &start_ip, arg)) < 0
      || (ret = dwarf_read_encoded_pointer (as, a, &addr, ip_range_encoding,
					    pi->gp, pi->start_ip, &ip_range, arg)) < 0)
    return ret;
  pi->start_ip = start_ip;
  pi->end_ip = start_ip + ip_range;
  pi->handler = dci.handler;

  if (dci.sized_augmentation)
    {
      if ((ret = dwarf_read_uleb128 (as, a, &addr, &aug_size, arg)) < 0)
	return ret;
      aug_end_addr = addr + aug_size;
    }

  if ((ret = dwarf_read_encoded_pointer (as, a, &addr, dci.lsda_encoding,
					 pi->gp, pi->start_ip, &pi->lsda, arg)) < 0)
    return ret;

  Debug (15, "FDE covers IP 0x%lx-0x%lx, LSDA=0x%lx\n",
	 (long) pi->start_ip, (long) pi->end_ip, (long) pi->lsda);

  if (need_unwind_info)
    {
      pi->format = UNW_INFO_FORMAT_TABLE;
      pi->unwind_info_size = sizeof (dci);
      pi->unwind_info = mempool_alloc (&dwarf_cie_info_pool);
      if (!pi->unwind_info)
	return -UNW_ENOMEM;

      if (dci.have_abi_marker)
	{
	  if ((ret = dwarf_readu16 (as, a, &addr, &dci.abi, arg)) < 0
	      || (ret = dwarf_readu16 (as, a, &addr, &dci.tag, arg)) < 0)
	    return ret;
	  Debug (13, "Found ABI marker = (abi=%u, tag=%u)\n",
		 dci.abi, dci.tag);
	}

      if (dci.sized_augmentation)
	dci.fde_instr_start = aug_end_addr;
      else
	dci.fde_instr_start = addr;
      dci.fde_instr_end = fde_end_addr;

      memcpy (pi->unwind_info, &dci, sizeof (dci));
    }
  return 0;
}