static void dump_backtrace_entry(unsigned long where, unsigned long stack) { print_ip_sym(where); if (in_exception_text(where)) dump_mem("", "Exception stack", stack, stack + sizeof(struct pt_regs)); }
int __kprobes arch_prepare_kprobe(struct kprobe *p) { kprobe_opcode_t insn; kprobe_opcode_t tmp_insn[MAX_INSN_SIZE]; unsigned long addr = (unsigned long)p->addr; int is; if (addr & 0x3 || in_exception_text(addr)) return -EINVAL; insn = *p->addr; p->opcode = insn; p->ainsn.insn = tmp_insn; switch (arm_kprobe_decode_insn(insn, &p->ainsn)) { case INSN_REJECTED: /* not supported */ return -EINVAL; case INSN_GOOD: /* instruction uses slot */ p->ainsn.insn = get_insn_slot(); if (!p->ainsn.insn) return -ENOMEM; for (is = 0; is < MAX_INSN_SIZE; ++is) p->ainsn.insn[is] = tmp_insn[is]; flush_insns(p->ainsn.insn, MAX_INSN_SIZE); break; case INSN_GOOD_NO_SLOT: /* instruction doesn't need insn slot */ p->ainsn.insn = NULL; break; } return 0; }
static void dump_backtrace_entry(unsigned long where, unsigned long stack) { print_ip_sym(where); if (in_exception_text(where)) dump_mem("", "Exception stack", stack, stack + sizeof(struct pt_regs) + 180); /* Additional 180 to workaround sp offset */ }
int __kprobes arch_prepare_kprobe(struct kprobe *p) { kprobe_opcode_t insn; kprobe_opcode_t tmp_insn[MAX_INSN_SIZE]; unsigned long addr = (unsigned long)p->addr; bool thumb; kprobe_decode_insn_t *decode_insn; int is; if (in_exception_text(addr)) return -EINVAL; #ifdef CONFIG_THUMB2_KERNEL thumb = true; addr &= ~1; /* Bit 0 would normally be set to indicate Thumb code */ insn = __mem_to_opcode_thumb16(((u16 *)addr)[0]); if (is_wide_instruction(insn)) { u16 inst2 = __mem_to_opcode_thumb16(((u16 *)addr)[1]); insn = __opcode_thumb32_compose(insn, inst2); decode_insn = thumb32_kprobe_decode_insn; } else decode_insn = thumb16_kprobe_decode_insn; #else /* !CONFIG_THUMB2_KERNEL */ thumb = false; if (addr & 0x3) return -EINVAL; insn = __mem_to_opcode_arm(*p->addr); decode_insn = arm_kprobe_decode_insn; #endif p->opcode = insn; p->ainsn.insn = tmp_insn; switch ((*decode_insn)(insn, &p->ainsn)) { case INSN_REJECTED: /* not supported */ return -EINVAL; case INSN_GOOD: /* instruction uses slot */ p->ainsn.insn = get_insn_slot(); if (!p->ainsn.insn) return -ENOMEM; for (is = 0; is < MAX_INSN_SIZE; ++is) p->ainsn.insn[is] = tmp_insn[is]; flush_insns(p->ainsn.insn, sizeof(p->ainsn.insn[0]) * MAX_INSN_SIZE); p->ainsn.insn_fn = (kprobe_insn_fn_t *) ((uintptr_t)p->ainsn.insn | thumb); break; case INSN_GOOD_NO_SLOT: /* instruction doesn't need insn slot */ p->ainsn.insn = NULL; break; } return 0; }
static int aed_walk_stackframe(struct stackframe *frame, struct aee_process_bt *bt, unsigned int stack_address) { int count; struct stackframe current_stk; bt->entries = aed_backtrace_buffer; memcpy(¤t_stk, frame, sizeof(struct stackframe)); for (count = 0; count < AEE_NR_FRAME; count++) { unsigned long prev_fp = current_stk.fp; int ret; bt->entries[bt->nr_entries].pc = current_stk.pc; bt->entries[bt->nr_entries].lr = current_stk.lr; snprintf(bt->entries[bt->nr_entries].pc_symbol, AEE_SZ_SYMBOL_S, "%pS", (void *)current_stk.pc); snprintf(bt->entries[bt->nr_entries].lr_symbol, AEE_SZ_SYMBOL_L, "%pS", (void *)current_stk.lr); bt->nr_entries++; if (bt->nr_entries >= AEE_NR_FRAME) { break; } ret = aed_unwind_frame(¤t_stk, stack_address); /* oops, reached end without exception. return original info */ if (ret < 0) break; if (in_exception_text(current_stk.pc) || ((current_stk.pc - FUNCTION_OFFSET) == (unsigned long)preempt_schedule_irq)) { struct pt_regs *regs = (struct pt_regs *)(prev_fp + 4); /* passed exception point, return this if unwinding is sucessful */ current_stk.pc = regs->ARM_pc; current_stk.lr = regs->ARM_lr; current_stk.fp = regs->ARM_fp; current_stk.sp = regs->ARM_sp; } } if (bt->nr_entries < AEE_NR_FRAME) { bt->entries[bt->nr_entries].pc = ULONG_MAX; bt->entries[bt->nr_entries].pc_symbol[0] = '\0'; bt->entries[bt->nr_entries].lr = ULONG_MAX; bt->entries[bt->nr_entries++].lr_symbol[0] = '\0'; } return 0; }
inline void aee_print_bt(struct pt_regs *regs) { int i; unsigned long high, bottom, fp; struct stackframe cur_frame; struct pt_regs *exp_regs; bottom = regs->ARM_sp; if (!virt_addr_valid(bottom)) { aee_nested_printf("invalid sp[%x]\n", regs); return; } high = ALIGN(bottom, THREAD_SIZE); cur_frame.lr = regs->ARM_lr; cur_frame.fp = regs->ARM_fp; cur_frame.pc = regs->ARM_pc; for (i = 0; i < AEE_MAX_EXCP_FRAME; i++) { fp = cur_frame.fp; if ((fp < (bottom + 12)) || ((fp + 4) >= (high + 8192))) { if (fp != 0) aee_nested_printf("fp(%x)", fp); break; } cur_frame.fp = *(unsigned long *)(fp - 12); cur_frame.lr = *(unsigned long *)(fp - 4); cur_frame.pc = *(unsigned long *)fp; if (! ((cur_frame.lr >= (PAGE_OFFSET + THREAD_SIZE)) && virt_addr_valid(cur_frame.lr))) break; if (in_exception_text(cur_frame.pc)) { exp_regs = (struct pt_regs *)(fp + 4); cur_frame.lr = exp_regs->ARM_pc; } aee_nested_printf("%08lx, ", cur_frame.lr); } aee_nested_printf("\n"); return; }
static int save_trace(struct stackframe *frame, void *d) { struct stack_trace_data *data = d; struct stack_trace *trace = data->trace; struct pt_regs *regs; unsigned long addr = frame->pc; if (data->no_sched_functions && in_sched_functions(addr)) return 0; if (data->skip) { data->skip--; return 0; } trace->entries[trace->nr_entries++] = addr; if (trace->nr_entries >= trace->max_entries) return 1; /* * in_exception_text() is designed to test if the PC is one of * the functions which has an exception stack above it, but * unfortunately what is in frame->pc is the return LR value, * not the saved PC value. So, we need to track the previous * frame PC value when doing this. */ addr = data->last_pc; data->last_pc = frame->pc; if (!in_exception_text(addr)) return 0; regs = (struct pt_regs *)frame->sp; trace->entries[trace->nr_entries++] = regs->ARM_pc; return trace->nr_entries >= trace->max_entries; }
void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) { struct stackframe frame; unsigned long irq_stack_ptr; int skip; pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk); if (!tsk) tsk = current; if (!try_get_task_stack(tsk)) return; /* * Switching between stacks is valid when tracing current and in * non-preemptible context. */ if (tsk == current && !preemptible()) irq_stack_ptr = IRQ_STACK_PTR(smp_processor_id()); else irq_stack_ptr = 0; if (tsk == current) { frame.fp = (unsigned long)__builtin_frame_address(0); frame.sp = current_stack_pointer; frame.pc = (unsigned long)dump_backtrace; } else { /* * task blocked in __switch_to */ frame.fp = thread_saved_fp(tsk); frame.sp = thread_saved_sp(tsk); frame.pc = thread_saved_pc(tsk); } #ifdef CONFIG_FUNCTION_GRAPH_TRACER frame.graph = tsk->curr_ret_stack; #endif skip = !!regs; printk("Call trace:\n"); while (1) { unsigned long where = frame.pc; unsigned long stack; int ret; /* skip until specified stack frame */ if (!skip) { dump_backtrace_entry(where); } else if (frame.fp == regs->regs[29]) { skip = 0; /* * Mostly, this is the case where this function is * called in panic/abort. As exception handler's * stack frame does not contain the corresponding pc * at which an exception has taken place, use regs->pc * instead. */ dump_backtrace_entry(regs->pc); } ret = unwind_frame(tsk, &frame); if (ret < 0) break; stack = frame.sp; if (in_exception_text(where)) { /* * If we switched to the irq_stack before calling this * exception handler, then the pt_regs will be on the * task stack. The easiest way to tell is if the large * pt_regs would overlap with the end of the irq_stack. */ if (stack < irq_stack_ptr && (stack + sizeof(struct pt_regs)) > irq_stack_ptr) stack = IRQ_STACK_TO_TASK_STACK(irq_stack_ptr); dump_mem("", "Exception stack", stack, stack + sizeof(struct pt_regs)); } } put_task_stack(tsk); }
int __kprobes arch_prepare_kprobe(struct kprobe *p) { kprobe_opcode_t insn; kprobe_opcode_t tmp_insn[MAX_INSN_SIZE]; unsigned long addr = (unsigned long)p->addr; bool thumb; kprobe_decode_insn_t *decode_insn; const union decode_action *actions; int is; const struct decode_checker **checkers; if (in_exception_text(addr)) return -EINVAL; #ifdef CONFIG_THUMB2_KERNEL thumb = true; addr &= ~1; /* Bit 0 would normally be set to indicate Thumb code */ insn = __mem_to_opcode_thumb16(((u16 *)addr)[0]); if (is_wide_instruction(insn)) { u16 inst2 = __mem_to_opcode_thumb16(((u16 *)addr)[1]); insn = __opcode_thumb32_compose(insn, inst2); decode_insn = thumb32_probes_decode_insn; actions = kprobes_t32_actions; checkers = kprobes_t32_checkers; } else { decode_insn = thumb16_probes_decode_insn; actions = kprobes_t16_actions; checkers = kprobes_t16_checkers; } #else /* !CONFIG_THUMB2_KERNEL */ thumb = false; if (addr & 0x3) return -EINVAL; insn = __mem_to_opcode_arm(*p->addr); decode_insn = arm_probes_decode_insn; actions = kprobes_arm_actions; checkers = kprobes_arm_checkers; #endif p->opcode = insn; p->ainsn.insn = tmp_insn; switch ((*decode_insn)(insn, &p->ainsn, true, actions, checkers)) { case INSN_REJECTED: /* not supported */ return -EINVAL; case INSN_GOOD: /* instruction uses slot */ p->ainsn.insn = get_insn_slot(); if (!p->ainsn.insn) return -ENOMEM; for (is = 0; is < MAX_INSN_SIZE; ++is) p->ainsn.insn[is] = tmp_insn[is]; flush_insns(p->ainsn.insn, sizeof(p->ainsn.insn[0]) * MAX_INSN_SIZE); p->ainsn.insn_fn = (probes_insn_fn_t *) ((uintptr_t)p->ainsn.insn | thumb); break; case INSN_GOOD_NO_SLOT: /* instruction doesn't need insn slot */ p->ainsn.insn = NULL; break; } /* * Never instrument insn like 'str r0, [sp, +/-r1]'. Also, insn likes * 'str r0, [sp, #-68]' should also be prohibited. * See __und_svc. */ if ((p->ainsn.stack_space < 0) || (p->ainsn.stack_space > MAX_STACK_SIZE)) return -EINVAL; return 0; }