/* 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, ®num, 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, ®num, 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, ®num, 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, ®num, 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, ®num, 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, ®num, 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, ®num, 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, ®num, 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, ®num, 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, ®num, 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, ®num, 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, ®num, 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; }
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; }