From cce82cdcece7b05d6245a46d2033bdb4a8c710f2 Mon Sep 17 00:00:00 2001 From: Igor Nabirushkin Date: Fri, 13 Feb 2015 21:35:02 +0400 Subject: [PATCH] misc: tegra-profiler: support eh_frame sections Support dwarf unwinding for arm32 files (based on eh_frame sections). Bug 1611069 Change-Id: Ie057ebcfcff75ccb2d3e62aa7cb85ac4b090930c Signed-off-by: Igor Nabirushkin Reviewed-on: http://git-master/r/707903 (cherry picked from commit 950855f44e647b22508f2d31fe373fffd9e0642f) Reviewed-on: http://git-master/r/747745 GVS: Gerrit_Virtual_Submit Reviewed-by: Andrey Trachenko Reviewed-by: Winnie Hsu --- drivers/misc/tegra-profiler/backtrace.c | 34 +- drivers/misc/tegra-profiler/comm.c | 18 +- drivers/misc/tegra-profiler/comm.h | 5 +- drivers/misc/tegra-profiler/dwarf_unwind.c | 538 ++++++++++++++------- drivers/misc/tegra-profiler/dwarf_unwind.h | 16 +- drivers/misc/tegra-profiler/eh_unwind.c | 171 ++++--- drivers/misc/tegra-profiler/eh_unwind.h | 27 +- drivers/misc/tegra-profiler/main.c | 2 +- drivers/misc/tegra-profiler/version.h | 2 +- include/linux/tegra_profiler.h | 39 +- 10 files changed, 555 insertions(+), 297 deletions(-) diff --git a/drivers/misc/tegra-profiler/backtrace.c b/drivers/misc/tegra-profiler/backtrace.c index 5c03114ee75..352095b1cfa 100644 --- a/drivers/misc/tegra-profiler/backtrace.c +++ b/drivers/misc/tegra-profiler/backtrace.c @@ -120,12 +120,8 @@ is_ex_entry_exist(struct pt_regs *regs, unsigned long addr, struct task_struct *task) { -#ifdef CONFIG_ARM64 - if (!compat_user_mode(regs)) - return quadd_aarch64_is_ex_entry_exist(regs, addr, task); -#endif - - return quadd_aarch32_is_ex_entry_exist(regs, addr, task); + return quadd_is_ex_entry_exist_dwarf(regs, addr, task) || + quadd_is_ex_entry_exist_arm32_ehabi(regs, addr, task); } static unsigned long __user * @@ -537,11 +533,21 @@ get_user_callchain_ut(struct pt_regs *regs, struct quadd_callchain *cc, struct task_struct *task) { -#ifdef CONFIG_ARM64 - if (!compat_user_mode(regs)) - return quadd_aarch64_get_user_callchain_ut(regs, cc, task); -#endif - return quadd_aarch32_get_user_callchain_ut(regs, cc, task); + int nr_prev; + + do { + nr_prev = cc->nr; + + quadd_get_user_cc_dwarf(regs, cc, task); + if (nr_prev > 0 && cc->nr == nr_prev) + break; + + nr_prev = cc->nr; + + quadd_get_user_cc_arm32_ehabi(regs, cc, task); + } while (nr_prev != cc->nr); + + return cc->nr; } static unsigned int @@ -554,11 +560,9 @@ get_user_callchain_mixed(struct pt_regs *regs, do { nr_prev = cc->nr; - get_user_callchain_ut(regs, cc, task); - if (nr_prev > 0 && cc->nr == nr_prev) - break; + quadd_get_user_cc_dwarf(regs, cc, task); - nr_prev = cc->nr; + quadd_get_user_cc_arm32_ehabi(regs, cc, task); __get_user_callchain_fp(regs, cc, task); } while (nr_prev != cc->nr); diff --git a/drivers/misc/tegra-profiler/comm.c b/drivers/misc/tegra-profiler/comm.c index 8f42289d7bd..7c4b6fcb1a7 100644 --- a/drivers/misc/tegra-profiler/comm.c +++ b/drivers/misc/tegra-profiler/comm.c @@ -440,13 +440,12 @@ device_ioctl(struct file *file, unsigned long ioctl_param) { int err = 0; - u64 *mmap_vm_start; struct quadd_mmap_area *mmap; struct quadd_parameters *user_params; struct quadd_comm_cap cap; struct quadd_module_state state; struct quadd_module_version versions; - struct quadd_extables extabs; + struct quadd_sections extabs; struct quadd_mmap_rb_info mmap_rb; if (ioctl_num != IOCTL_SETUP && @@ -576,19 +575,22 @@ device_ioctl(struct file *file, } break; - case IOCTL_SET_EXTAB: + case IOCTL_SET_SECTIONS_INFO: if (copy_from_user(&extabs, (void __user *)ioctl_param, sizeof(extabs))) { - pr_err("error: set_extab failed\n"); + pr_err("error: set_sections_info failed\n"); err = -EFAULT; goto error_out; } - mmap_vm_start = (u64 *) - &extabs.reserved[QUADD_EXT_IDX_MMAP_VM_START]; + pr_debug("%s: user_mmap_start: %#llx, sections vma: %#llx - %#llx\n", + __func__, + (unsigned long long)extabs.user_mmap_start, + (unsigned long long)extabs.vm_start, + (unsigned long long)extabs.vm_end); spin_lock(&comm_ctx.mmaps_lock); - mmap = find_mmap((unsigned long)*mmap_vm_start); + mmap = find_mmap(extabs.user_mmap_start); if (!mmap) { pr_err("%s: error: mmap is not found\n", __func__); err = -ENXIO; @@ -602,7 +604,7 @@ device_ioctl(struct file *file, err = comm_ctx.control->set_extab(&extabs, mmap); spin_unlock(&comm_ctx.mmaps_lock); if (err) { - pr_err("error: set_extab\n"); + pr_err("error: set_sections_info\n"); goto error_out; } break; diff --git a/drivers/misc/tegra-profiler/comm.h b/drivers/misc/tegra-profiler/comm.h index 66945ab766e..c89acd7db90 100644 --- a/drivers/misc/tegra-profiler/comm.h +++ b/drivers/misc/tegra-profiler/comm.h @@ -24,7 +24,7 @@ struct quadd_comm_cap; struct quadd_module_state; struct miscdevice; struct quadd_parameters; -struct quadd_extables; +struct quadd_sections; struct quadd_unwind_ctx; struct quadd_ring_buffer; @@ -58,8 +58,7 @@ struct quadd_comm_control_interface { uid_t *debug_app_uid); void (*get_capabilities)(struct quadd_comm_cap *cap); void (*get_state)(struct quadd_module_state *state); - - int (*set_extab)(struct quadd_extables *extabs, + int (*set_extab)(struct quadd_sections *extabs, struct quadd_mmap_area *mmap); void (*delete_mmap)(struct quadd_mmap_area *mmap); }; diff --git a/drivers/misc/tegra-profiler/dwarf_unwind.c b/drivers/misc/tegra-profiler/dwarf_unwind.c index 8699becaa64..9121a83fb49 100644 --- a/drivers/misc/tegra-profiler/dwarf_unwind.c +++ b/drivers/misc/tegra-profiler/dwarf_unwind.c @@ -43,16 +43,28 @@ enum { }; #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 { @@ -85,10 +97,10 @@ struct dw_fde_table { }; 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; @@ -101,6 +113,8 @@ struct regs_state { struct dwarf_cpu_context { struct regs_state rs_stack[DW_MAX_RS_STACK_DEPTH]; int depth; + + int dw_word_size; }; struct quadd_dwarf_context { @@ -110,14 +124,14 @@ 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 { @@ -165,8 +179,6 @@ struct eh_sec_data { unsigned char *data; }; -typedef u64 dw_word_t; - #define read_user_data(addr, retval) \ ({ \ long ret; \ @@ -186,21 +198,47 @@ typedef u64 dw_word_t; 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", @@ -327,12 +365,12 @@ ex_addr_to_mmap_addr(unsigned long addr, 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 @@ -340,12 +378,12 @@ mmap_addr_to_ex_addr(unsigned long addr, 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) @@ -516,12 +554,15 @@ dwarf_read_encoded_value(struct ex_region_info *ri, 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"); @@ -529,13 +570,13 @@ dwarf_read_encoded_value(struct ex_region_info *ri, 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__); @@ -545,16 +586,16 @@ dwarf_read_encoded_value(struct ex_region_info *ri, 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"); @@ -564,7 +605,7 @@ dwarf_read_encoded_value(struct ex_region_info *ri, if (err) return err; - return sizeof(dw_word_t); + return dw_word_size; case DW_EH_PE_sdata2: case DW_EH_PE_udata2: @@ -653,10 +694,10 @@ dwarf_read_encoded_value(struct ex_region_info *ri, 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 { @@ -700,7 +741,8 @@ dwarf_cfa_exec_insns(struct ex_region_info *ri, 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; @@ -716,7 +758,8 @@ dwarf_cfa_exec_insns(struct ex_region_info *ri, 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; @@ -744,7 +787,8 @@ dwarf_cfa_exec_insns(struct ex_region_info *ri, 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; @@ -756,7 +800,8 @@ dwarf_cfa_exec_insns(struct ex_region_info *ri, 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; @@ -769,7 +814,8 @@ dwarf_cfa_exec_insns(struct ex_region_info *ri, 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; @@ -782,13 +828,15 @@ dwarf_cfa_exec_insns(struct ex_region_info *ri, 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; @@ -799,7 +847,8 @@ dwarf_cfa_exec_insns(struct ex_region_info *ri, 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; @@ -808,7 +857,8 @@ dwarf_cfa_exec_insns(struct ex_region_info *ri, 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; @@ -818,13 +868,15 @@ dwarf_cfa_exec_insns(struct ex_region_info *ri, 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; @@ -836,7 +888,8 @@ dwarf_cfa_exec_insns(struct ex_region_info *ri, 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; @@ -847,7 +900,8 @@ dwarf_cfa_exec_insns(struct ex_region_info *ri, 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; @@ -858,7 +912,8 @@ dwarf_cfa_exec_insns(struct ex_region_info *ri, 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; @@ -875,14 +930,16 @@ dwarf_cfa_exec_insns(struct ex_region_info *ri, 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; @@ -893,12 +950,14 @@ dwarf_cfa_exec_insns(struct ex_region_info *ri, 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; @@ -910,12 +969,14 @@ dwarf_cfa_exec_insns(struct ex_region_info *ri, 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; @@ -927,12 +988,13 @@ dwarf_cfa_exec_insns(struct ex_region_info *ri, 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; @@ -944,7 +1006,8 @@ dwarf_cfa_exec_insns(struct ex_region_info *ri, 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; @@ -953,12 +1016,14 @@ dwarf_cfa_exec_insns(struct ex_region_info *ri, 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; @@ -992,12 +1057,14 @@ dwarf_cfa_exec_insns(struct ex_region_info *ri, 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; @@ -1012,7 +1079,8 @@ dwarf_cfa_exec_insns(struct ex_region_info *ri, 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; @@ -1023,7 +1091,8 @@ dwarf_cfa_exec_insns(struct ex_region_info *ri, 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; @@ -1033,13 +1102,15 @@ dwarf_cfa_exec_insns(struct ex_region_info *ri, 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; @@ -1073,7 +1144,7 @@ decode_cie_entry(struct ex_region_info *ri, 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; @@ -1082,7 +1153,7 @@ decode_cie_entry(struct ex_region_info *ri, 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; @@ -1105,22 +1176,24 @@ decode_cie_entry(struct ex_region_info *ri, 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; @@ -1137,7 +1210,7 @@ decode_cie_entry(struct ex_region_info *ri, 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; @@ -1158,24 +1231,26 @@ decode_cie_entry(struct ex_region_info *ri, 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; @@ -1184,18 +1259,19 @@ decode_cie_entry(struct ex_region_info *ri, 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; @@ -1244,19 +1320,20 @@ decode_fde_entry(struct ex_region_info *ri, 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; @@ -1266,7 +1343,8 @@ decode_fde_entry(struct ex_region_info *ri, 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; @@ -1344,6 +1422,8 @@ dwarf_get_bs_table(struct ex_region_info *ri, end = data + length; + pr_debug("hdr: %p\n", hdr); + if (hdr->version != 1) { pr_warn_once("warning: unknown eh hdr format\n"); return NULL; @@ -1354,7 +1434,7 @@ dwarf_get_bs_table(struct ex_region_info *ri, 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; @@ -1366,7 +1446,7 @@ dwarf_get_bs_table(struct ex_region_info *ri, 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; @@ -1401,19 +1481,23 @@ dwarf_decode_fde_cie(struct ex_region_info *ri, 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; @@ -1428,14 +1512,15 @@ dwarf_decode_fde_cie(struct ex_region_info *ri, 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; @@ -1474,8 +1559,10 @@ dwarf_find_fde(struct ex_region_info *ri, 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) { @@ -1490,7 +1577,8 @@ dwarf_find_fde(struct ex_region_info *ri, 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; @@ -1522,7 +1610,7 @@ dwarf_find_fde(struct ex_region_info *ri, 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; } @@ -1537,12 +1625,16 @@ dwarf_decode(struct ex_region_info *ri, 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); @@ -1566,19 +1658,39 @@ dwarf_decode(struct ex_region_info *ri, 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) @@ -1589,7 +1701,12 @@ unwind_frame(struct ex_region_info *ri, 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; @@ -1609,61 +1726,86 @@ unwind_frame(struct ex_region_info *ri, 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; } @@ -1675,22 +1817,28 @@ unwind_backtrace(struct quadd_callchain *cc, 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; } @@ -1699,10 +1847,12 @@ unwind_backtrace(struct quadd_callchain *cc, 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; @@ -1720,8 +1870,8 @@ unwind_backtrace(struct quadd_callchain *cc, 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); @@ -1731,9 +1881,9 @@ unwind_backtrace(struct quadd_callchain *cc, } 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; @@ -1742,6 +1892,7 @@ quadd_aarch64_is_ex_entry_exist(struct pt_regs *regs, 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; @@ -1750,15 +1901,18 @@ quadd_aarch64_is_ex_entry_exist(struct pt_regs *regs, 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) @@ -1768,17 +1922,18 @@ quadd_aarch64_is_ex_entry_exist(struct pt_regs *regs, } 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; @@ -1792,25 +1947,56 @@ quadd_aarch64_get_user_callchain_ut(struct pt_regs *regs, 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); @@ -1821,7 +2007,7 @@ quadd_aarch64_get_user_callchain_ut(struct pt_regs *regs, 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; @@ -1829,6 +2015,10 @@ quadd_aarch64_get_user_callchain_ut(struct pt_regs *regs, 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; } diff --git a/drivers/misc/tegra-profiler/dwarf_unwind.h b/drivers/misc/tegra-profiler/dwarf_unwind.h index a17afb7ab4e..de5d3085366 100644 --- a/drivers/misc/tegra-profiler/dwarf_unwind.h +++ b/drivers/misc/tegra-profiler/dwarf_unwind.h @@ -21,15 +21,15 @@ struct pt_regs; struct quadd_callchain; struct task_struct; -unsigned int -quadd_aarch64_get_user_callchain_ut(struct pt_regs *regs, - struct quadd_callchain *cc, - struct task_struct *task); - 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); + +unsigned int +quadd_get_user_cc_dwarf(struct pt_regs *regs, + struct quadd_callchain *cc, + struct task_struct *task); int quadd_dwarf_unwind_start(void); void quadd_dwarf_unwind_stop(void); diff --git a/drivers/misc/tegra-profiler/eh_unwind.c b/drivers/misc/tegra-profiler/eh_unwind.c index 0fecf0b1059..1c3e6801311 100644 --- a/drivers/misc/tegra-profiler/eh_unwind.c +++ b/drivers/misc/tegra-profiler/eh_unwind.c @@ -1,5 +1,5 @@ /* - * drivers/misc/tegra-profiler/exh_tables.c + * drivers/misc/tegra-profiler/eh_unwind.c * * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. * @@ -16,9 +16,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -/*#pragma message("--- version header: remove for static version ---")*/ -#include - #include #include #include @@ -149,29 +146,29 @@ read_mmap_data(struct quadd_mmap_area *mmap, const u32 *addr, u32 *retval) 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 @@ -190,25 +187,21 @@ prel31_to_addr(const u32 *ptr) 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; } @@ -313,9 +306,9 @@ remove_ex_region(struct regions_data *rd, } 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; @@ -340,7 +333,8 @@ search_ex_region(struct ex_region_info *array, 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; @@ -351,7 +345,7 @@ long quadd_search_ex_region(unsigned long key, struct ex_region_info *ri) 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)); @@ -360,6 +354,38 @@ out: 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; @@ -394,10 +420,10 @@ static void rd_free_rcu(struct rcu_head *rh) 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; @@ -443,17 +469,23 @@ int quadd_unwind_set_extab(struct quadd_extables *extabs, 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) @@ -517,7 +549,7 @@ quadd_unwind_set_tail_info(unsigned long vm_start, 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; @@ -599,33 +631,41 @@ error_out: 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; @@ -946,7 +986,9 @@ unwind_frame(struct ex_region_info *ri, } 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) { @@ -1027,6 +1069,7 @@ unwind_backtrace(struct quadd_callchain *cc, 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; @@ -1043,8 +1086,10 @@ unwind_backtrace(struct quadd_callchain *cc, 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; @@ -1074,9 +1119,9 @@ unwind_backtrace(struct quadd_callchain *cc, } 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; @@ -1090,10 +1135,8 @@ quadd_aarch32_get_user_callchain_ut(struct pt_regs *regs, 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) @@ -1134,7 +1177,7 @@ quadd_aarch32_get_user_callchain_ut(struct pt_regs *regs, 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; @@ -1142,13 +1185,16 @@ quadd_aarch32_get_user_callchain_ut(struct pt_regs *regs, 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; @@ -1164,7 +1210,7 @@ quadd_aarch32_is_ex_entry_exist(struct pt_regs *regs, 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; @@ -1176,6 +1222,7 @@ quadd_aarch32_is_ex_entry_exist(struct pt_regs *regs, if (err < 0) return 0; + /* EXIDX_CANTUNWIND */ if (value == 1) return 0; diff --git a/drivers/misc/tegra-profiler/eh_unwind.h b/drivers/misc/tegra-profiler/eh_unwind.h index 77fdfc92aba..7ce0f828b9e 100644 --- a/drivers/misc/tegra-profiler/eh_unwind.h +++ b/drivers/misc/tegra-profiler/eh_unwind.h @@ -17,6 +17,8 @@ #ifndef __QUADD_EH_UNWIND_H__ #define __QUADD_EH_UNWIND_H__ +#include + struct pt_regs; struct quadd_callchain; struct quadd_ctx; @@ -25,9 +27,9 @@ struct task_struct; struct quadd_mmap_area; 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); int quadd_unwind_init(void); void quadd_unwind_deinit(void); @@ -35,20 +37,21 @@ void quadd_unwind_deinit(void); int quadd_unwind_start(struct task_struct *task); void quadd_unwind_stop(void); -int quadd_unwind_set_extab(struct quadd_extables *extabs, +int quadd_unwind_set_extab(struct quadd_sections *extabs, struct quadd_mmap_area *mmap); void quadd_unwind_delete_mmap(struct quadd_mmap_area *mmap); 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); void quadd_unwind_set_tail_info(unsigned long vm_start, unsigned long tf_start, unsigned long tf_end); + struct extab_info { unsigned long addr; unsigned long length; @@ -56,16 +59,11 @@ struct extab_info { unsigned long mmap_offset; }; -struct extables { - struct extab_info extab; - struct extab_info exidx; -}; - struct ex_region_info { unsigned long vm_start; unsigned long vm_end; - struct extables tabs; + struct extab_info ex_sec[QUADD_SEC_TYPE_MAX]; struct quadd_mmap_area *mmap; struct list_head list; @@ -74,6 +72,7 @@ struct ex_region_info { unsigned long tf_end; }; -long quadd_search_ex_region(unsigned long key, struct ex_region_info *ri); +long +quadd_get_extabs_ehframe(unsigned long key, struct ex_region_info *ri); #endif /* __QUADD_EH_UNWIND_H__ */ diff --git a/drivers/misc/tegra-profiler/main.c b/drivers/misc/tegra-profiler/main.c index 0f1237fe57f..b737f9910cf 100644 --- a/drivers/misc/tegra-profiler/main.c +++ b/drivers/misc/tegra-profiler/main.c @@ -463,7 +463,7 @@ void quadd_get_state(struct quadd_module_state *state) } static int -set_extab(struct quadd_extables *extabs, +set_extab(struct quadd_sections *extabs, struct quadd_mmap_area *mmap) { return quadd_unwind_set_extab(extabs, mmap); diff --git a/drivers/misc/tegra-profiler/version.h b/drivers/misc/tegra-profiler/version.h index 7b3fc20f633..fb60632eb25 100644 --- a/drivers/misc/tegra-profiler/version.h +++ b/drivers/misc/tegra-profiler/version.h @@ -18,7 +18,7 @@ #ifndef __QUADD_VERSION_H #define __QUADD_VERSION_H -#define QUADD_MODULE_VERSION "1.88" +#define QUADD_MODULE_VERSION "1.89" #define QUADD_MODULE_BRANCH "Dev" #endif /* __QUADD_VERSION_H */ diff --git a/include/linux/tegra_profiler.h b/include/linux/tegra_profiler.h index 4225e7e9274..1aa39a4ad4b 100644 --- a/include/linux/tegra_profiler.h +++ b/include/linux/tegra_profiler.h @@ -20,7 +20,7 @@ #include #define QUADD_SAMPLES_VERSION 31 -#define QUADD_IO_VERSION 16 +#define QUADD_IO_VERSION 17 #define QUADD_IO_VERSION_DYNAMIC_RB 5 #define QUADD_IO_VERSION_RB_MAX_FILL_COUNT 6 @@ -34,6 +34,7 @@ #define QUADD_IO_VERSION_DATA_MMAP 14 #define QUADD_IO_VERSION_BT_LOWER_BOUND 15 #define QUADD_IO_VERSION_STACK_OFFSET 16 +#define QUADD_IO_VERSION_SECTIONS_INFO 17 #define QUADD_SAMPLE_VERSION_THUMB_MODE_FLAG 17 #define QUADD_SAMPLE_VERSION_GROUP_SAMPLES 18 @@ -94,14 +95,20 @@ /* * Send exception-handling tables info + * This ioctl is obsolete */ -#define IOCTL_SET_EXTAB _IOW(QUADD_IOCTL, 6, struct quadd_extables) +/*#define IOCTL_SET_EXTAB _IOW(QUADD_IOCTL, 6, struct quadd_extables)*/ /* * Send ring buffer mmap info */ #define IOCTL_SET_MMAP_RB _IOW(QUADD_IOCTL, 7, struct quadd_mmap_rb_info) +/* + * Send sections info + */ +#define IOCTL_SET_SECTIONS_INFO _IOW(QUADD_IOCTL, 8, struct quadd_sections) + #define QUADD_CPUMODE_TEGRA_POWER_CLUSTER_LP (1 << 29) /* LP CPU */ #define QUADD_CPUMODE_THUMB (1 << 30) /* thumb mode */ @@ -455,25 +462,35 @@ struct quadd_module_version { u32 reserved[4]; /* reserved fields for future extensions */ }; +enum { + QUADD_SEC_TYPE_EXTAB = 0, + QUADD_SEC_TYPE_EXIDX, + + QUADD_SEC_TYPE_EH_FRAME, + QUADD_SEC_TYPE_EH_FRAME_HDR, + + QUADD_SEC_TYPE_DEBUG_FRAME, + QUADD_SEC_TYPE_DEBUG_FRAME_HDR, + + QUADD_SEC_TYPE_MAX, +}; + struct quadd_sec_info { u64 addr; u64 length; -}; -enum { - QUADD_EXT_IDX_EXTAB_OFFSET = 0, - QUADD_EXT_IDX_EXIDX_OFFSET = 1, - QUADD_EXT_IDX_MMAP_VM_START = 2, + u64 mmap_offset; }; -struct quadd_extables { +struct quadd_sections { u64 vm_start; u64 vm_end; - struct quadd_sec_info extab; - struct quadd_sec_info exidx; + struct quadd_sec_info sec[QUADD_SEC_TYPE_MAX]; - u32 reserved[4]; /* reserved fields for future extensions */ + u64 user_mmap_start; + + u64 reserved[4]; /* reserved fields for future extensions */ }; struct quadd_mmap_rb_info { -- 2.39.2