Advanced kernel stack backtrace v2
PATCH 1/1 Advanced kernel stack backtrace v2
Advanced MIPS stack backtrace code
Disadvantages:
- Larger than the default code
- Requires KALLSYMS, making the kernel even larger
- Only partial support for 64-bit systems and what there is is untested.
Advantages:
- Produces backtraces including nested exceptions and interrupts.
- Uses __get_user() to handle stack and instructions access, making it much less vulnerable to stack corruption issues.
- Smart enough to handle instructions in branch delay slots.
- Detects where a register is used as a stack frame pointer
- Works in cases where a function returns from the middle
- Has been in use for well over a year
Changes for v2:
- Changed to using ARRAY_SIZE.
- Passes with no checkpatch.pl errors. There is one warning, which comes from a printk with no priority. This replaces a printk that also had no priority and the case could be made that correct priority to use is the default priority.
- Additional work to support 64-bit systems has been done, and warnings added to code that still needs work.
---
arch/mips/Kconfig.debug +0 -0
@@ -73,6 +73,17 @@ config RUNTIME_DEBUG
include/asm-mips/debug.h for debuging macros. If unsure, say N.
+config MIPS_ADVANCED_BACKTRACE
+ bool "More sophisticated backtrace code"
+ default n
+ depends on KALLSYMS
+ help
+ Use backtrace code that more completely handles the various + complexities of the MIPS processors, including branch delay + slots. This is substantially larger than the standard backtrace + code. Using this also prints the frame pointer for each function + in the call stack. + config MIPS_UNCACHED bool "Run uncached" depends on DEBUG_KERNEL && !SMP && !SGI_IP27
arch/mips/kernel/Makefile +0 -0
@@ -79,6 +79,7 @@ obj-$(CONFIG_I8253) += i8253.o
obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
+obj-$(CONFIG_MIPS_ADVANCED_BACKTRACE) += backtrace/
CFLAGS_cpu-bugs64.o = $(shell if $(CC) $(KBUILD_CFLAGS) -Wa,-mdaddi -c -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-DHAVE_AS_SET_DADDI"; fi)
arch/mips/kernel/backtrace/Makefile new file +4 -0
@@ -0,0 +1,4 @@ +# Makefile for Linux/MIPS advanced backtrace code + +obj-y += kernel-backtrace.o kernel-backtrace-symbols.o \ + simple-backtrace.o thread-backtrace.o
arch/mips/kernel/backtrace/kernel-backtrace-symbols.c new file +42 -0
42 lines not on this page · raw message
arch/mips/kernel/backtrace/kernel-backtrace.c new file +976 -0
976 lines not on this page · raw message
arch/mips/kernel/backtrace/simple-backtrace.c new file +1242 -0
1,242 lines not on this page · raw message
arch/mips/kernel/backtrace/thread-backtrace.c new file +384 -0
384 lines not on this page · raw message
arch/mips/kernel/entry.S +0 -0
@@ -40,6 +40,7 @@ FEXPORT(__ret_from_irq)
andi t0, t0, KU_USER beqz t0, resume_kernel
+ .globl resume_userspaceresume_userspace:
local_irq_disable # make sure we dont miss an
# interrupt setting need_resched
@@ -50,11 +51,13 @@ resume_userspace:
j restore_all
#ifdef CONFIG_PREEMPT
+ .globl resume_kernelresume_kernel:
local_irq_disable lw t0, TI_PRE_COUNT($28) bnez t0, restore_all -need_resched: + .globl _need_resched +_need_resched: LONG_L t0, TI_FLAGS($28) andi t1, t0, _TIF_NEED_RESCHED beqz t1, restore_all @@ -62,7 +65,7 @@ need_resched: andi t0, 1 beqz t0, restore_all jal preempt_schedule_irq
- b need_resched
+ b _need_resched
#endif
FEXPORT(ret_from_fork)
@@ -141,9 +144,11 @@ FEXPORT(restore_partial) # restore part
RESTORE_SP_AND_RET .set at
+ .globl work_pendingwork_pending:
andi t0, a2, _TIF_NEED_RESCHED # a2 is preloaded with TI_FLAGS
beqz t0, work_notifysig
+ .globl work_reschedwork_resched:
jal schedule
@@ -157,6 +162,7 @@ work_resched:
andi t0, a2, _TIF_NEED_RESCHED bnez t0, work_resched
+ .globl work_notifysigwork_notifysig: # deal with pending signals and
# notify-resume requests move a0, sp @@ -166,6 +172,7 @@ work_notifysig: # deal with pending s
FEXPORT(syscall_exit_work_partial)
SAVE_STATIC
+ .global syscall_exit_worksyscall_exit_work:
li t0, _TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT
and t0, a2 # a2 is preloaded with TI_FLAGS
arch/mips/kernel/scall32-o32.S +0 -0
@@ -54,6 +54,7 @@ NESTED(handle_sys, PT_SIZE, sp)
sw a3, PT_R26(sp) # save a3 for syscall restarting
bgez t3, stackargs
+ .globl stack_donestack_done:
lw t0, TI_FLAGS($28) # syscall tracing enabled? li t1, _TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT @@ -72,6 +73,7 @@ stack_done: # restarting 1: sw v0, PT_R2(sp) # result
+ .globl o32_syscall_exito32_syscall_exit:
local_irq_disable # make sure need_resched and
# signals dont change between
@@ -83,11 +85,13 @@ o32_syscall_exit:
j restore_partial
+ .globl o32_syscall_exit_worko32_syscall_exit_work:
j syscall_exit_work_partial
/* ------------------------------------------------------------------------ */
+ .globl syscall_trace_entrysyscall_trace_entry:
SAVE_STATIC move s0, t2
@@ -122,6 +126,7 @@ syscall_trace_entry:
* stack arguments from the user stack to the kernel stack.
* This Sucks (TM).
*/
+ .globl stackargs
stackargs:
lw t0, PT_R29(sp) # get old user stack pointer
@@ -132,7 +137,7 @@ stackargs:
lw t5, TI_ADDR_LIMIT($28) addu t4, t0, 32 and t5, t4
- bltz t5, bad_stack # -> sp is bad
+ bltz t5, _bad_stack # -> sp is bad
/* Ok, copy the args from the luser stack to the kernel stack. * t3 is the precomputed number of instruction bytes needed to @@ -162,17 +167,18 @@ stackargs: .set pop .section __ex_table,"a"
- PTR 1b,bad_stack
- PTR 2b,bad_stack
- PTR 3b,bad_stack
- PTR 4b,bad_stack
+ PTR 1b,_bad_stack
+ PTR 2b,_bad_stack
+ PTR 3b,_bad_stack
+ PTR 4b,_bad_stack
.previous /* * The stackpointer for a call with more than 4 arguments is bad. * We probably should handle this case a bit more drastic. */ -bad_stack: + .globl _bad_stack +_bad_stack: negu v0 # error sw v0, PT_R0(sp) sw v0, PT_R2(sp) @@ -183,6 +189,7 @@ bad_stack: /* * The system call does not exist in this kernel */ + .globl illegal_syscall illegal_syscall: li v0, -ENOSYS # error sw v0, PT_R2(sp)
@@ -213,8 +220,8 @@ illegal_syscall:
#endif
.section __ex_table,"a"
- PTR 1b, bad_stack
- PTR 2b, bad_stack
+ PTR 1b, _bad_stack
+ PTR 2b, _bad_stack
.previous
#else sw a1, 16(sp)
@@ -246,13 +253,16 @@ illegal_syscall:
j o32_syscall_exit # continue like a normal syscall
+ .globl no_memno_mem: li v0, -ENOMEM
jr ra
+ .globl bad_addressbad_address:
li v0, -EFAULT jr ra
+ .globl bad_alignmentbad_alignment:
li v0, -EINVAL jr ra @@ -305,6 +315,7 @@ bad_alignment: jr t2 /* Unreached */
+ .globl einval
einval: li v0, -EINVAL
jr ra
END(sys_syscall)
arch/mips/kernel/signal.c +0 -0
@@ -30,51 +30,11 @@
#include <asm/ucontext.h>
#include <asm/cpu-features.h>
#include <asm/war.h>
+#include <asm/sigframe.h>
#include "signal-common.h"
/*
- * Horribly complicated - with the bloody RM9000 workarounds enabled
- * the signal trampolines is moving to the end of the structure so we can
- * increase the alignment without breaking software compatibility.
- */
-#if ICACHE_REFILLS_WORKAROUND_WAR == 0
-
-struct sigframe {
- u32 sf_ass[4]; /* argument save space for o32 */
- u32 sf_code[2]; /* signal trampoline */
- struct sigcontext sf_sc;
- sigset_t sf_mask;
-};
-
-struct rt_sigframe {
- u32 rs_ass[4]; /* argument save space for o32 */
- u32 rs_code[2]; /* signal trampoline */
- struct siginfo rs_info;
- struct ucontext rs_uc;
-};
-
-#else
-
-struct sigframe {
- u32 sf_ass[4]; /* argument save space for o32 */
- u32 sf_pad[2];
- struct sigcontext sf_sc; /* hw context */
- sigset_t sf_mask;
- u32 sf_code[8] ____cacheline_aligned; /* signal trampoline */
-};
-
-struct rt_sigframe {
- u32 rs_ass[4]; /* argument save space for o32 */
- u32 rs_pad[2];
- struct siginfo rs_info;
- struct ucontext rs_uc;
- u32 rs_code[8] ____cacheline_aligned; /* signal trampoline */
-};
-
-#endif
-
-/*
* Helper routines
*/
static int protected_save_fp_context(struct sigcontext __user *sc)
arch/mips/kernel/signal32.c +0 -0
@@ -32,16 +32,10 @@
#include <asm/system.h>
#include <asm/fpu.h>
#include <asm/war.h>
+#include <asm/sigframe.h>
#include "signal-common.h"
-/*
- * Including <asm/unistd.h> would give use the 64-bit syscall numbers ...
- */
-#define __NR_O32_sigreturn 4119
-#define __NR_O32_rt_sigreturn 4193
-#define __NR_O32_restart_syscall 4253
-
/* 32-bit compatibility types */
typedef unsigned int __sighandler32_t;
arch/mips/kernel/traps.c +0 -0
@@ -42,6 +42,7 @@
#include <asm/mmu_context.h>
#include <asm/types.h>
#include <asm/stacktrace.h>
+#include <asm/kernel-backtrace.h>
extern asmlinkage void handle_int(void);
extern asmlinkage void handle_tlbm(void);
@@ -105,21 +106,83 @@ static int __init set_raw_show_trace(cha __setup("raw_show_trace", set_raw_show_trace); #endif
-static void show_backtrace(struct task_struct *task, const struct pt_regs *regs)
+#ifdef CONFIG_MIPS_ADVANCED_BACKTRACE
+/*
+ * print_bt_frame - Print one backtrace frame
+ * @arg: Pointer to the frame count
+ * @bt: Pointer to the kernel backtrace structure &kernel_bt.
+ * Returns: Zero if the backtrace is to continue, otherwise
+ * KERNEL_BACKTRACE_END to indicate backtrace termination.
+ */
+static int print_bt_frame(void *arg, struct kernel_bt *bt)
+{
+ int result = 0; /* Assume a good outcome */
+ unsigned *count = (unsigned *) arg;
+ unsigned long pc, fp;
+ static const int max_stack_frames = 50;
+
+ pc = bt->tbt.sbt.pc;
+ fp = bt->tbt.sbt.gprs [bt->tbt.sbt.framepointer];
+ print_ip_sym(pc);
+
+ /* Don't print more than the maximum number of stack frames. */
+ *count = *count + 1;
+
+ if (*count >= max_stack_frames) {
+ printk(KERN_WARNING "Exceeded maximum number of frames (%u)\n",
+ max_stack_frames);
+ result = KERNEL_BACKTRACE_END;
+ }
+
+ return result;
+}
+
+/**
+ * show_unwinding_backtrace - Print the backtrace by unwinding stack frames
+ * one at a time.
+ * @task: Pointer to the task structure for the task to be unwound
+ * @regs: Pointer to &pt_regs structure with the initial registers
+ */
+static void show_unwinding_backtrace(struct task_struct *task,
+ const struct pt_regs *regs)
+{
+ int frame_count = 0;
+ int rc;
+
+ rc = kernel_backtrace_pt_regs(regs, print_bt_frame, &frame_count);
+
+ if (rc != 0)
+ printk(KERN_WARNING "Backtrace terminated with error %d\n",
+ rc);
+}
+#else
+static void show_unwinding_backtrace(struct task_struct *task,
+ const struct pt_regs *regs)
{
unsigned long sp = regs->regs[29];
unsigned long ra = regs->regs[31];
unsigned long pc = regs->cp0_epc;- if (raw_show_trace || !__kernel_text_address(pc)) {
- show_raw_backtrace(sp);
- return;
- }
- printk("Call Trace:\n");
do {
print_ip_sym(pc);
pc = unwind_stack(task, &sp, pc, &ra);
} while (pc);+}
+#endif
+
+static void show_backtrace(struct task_struct *task, const struct pt_regs *regs) +{
+ unsigned long sp = regs->regs[29];
+ unsigned long pc = regs->cp0_epc;
+
+ if (raw_show_trace
+ || !__kernel_text_address(pc)
+ ) {
+ show_raw_backtrace(sp);
+ return;
+ }
+ printk("Symbolic Call Trace:\n");
+ show_unwinding_backtrace(task, regs);
printk("\n");
}include/asm-mips/kernel-backtrace-symbols.h new file +155 -0
155 lines not on this page · raw message
include/asm-mips/kernel-backtrace.h new file +81 -0
81 lines not on this page · raw message
include/asm-mips/ptrace.h +0 -0
@@ -23,6 +23,18 @@
#define DSP_CONTROL 77
#define ACX 78
+/* Register numbers */
+enum reg_num {
+ REG_ZERO, REG_AT, REG_V0, REG_V1,
+ REG_A0, REG_A1, REG_A2, REG_A3,
+ REG_T0, REG_T1, REG_T2, REG_T3,
+ REG_T4, REG_T5, REG_T6, REG_T7,
+ REG_S0, REG_S1, REG_S2, REG_S3,
+ REG_S4, REG_S5, REG_S6, REG_S7,
+ REG_T8, REG_T9, REG_K0, REG_K1,
+ REG_GP, REG_SP, REG_S8, REG_RA,
+ REG_ALL /* This last one is the number of GPRs */
+};
/*
* This struct defines the way the registers are stored on the stack during a
* system call/exception. As usual the registers k0/k1 aren't being saved.
@@ -34,7 +46,7 @@ struct pt_regs {
#endif
/* Saved main processor registers. */
- unsigned long regs[32];
+ unsigned long regs[REG_ALL];
/* Saved special registers. */ unsigned long cp0_status;
include/asm-mips/sigframe.h new file +73 -0
73 lines not on this page · raw message
include/asm-mips/simple-backtrace.h new file +331 -0
42 lines not on this page · raw message
include/asm-mips/stacktrace.h +0 -0
@@ -30,18 +30,49 @@ static __always_inline void prepare_fram
The rest of this diff is not shown. View the raw message.
".set noat\n\t" #ifdef CONFIG_64BIT "1: dla $1, 1b\n\t"
- "sd $1, %0\n\t"
- "sd $29, %1\n\t"
- "sd $31, %2\n\t"
+ "sd $1, %[epc]\n\t"
+ "sd $16, %[s0]\n\t"
+ "sd $17, %[s1]\n\t"
+ "sd $18, %[s2]\n\t"
+ "sd $19, %[s3]\n\t"
+ "sd $20, %[s4]\n\t"
+ "sd $21, %[s5]\n\t"
+ "sd $22, %[s6]\n\t"
+ "sd $23, %[s7]\n\t"
+ "sd $28, %[gp]\n\t"
+ "sd $29, %[sp]\n\t"
+ "sd $30, %[fp]\n\t"
+ "sd $31, %[ra]\n\t"
#else "1: la $1, 1b\n\t"
- "sw $1, %0\n\t"
- "sw $29, %1\n\t"
- "sw $31, %2\n\t"
+ "sw $1, %[epc]\n\t"
+ "sw $16, %[s0]\n\t"
+ "sw $17, %[s1]\n\t"
+ "sw $18, %[s2]\n\t"
+ "sw $19, %[s3]\n\t"
+ "sw $20, %[s4]\n\t"
+ "sw $21, %[s5]\n\t"
+ "sw $22, %[s6]\n\t"
+ "sw $23, %[s7]\n\t"
+ "sw $28, %[gp]\n\t"
+ "sw $29, %[sp]\n\t"
+ "sw $30, %[fp]\n\t"
+ "sw $31, %[ra]\n\t"
#endif ".set pop\n\t" - : "=m" (regs->cp0_epc), - "=m" (regs->regs[29]), "=m" (regs->regs[31]) + : [epc] "=m" (regs->cp0_epc), + [s0] "=m" (regs->regs[16]), + [s1] "=m" (regs->regs[17]), + [s2] "=m" (regs->regs[18]), + [s3] "=m" (regs->regs[19]), + [s4] "=m" (regs->regs[20]), + [s5] "=m" (regs->regs[21]), + [s6] "=m" (regs->regs[22]), + [s7] "=m" (regs->regs[23]), + [gp] "=m" (regs->regs[28]), + [sp] "=m" (regs->regs[29]), + [fp] "=m" (regs->regs[30]), + [ra] "=m" (regs->regs[31]) : : "memory"); }
include/asm-mips/thread-backtrace.h new file +242 -0
242 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Part of the diff is not shown here. View the raw message.