};
#define QUADD_AARCH64_REGISTERS 32
+#define QUADD_AARCH32_REGISTERS 16
-enum regs {
- FP = 29,
- LR = 30,
- SP = 31,
+#define QUADD_NUM_REGS QUADD_AARCH64_REGISTERS
+
+enum regs32 {
+ ARM32_FP_THUMB = 7,
+ ARM32_FP = 11,
+
+ ARM32_SP = 13,
+ ARM32_LR = 14,
+ ARM32_PC = 15
+};
+
+enum regs64 {
+ ARM64_FP = 29,
+ ARM64_LR = 30,
+ ARM64_SP = 31,
};
enum {
- DW_SEC_TYPE_IDX,
- DW_SEC_TYPE_TAB,
+ DW_MODE_ARM32,
+ DW_MODE_ARM64,
};
union dw_loc {
};
struct regs_state {
- struct reg_info reg[QUADD_AARCH64_REGISTERS];
+ struct reg_info reg[QUADD_NUM_REGS];
long cfa_offset;
- unsigned int cfa_register;
+ int cfa_register;
unsigned char *cfa_expr;
unsigned int cfa_expr_len;
struct dwarf_cpu_context {
struct regs_state rs_stack[DW_MAX_RS_STACK_DEPTH];
int depth;
+
+ int dw_word_size;
};
struct quadd_dwarf_context {
struct stackframe {
unsigned long pc;
- unsigned long lr;
- unsigned long sp;
- unsigned long fp;
+ unsigned long vregs[QUADD_NUM_REGS];
struct regs_state rs;
struct regs_state rs_initial;
unsigned long cfa;
+
+ int mode;
};
struct dw_cie {
unsigned char *data;
};
-typedef u64 dw_word_t;
-
#define read_user_data(addr, retval) \
({ \
long ret; \
static struct quadd_dwarf_context ctx;
+static inline int regnum_sp(int mode)
+{
+ return (mode == DW_MODE_ARM32) ?
+ ARM32_SP : ARM64_SP;
+}
+
+static inline int regnum_fp(int mode)
+{
+ return (mode == DW_MODE_ARM32) ?
+ ARM32_FP : ARM64_FP;
+}
+
+static inline int regnum_lr(int mode)
+{
+ return (mode == DW_MODE_ARM32) ?
+ ARM32_LR : ARM64_LR;
+}
+
+static inline unsigned long
+get_user_reg_size(int mode)
+{
+ return (mode == DW_MODE_ARM32) ?
+ sizeof(u32) : sizeof(u64);
+}
+
static inline int
validate_addr(struct ex_region_info *ri,
unsigned long addr,
unsigned long nbytes,
int st)
{
- struct extab_info *ei;
+ struct extab_info *ti;
struct quadd_mmap_area *mmap;
unsigned long start, end;
mmap = ri->mmap;
- ei = (st == DW_SEC_TYPE_IDX) ? &ri->tabs.exidx : &ri->tabs.extab;
- start = (unsigned long)mmap->data + ei->mmap_offset;
- end = start + ei->length;
+ ti = &ri->ex_sec[st];
+
+ start = (unsigned long)mmap->data + ti->mmap_offset;
+ end = start + ti->length;
if (unlikely(addr < start || addr > end - nbytes)) {
pr_err_once("%s: error: addr: %#lx, len: %ld, data: %#lx-%#lx\n",
struct ex_region_info *ri, int st)
{
unsigned long offset;
- struct extab_info *ei;
+ struct extab_info *ti;
- ei = (st == DW_SEC_TYPE_IDX) ? &ri->tabs.exidx : &ri->tabs.extab;
- offset = addr - ei->addr;
+ ti = &ri->ex_sec[st];
+ offset = addr - ti->addr;
- return ei->mmap_offset + offset + (unsigned long)ri->mmap->data;
+ return ti->mmap_offset + offset + (unsigned long)ri->mmap->data;
}
static inline unsigned long
struct ex_region_info *ri, int st)
{
unsigned long offset;
- struct extab_info *ei;
+ struct extab_info *ti;
- ei = (st == DW_SEC_TYPE_IDX) ? &ri->tabs.exidx : &ri->tabs.extab;
- offset = addr - ei->mmap_offset - (unsigned long)ri->mmap->data;
+ ti = &ri->ex_sec[st];
+ offset = addr - ti->mmap_offset - (unsigned long)ri->mmap->data;
- return ei->addr + offset;
+ return ti->addr + offset;
}
static inline int validate_regnum(struct regs_state *rs, int regnum)
char encoding,
int st)
{
- int count = 0;
+ int dw_word_size, count = 0;
long stmp = 0, err = 0;
unsigned long utmp, res = 0;
+ struct dwarf_cpu_context *cpu_ctx = this_cpu_ptr(ctx.cpu_ctx);
pr_debug("encoding: %#x\n", encoding);
+ dw_word_size = cpu_ctx->dw_word_size;
+
if (encoding == DW_EH_PE_omit) {
pr_debug("DW_EH_PE_omit\n");
return 0;
} else if (encoding == DW_EH_PE_aligned) {
unsigned long aligned = ALIGN((unsigned long)addr,
- sizeof(dw_word_t));
+ dw_word_size);
pr_debug("DW_EH_PE_aligned\n");
- if (sizeof(dw_word_t) == 4) {
+ if (dw_word_size == 4) {
*val = read_mmap_data_u32(ri, (u32 *)aligned, st, &err);
- } else if (sizeof(dw_word_t) == 8) {
+ } else if (dw_word_size == 8) {
*val = read_mmap_data_u64(ri, (u64 *)aligned, st, &err);
} else {
pr_err_once("%s: error: encoding\n", __func__);
if (err)
return err;
- return sizeof(dw_word_t);
+ return dw_word_size;
}
switch (encoding & 0x0f) {
case DW_EH_PE_absptr:
pr_debug("%s: absptr encoding\n", __func__);
- if (sizeof(dw_word_t) == 4) {
+ if (dw_word_size == 4) {
*val = read_mmap_data_u32(ri, (u32 *)addr, st, &err);
- } else if (sizeof(dw_word_t) == 8) {
+ } else if (dw_word_size == 8) {
*val = read_mmap_data_u64(ri, (u64 *)addr, st, &err);
} else {
pr_err_once("error: wrong dwarf size\n");
if (err)
return err;
- return sizeof(dw_word_t);
+ return dw_word_size;
case DW_EH_PE_sdata2:
case DW_EH_PE_udata2:
if (encoding & DW_EH_PE_indirect) {
pr_debug("DW_EH_PE_indirect\n");
- if (sizeof(dw_word_t) == 4) {
+ if (dw_word_size == 4) {
res = read_mmap_data_u32(ri, (u32 *)res,
st, &err);
- } else if (sizeof(dw_word_t) == 8) {
+ } else if (dw_word_size == 8) {
res = read_mmap_data_u64(ri, (u64 *)res,
st, &err);
} else {
c_insn = insn_start;
while (c_insn < insn_end && sf->pc <= pc) {
- insn = read_mmap_data_u8(ri, c_insn++, DW_SEC_TYPE_TAB, &err);
+ insn = read_mmap_data_u8(ri, c_insn++,
+ QUADD_SEC_TYPE_EH_FRAME, &err);
if (err)
return err;
case DW_CFA_offset:
reg = dw_cfa_operand(insn);
c_insn += dwarf_read_uleb128(ri, c_insn, &utmp,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
case DW_CFA_advance_loc1:
delta = read_mmap_data_u8(ri, c_insn++,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
case DW_CFA_advance_loc2:
delta = read_mmap_data_u16(ri, (u16 *)c_insn,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
case DW_CFA_advance_loc4:
delta = read_mmap_data_u32(ri, (u32 *)c_insn,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
case DW_CFA_offset_extended:
c_insn += dwarf_read_uleb128(ri, c_insn, &utmp,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
reg = utmp;
c_insn += dwarf_read_uleb128(ri, c_insn, &utmp,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
case DW_CFA_restore_extended:
c_insn += dwarf_read_uleb128(ri, c_insn, ®,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
case DW_CFA_undefined:
c_insn += dwarf_read_uleb128(ri, c_insn, ®,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
case DW_CFA_def_cfa:
c_insn += dwarf_read_uleb128(ri, c_insn, &utmp,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
rs->cfa_register = utmp;
c_insn += dwarf_read_uleb128(ri, c_insn, &utmp,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
case DW_CFA_def_cfa_register:
c_insn += dwarf_read_uleb128(ri, c_insn, &utmp,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
case DW_CFA_def_cfa_offset:
c_insn += dwarf_read_uleb128(ri, c_insn, &utmp,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
case DW_CFA_def_cfa_expression:
c_insn += dwarf_read_uleb128(ri, c_insn, &utmp,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
case DW_CFA_expression:
c_insn += dwarf_read_uleb128(ri, c_insn, ®,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
set_rule_exp(rs, reg, DW_WHERE_EXPR, c_insn);
c_insn += dwarf_read_uleb128(ri, c_insn, &utmp,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
case DW_CFA_offset_extended_sf:
c_insn += dwarf_read_uleb128(ri, c_insn, ®,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
c_insn += dwarf_read_sleb128(ri, c_insn, &stmp,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
case DW_CFA_val_offset:
c_insn += dwarf_read_uleb128(ri, c_insn, ®,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
c_insn += dwarf_read_uleb128(ri, c_insn, &utmp,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
case DW_CFA_val_offset_sf:
c_insn += dwarf_read_uleb128(ri, c_insn, ®,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME, &err);
if (err)
return err;
c_insn += dwarf_read_sleb128(ri, c_insn, &stmp,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
case DW_CFA_GNU_args_size:
c_insn += dwarf_read_uleb128(ri, c_insn, &utmp,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
case DW_CFA_GNU_negative_offset_extended:
c_insn += dwarf_read_uleb128(ri, c_insn, ®,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
c_insn += dwarf_read_uleb128(ri, c_insn, &utmp,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
case DW_CFA_def_cfa_sf:
c_insn += dwarf_read_uleb128(ri, c_insn, &utmp,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
c_insn += dwarf_read_sleb128(ri, c_insn, &stmp,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
case DW_CFA_def_cfa_offset_sf:
c_insn += dwarf_read_sleb128(ri, c_insn, &stmp,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
case DW_CFA_same_value:
c_insn += dwarf_read_uleb128(ri, c_insn, ®,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
case DW_CFA_val_expression:
c_insn += dwarf_read_uleb128(ri, c_insn, ®,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
set_rule_exp(rs, reg, DW_WHERE_VAL_EXPR, c_insn);
c_insn += dwarf_read_uleb128(ri, c_insn, &utmp,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
p += sizeof(u32);
- id = read_mmap_data_u32(ri, (u32 *)p, DW_SEC_TYPE_TAB, &err);
+ id = read_mmap_data_u32(ri, (u32 *)p, QUADD_SEC_TYPE_EH_FRAME, &err);
if (err)
return err;
if (id != 0)
return -QUADD_URC_TBL_IS_CORRUPT;
- cie_version = read_mmap_data_u8(ri, p++, DW_SEC_TYPE_TAB, &err);
+ cie_version = read_mmap_data_u8(ri, p++, QUADD_SEC_TYPE_EH_FRAME, &err);
if (err)
return err;
pr_debug("aug_string: %s\n", cie->aug_string);
p += dwarf_read_uleb128(ri, p, &cie->code_align_factor,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME, &err);
if (err)
return err;
p += dwarf_read_sleb128(ri, p, &cie->data_align_factor,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME, &err);
if (err)
return err;
if (cie_version == 1) {
cie->retaddr_reg = read_mmap_data_u8(ri, p++,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
} else {
- p += dwarf_read_uleb128(ri, p, &utmp, DW_SEC_TYPE_TAB, &err);
+ p += dwarf_read_uleb128(ri, p, &utmp,
+ QUADD_SEC_TYPE_EH_FRAME, &err);
if (err)
return err;
if (*aug == 'z') {
p += dwarf_read_uleb128(ri, p, &cie->aug_size,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME, &err);
if (err)
return err;
return -QUADD_URC_TBL_IS_CORRUPT;
if (*aug == 'L') {
- cie->lsda_encoding = read_mmap_data_u8(ri, p++,
- DW_SEC_TYPE_TAB,
- &err);
+ cie->lsda_encoding =
+ read_mmap_data_u8(ri, p++,
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
aug++;
} else if (*aug == 'R') {
- cie->fde_encoding = read_mmap_data_u8(ri, p++,
- DW_SEC_TYPE_TAB,
- &err);
+ cie->fde_encoding =
+ read_mmap_data_u8(ri, p++,
+ QUADD_SEC_TYPE_EH_FRAME,
+ &err);
if (err)
return err;
aug++;
pr_debug("fde_encoding: %#x\n", cie->fde_encoding);
} else if (*aug == 'P') {
- int count;
+ int cnt;
void *pcrel_base;
unsigned char handler_encoding;
unsigned long personality;
pcrel_base = (void *)
mmap_addr_to_ex_addr((unsigned long)p,
- ri, DW_SEC_TYPE_TAB);
-
- count = dwarf_read_encoded_value(ri, p, pcrel_base,
- &personality,
- handler_encoding,
- DW_SEC_TYPE_TAB);
- if (count < 0) {
+ ri,
+ QUADD_SEC_TYPE_EH_FRAME);
+
+ cnt = dwarf_read_encoded_value(ri, p, pcrel_base,
+ &personality,
+ handler_encoding,
+ QUADD_SEC_TYPE_EH_FRAME);
+ if (cnt < 0) {
pr_err_once("%s: error: personality routine\n",
__func__);
- return count;
+ return cnt;
}
- p += count;
+ p += cnt;
pr_debug("personality: %#lx\n", personality);
cie->personality = (void *)personality;
p += sizeof(u32);
pcrel_base = (unsigned char *)
- mmap_addr_to_ex_addr((unsigned long)p, ri, DW_SEC_TYPE_TAB);
+ mmap_addr_to_ex_addr((unsigned long)p, ri,
+ QUADD_SEC_TYPE_EH_FRAME);
count = dwarf_read_encoded_value(ri, p, pcrel_base,
&fde->initial_location,
cie->fde_encoding,
- DW_SEC_TYPE_TAB);
+ QUADD_SEC_TYPE_EH_FRAME);
if (count < 0)
return count;
p += count;
fde->address_range = read_mmap_data_u32(ri, (u32 *)p,
- DW_SEC_TYPE_TAB, &err);
+ QUADD_SEC_TYPE_EH_FRAME, &err);
if (err)
return err;
pr_debug("address_range: %#lx\n", fde->address_range);
if (cie->z_aug) {
- p += dwarf_read_uleb128(ri, p, &utmp, DW_SEC_TYPE_TAB, &err);
+ p += dwarf_read_uleb128(ri, p, &utmp,
+ QUADD_SEC_TYPE_EH_FRAME, &err);
if (err)
return err;
end = data + length;
+ pr_debug("hdr: %p\n", hdr);
+
if (hdr->version != 1) {
pr_warn_once("warning: unknown eh hdr format\n");
return NULL;
count = dwarf_read_encoded_value(ri, p, (void *)data_base,
&frame_ptr,
hdr->eh_frame_ptr_enc,
- DW_SEC_TYPE_IDX);
+ QUADD_SEC_TYPE_EH_FRAME_HDR);
if (count < 0)
return NULL;
count = dwarf_read_encoded_value(ri, p, (void *)data_base,
&fde_count, hdr->fde_count_enc,
- DW_SEC_TYPE_IDX);
+ QUADD_SEC_TYPE_EH_FRAME_HDR);
if (count < 0)
return NULL;
unsigned long cie_pointer, length;
unsigned char *frame_start;
unsigned long frame_len, addr;
+ struct extab_info *ti;
+
+ ti = &ri->ex_sec[QUADD_SEC_TYPE_EH_FRAME];
- addr = ri->tabs.extab.addr;
+ addr = ti->addr;
frame_start = (unsigned char *)
- ex_addr_to_mmap_addr(addr, ri, DW_SEC_TYPE_TAB);
- frame_len = ri->tabs.extab.length;
+ ex_addr_to_mmap_addr(addr, ri, QUADD_SEC_TYPE_EH_FRAME);
+
+ frame_len = ti->length;
pr_debug("eh frame: %p - %p\n",
frame_start, frame_start + frame_len);
p = (u32 *)fde_p;
- length = read_mmap_data_u32(ri, p++, DW_SEC_TYPE_TAB, &err);
+ length = read_mmap_data_u32(ri, p++, QUADD_SEC_TYPE_EH_FRAME, &err);
if (err)
return err;
pr_debug("FDE: fde_p: %p, offset: %#lx, len: %#lx\n",
fde_p, fde->offset, fde->length);
- cie_pointer = read_mmap_data_u32(ri, p, DW_SEC_TYPE_TAB, &err);
+ cie_pointer = read_mmap_data_u32(ri, p, QUADD_SEC_TYPE_EH_FRAME, &err);
if (err)
return err;
fde->cie_pointer = cie_pointer;
cie_p = (unsigned char *)p - cie_pointer;
- length = read_mmap_data_u32(ri, (u32 *)cie_p, DW_SEC_TYPE_TAB, &err);
+ length = read_mmap_data_u32(ri, (u32 *)cie_p,
+ QUADD_SEC_TYPE_EH_FRAME, &err);
if (err)
return err;
unsigned long fde_count = 0, data_base;
unsigned long fde_addr, init_loc;
struct dw_fde_table *bst;
+ struct extab_info *ti;
- data_base = ri->tabs.exidx.addr;
+ ti = &ri->ex_sec[QUADD_SEC_TYPE_EH_FRAME_HDR];
+ data_base = ti->addr;
bst = dwarf_get_bs_table(ri, data, length, data_base, &fde_count);
if (!bst || fde_count == 0) {
unsigned long start, end;
fde_addr = dw_bst_get_fde_addr(fi, data_base);
- fde_addr = ex_addr_to_mmap_addr(fde_addr, ri, DW_SEC_TYPE_TAB);
+ fde_addr = ex_addr_to_mmap_addr(fde_addr, ri,
+ QUADD_SEC_TYPE_EH_FRAME);
if (pc == init_loc)
return (void *)fde_addr;
return NULL;
fde_addr = dw_bst_get_fde_addr(fi, data_base);
- fde_addr = ex_addr_to_mmap_addr(fde_addr, ri, DW_SEC_TYPE_TAB);
+ fde_addr = ex_addr_to_mmap_addr(fde_addr, ri, QUADD_SEC_TYPE_EH_FRAME);
return (void *)fde_addr;
}
unsigned char *fde_p;
unsigned char *hdr_start;
unsigned long hdr_len, addr;
+ struct extab_info *ti;
- addr = ri->tabs.exidx.addr;
+ ti = &ri->ex_sec[QUADD_SEC_TYPE_EH_FRAME_HDR];
+
+ addr = ti->addr;
hdr_start = (unsigned char *)
- ex_addr_to_mmap_addr(addr, ri, DW_SEC_TYPE_IDX);
- hdr_len = ri->tabs.exidx.length;
+ ex_addr_to_mmap_addr(addr, ri, QUADD_SEC_TYPE_EH_FRAME_HDR);
+
+ hdr_len = ti->length;
pr_debug("eh frame hdr: %p - %p\n",
hdr_start, hdr_start + hdr_len);
return 0;
}
+static long def_cfa(struct stackframe *sf, struct regs_state *rs)
+{
+ int reg = rs->cfa_register;
+
+ if (reg >= 0) {
+ if (reg >= QUADD_NUM_REGS)
+ return -QUADD_URC_TBL_IS_CORRUPT;
+
+ pr_debug("r%d --> cfa (%#lx)\n", reg, sf->cfa);
+ sf->cfa = sf->vregs[reg];
+ }
+
+ sf->cfa += rs->cfa_offset;
+ pr_debug("cfa += %#lx (%#lx)\n", rs->cfa_offset, sf->cfa);
+
+ return 0;
+}
+
static long
unwind_frame(struct ex_region_info *ri,
struct stackframe *sf,
struct vm_area_struct *vma_sp,
unsigned int *unw_type)
{
+ int i;
long err;
unsigned char *insn_end;
- unsigned long addr, return_addr, fp;
+ unsigned long addr, return_addr, val, user_reg_size;
struct dw_fde fde;
struct dw_cie cie;
unsigned long pc = sf->pc;
struct regs_state *rs, *rs_initial;
+ int mode = sf->mode;
err = dwarf_decode(ri, &cie, &fde, pc);
if (err < 0)
rs = &sf->rs;
rs_initial = &sf->rs_initial;
- set_rule(rs, LR, DW_WHERE_UNDEF, 0);
+ rs->cfa_register = -1;
+ rs_initial->cfa_register = -1;
+
+ rs->cfa_register = 0;
+
+ set_rule(rs, regnum_lr(mode), DW_WHERE_UNDEF, 0);
if (cie.initial_insn) {
insn_end = cie.initial_insn + cie.initial_insn_len;
return err;
}
- if (!sf->cfa)
- sf->cfa = sf->sp + rs->cfa_offset;
- else
- sf->cfa += rs->cfa_offset;
+ pr_debug("mode: %s\n", (mode == DW_MODE_ARM32) ? "arm32" : "arm64");
+ pr_debug("initial cfa: %#lx\n", sf->cfa);
- pr_debug("pc: %#lx, lr: %#lx\n", sf->pc, sf->lr);
- pr_debug("sp: %#lx, fp: %#lx\n", sf->sp, sf->fp);
+ user_reg_size = get_user_reg_size(mode);
- pr_debug("fp rule: %#lx/%ld\n",
- rs->reg[FP].loc.reg, rs->reg[FP].loc.offset);
- pr_debug("lr rule: %#lx/%ld\n",
- rs->reg[LR].loc.reg, rs->reg[LR].loc.offset);
+ err = def_cfa(sf, rs);
+ if (err < 0)
+ return err;
+
+ pr_debug("pc: %#lx, lr: %#lx\n", sf->pc, sf->vregs[regnum_lr(mode)]);
+
+ pr_debug("sp: %#lx, fp: %#lx, fp_thumb: %#lx\n",
+ sf->vregs[regnum_sp(mode)],
+ sf->vregs[regnum_fp(mode)],
+ sf->vregs[ARM32_FP_THUMB]);
+
+ pr_debug("lr rule: %#lx/%ld (where: %u)\n",
+ rs->reg[regnum_lr(mode)].loc.reg,
+ rs->reg[regnum_lr(mode)].loc.offset,
+ rs->reg[regnum_lr(mode)].where);
+
+ pr_debug("fp rule: %#lx/%ld (where: %u)\n",
+ rs->reg[regnum_fp(mode)].loc.reg,
+ rs->reg[regnum_fp(mode)].loc.offset,
+ rs->reg[regnum_fp(mode)].where);
+
+ pr_debug("fp_thumb rule: %#lx/%ld (where: %u)\n",
+ rs->reg[ARM32_FP_THUMB].loc.reg,
+ rs->reg[ARM32_FP_THUMB].loc.offset,
+ rs->reg[ARM32_FP_THUMB].where);
pr_debug("cfa_offset: %ld (%#lx)\n",
rs->cfa_offset, rs->cfa_offset);
pr_debug("cfa_register: %u\n", rs->cfa_register);
- pr_debug("sf->cfa: %#lx\n", sf->cfa);
+ pr_debug("new cfa: %#lx\n", sf->cfa);
- if (rs->reg[LR].where == DW_WHERE_CFAREL) {
- addr = sf->cfa + rs->reg[LR].loc.offset;
- pr_debug("lr: cfa addr: %#lx\n", addr);
+ for (i = 0; i < QUADD_NUM_REGS; i++) {
+ switch (rs->reg[i].where) {
+ case DW_WHERE_UNDEF:
+ break;
- if (!validate_stack_addr(addr, vma_sp, sizeof(unsigned long)))
- return -QUADD_URC_SP_INCORRECT;
+ case DW_WHERE_SAME:
+ break;
- err = read_user_data((unsigned long __user *)addr, return_addr);
- if (err < 0)
- return err;
+ case DW_WHERE_CFAREL:
+ addr = sf->cfa + rs->reg[i].loc.offset;
- *unw_type = QUADD_UNW_TYPE_UT;
- } else {
- return_addr = sf->lr;
- *unw_type = QUADD_UNW_TYPE_LR_UT;
- }
+ if (!validate_stack_addr(addr, vma_sp, user_reg_size))
+ return -QUADD_URC_SP_INCORRECT;
- if (!validate_pc_addr(return_addr, sizeof(unsigned long)))
- return -QUADD_URC_PC_INCORRECT;
+ if (mode == DW_MODE_ARM32)
+ err = read_user_data((u32 __user *)addr, val);
+ else
+ err = read_user_data((unsigned long __user *)
+ addr, val);
- sf->pc = return_addr;
-
- if (rs->reg[FP].where == DW_WHERE_CFAREL) {
- addr = sf->cfa + rs->reg[FP].loc.offset;
- pr_debug("fp: cfa addr: %#lx\n", addr);
+ if (err < 0)
+ return err;
- if (!validate_stack_addr(addr, vma_sp, sizeof(unsigned long)))
- return -QUADD_URC_SP_INCORRECT;
+ sf->vregs[i] = val;
+ pr_debug("[r%d] DW_WHERE_CFAREL: new val: %#lx\n",
+ i, val);
- err = read_user_data((unsigned long __user *)addr, fp);
- if (err < 0)
- return err;
+ break;
- sf->fp = fp;
+ default:
+ pr_err_once("[r%d] error: unsupported rule\n",
+ rs->reg[i].where);
+ break;
+ }
}
- sf->sp = sf->cfa;
+ return_addr = sf->vregs[regnum_lr(mode)];
+ pr_debug("return_addr: %#lx\n", return_addr);
+
+ if (!validate_pc_addr(return_addr, user_reg_size))
+ return -QUADD_URC_PC_INCORRECT;
+
+ sf->pc = return_addr;
+ sf->vregs[regnum_sp(mode)] = sf->cfa;
return 0;
}
struct vm_area_struct *vma_sp,
struct task_struct *task)
{
- unsigned int unw_type = QUADD_UNW_TYPE_UT;
+ unsigned long user_reg_size;
struct ex_region_info ri_new;
+ unsigned int unw_type = QUADD_UNW_TYPE_UT;
+ int mode = sf->mode;
cc->unw_rc = QUADD_URC_FAILURE;
+ user_reg_size = get_user_reg_size(mode);
while (1) {
- long err;
+ long sp, err;
int nr_added;
struct vm_area_struct *vma_pc;
unsigned long addr, where = sf->pc;
struct mm_struct *mm = task->mm;
+ struct extab_info *ti;
if (!mm)
break;
- if (!validate_stack_addr(sf->sp, vma_sp, sizeof(sf->sp))) {
+ sp = sf->vregs[regnum_sp(mode)];
+
+ if (!validate_stack_addr(sp, vma_sp, user_reg_size)) {
cc->unw_rc = -QUADD_URC_SP_INCORRECT;
break;
}
if (!vma_pc)
break;
- addr = ri->tabs.exidx.addr;
+ ti = &ri->ex_sec[QUADD_SEC_TYPE_EH_FRAME_HDR];
+ addr = ti->addr;
- if (!is_vma_addr(addr, vma_pc, sizeof(unsigned long))) {
- err = quadd_search_ex_region(vma_pc->vm_start, &ri_new);
+ if (!is_vma_addr(addr, vma_pc, user_reg_size)) {
+ err = quadd_get_extabs_ehframe(vma_pc->vm_start,
+ &ri_new);
if (err) {
cc->unw_rc = QUADD_URC_TBL_NOT_EXIST;
break;
pr_debug("function at [<%08lx>] from [<%08lx>]\n",
where, sf->pc);
- cc->curr_sp = sf->sp;
- cc->curr_fp = sf->fp;
+ cc->curr_sp = sf->vregs[regnum_sp(mode)];
+ cc->curr_fp = sf->vregs[regnum_fp(mode)];
cc->curr_pc = sf->pc;
nr_added = quadd_callchain_store(cc, sf->pc, unw_type);
}
int
-quadd_aarch64_is_ex_entry_exist(struct pt_regs *regs,
- unsigned long addr,
- struct task_struct *task)
+quadd_is_ex_entry_exist_dwarf(struct pt_regs *regs,
+ unsigned long addr,
+ struct task_struct *task)
{
long err;
unsigned char *fde_p;
unsigned long hdr_len, a;
struct vm_area_struct *vma;
struct mm_struct *mm = task->mm;
+ struct extab_info *ti;
if (!regs || !mm)
return 0;
if (!vma)
return 0;
- err = quadd_search_ex_region(vma->vm_start, &ri);
+ err = quadd_get_extabs_ehframe(vma->vm_start, &ri);
if (err)
return 0;
- a = ri.tabs.exidx.addr;
+ ti = &ri.ex_sec[QUADD_SEC_TYPE_EH_FRAME_HDR];
+
+ a = ti->addr;
hdr_start = (unsigned char *)
- ex_addr_to_mmap_addr(a, &ri, DW_SEC_TYPE_IDX);
- hdr_len = ri.tabs.exidx.length;
+ ex_addr_to_mmap_addr(a, &ri, QUADD_SEC_TYPE_EH_FRAME_HDR);
+
+ hdr_len = ti->length;
fde_p = dwarf_find_fde(&ri, hdr_start, hdr_len, addr);
if (!fde_p)
}
unsigned int
-quadd_aarch64_get_user_callchain_ut(struct pt_regs *regs,
- struct quadd_callchain *cc,
- struct task_struct *task)
+quadd_get_user_cc_dwarf(struct pt_regs *regs,
+ struct quadd_callchain *cc,
+ struct task_struct *task)
{
long err;
- int i, nr_prev = cc->nr;
- unsigned long ip, lr, sp, fp;
+ int i, mode, nr_prev = cc->nr;
+ unsigned long ip, lr, sp, fp, fp_thumb;
struct vm_area_struct *vma, *vma_sp;
struct mm_struct *mm = task->mm;
struct ex_region_info ri;
struct stackframe sf;
+ struct dwarf_cpu_context *cpu_ctx = this_cpu_ptr(ctx.cpu_ctx);
if (!regs || !mm)
return 0;
ip = cc->curr_pc;
sp = cc->curr_sp;
fp = cc->curr_fp;
+ fp_thumb = 0;
lr = 0;
} else {
ip = instruction_pointer(regs);
lr = quadd_user_link_register(regs);
sp = quadd_user_stack_pointer(regs);
- fp = quadd_get_user_frame_pointer(regs);
+
+#ifdef CONFIG_ARM64
+ if (compat_user_mode(regs)) {
+ fp = regs->compat_usr(11);
+ fp_thumb = regs->compat_usr(7);
+ } else {
+ fp = regs->regs[29];
+ fp_thumb = 0;
+ }
+#else
+ fp = regs->ARM_fp;
+ fp_thumb = regs->ARM_r7;
+#endif
}
+#ifdef CONFIG_ARM64
+ if (compat_user_mode(regs))
+ mode = DW_MODE_ARM32;
+ else
+ mode = DW_MODE_ARM64;
+#else
+ mode = DW_MODE_ARM32;
+#endif
+
+
pr_debug("%s: pc: %#lx, lr: %#lx\n", __func__, ip, lr);
- pr_debug("%s: sp: %#lx, fp: %#lx\n", __func__, sp, fp);
+ pr_debug("%s: sp: %#lx, fp: %#lx, fp_thumb: %#lx\n",
+ __func__, sp, fp, fp_thumb);
+ sf.vregs[regnum_lr(mode)] = lr;
sf.pc = ip;
- sf.lr = lr;
- sf.sp = sp;
- sf.fp = fp;
+ sf.vregs[regnum_sp(mode)] = sp;
+ sf.vregs[regnum_fp(mode)] = fp;
+
+ sf.vregs[ARM32_FP_THUMB] = fp_thumb;
+
+ cpu_ctx->dw_word_size = (mode == DW_MODE_ARM32) ?
+ sizeof(u32) : sizeof(u64);
+
+ sf.mode = mode;
sf.cfa = 0;
- for (i = 0; i < ARRAY_SIZE(sf.rs.reg); i++)
+ for (i = 0; i < QUADD_NUM_REGS; i++)
set_rule(&sf.rs, i, DW_WHERE_UNDEF, 0);
vma = find_vma(mm, ip);
if (!vma_sp)
return 0;
- err = quadd_search_ex_region(vma->vm_start, &ri);
+ err = quadd_get_extabs_ehframe(vma->vm_start, &ri);
if (err) {
cc->unw_rc = QUADD_URC_TBL_NOT_EXIST;
return 0;
unwind_backtrace(cc, &ri, &sf, vma_sp, task);
+ pr_debug("%s: mode: %s, cc->nr: %d --> %d\n", __func__,
+ (mode == DW_MODE_ARM32) ? "arm32" : "arm64",
+ nr_prev, cc->nr);
+
return cc->nr;
}
/*
- * drivers/misc/tegra-profiler/exh_tables.c
+ * drivers/misc/tegra-profiler/eh_unwind.c
*
* Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved.
*
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-/*#pragma message("--- version header: remove for static version ---")*/
-#include <linux/version.h>
-
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/slab.h>
static inline unsigned long
ex_addr_to_mmap_addr(unsigned long addr,
struct ex_region_info *ri,
- int exidx)
+ int sec_type)
{
unsigned long offset;
- struct extab_info *ei;
+ struct extab_info *ti;
- ei = exidx ? &ri->tabs.exidx : &ri->tabs.extab;
- offset = addr - ei->addr;
+ ti = &ri->ex_sec[sec_type];
+ offset = addr - ti->addr;
- return ei->mmap_offset + offset + (unsigned long)ri->mmap->data;
+ return ti->mmap_offset + offset + (unsigned long)ri->mmap->data;
}
static inline unsigned long
mmap_addr_to_ex_addr(unsigned long addr,
struct ex_region_info *ri,
- int exidx)
+ int sec_type)
{
unsigned long offset;
- struct extab_info *ei;
+ struct extab_info *ti;
- ei = exidx ? &ri->tabs.exidx : &ri->tabs.extab;
- offset = addr - ei->mmap_offset - (unsigned long)ri->mmap->data;
+ ti = &ri->ex_sec[sec_type];
+ offset = addr - ti->mmap_offset - (unsigned long)ri->mmap->data;
- return ei->addr + offset;
+ return ti->addr + offset;
}
static inline u32
static unsigned long
mmap_prel31_to_addr(const u32 *ptr, struct ex_region_info *ri,
- int is_src_exidx, int is_dst_exidx, int to_mmap)
+ int src_type, int dst_type, int to_mmap)
{
+ s32 offset;
u32 value, addr;
unsigned long addr_res;
- s32 offset;
- struct extab_info *ei_src, *ei_dst;
-
- ei_src = is_src_exidx ? &ri->tabs.exidx : &ri->tabs.extab;
- ei_dst = is_dst_exidx ? &ri->tabs.exidx : &ri->tabs.extab;
value = *ptr;
offset = (((s32)value) << 1) >> 1;
- addr = mmap_addr_to_ex_addr((unsigned long)ptr, ri, is_src_exidx);
+ addr = mmap_addr_to_ex_addr((unsigned long)ptr, ri, src_type);
addr += offset;
addr_res = addr;
if (to_mmap)
- addr_res = ex_addr_to_mmap_addr(addr_res, ri, is_dst_exidx);
+ addr_res = ex_addr_to_mmap_addr(addr_res, ri, dst_type);
return addr_res;
}
}
static struct ex_region_info *
-search_ex_region(struct ex_region_info *array,
- unsigned long size,
- unsigned long key)
+__search_ex_region(struct ex_region_info *array,
+ unsigned long size,
+ unsigned long key)
{
unsigned int i_min, i_max, mid;
return NULL;
}
-long quadd_search_ex_region(unsigned long key, struct ex_region_info *ri)
+static long
+search_ex_region(unsigned long key, struct ex_region_info *ri)
{
struct regions_data *rd;
struct ex_region_info *ri_p = NULL;
if (!rd)
goto out;
- ri_p = search_ex_region(rd->entries, rd->curr_nr, key);
+ ri_p = __search_ex_region(rd->entries, rd->curr_nr, key);
if (ri_p)
memcpy(ri, ri_p, sizeof(*ri));
return ri_p ? 0 : -ENOENT;
}
+static long
+get_extabs_ehabi(unsigned long key, struct ex_region_info *ri)
+{
+ long err;
+ struct extab_info *ti_extab, *ti_exidx;
+
+ err = search_ex_region(key, ri);
+ if (err < 0)
+ return err;
+
+ ti_extab = &ri->ex_sec[QUADD_SEC_TYPE_EXTAB];
+ ti_exidx = &ri->ex_sec[QUADD_SEC_TYPE_EXIDX];
+
+ return (ti_extab->length && ti_exidx->length) ? 0 : -ENOENT;
+}
+
+long
+quadd_get_extabs_ehframe(unsigned long key, struct ex_region_info *ri)
+{
+ long err;
+ struct extab_info *ti_ehfr, *ti_ehfr_hdr;
+
+ err = search_ex_region(key, ri);
+ if (err < 0)
+ return err;
+
+ ti_ehfr = &ri->ex_sec[QUADD_SEC_TYPE_EH_FRAME];
+ ti_ehfr_hdr = &ri->ex_sec[QUADD_SEC_TYPE_EH_FRAME_HDR];
+
+ return (ti_ehfr->length && ti_ehfr_hdr->length) ? 0 : -ENOENT;
+}
+
static struct regions_data *rd_alloc(unsigned long size)
{
struct regions_data *rd;
rd_free(rd);
}
-int quadd_unwind_set_extab(struct quadd_extables *extabs,
+int quadd_unwind_set_extab(struct quadd_sections *extabs,
struct quadd_mmap_area *mmap)
{
- int err = 0;
+ int i, err = 0;
unsigned long nr_entries, nr_added, new_size;
struct ex_region_info ri_entry;
struct extab_info *ti;
ri_entry.tf_start = 0;
ri_entry.tf_end = 0;
- ti = &ri_entry.tabs.exidx;
- ti->addr = extabs->exidx.addr;
- ti->length = extabs->exidx.length;
- ti->mmap_offset = extabs->reserved[QUADD_EXT_IDX_EXIDX_OFFSET];
- ctx.ex_tables_size += ti->length;
+ for (i = 0; i < QUADD_SEC_TYPE_MAX; i++) {
+ struct quadd_sec_info *si = &extabs->sec[i];
- ti = &ri_entry.tabs.extab;
- ti->addr = extabs->extab.addr;
- ti->length = extabs->extab.length;
- ti->mmap_offset = extabs->reserved[QUADD_EXT_IDX_EXTAB_OFFSET];
- ctx.ex_tables_size += ti->length;
+ ti = &ri_entry.ex_sec[i];
+
+ if (!si->addr) {
+ ti->addr = 0;
+ ti->length = 0;
+ ti->mmap_offset = 0;
+
+ continue;
+ }
+
+ ti->addr = si->addr;
+ ti->length = si->length;
+ ti->mmap_offset = si->mmap_offset;
+ }
nr_added = add_ex_region(rd_new, &ri_entry);
if (nr_added == 0)
rd_new->curr_nr = nr_entries;
- ri = search_ex_region(rd_new->entries, nr_entries, vm_start);
+ ri = __search_ex_region(rd_new->entries, nr_entries, vm_start);
if (!ri)
goto error_free;
static const struct unwind_idx *
unwind_find_idx(struct ex_region_info *ri, u32 addr)
{
- unsigned long length;
u32 value;
+ unsigned long length;
+ struct extab_info *ti;
struct unwind_idx *start;
struct unwind_idx *stop;
struct unwind_idx *mid = NULL;
- length = ri->tabs.exidx.length / sizeof(*start);
+
+ ti = &ri->ex_sec[QUADD_SEC_TYPE_EXIDX];
+
+ length = ti->length / sizeof(*start);
if (unlikely(!length))
return NULL;
- start = (struct unwind_idx *)((char *)ri->mmap->data +
- ri->tabs.exidx.mmap_offset);
+ start = (struct unwind_idx *)((char *)ri->mmap->data + ti->mmap_offset);
stop = start + length - 1;
- value = (u32)mmap_prel31_to_addr(&start->addr_offset, ri, 1, 0, 0);
+ value = (u32)mmap_prel31_to_addr(&start->addr_offset, ri,
+ QUADD_SEC_TYPE_EXIDX,
+ QUADD_SEC_TYPE_EXTAB, 0);
if (addr < value)
return NULL;
- value = (u32)mmap_prel31_to_addr(&stop->addr_offset, ri, 1, 0, 0);
+ value = (u32)mmap_prel31_to_addr(&stop->addr_offset, ri,
+ QUADD_SEC_TYPE_EXIDX,
+ QUADD_SEC_TYPE_EXTAB, 0);
if (addr >= value)
return NULL;
while (start < stop - 1) {
mid = start + ((stop - start) >> 1);
- value = (u32)mmap_prel31_to_addr(&mid->addr_offset,
- ri, 1, 0, 0);
+ value = (u32)mmap_prel31_to_addr(&mid->addr_offset, ri,
+ QUADD_SEC_TYPE_EXIDX,
+ QUADD_SEC_TYPE_EXTAB, 0);
if (addr < value)
stop = mid;
} else if ((val & 0x80000000) == 0) {
/* prel31 to the unwind table */
ctrl.insn = (u32 *)(unsigned long)
- mmap_prel31_to_addr(&idx->insn, ri, 1, 0, 1);
+ mmap_prel31_to_addr(&idx->insn, ri,
+ QUADD_SEC_TYPE_EXIDX,
+ QUADD_SEC_TYPE_EXTAB, 1);
if (!ctrl.insn)
return -QUADD_URC_EACCESS;
} else if ((val & 0xff000000) == 0x80000000) {
while (1) {
long err;
int nr_added;
+ struct extab_info *ti;
unsigned long where = frame->pc;
struct vm_area_struct *vma_pc;
struct mm_struct *mm = task->mm;
if (!vma_pc)
break;
- if (!is_vma_addr(ri->tabs.exidx.addr, vma_pc, sizeof(u32))) {
- err = quadd_search_ex_region(vma_pc->vm_start, &ri_new);
+ ti = &ri->ex_sec[QUADD_SEC_TYPE_EXIDX];
+
+ if (!is_vma_addr(ti->addr, vma_pc, sizeof(u32))) {
+ err = get_extabs_ehabi(vma_pc->vm_start, &ri_new);
if (err) {
cc->unw_rc = QUADD_URC_TBL_NOT_EXIST;
break;
}
unsigned int
-quadd_aarch32_get_user_callchain_ut(struct pt_regs *regs,
- struct quadd_callchain *cc,
- struct task_struct *task)
+quadd_get_user_cc_arm32_ehabi(struct pt_regs *regs,
+ struct quadd_callchain *cc,
+ struct task_struct *task)
{
long err;
int nr_prev = cc->nr;
return 0;
#ifdef CONFIG_ARM64
- if (!compat_user_mode(regs)) {
- pr_warn_once("user_mode 64: unsupported\n");
+ if (!compat_user_mode(regs))
return 0;
- }
#endif
if (cc->unw_rc == QUADD_URC_LEVEL_TOO_DEEP)
if (!vma_sp)
return 0;
- err = quadd_search_ex_region(vma->vm_start, &ri);
+ err = get_extabs_ehabi(vma->vm_start, &ri);
if (err) {
cc->unw_rc = QUADD_URC_TBL_NOT_EXIST;
return 0;
unwind_backtrace(cc, &ri, &frame, vma_sp, task);
+ pr_debug("%s: exit, cc->nr: %d --> %d\n",
+ __func__, nr_prev, cc->nr);
+
return cc->nr;
}
int
-quadd_aarch32_is_ex_entry_exist(struct pt_regs *regs,
- unsigned long addr,
- struct task_struct *task)
+quadd_is_ex_entry_exist_arm32_ehabi(struct pt_regs *regs,
+ unsigned long addr,
+ struct task_struct *task)
{
long err;
u32 value;
if (!vma)
return 0;
- err = quadd_search_ex_region(vma->vm_start, &ri);
+ err = get_extabs_ehabi(vma->vm_start, &ri);
if (err)
return 0;
if (err < 0)
return 0;
+ /* EXIDX_CANTUNWIND */
if (value == 1)
return 0;