Skip to contentSkip to conversation

Advanced kernel stack backtrace v2

PATCH New David VomLehn 1 participant 1 message May 16, 2008 MIPS

lore
Share & tools
lore
#0 lore raw

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
Index: linux-2.6.25.1/arch/mips/Kconfig.debug
===================================================================
@@ -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
Index: linux-2.6.25.1/arch/mips/kernel/Makefile
===================================================================
@@ -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
Index: linux-2.6.25.1/arch/mips/kernel/backtrace/Makefile
===================================================================
@@ -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
Index: linux-2.6.25.1/arch/mips/kernel/entry.S
===================================================================
@@ -40,6 +40,7 @@ FEXPORT(__ret_from_irq)
	andi	t0, t0, KU_USER
	beqz	t0, resume_kernel

+ .globl resume_userspace
resume_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_kernel
resume_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_pending
work_pending:
andi t0, a2, _TIF_NEED_RESCHED # a2 is preloaded with TI_FLAGS
beqz t0, work_notifysig
+ .globl work_resched
work_resched:
jal schedule

@@ -157,6 +162,7 @@ work_resched:

	andi	t0, a2, _TIF_NEED_RESCHED
	bnez	t0, work_resched

+ .globl work_notifysig
work_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_work
syscall_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
Index: linux-2.6.25.1/arch/mips/kernel/scall32-o32.S
===================================================================
@@ -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_done
stack_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_exit
o32_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_work
o32_syscall_exit_work:
j syscall_exit_work_partial

/* ------------------------------------------------------------------------ */

+ .globl syscall_trace_entry
syscall_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_mem
no_mem: li v0, -ENOMEM
jr ra

+ .globl bad_address
bad_address:

	li	v0, -EFAULT
	jr	ra

+ .globl bad_alignment
bad_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
Index: linux-2.6.25.1/arch/mips/kernel/signal.c
===================================================================
@@ -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
Index: linux-2.6.25.1/arch/mips/kernel/signal32.c
===================================================================
@@ -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
Index: linux-2.6.25.1/arch/mips/kernel/traps.c
===================================================================
@@ -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
Index: linux-2.6.25.1/include/asm-mips/ptrace.h
===================================================================
@@ -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
Index: linux-2.6.25.1/include/asm-mips/stacktrace.h
===================================================================
@@ -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.