From bd5f43b119813c92d097d7f8c88609985b82c263 Mon Sep 17 00:00:00 2001 From: Susumu Komae Date: Thu, 29 Jan 2015 15:48:05 +0900 Subject: [PATCH] support PTRACE_SINGLESTEP. support debug/int3 exception. --- arch/x86/kernel/cpu.c | 48 +++++++++++++++++++++++++++++ arch/x86/kernel/include/registers.h | 8 +++++ arch/x86/kernel/interrupt.S | 22 +++++++++++++ arch/x86/kernel/syscall.c | 5 +++ kernel/syscall.c | 15 +++++++-- 5 files changed, 95 insertions(+), 3 deletions(-) diff --git a/arch/x86/kernel/cpu.c b/arch/x86/kernel/cpu.c index ca2b65b6..5e248734 100644 --- a/arch/x86/kernel/cpu.c +++ b/arch/x86/kernel/cpu.c @@ -107,6 +107,7 @@ void reload_idt(void) static struct list_head handlers[256 - 32]; extern char page_fault[], general_protection_exception[]; +extern char debug_exception[], int3_exception[]; static void init_idt(void) { @@ -125,6 +126,9 @@ static void init_idt(void) set_idt_entry(13, (unsigned long)general_protection_exception); set_idt_entry(14, (unsigned long)page_fault); + set_idt_entry_trap_gate(1, (unsigned long)debug_exception); + set_idt_entry_trap_gate(3, (unsigned long)int3_exception); + reload_idt(); } @@ -467,6 +471,50 @@ void gpe_handler(struct x86_regs *regs) // panic("GPF"); } +void debug_handler(struct x86_regs *regs) +{ + unsigned long db6; + int si_code = 0; + struct siginfo info; + +#ifdef DEBUG_PRINT_CPU + kprintf("debug exception (err: %lx, %lx:%lx)\n", + regs->error, regs->cs, regs->rip); + arch_show_interrupt_context(regs); +#endif + + asm("mov %%db6, %0" :"=r" (db6)); + if (db6 & DB6_BS) { + regs->rflags &= ~RFLAGS_TF; + si_code = TRAP_TRACE; + } else if (db6 & (DB6_B3|DB6_B2|DB6_B1|DB6_B0)) { + si_code = TRAP_HWBKPT; + } + + memset(&info, '\0', sizeof info); + info.si_code = si_code; + set_signal(SIGTRAP, regs, &info); + check_signal(0, regs); + check_need_resched(); +} + +void int3_handler(struct x86_regs *regs) +{ + struct siginfo info; + +#ifdef DEBUG_PRINT_CPU + kprintf("int3 exception (err: %lx, %lx:%lx)\n", + regs->error, regs->cs, regs->rip); + arch_show_interrupt_context(regs); +#endif + + memset(&info, '\0', sizeof info); + info.si_code = TRAP_BRKPT; + set_signal(SIGTRAP, regs, &info); + check_signal(0, regs); + check_need_resched(); +} + void x86_issue_ipi(unsigned int apicid, unsigned int low) { lapic_icr_write(apicid << LAPIC_ICR_ID_SHIFT, low); diff --git a/arch/x86/kernel/include/registers.h b/arch/x86/kernel/include/registers.h index f8bc8439..4e9a94f6 100644 --- a/arch/x86/kernel/include/registers.h +++ b/arch/x86/kernel/include/registers.h @@ -34,6 +34,14 @@ #define RFLAGS_VIP (1 << 20) #define RFLAGS_ID (1 << 21) +#define DB6_B0 (1 << 0) +#define DB6_B1 (1 << 1) +#define DB6_B2 (1 << 2) +#define DB6_B3 (1 << 3) +#define DB6_BD (1 << 13) +#define DB6_BS (1 << 14) +#define DB6_BT (1 << 15) + #define MSR_EFER 0xc0000080 #define MSR_STAR 0xc0000081 #define MSR_LSTAR 0xc0000082 diff --git a/arch/x86/kernel/interrupt.S b/arch/x86/kernel/interrupt.S index 59dd258e..8af63d16 100644 --- a/arch/x86/kernel/interrupt.S +++ b/arch/x86/kernel/interrupt.S @@ -154,3 +154,25 @@ enter_user_mode: POP_ALL_REGS addq $8, %rsp iretq + +.globl debug_exception +debug_exception: + cld + pushq $0 /* error */ + PUSH_ALL_REGS + movq %rsp, %rdi + call debug_handler + POP_ALL_REGS + addq $8, %rsp + iretq + +.globl int3_exception +int3_exception: + cld + pushq $0 /* error */ + PUSH_ALL_REGS + movq %rsp, %rdi + call int3_handler + POP_ALL_REGS + addq $8, %rsp + iretq diff --git a/arch/x86/kernel/syscall.c b/arch/x86/kernel/syscall.c index b34a96c9..0293d675 100644 --- a/arch/x86/kernel/syscall.c +++ b/arch/x86/kernel/syscall.c @@ -384,6 +384,11 @@ void clear_single_step(struct process *proc) proc->uctx->rflags &= ~RFLAGS_TF; } +void set_single_step(struct process *proc) +{ + proc->uctx->rflags |= RFLAGS_TF; +} + extern void coredump(struct process *proc, void *regs); static void ptrace_report_signal(struct process *proc, struct x86_regs *regs, int sig) diff --git a/kernel/syscall.c b/kernel/syscall.c index edb5dadd..1349b006 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -2557,6 +2557,8 @@ SYSCALL_DECLARE(getrlimit) } extern int ptrace_traceme(void); +extern void clear_single_step(struct process *proc); +extern void set_single_step(struct process *proc); static int ptrace_wakeup_sig(int pid, long request, long data) { dkprintf("ptrace_wakeup_sig,pid=%d,data=%08x\n", pid, data); @@ -2588,6 +2590,14 @@ static int ptrace_wakeup_sig(int pid, long request, long data) { } break; case PTRACE_CONT: + case PTRACE_SINGLESTEP: + case PTRACE_SYSCALL: + if (request == PTRACE_SINGLESTEP) { + set_single_step(child); + } + if (request == PTRACE_SYSCALL) { + /* TODO: may set PTRACE_SYSCALL flag */ + } if(data != 0 && data != SIGSTOP) { struct process *proc; @@ -2823,8 +2833,6 @@ out: return ret; } -extern void clear_single_step(struct process *proc); - static int ptrace_detach(int pid, int data) { int error; @@ -2991,7 +2999,8 @@ SYSCALL_DECLARE(ptrace) dkprintf("PTRACE_POKEDATA: addr=%p data=%p\n", addr, data); break; case PTRACE_SINGLESTEP: - dkprintf("ptrace: unimplemented ptrace(PTRACE_SINGLESTEP) called.\n"); + dkprintf("ptrace: PTRACE_SINGLESTEP: data=%d\n", data); + error = ptrace_wakeup_sig(pid, request, data); break; case PTRACE_GETFPREGS: dkprintf("ptrace: unimplemented ptrace(PTRACE_GETFPREGS) called.\n");