linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
From: Corey Minyard <minyard@acm.org>
To: linuxppc-dev@lists.linuxppc.org
Subject: debug_setcontext syscall
Date: Wed, 17 Dec 2003 11:03:35 -0600	[thread overview]
Message-ID: <3FE08C67.4020403@acm.org> (raw)

[-- Attachment #1: Type: text/plain, Size: 497 bytes --]

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

[-- Attachment #2: test_dbg_setcontext.c --]
[-- Type: text/plain, Size: 4627 bytes --]


#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);
}

[-- Attachment #3: ppc_dbg-2.5.diff --]
[-- Type: text/plain, Size: 4861 bytes --]

diff -ur 2.5.old/arch/ppc/kernel/misc.S 2.5/arch/ppc/kernel/misc.S
--- 2.5.old/arch/ppc/kernel/misc.S	2003-12-15 11:20:44.000000000 -0600
+++ 2.5/arch/ppc/kernel/misc.S	2003-12-14 21:31:49.000000000 -0600
@@ -1385,3 +1385,5 @@
 	.long sys_statfs64
 	.long sys_fstatfs64
 	.long ppc_fadvise64_64
+	.long sys_ni_syscall	/* 255 */
+	.long sys_debug_setcontext
diff -ur 2.5.old/arch/ppc/kernel/signal.c 2.5/arch/ppc/kernel/signal.c
--- 2.5.old/arch/ppc/kernel/signal.c	2003-12-15 11:21:16.000000000 -0600
+++ 2.5/arch/ppc/kernel/signal.c	2003-12-17 07:50:15.000000000 -0600
@@ -445,6 +445,88 @@
 	return 0;
 }

+int sys_debug_setcontext(struct ucontext __user *ctx,
+			 int ndbg, struct sig_dbg_op *dbg,
+			 int r6, int r7, int r8,
+			 struct pt_regs *regs)
+{
+	unsigned char tmp;
+	struct sig_dbg_op op;
+	int i;
+	unsigned long new_msr = regs->msr;
+#if defined(CONFIG_4xx)
+	unsigned long new_dbcr0 = current->thread.dbcr0;
+#endif
+
+	for (i=0; i<ndbg; i++) {
+		if (__copy_from_user(&op, dbg, sizeof(op)))
+			return -EFAULT;
+		switch (op.dbg_type) {
+		case SIG_DBG_SINGLE_STEPPING:
+#if defined(CONFIG_4xx)
+			if (op.dbg_value) {
+				new_msr |= MSR_DE;
+				new_dbcr0 |= (DBCR0_IDM | DBCR0_IC);
+			} else {
+				new_msr &= ~MSR_DE;
+				new_dbcr0 &= ~(DBCR0_IDM | DBCR0_IC);
+			}
+#else
+			if (op.dbg_value)
+				new_msr |= MSR_SE;
+			else
+				new_msr &= ~MSR_SE;
+#endif
+			break;
+		case SIG_DBG_BRANCH_TRACING:
+#if defined(CONFIG_4xx)
+			return -EINVAL;
+#else
+			if (op.dbg_value)
+				new_msr |= MSR_BE;
+			else
+				new_msr &= ~MSR_BE;
+#endif
+			break;
+
+		default:
+			return -EINVAL;
+		}
+	}
+
+	if (verify_area(VERIFY_READ, ctx, sizeof(*ctx))
+	    || __get_user(tmp, (u8 *) ctx)
+	    || __get_user(tmp, (u8 *) (ctx + 1) - 1))
+		return -EFAULT;
+
+	/* We wait until here to actually install the values in the
+	   registers so if we fail in the above loop, it will not
+	   affect the contents of these registers.  After this point,
+	   failure is a problem, anyway, and it's very unlikely unless
+	   the user is really doing something wrong. */
+	regs->msr = new_msr;
+#if defined(CONFIG_4xx)
+	current->thread.dbcr0 = new_dbcr0;
+#endif
+
+	/*
+	 * If we get a fault copying the context into the kernel's
+	 * image of the user's registers, we can't just return -EFAULT
+	 * because the user's registers will be corrupted.  For instance
+	 * the NIP value may have been updated but not some of the
+	 * other registers.  Given that we have done the verify_area
+	 * and successfully read the first and last bytes of the region
+	 * above, this should only happen in an out-of-memory situation
+	 * or if another thread unmaps the region containing the context.
+	 * We kill the task with a SIGSEGV in this situation.
+	 */
+	if (do_setcontext(ctx, regs))
+		do_exit(SIGSEGV);
+	sigreturn_exit(regs);
+	/* doesn't actually return back to here */
+	return 0;
+}
+
 /*
  * OK, we're invoking a handler
  */
diff -ur 2.5.old/arch/ppc/kernel/traps.c 2.5/arch/ppc/kernel/traps.c
--- 2.5.old/arch/ppc/kernel/traps.c	2003-12-15 11:18:41.000000000 -0600
+++ 2.5/arch/ppc/kernel/traps.c	2003-12-14 21:27:33.000000000 -0600
@@ -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);
diff -ur 2.5.old/include/asm-ppc/signal.h 2.5/include/asm-ppc/signal.h
--- 2.5.old/include/asm-ppc/signal.h	2003-12-15 11:18:33.000000000 -0600
+++ 2.5/include/asm-ppc/signal.h	2003-12-14 21:27:40.000000000 -0600
@@ -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
diff -ur 2.5.old/include/asm-ppc/unistd.h 2.5/include/asm-ppc/unistd.h
--- 2.5.old/include/asm-ppc/unistd.h	2003-12-15 11:18:41.000000000 -0600
+++ 2.5/include/asm-ppc/unistd.h	2003-12-14 21:29:24.000000000 -0600
@@ -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


                 reply	other threads:[~2003-12-17 17:03 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=3FE08C67.4020403@acm.org \
    --to=minyard@acm.org \
    --cc=linuxppc-dev@lists.linuxppc.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).