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, +}; + +