debug_setcontext syscall
The following patch adds a syscall to the current linuxppc-2.5 tree. This syscall allows in-application debuggers to work; it allows debugging actions (currently setting the single-step bit and branch trace enable bits) to be done for a setcontext, like a return from a signal handler.
I have also attached a small program that demonstrates the usage of the single stepping and branch tracing.
I'd like to get this into the standard kernel, but this is (obviously) just for comment now.
-Corey
#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include <sys/mman.h>
#include <asm/unistd.h>
struct my_sig_dbg_op {
int dbg_type;
unsigned long dbg_value;
};/* Enable or disable single-stepping. The value sets the state. */
#define MY_SIG_DBG_SINGLE_STEPPING 1
/* Enable or disable branch tracing. The value sets the state. */
#define MY_SIG_DBG_BRANCH_TRACING 2
#ifndef __NR_dbg_sigreturn
#define __NR_dbg_sigreturn 256
#endif
/* Create the debug return syscall. */
_syscall3(int, dbg_sigreturn,
void *, ucontext, int, ndbg, struct my_sig_dbg_op *, op);
volatile int called = 0;
volatile int called2 = 0;
volatile int called3 = 0;
volatile int trap_called = 0;
void sighand(int sig, siginfo_t *info, void *ucontext)
{
struct my_sig_dbg_op op;
called++;
kill(getpid(), SIGUSR2);
op.dbg_type = MY_SIG_DBG_SINGLE_STEPPING;
op.dbg_value = 1;
dbg_sigreturn(ucontext, 1, &op);
}void sighand2(int sig, siginfo_t *info, void *ucontext)
{
kill(getpid(), SIGPWR);
called2++;
}void sighand3(int sig, siginfo_t *info, void *ucontext)
{
struct my_sig_dbg_op op;
called3++;
op.dbg_type = MY_SIG_DBG_SINGLE_STEPPING;
op.dbg_value = 1;
dbg_sigreturn(ucontext, 1, &op);
}#define PAGE_SIZE 4096
#define TO_PAGEBASE(a) (((unsigned int) (a)) & (~(PAGE_SIZE-1))) #define TRAP_INSTRUCTION 0x0ce00097
static int
write_instruction(unsigned char *address,
unsigned long new_instr,
unsigned long *old_instr)
{
char *pagebase = (char *) TO_PAGEBASE(address);
/* FIXME - currently assuming read-only executable memory, need a
way to fetch the old memory protection. */
if (mprotect(pagebase,
PAGE_SIZE,
PROT_READ | PROT_WRITE | PROT_EXEC) != 0) {
/* Couldn't change memory protection, return an error */
return -1;
}
if (old_instr)
*old_instr = *((volatile unsigned long *) address);
*((volatile unsigned long *) address) = new_instr;
mprotect(pagebase, PAGE_SIZE, PROT_READ | PROT_EXEC);
/* Flush the cache for the instruction. */
__asm__ ("dcbst 0,%0\n\ticbi 0,%0" : : "r" (address));
return 0;
}unsigned char *instr_addr;
unsigned long old_instr;
int restore = 0;
int tracing = 0;
/* The "old" ucontext. */
struct old_sigcontext_struct {
unsigned long _unused[4];
int signal;
unsigned long handler;
unsigned long oldmask;
struct pt_regs *regs;
};
struct old_ucontext {
unsigned long uc_flags;
struct ucontext *uc_link;
stack_t uc_stack;
struct sigcontext_struct uc_mcontext;
sigset_t uc_sigmask; /* mask last for extensibility */
};unsigned long dbg_get_instruction_ptr_from_ucontext(void *ucontext)
{
struct old_ucontext *uc = ucontext;
struct pt_regs *regs = uc->uc_mcontext.regs;
return regs->nip;
}void sigtrap(int sig, siginfo_t *info, void *ucontext)
{
int old_errno = errno;
trap_called++;
if (restore) {
write_instruction(instr_addr, old_instr, NULL);
restore = 0;
}
if (tracing) {
char buf[100];
sprintf(buf, "Trap at %8.8x\n", dbg_get_instruction_ptr_from_ucontext(ucontext));
__libc_write(2, buf, strlen(buf));
if (trap_called > 20)
tracing = 0;
else {
struct my_sig_dbg_op op;
errno = old_errno;
op.dbg_type = MY_SIG_DBG_BRANCH_TRACING;
op.dbg_value = 1;
dbg_sigreturn(ucontext, 1, &op);
}
}
errno = old_errno;
}void call_printf(void)
{
printf("test\n");
}int main(int argc, char *argv)
{
struct sigaction act;
int rv;
act.sa_sigaction = sighand;
act.sa_flags = SA_SIGINFO;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask, SIGUSR2);
rv = sigaction(SIGUSR1, &act, NULL);
if (rv == -1) {
perror("sigaction");
exit(1);
}
act.sa_sigaction = sighand2;
rv = sigaction(SIGUSR2, &act, NULL);
if (rv == -1) {
perror("sigaction");
exit(1);
}
act.sa_sigaction = sighand3;
rv = sigaction(SIGPWR, &act, NULL);
if (rv == -1) {
perror("sigaction");
exit(1);
}
act.sa_sigaction = sigtrap;
rv = sigaction(SIGTRAP, &act, NULL);
if (rv == -1) {
perror("sigaction");
exit(1);
}
kill(getpid(), SIGUSR1);
if (!called)
printf("Didn't get called\n");
else
printf("Got called %d times\n", called);
if (!called2)
printf("Didn't get called 2\n");
else
printf("Got called 2 %d times\n", called2);
if (!called3)
printf("Didn't get called 3\n");
else
printf("Got called 3 %d times\n", called3);
if (!trap_called) {
printf("ERROR: Didn't get trapped\n");
exit(1);
} else
printf("Got trapped %d times\n", trap_called);
instr_addr = (unsigned char *) call_printf;
write_instruction(instr_addr, TRAP_INSTRUCTION, &old_instr);
restore = 1;
tracing = 1;
call_printf();
exit(0);
}arch/ppc/kernel/misc.S +2 -0
@@ -1385,3 +1385,5 @@ .long sys_statfs64 .long sys_fstatfs64 .long ppc_fadvise64_64 + .long sys_ni_syscall /* 255 */ + .long sys_debug_setcontext
arch/ppc/kernel/signal.c +82 -0
88 lines not on this page · raw message
arch/ppc/kernel/traps.c +1 -1
@@ -462,7 +462,7 @@void SingleStepException (struct pt_regs *regs) { - regs->msr &= ~MSR_SE ;/* Turn off 'trace' bit */ + regs->msr &= ~(MSR_SE |MSR_BE );/* Turn off 'trace' bits */ if (debugger_sstep (regs))return ;_exception (SIGTRAP , regs,TRAP_TRACE ,0 );
include/asm-ppc/signal.h +19 -0
@@ -153,4 +153,23 @@#define ptrace_signal_deliver (regs, cookie) do { } while (0)#endif /* __KERNEL__ */ +/* +* These are parameters to dbg_sigreturn syscall. They enable or +* disable certain debugging things that can be done from signal +* handlers. The dbg_sigreturn syscall *must* be called from a +* SA_SIGINFO signal so the ucontext can be passed to it. It takes an +* array of struct sig_dbg_op, which has the debug operations to +* perform before returning from the signal. +*/ +struct sig_dbg_op { +int dbg_type; +unsigned long dbg_value; +}; + +/* Enable or disable single-stepping. The value sets the state. */ +#define SIG_DBG_SINGLE_STEPPING 1 + +/* Enable or disable branch tracing. The value sets the state. */ +#define SIG_DBG_BRANCH_TRACING 2 +#endif
include/asm-ppc/unistd.h +2 -1
@@ -259,8 +259,9 @@#define __NR_statfs64 252#define __NR_fstatfs64 253#define __NR_fadvise64_64 254 +#define __NR_debug_setcontext 256 -#define __NR_syscalls 255 +#define __NR_syscalls 257#define __NR (n) #n