PROTECTED int unw_get_save_loc (unw_cursor_t *cursor, int reg, unw_save_loc_t *sloc) { struct cursor *c = (struct cursor *) cursor; dwarf_loc_t loc; loc = DWARF_NULL_LOC; /* default to "not saved" */ #warning FIX ME! memset (sloc, 0, sizeof (sloc)); if (DWARF_IS_NULL_LOC (loc)) { sloc->type = UNW_SLT_NONE; return 0; } #if !defined(UNW_LOCAL_ONLY) if (DWARF_IS_REG_LOC (loc)) { sloc->type = UNW_SLT_REG; sloc->u.regnum = DWARF_GET_LOC (loc); } else #endif { sloc->type = UNW_SLT_MEMORY; sloc->u.addr = DWARF_GET_LOC (loc); } return 0; }
PROTECTED int unw_get_save_loc (unw_cursor_t *cursor, int reg, unw_save_loc_t *sloc) { struct cursor *c = (struct cursor *) cursor; dwarf_loc_t loc; switch (reg) { case UNW_SH_R0: case UNW_SH_R1: case UNW_SH_R2: case UNW_SH_R3: case UNW_SH_R4: case UNW_SH_R5: case UNW_SH_R6: case UNW_SH_R7: case UNW_SH_R8: case UNW_SH_R9: case UNW_SH_R10: case UNW_SH_R11: case UNW_SH_R12: case UNW_SH_R13: case UNW_SH_R14: case UNW_SH_R15: case UNW_SH_PC: case UNW_SH_PR: loc = c->dwarf.loc[reg]; break; default: loc = DWARF_NULL_LOC; /* default to "not saved" */ break; } memset (sloc, 0, sizeof (*sloc)); if (DWARF_IS_NULL_LOC (loc)) { sloc->type = UNW_SLT_NONE; return 0; } #if !defined(UNW_LOCAL_ONLY) if (DWARF_IS_REG_LOC (loc)) { sloc->type = UNW_SLT_REG; sloc->u.regnum = DWARF_GET_LOC (loc); } else #endif { sloc->type = UNW_SLT_MEMORY; sloc->u.addr = DWARF_GET_LOC (loc); } return 0; }
PROTECTED int unw_get_save_loc (unw_cursor_t *cursor, int reg, unw_save_loc_t *sloc) { struct cursor *c = (struct cursor *) cursor; dwarf_loc_t loc; loc = DWARF_NULL_LOC; /* default to "not saved" */ switch (reg) { case UNW_X86_64_RBX: loc = c->dwarf.loc[RBX]; break; case UNW_X86_64_RSP: loc = c->dwarf.loc[RSP]; break; case UNW_X86_64_RBP: loc = c->dwarf.loc[RBP]; break; case UNW_X86_64_R12: loc = c->dwarf.loc[R12]; break; case UNW_X86_64_R13: loc = c->dwarf.loc[R13]; break; case UNW_X86_64_R14: loc = c->dwarf.loc[R14]; break; case UNW_X86_64_R15: loc = c->dwarf.loc[R15]; break; default: break; } memset (sloc, 0, sizeof (unw_save_loc_t)); if (DWARF_IS_NULL_LOC (loc)) { sloc->type = UNW_SLT_NONE; return 0; } #if !defined(UNW_LOCAL_ONLY) if (DWARF_IS_REG_LOC (loc)) { sloc->type = UNW_SLT_REG; sloc->u.regnum = DWARF_GET_LOC (loc); } else #endif { sloc->type = UNW_SLT_MEMORY; sloc->u.addr = DWARF_GET_LOC (loc); } return 0; }
static int apply_reg_state (struct dwarf_cursor *c, struct dwarf_reg_state *rs) { unw_word_t regnum, addr, cfa, ip; unw_word_t prev_ip, prev_cfa; unw_addr_space_t as; dwarf_loc_t cfa_loc; unw_accessors_t *a; int i, ret; void *arg; prev_ip = c->ip; prev_cfa = c->cfa; as = c->as; arg = c->as_arg; a = unw_get_accessors (as); /* Evaluate the CFA first, because it may be referred to by other expressions. */ if (rs->reg[DWARF_CFA_REG_COLUMN].where == DWARF_WHERE_REG) { /* CFA is equal to [reg] + offset: */ /* As a special-case, if the stack-pointer is the CFA and the stack-pointer wasn't saved, popping the CFA implicitly pops the stack-pointer as well. */ if ((rs->reg[DWARF_CFA_REG_COLUMN].val == UNW_TDEP_SP) && (UNW_TDEP_SP < ARRAY_SIZE(rs->reg)) && (rs->reg[UNW_TDEP_SP].where == DWARF_WHERE_SAME)) cfa = c->cfa; else { regnum = dwarf_to_unw_regnum (rs->reg[DWARF_CFA_REG_COLUMN].val); if ((ret = unw_get_reg ((unw_cursor_t *) c, regnum, &cfa)) < 0) return ret; } cfa += rs->reg[DWARF_CFA_OFF_COLUMN].val; } else { /* CFA is equal to EXPR: */ assert (rs->reg[DWARF_CFA_REG_COLUMN].where == DWARF_WHERE_EXPR); addr = rs->reg[DWARF_CFA_REG_COLUMN].val; if ((ret = eval_location_expr (c, as, a, addr, &cfa_loc, arg)) < 0) return ret; /* the returned location better be a memory location... */ if (DWARF_IS_REG_LOC (cfa_loc)) return -UNW_EBADFRAME; cfa = DWARF_GET_LOC (cfa_loc); } for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) { switch ((dwarf_where_t) rs->reg[i].where) { case DWARF_WHERE_UNDEF: c->loc[i] = DWARF_NULL_LOC; break; case DWARF_WHERE_SAME: break; case DWARF_WHERE_CFAREL: c->loc[i] = DWARF_MEM_LOC (c, cfa + rs->reg[i].val); break; case DWARF_WHERE_REG: c->loc[i] = DWARF_REG_LOC (c, dwarf_to_unw_regnum (rs->reg[i].val)); break; case DWARF_WHERE_EXPR: addr = rs->reg[i].val; if ((ret = eval_location_expr (c, as, a, addr, c->loc + i, arg)) < 0) return ret; break; case DWARF_WHERE_VAL_EXPR: addr = rs->reg[i].val; if ((ret = eval_location_expr (c, as, a, addr, c->loc + i, arg)) < 0) return ret; c->loc[i] = DWARF_VAL_LOC (c, DWARF_GET_LOC (c->loc[i])); break; } } c->cfa = cfa; /* DWARF spec says undefined return address location means end of stack. */ if (DWARF_IS_NULL_LOC (c->loc[c->ret_addr_column])) c->ip = 0; else { ret = dwarf_get (c, c->loc[c->ret_addr_column], &ip); if (ret < 0) return ret; c->ip = ip; } /* XXX: check for ip to be code_aligned */ if (c->ip == prev_ip && c->cfa == prev_cfa) { Dprintf ("%s: ip and cfa unchanged; stopping here (ip=0x%lx)\n", __FUNCTION__, (long) c->ip); return -UNW_EBADFRAME; } if (c->stash_frames) tdep_stash_frame (c, rs); return 0; }
HIDDEN void tdep_stash_frame (struct dwarf_cursor *d, struct dwarf_reg_state *rs) { struct cursor *c = (struct cursor *) dwarf_to_cursor (d); unw_tdep_frame_t *f = &c->frame_info; Debug (4, "ip=0x%lx cfa=0x%lx type %d cfa [where=%d val=%ld] cfaoff=%ld" " ra=0x%lx fp [where=%d val=%ld @0x%lx] lr [where=%d val=%ld @0x%lx] " "sp [where=%d val=%ld @0x%lx]\n", d->ip, d->cfa, f->frame_type, rs->reg.where[DWARF_CFA_REG_COLUMN], rs->reg.val[DWARF_CFA_REG_COLUMN], rs->reg.val[DWARF_CFA_OFF_COLUMN], DWARF_GET_LOC(d->loc[rs->ret_addr_column]), rs->reg.where[FP], rs->reg.val[FP], DWARF_GET_LOC(d->loc[FP]), rs->reg.where[LR], rs->reg.val[LR], DWARF_GET_LOC(d->loc[LR]), rs->reg.where[SP], rs->reg.val[SP], DWARF_GET_LOC(d->loc[SP])); /* A standard frame is defined as: - CFA is register-relative offset off FP or SP; - Return address is saved in LR; - FP is unsaved or saved at CFA+offset, offset != -1; - LR is unsaved or saved at CFA+offset, offset != -1; - SP is unsaved or saved at CFA+offset, offset != -1. */ if (f->frame_type == UNW_AARCH64_FRAME_OTHER && (rs->reg.where[DWARF_CFA_REG_COLUMN] == DWARF_WHERE_REG) && (rs->reg.val[DWARF_CFA_REG_COLUMN] == FP || rs->reg.val[DWARF_CFA_REG_COLUMN] == SP) && labs(rs->reg.val[DWARF_CFA_OFF_COLUMN]) < (1 << 29) && rs->ret_addr_column == LR && (rs->reg.where[FP] == DWARF_WHERE_UNDEF || rs->reg.where[FP] == DWARF_WHERE_SAME || (rs->reg.where[FP] == DWARF_WHERE_CFAREL && labs(rs->reg.val[FP]) < (1 << 29) && rs->reg.val[FP]+1 != 0)) && (rs->reg.where[LR] == DWARF_WHERE_UNDEF || rs->reg.where[LR] == DWARF_WHERE_SAME || (rs->reg.where[LR] == DWARF_WHERE_CFAREL && labs(rs->reg.val[LR]) < (1 << 29) && rs->reg.val[LR]+1 != 0)) && (rs->reg.where[SP] == DWARF_WHERE_UNDEF || rs->reg.where[SP] == DWARF_WHERE_SAME || (rs->reg.where[SP] == DWARF_WHERE_CFAREL && labs(rs->reg.val[SP]) < (1 << 29) && rs->reg.val[SP]+1 != 0))) { /* Save information for a standard frame. */ f->frame_type = UNW_AARCH64_FRAME_STANDARD; f->cfa_reg_sp = (rs->reg.val[DWARF_CFA_REG_COLUMN] == SP); f->cfa_reg_offset = rs->reg.val[DWARF_CFA_OFF_COLUMN]; if (rs->reg.where[FP] == DWARF_WHERE_CFAREL) f->fp_cfa_offset = rs->reg.val[FP]; if (rs->reg.where[LR] == DWARF_WHERE_CFAREL) f->lr_cfa_offset = rs->reg.val[LR]; if (rs->reg.where[SP] == DWARF_WHERE_CFAREL) f->sp_cfa_offset = rs->reg.val[SP]; Debug (4, " standard frame\n"); } else Debug (4, " unusual frame\n"); }
HIDDEN void tdep_stash_frame (struct dwarf_cursor *d, struct dwarf_reg_state *rs) { struct cursor *c = (struct cursor *) dwarf_to_cursor (d); unw_tdep_frame_t *f = &c->frame_info; Debug (4, "ip=0x%lx cfa=0x%lx type %d cfa [where=%d val=%ld] cfaoff=%ld" " ra=0x%lx rbp [where=%d val=%ld @0x%lx] rsp [where=%d val=%ld @0x%lx]\n", d->ip, d->cfa, f->frame_type, rs->reg[DWARF_CFA_REG_COLUMN].where, rs->reg[DWARF_CFA_REG_COLUMN].val, rs->reg[DWARF_CFA_OFF_COLUMN].val, DWARF_GET_LOC(d->loc[d->ret_addr_column]), rs->reg[RBP].where, rs->reg[RBP].val, DWARF_GET_LOC(d->loc[RBP]), rs->reg[RSP].where, rs->reg[RSP].val, DWARF_GET_LOC(d->loc[RSP])); /* A standard frame is defined as: - CFA is register-relative offset off RBP or RSP; - Return address is saved at CFA-8; - RBP is unsaved or saved at CFA+offset, offset != -1; - RSP is unsaved or saved at CFA+offset, offset != -1. */ if (f->frame_type == UNW_X86_64_FRAME_OTHER && (rs->reg[DWARF_CFA_REG_COLUMN].where == DWARF_WHERE_REG) && (rs->reg[DWARF_CFA_REG_COLUMN].val == RBP || rs->reg[DWARF_CFA_REG_COLUMN].val == RSP) && labs((long) rs->reg[DWARF_CFA_OFF_COLUMN].val) < (1 << 29) && DWARF_GET_LOC(d->loc[d->ret_addr_column]) == d->cfa-8 && (rs->reg[RBP].where == DWARF_WHERE_UNDEF || rs->reg[RBP].where == DWARF_WHERE_SAME || (rs->reg[RBP].where == DWARF_WHERE_CFAREL && labs((long) rs->reg[RBP].val) < (1 << 14) && rs->reg[RBP].val+1 != 0)) && (rs->reg[RSP].where == DWARF_WHERE_UNDEF || rs->reg[RSP].where == DWARF_WHERE_SAME || (rs->reg[RSP].where == DWARF_WHERE_CFAREL && labs((long) rs->reg[RSP].val) < (1 << 14) && rs->reg[RSP].val+1 != 0))) { /* Save information for a standard frame. */ f->frame_type = UNW_X86_64_FRAME_STANDARD; f->cfa_reg_rsp = (rs->reg[DWARF_CFA_REG_COLUMN].val == RSP); f->cfa_reg_offset = rs->reg[DWARF_CFA_OFF_COLUMN].val; if (rs->reg[RBP].where == DWARF_WHERE_CFAREL) f->rbp_cfa_offset = rs->reg[RBP].val; if (rs->reg[RSP].where == DWARF_WHERE_CFAREL) f->rsp_cfa_offset = rs->reg[RSP].val; Debug (4, " standard frame\n"); } /* Signal frame was detected via augmentation in tdep_fetch_frame() */ else if (f->frame_type == UNW_X86_64_FRAME_SIGRETURN) { /* Later we are going to fish out {RBP,RSP,RIP} from sigcontext via their ucontext_t offsets. Confirm DWARF info agrees with the offsets we expect. */ #ifndef NDEBUG const unw_word_t uc = c->sigcontext_addr; assert (DWARF_GET_LOC(d->loc[RIP]) - uc == UC_MCONTEXT_GREGS_RIP); assert (DWARF_GET_LOC(d->loc[RBP]) - uc == UC_MCONTEXT_GREGS_RBP); assert (DWARF_GET_LOC(d->loc[RSP]) - uc == UC_MCONTEXT_GREGS_RSP); #endif Debug (4, " sigreturn frame\n"); } /* PLT and guessed RBP-walked frames are handled in unw_step(). */ else Debug (4, " unusual frame\n"); }
PROTECTED int unw_step (unw_cursor_t *cursor) { struct cursor *c = (struct cursor *) cursor; int ret, i; Debug (1, "(cursor=%p, ip=0x%08x)\n", c, (unsigned) c->dwarf.ip); /* ANDROID support update. */ /* Save the current ip/cfa to prevent looping if the decode yields the same ip/cfa as before. */ unw_word_t old_ip = c->dwarf.ip; unw_word_t old_cfa = c->dwarf.cfa; /* End of ANDROID update. */ /* Try DWARF-based unwinding... */ ret = dwarf_step (&c->dwarf); #if !defined(UNW_LOCAL_ONLY) /* Do not use this method on a local unwind. There is a very high * probability this method will try to access unmapped memory, which * will crash the process. Since this almost never actually works, * it should be okay to skip. */ if (ret < 0) { /* DWARF failed, let's see if we can follow the frame-chain or skip over the signal trampoline. */ struct dwarf_loc ebp_loc, eip_loc; /* We could get here because of missing/bad unwind information. Validate all addresses before dereferencing. */ c->validate = 1; Debug (13, "dwarf_step() failed (ret=%d), trying frame-chain\n", ret); if (unw_is_signal_frame (cursor)) { ret = unw_handle_signal_frame(cursor); if (ret < 0) { Debug (2, "returning 0\n"); return 0; } } else { ret = dwarf_get (&c->dwarf, c->dwarf.loc[EBP], &c->dwarf.cfa); if (ret < 0) { Debug (2, "returning %d\n", ret); return ret; } Debug (13, "[EBP=0x%x] = 0x%x\n", DWARF_GET_LOC (c->dwarf.loc[EBP]), c->dwarf.cfa); ebp_loc = DWARF_LOC (c->dwarf.cfa, 0); eip_loc = DWARF_LOC (c->dwarf.cfa + 4, 0); c->dwarf.cfa += 8; /* Mark all registers unsaved, since we don't know where they are saved (if at all), except for the EBP and EIP. */ for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) c->dwarf.loc[i] = DWARF_NULL_LOC; c->dwarf.loc[EBP] = ebp_loc; c->dwarf.loc[EIP] = eip_loc; } c->dwarf.ret_addr_column = EIP; if (!DWARF_IS_NULL_LOC (c->dwarf.loc[EBP])) { ret = dwarf_get (&c->dwarf, c->dwarf.loc[EIP], &c->dwarf.ip); if (ret < 0) { Debug (13, "dwarf_get([EIP=0x%x]) failed\n", DWARF_GET_LOC (c->dwarf.loc[EIP])); Debug (2, "returning %d\n", ret); return ret; } else { Debug (13, "[EIP=0x%x] = 0x%x\n", DWARF_GET_LOC (c->dwarf.loc[EIP]), c->dwarf.ip); } } else c->dwarf.ip = 0; } #endif /* ANDROID support update. */ if (ret >= 0) { if (c->dwarf.ip) { /* Adjust the pc to the instruction before. */ c->dwarf.ip--; } /* If the decode yields the exact same ip/cfa as before, then indicate the unwind is complete. */ if (old_ip == c->dwarf.ip && old_cfa == c->dwarf.cfa) { Dprintf ("%s: ip and cfa unchanged; stopping here (ip=0x%lx)\n", __FUNCTION__, (long) c->dwarf.ip); return -UNW_EBADFRAME; } c->dwarf.frame++; } /* End of ANDROID update. */ if (unlikely (ret <= 0)) return 0; return (c->dwarf.ip == 0) ? 0 : 1; }
PROTECTED int unw_step (unw_cursor_t *cursor) { struct cursor *c = (struct cursor *) cursor; int ret, i; Debug (1, "(cursor=%p, ip=0x%08x)\n", c, (unsigned) c->dwarf.ip); /* Try DWARF-based unwinding... */ ret = dwarf_step (&c->dwarf); if (ret < 0 && ret != -UNW_ENOINFO) { Debug (2, "returning %d\n", ret); return ret; } if (unlikely (ret < 0)) { /* DWARF failed, let's see if we can follow the frame-chain or skip over the signal trampoline. */ struct dwarf_loc ebp_loc, eip_loc; /* We could get here because of missing/bad unwind information. Validate all addresses before dereferencing. */ c->validate = 1; Debug (13, "dwarf_step() failed (ret=%d), trying frame-chain\n", ret); if (unw_is_signal_frame (cursor)) { ret = unw_handle_signal_frame(cursor); if (ret < 0) { Debug (2, "returning 0\n"); return 0; } } else { ret = dwarf_get (&c->dwarf, c->dwarf.loc[EBP], &c->dwarf.cfa); if (ret < 0) { Debug (2, "returning %d\n", ret); return ret; } Debug (13, "[EBP=0x%x] = 0x%x\n", DWARF_GET_LOC (c->dwarf.loc[EBP]), c->dwarf.cfa); ebp_loc = DWARF_LOC (c->dwarf.cfa, 0); eip_loc = DWARF_LOC (c->dwarf.cfa + 4, 0); c->dwarf.cfa += 8; /* Mark all registers unsaved, since we don't know where they are saved (if at all), except for the EBP and EIP. */ for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) c->dwarf.loc[i] = DWARF_NULL_LOC; c->dwarf.loc[EBP] = ebp_loc; c->dwarf.loc[EIP] = eip_loc; } c->dwarf.ret_addr_column = EIP; if (!DWARF_IS_NULL_LOC (c->dwarf.loc[EBP])) { ret = dwarf_get (&c->dwarf, c->dwarf.loc[EIP], &c->dwarf.ip); if (ret < 0) { Debug (13, "dwarf_get([EIP=0x%x]) failed\n", DWARF_GET_LOC (c->dwarf.loc[EIP])); Debug (2, "returning %d\n", ret); return ret; } else { Debug (13, "[EIP=0x%x] = 0x%x\n", DWARF_GET_LOC (c->dwarf.loc[EIP]), c->dwarf.ip); } } else c->dwarf.ip = 0; } ret = (c->dwarf.ip == 0) ? 0 : 1; Debug (2, "returning %d\n", ret); return ret; }