static inline int eval_location_expr (struct dwarf_cursor *c, unw_addr_space_t as, unw_accessors_t *a, unw_word_t addr, dwarf_loc_t *locp, void *arg) { int ret, is_register; unw_word_t len, val; /* read the length of the expression: */ if ((ret = dwarf_read_uleb128 (as, a, &addr, &len, arg)) < 0) return ret; /* evaluate the expression: */ if ((ret = dwarf_eval_expr (c, &addr, len, &val, &is_register)) < 0) return ret; if (is_register) *locp = DWARF_REG_LOC (c, dwarf_to_unw_regnum (val)); else *locp = DWARF_MEM_LOC (c, val); 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; }
static inline dwarf_loc_t linux_scratch_loc (struct cursor *c, unw_regnum_t reg) { unw_word_t addr = c->sigcontext_addr, fpstate_addr, off; int ret, is_fpstate = 0; switch (c->sigcontext_format) { case X86_SCF_NONE: return DWARF_REG_LOC (&c->dwarf, reg); case X86_SCF_LINUX_SIGFRAME: break; case X86_SCF_LINUX_RT_SIGFRAME: addr += LINUX_UC_MCONTEXT_OFF; break; } switch (reg) { case UNW_X86_GS: off = LINUX_SC_GS_OFF; break; case UNW_X86_FS: off = LINUX_SC_FS_OFF; break; case UNW_X86_ES: off = LINUX_SC_ES_OFF; break; case UNW_X86_DS: off = LINUX_SC_DS_OFF; break; case UNW_X86_EDI: off = LINUX_SC_EDI_OFF; break; case UNW_X86_ESI: off = LINUX_SC_ESI_OFF; break; case UNW_X86_EBP: off = LINUX_SC_EBP_OFF; break; case UNW_X86_ESP: off = LINUX_SC_ESP_OFF; break; case UNW_X86_EBX: off = LINUX_SC_EBX_OFF; break; case UNW_X86_EDX: off = LINUX_SC_EDX_OFF; break; case UNW_X86_ECX: off = LINUX_SC_ECX_OFF; break; case UNW_X86_EAX: off = LINUX_SC_EAX_OFF; break; case UNW_X86_TRAPNO: off = LINUX_SC_TRAPNO_OFF; break; case UNW_X86_EIP: off = LINUX_SC_EIP_OFF; break; case UNW_X86_CS: off = LINUX_SC_CS_OFF; break; case UNW_X86_EFLAGS: off = LINUX_SC_EFLAGS_OFF; break; case UNW_X86_SS: off = LINUX_SC_SS_OFF; break; /* The following is probably not correct for all possible cases. Somebody who understands this better should review this for correctness. */ case UNW_X86_FCW: is_fpstate = 1; off = LINUX_FPSTATE_CW_OFF; break; case UNW_X86_FSW: is_fpstate = 1; off = LINUX_FPSTATE_SW_OFF; break; case UNW_X86_FTW: is_fpstate = 1; off = LINUX_FPSTATE_TAG_OFF; break; case UNW_X86_FCS: is_fpstate = 1; off = LINUX_FPSTATE_CSSEL_OFF; break; case UNW_X86_FIP: is_fpstate = 1; off = LINUX_FPSTATE_IPOFF_OFF; break; case UNW_X86_FEA: is_fpstate = 1; off = LINUX_FPSTATE_DATAOFF_OFF; break; case UNW_X86_FDS: is_fpstate = 1; off = LINUX_FPSTATE_DATASEL_OFF; break; case UNW_X86_MXCSR: is_fpstate = 1; off = LINUX_FPSTATE_MXCSR_OFF; break; /* stacked fp registers */ case UNW_X86_ST0: case UNW_X86_ST1: case UNW_X86_ST2: case UNW_X86_ST3: case UNW_X86_ST4: case UNW_X86_ST5: case UNW_X86_ST6: case UNW_X86_ST7: is_fpstate = 1; off = LINUX_FPSTATE_ST0_OFF + 10*(reg - UNW_X86_ST0); break; /* SSE fp registers */ case UNW_X86_XMM0_lo: case UNW_X86_XMM0_hi: case UNW_X86_XMM1_lo: case UNW_X86_XMM1_hi: case UNW_X86_XMM2_lo: case UNW_X86_XMM2_hi: case UNW_X86_XMM3_lo: case UNW_X86_XMM3_hi: case UNW_X86_XMM4_lo: case UNW_X86_XMM4_hi: case UNW_X86_XMM5_lo: case UNW_X86_XMM5_hi: case UNW_X86_XMM6_lo: case UNW_X86_XMM6_hi: case UNW_X86_XMM7_lo: case UNW_X86_XMM7_hi: is_fpstate = 1; off = LINUX_FPSTATE_XMM0_OFF + 8*(reg - UNW_X86_XMM0_lo); break; case UNW_X86_XMM0: case UNW_X86_XMM1: case UNW_X86_XMM2: case UNW_X86_XMM3: case UNW_X86_XMM4: case UNW_X86_XMM5: case UNW_X86_XMM6: case UNW_X86_XMM7: is_fpstate = 1; off = LINUX_FPSTATE_XMM0_OFF + 16*(reg - UNW_X86_XMM0); break; case UNW_X86_FOP: case UNW_X86_TSS: case UNW_X86_LDT: default: return DWARF_REG_LOC (&c->dwarf, reg); } if (is_fpstate) { if ((ret = dwarf_get (&c->dwarf, DWARF_MEM_LOC (&c->dwarf, addr + LINUX_SC_FPSTATE_OFF), &fpstate_addr)) < 0) return DWARF_NULL_LOC; if (!fpstate_addr) return DWARF_NULL_LOC; return DWARF_MEM_LOC (c, fpstate_addr + off); } else return DWARF_MEM_LOC (c, addr + off); }
HIDDEN dwarf_loc_t x86_get_scratch_loc (struct cursor *c, unw_regnum_t reg) { unw_word_t addr = c->sigcontext_addr, off, xmm_off; unw_word_t fpstate, fpformat; int ret, is_fpstate = 0, is_xmmstate = 0; switch (c->sigcontext_format) { case X86_SCF_NONE: return DWARF_REG_LOC (&c->dwarf, reg); case X86_SCF_FREEBSD_SIGFRAME: addr += offsetof(struct sigframe, sf_uc) + FREEBSD_UC_MCONTEXT_OFF; break; case X86_SCF_FREEBSD_SIGFRAME4: abort(); break; case X86_SCF_FREEBSD_OSIGFRAME: /* XXXKIB */ abort(); break; case X86_SCF_FREEBSD_SYSCALL: /* XXXKIB */ abort(); break; default: /* XXXKIB */ abort(); break; } off = 0; /* shut gcc warning */ switch (reg) { case UNW_X86_GS: off = FREEBSD_UC_MCONTEXT_GS_OFF; break; case UNW_X86_FS: off = FREEBSD_UC_MCONTEXT_FS_OFF; break; case UNW_X86_ES: off = FREEBSD_UC_MCONTEXT_ES_OFF; break; case UNW_X86_DS: off = FREEBSD_UC_MCONTEXT_SS_OFF; break; case UNW_X86_EDI: off = FREEBSD_UC_MCONTEXT_EDI_OFF; break; case UNW_X86_ESI: off = FREEBSD_UC_MCONTEXT_ESI_OFF; break; case UNW_X86_EBP: off = FREEBSD_UC_MCONTEXT_EBP_OFF; break; case UNW_X86_ESP: off = FREEBSD_UC_MCONTEXT_ESP_OFF; break; case UNW_X86_EBX: off = FREEBSD_UC_MCONTEXT_EBX_OFF; break; case UNW_X86_EDX: off = FREEBSD_UC_MCONTEXT_EDX_OFF; break; case UNW_X86_ECX: off = FREEBSD_UC_MCONTEXT_ECX_OFF; break; case UNW_X86_EAX: off = FREEBSD_UC_MCONTEXT_EAX_OFF; break; case UNW_X86_TRAPNO: off = FREEBSD_UC_MCONTEXT_TRAPNO_OFF; break; case UNW_X86_EIP: off = FREEBSD_UC_MCONTEXT_EIP_OFF; break; case UNW_X86_CS: off = FREEBSD_UC_MCONTEXT_CS_OFF; break; case UNW_X86_EFLAGS: off = FREEBSD_UC_MCONTEXT_EFLAGS_OFF; break; case UNW_X86_SS: off = FREEBSD_UC_MCONTEXT_SS_OFF; break; case UNW_X86_FCW: is_fpstate = 1; off = FREEBSD_UC_MCONTEXT_CW_OFF; xmm_off = FREEBSD_UC_MCONTEXT_CW_XMM_OFF; break; case UNW_X86_FSW: is_fpstate = 1; off = FREEBSD_UC_MCONTEXT_SW_OFF; xmm_off = FREEBSD_UC_MCONTEXT_SW_XMM_OFF; break; case UNW_X86_FTW: is_fpstate = 1; xmm_off = FREEBSD_UC_MCONTEXT_TAG_XMM_OFF; off = FREEBSD_UC_MCONTEXT_TAG_OFF; break; case UNW_X86_FCS: is_fpstate = 1; off = FREEBSD_UC_MCONTEXT_CSSEL_OFF; xmm_off = FREEBSD_UC_MCONTEXT_CSSEL_XMM_OFF; break; case UNW_X86_FIP: is_fpstate = 1; off = FREEBSD_UC_MCONTEXT_IPOFF_OFF; xmm_off = FREEBSD_UC_MCONTEXT_IPOFF_XMM_OFF; break; case UNW_X86_FEA: is_fpstate = 1; off = FREEBSD_UC_MCONTEXT_DATAOFF_OFF; xmm_off = FREEBSD_UC_MCONTEXT_DATAOFF_XMM_OFF; break; case UNW_X86_FDS: is_fpstate = 1; off = FREEBSD_US_MCONTEXT_DATASEL_OFF; xmm_off = FREEBSD_US_MCONTEXT_DATASEL_XMM_OFF; break; case UNW_X86_MXCSR: is_fpstate = 1; is_xmmstate = 1; xmm_off = FREEBSD_UC_MCONTEXT_MXCSR_XMM_OFF; break; /* stacked fp registers */ case UNW_X86_ST0: case UNW_X86_ST1: case UNW_X86_ST2: case UNW_X86_ST3: case UNW_X86_ST4: case UNW_X86_ST5: case UNW_X86_ST6: case UNW_X86_ST7: is_fpstate = 1; off = FREEBSD_UC_MCONTEXT_ST0_OFF + 10*(reg - UNW_X86_ST0); xmm_off = FREEBSD_UC_MCONTEXT_ST0_XMM_OFF + 10*(reg - UNW_X86_ST0); break; /* SSE fp registers */ case UNW_X86_XMM0_lo: case UNW_X86_XMM0_hi: case UNW_X86_XMM1_lo: case UNW_X86_XMM1_hi: case UNW_X86_XMM2_lo: case UNW_X86_XMM2_hi: case UNW_X86_XMM3_lo: case UNW_X86_XMM3_hi: case UNW_X86_XMM4_lo: case UNW_X86_XMM4_hi: case UNW_X86_XMM5_lo: case UNW_X86_XMM5_hi: case UNW_X86_XMM6_lo: case UNW_X86_XMM6_hi: case UNW_X86_XMM7_lo: case UNW_X86_XMM7_hi: is_fpstate = 1; is_xmmstate = 1; xmm_off = FREEBSD_UC_MCONTEXT_XMM0_OFF + 8*(reg - UNW_X86_XMM0_lo); break; case UNW_X86_XMM0: case UNW_X86_XMM1: case UNW_X86_XMM2: case UNW_X86_XMM3: case UNW_X86_XMM4: case UNW_X86_XMM5: case UNW_X86_XMM6: case UNW_X86_XMM7: is_fpstate = 1; is_xmmstate = 1; xmm_off = FREEBSD_UC_MCONTEXT_XMM0_OFF + 16*(reg - UNW_X86_XMM0); break; case UNW_X86_FOP: case UNW_X86_TSS: case UNW_X86_LDT: default: return DWARF_REG_LOC (&c->dwarf, reg); } if (is_fpstate) { if ((ret = dwarf_get (&c->dwarf, DWARF_MEM_LOC (&c->dwarf, addr + FREEBSD_UC_MCONTEXT_FPSTATE_OFF), &fpstate)) < 0) return DWARF_NULL_LOC; if (fpstate == FREEBSD_UC_MCONTEXT_FPOWNED_NONE) return DWARF_NULL_LOC; if ((ret = dwarf_get (&c->dwarf, DWARF_MEM_LOC (&c->dwarf, addr + FREEBSD_UC_MCONTEXT_FPFORMAT_OFF), &fpformat)) < 0) return DWARF_NULL_LOC; if (fpformat == FREEBSD_UC_MCONTEXT_FPFMT_NODEV || (is_xmmstate && fpformat != FREEBSD_UC_MCONTEXT_FPFMT_XMM)) return DWARF_NULL_LOC; if (is_xmmstate) off = xmm_off; } return DWARF_MEM_LOC (c, addr + off); }