diff --git a/sys/i386/i386/db_trace.c b/sys/i386/i386/db_trace.c index 99f941b4e301..ea2c20884222 100644 --- a/sys/i386/i386/db_trace.c +++ b/sys/i386/i386/db_trace.c @@ -401,9 +401,33 @@ db_backtrace(struct thread *td, struct trapframe *tf, struct i386_frame *frame, int *argp; db_expr_t offset; c_db_sym_t sym; - int narg, quit; + int instr, narg, quit; boolean_t first; + /* + * If an indirect call via an invalid pointer caused a trap, + * %pc contains the invalid address while the return address + * of the unlucky caller has been saved by CPU on the stack + * just before the trap frame. In this case, try to recover + * the caller's address so that the first frame is assigned + * to the right spot in the right function, for that is where + * the failure actually happened. + * + * This trick depends on the fault address stashed in tf_err + * by trap_fatal() before entering KDB. + */ + if (kdb_frame && pc == kdb_frame->tf_err) { + /* + * Find where the trap frame actually ends. + * It won't contain tf_esp or tf_ss unless crossing rings. + */ + if (ISPL(kdb_frame->tf_cs)) + instr = (int)(kdb_frame + 1); + else + instr = (int)&kdb_frame->tf_esp; + pc = db_get_value(instr, 4, FALSE); + } + if (count == -1) count = 1024; @@ -428,8 +452,6 @@ db_backtrace(struct thread *td, struct trapframe *tf, struct i386_frame *frame, actframe = frame; if (first) { if (tf != NULL) { - int instr; - instr = db_get_value(pc, 4, FALSE); if ((instr & 0xffffff) == 0x00e58955) { /* pushl %ebp; movl %esp, %ebp */ diff --git a/sys/i386/i386/trap.c b/sys/i386/i386/trap.c index 6e1aec2dbe6b..21b0773faf41 100644 --- a/sys/i386/i386/trap.c +++ b/sys/i386/i386/trap.c @@ -858,10 +858,13 @@ trap_fatal(frame, eva) if (debugger_on_panic || kdb_active) { register_t eflags; eflags = intr_disable(); + frame->tf_err = eva; /* smuggle fault address to ddb */ if (kdb_trap(type, 0, frame)) { + frame->tf_err = code; /* restore error code */ intr_restore(eflags); return; } + frame->tf_err = code; /* restore error code */ intr_restore(eflags); } #endif