All of lore.kernel.org
 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.