summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Thibault <samuel.thibault@ens-lyon.org>2021-12-29 14:28:49 +0100
committerSamuel Thibault <samuel.thibault@ens-lyon.org>2021-12-29 14:28:49 +0100
commit48e5b524be945e3eecaf88583a5aa50a5f5ca50a (patch)
tree7b3c48d5b391d245041b0122bb25c92f283161c9
parenta7a099032d9f68d4338b021a6323689feacbc47a (diff)
ddb: Unwind over RET
When we don't have a frame but PC points to a RET instruction (e.g. in the userland system call assembly code), we can trivially unwind it.
-rw-r--r--i386/i386/db_trace.c35
-rw-r--r--i386/i386/db_trace.h1
2 files changed, 30 insertions, 6 deletions
diff --git a/i386/i386/db_trace.c b/i386/i386/db_trace.c
index 6c59864a..2f47c325 100644
--- a/i386/i386/db_trace.c
+++ b/i386/i386/db_trace.c
@@ -333,7 +333,7 @@ db_stack_trace_cmd(
{
boolean_t trace_thread = FALSE;
struct i386_frame *frame;
- db_addr_t callpc;
+ db_addr_t callpc, sp;
int flags = 0;
thread_t th;
@@ -351,6 +351,7 @@ db_stack_trace_cmd(
if (!have_addr && !trace_thread) {
frame = (struct i386_frame *)ddb_regs.ebp;
+ sp = (db_addr_t)ddb_regs.uesp;
callpc = (db_addr_t)ddb_regs.eip;
th = current_thread();
} else if (trace_thread) {
@@ -369,6 +370,7 @@ db_stack_trace_cmd(
}
if (th == current_thread()) {
frame = (struct i386_frame *)ddb_regs.ebp;
+ sp = (db_addr_t)ddb_regs.uesp;
callpc = (db_addr_t)ddb_regs.eip;
} else {
if (th->pcb == 0) {
@@ -385,23 +387,26 @@ db_stack_trace_cmd(
db_printf("\n");
frame = (struct i386_frame *) (iss->ebp);
+ sp = (db_addr_t) (iss->uesp);
callpc = (db_addr_t) (iss->eip);
} else {
struct i386_kernel_state *iks;
iks = STACK_IKS(th->kernel_stack);
frame = (struct i386_frame *) (iks->k_ebp);
+ sp = (db_addr_t) (iks->k_esp);
callpc = (db_addr_t) (iks->k_eip);
}
}
} else {
frame = (struct i386_frame *)addr;
+ sp = (db_addr_t)addr;
th = (db_default_thread)? db_default_thread: current_thread();
callpc = (db_addr_t)db_get_task_value((long)&frame->f_retaddr, sizeof(long),
FALSE,
(th == THREAD_NULL) ? TASK_NULL : th->task);
}
- db_i386_stack_trace( th, frame, callpc, count, flags );
+ db_i386_stack_trace( th, frame, sp, callpc, count, flags );
}
@@ -409,6 +414,7 @@ void
db_i386_stack_trace(
const thread_t th,
struct i386_frame *frame,
+ db_addr_t sp,
db_addr_t callpc,
db_expr_t count,
int flags)
@@ -489,9 +495,23 @@ db_i386_stack_trace(
db_printf("%s(", name);
if (!frame) {
- db_printf(")\n");
+ db_printf(")\n");
+ }
+
+ if (sp) {
+ unsigned char inst = db_get_task_value(callpc, sizeof(char), FALSE, task);
+ if (inst == 0xc3) {
+ /* RET, unwind this directly */
+ callpc = db_get_task_value(sp, sizeof(callpc), FALSE, task);
+ sp += sizeof(callpc);
+ continue;
+ }
+ }
+
+ if (!frame) {
break;
}
+
argp = &frame->f_arg0;
while (narg > 0) {
db_printf("%x", db_get_task_value((long)argp,sizeof(long),FALSE,task));
@@ -595,7 +615,7 @@ void db_trace_cproc(
jmp_buf_t db_jmpbuf;
jmp_buf_t *prev = db_recover;
task_t task;
- db_addr_t pc, fp;
+ db_addr_t pc, fp, sp;
task = (thread == THREAD_NULL)? TASK_NULL: thread->task;
@@ -619,6 +639,7 @@ void db_trace_cproc(
if ((s != 0) && (c != 0)) {
pc = db_get_task_value(c + db_cprocsw_pc_offset, 4, FALSE, task);
fp = c + db_cprocsw_framep_offset;
+ sp = 0; // TODO
} else {
db_addr_t sb;
vm_size_t ss;
@@ -633,12 +654,14 @@ void db_trace_cproc(
if (thread != THREAD_NULL) {
pc = thread->pcb->iss.eip;
fp = thread->pcb->iss.ebp;
- } else
+ sp = thread->pcb->iss.uesp;
+ } else {
fp = -1;
+ }
}
if (fp != -1)
- db_i386_stack_trace(thread, (struct i386_frame*)fp, pc,
+ db_i386_stack_trace(thread, (struct i386_frame*)fp, sp, pc,
-1, F_USER_TRACE);
}
diff --git a/i386/i386/db_trace.h b/i386/i386/db_trace.h
index 604654c5..4684f57e 100644
--- a/i386/i386/db_trace.h
+++ b/i386/i386/db_trace.h
@@ -25,6 +25,7 @@ void
db_i386_stack_trace(
thread_t th,
struct i386_frame *frame,
+ db_addr_t sp,
db_addr_t callpc,
db_expr_t count,
int flags);