The last posted patch lost a printsiginfo patch that used macro tricks to avoid that signal.h problem but I changed to use an opaque pointer for next_event/dispatch_event to avoid the problem. It is assumed that a gdbserver is running and the strace gdbserver backend communicates with that, typically using tcp. This is analogous to gdb/gdbserver behavior.

diff --git a/Makefile.am b/Makefile.am
index 4aa9846..1f216b7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -47,13 +47,22 @@ ARCH                = @arch@

 ACLOCAL_AMFLAGS = -I m4
 AM_CFLAGS = $(WARN_CFLAGS)
-AM_CPPFLAGS = -I$(builddir)/$(OS)/$(ARCH) \
+OS_CPPFLAGS = -I$(builddir)/$(OS)/$(ARCH) \
              -I$(srcdir)/$(OS)/$(ARCH) \
              -I$(builddir)/$(OS) \
              -I$(srcdir)/$(OS) \
              -I$(builddir) \
              -I$(srcdir)

+if ENABLE_GDBSERVER
+GDBSERVER_CPPFLAGS = -I$(builddir)/gdbserver/$(ARCH) \
+             -I$(srcdir)/gdbserver/$(ARCH) \
+             -I$(builddir)/gdbserver \
+             -I$(srcdir)/gdbserver $(OS_CPPFLAGS)
+endif
+
+AM_CPPFLAGS = $(OS_CPPFLAGS) $(GDBSERVER_CPPFLAGS)
+
 AM_CFLAGS_FOR_BUILD = $(WARN_CFLAGS_FOR_BUILD)
 AM_CPPFLAGS_FOR_BUILD = $(AM_CPPFLAGS)

@@ -325,6 +334,18 @@ strace_SOURCES =   \
        xmalloc.h       \
        # end of strace_SOURCES

+if ENABLE_GDBSERVER
+# Note that (some of) the files inside this directory are GPLv3-licensed,
+# so the package which includes them will also be tainted by GPLv3 license.
+strace_SOURCES +=              \
+       gdbserver/gdbserver.c   \
+       gdbserver/protocol.c    \
+       gdbserver/protocol.h    \
+       gdbserver/signals.def   \
+       gdbserver/signals.h     \
+       # end of gdbserver strace_SOURCES
+endif
+
 if USE_LIBUNWIND
 strace_SOURCES += unwind.c
 strace_CPPFLAGS += $(libunwind_CPPFLAGS)
@@ -873,6 +894,13 @@ EXTRA_DIST =                               \
        xlat/gen.sh                     \
        xlate.el

+if ENABLE_GDBSERVER
+EXTRA_DIST +=                                  \
+       gdbserver/x86_64/gdb_arch_defs.h        \
+       gdbserver/x86_64/gdb_get_regs.c         \
+       # End of gdbserver EXTRA_DIS
+endif
+
 .PHONY: check-valgrind-local
 check-valgrind-local:

diff --git a/README b/README
index bc70155..278c80a 100644
--- a/README
+++ b/README
@@ -3,6 +3,10 @@ This is strace, a system call tracer for Linux.
 strace is released under a Berkeley-style license at the request
 of Paul Kranenburg.

+The gdbserver feature of strace is released under the same license,
+except for a few sources copied from gdb which are required to use
+GPL version 3 or later.  See COPYING3 for details.
+
 See the file CREDITS for a list of authors and other contributors.
 See the file INSTALL for compilation and installation instructions.
 See the file NEWS for information on what has changed in recent versions.
diff --git a/configure.ac b/configure.ac
index 29285db..770c543 100644
--- a/configure.ac
+++ b/configure.ac
@@ -42,7 +42,7 @@ AC_COPYRIGHT([Copyright (c) 1999-]copyright_year[ The strace developers.])
 AC_CONFIG_SRCDIR([strace.c])
 AC_CONFIG_AUX_DIR([.])
 AC_CONFIG_HEADERS([config.h])
-AM_INIT_AUTOMAKE([foreign nostdinc dist-xz silent-rules parallel-tests 1.13]) +AM_INIT_AUTOMAKE([foreign nostdinc dist-xz silent-rules parallel-tests subdir-objects 1.13])
 AM_MAINTAINER_MODE
 AC_CANONICAL_HOST

@@ -868,6 +868,30 @@ fi
 AM_CONDITIONAL([USE_LIBUNWIND], [test "x$use_libunwind" = xyes])
 AC_MSG_RESULT([$use_libunwind])

+
+dnl gdbserver
+AC_ARG_ENABLE([gdbserver],
+             [AS_HELP_STRING([--enable-gdbserver],
+                             [enable gdbserver backend (includes GPLv3 
code)])],
+             [], [enable_gdbserver=check])
+
+use_gdbserver=no
+AS_IF([test "x$enable_gdbserver" != xno],
+       [AC_CHECK_FILE([$srcdir/gdbserver/$arch/gdb_arch_defs.h],
+               [use_gdbserver=yes],
+               [if test "x$enable_gdbserver" != xcheck; then
+ AC_MSG_FAILURE([failed to find arch-specific gdbserver definitions $srcdir/gdbserver/$arch/gdb_arch_defs.h])
+                fi])]
+)
+
+AC_MSG_CHECKING([whether to enable gdbserver backend support])
+if test "x$use_gdbserver" = xyes; then
+       AC_DEFINE([ENABLE_GDBSERVER], 1, [Enable gdbserver backend support])
+fi
+AM_CONDITIONAL([ENABLE_GDBSERVER], [test "x$use_gdbserver" = xyes])
+AC_MSG_RESULT([$use_gdbserver])
+
+
 if test "$arch" = mips && test "$no_create" != yes; then
        mkdir -p linux/mips
        if $srcdir/linux/mips/genstub.sh linux/mips; then
