Hi Frank, Current gdb stub seems to cause a SIGSTOP when a signal handler is called. And this behaviour is not consistent. This behaviour is different from when gdb was invoked on the program without the stap.
I believe the STOP at signal handler would only happen when UTRACE_SIGNAL_HANDLER or UTRACE_SIGNAL_REPORT before the utrace-gdb quiesce handler gets called. The patch is as below. commit eb53493b0208bc036dc570560ac3449aed450fbf Author: Srikar Dronamraju <sri...@linux.vnet.ibm.com> Date: Thu Jul 9 21:03:45 2009 +0530 make sure quiesce doesnt make the process to stop at the signal handler diff --git a/kernel/utrace-gdb.c b/kernel/utrace-gdb.c index 3835761..cbed911 100644 --- a/kernel/utrace-gdb.c +++ b/kernel/utrace-gdb.c @@ -10,7 +10,7 @@ * Red Hat Author: Frank Ch. Eigler */ -/* #define DEBUG 1 */ +#define DEBUG 1 #include <asm/syscall.h> #include <asm/signal.h> @@ -249,6 +249,7 @@ u32 gdb_utrace_report_signal(u32 action, if (utrace_signal_action(action) == UTRACE_SIGNAL_REPORT || utrace_signal_action(action) == UTRACE_SIGNAL_HANDLER) { /* case 5 */ /* NB: disregard p->at_quiesce_do */ + p->at_quiesce_do = UTRACE_RESUME; ret = UTRACE_RESUME | utrace_signal_action(action); } else if (p->skip_signals > 0 /*&& kern_p*/) { /* case 4 */ p->skip_signals --; > Hi - > > Further to http://sourceware.org/ml/systemtap/2009-q2/msg00969.html, I > attach another snapshot of my gdb-stub in linux-kernel prototype. It's > working a lot better. Usage is as before: > > % PROCESS & > [1] 21175 > % gdb PROCESS > (gdb) target remote /proc/21175/gdb > (gdb) # whatever strikes your fancy > > Known limitations: > - http://sourceware.org/ml/gdb/2009-07/msg00036.html > (occasional "Remote failure reply: E....." error) > - only for single-threaded programs > - x86-64 and x86 only > - floating poing registers not yet done > - checkpatch.pl not yet satisfied > > This patch should apply to recent utrace-patched kernels. > > - FChE > > > diff --git a/fs/proc/base.c b/fs/proc/base.c > index 3326bbf..0afb05a 100644 > --- a/fs/proc/base.c > +++ b/fs/proc/base.c > @@ -77,6 +77,7 @@ > #include <linux/audit.h> > #include <linux/poll.h> > #include <linux/nsproxy.h> > +#include <linux/utrace.h> > #include <linux/oom.h> > #include <linux/elf.h> > #include <linux/pid_namespace.h> > @@ -2542,6 +2543,9 @@ static const struct pid_entry tgid_base_stuff[] = { > #ifdef CONFIG_TASK_IO_ACCOUNTING > INF("io", S_IRUGO, proc_tgid_io_accounting), > #endif > +#ifdef CONFIG_UTRACE_GDB > + REG("gdb", S_IRUSR|S_IWUSR, proc_gdb_operations), > +#endif > }; > > static int proc_tgid_base_readdir(struct file * filp, > diff --git a/include/linux/utrace.h b/include/linux/utrace.h > index f877ec6..f33a5da 100644 > --- a/include/linux/utrace.h > +++ b/include/linux/utrace.h > @@ -689,4 +689,8 @@ static inline __must_check int utrace_barrier_pid(struct > pid *pid, > > #endif /* CONFIG_UTRACE */ > > +#ifdef CONFIG_UTRACE_GDB > +extern const struct file_operations proc_gdb_operations; > +#endif > + > #endif /* linux/utrace.h */ > diff --git a/init/Kconfig b/init/Kconfig > index a6987df..7ffa60d 100644 > --- a/init/Kconfig > +++ b/init/Kconfig > @@ -1158,6 +1158,14 @@ menuconfig UTRACE > kernel interface exported to kernel modules, to track events in > user threads, extract and change user thread state. > > +config UTRACE_GDB > + bool "/proc/<pid>/gdb file for gdb remote connection" > + select UTRACE > + default y > + help > + Enable the utrace-based /proc/<pid>/gdb process debugging > + interface, for connection using the gdb remote protocol. > + > source "block/Kconfig" > > config PREEMPT_NOTIFIERS > diff --git a/kernel/Makefile b/kernel/Makefile > index a79634e..21457e6 100644 > --- a/kernel/Makefile > +++ b/kernel/Makefile > @@ -69,6 +69,7 @@ obj-$(CONFIG_RESOURCE_COUNTERS) += res_counter.o > obj-$(CONFIG_STOP_MACHINE) += stop_machine.o > obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o > obj-$(CONFIG_UTRACE) += utrace.o > +obj-$(CONFIG_UTRACE_GDB) += utrace-gdb.o > obj-$(CONFIG_AUDIT) += audit.o auditfilter.o > obj-$(CONFIG_AUDITSYSCALL) += auditsc.o > obj-$(CONFIG_AUDIT_TREE) += audit_tree.o > diff --git a/kernel/utrace-gdb.c b/kernel/utrace-gdb.c > new file mode 100644 > index 0000000..3835761 > --- /dev/null > +++ b/kernel/utrace-gdb.c > @@ -0,0 +1,1148 @@ > +/* > + * utrace-based gdb remote protocol server for user processes > + * > + * Copyright (C) 2009 Red Hat, Inc. All rights reserved. > + * > + * This copyrighted material is made available to anyone wishing to use, > + * modify, copy, or redistribute it subject to the terms and conditions > + * of the GNU General Public License v.2. > + * > + * Red Hat Author: Frank Ch. Eigler > + */ > + > +/* #define DEBUG 1 */ > + > +#include <asm/syscall.h> > +#include <asm/signal.h> > +#include <linux/ptrace.h> > +#include <linux/err.h> > +#include <linux/pid.h> > +#include <linux/sched.h> > +#include <linux/module.h> > +#include <linux/init.h> > +#include <linux/mm.h> > +#include <linux/proc_fs.h> > +#include <linux/ctype.h> > +#include <linux/regset.h> > +#include <linux/utrace.h> > +#include <linux/tracehook.h> > + > + > + > +/** struct gdb_connection - Tracks one active gdb-process session. > + */ > + > +#define GDB_BUFMAX 4096 > + > + > +struct gdb_connection { > + pid_t target; > + struct utrace_engine *engine; > + > + /* changed under output_mutex */ > + int at_quiesce_do; > + unsigned char stopcode[GDB_BUFMAX]; // set <=> at_quiesce_do = > UTRACE_STOP > + int skip_signals; > + int stop_signals; > + /* XXX: per-thread later */ > + > + char output_buf[GDB_BUFMAX]; > + size_t output_buf_size; > + loff_t output_buf_read; > + struct mutex output_mutex; > + wait_queue_head_t output_wait; > + > + char input_buf[GDB_BUFMAX]; > + size_t input_buf_size; > + struct mutex input_mutex; > + wait_queue_head_t input_wait; > + > + struct list_head link; > +}; > + > + > +static LIST_HEAD(gdb_connections); > +static DEFINE_MUTEX(gdb_connections_mutex); > +static const struct utrace_engine_ops gdb_utrace_ops; > + > + > + > +/* ------------------------------------------------------------------------ > */ > + > +static unsigned byteme (unsigned char hex1, unsigned char hex2) > +{ > + return (isdigit(hex1) ? hex1-'0' : tolower(hex1)-'a'+10) * 16 + > + (isdigit(hex2) ? hex2-'0' : tolower(hex2)-'a'+10); > +} > + > + > + > +/* Begin a new packet. Add the $, and remember where we put it. > + * Return the offset for later checksum addition via > + * push_output_packet_end. */ > +static size_t push_output_packet_start (struct gdb_connection *p) > +{ > + size_t start = p->output_buf_size; > + > + BUG_ON (p->output_buf_size + 1 >= GDB_BUFMAX); > + p->output_buf[p->output_buf_size++] = '$'; > + return start; > +} > + > + > +/* Add a character to the output queue. Assumes output_mutex held. */ > +static void push_output (struct gdb_connection *p, unsigned char c) > +{ > + /* We know some space must exist; we check for this in > + proc_gdb_write() for example. */ > + BUG_ON (p->output_buf_size >= GDB_BUFMAX); > + p->output_buf[p->output_buf_size++] = c; > +} > + > + > +static char hex[] = "0123456789ABCDEF"; > + > +/* Add a byte (hexified) to the output queue. Assumes output_mutex held. */ > +static void push_output_hex (struct gdb_connection *p, unsigned char c) > +{ > + /* We know some space must exist; we check for this in > + proc_gdb_write() for example. */ > + BUG_ON (p->output_buf_size >= GDB_BUFMAX); > + p->output_buf[p->output_buf_size++] = hex[(c & 0xf0) >> 4]; > + p->output_buf[p->output_buf_size++] = hex[(c & 0x0f) >> 0]; > +} > + > + > +/* Finish the last packet. Starting after the given '$' offset, compute > + * the checksum and append it. */ > +static void push_output_packet_end (struct gdb_connection *p, size_t start) > +{ > + unsigned char checksum = 0; > + int i; > + > + BUG_ON (p->output_buf_size + 3 >= GDB_BUFMAX); > + BUG_ON (p->output_buf[start] != '$'); > + > + for (i=start+1; i<p->output_buf_size; i++) > + checksum += p->output_buf[i]; > + > + p->output_buf[p->output_buf_size++] = '#'; > + p->output_buf[p->output_buf_size++] = hex[(checksum & 0xf0) >> 4]; > + p->output_buf[p->output_buf_size++] = hex[(checksum & 0x0f) >> 0]; > +} > + > + > +/* Add a complete packet payload to the output queue. */ > +static void push_output_packet (struct gdb_connection *p, const char *s) > +{ > + size_t ss = strlen(s); > + size_t start; > + int i; > + > + start = push_output_packet_start(p); > + for (i=0; i<ss; i++) > + push_output(p, s[i]); > + push_output_packet_end(p, start); > +} > + > + > + > +/* ------------------------------------------------------------------------ > */ > + > +/* utrace callbacks */ > + > + > +u32 gdb_utrace_report_quiesce(enum utrace_resume_action action, > + struct utrace_engine *engine, > + struct task_struct *task, > + unsigned long event) > +{ > + struct gdb_connection *p = engine->data; > + pr_debug ("report_quiesce %d event 0x%lx 0x%x->0x%x\n", task->pid, > + event, action, p->at_quiesce_do); > + > + return p->at_quiesce_do; > +} > + > + > +u32 gdb_utrace_report_clone(enum utrace_resume_action action, > + struct utrace_engine *engine, > + struct task_struct *parent, > + unsigned long clone_flags, > + struct task_struct *child) > +{ > + pr_debug ("report_clone %d->%d\n", parent->pid, child->pid); > + > + if (clone_flags & CLONE_THREAD) { > + printk (KERN_WARNING "unsupported multithreading on > /proc/%d/gdb.\n", > + task_pid_nr (parent)); > + } > + /* XXX: is there anything else to do here? */ > + return UTRACE_RESUME; > +} > + > + > +u32 gdb_utrace_report_exec(enum utrace_resume_action action, > + struct utrace_engine *engine, > + struct task_struct *task, > + const struct linux_binfmt *fmt, > + const struct linux_binprm *bprm, > + struct pt_regs *regs) > +{ > + /* XXX: Model an exec as if it were an exit. */ > + struct gdb_connection *p = engine->data; > + > + pr_debug ("report_exec %d->%s\n", task->pid, task->comm); > + > + mutex_lock(&p->output_mutex); > + > + p->at_quiesce_do = UTRACE_STOP; > + snprintf (p->stopcode, GDB_BUFMAX, "W%02x", 0); > + push_output_packet (p, p->stopcode); > + > + mutex_unlock(&p->output_mutex); > + wake_up(&p->output_wait); > + > + /* Suspend the exec operation, to ensure that the connected gdb > + receives the notification packet, and lets us go. */ > + return UTRACE_STOP; > +} > + > + > +u32 gdb_utrace_report_signal(u32 action, > + struct utrace_engine *engine, > + struct task_struct *task, > + struct pt_regs *regs, > + siginfo_t *info, > + const struct k_sigaction *orig_ka, > + struct k_sigaction *return_ka) > +{ > + struct gdb_connection *p = engine->data; > + u32 ret = action; > + int kern_p; > + > + mutex_lock(&p->output_mutex); > + > + kern_p = (info != SEND_SIG_NOINFO && (is_si_special(info) || > SI_FROMKERNEL(info))); > + > + pr_debug ("report_signal %d (0x%x) kern %d skip %d stop %d\n", > + task->pid, action, kern_p, p->skip_signals, > p->stop_signals); > + > + /* The target is about to receive a signal. There are several > + * cases: > + * > + * 1) This is an ordinary signal. We UTRACE_STOP to notify gdb. > + * > + * 2) This is a SIGTRAP arising from a breakpoint. We UTRACE_STOP. > + * > + * 3) This is a signal our code injected to stop the process, in lieu > + * of UTRACE_INTERRUPT. We UTRACE_STOP | UTRACE_SIGNAL_IGN. > + * > + * 4) This is a signal our code injected on behalf of gdb (C/S/I > packets). > + * We UTRACE_RESUME. > + * > + * 5) This is a UTRACE_SIGNAL_REPORT or UTRACE_SIGNAL_HANDLER event. > + * Just let utrace continue, as these signal events of minor internal > + * interest. > + */ > + > + if (utrace_signal_action(action) == UTRACE_SIGNAL_REPORT || > + utrace_signal_action(action) == UTRACE_SIGNAL_HANDLER) { /* case > 5 */ > + /* NB: disregard p->at_quiesce_do */ > + ret = UTRACE_RESUME | utrace_signal_action(action); > + } else if (p->skip_signals > 0 /*&& kern_p*/) { /* case 4 */ > + p->skip_signals --; > + p->at_quiesce_do = UTRACE_RESUME; > + ret = UTRACE_RESUME; /* deliver */ > + } else if (p->stop_signals > 0 /*&& kern_p*/) { /* Case 3 */ > + p->stop_signals --; > + snprintf (p->stopcode, GDB_BUFMAX, "S%02x", info->si_signo); > + push_output_packet (p, p->stopcode); > + p->at_quiesce_do = UTRACE_STOP; > + ret = UTRACE_STOP | UTRACE_SIGNAL_IGN; > + } else { /* Cases 1, 2 */ > + snprintf (p->stopcode, GDB_BUFMAX, "S%02x", info->si_signo); > + push_output_packet (p, p->stopcode); > + p->at_quiesce_do = UTRACE_STOP; > + ret = UTRACE_STOP; > + } > + > + pr_debug ("action 0x%x\n", ret); > + > + mutex_unlock(&p->output_mutex); > + wake_up(&p->output_wait); > + > + return ret; > +} > + > + > +u32 gdb_utrace_report_exit(enum utrace_resume_action action, > + struct utrace_engine *engine, > + struct task_struct *task, > + long orig_code, long *code) > +{ > + struct gdb_connection *p = engine->data; > + > + pr_debug ("report_exit %d (%lx)\n", task->pid, (unsigned long) > orig_code); > + > + mutex_lock(&p->output_mutex); > + > + p->at_quiesce_do = UTRACE_STOP; > + snprintf (p->stopcode, GDB_BUFMAX, > + "W%02x", (unsigned)(orig_code & 0xFF)); > + push_output_packet (p, p->stopcode); > + > + mutex_unlock(&p->output_mutex); > + wake_up(&p->output_wait); > + > + /* Suspend the exit operation, to ensure that the connected gdb > + receives the notification packet, and lets us go. */ > + return UTRACE_STOP; > +} > + > + > +u32 gdb_utrace_report_death(struct utrace_engine *engine, > + struct task_struct *task, > + bool group_dead, int signal) > +{ > + struct gdb_connection *p = engine->data; > + > + pr_debug ("report_death %d (%d)\n", task->pid, signal); > + > + mutex_lock(&p->output_mutex); > + > + p->at_quiesce_do = UTRACE_DETACH; > + snprintf (p->stopcode, GDB_BUFMAX, "X%2x", (unsigned)(signal & > 0xFF)); > + push_output_packet (p, p->stopcode); > + > + p->engine = NULL; > + > + mutex_unlock(&p->output_mutex); > + wake_up(&p->output_wait); > + > + return UTRACE_DETACH; > +} > + > + > + > +static const struct utrace_engine_ops gdb_utrace_ops = { > + .report_quiesce = gdb_utrace_report_quiesce, > + .report_signal = gdb_utrace_report_signal, > + .report_death = gdb_utrace_report_death, > + .report_exit = gdb_utrace_report_exit, > + .report_exec = gdb_utrace_report_exec, > + .report_clone = gdb_utrace_report_clone, > + /* XXX: syscall trapping is also possible. */ > +}; > + > + > + > +/* XXX: arch-dependent lookup of gdb remote protocol register > + * numbering. The register numbers (user-side) & expected sizes come > + * from gdb's regformats/FOO-linux.dat. The regset (kernel-side) > + * numbers could come from offsetof/sizeof constructs based upon each > + * arch's asm/user*.h. > + */ > + > +struct gdb_map_regset { > + unsigned pos; /* regset offset */ > + unsigned count; /* regset byte count */ > + unsigned rsn; /* regset number */ > + unsigned bytes; /* gdb's view of register width; <= count */ > +}; > + > +struct gdb_map_regset arch_i386_map_regset[] = { > + [0]={ /* eax */ 6*4, 4, NT_PRSTATUS, 4, }, > + [1]={ /* ecx */ 1*4, 4, NT_PRSTATUS, 4, }, > + [2]={ /* edx */ 2*4, 4, NT_PRSTATUS, 4, }, > + [3]={ /* ebx */ 0*4, 4, NT_PRSTATUS, 4, }, > + [4]={ /* esp */ 15*4, 4, NT_PRSTATUS, 4, }, > + [5]={ /* ebp */ 5*4, 4, NT_PRSTATUS, 4, }, > + [6]={ /* esi */ 3*4, 4, NT_PRSTATUS, 4, }, > + [7]={ /* edi */ 4*4, 4, NT_PRSTATUS, 4, }, > + [8]={ /* eip */ 12*4, 4, NT_PRSTATUS, 4, }, > + [9]={ /* eflags */ 14*4, 4, NT_PRSTATUS, 4, }, > + [10]={ /* cs */ 13*4, 4, NT_PRSTATUS, 4, }, > + [11]={ /* ss */ 16*4, 4, NT_PRSTATUS, 4, }, > + [12]={ /* ds */ 7*4, 4, NT_PRSTATUS, 4, }, > + [13]={ /* es */ 8*4, 4, NT_PRSTATUS, 4, }, > + [14]={ /* fs */ 9*4, 4, NT_PRSTATUS, 4, }, > + [15]={ /* gs */ 10*4, 4, NT_PRSTATUS, 4, }, > + [16]={ /* st0 */ 0, 0, NT_PRFPREG, 10, }, > + [17]={ /* st1 */ 0, 0, NT_PRFPREG, 10, }, > + [18]={ /* st2 */ 0, 0, NT_PRFPREG, 10, }, > + [19]={ /* st3 */ 0, 0, NT_PRFPREG, 10, }, > + [20]={ /* st4 */ 0, 0, NT_PRFPREG, 10, }, > + [21]={ /* st5 */ 0, 0, NT_PRFPREG, 10, }, > + [22]={ /* st6 */ 0, 0, NT_PRFPREG, 10, }, > + [23]={ /* st7 */ 0, 0, NT_PRFPREG, 10, }, > + [24]={ /* fctrl */ 0, 0, NT_PRFPREG, 4, }, > + [25]={ /* fstat */ 0, 0, NT_PRFPREG, 4, }, > + [26]={ /* ftag */ 0, 0, NT_PRFPREG, 4, }, > + [27]={ /* fiseg */ 0, 0, NT_PRFPREG, 4, }, > + [28]={ /* fioff */ 0, 0, NT_PRFPREG, 4, }, > + [29]={ /* foseg */ 0, 0, NT_PRFPREG, 4, }, > + [30]={ /* fooff */ 0, 0, NT_PRFPREG, 4, }, > + [31]={ /* fop */ 0, 0, NT_PRFPREG, 4, }, > + [32]={ /* xmm0 */ 0, 0, NT_PRFPREG, 16, }, > + [33]={ /* xmm1 */ 0, 0, NT_PRFPREG, 16, }, > + [34]={ /* xmm2 */ 0, 0, NT_PRFPREG, 16, }, > + [35]={ /* xmm3 */ 0, 0, NT_PRFPREG, 16, }, > + [36]={ /* xmm4 */ 0, 0, NT_PRFPREG, 16, }, > + [37]={ /* xmm5 */ 0, 0, NT_PRFPREG, 16, }, > + [38]={ /* xmm6 */ 0, 0, NT_PRFPREG, 16, }, > + [39]={ /* xmm7 */ 0, 0, NT_PRFPREG, 16, }, > + [40]={ /* mxcsr */ 0, 0, NT_PRFPREG, 4, }, > + [41]={ /* orig_eax*/ 0, 0, NT_PRSTATUS, 4, }, > +}; > + > + > +struct gdb_map_regset arch_x86_64_map_regset[] = { > + [0]={ /* rax */ 10*8, 8, NT_PRSTATUS, 8, }, > + [1]={ /* rbx */ 5*8, 8, NT_PRSTATUS, 8, }, > + [2]={ /* rcx */ 11*8, 8, NT_PRSTATUS, 8, }, > + [3]={ /* rdx */ 12*8, 8, NT_PRSTATUS, 8, }, > + [4]={ /* rsi */ 13*8, 8, NT_PRSTATUS, 8, }, > + [5]={ /* rdi */ 14*8, 8, NT_PRSTATUS, 8, }, > + [6]={ /* rbp */ 4*8, 8, NT_PRSTATUS, 8, }, > + [7]={ /* rsp */ 19*8, 8, NT_PRSTATUS, 8, }, > + [8]={ /* r8 */ 9*8, 8, NT_PRSTATUS, 8, }, > + [9]={ /* r9 */ 8*8, 8, NT_PRSTATUS, 8, }, > + [10]={ /* r10 */ 7*8, 8, NT_PRSTATUS, 8, }, > + [11]={ /* r11 */ 6*8, 8, NT_PRSTATUS, 8, }, > + [12]={ /* r12 */ 3*8, 8, NT_PRSTATUS, 8, }, > + [13]={ /* r13 */ 2*8, 8, NT_PRSTATUS, 8, }, > + [14]={ /* r14 */ 1*8, 8, NT_PRSTATUS, 8, }, > + [15]={ /* r15 */ 0*8, 8, NT_PRSTATUS, 8, }, > + [16]={ /* rip */ 16*8, 8, NT_PRSTATUS, 8, }, > + [17]={ /* flags */ 18*8, 8, NT_PRSTATUS, 4, }, > + [18]={ /* cs */ 17*8, 8, NT_PRSTATUS, 4, }, > + [19]={ /* ss */ 20*8, 8, NT_PRSTATUS, 4, }, > + [20]={ /* ds */ 23*8, 8, NT_PRSTATUS, 4, }, > + [21]={ /* es */ 24*8, 8, NT_PRSTATUS, 4, }, > + [22]={ /* fs */ 25*8, 8, NT_PRSTATUS, 4, }, > + [23]={ /* gs */ 26*8, 8, NT_PRSTATUS, 4, }, > + [24]={ /* st0 */ 0, 0, NT_PRFPREG, 10, }, > + [25]={ /* st1 */ 0, 0, NT_PRFPREG, 10, }, > + [26]={ /* st2 */ 0, 0, NT_PRFPREG, 10, }, > + [27]={ /* st3 */ 0, 0, NT_PRFPREG, 10, }, > + [28]={ /* st4 */ 0, 0, NT_PRFPREG, 10, }, > + [29]={ /* st5 */ 0, 0, NT_PRFPREG, 10, }, > + [30]={ /* st6 */ 0, 0, NT_PRFPREG, 10, }, > + [31]={ /* st7 */ 0, 0, NT_PRFPREG, 10, }, > + [32]={ /* fctrl */ 0, 0, NT_PRFPREG, 4, }, > + [33]={ /* fstat */ 0, 0, NT_PRFPREG, 4, }, > + [34]={ /* ftag */ 0, 0, NT_PRFPREG, 4, }, > + [35]={ /* fiseg */ 0, 0, NT_PRFPREG, 4, }, > + [36]={ /* fioff */ 0, 0, NT_PRFPREG, 4, }, > + [37]={ /* foseg */ 0, 0, NT_PRFPREG, 4, }, > + [38]={ /* fooff */ 0, 0, NT_PRFPREG, 4, }, > + [39]={ /* fop */ 0, 0, NT_PRFPREG, 4, }, > + [40]={ /* xmm0 */ 0, 0, NT_PRFPREG, 16, }, > + [41]={ /* xmm1 */ 0, 0, NT_PRFPREG, 16, }, > + [42]={ /* xmm2 */ 0, 0, NT_PRFPREG, 16, }, > + [43]={ /* xmm3 */ 0, 0, NT_PRFPREG, 16, }, > + [44]={ /* xmm4 */ 0, 0, NT_PRFPREG, 16, }, > + [45]={ /* xmm5 */ 0, 0, NT_PRFPREG, 16, }, > + [46]={ /* xmm6 */ 0, 0, NT_PRFPREG, 16, }, > + [47]={ /* xmm7 */ 0, 0, NT_PRFPREG, 16, }, > + [48]={ /* xmm8 */ 0, 0, NT_PRFPREG, 16, }, > + [49]={ /* xmm9 */ 0, 0, NT_PRFPREG, 16, }, > + [50]={ /* xmm10 */ 0, 0, NT_PRFPREG, 16, }, > + [51]={ /* xmm11 */ 0, 0, NT_PRFPREG, 16, }, > + [52]={ /* xmm12 */ 0, 0, NT_PRFPREG, 16, }, > + [53]={ /* xmm13 */ 0, 0, NT_PRFPREG, 16, }, > + [54]={ /* xmm14 */ 0, 0, NT_PRFPREG, 16, }, > + [55]={ /* xmm15 */ 0, 0, NT_PRFPREG, 16, }, > + [56]={ /* mxcsr */ 0, 0, NT_PRFPREG, 4, }, > + [57]={ /* orig_rax*/ 15*8, 8, NT_PRSTATUS, 8, }, > +}; > + > + > + > +static int gdb_remote_register_info(struct gdb_connection *p, > + struct task_struct *task, > + unsigned number, > + unsigned *pos, unsigned *count, > + unsigned *bytes) > +{ > + const struct user_regset_view *rs = task_user_regset_view(task); > + int rsn = -1; > + > + if(rs == 0) > + return -ENOENT; > + > + /* pr_debug ("gdb_remote_register_info rs=%p rs->n=%u\n", rs, > rs->n); */ > + > +#define GMRSIZE (sizeof(struct gdb_map_regset)) > + > + if(rs->e_machine == EM_386) { > + if (number < sizeof(arch_i386_map_regset)/GMRSIZE) { > + *pos = arch_i386_map_regset[number].pos; > + *count = arch_i386_map_regset[number].count; > + *bytes = arch_i386_map_regset[number].bytes; > + rsn = arch_i386_map_regset[number].rsn; > + } > + } else if(rs->e_machine == EM_X86_64) { > + if (number < sizeof(arch_x86_64_map_regset)/GMRSIZE) { > + *pos = arch_x86_64_map_regset[number].pos; > + *count = arch_x86_64_map_regset[number].count; > + *bytes = arch_x86_64_map_regset[number].bytes; > + rsn = arch_x86_64_map_regset[number].rsn; > + } > + } /* else ... rsn stays -1. */ > + > +#undef GMRSIZE > + > + /* Now map to the per-architecture regset index, based on the > + elf core_note_type we found. */ > + if (rsn >= 0) { > + unsigned j; > + for(j=0; j<rs->n; j++) { > + if(rs->regsets[j].core_note_type == rsn) > + return j; > + } > + } > + > + /* Invalid machines, register numbers, rsns, or unset rsns all > + * fall through here. > + */ > + return -ENOENT; > +} > + > + > + > +/* Process an entire, checksum-confirmed $command# at the front of > + * p->input_buf[]. The input and output mutexes are being held. > + */ > +static void handle_gdb_command_packet (struct gdb_connection *p, struct > task_struct *task) > +{ > + unsigned long arg1, arg2, arg3; > + size_t op_start; > + int rc = 0; > + int i, j; > + > + pr_debug ("gdb packet code %c\n", p->input_buf[1]); > + > + switch (p->input_buf[1]) { > + case '?': > + if (p->at_quiesce_do != UTRACE_STOP) { > + /* shouldn't happen */ > + send_sig(SIGTRAP, task, 1); > +#if 0 > + rc = utrace_control (task, p->engine, > UTRACE_INTERRUPT); > + if (rc == -EINPROGRESS) > + rc = utrace_barrier(task, p->engine); > +#endif > + > + /* Note that we don't enqueue a reply packet here, > + but make gdb wait for a response from the > + utrace report_FOO callbacks. */ > + p->skip_signals ++; > + } else { > + push_output_packet (p, p->stopcode); > + } > + break; > + > + case 'i': /* [ADDR[,NNN]] */ > + case 's': /* [ADDR] */ > + /* XXX: if !arch_has_single_step() ... then what? */ > + case 'c': /* [ADDR] */ > + rc = sscanf(& p->input_buf[2], "%lx,%lx", &arg1, &arg2); > + if (rc >= 1) { /* Have a PC? */ > + /* XXX: set it */ > + } > + if (rc >= 2) { /* ,NNN present */ > + /* XXX: disregard it. */ > + } > + /* XXX: args ignored */ > + p->stopcode[0]='\0'; > + p->at_quiesce_do = > + ((p->input_buf[1]=='c' || !arch_has_single_step()) > + ? UTRACE_RESUME : UTRACE_SINGLESTEP); > + if (p->at_quiesce_do == UTRACE_SINGLESTEP) > + p->stop_signals ++; > + utrace_control (task, p->engine, p->at_quiesce_do); > + break; > + case 'C': /* SIG[;ADDR] */ > + case 'S': /* SIG[;ADDR] */ > + /* XXX: if !arch_has_single_step() ... then what? */ > + case 'I': /* SIG[;ADDR[,NNN?]] */ > + rc = sscanf(& p->input_buf[2], "%lx;%lx,%lx", &arg1, &arg2, > &arg3); > + if (rc >= 1) { /* SIG present */ > + send_sig ((int)arg1, task, 1); > + } > + if (rc >= 2) { /* ;ADDR present */ > + /* XXX: not done */ > + } > + if (rc >= 3) { /* ,NNN present */ > + /* XXX: disregard it. */ > + } > + p->skip_signals ++; > + p->stopcode[0]='\0'; > + p->at_quiesce_do = > + ((p->input_buf[1]=='C' || !arch_has_single_step()) > + ? UTRACE_RESUME : UTRACE_SINGLESTEP); > + if (p->at_quiesce_do == UTRACE_SINGLESTEP) > + p->stop_signals ++; > + utrace_control (task, p->engine, p->at_quiesce_do); > + /* Response will come at next report_signal. */ > + break; > + case 'D': > + push_output_packet (p, "OK"); > + /* NB: the .release fop callback performs actual utrace > detach. */ > + break; > + case 'g': > + op_start = push_output_packet_start(p); > + /* GDB_BUFMAX stands for some random large number, > + * known to be larger than the number of gdb indexed > + * registers. */ > + for (i=0; i<GDB_BUFMAX; i++) { > + unsigned rs_count; > + unsigned rs_pos; > + unsigned bytes; > + const struct user_regset_view* rsv; > + const struct user_regset* rs; > + unsigned char reg_contents[16]; /* maximum reg. > width */ > + > + int rsn = gdb_remote_register_info(p, task, i, > + &rs_pos, > &rs_count, > + &bytes); > + > + if (rsn < 0) > + break; > + > + /* If we want to extract register data, make sure > + we're fetching at least that much. */ > + BUG_ON (rs_count > 0 && rs_count < bytes); > + /* Assert reg_contents size is right. */ > + BUG_ON(sizeof(reg_contents) < bytes || > + sizeof(reg_contents) < rs_count); > + > + if (rs_count) { /* real register */ > + rsv = task_user_regset_view(task); > + BUG_ON(rsn >= rsv->n); > + rs = & rsv->regsets[rsn]; > + > + /* Extract the register value into > reg_contents[]. */ > + rc = (rs->get) (task, rs, rs_pos, rs_count, > + reg_contents, NULL); > + if (rc) > + break; > + } else { /* dummy value */ > + memset (reg_contents, 0, > sizeof(reg_contents)); > + } > + > + /* Hex-dump it. */ > + /* pr_debug ("gdb register %d => rsn %d p%u c%u b%u > (", > + i, rsn, rs_pos, rs_count, bytes); */ > + /* XXX: endianness adjust for count != bytes */ > + for(j=0; j<bytes; j++) { > + /* pr_debug("%02x", reg_contents[j]);*/ > + push_output_hex(p, reg_contents[j]); > + } > + /* pr_debug(")\n"); */ > + > + } > + push_output_packet_end(p, op_start); > + break; > + case 'G': > + i = 0; > + op_start = 2; /* use as input pointer, past $G in command */ > + while(p->input_buf[op_start] != '#' && > + op_start < p->input_buf_size) { > + unsigned rs_count; > + unsigned rs_pos; > + unsigned bytes; > + const struct user_regset_view* rsv; > + const struct user_regset* rs; > + unsigned char reg_contents[16]; /* maximum reg. > width */ > + > + int rsn = gdb_remote_register_info(p, task, i, > + &rs_pos, > &rs_count, > + &bytes); > + /* pr_debug ("gdb register %d => rsn %d p%u c%u > b%u\n", > + i, rsn, rs_pos, rs_count, bytes); */ > + > + if (rsn < 0) > + break; > + > + /* If we want to extract register data, make sure > + we're fetching at least that much. */ > + BUG_ON(rs_count > 0 && rs_count < bytes); > + /* Assert reg_contents size is right. */ > + BUG_ON(sizeof(reg_contents) < bytes || > + sizeof(reg_contents) < rs_count); > + > + /* Remaining packet too short? */ > + if ((op_start + 2*bytes + 3) < p->input_buf_size) > + break; > + > + /* 0-fill the register copy. XXX initialize > + * it from rs->get() instead? > + */ > + memset (reg_contents, 0, sizeof(reg_contents)); > + > + /* Hex-unconvert all the bytes. */ > + /* XXX: endianness adjust for count != bytes */ > + for(j=0; j<bytes; j++) > + > reg_contents[j]=byteme(p->input_buf[op_start+2*j], > + > p->input_buf[op_start+2*j+1]); > + op_start += 2*bytes; > + > + if (rs_count) { /* real register */ > + BUG_ON(rs_count > sizeof(reg_contents)); > + rsv = task_user_regset_view(task); > + BUG_ON(rsn >= rsv->n); > + rs = & rsv->regsets[rsn]; > + > + /* Set the register value from > reg_contents[]. */ > + rc = (rs->set) (task, rs, rs_pos, rs_count, > + reg_contents, NULL); > + if (rc) > + break; > + } else { /* dummy register */ > + ; > + } > + } > + if (p->input_buf[op_start] == '#' && rc == 0) > + push_output_packet (p, "OK"); > + else > + push_output_packet (p, "E01"); > > + break; > + case 'p': /* REG */ > + break; > + case 'P': /* REG=VAL */ > + break; > + case 'm': /* ADDR,LENGTH */ > + rc = sscanf(& p->input_buf[2], "%lx,%lx", &arg1, &arg2); > + if (rc != 2) > + push_output_packet(p, "E01"); > + else { > + size_t o = push_output_packet_start (p); > + while (arg2 > 0) { > + unsigned char value; > + > + /* Simply stop looping if requested > + length was too large. gdb will > + probably retry from this point > + on. */ > + if (p->output_buf_size + 5 > GDB_BUFMAX) > + break; > + > + rc = access_process_vm(task, arg1, &value, > 1, 0); > + if (rc != 1) > + break; /* EFAULT */ > + else > + push_output_hex (p, value); > + > + arg1++; > + arg2--; > + } > + push_output_packet_end (p, o); > + } > + break; > + case 'M': /* ADDR,LENGTH:XX */ > + /* `i' will index p->input_buf to consume XX hex bytes. */ > + rc = sscanf(& p->input_buf[2], "%lx,%lx:%n", > + &arg1, &arg2, &i); > + op_start = i + 2; /* Skip the leading $M also. */ > + if (rc < 2) { > + push_output_packet(p, "E01"); > + break; > + } > + while (arg2 > 0) { > + unsigned char value; > + > + /* Check that enough input bytes left for > + * these two hex chars, plus the #XX checksum. > + */ > + if (i+4 >= p->input_buf_size) > + break; > + > + value = byteme(p->input_buf[i], > + p->input_buf[i+1]); > + rc = access_process_vm(task, arg1, &value, 1, 1); > + if (rc != 1) > + break; /* EFAULT */ > + > + i += 2; > + arg1++; > + arg2--; > + } > + if (arg2 != 0) > + push_output_packet(p, "E02"); > + else > + push_output_packet(p, "OK"); > + break; > + default: > + push_output_packet (p, ""); > + } > +} > + > + > + > + > +/* ------------------------------------------------------------------------ > */ > + > +/* gdb control callbacks */ > + > +#define get_proc_task(inode) get_pid_task(PROC_I((inode))->pid, PIDTYPE_PID) > + > +static int proc_gdb_open(struct inode *inode, struct file *filp) > +{ > + struct task_struct *task = get_proc_task(inode); > + int ret = -EBUSY; > + struct gdb_connection *p; > + struct list_head *l; > + > + pr_debug ("opened /proc/%d/gdb\n", task->pid); > + > + /* Reject kernel threads. */ > + if (task->flags & PF_KTHREAD) { > + ret = -EINVAL; > + goto out; > + } > + > + /* Reject if connection is for other than tg-leader thread. */ > + if (task_pid_nr(task) != task_tgid_nr(task)) { > + ret = -EINVAL; > + goto out; > + } > + > + mutex_lock (& gdb_connections_mutex); > + > + /* Reject if a connection exists for the thread group > + * leader. > + */ > + list_for_each(l, &gdb_connections) { > + p = list_entry (l, struct gdb_connection, link); > + if (p->target == task_tgid_nr(task)) { > + ret = -EBUSY; > + goto out_mutex; > + } > + } > + /* (Don't unlock yet, to defeat a race of two concurrent opens.) */ > + > + p = kzalloc(sizeof (struct gdb_connection), GFP_KERNEL); > + if (!p) { > + ret = -ENOMEM; > + goto out_mutex; > + } > + > + /* Send initial ping to gdb. */ > + push_output_packet (p, ""); > + > + mutex_init(& p->output_mutex); > + init_waitqueue_head(& p->output_wait); > + > + mutex_init(& p->input_mutex); > + init_waitqueue_head(& p->input_wait); > + > + p->target = task->tgid; > + > + /* NB: During attach, we don't want to bother the target. > + Soon though a send_sig will interrupt it. */ > + p->at_quiesce_do = UTRACE_RESUME; > + > + p->engine = utrace_attach_task(task, > + UTRACE_ATTACH_CREATE | > + UTRACE_ATTACH_EXCLUSIVE, > + &gdb_utrace_ops, > + p); > + if (IS_ERR(p->engine) || p->engine==NULL) { > + ret = -EINVAL; > + goto out_free; > + } > + > + ret = utrace_set_events(task, p->engine, > + UTRACE_EVENT_SIGNAL_ALL| > + UTRACE_EVENT(QUIESCE)| > + UTRACE_EVENT(DEATH)| > + UTRACE_EVENT(EXIT)| > + UTRACE_EVENT(EXEC)| > + UTRACE_EVENT(CLONE)); > + pr_debug ("utrace_set_events sent, ret=%d\n", ret); > + if (!ret) > + ; > + > + filp->private_data = p; > + > + INIT_LIST_HEAD(& p->link); > + list_add(&gdb_connections, &p->link); > + > + p->stop_signals ++; > + send_sig(SIGTRAP, task, 1); > +#if 0 > + ret = utrace_control(task, p->engine, UTRACE_INTERRUPT); > + if (ret == -EINPROGRESS) > + ret = utrace_barrier(task, p->engine); > +#endif > + > + goto out_mutex; > + > +out_free: > + kfree(p); > +out_mutex: > + mutex_unlock (& gdb_connections_mutex); > +out: > + return ret; > +} > + > + > +static int proc_gdb_release(struct inode *inode, struct file *filp) > +{ > + struct task_struct *task = get_proc_task(inode); > + struct gdb_connection *p = filp->private_data; > + int ret = 0; > + > + mutex_lock (& gdb_connections_mutex); > + > + if (task == NULL) { > + /* The thread is already gone; report_death was already > called. */ > + pr_debug ("gdb %d releasing old\n", p->target); > + } else { > + pr_debug ("gdb %d releasing current\n", p->target); > + > + ret = utrace_set_events(task, p->engine, 0); > + if (ret == -EINPROGRESS) > + ret = utrace_barrier(task, p->engine); > + /* No more callbacks will be received! */ > + > + ret = utrace_control(task, p->engine, UTRACE_DETACH); /* => > RESUME */ > + if (ret == -EINPROGRESS) > + ret = utrace_barrier(task, p->engine); > + > + utrace_engine_put (p->engine); > + } > + > + list_del(&p->link); > + kfree(p); > + > + mutex_unlock (& gdb_connections_mutex); > + > + return ret; > +} > + > + > + > +static int proc_gdb_ioctl(struct inode *inode, struct file *file, > + unsigned int cmd, unsigned long arg) > +{ > + /* XXX: GDB usually thinks that a file name for "target > + * remote" implies a serial port with tty-ish ioctl's > + * available. We pretend to accept them all. */ > + return 0; > +} > + > + > + > +static ssize_t proc_gdb_read(struct file *filp, char __user *buf, > + size_t count, loff_t *ppos) > +{ > + struct gdb_connection *p = filp->private_data; > + struct task_struct *task; > + int rc = 0; > + size_t len; > + > + task = find_task_by_vpid (p->target); > + if (!task) > + return -EINVAL; > + > + if ((p->output_buf_size <= p->output_buf_read) && > + filp->f_flags & O_NONBLOCK) > + return -EAGAIN; > + > +again: > + rc = wait_event_interruptible (p->output_wait, > + (p->output_buf_size > > p->output_buf_read)); > + if (rc) > + goto out; > + > + mutex_lock(&p->output_mutex); > + > + if(p->output_buf_size <= p->output_buf_read) { > + mutex_unlock(&p->output_mutex); > + goto again; > + } > + > + len = min (count, (size_t)(p->output_buf_size - p->output_buf_read)); > + if (copy_to_user (buf, & p->output_buf[p->output_buf_read], len)) { > + rc = -EFAULT; > + goto out_unlock; > + } > + > + pr_debug ("sent %u bytes (%ld left) data (%.*s)\n", > + (unsigned)len, > + ((long)p->output_buf_size-(long)p->output_buf_read)-len, > + (int)len, & p->output_buf[p->output_buf_read]); > + > + p->output_buf_read += len; > + rc = len; > + > + /* If whole packet is consumed, reset for next one. */ > + BUG_ON (p->output_buf_read > p->output_buf_size); > + if (p->output_buf_read == p->output_buf_size) { > + p->output_buf_read = 0; > + p->output_buf_size = 0; > + } > + > +out_unlock: > + mutex_unlock(&p->output_mutex); > + > +out: > + return rc; > +} > + > + > +static ssize_t proc_gdb_write(struct file *filp, const char __user *buf, > + size_t count, loff_t *ppos) > +{ > + struct gdb_connection *p = filp->private_data; > + size_t last_input_buf_size; > + struct task_struct *task; > + size_t len; > + int ret = 0; > + > + task = find_task_by_vpid (p->target); > + if (!task) > + return -EINVAL; > + > +again: > + ret = wait_event_interruptible (p->input_wait, > + (p->input_buf_size < GDB_BUFMAX)); > + if (ret) > + goto out; > + > + mutex_lock(&p->input_mutex); > + if (p->input_buf_size == GDB_BUFMAX) { > + mutex_unlock(&p->input_mutex); > + goto again; > + } > + mutex_lock(&p->output_mutex); > + > + /* We now know there is some room in the input buffer. Upon > + entry, the input_buf will either be empty, or contain a > + partial gdb request packet. */ > + > + /* Copy the data. */ > + len = min (count, (size_t)(GDB_BUFMAX - p->input_buf_size)); > + if (copy_from_user (& p->input_buf[p->input_buf_size], buf, len)) { > + ret = -EFAULT; > + goto out_unlock; > + } > + > + /* pr_debug ("received data %.*s\n", (int)len, & > p->input_buf[p->input_buf_size]); */ > + > + p->input_buf_size += len; > + ret = len; > + > + /* Process any packets in the buffer to restore the incoming > + invariant. (Normal GDB will not send more than one packet > + before waiting for a response.) */ > + > + /* We iterate until we can no longer shrink the input buffer. > Usually > + we will not iterate more than once, since there may be one +/- > + ack byte and/or one gdb packet. */ > + last_input_buf_size = 0; > + while (p->input_buf_size > + && p->input_buf_size != last_input_buf_size) { > + last_input_buf_size = p->input_buf_size; > + > + if (p->input_buf[0] == '+') { > + /* This must have been an ack to our > + * previously output packet. > + * Consume the input. > + */ > + memmove (&p->input_buf[0], &p->input_buf[1], > --p->input_buf_size); > + } else if (p->input_buf[0] == '-') { > + /* Whoops, a nak. Unfortunately, we don't > + * handle transmission errors by > + * retransmitting the last output_buf; it's > + * already gone. OTOH we should not encounter > + * transmission errors on a reliable channel > + * such as a read syscall. > + * Consume the input. > + */ > + printk(KERN_WARNING "Unexpected NAK received" > + "on /proc/%d/gdb connection.\n", > task_pid_nr(task)); > + memmove (&p->input_buf[0], &p->input_buf[1], > --p->input_buf_size); > + } else if (p->input_buf[0] == 3) { /* ^C == INTR */ > + /* NB: don't overwrite 'ret'. */ > + pr_debug ("received gdb interrupt\n"); > + p->stop_signals ++; > + send_sig(SIGTRAP, task, 1); > +#if 0 > + int rc = utrace_control(task, p->engine, > UTRACE_INTERRUPT); > + if (rc == -EINPROGRESS) > + rc = utrace_barrier(task, p->engine); > +#endif > + /* p->at_quiesce_do will be set in > report_signal(SIGNAL_REPORT) */ > + /* NB: this packet does not generate an +/- ack. > + Consume the input. */ > + memmove (&p->input_buf[0], &p->input_buf[1], > --p->input_buf_size); > + } else if (p->input_buf[0] == '$') { /* command packet */ > + int j; > + unsigned char checksum = 0; > + for (j=1; j<p->input_buf_size-2; j++) { > + if (p->input_buf[j] == '#') { > + unsigned char checksum2; > + checksum2 = byteme > (p->input_buf[j+1], > + > p->input_buf[j+2]); > + pr_debug ("received gdb packet > %.*s\n", > + j+3, & p->input_buf[0]); > + if (checksum == checksum2) { > + push_output (p, '+'); > + handle_gdb_command_packet > (p, task); > + } else { > + push_output (p, '-'); > + } > + /* Consume the whole packet. */ > + p->input_buf_size -= (j+3); > + memmove(&p->input_buf[0], > &p->input_buf[j+3], > + p->input_buf_size); > + break; > + } else { > + checksum += p->input_buf[j]; > + } > + } /* End searching for end of packet */ > + > + /* We may not have found the #<hex><hex> > + * checksum. If so, leave the partial packet > + * in input_buf. Since input_buf_size will > + * not have decreased, the while() loop above > + * will detect a fixpoint and exit. > + * > + * Alternately, there could be another gdb packet > + * just behind the one we just consumed. In this > + * we'll iterate one more time in this loop. > + */ > + } else { /* junk character */ > + printk(KERN_WARNING "Unexpected character (%x) > received" > + " on /proc/%d/gdb connection.\n", > + (int) p->input_buf[0], task_pid_nr(task)); > + /* Consume the input. */ > + memmove (&p->input_buf[0], &p->input_buf[1], > --p->input_buf_size); > + } > + } > + > +out_unlock: > + wake_up(&p->input_wait); /* Probably have more room in input_buf. */ > + wake_up(&p->output_wait); /* Probably have data in output_buf. */ > + > + mutex_unlock(&p->output_mutex); > + mutex_unlock(&p->input_mutex); > +out: > + return ret; > +} > + > + > +const struct file_operations proc_gdb_operations = { > + .open = proc_gdb_open, > + .read = proc_gdb_read, > + .write = proc_gdb_write, > + .release = proc_gdb_release, > + .ioctl = proc_gdb_ioctl, > +}; > + > +