--- /dev/null
+// Derived from Plan 9 from User Space src/libmach/Linux.c
+// http://code.swtch.com/plan9port/src/tip/src/libmach/Linux.c
+//
+//     Copyright © 1994-1999 Lucent Technologies Inc.
+//     Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
+//     Portions Copyright © 1997-1999 Vita Nuova Limited.
+//     Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
+//     Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
+//     Portions Copyright © 2001-2007 Russ Cox.
+//     Portions Copyright © 2009 The Go Authors.  All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#include <u.h>
+#include <sys/ptrace.h>
+#include <sys/signal.h>
+#include <errno.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach_amd64.h>
+#include <ureg_amd64.h>
+#undef waitpid
+
+typedef struct Ureg Ureg;
+
+static Maprw ptracesegrw;
+static Maprw ptraceregrw;
+
+// /usr/include/asm-x86_64/user.h
+struct user_regs_struct {
+       unsigned long r15,r14,r13,r12,rbp,rbx,r11,r10;
+       unsigned long r9,r8,rax,rcx,rdx,rsi,rdi,orig_rax;
+       unsigned long rip,cs,eflags;
+       unsigned long rsp,ss;
+       unsigned long fs_base, gs_base;
+       unsigned long ds,es,fs,gs; 
+}; 
+
+static int
+isstopped(int pid)
+{
+       char buf[1024];
+       int fd, n;
+       char *p;
+
+       snprint(buf, sizeof buf, "/proc/%d/stat", pid);
+       if((fd = open(buf, OREAD)) < 0)
+               return 0;
+       n = read(fd, buf, sizeof buf-1);
+       close(fd);
+       if(n <= 0)
+               return 0;
+       buf[n] = 0;
+
+       /* command name is in parens, no parens afterward */
+       p = strrchr(buf, ')');
+       if(p == nil || *++p != ' ')
+               return 0;
+       ++p;
+
+       /* next is state - T is stopped for tracing */
+       return *p == 'T';
+}
+
+static int
+waitstop(int pid)
+{
+       int p, status;
+
+       if(isstopped(pid))
+               return 0;
+       for(;;){
+               p = waitpid(pid, &status, WUNTRACED|__WALL);
+               if(p <= 0){
+                       if(errno == ECHILD){
+                               if(isstopped(pid))
+                                       return 0;
+                       }
+                       return -1;
+               }
+               if(WIFEXITED(status) || WIFSTOPPED(status))
+                       return 0;
+       }
+}
+
+static int attachedpids[1000];
+static int nattached;
+
+static int
+ptraceattach(int pid)
+{
+       int i;
+
+       for(i=0; i<nattached; i++)
+               if(attachedpids[i] == pid)
+                       return 0;
+       if(nattached == nelem(attachedpids)){
+               werrstr("attached to too many processes");
+               return -1;
+       }
+
+       if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0){
+               werrstr("ptrace attach %d: %r", pid);
+               return -1;
+       }
+       
+       if(waitstop(pid) < 0){
+               fprint(2, "waitstop %d: %r", pid);
+               ptrace(PTRACE_DETACH, pid, 0, 0);
+               return -1;
+       }
+       attachedpids[nattached++] = pid;
+       return 0;
+}
+
+Map*
+attachproc(int pid, Fhdr *fp)
+{
+       char buf[64];
+       Map *map;
+       vlong n;
+
+       if(ptraceattach(pid) < 0)
+               return nil;
+
+       map = newmap(0, 4);
+       if (!map)
+               return 0;
+       map->pid = pid;
+       if(mach->regsize)
+               setmap(map, -1, 0, mach->regsize, 0, "regs", ptraceregrw);
+//     if(mach->fpregsize)
+//             setmap(map, -1, mach->regsize, mach->regsize+mach->fpregsize, 0, "fpregs", ptraceregrw);
+       setmap(map, -1, fp->txtaddr, fp->txtaddr+fp->txtsz, fp->txtaddr, "*text", ptracesegrw);
+       setmap(map, -1, fp->dataddr, mach->utop, fp->dataddr, "*data", ptracesegrw);
+       return map;
+}
+       
+void
+detachproc(Map *m)
+{
+       if(m->pid > 0)
+               ptrace(PTRACE_DETACH, m->pid, 0, 0);
+       free(m);
+}
+
+/* /proc/pid/stat contains 
+       pid
+       command in parens
+       0. state
+       1. ppid
+       2. pgrp
+       3. session
+       4. tty_nr
+       5. tpgid
+       6. flags (math=4, traced=10)
+       7. minflt
+       8. cminflt
+       9. majflt
+       10. cmajflt
+       11. utime
+       12. stime
+       13. cutime
+       14. cstime
+       15. priority
+       16. nice
+       17. 0
+       18. itrealvalue
+       19. starttime
+       20. vsize
+       21. rss
+       22. rlim
+       23. startcode
+       24. endcode
+       25. startstack
+       26. kstkesp
+       27. kstkeip
+       28. pending signal bitmap
+       29. blocked signal bitmap
+       30. ignored signal bitmap
+       31. caught signal bitmap
+       32. wchan
+       33. nswap
+       34. cnswap
+       35. exit_signal
+       36. processor
+*/
+
+int
+procnotes(int pid, char ***pnotes)
+{
+       char buf[1024], *f[40];
+       int fd, i, n, nf;
+       char *p, *s, **notes;
+       ulong sigs;
+       extern char *_p9sigstr(int, char*);
+
+       *pnotes = nil;
+       snprint(buf, sizeof buf, "/proc/%d/stat", pid);
+       if((fd = open(buf, OREAD)) < 0){
+               fprint(2, "open %s: %r\n", buf);
+               return -1;
+       }
+       n = read(fd, buf, sizeof buf-1);
+       close(fd);
+       if(n <= 0){
+               fprint(2, "read %s: %r\n", buf);
+               return -1;
+       }
+       buf[n] = 0;
+
+       /* command name is in parens, no parens afterward */
+       p = strrchr(buf, ')');
+       if(p == nil || *++p != ' '){
+               fprint(2, "bad format in /proc/%d/stat\n", pid);
+               return -1;
+       }
+       ++p;
+
+       nf = tokenize(p, f, nelem(f));
+       if(0) print("code 0x%lux-0x%lux stack 0x%lux kstk 0x%lux keip 0x%lux pending 0x%lux\n",
+               strtoul(f[23], 0, 0), strtoul(f[24], 0, 0), strtoul(f[25], 0, 0),
+               strtoul(f[26], 0, 0), strtoul(f[27], 0, 0), strtoul(f[28], 0, 0));
+       if(nf <= 28)
+               return -1;
+
+       sigs = strtoul(f[28], 0, 0) & ~(1<<SIGCONT);
+       if(sigs == 0){
+               *pnotes = nil;
+               return 0;
+       }
+
+       notes = mallocz(32*sizeof(char*), 0);
+       if(notes == nil)
+               return -1;
+       n = 0;
+       for(i=0; i<32; i++){
+               if((sigs&(1<<i)) == 0)
+                       continue;
+               if((s = _p9sigstr(i, nil)) == nil)
+                       continue;
+               notes[n++] = s;
+       }
+       *pnotes = notes;
+       return n;
+}
+
+int
+ctlproc(int pid, char *msg)
+{
+       int i, p, status;
+
+       if(strcmp(msg, "attached") == 0){
+               for(i=0; i<nattached; i++)
+                       if(attachedpids[i]==pid)
+                               return 0;
+               if(nattached == nelem(attachedpids)){
+                       werrstr("attached to too many processes");
+                       return -1;
+               }
+               attachedpids[nattached++] = pid;
+               return 0;
+       }
+
+       if(strcmp(msg, "hang") == 0){
+               if(pid == getpid())
+                       return ptrace(PTRACE_TRACEME, 0, 0, 0);
+               werrstr("can only hang self");
+               return -1;
+       }
+       if(strcmp(msg, "kill") == 0)
+               return ptrace(PTRACE_KILL, pid, 0, 0);
+       if(strcmp(msg, "startstop") == 0){
+               if(ptrace(PTRACE_CONT, pid, 0, 0) < 0)
+                       return -1;
+               goto waitstop;
+       }
+       if(strcmp(msg, "sysstop") == 0){
+               if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
+                       return -1;
+               goto waitstop;
+       }
+       if(strcmp(msg, "stop") == 0){
+               if(kill(pid, SIGSTOP) < 0)
+                       return -1;
+               goto waitstop;
+       }
+       if(strcmp(msg, "step") == 0){
+               if(ptrace(PTRACE_SINGLESTEP, pid, 0, 0) < 0)
+                       return -1;
+               goto waitstop;
+       }
+       if(strcmp(msg, "waitstop") == 0){
+       waitstop:
+               if(isstopped(pid))
+                       return 0;
+               for(;;){
+                       p = waitpid(pid, &status, WUNTRACED|__WALL);
+                       if(p <= 0){
+                               if(errno == ECHILD){
+                                       if(isstopped(pid))
+                                               return 0;
+                               }
+                               return -1;
+                       }
+/*fprint(2, "got pid %d status %x\n", pid, status); */
+                       if(WIFEXITED(status) || WIFSTOPPED(status))
+                               return 0;
+               }
+       }
+       if(strcmp(msg, "start") == 0)
+               return ptrace(PTRACE_CONT, pid, 0, 0);
+       werrstr("unknown control message '%s'", msg);
+       return -1;
+}
+
+char*
+proctextfile(int pid)
+{
+       static char buf[1024], pbuf[128];
+
+       snprint(pbuf, sizeof pbuf, "/proc/%d/exe", pid);
+       if(readlink(pbuf, buf, sizeof buf) >= 0)
+               return strdup(buf);
+       if(access(pbuf, AEXIST) >= 0)
+               return strdup(pbuf);
+       return nil;
+}
+
+int
+procthreadpids(int pid, int **thread)
+{
+       int i, fd, nd, *t, nt;
+       char buf[100];
+       Dir *d;
+       
+       snprint(buf, sizeof buf, "/proc/%d/task", pid);
+       if((fd = open(buf, OREAD)) < 0)
+               return -1;
+       nd = dirreadall(fd, &d);
+       close(fd);
+       if(nd < 0)
+               return -1;
+       nt = 0;
+       for(i=0; i<nd; i++)
+               if(d[i].mode&DMDIR)
+                       nt++;
+       t = malloc(nt*sizeof t[0]);
+       nt = 0;
+       for(i=0; i<nd; i++)
+               if(d[i].mode&DMDIR)
+                       t[nt++] = atoi(d[i].name);
+       *thread = t;
+       return nt;
+}
+
+static int
+ptracerw(int type, int xtype, int isr, int pid, uvlong addr, void *v, uint n)
+{
+       int i;
+       uintptr u;
+       uchar buf[sizeof(uintptr)];
+
+       for(i=0; i<n; i+=sizeof(uintptr)){
+               if(isr){
+                       errno = 0;
+                       u = ptrace(type, pid, addr+i, 0);
+                       if(errno)
+                               goto ptraceerr;
+                       if(n-i >= sizeof(uintptr))
+                               *(uintptr*)((char*)v+i) = u;
+                       else{
+                               *(uintptr*)buf = u;
+                               memmove((char*)v+i, buf, n-i);
+                       }
+               }else{
+                       if(n-i >= sizeof(uintptr))
+                               u = *(uintptr*)((char*)v+i);
+                       else{
+                               errno = 0;
+                               u = ptrace(xtype, pid, addr+i, 0);
+                               if(errno)
+                                       return -1;
+                               *(uintptr*)buf = u;
+                               memmove(buf, (char*)v+i, n-i);
+                               u = *(uintptr*)buf;
+                       }
+                       if(ptrace(type, pid, addr+i, u) < 0)
+                               goto ptraceerr;
+               }
+       }
+       return 0;
+
+ptraceerr:
+       werrstr("ptrace %s addr=%#llux pid=%d: %r", isr ? "read" : "write", addr, pid);
+       return -1;
+}
+
+static int
+ptracesegrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr)
+{
+       return ptracerw(isr ? PTRACE_PEEKDATA : PTRACE_POKEDATA, PTRACE_PEEKDATA,
+               isr, map->pid, addr, v, n);
+}
+
+static int
+go2linux(uvlong addr)
+{
+       switch(addr){
+       case offsetof(Ureg, ax):
+               return offsetof(struct user_regs_struct, rax);
+       case offsetof(Ureg, bx):
+               return offsetof(struct user_regs_struct, rbx);
+       case offsetof(Ureg, cx):
+               return offsetof(struct user_regs_struct, rcx);
+       case offsetof(Ureg, dx):
+               return offsetof(struct user_regs_struct, rdx);
+       case offsetof(Ureg, si):
+               return offsetof(struct user_regs_struct, rsi);
+       case offsetof(Ureg, di):
+               return offsetof(struct user_regs_struct, rdi);
+       case offsetof(Ureg, bp):
+               return offsetof(struct user_regs_struct, rbp);
+       case offsetof(Ureg, r8):
+               return offsetof(struct user_regs_struct, r8);
+       case offsetof(Ureg, r9):
+               return offsetof(struct user_regs_struct, r9);
+       case offsetof(Ureg, r10):
+               return offsetof(struct user_regs_struct, r10);
+       case offsetof(Ureg, r11):
+               return offsetof(struct user_regs_struct, r11);
+       case offsetof(Ureg, r12):
+               return offsetof(struct user_regs_struct, r12);
+       case offsetof(Ureg, r13):
+               return offsetof(struct user_regs_struct, r13);
+       case offsetof(Ureg, r14):
+               return offsetof(struct user_regs_struct, r14);
+       case offsetof(Ureg, r15):
+               return offsetof(struct user_regs_struct, r15);
+       case offsetof(Ureg, ds):
+               return offsetof(struct user_regs_struct, ds);
+       case offsetof(Ureg, es):
+               return offsetof(struct user_regs_struct, es);
+       case offsetof(Ureg, fs):
+               return offsetof(struct user_regs_struct, fs);
+       case offsetof(Ureg, gs):
+               return offsetof(struct user_regs_struct, gs);
+       case offsetof(Ureg, ip):
+               return offsetof(struct user_regs_struct, rip);
+       case offsetof(Ureg, cs):
+               return offsetof(struct user_regs_struct, cs);
+       case offsetof(Ureg, flags):
+               return offsetof(struct user_regs_struct, eflags);
+       case offsetof(Ureg, sp):
+               return offsetof(struct user_regs_struct, rsp);
+       case offsetof(Ureg, ss):
+               return offsetof(struct user_regs_struct, ss);
+       }
+       return -1;
+}
+
+static int
+ptraceregrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr)
+{
+       int laddr;
+       uvlong u;
+       
+       if((laddr = go2linux(addr)) < 0){
+               if(isr){
+                       memset(v, 0, n);
+                       return 0;
+               }
+               werrstr("register %llud not available", addr);
+               return -1;
+       }
+       
+       if(isr){
+               errno = 0;
+               u = ptrace(PTRACE_PEEKUSER, map->pid, laddr, 0);
+               if(errno)
+                       goto ptraceerr;
+               switch(n){
+               case 1:
+                       *(uint8*)v = u;
+                       break;
+               case 2:
+                       *(uint16*)v = u;
+                       break;
+               case 4:
+                       *(uint32*)v = u;
+                       break;
+               case 8:
+                       *(uint64*)v = u;
+                       break;
+               default:
+                       werrstr("bad register size");
+                       return -1;
+               }
+       }else{
+               switch(n){
+               case 1:
+                       u = *(uint8*)v;
+                       break;
+               case 2:
+                       u = *(uint16*)v;
+                       break;
+               case 4:
+                       u = *(uint32*)v;
+                       break;
+               case 8:
+                       u = *(uint64*)v;
+                       break;
+               default:
+                       werrstr("bad register size");
+                       return -1;
+               }
+               if(ptrace(PTRACE_POKEUSER, map->pid, laddr, (void*)(uintptr)u) < 0)
+                       goto ptraceerr;
+       }
+       return 0;
+
+ptraceerr:
+       werrstr("ptrace %s register laddr=%d pid=%d: %r", isr ? "read" : "write", laddr, map->pid);
+       return -1;      
+}