diff --git a/defs.h b/defs.h
index 06a4baf..3607140 100644
--- a/defs.h
+++ b/defs.h
@@ -253,6 +253,7 @@ struct tcb {
 #define TCB_TAMPERED   0x40    /* A syscall has been tampered with */
 #define TCB_HIDE_LOG   0x80    /* We should hide everything (until execve) */
#define TCB_SKIP_DETACH_ON_FIRST_EXEC 0x100 /* -b execve should skip detach on first execve */ +#define TCB_GDB_CONT_PID_TID 0x200 /* Use vCont;c:pPID.TID for for gdb backend */

 /* qualifier flags */
 #define QUAL_TRACE     0x001   /* this system call should be traced */
@@ -274,6 +275,98 @@ struct tcb {
 #define filtered(tcp)  ((tcp)->flags & TCB_FILTERED)
 #define hide_log(tcp)  ((tcp)->flags & TCB_HIDE_LOG)

+enum trace_event {
+       /* Break the main loop. */
+       TE_BREAK,
+
+       /* Call next_event() again. */
+       TE_NEXT,
+
+       /* Restart the tracee with signal 0 and call next_event() again. */
+       TE_RESTART,
+
+       /*
+        * For all the events below, current_tcp is set to current tracee's
+        * tcb.  All the suggested actions imply that you want to continue
+        * tracing of the current tracee; alternatively, you can detach it.
+        */
+
+       /*
+        * Syscall entry or exit.
+        * Restart the tracee with signal 0, or with an injected signal number.
+        */
+       TE_SYSCALL_STOP,
+
+       /*
+        * Tracee received signal with number WSTOPSIG(*pstatus); signal info
+        * is written to *si.  Restart the tracee (with that signal number
+        * if you want to deliver it).
+        */
+       TE_SIGNAL_DELIVERY_STOP,
+
+       /*
+        * Tracee was killed by a signal with number WTERMSIG(*pstatus).
+        */
+       TE_SIGNALLED,
+
+       /*
+        * Tracee was stopped by a signal with number WSTOPSIG(*pstatus).
+        * Restart the tracee with that signal number.
+        */
+       TE_GROUP_STOP,
+
+       /*
+        * Tracee exited with status WEXITSTATUS(*pstatus).
+        */
+       TE_EXITED,
+
+       /*
+        * Tracee is going to perform execve().
+        * Restart the tracee with signal 0.
+        */
+       TE_STOP_BEFORE_EXECVE,
+
+       /*
+        * Tracee is going to terminate.
+        * Restart the tracee with signal 0.
+        */
+       TE_STOP_BEFORE_EXIT,
+};
+
+/* Backend Dispatch Table */
+struct backend {
+       void (*attach_tcb) (struct tcb *tcp);
+       void (*cleanup) (void);
+       void (*detach) (struct tcb *tcp);
+       bool (*dispatch_event) (enum trace_event, int *pstatus, void *si);
+       void (*end_init) (void);
+       long (*get_regs) (pid_t pid, void *io);
+       int (*get_scno) (struct tcb *tcp);
+       int (*getfdpath) (struct tcb *tcp, int fd, char *buf, unsigned bufsize);
+       bool (*handle_arg) (char arg, char *optarg);
+       enum trace_event (*next_event) (int *pstatus, void *si);
+        bool (*prog_pid_check) (char *exec_name, int nprocs);
+       bool (*start_init) (void);
+       void (*startup_child) (char **argv);
+       long (*set_regs) (pid_t pid, void *io);
+       bool (*trace) (void);
+ int (*umoven) (struct tcb *const tcp, kernel_ulong_t addr, unsigned int len,
+              void *const our_addr);
+ int (*umovestr) (struct tcb *const tcp, kernel_ulong_t addr, unsigned int len, char *laddr);
+       int (*upeek_) (int pid, unsigned long off, kernel_ulong_t *res);
+       int (*upoke_) (int pid, unsigned long off, kernel_ulong_t val);
+ bool (*verify_args) (const char *username, bool daemon, unsigned int *follow_fork);
+       void *data;
+} backend;
+
+
+extern int upeek(int pid, unsigned long, kernel_ulong_t *);
+extern int upoke(int pid, unsigned long, kernel_ulong_t);
+extern bool gdb_handle_arg (char arg, char *optarg);
+
+#define upeek(pid,off,res) backend.upeek_(pid, off, res)
+#define upoke(pid,off,val) backend.upoke_(pid, off, val)
+
 #include "xlat.h"

 extern const struct xlat addrfams[];
@@ -394,6 +487,8 @@ extern int read_int_from_file(const char *, int *);
 extern void set_sortby(const char *);
 extern void set_overhead(int);
 extern void print_pc(struct tcb *);
+extern int trace_syscall(struct tcb *, unsigned int *);
+extern void update_personality(struct tcb *tcp, unsigned int personality);

 extern int syscall_entering_decode(struct tcb *);
 extern int syscall_entering_trace(struct tcb *, unsigned int *);
@@ -428,6 +523,7 @@ extern void *get_tcb_priv_data(const struct tcb *);
 extern int set_tcb_priv_data(struct tcb *, void *priv_data,
                             void (*free_priv_data)(void *));
 extern void free_tcb_priv_data(struct tcb *);
+extern struct tcb *pid2tcb(int pid);

 static inline unsigned long get_tcb_priv_ulong(const struct tcb *tcp)
 {
@@ -442,7 +538,7 @@ static inline int set_tcb_priv_ulong(struct tcb *tcp, unsigned long val)
 extern int
 umoven(struct tcb *, kernel_ulong_t addr, unsigned int len, void *laddr);
 #define umove(pid, addr, objp) \
-       umoven((pid), (addr), sizeof(*(objp)), (void *) (objp))
+       backend.umoven((pid), (addr), sizeof(*(objp)), (void *) (objp))

 extern int
 umoven_or_printaddr(struct tcb *, kernel_ulong_t addr,
@@ -457,9 +553,6 @@ umoven_or_printaddr_ignore_syserror(struct tcb *, kernel_ulong_t addr,
 extern int
umovestr(struct tcb *, kernel_ulong_t addr, unsigned int len, char *laddr);

-extern int upeek(int pid, unsigned long, kernel_ulong_t *);
-extern int upoke(int pid, unsigned long, kernel_ulong_t);
-
 extern bool
 print_array(struct tcb *,
            kernel_ulong_t start_addr,
diff --git a/desc.c b/desc.c
index 52e58c8..6472a94 100644
--- a/desc.c
+++ b/desc.c
@@ -148,7 +148,7 @@ decode_select(struct tcb *const tcp, const kernel_ulong_t *const args,
                        int first = 1;

                        addr = args[i+1];
-                       if (!addr || !fds || umoven(tcp, addr, fdsize, fds) < 0)
+                       if (!addr || !fds || backend.umoven(tcp, addr, fdsize, 
fds) < 0)
                                continue;
                        for (j = 0;; j++) {
                                j = next_set_bit(fds, j, nfds);
diff --git a/dirent.c b/dirent.c
index 2ab626b..52be6be 100644
--- a/dirent.c
+++ b/dirent.c
@@ -101,7 +101,7 @@ SYS_FUNC(getdents)

        if (len) {
                buf = malloc(len);
-               if (!buf || umoven(tcp, tcp->u_arg[1], len, buf) < 0) {
+               if (!buf || backend.umoven(tcp, tcp->u_arg[1], len, buf) < 0) {
                        tprints(", ");
                        printaddr(tcp->u_arg[1]);
                        tprintf(", %u", count);
diff --git a/dirent64.c b/dirent64.c
index 4172d64..8927b9f 100644
--- a/dirent64.c
+++ b/dirent64.c
@@ -69,7 +69,7 @@ SYS_FUNC(getdents64)

        if (len) {
                buf = malloc(len);
-               if (!buf || umoven(tcp, tcp->u_arg[1], len, buf) < 0) {
+               if (!buf || backend.umoven(tcp, tcp->u_arg[1], len, buf) < 0) {
                        tprints(", ");
                        printaddr(tcp->u_arg[1]);
                        tprintf(", %u", count);
diff --git a/dm.c b/dm.c
index 28863a8..1e9bd53 100644
--- a/dm.c
+++ b/dm.c
@@ -455,7 +455,7 @@ dm_known_ioctl(struct tcb *const tcp, const unsigned int code,
                ioc = alloca(sizeof(*ioc));
        }

-       if ((umoven(tcp, arg, offsetof(struct dm_ioctl, data), ioc) < 0) ||
+ if ((backend.umoven(tcp, arg, offsetof(struct dm_ioctl, data), ioc) < 0) ||
            (ioc->data_size < offsetof(struct dm_ioctl, data_size))) {
                if (entering(tcp))
                        free(ioc);
diff --git a/execve.c b/execve.c
index 5455b15..f40e142 100644
--- a/execve.c
+++ b/execve.c
@@ -54,7 +54,7 @@ printargv(struct tcb *const tcp, kernel_ulong_t addr)
                        char data[sizeof(kernel_ulong_t)];
                } cp;

-               if (umoven(tcp, addr, wordsize, cp.data)) {
+               if (backend.umoven(tcp, addr, wordsize, cp.data)) {
                        if (sep == start_sep)
                                printaddr(addr);
                        else
@@ -89,7 +89,7 @@ printargc(struct tcb *const tcp, kernel_ulong_t addr)
        char *cp = NULL;

        for (; addr; addr += current_wordsize, ++count) {
-               if (umoven(tcp, addr, current_wordsize, &cp)) {
+               if (backend.umoven(tcp, addr, current_wordsize, &cp)) {
                        if (!count)
                                return;

diff --git a/file_handle.c b/file_handle.c
index 46a61c5..e1b21ac 100644
--- a/file_handle.c
+++ b/file_handle.c
@@ -84,7 +84,7 @@ SYS_FUNC(name_to_handle_at)
                                tprintf(", handle_type=%d", h.handle_type);
                                if (h.handle_bytes > MAX_HANDLE_SZ)
                                        h.handle_bytes = MAX_HANDLE_SZ;
-                               if (!umoven(tcp, addr + sizeof(h), 
h.handle_bytes,
+                               if (!backend.umoven(tcp, addr + sizeof(h), 
h.handle_bytes,
                                            f_handle)) {
                                        tprints(", f_handle=0x");
                                        for (i = 0; i < h.handle_bytes; ++i)
@@ -121,7 +121,7 @@ SYS_FUNC(open_by_handle_at)
                        h.handle_bytes, h.handle_type);
                if (h.handle_bytes > MAX_HANDLE_SZ)
                        h.handle_bytes = MAX_HANDLE_SZ;
-               if (!umoven(tcp, addr + sizeof(h), h.handle_bytes, &f_handle)) {
+               if (!backend.umoven(tcp, addr + sizeof(h), h.handle_bytes, 
&f_handle)) {
                        unsigned int i;

                        tprints(", f_handle=0x");
diff --git a/gdbserver/gdbserver.c b/gdbserver/gdbserver.c
new file mode 100644
index 0000000..68b8266
--- /dev/null
+++ b/gdbserver/gdbserver.c
@@ -0,0 +1,1223 @@
+ /* Implementation of strace features over the GDB remote protocol.
+ *
+ * Copyright (c) 2015, 2016 Red Hat Inc.
+ * Copyright (c) 2015 Josh Stone <cuvi...@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "defs.h"
+
+#define _GNU_SOURCE 1
+#include <stdlib.h>
+#include <sys/wait.h>
+
+#include "gdb_arch_defs.h"
+#include "protocol.h"
+#include "scno.h"
+#include "signals.h"
+
+/* FIXME jistone: export hacks */
+struct tcb *pid2tcb(int pid);
+struct tcb *alloctcb(int pid);
+void droptcb(struct tcb *tcp);
+void newoutf(struct tcb *tcp);
+void print_signalled(struct tcb *tcp, const int pid, int status);
+void print_exited(struct tcb *tcp, const int pid, int status);
+void print_stopped(struct tcb *tcp, const siginfo_t *si, const unsigned int sig); +void set_sigaction(int signo, void (*sighandler)(int), struct sigaction *oldact);
+
+struct tcb *current_tcp;
+int strace_child;
+int detach_on_execve;
+static volatile int interrupted;
+char *gdbserver = NULL;
+static int general_pid; /* process id that gdbserver is focused on */
+static int general_tid; /* thread id that gdbserver is focused on */
+static struct gdb_stop_reply stop;
+static struct gdb_conn* gdb = NULL;
+static bool gdb_extended = false;
+static bool gdb_multiprocess = false;
+static bool gdb_vcont = false;
+static bool gdb_nonstop = false;
+
+static const char * const gdb_signal_names[] = {
+#define SET(symbol, constant, name, string) \
+       [constant] = name,
+#include "signals.def"
+#undef SET
+};
+
+static int gdb_signal_map[SUPPORTED_PERSONALITIES][GDB_SIGNAL_LAST];
+
+enum gdb_stop {
+       gdb_stop_unknown, /* O or F or anything else */
+       gdb_stop_error, /* E */
+       gdb_stop_signal, /* S or T */
+       gdb_stop_exited, /* W */
+       gdb_stop_terminated, /* X */
+
+       /* specific variants of gdb_stop_signal 05 */
+       gdb_stop_trap, /* missing or unrecognized stop reason */
+       gdb_stop_syscall_entry,
+       gdb_stop_syscall_return,
+};
+
+
+struct gdb_stop_reply {
+       char *reply;
+       size_t size;
+
+       enum gdb_stop type;
+       int code; /* error, signal, exit status, scno */
+       int pid; /* process id, aka kernel tgid */
+       int tid; /* thread id, aka kernel tid */
+};
+
+static int
+gdb_map_signal(unsigned int gdb_sig) {
+       /* strace "SIG_0" vs. gdb "0" -- it's all zero */
+       if (gdb_sig == GDB_SIGNAL_0)
+               return 0;
+
+       /* real-time signals are "special", not even fully contiguous */
+       if (gdb_sig == GDB_SIGNAL_REALTIME_32)
+               return 32;
+       if (GDB_SIGNAL_REALTIME_33 <= gdb_sig &&
+                       gdb_sig <= GDB_SIGNAL_REALTIME_63)
+               return gdb_sig - GDB_SIGNAL_REALTIME_33 + 33;
+       if (GDB_SIGNAL_REALTIME_64 <= gdb_sig &&
+                       gdb_sig <= GDB_SIGNAL_REALTIME_127)
+               return gdb_sig - GDB_SIGNAL_REALTIME_64 + 64;
+
+       const char *gdb_signame = gdb_signal_names[gdb_sig];
+       if (!gdb_signame)
+               return -1;
+
+       /* many of the other signals line up, but not all. */
+       if (gdb_sig < nsignals && !strcmp(gdb_signame, signame(gdb_sig)))
+               return gdb_sig;
+
+       /* scan the rest for a match */
+       unsigned int sig;
+       for (sig = 1; sig < nsignals; ++sig) {
+               if (sig == gdb_sig)
+                       continue;
+               if (!strcmp(gdb_signame, signame(sig)))
+                       return sig;
+       }
+
+       return -1;
+}
+
+static void
+gdb_signal_map_init(void)
+{
+       unsigned int pers, old_pers = current_personality;
+
+       for (pers = 0; pers < SUPPORTED_PERSONALITIES; ++pers) {
+               if (current_personality != pers)
+                       set_personality(pers);
+
+               unsigned int gdb_sig;
+               int *map = gdb_signal_map[pers];
+               for (gdb_sig = 0; gdb_sig < GDB_SIGNAL_LAST; ++gdb_sig)
+                       map[gdb_sig] = gdb_map_signal(gdb_sig);
+       }
+
+       if (old_pers != current_personality)
+               set_personality(old_pers);
+}
+
+static int
+gdb_signal_to_target(struct tcb *tcp, unsigned int signal)
+{
+       unsigned int pers = tcp->currpers;
+       if (pers < SUPPORTED_PERSONALITIES && signal < GDB_SIGNAL_LAST)
+               return gdb_signal_map[pers][signal];
+       return -1;
+}
+
+static void
+gdb_parse_thread(const char *id, int *pid, int *tid)
+{
+       if (*id == 'p') {
+               /* pPID or pPID.TID */
+               ++id;
+               *pid = gdb_decode_hex_str(id);
+
+               /* stop messages should always have the TID, */
+               /* but if not, just use the PID. */
+               char *dot = strchr(id, '.');
+               if (!dot) {
+                       *tid = *pid;
+               } else {
+                       *tid = gdb_decode_hex_str(dot + 1);
+               }
+       } else {
+               /* just TID, assume same PID */
+               *tid = gdb_decode_hex_str(id);
+               *pid = *tid;
+       }
+}
+
+static void
+gdb_recv_signal(struct gdb_stop_reply *stop)
+{
+       char *reply = stop->reply;
+
+       stop->code = gdb_decode_hex_n(&reply[1], 2);
+       stop->type = (stop->code == GDB_SIGNAL_TRAP ||
+                       stop->code == GDB_SIGNAL_0)
+               ? gdb_stop_trap : gdb_stop_signal;
+
+       /* tokenize the n:r pairs */
+       char *info = strdupa(reply + 3);
+       char *savetok = NULL, *nr;
+       for (nr = strtok_r(info, ";", &savetok); nr;
+                       nr = strtok_r(NULL, ";", &savetok)) {
+               char *n = strtok(nr, ":");
+               char *r = strtok(NULL, "");
+               if (!n || !r)
+                       continue;
+
+               if (!strcmp(n, "thread")) {
+                       if (stop->pid == -1) {
+                               gdb_parse_thread(r, &stop->pid, &stop->tid);
+                               general_pid = stop->pid;
+                               general_tid = stop->tid;
+                       }
+                       else
+                               /* an optional 2nd thread component is the */
+                               /* thread that gdbserver is focused on */
+                               gdb_parse_thread(r, &general_pid, &general_tid);
+               }
+               else if (!strcmp(n, "syscall_entry")) {
+                       if (stop->type == gdb_stop_trap) {
+                               stop->type = gdb_stop_syscall_entry;
+                               stop->code = gdb_decode_hex_str(r);
+                       }
+               }
+               else if (!strcmp(n, "syscall_return")) {
+                       if (stop->type == gdb_stop_trap) {
+                               stop->type = gdb_stop_syscall_return;
+                               stop->code = gdb_decode_hex_str(r);
+                       }
+               }
+       }
+
+       /* TODO guess architecture by the size of reported registers? */
+}
+
+static void
+gdb_recv_exit(struct gdb_stop_reply *stop)
+{
+       char *reply = stop->reply;
+
+       stop->type = reply[0] == 'W' ?
+               gdb_stop_exited : gdb_stop_terminated;
+       stop->code = gdb_decode_hex_str(&reply[1]);
+
+       const char *process = strstr(reply, ";process:");
+       if (process) {
+               stop->pid = gdb_decode_hex_str(process + 9);
+
+               /* we don't really know the tid, so just use PID for now */
+               /* XXX should exits enumerate all threads we know of a process? 
*/
+               stop->tid = stop->pid;
+       }
+}
+
+static struct gdb_stop_reply
+gdb_recv_stop(struct gdb_stop_reply *stop_reply)
+{
+       struct gdb_stop_reply stop = {
+               .reply = NULL,
+               .size = 0,
+
+               .type = gdb_stop_unknown,
+               .code = -1,
+               .pid = -1,
+               .tid = -1,
+       };
+       char *reply = NULL;
+       size_t stop_size;
+
+
+       if (stop_reply)
+               /* pop_notification gave us a cached notification */
+           stop = *stop_reply;
+       else
+           stop.reply = gdb_recv(gdb, &stop.size, true);
+
+       if (debug_flag)
+       {
+               error_msg("%s\n", stop.reply);
+               fflush(stdout);
+       }
+
+       if (gdb_has_non_stop(gdb) && !stop_reply) {
+           /* non-stop packet order:
+              client sends: $vCont;c
+              server sends: OK
+              server sends: %Stop:T05syscall_entry (possibly out of order)
+              client sends: $vStopped
+              server possibly sends 0 or more: T05syscall_entry
+              client sends to each: $vStopped
+              server sends: OK
+           */
+            /* Do we have an out of order notification?  (see gdb_recv) */
+            reply = pop_notification(&stop_size);
+            if (reply) {
+                 if (debug_flag)
+                      error_msg("popped %s\n", reply);
+                 stop.reply = reply;
+                 reply = gdb_recv(gdb, &stop_size, false); /* vContc OK */
+            }
+            else {
+                    while (stop.reply[0] != 'T' && stop.reply[0] != 'W')
+                            stop.reply = gdb_recv(gdb, &stop.size, true);
+            }
+       }
+       if (gdb_has_non_stop(gdb) && (stop.reply[0] == 'T')) {
+               do {
+                       size_t this_size;
+                       gdb_send(gdb,"vStopped",8);
+                       reply = gdb_recv(gdb, &this_size, true);
+                       if (strcmp(reply, "OK") == 0)
+                               break;
+                       push_notification(reply, this_size);
+               } while (true);
+       }
+
+       /* all good packets are at least 3 bytes */
+       switch (stop.size >= 3 ? stop.reply[0] : 0) {
+       case 'E':
+               stop.type = gdb_stop_error;
+               stop.code = gdb_decode_hex_n(stop.reply + 1, 2);
+               break;
+       case 'S':
+       case 'T':
+               gdb_recv_signal(&stop);
+               break;
+       case 'W':
+       case 'X':
+               gdb_recv_exit(&stop);
+               break;
+       default:
+               stop.type = gdb_stop_unknown;
+               break;
+       }
+
+       return stop;
+}
+
+static bool
+gdb_ok(void)
+{
+       size_t size;
+       char *reply = gdb_recv(gdb, &size, false);
+       bool ok = size == 2 && !strcmp(reply, "OK");
+       free(reply);
+       return ok;
+}
+
+
+bool
+gdb_prog_pid_check (char *exec_name, int nprocs)
+{
+ /* under gdbserver, we can reasonably allow having neither to use existing targets. */
+       if (!exec_name && !nprocs && !gdbserver)
+               return false;
+       return true;
+}
+
+
+bool
+gdb_start_init(void)
+{
+# if ! defined X86_64
+       error_msg("-G is not supported on this target.");
+       return false;           /* Only supported on x86_64 */
+# endif
+
+       gdb_signal_map_init();
+
+       if (gdbserver[0] == '|')
+               gdb = gdb_begin_command(gdbserver + 1);
+       else if (strchr(gdbserver, ':') && !strchr(gdbserver, '/')) {
+               if (strchr(gdbserver, ';')) {
+                       const char *stop_option;
+                       gdbserver = strtok(gdbserver, ";");
+                       stop_option = strtok(NULL, "");
+                       stop_option += strspn(" ", stop_option);
+                       if (!strcmp(stop_option, "non-stop"))
+                               gdb_nonstop = true;
+               }
+               const char *node = strtok(gdbserver, ":");
+               const char *service = strtok(NULL, "");
+               gdb = gdb_begin_tcp(node, service);
+       } else
+               gdb = gdb_begin_path(gdbserver);
+
+       if (!gdb_start_noack(gdb))
+               error_msg("couldn't enable GDB server noack mode");
+
+       char multi_cmd[] = "qSupported:multiprocess+;QThreadEvents+"
+               ";fork-events+;vfork-events+;exec-events+";
+
+       sprintf(multi_cmd, "qSupported:multiprocess+;QThreadEvents+;%s%s",
+                       followfork ? ";fork-events+;vfork-events+" : "",
+                       detach_on_execve ? ";exec-events" : "");
+
+       gdb_send(gdb, multi_cmd, sizeof(multi_cmd) - 1);
+
+       size_t size;
+       bool gdb_fork;
+       char *reply = gdb_recv(gdb, &size, false);
+       gdb_multiprocess = strstr(reply, "multiprocess+") != NULL;
+       if (!gdb_multiprocess)
+               error_msg("couldn't enable GDB server multiprocess mode");
+       if (followfork) {
+               gdb_fork = strstr(reply, "fork-events+") != NULL;
+               if (!gdb_fork)
+                       error_msg("couldn't enable GDB server fork events 
handling");
+               gdb_fork = strstr(reply, "vfork-events+") != NULL;
+               if (!gdb_fork)
+                       error_msg("couldn't enable GDB server vfork events 
handling");
+       }
+       if (!detach_on_execve) {
+               if (!strstr(reply, "exec-events+"))
+                       error_msg("couldn't enable GDB server exec events 
handling");
+       }
+       free(reply);
+
+       static const char extended_cmd[] = "!";
+       gdb_send(gdb, extended_cmd, sizeof(extended_cmd) - 1);
+       gdb_extended = gdb_ok();
+       if (!gdb_extended)
+               error_msg("couldn't enable GDB server extended mode");
+
+ static const char pass_signals[] = "QPassSignals:e;10;14;17;1a;1b;1c;21;24;25;2c;4c;97;";
+       gdb_send(gdb, pass_signals, sizeof(pass_signals) - 1);
+       if (!gdb_ok())
+               error_msg("couldn't enable GDB server signal passing");
+
+ static const char program_signals[] = "QProgramSignals:0;1;3;4;6;7;8;9;a;b;c;d;e;f;10;11;12;13;14;15;16;17;18;19;1a;1b;1c;1d;1e;1f;20;21;22;23;24;25;26;27;28;29;2a;2b;2c;2d;2e;2f;30;31;32;33;34;35;36;37;38;39;3a;3b;3c;3d;3e;3f;40;41;42;43;44;45;46;47;48;49;4a;4b;4c;4d;4e;4f;50;51;52;53;54;55;56;57;58;59;5a;5b;5c;5d;5e;5f;60;61;62;63;64;65;66;67;68;69;6a;6b;6c;6d;6e;6f;70;71;72;73;74;75;76;77;78;79;7a;7b;7c;7d;7e;7f;80;81;82;83;84;85;86;87;88;89;8a;8b;8c;8d;8e;8f;90;91;92;93;94;95;96;97;";
+       gdb_send(gdb, program_signals, sizeof(program_signals) - 1);
+       if (!gdb_ok())
+               error_msg("couldn't enable GDB server signal passing");
+
+       static const char vcont_cmd[] = "vCont?";
+       gdb_send(gdb, vcont_cmd, sizeof(vcont_cmd) - 1);
+       reply = gdb_recv(gdb, &size, false);
+       gdb_vcont = strncmp(reply, "vCont", 5) == 0;
+       if (!gdb_vcont)
+               error_msg("GDB server doesn't support vCont");
+       free(reply);
+       return true;
+}
+
+
+static void
+gdb_init_syscalls(void)
+{
+       static const char syscall_cmd[] = "QCatchSyscalls:1";
+       const char *syscall_set = "";
+       bool want_syscall_set = false;
+       unsigned sci;
+
+       /* Only send syscall list if a filtered list was given with -e */
+       for (sci = 0; sci < nsyscalls; sci++)
+               if (! (qual_flags(sci) & QUAL_TRACE)) {
+                       want_syscall_set = true;
+                       break;
+               }
+
+       for (sci = 0; want_syscall_set && sci < nsyscalls; sci++)
+               if (qual_flags(sci) & QUAL_TRACE)
+                       if (asprintf ((char**)&syscall_set, "%s;%x", syscall_set, 
sci) < 0)
+                               error_msg("couldn't enable GDB server syscall 
catching");
+
+       if (want_syscall_set)
+               asprintf ((char**)&syscall_set, "%s%s", syscall_cmd, 
syscall_set);
+       else
+               syscall_set = syscall_cmd;
+       gdb_send(gdb, syscall_set, strlen(syscall_set));
+       if (!gdb_ok())
+               error_msg("couldn't enable GDB server syscall catching");
+}
+
+static struct tcb*
+gdb_find_thread(int tid, bool current)
+{
+       if (tid < 0)
+               return NULL;
+
+       /* Look up 'tid' in our table. */
+       struct tcb *tcp = pid2tcb(tid);
+       if (!tcp) {
+               tcp = alloctcb(tid);
+               tcp->flags |= TCB_GDB_CONT_PID_TID;
+               tcp->flags |= TCB_ATTACHED | TCB_STARTUP;
+               newoutf(tcp);
+
+               if (!current) {
+                       char cmd[] = "Hgxxxxxxxx";
+                       sprintf(cmd, "Hg%x", tid);
+                       gdb_send(gdb, cmd, strlen(cmd));
+                       current = gdb_ok();
+                       if (!current)
+                               error_msg("couldn't set GDB server to thread 
%d", tid);
+               }
+       }
+       return tcp;
+}
+
+static void
+gdb_enumerate_threads(void)
+{
+       /* qfThreadInfo [qsThreadInfo]...
+        * -> m thread
+        * -> m thread,thread
+        * -> l (finished) */
+
+       static const char qfcmd[] = "qfThreadInfo";
+
+       gdb_send(gdb, qfcmd, sizeof(qfcmd) - 1);
+
+       size_t size;
+       char *reply = gdb_recv(gdb, &size, false);
+       while (reply[0] == 'm') {
+               char *thread;
+               for (thread = strtok(reply + 1, ","); thread;
+                               thread = strtok(NULL, "")) {
+                       int pid, tid;
+                       gdb_parse_thread(thread, &pid, &tid);
+
+                       struct tcb *tcp = gdb_find_thread(tid, false);
+                       if (tcp && !current_tcp)
+                               current_tcp = tcp;
+               }
+
+               free(reply);
+
+               static const char qscmd[] = "qsThreadInfo";
+               gdb_send(gdb, qscmd, sizeof(qscmd) - 1);
+               reply = gdb_recv(gdb, &size, false);
+       }
+
+       free(reply);
+}
+
+static void
+interrupt(int sig)
+{
+       interrupted = sig;
+}
+
+void
+gdb_end_init(void)
+{
+       /* TODO interface with -I? */
+       set_sigaction(SIGHUP, interrupt, NULL);
+       set_sigaction(SIGINT, interrupt, NULL);
+       set_sigaction(SIGQUIT, interrupt, NULL);
+       set_sigaction(SIGPIPE, interrupt, NULL);
+       set_sigaction(SIGTERM, interrupt, NULL);
+
+       /* We enumerate all attached threads to be sure, especially
+        * since we get all threads on vAttach, not just the one
+        * pid. */
+       gdb_enumerate_threads();
+
+       /* Everything was stopped from startup_child/startup_attach,
+        * now continue them all so the next reply will be a stop
+        * packet */
+       if (gdb_vcont) {
+               static const char cmd[] = "vCont;c";
+               gdb_send(gdb, cmd, sizeof(cmd) - 1);
+       } else {
+               static const char cmd[] = "c";
+               gdb_send(gdb, cmd, sizeof(cmd) - 1);
+       }
+}
+
+void
+gdb_cleanup(void)
+{
+       if (gdb)
+               gdb_end(gdb);
+       gdb = NULL;
+}
+
+void
+gdb_startup_child(char **argv)
+{
+       if (!gdb)
+               error_msg_and_die("GDB server not connected!");
+
+       if (!gdb_extended)
+               error_msg_and_die("GDB server doesn't support starting 
processes!");
+
+       /* Without knowing gdb's current tid, vCont of the correct thread for
+          the multithreaded nonstop case is difficult, so default to all-stop 
*/
+
+       size_t i;
+       size_t size = 4; /*vRun */
+       for (i = 0; argv[i]; ++i) {
+               size += 1 + 2 * strlen(argv[i]); /*;hexified-argument */
+       }
+
+       if (gdb_nonstop) {
+               static const char nonstop_cmd[] = "QNonStop:1";
+               gdb_send(gdb, nonstop_cmd, sizeof(nonstop_cmd) - 1);
+               if (!gdb_ok())
+                       gdb_nonstop = false;
+       }
+
+       {
+               char cmd[size];
+               char *cmd_ptr = cmd;
+               memcpy(cmd_ptr, "vRun", 4);
+               cmd_ptr += 4;
+               for (i = 0; argv[i]; ++i) {
+                       *cmd_ptr++ = ';';
+                       const char *arg = argv[i];
+                       while (*arg) {
+                               gdb_encode_hex(*arg++, cmd_ptr);
+                               cmd_ptr += 2;
+                       }
+               }
+
+               gdb_send(gdb, cmd, size);
+       }
+
+       struct gdb_stop_reply stop = gdb_recv_stop(NULL);
+       if (stop.size == 0)
+               error_msg_and_die("GDB server doesn't support vRun!");
+       switch (stop.type) {
+       case gdb_stop_error:
+               error_msg_and_die("GDB server failed vRun with %.*s",
+                               (int)stop.size, stop.reply);
+       case gdb_stop_trap:
+               break;
+       default:
+               error_msg_and_die("GDB server expected vRun trap, got: %.*s",
+                               (int)stop.size, stop.reply);
+       }
+
+       pid_t tid = stop.tid;
+       free(stop.reply);
+
+       strace_child = tid;
+
+       struct tcb *tcp = alloctcb(tid);
+       tcp->flags |= TCB_ATTACHED | TCB_STARTUP;
+       newoutf(tcp);
+       gdb_init_syscalls();
+
+       if (gdb_nonstop)
+               gdb_set_non_stop(gdb, true);
+       else
+               gdb_set_non_stop(gdb, false);
+       /* TODO normal strace attaches right before exec, so the first
+        * syscall seen is the execve with all its arguments.  Need to
+        * emulate that here? */
+       tcp->flags &= ~TCB_HIDE_LOG;
+}
+
+void
+gdb_attach_tcb(struct tcb *tcp)
+{
+       if (!gdb)
+               error_msg_and_die("GDB server not connected!");
+
+       if (!gdb_extended)
+               error_msg_and_die("GDB server doesn't support attaching "
+                               "processes");
+
+       struct gdb_stop_reply stop;
+       static const char nonstop_cmd[] = "QNonStop:1";
+       char vattach_cmd[] = "vAttach;XXXXXXXX";
+
+       gdb_send(gdb, nonstop_cmd, sizeof(nonstop_cmd) - 1);
+       if (gdb_ok())
+              gdb_set_non_stop(gdb, true);
+
+       sprintf(vattach_cmd, "vAttach;%x", tcp->pid);
+       gdb_send(gdb, vattach_cmd, strlen(vattach_cmd));
+
+       do {
+               /*
+                 non-stop packet order:
+                 client sends: vCont;t
+                 server sends: OK
+                 server sends: Stop:T05swbreak:;
+                 client sends: vStopped
+                 [ server sends: T05swbreak:;
+                   client sends: vStopped ]
+                 server sends: OK
+               */
+               char h_cmd[] = "Hgxxxxxxxx";
+               char vcont_cmd[] = "vCont;t:pXXXXXXXX";
+               if (!gdb_ok()) {
+                    stop.type = gdb_stop_unknown;
+                    break;
+               }
+               sprintf(h_cmd, "Hg%x.-1", tcp->pid);
+               gdb_send(gdb, h_cmd, strlen(h_cmd));
+               if (!gdb_ok()) {
+                    stop.type = gdb_stop_unknown;
+                    break;
+               }
+               sprintf(vcont_cmd, "vCont;t:p%x.-1", tcp->pid);
+               gdb_send(gdb, vcont_cmd, strlen(vcont_cmd));
+               stop = gdb_recv_stop(NULL);
+       } while (0);
+
+       if (stop.type == gdb_stop_unknown) {
+               static const char nonstop_cmd[] = "QNonStop:0";
+               gdb_send(gdb, nonstop_cmd, sizeof(nonstop_cmd) - 1);
+               if (gdb_ok())
+                       gdb_set_non_stop(gdb, false);
+               else
+                       error_msg_and_die("Cannot connect to process %d: "
+                                       "GDB server doesn't support vAttach!",
+                                       tcp->pid);
+               gdb_send(gdb, vattach_cmd, strlen(vattach_cmd));
+               stop = gdb_recv_stop(NULL);
+               if (stop.size == 0)
+                       error_msg_and_die("Cannot connect to process %d: "
+                                       "GDB server doesn't support vAttach!",
+                                       tcp->pid);
+               switch (stop.type) {
+               case gdb_stop_error:
+                       error_msg_and_die("Cannot connect to process %d: "
+                                       "GDB server failed vAttach with %.*s",
+                                       tcp->pid, (int)stop.size, stop.reply);
+               case gdb_stop_trap:
+                       break;
+               case gdb_stop_signal:
+                       if (stop.code == 0)
+                               break;
+                       /* fallthrough */
+               default:
+                       error_msg_and_die("Cannot connect to process %d: "
+                                       "GDB server expected vAttach trap, got: 
%.*s",
+                                         tcp->pid, (int)stop.size, stop.reply);
+           }
+         }
+
+       pid_t tid = stop.tid;
+       free(stop.reply);
+
+       if (tid != tcp->pid) {
+               droptcb(tcp);
+               tcp = alloctcb(tid);
+       }
+       tcp->flags |= TCB_ATTACHED | TCB_STARTUP;
+       newoutf(tcp);
+       gdb_init_syscalls();
+
+       if (!qflag)
+               fprintf(stderr, "Process %u attached in %s mode\n", tcp->pid,
+                       gdb_has_non_stop(gdb) ? "non-stop" : "all-stop");
+}
+
+void
+gdb_detach(struct tcb *tcp)
+{
+       static bool already_detaching = false;
+
+       if (! already_detaching)
+               already_detaching = true;
+       if (already_detaching || gdb == NULL)
+               return;
+       if (gdb_multiprocess) {
+               char cmd[] = "D;XXXXXXXX";
+               sprintf(cmd, "D;%x", tcp->pid);
+               gdb_send(gdb, cmd, strlen(cmd));
+       } else {
+               static const char cmd[] = "D";
+               gdb_send(gdb, cmd, sizeof(cmd) - 1);
+       }
+
+       if (!gdb_ok()) {
+               /* is it still alive? */
+               char cmd[] = "T;XXXXXXXX";
+               sprintf(cmd, "T;%x", tcp->pid);
+               gdb_send(gdb, cmd, strlen(cmd));
+               if (gdb_ok())
+                       error_msg("GDB server failed to detach %d", tcp->pid);
+               /* otherwise it's dead, or already detached, fine. */
+       }
+
+       if (!qflag && (tcp->flags & TCB_ATTACHED))
+               error_msg("Process %u detached", tcp->pid);
+
+       droptcb(tcp);
+}
+
+
+enum trace_event
+gdb_next_event(int *pstatus, siginfo_t *si)
+{
+       int gdb_sig = 0;
+       pid_t tid;
+       struct tcb *tcp = NULL;
+
+       if (interrupted)
+               return TE_BREAK;
+
+       stop.reply = pop_notification(&stop.size);
+       if (stop.reply)     /* cached out of order notification? */
+               stop = gdb_recv_stop(&stop);
+       else
+               stop = gdb_recv_stop(NULL);
+       if (stop.size == 0)
+               error_msg_and_die("GDB server gave an empty stop reply!?");
+
+       switch (stop.type) {
+       case gdb_stop_unknown:
+               error_msg_and_die("GDB server stop reply unknown: %.*s",
+                               (int)stop.size, stop.reply);
+               break;
+       case gdb_stop_error:
+               /* vCont error -> no more processes */
+               free(stop.reply);
+               return TE_BREAK;
+       default:
+               break;
+       }
+
+
+       tid = -1;
+       tcp = NULL;
+
+       if (gdb_multiprocess) {
+               tid = stop.tid;
+               tcp = gdb_find_thread(tid, true);
+               /* Set current output file */
+               current_tcp = tcp;
+       } else if (current_tcp) {
+               tcp = current_tcp;
+               tid = tcp->pid;
+       }
+       if (tid < 0 || tcp == NULL)
+               error_msg_and_die("couldn't read tid from stop reply: %.*s",
+                               (int)stop.size, stop.reply);
+
+       switch (stop.type) {
+       case gdb_stop_exited:
+               *pstatus = W_EXITCODE(stop.code, 0);
+               return TE_EXITED;
+
+       case gdb_stop_terminated:
+               *pstatus = W_EXITCODE(0, gdb_signal_to_target(tcp, stop.code));
+               return TE_SIGNALLED;
+
+       case gdb_stop_unknown:  /* already handled above */
+       case gdb_stop_error:    /* already handled above */
+       case gdb_stop_trap:     /* misc trap */
+               break;
+       case gdb_stop_syscall_entry:
+               /* If we thought we were already in a syscall --
+                * missed a return? -- skipping this report doesn't do
+                * much good.  Might as well force it to be a new
+                * entry regardless to sync up. */
+               tcp->flags &= ~TCB_INSYSCALL;
+               tcp->scno = stop.code;
+               gdb_sig = stop.code;
+               *pstatus = gdb_signal_to_target(tcp, gdb_sig);
+               if (stop.code == __NR_exit_group)
+                       return TE_GROUP_STOP;
+               else
+                       return TE_SYSCALL_STOP;
+
+       case gdb_stop_syscall_return:
+               /* If we missed the entry, recording a return will
+                * only confuse things, so let's just report the good
+                * ones. */
+               if (exiting(tcp)) {
+                       tcp->scno = stop.code;
+                       gdb_sig = stop.code;
+                       *pstatus = gdb_signal_to_target(tcp, gdb_sig);
+                       return TE_SYSCALL_STOP;
+               }
+               break;
+
+       case gdb_stop_signal:
+       {
+               size_t siginfo_size;
+               char *siginfo_reply =
+                               gdb_xfer_read(gdb, "siginfo", "", 
&siginfo_size);
+               if (siginfo_reply && siginfo_size == sizeof(siginfo_t))
+                       si = (siginfo_t *) siginfo_reply;
+
+               /* XXX gdbserver returns "native" siginfo of 32/64-bit
+                * target but strace expects its own format as
+                * PTRACE_GETSIGINFO would have given it.  (i.e. need
+                * to reverse siginfo_fixup)
+                * ((i.e. siginfo_from_compat_siginfo)) */
+
+               gdb_sig = stop.code;
+               *pstatus = gdb_signal_to_target(tcp, gdb_sig);
+               free(siginfo_reply);
+               return TE_SIGNAL_DELIVERY_STOP;
+       }
+
+       default:
+               /* TODO Do we need to handle gdb_multiprocess here? */
+               break;
+       }
+
+       return TE_RESTART;
+}
+
+/* Returns true iff the main trace loop has to continue.  The gdb
+ * connection should be ready for a stop reply on entry,p and we'll
+ * leave it the same way if we return true. */
+
+bool
+gdb_dispatch_event(enum trace_event ret, int *pstatus, void *si_p)
+{
+       siginfo_t *si = (siginfo_t*)si_p;
+       int gdb_sig = 0;
+       struct tcb *tcp = current_tcp;
+       pid_t tid;
+       unsigned int sig = 0;
+
+
+       /* Exit if the process has gone away */
+       if (tcp == 0)
+               return false;
+       tid = tcp->pid;
+       if (! (tcp->flags & TCB_GDB_CONT_PID_TID)) {
+               char cmd[] = "Hgxxxxxxxx";
+               sprintf(cmd, "Hg%x.%x", general_pid, general_tid);
+               if (debug_flag)
+                       error_msg("%s %s\n", __FUNCTION__, cmd);
+       }
+       /* TODO need code equivalent to PTRACE_EVENT_EXEC? */
+
+       /* Is this the very first time we see this tracee stopped? */
+       if (tcp->flags & TCB_STARTUP) {
+               tcp->flags &= ~TCB_STARTUP;
+               if (get_scno(tcp) == 1)
+                       tcp->s_prev_ent = tcp->s_ent;
+       }
+
+       /* TODO cflag means we need to update tcp->dtime/stime usually
+        * through wait rusage, but how can we do it? */
+
+       free (stop.reply);
+
+       switch (ret) {
+       case TE_BREAK:
+               return false;
+
+       case TE_RESTART:
+               break;
+
+       case TE_SYSCALL_STOP:
+               trace_syscall(tcp, &sig);
+               break;
+
+       case TE_SIGNAL_DELIVERY_STOP:
+               sig = *pstatus;
+               gdb_sig = stop.code;
+               print_stopped(tcp, si, *pstatus);
+               break;
+
+       case TE_SIGNALLED:
+               print_signalled(tcp, tid, *pstatus);
+               droptcb(tcp);
+               return false;
+
+       case TE_EXITED:
+               print_exited(tcp, tid, *pstatus);
+               droptcb(tcp);
+               /* Don't continue if the process exited */
+               if (!gdb_multiprocess || gdb_has_non_stop(gdb))
+                       return false;
+               break;
+
+       case TE_STOP_BEFORE_EXECVE:
+       case TE_STOP_BEFORE_EXIT:
+               /* TODO handle this? */
+               return false;
+
+       case TE_GROUP_STOP:
+               trace_syscall(tcp, &sig);
+               sig = *pstatus;
+               return false;
+
+       case TE_NEXT:
+               break;
+       }
+
+       /* We handled quick cases, we are permitted to interrupt now. */
+       if (interrupted)
+               return false;
+
+       /* Don't continue gdbserver until we handle any queued notifications */
+       if (have_notification())
+               return true;
+
+       if (gdb_sig) {
+               if (gdb_vcont) {
+                       /* send the signal to this target and continue everyone 
else */
+                       char cmd[] = "vCont;Cxx:xxxxxxxx;c";
+                       sprintf(cmd, "vCont;C%02x:%x;c", gdb_sig, tid);
+                       gdb_send(gdb, cmd, strlen(cmd));
+               } else {
+                       /* just send the signal */
+                       char cmd[] = "Cxx";
+                       sprintf(cmd, "C%02x", gdb_sig);
+                       gdb_send(gdb, cmd, strlen(cmd));
+               }
+       } else {
+               if (gdb_vcont) {
+                       /* For non-stop use $vCont;c:pid.tid where
+                        * pid.tid is the thread gdbserver is focused
+                        * on */
+                       char cmd[] = "vCont;c:xxxxxxxx.xxxxxxxx";
+
+                       struct tcb *general_tcp = gdb_find_thread(general_tid, 
true);
+                       if (gdb_has_non_stop(gdb) && general_pid != general_tid
+                                       && general_tcp->flags & 
TCB_GDB_CONT_PID_TID)
+                               sprintf(cmd, "vCont;c:p%x.%x", general_pid, 
general_tid);
+                       else
+                               sprintf(cmd, "vCont;c");
+                       gdb_send(gdb, cmd, sizeof(cmd) - 1);
+               } else {
+                       static const char cmd[] = "c";
+                       gdb_send(gdb, cmd, sizeof(cmd) - 1);
+               }
+       }
+
+       return true;
+}
+
+
+char *
+gdb_get_all_regs(pid_t tid, size_t *size)
+{
+       if (!gdb)
+               return NULL;
+
+       /* NB: this assumes gdbserver's current thread is also tid.  If that
+        * may not be the case, we should send "HgTID" first, and restore.  */
+       gdb_send(gdb, "g", 1);
+       return gdb_recv(gdb, size, false);
+}
+
+
+#ifdef GDBSERVER_ARCH_HAS_GET_REGS
+# include "gdb_get_regs.c"
+#else
+long gdb_get_regs(pid_t pid, void *io) { return -1; }
+#endif
+
+
+#ifdef GDBSERVER_ARCH_HAS_SET_REGS
+# include "gdb_set_regs.c"
+#else
+long gdb_set_regs(pid_t pid, void *io) { return -1; }
+#endif
+
+
+int
+gdb_get_scno(struct tcb *tcp)
+{
+       return 1;
+}
+
+int
+gdb_read_mem(pid_t tid, long addr, unsigned int len, bool check_nil, char *out)
+{
+       if (!gdb) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       /* NB: this assumes gdbserver's current thread is also tid.  If that
+        * may not be the case, we should send "HgTID" first, and restore.  */
+       while (len) {
+               char cmd[] = "mxxxxxxxxxxxxxxxx,xxxx";
+               unsigned int chunk_len = len < 0x1000 ? len : 0x1000;
+               sprintf(cmd, "m%lx,%x", addr, chunk_len);
+               gdb_send(gdb, cmd, strlen(cmd));
+
+               size_t size;
+               char *reply = gdb_recv(gdb, &size, false);
+               if (size < 2 || reply[0] == 'E' || size > len * 2
+                   || gdb_decode_hex_buf(reply, size, out) < 0) {
+                       free(reply);
+                       errno = EINVAL;
+                       return -1;
+               }
+
+               chunk_len = size / 2;
+               if (check_nil && strnlen(out, chunk_len) < chunk_len)
+               {
+                       free(reply);
+                       return 1;
+               }
+
+               addr += chunk_len;
+               out += chunk_len;
+               len -= chunk_len;
+               free(reply);
+       }
+
+       return 0;
+}
+
+
+int
+gdb_write_mem(pid_t tid, long addr, unsigned int len, char *buffer)
+{
+       unsigned int i, j;
+       const char packet_template[] = "Xxxxxxxxxxxxxxxxx,xxxx:";
+       char cmd[strlen(packet_template) + len];
+
+       if (!gdb) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       /* NB: this assumes gdbserver's current thread is also tid.  If that
+        * may not be the case, we should send "HgTID" first, and restore.  */
+       sprintf(cmd, "X%lx,%x:", addr, len);
+       j = strlen(cmd);
+       for (i = 0; i < len; i++)
+               cmd[j++] = buffer[i];
+       cmd[j] = '\0';
+       gdb_send(gdb, cmd, strlen(cmd));
+       if (!gdb_ok())
+               error_msg("Failed to poke data to GDB server");
+
+       return 0;
+}
+
+
+int
+gdb_umoven(struct tcb *const tcp, kernel_ulong_t addr, unsigned int len,
+               void *const our_addr)
+{
+       return gdb_read_mem(tcp->pid, addr, len, false, our_addr);
+}
+
+
+int
+gdb_umovestr(struct tcb *const tcp, kernel_ulong_t addr, unsigned int len, char *laddr)
+{
+       return gdb_read_mem(tcp->pid, addr, len, true, laddr);
+}
+
+int
+gdb_upeek(int pid, unsigned long off, kernel_ulong_t *res)
+{
+       return gdb_read_mem(pid, off, current_wordsize, false, (char*)res);
+}
+
+
+int
+gdb_upoke(int pid, unsigned long off, kernel_ulong_t res)
+{
+       kernel_ulong_t buffer = res;
+       return gdb_write_mem(pid, off, current_wordsize, (char*)&buffer);
+}
+
+
+int
+gdb_getfdpath(struct tcb *tcp, int fd, char *buf, unsigned bufsize)
+{
+       if (!gdb || fd < 0)
+               return -1;
+
+       /*
+        * As long as we assume a Linux target, we can peek at their procfs
+        * just like normal getfdpath does.  Maybe that won't always be true.
+        */
+       char linkpath[sizeof("/proc/%u/fd/%u") + 2 * sizeof(int)*3];
+       sprintf(linkpath, "/proc/%u/fd/%u", tcp->pid, fd);
+       return gdb_readlink(gdb, linkpath, buf, bufsize);
+}
+
+
+bool
+gdb_verify_args(const char *username, bool daemon, unsigned int *follow_fork)
+{
+       if (username) {
+               error_msg_and_die("-u and -G are mutually exclusive");
+       }
+
+       if (daemon) {
+               error_msg_and_die("-D and -G are mutually exclusive");
+       }
+
+       if (!*follow_fork) {
+               error_msg("-G is always multithreaded, implies -f");
+               *follow_fork = 1;
+       }
+
+#ifdef USE_LIBUNWIND
+       if (stack_trace_enabled)
+               error_msg_and_die("Simultaneous usage of gdbserver backend (-G) and 
"
+                               "stack tracing (-k) is not supported");
+#endif
+
+       return true;
+}
+
+
+bool
+gdb_handle_arg(char arg, char *optarg)
+{
+       if (arg != 'G')
+               return false;
+
+       gdbserver = optarg;
+       backend.attach_tcb = gdb_attach_tcb;
+       backend.cleanup = gdb_cleanup;
+       backend.detach = gdb_detach;
+       backend.dispatch_event = gdb_dispatch_event;
+       backend.end_init = gdb_end_init;
+       backend.get_regs = gdb_get_regs;
+       backend.get_scno = gdb_get_scno;
+       backend.getfdpath = gdb_getfdpath;
+       backend.next_event = gdb_next_event;
+       backend.prog_pid_check = gdb_prog_pid_check;
+       backend.start_init = gdb_start_init;
+       backend.startup_child = gdb_startup_child;
+       backend.umoven = gdb_umoven;
+       backend.umovestr = gdb_umovestr;
+       backend.upeek_ = gdb_upeek;
+       backend.upoke_ = gdb_upoke;
+       backend.verify_args = gdb_verify_args;
+       return true;
+}
diff --git a/gdbserver/protocol.c b/gdbserver/protocol.c
new file mode 100644
index 0000000..1ef5660
--- /dev/null
+++ b/gdbserver/protocol.c
@@ -0,0 +1,734 @@
+/* Simple implementation of a GDB remote protocol client.
+ *
+ * Copyright (c) 2015 Red Hat Inc.
+ * Copyright (c) 2015 Josh Stone <cuvi...@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _GNU_SOURCE 1
+#include <err.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <signal.h>
+#include <spawn.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+
+#include <netinet/in.h>
+
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+
+#include "protocol.h"
+#include "defs.h"
+
+struct gdb_conn {
+       FILE *in;
+       FILE *out;
+       bool ack;
+       bool non_stop;
+};
+
+/* non-stop notifications (see gdb_recv_stop) */
+struct notifications_s {
+    int size;
+    int start;
+    int count;
+    char **packet;
+} notifications;
+
+
+void
+gdb_encode_hex(uint8_t byte, char* out) {
+       static const char value_hex[16] = {
+               '0', '1', '2', '3', '4', '5', '6', '7',
+               '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
+       };
+       *out++ = value_hex[byte >> 4];
+       *out++ = value_hex[byte & 0xf];
+}
+
+char *
+gdb_encode_hex_string(const char *str)
+{
+       char *out = malloc(2 * strlen(str) + 1);
+       if (out) {
+               char *out_ptr = out;
+               while (*str) {
+                       gdb_encode_hex(*str++, out_ptr);
+                       out_ptr += 2;
+               }
+               *out_ptr = '\0';
+       }
+       return out;
+}
+
+
+static inline uint8_t
+hex_nibble(uint8_t hex)
+{
+       static const uint8_t hex_value[256] = {
+               [0 ... '0'-1] = UINT8_MAX,
+               0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+               ['9'+1 ... 'A'-1] = UINT8_MAX,
+               10, 11, 12, 13, 14, 15,
+               ['F'+1 ... 'a'-1] = UINT8_MAX,
+               10, 11, 12, 13, 14, 15,
+               ['f'+1 ... 255] = UINT8_MAX,
+       };
+       return hex_value[hex];
+}
+
+uint16_t gdb_decode_hex(char msb, char lsb)
+{
+       uint8_t high_nibble = hex_nibble(msb);
+       uint8_t low_nibble = hex_nibble(lsb);
+       if (high_nibble >= 16 || low_nibble >= 16)
+               return UINT16_MAX;
+       return 16 * hex_nibble(msb) + hex_nibble(lsb);
+}
+
+uint64_t gdb_decode_hex_n(const char *bytes, size_t n)
+{
+       uint64_t value = 0;
+       while (n--) {
+               uint8_t nibble = hex_nibble(*bytes++);
+               if (nibble >= 16)
+                       break;
+               value = 16 * value + nibble;
+       }
+       return value;
+}
+
+uint64_t gdb_decode_hex_str(const char *bytes)
+{
+       uint64_t value = 0;
+       while (*bytes) {
+               uint8_t nibble = hex_nibble(*bytes++);
+               if (nibble >= 16)
+                       break;
+               value = 16 * value + nibble;
+       }
+       return value;
+}
+
+int64_t gdb_decode_signed_hex_str(const char *bytes)
+{
+       return (*bytes == '-')
+               ? -(int64_t)gdb_decode_hex_str(bytes + 1)
+               : (int64_t)gdb_decode_hex_str(bytes);
+}
+
+int gdb_decode_hex_buf(const char *bytes, size_t n, char *out)
+{
+       if (n & 1)
+               return -1;
+
+       while (n > 1) {
+               uint16_t byte = gdb_decode_hex(bytes[0], bytes[1]);
+               if (byte > UINT8_MAX)
+                       return -1;
+
+               *out++ = byte;
+               bytes += 2;
+               n -= 2;
+       }
+       return 0;
+}
+
+
+static struct gdb_conn *
+gdb_begin(int fd)
+{
+       struct gdb_conn *conn = xcalloc(1, sizeof(struct gdb_conn));
+
+       conn->ack = true;
+
+       /* duplicate the handle to separate read/write state */
+       int fd2 = dup(fd);
+       if (fd2 < 0)
+               perror_msg_and_die("dup");
+
+       /* open a FILE* for reading */
+       conn->in = fdopen(fd, "rb");
+       if (conn->in == NULL)
+               perror_msg_and_die("fdopen in");
+
+       /* open a FILE* for writing */
+       conn->out = fdopen(fd2, "wb");
+       if (conn->out == NULL)
+               perror_msg_and_die("fdopen out");
+
+       /* reset line state by acking any earlier input */
+       fputc('+', conn->out);
+       fflush(conn->out);
+
+       return conn;
+}
+
+#define ZERO_OR_DIE(f, ...) \
+       do { \
+               int ret = f(__VA_ARGS__); \
+               if (ret) \
+                       perror_msg_and_die(#f); \
+       } while (0)
+
+struct gdb_conn *
+gdb_begin_command(const char *command)
+{
+       int fds[2];
+       pid_t pid;
+       posix_spawn_file_actions_t file_actions;
+       const char* sh = "/bin/sh";
+       const char *const const_argv[] = {"sh", "-c", command, NULL};
+       char *const *argv = (char *const *) const_argv;
+
+ /* Create a bidirectional "pipe", [0] for us and [1] for the command stdio. */
+       if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0)
+               perror_msg_and_die("socketpair");
+
+       ZERO_OR_DIE(posix_spawn_file_actions_init, &file_actions);
+
+       /* Close our end in the child. */
+       ZERO_OR_DIE(posix_spawn_file_actions_addclose, &file_actions, fds[0]);
+
+       /* Copy the child's end to its stdout and stdin. */
+       if (fds[1] != STDOUT_FILENO) {
+               ZERO_OR_DIE(posix_spawn_file_actions_adddup2, &file_actions,
+                           fds[1], STDOUT_FILENO);
+               ZERO_OR_DIE(posix_spawn_file_actions_addclose, &file_actions,
+                           fds[1]);
+       }
+       ZERO_OR_DIE(posix_spawn_file_actions_adddup2, &file_actions,
+                   STDOUT_FILENO, STDIN_FILENO);
+
+       /* Spawn the actual command. */
+       ZERO_OR_DIE(posix_spawn, &pid, sh, &file_actions, NULL, argv, environ);
+
+       /* Cleanup. */
+       ZERO_OR_DIE(posix_spawn_file_actions_destroy, &file_actions);
+
+       close(fds[1]);
+
+       /* Avoid SIGPIPE when the command quits. */
+       signal(SIGPIPE, SIG_IGN);
+
+       /* initialize the rest of gdb on this handle */
+       return gdb_begin(fds[0]);
+}
+
+struct gdb_conn *
+gdb_begin_tcp(const char *node, const char *service)
+{
+       /* NB: gdb doesn't support IPv6 - should we? */
+       const struct addrinfo hints = {
+               .ai_family = AF_UNSPEC,
+               .ai_socktype = SOCK_STREAM,
+       };
+
+       struct addrinfo *result = NULL;
+       int s = getaddrinfo(node, service, &hints, &result);
+       if (s)
+               error_msg_and_die("getaddrinfo: %s", gai_strerror(s));
+
+       int fd = -1;
+       struct addrinfo *ai;
+       for (ai = result; ai; ai = ai->ai_next) {
+               /* open the socket and start the tcp connection */
+               fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+               if (fd < 0)
+                       continue;
+
+               if (connect(fd, ai->ai_addr, ai->ai_addrlen) == 0)
+                       break;
+
+               close(fd);
+               fd = -1;
+       }
+
+       freeaddrinfo(result);
+       if (fd < 0)
+               error_msg_and_die("Cannot connect to GDB server");
+
+       /* initialize the rest of gdb on this handle */
+       return gdb_begin(fd);
+}
+
+struct gdb_conn *
+gdb_begin_path(const char *path)
+{
+       int fd = open(path, O_RDWR);
+       if (fd < 0)
+               perror_msg_and_die("open");
+
+       /* initialize the rest of gdb on this handle */
+       return gdb_begin(fd);
+}
+
+
+void
+gdb_end(struct gdb_conn *conn)
+{
+       fclose(conn->in);
+       fclose(conn->out);
+       free(conn);
+}
+
+
+static void
+send_packet(FILE *out, const char *command, size_t size)
+{
+       /* compute the checksum -- simple mod256 addition */
+       size_t i;
+       uint8_t sum = 0;
+       for (i = 0; i < size; ++i)
+               sum += (uint8_t)command[i];
+
+       /* NB: seems neither escaping nor RLE is generally expected by
+        * gdbserver.  e.g. giving "invalid hex digit" on an RLE'd
+        * address.  So just write raw here, and maybe let higher levels
+        * escape/RLE. */
+
+       if (debug_flag) {
+               error_msg("\tSending packet: $%s", command);
+               fflush(stdout);
+       }
+       fputc('$', out); /* packet start */
+       fwrite(command, 1, size, out); /* payload */
+       fprintf(out, "#%02x", sum); /* packet end, checksum */
+       fflush(out);
+
+       if (ferror(out))
+               error_msg("Error sending message \"$%s\" to GDB server",
+                         command);
+       else if (feof(out))
+               error_msg_and_die("Connection to GDB server has been closed");
+}
+
+void
+gdb_send(struct gdb_conn *conn, const char *command, size_t size)
+{
+       bool acked = false;
+       do {
+               send_packet(conn->out, command, size);
+
+               if (!conn->ack)
+                       break;
+
+               /* look for '+' ACK or '-' NACK/resend */
+               acked = fgetc_unlocked(conn->in) == '+';
+       } while (!acked);
+}
+
+
+/* push_notification/pop_notification caches notifications which
+ *   arrive via the following dialogue:
+ *   [ server: %Stop:T05syscall_entry...
+ *     client: $vStopped ]*
+ *     server: OK
+ */
+
+void
+push_notification(char *packet, size_t packet_size)
+{
+       int idx;
+
+       if (strncmp(packet+3, "syscall", 7) != 0)
+               return;
+
+       if (notifications.size == 0) {
+               notifications.size = 32;
+               notifications.start = 0;
+               notifications.count = 0;
+ notifications.packet = xmalloc(sizeof(notifications.packet) * notifications.size);
+       }
+
+       if (notifications.count == notifications.size) {
+               error_msg("Buffer overflow");
+       } else {
+               idx = notifications.start + notifications.count++;
+               if (idx >= notifications.size) {
+                       idx = 0;
+               }
+               notifications.packet[idx] = packet;
+       }
+
+       if (debug_flag)
+ printf /*error_msg*/("Pushed %s (%d items in queue)\n", packet, notifications.count);
+}
+
+
+char*
+pop_notification(size_t *size)
+{
+       char *packet;
+       if (notifications.count == 0) {
+               return (char*)NULL;
+       } else {
+               packet = notifications.packet[notifications.start];
+               notifications.start++;
+               notifications.count--;
+               if (notifications.start == notifications.size)
+                       notifications.start = 0;
+       }
+
+       if (debug_flag) {
+               error_msg("Popped %s (%d items in queue)", packet, 
notifications.count);
+       }
+
+       return packet;
+}
+
+
+bool
+have_notification(void)
+{
+       return (notifications.count == 0 ? false : true);
+}
+
+
+void
+dump_notifications(char *packet, int pid, int tid)
+{
+       int idx;
+
+       for (idx = notifications.start; idx < notifications.count; idx++) {
+               if (notifications.packet[idx] != NULL)
+                       printf ("Notify Dump: %s\n", notifications.packet[idx]);
+       }
+}
+
+
+static char *
+recv_packet(FILE *in, size_t *ret_size, bool* ret_sum_ok)
+{
+       size_t i = 0;
+       size_t size = 4096;
+       char *reply = xmalloc(size);
+
+       int c;
+       uint8_t sum = 0;
+       bool escape = false;
+
+       /* fast-forward to the first start of packet */
+       while ((c = fgetc_unlocked(in)) != EOF && (c != '$' && c != '%'));
+       if (c == '%')
+               ungetc (c, in);
+
+       while ((c = fgetc_unlocked(in)) != EOF) {
+               sum += (uint8_t)c;
+               switch (c) {
+               case '$': /* new packet?  start over... */
+                       i = 0;
+                       sum = 0;
+                       escape = false;
+                       continue;
+               case '%':
+               {
+                       char pcr[6];
+
+                       int idx = 0;
+
+                       i = 0;
+                       sum = 0;
+                       escape = false;
+                       for (idx = 0; idx < 5; idx++)
+                       {
+                               pcr[idx] = fgetc_unlocked(in);
+                               sum += (uint8_t)pcr[idx];
+                       }
+                       if (strncmp(pcr, "Stop:", 5) == 0)
+                               continue;
+                       continue;
+               }
+               case '#': /* end of packet */
+                       sum -= c; /* not part of the checksum */
+                       {
+                               uint8_t msb = fgetc_unlocked(in);
+                               uint8_t lsb = fgetc_unlocked(in);
+                               *ret_sum_ok = sum == gdb_decode_hex(msb, lsb);
+                       }
+                       *ret_size = i;
+
+                       /* terminate it for good measure */
+                       if (i == size) {
+                               reply = realloc(reply, size + 1);
+                               if (reply == NULL)
+                                       perror_msg_and_die("realloc");
+                       }
+                       reply[i] = '\0';
+
+                       if (debug_flag) {
+                               error_msg("\tPacket received: %s", reply);
+                               fflush(stdout);
+                       }
+                       return reply;
+
+               case '}': /* escape: next char is XOR 0x20 */
+                       escape = true;
+                       continue;
+
+               case '*':
+                       /* run-length-encoding The next character tells how
+                        * many times to repeat the last character we saw.
+                        * The count is added to 29, so that the
+                        * minimum-beneficial RLE 3 is the first printable
+                        * character ' '.  The count character can't be >126
+                        * or '$'/'#' packet markers. */
+
+                       if (i > 0) { /* need something to repeat! */
+                               int c2 = fgetc_unlocked(in);
+                               if (c2 < 29 || c2 > 126 || c2 == '$' || c2 == 
'#') {
+                                       /* invalid count character! */
+                                       ungetc(c2, in);
+                               } else {
+                                       int count = c2 - 29;
+
+                                       /* get a bigger buffer if needed */
+                                       if (i + count > size) {
+                                               size *= 2;
+                                               reply = realloc(reply, size);
+                                               if (reply == NULL)
+                                                       
perror_msg_and_die("realloc");
+                                       }
+
+                                       /* fill the repeated character */
+                                       memset(&reply[i], reply[i - 1], count);
+                                       i += count;
+                                       sum += c2;
+                                       continue;
+                               }
+                       }
+               }
+
+               /* XOR an escaped character */
+               if (escape) {
+                       c ^= 0x20;
+                       escape = false;
+               }
+
+               /* get a bigger buffer if needed */
+               if (i == size) {
+                       size *= 2;
+                       reply = realloc(reply, size);
+                       if (reply == NULL)
+                               perror_msg_and_die("realloc");
+               }
+
+               /* add one character */
+               reply[i++] = c;
+       }
+
+       if (ferror(in))
+               error_msg_and_die("got stream error while receiving GDB server"
+                                  " packet");
+       else if (feof(in)) {
+               error_msg_and_die("connection closed unexpectedly while "
+                               "receiving GDB server packet");
+       }
+
+       error_msg_and_die("unknown GDB server connection error");
+}
+
+char *
+gdb_recv(struct gdb_conn *conn, size_t *size, bool want_stop)
+{
+       char *reply;
+       bool acked = false;
+
+       do {
+               reply = recv_packet(conn->in, size, &acked);
+
+               /* (See gdb_recv_stop for non-stop packet order)
+                  If a notification arrived while expecting another packet
+                  type, then cache the notification. */
+               if (! want_stop && strncmp(reply, "T05syscall", 10) == 0) {
+                       push_notification(reply, *size);
+                       reply = recv_packet(conn->in, size, &acked);
+               }
+
+               if (conn->ack) {
+                       /* send +/- depending on checksum result, retry if 
needed */
+                       fputc(acked ? '+' : '-', conn->out);
+                       fflush(conn->out);
+                       if (!acked)
+                               free(reply);
+               }
+       } while (conn->ack && !acked);
+
+       return reply;
+}
+
+bool
+gdb_start_noack(struct gdb_conn *conn)
+{
+       static const char cmd[] = "QStartNoAckMode";
+       gdb_send(conn, cmd, sizeof(cmd) - 1);
+
+       size_t size;
+       char *reply = gdb_recv(conn, &size, false);
+       bool ok = size == 2 && !strcmp(reply, "OK");
+       free(reply);
+
+       if (ok)
+               conn->ack = false;
+       return ok ? "OK" : "";
+}
+
+void
+gdb_set_non_stop(struct gdb_conn *conn, bool val)
+{
+       conn->non_stop = val;
+}
+
+bool
+gdb_has_non_stop(struct gdb_conn *conn)
+{
+       return conn->non_stop;
+}
+
+/* Read complete qXfer data, returned as binary with the size.
+ * On error, returns NULL with size set to the error code.  */
+char *
+gdb_xfer_read(struct gdb_conn *conn,
+       const char *object, const char *annex,
+       /* out */ size_t *ret_size)
+{
+       size_t error = 0;
+       size_t offset = 0;
+       char *data = NULL;
+       do {
+               char *cmd;
+               int cmd_size = asprintf(&cmd, "qXfer:%s:read:%s:%zx,%x",
+                                       object ?: "", annex ?: "", offset, 
0xfff /* XXX PacketSize */);
+               if (cmd_size < 0) {
+                       break;
+               }
+
+               gdb_send(conn, cmd, strlen(cmd));
+               free(cmd);
+
+               size_t size;
+               char *reply = gdb_recv(conn, &size, false);
+               char c = reply[0];
+               switch (c) {
+               case 'm':
+               case 'l':
+                       data = realloc(data, offset + size - 1);
+                       memcpy(data + offset, reply + 1, size - 1);
+                       free(reply);
+                       offset += size - 1;
+                       if (c == 'l') {
+                               *ret_size = offset;
+                               return data;
+                       }
+                       continue;
+               case 'E':
+                       error = gdb_decode_hex_str(reply + 1);
+                       break;
+               }
+               free(reply);
+               break;
+       } while (0);
+
+       free(data);
+       *ret_size = error;
+       return NULL;
+}
+
+
+struct vfile_response {
+       char *reply;
+       int64_t result;
+       int64_t errnum; /* avoid 'errno' macros */
+       size_t attachment_size;
+       const char *attachment;
+};
+
+static struct vfile_response
+gdb_vfile(struct gdb_conn *conn, const char *operation, const char *parameters)
+{
+       struct vfile_response res = { NULL, -1, 0, 0, NULL };
+
+       char *cmd;
+       int cmd_size = asprintf(&cmd, "vFile:%s:%s", operation, parameters);
+       if (cmd_size < 0) {
+               return res;
+       }
+
+       gdb_send(conn, cmd, strlen(cmd));
+       free(cmd);
+
+       size_t size;
+       res.reply = gdb_recv(conn, &size, false);
+       if (size > 1 && res.reply[0] == 'F') {
+               /* F result [, errno] [; attachment] */
+               res.result = gdb_decode_signed_hex_str(res.reply + 1);
+
+               const char *attachment = memchr(res.reply, ';', size);
+               if (attachment) {
+                       res.attachment = attachment + 1;
+                       res.attachment_size = size - (res.attachment - 
res.reply);
+               }
+
+               const char *errnum = memchr(res.reply, ',', size - 
res.attachment_size);
+               if (errnum)
+                       res.errnum = gdb_decode_signed_hex_str(errnum + 1);
+       }
+       return res;
+}
+
+int
+gdb_readlink(struct gdb_conn *conn, const char *linkpath,
+       char *buf, unsigned bufsize)
+{
+       char *parameters = gdb_encode_hex_string(linkpath);
+       if (!parameters)
+               return -1;
+
+       struct vfile_response res = gdb_vfile(conn, "readlink", parameters);
+       free(parameters);
+
+       int ret = -1;
+       if (res.result >= 0 && res.attachment != NULL
+               && res.result == (int64_t)res.attachment_size) {
+               size_t data_len = res.attachment_size;
+               if (data_len >= bufsize)
+                       data_len = bufsize - 1; /* truncate -- ok? */
+               memcpy(buf, res.attachment, data_len);
+               buf[data_len] = 0;
+               ret = data_len;
+       }
+       free(res.reply);
+       return ret;
+}
diff --git a/gdbserver/protocol.h b/gdbserver/protocol.h
new file mode 100644
index 0000000..7220052
--- /dev/null
+++ b/gdbserver/protocol.h
@@ -0,0 +1,77 @@
+#ifndef STRACE_GDBSERVER_PROTOCOL_H
+#define STRACE_GDBSERVER_PROTOCOL_H
+
+/* Simple interface of a GDB remote protocol client.
+ *
+ * Copyright (c) 2015 Red Hat Inc.
+ * Copyright (c) 2015 Josh Stone <cuvi...@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+struct gdb_conn;
+
+void gdb_encode_hex(uint8_t byte, char *out);
+uint16_t gdb_decode_hex(char msb, char lsb);
+uint64_t gdb_decode_hex_n(const char *bytes, size_t n);
+uint64_t gdb_decode_hex_str(const char *bytes);
+int gdb_decode_hex_buf(const char *bytes, size_t n, char *out);
+
+struct gdb_conn *gdb_begin_command(const char *command);
+struct gdb_conn *gdb_begin_tcp(const char *node, const char *service);
+struct gdb_conn *gdb_begin_path(const char *path);
+
+void gdb_end(struct gdb_conn *conn);
+
+void gdb_send(struct gdb_conn *conn, const char *command, size_t size);
+
+char *gdb_recv(struct gdb_conn *conn, /* out */ size_t *size, bool want_stop);
+
+bool gdb_start_noack(struct gdb_conn *conn);
+
+void gdb_set_non_stop(struct gdb_conn *conn, bool val);
+
+bool gdb_has_non_stop(struct gdb_conn *conn);
+
+char* pop_notification(size_t *size);
+
+void push_notification(char *packet, size_t packet_size);
+
+bool have_notification();
+
+/* Read complete qXfer data, returned as binary with the size.
+ * On error, returns NULL with size set to the error code.
+ */
+char *gdb_xfer_read(struct gdb_conn *conn,
+        const char *object, const char *annex,
+        /* out */ size_t *size);
+
+int gdb_readlink(struct gdb_conn *conn, const char *linkpath,
+        char *buf, unsigned bufsize);
+
+#endif /* !STRACE_GDBSERVER_PROTOCOL_H */
diff --git a/gdbserver/signals.def b/gdbserver/signals.def
new file mode 100644
index 0000000..3f49980
--- /dev/null
+++ b/gdbserver/signals.def
@@ -0,0 +1,200 @@
+/* Target signal numbers for GDB and the GDB remote protocol.
+   Copyright (C) 2010-2015 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Used some places (e.g. stop_signal) to record the concept that
+   there is no signal.  */
+SET (GDB_SIGNAL_0, 0, "0", "Signal 0")
+#define GDB_SIGNAL_FIRST GDB_SIGNAL_0
+SET (GDB_SIGNAL_HUP, 1, "SIGHUP", "Hangup")
+SET (GDB_SIGNAL_INT, 2, "SIGINT", "Interrupt")
+SET (GDB_SIGNAL_QUIT, 3, "SIGQUIT", "Quit")
+SET (GDB_SIGNAL_ILL, 4, "SIGILL", "Illegal instruction")
+SET (GDB_SIGNAL_TRAP, 5, "SIGTRAP", "Trace/breakpoint trap")
+SET (GDB_SIGNAL_ABRT, 6, "SIGABRT", "Aborted")
+SET (GDB_SIGNAL_EMT, 7, "SIGEMT", "Emulation trap")
+SET (GDB_SIGNAL_FPE, 8, "SIGFPE", "Arithmetic exception")
+SET (GDB_SIGNAL_KILL, 9, "SIGKILL", "Killed")
+SET (GDB_SIGNAL_BUS, 10, "SIGBUS", "Bus error")
+SET (GDB_SIGNAL_SEGV, 11, "SIGSEGV", "Segmentation fault")
+SET (GDB_SIGNAL_SYS, 12, "SIGSYS", "Bad system call")
+SET (GDB_SIGNAL_PIPE, 13, "SIGPIPE", "Broken pipe")
+SET (GDB_SIGNAL_ALRM, 14, "SIGALRM", "Alarm clock")
+SET (GDB_SIGNAL_TERM, 15, "SIGTERM", "Terminated")
+SET (GDB_SIGNAL_URG, 16, "SIGURG", "Urgent I/O condition")
+SET (GDB_SIGNAL_STOP, 17, "SIGSTOP", "Stopped (signal)")
+SET (GDB_SIGNAL_TSTP, 18, "SIGTSTP", "Stopped (user)")
+SET (GDB_SIGNAL_CONT, 19, "SIGCONT", "Continued")
+SET (GDB_SIGNAL_CHLD, 20, "SIGCHLD", "Child status changed")
+SET (GDB_SIGNAL_TTIN, 21, "SIGTTIN", "Stopped (tty input)")
+SET (GDB_SIGNAL_TTOU, 22, "SIGTTOU", "Stopped (tty output)")
+SET (GDB_SIGNAL_IO, 23, "SIGIO", "I/O possible")
+SET (GDB_SIGNAL_XCPU, 24, "SIGXCPU", "CPU time limit exceeded")
+SET (GDB_SIGNAL_XFSZ, 25, "SIGXFSZ", "File size limit exceeded")
+SET (GDB_SIGNAL_VTALRM, 26, "SIGVTALRM", "Virtual timer expired")
+SET (GDB_SIGNAL_PROF, 27, "SIGPROF", "Profiling timer expired")
+SET (GDB_SIGNAL_WINCH, 28, "SIGWINCH", "Window size changed")
+SET (GDB_SIGNAL_LOST, 29, "SIGLOST", "Resource lost")
+SET (GDB_SIGNAL_USR1, 30, "SIGUSR1", "User defined signal 1")
+SET (GDB_SIGNAL_USR2, 31, "SIGUSR2", "User defined signal 2")
+SET (GDB_SIGNAL_PWR, 32, "SIGPWR", "Power fail/restart")
+/* Similar to SIGIO.  Perhaps they should have the same number.  */
+SET (GDB_SIGNAL_POLL, 33, "SIGPOLL", "Pollable event occurred")
+SET (GDB_SIGNAL_WIND, 34, "SIGWIND", "SIGWIND")
+SET (GDB_SIGNAL_PHONE, 35, "SIGPHONE", "SIGPHONE")
+SET (GDB_SIGNAL_WAITING, 36, "SIGWAITING", "Process's LWPs are blocked")
+SET (GDB_SIGNAL_LWP, 37, "SIGLWP", "Signal LWP")
+SET (GDB_SIGNAL_DANGER, 38, "SIGDANGER", "Swap space dangerously low")
+SET (GDB_SIGNAL_GRANT, 39, "SIGGRANT", "Monitor mode granted")
+SET (GDB_SIGNAL_RETRACT, 40, "SIGRETRACT",
+     "Need to relinquish monitor mode")
+SET (GDB_SIGNAL_MSG, 41, "SIGMSG", "Monitor mode data available")
+SET (GDB_SIGNAL_SOUND, 42, "SIGSOUND", "Sound completed")
+SET (GDB_SIGNAL_SAK, 43, "SIGSAK", "Secure attention")
+SET (GDB_SIGNAL_PRIO, 44, "SIGPRIO", "SIGPRIO")
+SET (GDB_SIGNAL_REALTIME_33, 45, "SIG33", "Real-time event 33")
+SET (GDB_SIGNAL_REALTIME_34, 46, "SIG34", "Real-time event 34")
+SET (GDB_SIGNAL_REALTIME_35, 47, "SIG35", "Real-time event 35")
+SET (GDB_SIGNAL_REALTIME_36, 48, "SIG36", "Real-time event 36")
+SET (GDB_SIGNAL_REALTIME_37, 49, "SIG37", "Real-time event 37")
+SET (GDB_SIGNAL_REALTIME_38, 50, "SIG38", "Real-time event 38")
+SET (GDB_SIGNAL_REALTIME_39, 51, "SIG39", "Real-time event 39")
+SET (GDB_SIGNAL_REALTIME_40, 52, "SIG40", "Real-time event 40")
+SET (GDB_SIGNAL_REALTIME_41, 53, "SIG41", "Real-time event 41")
+SET (GDB_SIGNAL_REALTIME_42, 54, "SIG42", "Real-time event 42")
+SET (GDB_SIGNAL_REALTIME_43, 55, "SIG43", "Real-time event 43")
+SET (GDB_SIGNAL_REALTIME_44, 56, "SIG44", "Real-time event 44")
+SET (GDB_SIGNAL_REALTIME_45, 57, "SIG45", "Real-time event 45")
+SET (GDB_SIGNAL_REALTIME_46, 58, "SIG46", "Real-time event 46")
+SET (GDB_SIGNAL_REALTIME_47, 59, "SIG47", "Real-time event 47")
+SET (GDB_SIGNAL_REALTIME_48, 60, "SIG48", "Real-time event 48")
+SET (GDB_SIGNAL_REALTIME_49, 61, "SIG49", "Real-time event 49")
+SET (GDB_SIGNAL_REALTIME_50, 62, "SIG50", "Real-time event 50")
+SET (GDB_SIGNAL_REALTIME_51, 63, "SIG51", "Real-time event 51")
+SET (GDB_SIGNAL_REALTIME_52, 64, "SIG52", "Real-time event 52")
+SET (GDB_SIGNAL_REALTIME_53, 65, "SIG53", "Real-time event 53")
+SET (GDB_SIGNAL_REALTIME_54, 66, "SIG54", "Real-time event 54")
+SET (GDB_SIGNAL_REALTIME_55, 67, "SIG55", "Real-time event 55")
+SET (GDB_SIGNAL_REALTIME_56, 68, "SIG56", "Real-time event 56")
+SET (GDB_SIGNAL_REALTIME_57, 69, "SIG57", "Real-time event 57")
+SET (GDB_SIGNAL_REALTIME_58, 70, "SIG58", "Real-time event 58")
+SET (GDB_SIGNAL_REALTIME_59, 71, "SIG59", "Real-time event 59")
+SET (GDB_SIGNAL_REALTIME_60, 72, "SIG60", "Real-time event 60")
+SET (GDB_SIGNAL_REALTIME_61, 73, "SIG61", "Real-time event 61")
+SET (GDB_SIGNAL_REALTIME_62, 74, "SIG62", "Real-time event 62")
+SET (GDB_SIGNAL_REALTIME_63, 75, "SIG63", "Real-time event 63")
+
+/* Used internally by Solaris threads.  See signal(5) on Solaris.  */
+SET (GDB_SIGNAL_CANCEL, 76, "SIGCANCEL", "LWP internal signal")
+
+/* Yes, this pains me, too.  But LynxOS didn't have SIG32, and now
+   GNU/Linux does, and we can't disturb the numbering, since it's
+   part of the remote protocol.  Note that in some GDB's
+   GDB_SIGNAL_REALTIME_32 is number 76.  */
+SET (GDB_SIGNAL_REALTIME_32, 77, "SIG32", "Real-time event 32")
+/* Yet another pain, IRIX 6 has SIG64. */
+SET (GDB_SIGNAL_REALTIME_64, 78, "SIG64", "Real-time event 64")
+/* Yet another pain, GNU/Linux MIPS might go up to 128. */
+SET (GDB_SIGNAL_REALTIME_65, 79, "SIG65", "Real-time event 65")
+SET (GDB_SIGNAL_REALTIME_66, 80, "SIG66", "Real-time event 66")
+SET (GDB_SIGNAL_REALTIME_67, 81, "SIG67", "Real-time event 67")
+SET (GDB_SIGNAL_REALTIME_68, 82, "SIG68", "Real-time event 68")
+SET (GDB_SIGNAL_REALTIME_69, 83, "SIG69", "Real-time event 69")
+SET (GDB_SIGNAL_REALTIME_70, 84, "SIG70", "Real-time event 70")
+SET (GDB_SIGNAL_REALTIME_71, 85, "SIG71", "Real-time event 71")
+SET (GDB_SIGNAL_REALTIME_72, 86, "SIG72", "Real-time event 72")
+SET (GDB_SIGNAL_REALTIME_73, 87, "SIG73", "Real-time event 73")
+SET (GDB_SIGNAL_REALTIME_74, 88, "SIG74", "Real-time event 74")
+SET (GDB_SIGNAL_REALTIME_75, 89, "SIG75", "Real-time event 75")
+SET (GDB_SIGNAL_REALTIME_76, 90, "SIG76", "Real-time event 76")
+SET (GDB_SIGNAL_REALTIME_77, 91, "SIG77", "Real-time event 77")
+SET (GDB_SIGNAL_REALTIME_78, 92, "SIG78", "Real-time event 78")
+SET (GDB_SIGNAL_REALTIME_79, 93, "SIG79", "Real-time event 79")
+SET (GDB_SIGNAL_REALTIME_80, 94, "SIG80", "Real-time event 80")
+SET (GDB_SIGNAL_REALTIME_81, 95, "SIG81", "Real-time event 81")
+SET (GDB_SIGNAL_REALTIME_82, 96, "SIG82", "Real-time event 82")
+SET (GDB_SIGNAL_REALTIME_83, 97, "SIG83", "Real-time event 83")
+SET (GDB_SIGNAL_REALTIME_84, 98, "SIG84", "Real-time event 84")
+SET (GDB_SIGNAL_REALTIME_85, 99, "SIG85", "Real-time event 85")
+SET (GDB_SIGNAL_REALTIME_86, 100, "SIG86", "Real-time event 86")
+SET (GDB_SIGNAL_REALTIME_87, 101, "SIG87", "Real-time event 87")
+SET (GDB_SIGNAL_REALTIME_88, 102, "SIG88", "Real-time event 88")
+SET (GDB_SIGNAL_REALTIME_89, 103, "SIG89", "Real-time event 89")
+SET (GDB_SIGNAL_REALTIME_90, 104, "SIG90", "Real-time event 90")
+SET (GDB_SIGNAL_REALTIME_91, 105, "SIG91", "Real-time event 91")
+SET (GDB_SIGNAL_REALTIME_92, 106, "SIG92", "Real-time event 92")
+SET (GDB_SIGNAL_REALTIME_93, 107, "SIG93", "Real-time event 93")
+SET (GDB_SIGNAL_REALTIME_94, 108, "SIG94", "Real-time event 94")
+SET (GDB_SIGNAL_REALTIME_95, 109, "SIG95", "Real-time event 95")
+SET (GDB_SIGNAL_REALTIME_96, 110, "SIG96", "Real-time event 96")
+SET (GDB_SIGNAL_REALTIME_97, 111, "SIG97", "Real-time event 97")
+SET (GDB_SIGNAL_REALTIME_98, 112, "SIG98", "Real-time event 98")
+SET (GDB_SIGNAL_REALTIME_99, 113, "SIG99", "Real-time event 99")
+SET (GDB_SIGNAL_REALTIME_100, 114, "SIG100", "Real-time event 100")
+SET (GDB_SIGNAL_REALTIME_101, 115, "SIG101", "Real-time event 101")
+SET (GDB_SIGNAL_REALTIME_102, 116, "SIG102", "Real-time event 102")
+SET (GDB_SIGNAL_REALTIME_103, 117, "SIG103", "Real-time event 103")
+SET (GDB_SIGNAL_REALTIME_104, 118, "SIG104", "Real-time event 104")
+SET (GDB_SIGNAL_REALTIME_105, 119, "SIG105", "Real-time event 105")
+SET (GDB_SIGNAL_REALTIME_106, 120, "SIG106", "Real-time event 106")
+SET (GDB_SIGNAL_REALTIME_107, 121, "SIG107", "Real-time event 107")
+SET (GDB_SIGNAL_REALTIME_108, 122, "SIG108", "Real-time event 108")
+SET (GDB_SIGNAL_REALTIME_109, 123, "SIG109", "Real-time event 109")
+SET (GDB_SIGNAL_REALTIME_110, 124, "SIG110", "Real-time event 110")
+SET (GDB_SIGNAL_REALTIME_111, 125, "SIG111", "Real-time event 111")
+SET (GDB_SIGNAL_REALTIME_112, 126, "SIG112", "Real-time event 112")
+SET (GDB_SIGNAL_REALTIME_113, 127, "SIG113", "Real-time event 113")
+SET (GDB_SIGNAL_REALTIME_114, 128, "SIG114", "Real-time event 114")
+SET (GDB_SIGNAL_REALTIME_115, 129, "SIG115", "Real-time event 115")
+SET (GDB_SIGNAL_REALTIME_116, 130, "SIG116", "Real-time event 116")
+SET (GDB_SIGNAL_REALTIME_117, 131, "SIG117", "Real-time event 117")
+SET (GDB_SIGNAL_REALTIME_118, 132, "SIG118", "Real-time event 118")
+SET (GDB_SIGNAL_REALTIME_119, 133, "SIG119", "Real-time event 119")
+SET (GDB_SIGNAL_REALTIME_120, 134, "SIG120", "Real-time event 120")
+SET (GDB_SIGNAL_REALTIME_121, 135, "SIG121", "Real-time event 121")
+SET (GDB_SIGNAL_REALTIME_122, 136, "SIG122", "Real-time event 122")
+SET (GDB_SIGNAL_REALTIME_123, 137, "SIG123", "Real-time event 123")
+SET (GDB_SIGNAL_REALTIME_124, 138, "SIG124", "Real-time event 124")
+SET (GDB_SIGNAL_REALTIME_125, 139, "SIG125", "Real-time event 125")
+SET (GDB_SIGNAL_REALTIME_126, 140, "SIG126", "Real-time event 126")
+SET (GDB_SIGNAL_REALTIME_127, 141, "SIG127", "Real-time event 127")
+
+SET (GDB_SIGNAL_INFO, 142, "SIGINFO", "Information request")
+
+/* Some signal we don't know about.  */
+SET (GDB_SIGNAL_UNKNOWN, 143, NULL, "Unknown signal")
+
+/* Use whatever signal we use when one is not specifically specified
+   (for passing to proceed and so on).  */
+SET (GDB_SIGNAL_DEFAULT, 144, NULL,
+     "Internal error: printing GDB_SIGNAL_DEFAULT")
+
+/* Mach exceptions.  In versions of GDB before 5.2, these were just before
+   GDB_SIGNAL_INFO if you were compiling on a Mach host (and missing
+   otherwise).  */
+SET (GDB_EXC_BAD_ACCESS, 145, "EXC_BAD_ACCESS", "Could not access memory")
+SET (GDB_EXC_BAD_INSTRUCTION, 146, "EXC_BAD_INSTRUCTION",
+     "Illegal instruction/operand")
+SET (GDB_EXC_ARITHMETIC, 147, "EXC_ARITHMETIC", "Arithmetic exception")
+SET (GDB_EXC_EMULATION, 148, "EXC_EMULATION", "Emulation instruction")
+SET (GDB_EXC_SOFTWARE, 149, "EXC_SOFTWARE", "Software generated exception")
+SET (GDB_EXC_BREAKPOINT, 150, "EXC_BREAKPOINT", "Breakpoint")
+
+/* If you are adding a new signal, add it just above this comment.  */
+
+/* Last and unused enum value, for sizing arrays, etc.  */
+SET (GDB_SIGNAL_LAST, 151, NULL, "GDB_SIGNAL_LAST")
diff --git a/gdbserver/signals.h b/gdbserver/signals.h
new file mode 100644
index 0000000..f7ed973
--- /dev/null
+++ b/gdbserver/signals.h
@@ -0,0 +1,58 @@
+/* Target signal numbers for GDB and the GDB remote protocol.
+   Copyright (C) 1986-2015 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef GDB_SIGNALS_H
+#define GDB_SIGNALS_H
+
+/* The numbering of these signals is chosen to match traditional unix
+   signals (insofar as various unices use the same numbers, anyway).
+   It is also the numbering of the GDB remote protocol.  Other remote
+   protocols, if they use a different numbering, should make sure to
+   translate appropriately.
+
+   Since these numbers have actually made it out into other software
+   (stubs, etc.), you mustn't disturb the assigned numbering.  If you
+   need to add new signals here, add them to the end of the explicitly
+   numbered signals, at the comment marker.  Add them unconditionally,
+   not within any #if or #ifdef.
+
+   This is based strongly on Unix/POSIX signals for several reasons:
+   (1) This set of signals represents a widely-accepted attempt to
+   represent events of this sort in a portable fashion, (2) we want a
+   signal to make it from wait to child_wait to the user intact, (3) many
+   remote protocols use a similar encoding.  However, it is
+   recognized that this set of signals has limitations (such as not
+   distinguishing between various kinds of SIGSEGV, or not
+   distinguishing hitting a breakpoint from finishing a single step).
+   So in the future we may get around this either by adding additional
+   signals for breakpoint, single-step, etc., or by adding signal
+   codes; the latter seems more in the spirit of what BSD, System V,
+   etc. are doing to address these issues.  */
+
+/* For an explanation of what each signal means, see
+   gdb_signal_to_string.  */
+
+enum gdb_signal
+  {
+#define SET(symbol, constant, name, string) \
+    symbol = constant,
+#include "signals.def"
+#undef SET
+  };
+
+#endif /* #ifndef GDB_SIGNALS_H */
diff --git a/gdbserver/x86_64/gdb_get_regs.c b/gdbserver/x86_64/gdb_get_regs.c
new file mode 100644
index 0000000..ecd103a
--- /dev/null
+++ b/gdbserver/x86_64/gdb_get_regs.c
@@ -0,0 +1,129 @@
+long
+gdb_get_regs (pid_t pid, void *io)
+{
+#include <sys/user.h>
+/* for struct iovec */
+#include <sys/uio.h>
+
+// Begin adapted from linux/x86_64/arch_regs.c
+       struct i386_user_regs_struct {
+               uint32_t ebx;
+               uint32_t ecx;
+               uint32_t edx;
+               uint32_t esi;
+               uint32_t edi;
+               uint32_t ebp;
+               uint32_t eax;
+               uint32_t xds;
+               uint32_t xes;
+               uint32_t xfs;
+               uint32_t xgs;
+               uint32_t orig_eax;
+               uint32_t eip;
+               uint32_t xcs;
+               uint32_t eflags;
+               uint32_t esp;
+               uint32_t xss;
+       };
+       struct iovec *x86_io;
+       static union {
+               struct user_regs_struct      x86_64_r;
+               struct i386_user_regs_struct i386_r;
+       } *x86_regs_union;
+
+       x86_io = (struct iovec*)io;
+       x86_regs_union = x86_io->iov_base;
+
+#define x86_64_regs x86_regs_union->x86_64_r
+#define i386_regs   x86_regs_union->i386_r
+// End adapted from linux/x86_64/arch_regs.c
+
+        int get_regs_error;
+        size_t size;
+        char *regs = gdb_get_all_regs(pid, &size);
+       struct tcb *tcp = pid2tcb(pid);
+        if (regs) {
+                if (size == 0 || regs[0] == 'E') {
+                        get_regs_error = -1;
+                        free(regs);
+                        return get_regs_error;
+                }
+
+               if (size == 624) {
+                       get_regs_error = 0;
+                       x86_io->iov_len = sizeof(i386_regs);
+
+                       /* specified in 32bit-core.xml */
+                       i386_regs.eax = be32toh(gdb_decode_hex_n(&regs[0], 8));
+                       i386_regs.ecx = be32toh(gdb_decode_hex_n(&regs[8], 8));
+                       i386_regs.edx = be32toh(gdb_decode_hex_n(&regs[16], 8));
+                       i386_regs.ebx = be32toh(gdb_decode_hex_n(&regs[24], 8));
+                       i386_regs.esp = be32toh(gdb_decode_hex_n(&regs[32], 8));
+                       i386_regs.ebp = be32toh(gdb_decode_hex_n(&regs[40], 8));
+                       i386_regs.esi = be32toh(gdb_decode_hex_n(&regs[48], 8));
+                       i386_regs.edi = be32toh(gdb_decode_hex_n(&regs[56], 8));
+                       i386_regs.eip = be32toh(gdb_decode_hex_n(&regs[60], 8));
+                       i386_regs.eflags = be32toh(gdb_decode_hex_n(&regs[68], 
8));
+                       i386_regs.xcs = be32toh(gdb_decode_hex_n(&regs[76], 8));
+                       i386_regs.xss = be32toh(gdb_decode_hex_n(&regs[84], 8));
+                       i386_regs.xds = be32toh(gdb_decode_hex_n(&regs[92], 8));
+                       i386_regs.xes = be32toh(gdb_decode_hex_n(&regs[100], 
8));
+                       i386_regs.xfs = be32toh(gdb_decode_hex_n(&regs[108], 
8));
+                       i386_regs.xgs = be32toh(gdb_decode_hex_n(&regs[116], 
8));
+
+                       /* specified in 32bit-linux.xml */
+                       i386_regs.orig_eax = 
be64toh(gdb_decode_hex_n(&regs[616], 8));
+
+                       update_personality(tcp, 1);
+                       free(regs);
+                       return 0;
+               }
+
+               else if (size >= 1088) {
+                       get_regs_error = 0;
+                       x86_io->iov_len = sizeof(x86_64_regs);
+
+                       /* specified in 64bit-core.xml */
+                       x86_64_regs.rax = be64toh(gdb_decode_hex_n(&regs[0], 
16));
+                       x86_64_regs.rbx = be64toh(gdb_decode_hex_n(&regs[16], 
16));
+                       x86_64_regs.rcx = be64toh(gdb_decode_hex_n(&regs[32], 
16));
+                       x86_64_regs.rdx = be64toh(gdb_decode_hex_n(&regs[48], 
16));
+                       x86_64_regs.rsi = be64toh(gdb_decode_hex_n(&regs[64], 
16));
+                       x86_64_regs.rdi = be64toh(gdb_decode_hex_n(&regs[80], 
16));
+                       x86_64_regs.rbp = be64toh(gdb_decode_hex_n(&regs[96], 
16));
+                       x86_64_regs.rsp = be64toh(gdb_decode_hex_n(&regs[112], 
16));
+                       x86_64_regs.r8  = be64toh(gdb_decode_hex_n(&regs[128], 
16));
+                       x86_64_regs.r9  = be64toh(gdb_decode_hex_n(&regs[144], 
16));
+                       x86_64_regs.r10 = be64toh(gdb_decode_hex_n(&regs[160], 
16));
+                       x86_64_regs.r11 = be64toh(gdb_decode_hex_n(&regs[176], 
16));
+                       x86_64_regs.r12 = be64toh(gdb_decode_hex_n(&regs[192], 
16));
+                       x86_64_regs.r13 = be64toh(gdb_decode_hex_n(&regs[208], 
16));
+                       x86_64_regs.r14 = be64toh(gdb_decode_hex_n(&regs[224], 
16));
+                       x86_64_regs.r15 = be64toh(gdb_decode_hex_n(&regs[240], 
16));
+                       x86_64_regs.rip = be64toh(gdb_decode_hex_n(&regs[256], 
16));
+                       x86_64_regs.eflags = 
be32toh(gdb_decode_hex_n(&regs[272], 8));
+                       x86_64_regs.cs = be32toh(gdb_decode_hex_n(&regs[280], 
8));
+                       x86_64_regs.ss = be32toh(gdb_decode_hex_n(&regs[288], 
8));
+                       x86_64_regs.ds = be32toh(gdb_decode_hex_n(&regs[296], 
8));
+                       x86_64_regs.es = be32toh(gdb_decode_hex_n(&regs[304], 
8));
+                       x86_64_regs.fs = be32toh(gdb_decode_hex_n(&regs[312], 
8));
+                       x86_64_regs.gs = be32toh(gdb_decode_hex_n(&regs[320], 
8));
+
+                       /* specified in 64bit-linux.xml */
+                       x86_64_regs.orig_rax = 
be64toh(gdb_decode_hex_n(&regs[1072], 16));
+
+                       update_personality(tcp, 0);
+                       free(regs);
+                       return get_regs_error;
+               }
+
+                else {
+                        get_regs_error = -1;
+                        free(regs);
+                        return get_regs_error;
+                }
+       }
+        return -1;
+}
+
+
diff --git a/ipc_shm.c b/ipc_shm.c
index 7284b18..9503f81 100644
--- a/ipc_shm.c
+++ b/ipc_shm.c
@@ -78,7 +78,7 @@ SYS_FUNC(shmat)
                                uint64_t r64;
                                uint32_t r32;
                        } u;
-                       if (umoven(tcp, tcp->u_arg[2], current_wordsize, &u) < 
0)
+                       if (backend.umoven(tcp, tcp->u_arg[2], current_wordsize, 
&u) < 0)
                                return RVAL_NONE;
                        tcp->u_rval = (sizeof(u.r32) == current_wordsize)
                                      ? u.r32 : u.r64;
diff --git a/mem.c b/mem.c
index 00f5610..78b2bd0 100644
--- a/mem.c
+++ b/mem.c
@@ -276,7 +276,7 @@ SYS_FUNC(mincore)
                len = len / page_size + (len & page_mask ? 1 : 0);
                if (syserror(tcp) || !verbose(tcp) ||
                    !tcp->u_arg[2] || !(vec = malloc(len)) ||
-                   umoven(tcp, tcp->u_arg[2], len, vec) < 0)
+                   backend.umoven(tcp, tcp->u_arg[2], len, vec) < 0)
                        printaddr(tcp->u_arg[2]);
                else {
                        unsigned long i;
diff --git a/msghdr.c b/msghdr.c
index dd9c430..00b60cf 100644
--- a/msghdr.c
+++ b/msghdr.c
@@ -297,7 +297,7 @@ decode_msg_control(struct tcb *const tcp, const kernel_ulong_t addr,
                                   ? get_optmem_max() : in_control_len;
        unsigned int buf_len = control_len;
        char *buf = buf_len < cmsg_size ? NULL : malloc(buf_len);
-       if (!buf || umoven(tcp, addr, buf_len, buf) < 0) {
+       if (!buf || backend.umoven(tcp, addr, buf_len, buf) < 0) {
                printaddr(addr);
                free(buf);
                return;
diff --git a/pathtrace.c b/pathtrace.c
index 5258238..39f5b17 100644
--- a/pathtrace.c
+++ b/pathtrace.c
@@ -59,7 +59,7 @@ upathmatch(struct tcb *const tcp, const kernel_ulong_t upath,
 {
        char path[PATH_MAX + 1];

-       return umovestr(tcp, upath, sizeof(path), path) > 0 &&
+       return backend.umovestr(tcp, upath, sizeof(path), path) > 0 &&
                pathmatch(path, set);
 }

@@ -70,7 +70,7 @@ static bool
 fdmatch(struct tcb *tcp, int fd, struct path_set *set)
 {
        char path[PATH_MAX + 1];
-       int n = getfdpath(tcp, fd, path, sizeof(path));
+       int n = backend.getfdpath(tcp, fd, path, sizeof(path));

        return n >= 0 && pathmatch(path, set);
 }
@@ -296,7 +296,7 @@ pathtrace_match_set(struct tcb *tcp, struct path_set *set)
                for (i = 1; i <= 3; ++i) {
                        if (args[i] == 0)
                                continue;
-                       if (umoven(tcp, args[i], fdsize, fds) < 0) {
+                       if (backend.umoven(tcp, args[i], fdsize, fds) < 0) {
                                continue;
                        }
                        for (j = 0;; j++) {
diff --git a/rt_sigreturn.c b/rt_sigreturn.c
index af705c3..38f1e94 100644
--- a/rt_sigreturn.c
+++ b/rt_sigreturn.c
@@ -27,6 +27,29 @@

 #include "defs.h"

+/* Avoids the "#define si_pid _sifields._kill.si_pid" in siginfo.h */
+
+#pragma push_macro ("si_pid")
+#undef si_pid
+#pragma push_macro ("si_overrun")
+#undef si_overrun
+#pragma push_macro ("si_addr")
+#undef si_addr
+#pragma push_macro ("si_band")
+#undef si_band
+#pragma push_macro ("si_uid")
+#undef si_uid
+#pragma push_macro ("si_status")
+#undef si_status
+#pragma push_macro ("si_utime")
+#undef si_utime
+#pragma push_macro ("si_fd")
+#undef si_fd
+#pragma push_macro ("si_addr_lsb")
+#undef si_addr_lsb
+#pragma push_macro ("si_stime")
+#undef si_stime
+
 #include DEF_MPERS_TYPE(struct_rt_sigframe)

 #include "rt_sigframe.h"
@@ -38,6 +61,17 @@
                offsetof(struct_rt_sigframe, uc.uc_sigmask)
 #endif

+#pragma pop_macro ("si_pid")
+#pragma pop_macro ("si_overrun")
+#pragma pop_macro ("si_addr")
+#pragma pop_macro ("si_band")
+#pragma pop_macro ("si_uid")
+#pragma pop_macro ("si_status")
+#pragma pop_macro ("si_utime")
+#pragma pop_macro ("si_fd")
+#pragma pop_macro ("si_addr_lsb")
+#pragma pop_macro ("si_stime")
+
 SYS_FUNC(rt_sigreturn)
 {
        const kernel_ulong_t sf_addr = get_rt_sigframe_addr(tcp);
diff --git a/strace.c b/strace.c
index 6ed86a6..bec7e4f 100644
--- a/strace.c
+++ b/strace.c
@@ -129,11 +129,11 @@ bool not_failing_only;
 /* Show path associated with fd arguments */
 unsigned int show_fd_path;

-static bool detach_on_execve;
+bool detach_on_execve;

 static int exit_code;
-static int strace_child;
-static int strace_tracer_pid;
+int strace_child = 0;
+static int strace_tracer_pid = 0;

 static const char *username;
 static uid_t run_uid;
@@ -147,8 +147,8 @@ static const char *outfname;
 /* If -ff, points to stderr. Else, it's our common output log */
 static FILE *shared_log;

-struct tcb *printing_tcp;
-static struct tcb *current_tcp;
+struct tcb *printing_tcp = NULL;
+struct tcb *current_tcp;

 static struct tcb **tcbtab;
 static unsigned int nprocs, tcbtabsize;
@@ -163,6 +163,12 @@ static void detach(struct tcb *tcp);
 static void cleanup(void);
 static void interrupt(int sig);
 static sigset_t start_set, blocked_set;
+static enum trace_event next_event (int *pstatus, void *si);
+static bool dispatch_event (enum trace_event, int *pstatus, void *si);
+
+int    umoven(struct tcb *const tcp, kernel_ulong_t addr, unsigned int len,
+               void *const our_addr);
+int    default_arch_get_scno(struct tcb *tcp);

 #ifdef HAVE_SIG_ATOMIC_T
 static volatile sig_atomic_t interrupted;
@@ -664,7 +670,7 @@ tabto(void)
  * Otherwise, "strace -oFILE -ff -p<nonexistant_pid>"
  * may create bogus empty FILE.<nonexistant_pid>, and then die.
  */
-static void
+void
 newoutf(struct tcb *tcp)
 {
        tcp->outf = shared_log; /* if not -ff mode, the same file is for all */
@@ -699,7 +705,7 @@ expand_tcbtab(void)
                tcbtab[tcbtabsize++] = newtcbs++;
 }

-static struct tcb *
+struct tcb *
 alloctcb(int pid)
 {
        unsigned int i;
@@ -763,7 +769,7 @@ free_tcb_priv_data(struct tcb *tcp)
        }
 }

-static void
+void
 droptcb(struct tcb *tcp)
 {
        if (tcp->pid == 0)
@@ -1111,7 +1117,7 @@ startup_attach(void)
                        continue;
                }

-               attach_tcb(tcp);
+               backend.attach_tcb(tcp);

                if (interactive) {
                        sigprocmask(SIG_SETMASK, &start_set, NULL);
@@ -1539,7 +1545,22 @@ get_os_release(void)
        return rel;
 }

-static void
+
+static bool
+prog_pid_check (char *exec_name, int nprocs)
+{
+       return exec_name || nprocs;
+}
+
+
+static bool
+handle_arg (char arg, char *optarg)
+{
+       return false;
+}
+
+
+void
set_sigaction(int signo, void (*sighandler)(int), struct sigaction *oldact)
 {
        /* if signal handler is a function, add the signal to blocked_set */
@@ -1550,6 +1571,21 @@ set_sigaction(int signo, void (*sighandler)(int), struct sigaction *oldact)
        sigaction(signo, &sa, oldact);
 }

+
+static void
+end_init(void) {}
+
+static void
+final_cleanup(void) {}
+
+static bool
+verify_args(const char *username, bool daemon, unsigned int *follow_fork)
+{return true;}
+
+static bool
+start_init(void)
+{return true;}
+
 /*
  * Initialization part of main() was eating much stack (~0.5k),
  * which was unused after init.
@@ -1564,6 +1600,25 @@ init(int argc, char *argv[])
        int c, i;
        int optF = 0;

+       backend.attach_tcb = attach_tcb;
+       backend.cleanup = final_cleanup;
+       backend.detach = detach;
+       backend.dispatch_event = dispatch_event;
+       backend.end_init = end_init;
+       backend.get_regs = NULL;
+       backend.get_scno = default_arch_get_scno;
+       backend.getfdpath = getfdpath;
+       backend.handle_arg = handle_arg;
+       backend.next_event = next_event;
+       backend.prog_pid_check = prog_pid_check;
+       backend.start_init = start_init;
+       backend.startup_child = startup_child;
+       backend.umoven = umoven;
+       backend.umovestr = umovestr;
+       backend.upeek_ = upeek;
+       backend.upoke_ = upoke;
+       backend.verify_args = verify_args;
+
        if (!program_invocation_name || !*program_invocation_name) {
                static char name[] = "strace";
                program_invocation_name =
@@ -1590,6 +1645,7 @@ init(int argc, char *argv[])
                "k"
 #endif
                "D"
+               "G:"
                "a:e:o:O:p:s:S:u:E:P:I:")) != EOF) {
                switch (c) {
                case 'b':
@@ -1694,6 +1750,11 @@ init(int argc, char *argv[])
                case 'u':
                        username = optarg;
                        break;
+#ifdef ENABLE_GDBSERVER
+               case 'G':
+                       gdb_handle_arg (c, optarg);
+                       break;
+#endif
 #ifdef USE_LIBUNWIND
                case 'k':
                        stack_trace_enabled = true;
@@ -1709,7 +1770,8 @@ init(int argc, char *argv[])
                                error_opt_arg(c, optarg);
                        break;
                default:
-                       error_msg_and_help(NULL);
+                       if (! backend.handle_arg(c, optarg))
+                               error_msg_and_help(NULL);
                        break;
                }
        }
@@ -1717,7 +1779,7 @@ init(int argc, char *argv[])
        argv += optind;
        argc -= optind;

-       if (argc < 0 || (!argv[0] && !nprocs)) {
+       if (argc < 0 || ! backend.prog_pid_check(argv[0], nprocs)) {
                error_msg_and_help("must have PROG [ARGS] or -p PID");
        }

@@ -1728,6 +1790,8 @@ init(int argc, char *argv[])
        if (!followfork)
                followfork = optF;

+       backend.verify_args(username, daemonized_tracer, &followfork);
+
        if (followfork >= 2 && cflag) {
                error_msg_and_help("(-c or -C) and -ff are mutually exclusive");
        }
@@ -1759,6 +1823,9 @@ init(int argc, char *argv[])
                tflag = 1;
        }

+       if (backend.start_init && ! backend.start_init())
+                       error_msg_and_die("Cannot initialize backend on this 
target.");
+
        acolumn_spaces = xmalloc(acolumn + 1);
        memset(acolumn_spaces, ' ', acolumn);
        acolumn_spaces[acolumn] = '\0';
@@ -1865,7 +1932,7 @@ init(int argc, char *argv[])
         * in the startup_child() mode we kill the spawned process anyway.
         */
        if (argv[0]) {
-               startup_child(argv);
+               backend.startup_child(argv);
        }

        set_sigaction(SIGTTOU, SIG_IGN, NULL);
@@ -1889,6 +1956,9 @@ init(int argc, char *argv[])
        if (nprocs != 0 || daemonized_tracer)
                startup_attach();

+       if (backend.end_init)
+               backend.end_init();
+
        /* Do we want pids printed in our -o OUTFILE?
         * -ff: no (every pid has its own file); or
         * -f: yes (there can be more pids in the future); or
@@ -1897,7 +1967,7 @@ init(int argc, char *argv[])
print_pid_pfx = (outfname && followfork < 2 && (followfork == 1 || nprocs > 1));
 }

-static struct tcb *
+struct tcb *
 pid2tcb(int pid)
 {
        unsigned int i;
@@ -1936,10 +2006,12 @@ cleanup(void)
                        kill(tcp->pid, SIGCONT);
                        kill(tcp->pid, fatal_sig);
                }
-               detach(tcp);
+               backend.detach(tcp);
        }
        if (cflag)
                call_summary(shared_log);
+
+       backend.cleanup();
 }

 static void
@@ -2077,7 +2149,7 @@ maybe_switch_tcbs(struct tcb *tcp, const int pid)
        return tcp;
 }

-static void
+void
 print_signalled(struct tcb *tcp, const int pid, int status)
 {
        if (pid == strace_child) {
@@ -2100,7 +2172,7 @@ print_signalled(struct tcb *tcp, const int pid, int status)
        }
 }

-static void
+void
 print_exited(struct tcb *tcp, const int pid, int status)
 {
        if (pid == strace_child) {
@@ -2116,7 +2188,7 @@ print_exited(struct tcb *tcp, const int pid, int status)
        }
 }

-static void
+void
print_stopped(struct tcb *tcp, const siginfo_t *si, const unsigned int sig)
 {
        if (cflag != CFLAG_ONLY_STATS
@@ -2194,67 +2266,10 @@ print_event_exit(struct tcb *tcp)
        line_ended();
 }

-enum trace_event {
-       /* Break the main loop. */
-       TE_BREAK,
-
-       /* Call next_event() again. */
-       TE_NEXT,
-
-       /* Restart the tracee with signal 0 and call next_event() again. */
-       TE_RESTART,
-
-       /*
-        * For all the events below, current_tcp is set to current tracee's
-        * tcb.  All the suggested actions imply that you want to continue
-        * tracing of the current tracee; alternatively, you can detach it.
-        */
-
-       /*
-        * Syscall entry or exit.
-        * Restart the tracee with signal 0, or with an injected signal number.
-        */
-       TE_SYSCALL_STOP,
-
-       /*
-        * Tracee received signal with number WSTOPSIG(*pstatus); signal info
-        * is written to *si.  Restart the tracee (with that signal number
-        * if you want to deliver it).
-        */
-       TE_SIGNAL_DELIVERY_STOP,
-
-       /*
-        * Tracee was killed by a signal with number WTERMSIG(*pstatus).
-        */
-       TE_SIGNALLED,
-
-       /*
-        * Tracee was stopped by a signal with number WSTOPSIG(*pstatus).
-        * Restart the tracee with that signal number.
-        */
-       TE_GROUP_STOP,
-
-       /*
-        * Tracee exited with status WEXITSTATUS(*pstatus).
-        */
-       TE_EXITED,
-
-       /*
-        * Tracee is going to perform execve().
-        * Restart the tracee with signal 0.
-        */
-       TE_STOP_BEFORE_EXECVE,
-
-       /*
-        * Tracee is going to terminate.
-        * Restart the tracee with signal 0.
-        */
-       TE_STOP_BEFORE_EXIT,
-};
-
 static enum trace_event
-next_event(int *pstatus, siginfo_t *si)
+next_event(int *pstatus, void *si_p)
 {
+       siginfo_t *si = (siginfo_t*)si_p;
        int pid;
        int wait_errno;
        int status;
@@ -2406,7 +2421,7 @@ next_event(int *pstatus, siginfo_t *si)
        }
 }

-static int
+int
 trace_syscall(struct tcb *tcp, unsigned int *sig)
 {
        if (entering(tcp)) {
@@ -2432,8 +2447,9 @@ trace_syscall(struct tcb *tcp, unsigned int *sig)

 /* Returns true iff the main trace loop has to continue. */
 static bool
-dispatch_event(enum trace_event ret, int *pstatus, siginfo_t *si)
+dispatch_event(enum trace_event ret, int *pstatus, void *si_p)
 {
+       siginfo_t *si = (siginfo_t*)si_p;
        unsigned int restart_op = PTRACE_SYSCALL;
        unsigned int restart_sig = 0;

@@ -2517,7 +2533,7 @@ dispatch_event(enum trace_event ret, int *pstatus, siginfo_t *si)
                        if (current_tcp->flags & TCB_SKIP_DETACH_ON_FIRST_EXEC) 
{
                                current_tcp->flags &= 
~TCB_SKIP_DETACH_ON_FIRST_EXEC;
                        } else {
-                               detach(current_tcp); /* do "-b execve" thingy */
+                               backend.detach(current_tcp); /* do "-b execve" 
thingy */
                                return true;
                        }
                }
@@ -2584,6 +2600,7 @@ terminate(void)
        exit(exit_code);
 }

+
 int
 main(int argc, char *argv[])
 {
@@ -2593,7 +2610,7 @@ main(int argc, char *argv[])

        int status;
        siginfo_t si;
-       while (dispatch_event(next_event(&status, &si), &status, &si))
+ while (backend.dispatch_event(backend.next_event(&status, &si), &status, &si))
                ;
        terminate();
 }
diff --git a/syscall.c b/syscall.c
index b1047fe..e3e0947 100644
--- a/syscall.c
+++ b/syscall.c
@@ -296,7 +296,7 @@ set_personality(int personality)
 # endif
 }

-static void
+void
 update_personality(struct tcb *tcp, unsigned int personality)
 {
        if (personality == current_personality)
@@ -338,7 +338,7 @@ decode_socket_subcall(struct tcb *tcp)
        const unsigned int nargs = sysent[scno].nargs;
        uint64_t buf[nargs];

-       if (umoven(tcp, tcp->u_arg[1], nargs * current_wordsize, buf) < 0)
+       if (backend.umoven(tcp, tcp->u_arg[1], nargs * current_wordsize, buf) < 
0)
                return;

        tcp->scno = scno;
@@ -409,7 +409,7 @@ decode_mips_subcall(struct tcb *tcp)
         * see linux/mips/get_syscall_args.c
         */
        if (tcp->s_ent->nargs == MAX_ARGS) {
-               if (umoven(tcp,
+               if (backend.umoven(tcp,
                           mips_REG_SP + MAX_ARGS * sizeof(tcp->u_arg[0]),
                           sizeof(tcp->u_arg[0]),
                           &tcp->u_arg[MAX_ARGS - 1]) < 0)
@@ -1111,6 +1111,9 @@ ptrace_setregs(pid_t pid)
 static void
 get_regs(pid_t pid)
 {
+       if (backend.get_regs)
+ get_regs_error = backend.get_regs (pid, (struct iovec*)&ARCH_IOVEC_FOR_GETREGSET);
+
 #undef USE_GET_SYSCALL_RESULT_REGS
 #ifdef ptrace_getregset_or_getregs

@@ -1155,6 +1158,9 @@ get_regs(pid_t pid)
 static int
 set_regs(pid_t pid)
 {
+       if (backend.set_regs)
+ return = backend.set_regs (pid, (struct iovec*)&ARCH_IOVEC_FOR_GETREGSET);
+
        return ptrace_setregset_or_setregs(pid);
 }
 #endif /* ptrace_setregset_or_setregs */
@@ -1174,6 +1180,16 @@ free_sysent_buf(void *ptr)
 }

 /*
+ * Default case calls arch_get_scno which sets tcp->scno.
+ * A backend can redefine this to set that in a customized way.
+ */
+int
+default_arch_get_scno(struct tcb *tcp)
+{
+       return arch_get_scno(tcp);
+}
+
+/*
  * Returns:
* 0: "ignore this ptrace stop", syscall_entering_decode() should return a "bail
  *    out silently" code.
@@ -1189,7 +1205,9 @@ get_scno(struct tcb *tcp)
        if (get_regs_error)
                return -1;

-       int rc = arch_get_scno(tcp);
+       int rc;
+       rc = backend.get_scno(tcp);
+
        if (rc != 1)
                return rc;

diff --git a/sysctl.c b/sysctl.c
index 176973a..3d2a9ed 100644
--- a/sysctl.c
+++ b/sysctl.c
@@ -56,7 +56,7 @@ SYS_FUNC(sysctl)
        size = sizeof(int) * (unsigned long) info.nlen;
name = (size / sizeof(int) != (unsigned long) info.nlen) ? NULL : malloc(size);
        if (name == NULL ||
-           umoven(tcp, (unsigned long) info.name, size, name) < 0) {
+           backend.umoven(tcp, (unsigned long) info.name, size, name) < 0) {
                free(name);
                if (entering(tcp))
                        tprintf("{%p, %d, %p, %p, %p, %lu}",
diff --git a/sysmips.c b/sysmips.c
index e095cb3..c57e2d5 100644
--- a/sysmips.c
+++ b/sysmips.c
@@ -55,7 +55,7 @@ SYS_FUNC(sysmips)

                if (!verbose(tcp))
                        break;
-               if (umovestr(tcp, tcp->u_arg[1], (__NEW_UTS_LEN + 1),
+               if (backend.umovestr(tcp, tcp->u_arg[1], (__NEW_UTS_LEN + 1),
                             nodename) < 0) {
                        printaddr(tcp->u_arg[1]);
                } else {
diff --git a/tests/.gitignore b/tests/.gitignore
index dc92930..e7429ad 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -85,6 +85,7 @@ ftruncate
 ftruncate64
 futex
 futimesat
+gdbrsp
 gen_tests.am
 get_mempolicy
 getcpu
diff --git a/tests/Makefile.am b/tests/Makefile.am
index f2109fd..3db3721 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -96,6 +96,7 @@ check_PROGRAMS = $(PURE_EXECUTABLES) \
        execveat-v \
        filter-unavailable \
        fork-f \
+       gdbrsp \
        getpid  \
        getppid \
        gettid \
@@ -202,6 +203,7 @@ DECODER_TESTS = \
        execve.test \
        fadvise64.test \
        futex.test \
+       gdbrsp.test \
        getuid.test \
        ioctl.test \
        ioctl_dm-v.test \
diff --git a/tests/gdbrsp.c b/tests/gdbrsp.c
new file mode 100644
index 0000000..f39a7c9
--- /dev/null
+++ b/tests/gdbrsp.c
@@ -0,0 +1,79 @@
+/* This file is used to test the 'catch syscall' feature on GDB.
+
+   Please, if you are going to edit this file DO NOT change the syscalls
+   being called (nor the order of them).  If you really must do this, then
+   take a look at catch-syscall.exp and modify there too.
+
+   Written by Sergio Durigan Junior <sergi...@linux.vnet.ibm.com>
+   September, 2008
+
+   This is the gdb catch-syscall.c test */
+
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sched.h>
+
+/* These are the syscalls numbers used by the test.  */
+
+int close_syscall = SYS_close;
+int chroot_syscall = SYS_chroot;
+/* GDB had a bug where it couldn't catch syscall number 0 (PR 16297).
+   In most GNU/Linux architectures, syscall number 0 is
+   restart_syscall, which can't be called from userspace.  However,
+   the "read" syscall is zero on x86_64.  */
+int read_syscall = SYS_read;
+#ifdef SYS_pipe
+int pipe_syscall = SYS_pipe;
+#else
+int pipe2_syscall = SYS_pipe2;
+#endif
+int write_syscall = SYS_write;
+#if defined(__arm__)
+/* Although 123456789 is an illegal syscall umber on arm linux, kernel
+   sends SIGILL rather than returns -ENOSYS.  However, arm linux kernel
+   returns -ENOSYS if syscall number is within 0xf0001..0xf07ff, so we
+   can use 0xf07ff for unknown_syscall in test.  */
+int unknown_syscall = 0x0f07ff;
+#else
+int unknown_syscall = 123456789;
+#endif
+int exit_group_syscall = SYS_exit_group;
+
+/* Set by the test when it wants execve.  */
+int do_execve = 0;
+
+int
+main (int argc, char *const argv[])
+{
+       int fd[2];
+       char buf1[2] = "a";
+       char buf2[2];
+
+       /* Test a simple self-exec, but only on request.  */
+       if (do_execve)
+         execv (*argv, argv);
+
+       /* A close() with a wrong argument.  We are only
+          interested in the syscall.  */
+       close (-1);
+
+       chroot (".");
+
+       pipe (fd);
+
+       write (fd[1], buf1, sizeof (buf1));
+       read (fd[0], buf2, sizeof (buf2));
+
+       /* Test vfork-event interactions.  Child exits immediately.
+          (Plain fork won't work on no-mmu kernel configurations.)  */
+       if (vfork () == 0)
+         _exit (0);
+
+       /* Trigger an intentional ENOSYS.  */
+       syscall (unknown_syscall);
+
+       /* The last syscall.  Do not change this.  */
+       _exit (0);
+}
diff --git a/tests/gdbrsp.test b/tests/gdbrsp.test
new file mode 100755
index 0000000..a0b83d7
--- /dev/null
+++ b/tests/gdbrsp.test
@@ -0,0 +1,107 @@
+#!/bin/sh
+#
+# Check -G option: gdb remote serial protocol
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. The name of the author may not be used to endorse or promote products
+#    derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+. "${srcdir=.}/init.sh"
+
+check_prog gdb
+check_prog gdbserver
+
+gdb -batch -iex 'target remote | gdbserver --remote-debug - /usr/bin/true' >| /tmp/,gdb 2>&1
+if grep QCatchSyscalls /tmp/,gdb > /dev/null
+then :
+else framework_skip_ "Correct version of gdbserver is not available"
+fi
+
+run_prog > /dev/null
+
+# all-stop
+gdbserver --once --multi :65432 &
+if [ $? -gt 0 ] ; then
+       framework_skip_ "Unable to start gdbserver"
+fi
+$STRACE -G localhost:65432 $(readlink -f ../gdbrsp) >& $LOG
+
+EXPECTED="$LOG.expected"
+cat > "$EXPECTED" << __EOF__
+close.-1..* = -1 EBADF .Bad file descriptor.
+chroot.* = -1 EPERM .Operation not permitted.*
+pipe..3, 4...* = 0
+write.4, "a.0", 2..* = 2
+read.3, "a.0", 2..* = 2
+vfork.*unfinished.*
+.pid.*exited with 0.*
+.*vfork resumed.* =.*
+__EOF__
+
+match_grep "$LOG" "$EXPECTED"
+rm -f $EXPECTED
+
+# non-stop
+gdbserver --once --multi :65432 &
+if [ $? -gt 0 ] ; then
+       framework_skip_ "Unable to start gdbserver"
+fi
+$STRACE -G 'localhost:65432;non-stop' $(readlink -f ../gdbrsp) >& $LOG
+
+EXPECTED="$LOG.expected"
+cat > "$EXPECTED" << __EOF__
+close.-1..* = -1 EBADF .Bad file descriptor.
+chroot.* = -1 EPERM .Operation not permitted.*
+pipe..3, 4...* = 0
+write.4, "a.0", 2..* = 2
+read.3, "a.0", 2..* = 2
+vfork.*= [0-9][0-9]*
+syscall_123456789.*= -1.*
+exit_group.*
+__EOF__
+
+match_grep "$LOG" "$EXPECTED"
+rm -f $EXPECTED
+
+# -e write,read
+
+if [ $STRACE_ARCH = x86_64 ] ; then
+    gdbserver --once --multi :65432 &
+    if [ $? -gt 0 ] ; then
+       framework_skip_ "Unable to start gdbserver"
+    fi
+ $STRACE -G localhost:65432 -e write,read $(readlink -f ../gdbrsp) >& $LOG
+
+    EXPECTED="$LOG.expected"
+    cat > "$EXPECTED" << __EOF__
+write.4, "a.0", 2..* = 2
+read.3, "a.0", 2..* = 2
+__EOF__
+
+    match_grep "$LOG" "$EXPECTED"
+    rm -f $EXPECTED
+fi
+
+# TODO -pPID
+
+exit 0
diff --git a/upeek.c b/upeek.c
index 22ee82d..d3f96a1 100644
--- a/upeek.c
+++ b/upeek.c
@@ -35,6 +35,8 @@
 #include "defs.h"
 #include "ptrace.h"

+#undef upeek
+
 int
 upeek(int pid, unsigned long off, kernel_ulong_t *res)
 {
diff --git a/upoke.c b/upoke.c
index dda0b8d..be4cea6 100644
--- a/upoke.c
+++ b/upoke.c
@@ -28,6 +28,8 @@
 #include "defs.h"
 #include "ptrace.h"

+#undef upoke
+
 int
 upoke(int pid, unsigned long off, kernel_ulong_t val)
 {
diff --git a/util.c b/util.c
index 05c9fb8..4e77084 100644
--- a/util.c
+++ b/util.c
@@ -427,7 +427,7 @@ void
 printfd(struct tcb *tcp, int fd)
 {
        char path[PATH_MAX + 1];
-       if (show_fd_path && getfdpath(tcp, fd, path, sizeof(path)) >= 0) {
+       if (show_fd_path && backend.getfdpath(tcp, fd, path, sizeof(path)) >= 
0) {
                const char *str;
                size_t len;
                unsigned long inode;
@@ -696,7 +696,7 @@ printpathn(struct tcb *const tcp, const kernel_ulong_t addr, unsigned int n)
                n = sizeof(path) - 1;

        /* Fetch one byte more to find out whether path length > n. */
-       nul_seen = umovestr(tcp, addr, n + 1, path);
+       nul_seen = backend.umovestr(tcp, addr, n + 1, path);
        if (nul_seen < 0)
                printaddr(addr);
        else {
@@ -755,9 +755,9 @@ printstr_ex(struct tcb *const tcp, const kernel_ulong_t addr,
        if (size > len)
                size = len;
        if (style & QUOTE_0_TERMINATED)
-               rc = umovestr(tcp, addr, size, str);
+               rc = backend.umovestr(tcp, addr, size, str);
        else
-               rc = umoven(tcp, addr, size, str);
+               rc = backend.umoven(tcp, addr, size, str);

        if (rc < 0) {
                printaddr(addr);
@@ -814,7 +814,7 @@ dumpiov_upto(struct tcb *const tcp, const int len, const kernel_ulong_t addr,
                error_msg("Out of memory");
                return;
        }
-       if (umoven(tcp, addr, size, iov) >= 0) {
+       if (backend.umoven(tcp, addr, size, iov) >= 0) {
                for (i = 0; i < len; i++) {
                        kernel_ulong_t iov_len = iov_iov_len(i);
                        if (iov_len > data_size)
@@ -864,7 +864,7 @@ dumpstr(struct tcb *const tcp, const kernel_ulong_t addr, const int len)
                strsize = len + 16;
        }

-       if (umoven(tcp, addr, len, str) < 0)
+       if (backend.umoven(tcp, addr, len, str) < 0)
                return;

        /* Space-pad to 16 bytes */
@@ -906,12 +906,13 @@ dumpstr(struct tcb *const tcp, const kernel_ulong_t addr, const int len)
        }
 }

+
 int
 umoven_or_printaddr(struct tcb *const tcp, const kernel_ulong_t addr,
                    const unsigned int len, void *const our_addr)
 {
        if (!addr || !verbose(tcp) || (exiting(tcp) && syserror(tcp)) ||
-           umoven(tcp, addr, len, our_addr) < 0) {
+           backend.umoven(tcp, addr, len, our_addr) < 0) {
                printaddr(addr);
                return -1;
        }
@@ -924,7 +925,7 @@ umoven_or_printaddr_ignore_syserror(struct tcb *const tcp,
                                    const unsigned int len,
                                    void *const our_addr)
 {
-       if (!addr || !verbose(tcp) || umoven(tcp, addr, len, our_addr) < 0) {
+ if (!addr || !verbose(tcp) || backend.umoven(tcp, addr, len, our_addr) < 0) {
                printaddr(addr);
                return -1;
        }

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Strace-devel mailing list
Strace-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/strace-devel

Reply via email to