#include <linux/module.h>
 #include <linux/sched.h>
 #include <asm/sysrq.h>
+#include <os.h>
 
 struct stack_frame {
        struct stack_frame *next_frame;
 /*Stolen from arch/i386/kernel/traps.c */
 static const int kstack_depth_to_print = 24;
 
-static unsigned long get_frame_pointer(struct task_struct *task)
+static unsigned long get_frame_pointer(struct task_struct *task,
+                                      struct pt_regs *segv_regs)
 {
        if (!task || task == current)
-               return current_bp();
+               return segv_regs ? PT_REGS_BP(segv_regs) : current_bp();
        else
                return KSTK_EBP(task);
 }
 
+static unsigned long *get_stack_pointer(struct task_struct *task,
+                                       struct pt_regs *segv_regs)
+{
+       if (!task || task == current)
+               return segv_regs ? (unsigned long *)PT_REGS_SP(segv_regs) : current_sp();
+       else
+               return (unsigned long *)KSTK_ESP(task);
+}
+
 void show_stack(struct task_struct *task, unsigned long *stack)
 {
        unsigned long *sp = stack, bp = 0;
+       struct pt_regs *segv_regs = current->thread.segv_regs;
        int i;
 
+       if (!segv_regs && os_is_signal_stack()) {
+               printk(KERN_ERR "Received SIGSEGV in SIGSEGV handler,"
+                               " aborting stack trace!\n");
+               return;
+       }
+
 #ifdef CONFIG_FRAME_POINTER
-       bp = get_frame_pointer(task);
+       bp = get_frame_pointer(task, segv_regs);
 #endif
 
-       if (!stack) {
-               if (!task || task == current)
-                       sp = current_sp();
-               else
-                       sp = (unsigned long *)KSTK_ESP(task);
-       }
+       if (!stack)
+               sp = get_stack_pointer(task, segv_regs);
 
        printk(KERN_INFO "Stack:\n");
        stack = sp;
 
        int is_write = FAULT_WRITE(fi);
        unsigned long address = FAULT_ADDRESS(fi);
 
+       if (regs)
+               current->thread.segv_regs = container_of(regs, struct pt_regs, regs);
+
        if (!is_user && (address >= start_vm) && (address < end_vm)) {
                flush_tlb_kernel_vm();
-               return 0;
+               goto out;
        }
        else if (current->mm == NULL) {
                show_regs(container_of(regs, struct pt_regs, regs));
 
        catcher = current->thread.fault_catcher;
        if (!err)
-               return 0;
+               goto out;
        else if (catcher != NULL) {
                current->thread.fault_addr = (void *) address;
                UML_LONGJMP(catcher, 1);
        else if (current->thread.fault_addr != NULL)
                panic("fault_addr set but no fault catcher");
        else if (!is_user && arch_fixup(ip, regs))
-               return 0;
+               goto out;
 
        if (!is_user) {
                show_regs(container_of(regs, struct pt_regs, regs));
                current->thread.arch.faultinfo = fi;
                force_sig_info(SIGSEGV, &si, current);
        }
+
+out:
+       if (regs)
+               current->thread.segv_regs = NULL;
+
        return 0;
 }