tbetter unwinding for 386. - plan9port - [fork] Plan 9 from user space
git clone git://src.adamsgaard.dk/plan9port
Log
Files
Refs
README
LICENSE
---
commit 1cc215aaf92a6cf3cea436f2c215a84839fd59bc
parent cdf1805191ba4ab5b8fbb1697a95fe0d32e25ee6
Author: rsc 
Date:   Sat, 25 Dec 2004 22:03:28 +0000

better unwinding for 386.

command-line extraction from core files on linux and freebsd.

move linux ureg into ureg386.h (used in many places).

Diffstat:
  M src/libmach/Linux.c                 |     265 +++++++++++++++++++++++++++----
  M src/libmach/crackelf.c              |      13 ++++++++++---
  M src/libmach/elf.h                   |       4 ++++
  M src/libmach/elfcorefreebsd386.c     |      47 +++++++++++++++++++++++++------
  M src/libmach/elfcorelinux386.c       |      91 +++++++++++++++----------------
  M src/libmach/frame.c                 |       2 +-
  M src/libmach/mach386.c               |     119 +++++++++++++++++++++++++++----
  M src/libmach/machpower.c             |       2 +-
  M src/libmach/mkfile                  |       9 +++++++++
  M src/libmach/sym.c                   |      23 ++++++++++++++++++++---
  M src/libmach/symdwarf.c              |       4 ++--
  M src/libmach/symelf.c                |      18 ++++++++++++++++++
  M src/libmach/ureg386.h               |       3 +++

13 files changed, 493 insertions(+), 107 deletions(-)
---
diff --git a/src/libmach/Linux.c b/src/libmach/Linux.c
t@@ -18,10 +18,12 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
 #include 
+#include 
 #include "ureg386.h"
 
 Mach *machcpu = &mach386;
t@@ -34,42 +36,27 @@ struct PtraceRegs
         int pid;
 };
 
-static int ptracerw(Map*, Seg*, ulong, void*, uint, int);
+static int ptracesegrw(Map*, Seg*, ulong, void*, uint, int);
 static int ptraceregrw(Regs*, char*, ulong*, int);
 
 static int attachedpids[1000];
 static int nattached;
 
-void
-unmapproc(Map *map)
-{
-        int i;
-
-        if(map == nil)
-                return;
-        for(i=0; inseg; i++)
-                while(inseg && map->seg[i].pid){
-                        map->nseg--;
-                        memmove(&map->seg[i], &map->seg[i+1], 
-                                (map->nseg-i)*sizeof(map->seg[0]));
-                }
-}
-
 int
-mapproc(int pid, Map *map, Regs **rp)
+ptraceattach(int pid)
 {
         int i;
-        Seg s;
-        PtraceRegs *r;
 
+        /*
         if(nattached==1 && attachedpids[0] == pid)
                 goto already;
         if(nattached)
                 detachproc(attachedpids[0]);
-
+        */
+        
         for(i=0; inseg; i++)
+                while(inseg && map->seg[i].pid){
+                        map->nseg--;
+                        memmove(&map->seg[i], &map->seg[i+1], 
+                                (map->nseg-i)*sizeof(map->seg[0]));
+                }
+}
+
+
+
+int
+mapproc(int pid, Map *map, Regs **rp)
+{
+        Seg s;
+        PtraceRegs *r;
+
+        if(ptraceattach(pid) < 0)
+                return -1;
 
-already:
         memset(&s, 0, sizeof s);
         s.base = 0;
         s.size = 0xFFFFFFFF;
         s.offset = 0;
         s.name = "data";
         s.file = nil;
-        s.rw = ptracerw;
+        s.rw = ptracesegrw;
         s.pid = pid;
         if(addseg(map, s) < 0){
                 fprint(2, "addseg: %r\n");
t@@ -126,17 +140,16 @@ detachproc(int pid)
 }
 
 static int
-ptracerw(Map *map, Seg *seg, ulong addr, void *v, uint n, int isr)
+ptracerw(int type, int xtype, int isr, int pid, ulong addr, void *v, uint n)
 {
         int i;
         u32int u;
         uchar buf[4];
 
-        addr += seg->base;
         for(i=0; ipid, addr+i, 0);
+                        u = ptrace(type, pid, addr+i, 0);
                         if(errno)
                                 goto ptraceerr;
                         if(n-i >= 4)
t@@ -150,14 +163,14 @@ ptracerw(Map *map, Seg *seg, ulong addr, void *v, uint n, int isr)
                                 u = *(u32int*)((char*)v+i);
                         else{
                                 errno = 0;
-                                u = ptrace(PTRACE_PEEKDATA, seg->pid, addr+i, 0);
+                                u = ptrace(xtype, pid, addr+i, 0);
                                 if(errno)
                                         return -1;
                                 *(u32int*)buf = u;
                                 memmove(buf, (char*)v+i, n-i);
                                 u = *(u32int*)buf;
                         }
-                        if(ptrace(PTRACE_POKEDATA, seg->pid, addr+i, &u) < 0)
+                        if(ptrace(type, pid, addr+i, &u) < 0)
                                 goto ptraceerr;
                 }
         }
t@@ -168,6 +181,14 @@ ptraceerr:
         return -1;
 }
 
+static int
+ptracesegrw(Map *map, Seg *seg, ulong addr, void *v, uint n, int isr)
+{
+        addr += seg->base;
+        return ptracerw(isr ? PTRACE_PEEKDATA : PTRACE_POKEDATA, PTRACE_PEEKDATA,
+                isr, seg->pid, addr, v, n);
+}
+
 static char* linuxregs[] = {
         "BX",
         "CX",
t@@ -483,3 +504,191 @@ proctextfile(int pid)
         Bterm(b);
 #endif
 
+/*
+ * bottom-end functions for libthread_db to call
+ */
+enum
+{
+        PS_OK,
+        PS_ERR,
+        PS_BADPID,
+        PS_BADLWPID,
+        PS_BADADDR,
+        PS_NOSYM,
+        PS_NOFPREGS,
+};
+
+pid_t
+ps_getpid(struct ps_prochandle *ph)
+{
+        return ph->pid;
+}
+
+int
+ps_pstop(const struct ps_prochandle *ph)
+{
+        return PS_ERR;
+}
+
+int
+ps_pcontinue(const struct ps_prochandle *ph)
+{
+        return PS_ERR;
+}
+
+int
+ps_lstop(const struct ps_prochandle *ph)
+{
+        return PS_ERR;
+}
+
+int
+ps_lcontinue(const struct ps_prochandle *ph)
+{
+        return PS_ERR;
+}
+
+/* read/write data or text memory */
+int
+ps_pdread(struct ps_prochandle *ph, psaddr_t addr, void *v, size_t sz)
+{
+//print("read %d %p %d\n", ph->pid, addr, sz);
+        if(ptracerw(PTRACE_PEEKDATA, 0, 1, ph->pid, (ulong)addr, v, sz) < 0)
+                return PS_ERR;
+//print("        => 0x%lux\n", *(ulong*)v);
+        return PS_OK;
+}
+
+int
+ps_pdwrite(struct ps_prochandle *ph, psaddr_t addr, void *v, size_t sz)
+{
+//print("write %d %p\n", ph->pid, addr);
+        if(ptracerw(PTRACE_POKEDATA, PTRACE_PEEKDATA, 0, ph->pid, (ulong)addr, v, sz) < 0)
+                return PS_ERR;
+        return PS_OK;
+}
+
+int
+ps_ptread(struct ps_prochandle *ph, psaddr_t addr, void *v, size_t sz)
+{
+//print("read %d %p\n", ph->pid, addr);
+        if(ptracerw(PTRACE_PEEKTEXT, 0, 1, ph->pid, (ulong)addr, v, sz) < 0)
+                return PS_ERR;
+        return PS_OK;
+}
+
+int
+ps_ptwrite(struct ps_prochandle *ph, psaddr_t addr, void *v, size_t sz)
+{
+//print("write %d %p\n", ph->pid, addr);
+        if(ptracerw(PTRACE_POKETEXT, PTRACE_PEEKTEXT, 0, ph->pid, (ulong)addr, v, sz) < 0)
+                return PS_ERR;
+        return PS_OK;
+}
+
+int
+ps_lgetregs(struct ps_prochandle *ph, lwpid_t lwp, prgregset_t regs)
+{
+        if(lwp == 0){
+                memset(regs, 0xfe, sizeof(regs[0])*nelem(linuxregs));
+                return PS_OK;
+        }
+//print("getregs %d %p (%d)\n", lwp, regs, sizeof(regs[0])*nelem(linuxregs));
+        
+        if(ptraceattach(lwp) < 0){
+                fprint(2, "ptrace attach: %r\n");
+                return PS_ERR;
+        }
+
+        if(ptracerw(PTRACE_PEEKUSER, 0, 1, lwp, 0, regs, sizeof(regs[0])*nelem(linuxregs)) < 0){
+                fprint(2, "ptrace: %r\n");
+                return PS_ERR;
+        }
+        return PS_OK;
+}
+
+int
+ps_lsetregs(struct ps_prochandle *ph, lwpid_t lwp, prgregset_t regs)
+{
+print("setregs %d\n", lwp);
+        if(ptracerw(PTRACE_POKEUSER, PTRACE_PEEKUSER, 1, lwp, 0, regs, sizeof(regs[0])*nelem(linuxregs)) < 0)
+                return PS_ERR;
+        return PS_OK;
+}
+
+int
+ps_lgetfpregs(struct ps_prochandle *ph, lwpid_t lwp, prfpregset_t *fpregs)
+{
+        if(ptracerw(PTRACE_PEEKUSER, 0, 1, lwp, 18*4, fpregs, sizeof *fpregs) < 0)
+                return PS_ERR;
+        return PS_OK;
+}
+
+int
+ps_lsetfpregs(struct ps_prochandle *ph, lwpid_t lwp, prfpregset_t *fpregs)
+{
+        if(ptracerw(PTRACE_POKEUSER, PTRACE_PEEKUSER, 1, lwp, 18*4, fpregs, sizeof *fpregs) < 0)
+                return PS_ERR;
+        return PS_OK;
+}
+
+/* Fetch the special per-thread address associated with the given LWP.
+   This call is only used on a few platforms (most use a normal register).
+   The meaning of the `int' parameter is machine-dependent.  */
+int
+ps_get_thread_area(struct ps_prochandle *ph, lwpid_t lwp, int xxx, psaddr_t *addr)
+{
+        return PS_NOSYM;
+}
+
+/* Look up the named symbol in the named DSO in the symbol tables
+   associated with the process being debugged, filling in *SYM_ADDR
+   with the corresponding run-time address.  */
+int
+ps_pglobal_lookup(struct ps_prochandle *ph, char *object_name, char *sym_name, psaddr_t *sym_addr)
+{
+        Fhdr *fp;
+        ulong addr;
+
+        if((fp = findhdr(object_name)) == nil){
+print("lookup %d %s %s => no such hdr\n", ph->pid, object_name, sym_name);
+                return PS_NOSYM;
+        }
+        if(elfsymlookup(fp->elf, sym_name, &addr) < 0){
+print("lookup %d %s %s => name not found\n", ph->pid, object_name, sym_name);
+                return PS_NOSYM;
+        }
+print("lookup %d %s %s => 0x%lux\n", ph->pid, object_name, sym_name, addr);
+        *sym_addr = (void*)(addr+fp->base);
+        return PS_OK;
+}
+
+Ureg*
+_linux2ureg386(UregLinux386 *l)
+{
+        Ureg *u;
+
+        u = malloc(sizeof(Ureg));
+        if(u == nil)
+                return nil;
+        u->di = l->edi;
+        u->si = l->esi;
+        u->bp = l->ebp;
+        u->nsp = l->esp;
+        u->bx = l->ebx;
+        u->dx = l->edx;
+        u->cx = l->ecx;
+        u->ax = l->eax;
+        u->gs = l->xgs;
+        u->fs = l->xfs;
+        u->es = l->xes;
+        u->ds = l->xds;
+        u->trap = ~0; // l->trapno;
+        u->ecode = ~0; // l->err;
+        u->pc = l->eip;
+        u->cs = l->xcs;
+        u->flags = l->eflags;
+        u->sp = l->esp;
+        u->ss = l->xss;
+        return u;
+}
diff --git a/src/libmach/crackelf.c b/src/libmach/crackelf.c
t@@ -40,11 +40,18 @@ static struct
         uint mtype;
         uint atype;
         int (*coreregs)(Elf*, ElfNote*, uchar**);
+        int (*corecmd)(Elf*, ElfNote*, char**);
 } ctab[] = 
 {        /* Font Tab 4 */
-        M386,                ALINUX,                coreregslinux386,
-        M386,                ANONE,                coreregslinux386,        /* [sic] */
-        M386,                AFREEBSD,        coreregsfreebsd386,
+        M386,                ALINUX,
+                coreregslinux386,
+                corecmdlinux386,
+        M386,                ANONE,
+                coreregslinux386,        /* [sic] */
+                corecmdlinux386,        /* [sic] */
+        M386,                AFREEBSD,
+                coreregsfreebsd386,
+                corecmdfreebsd386,
 };
 
 int
diff --git a/src/libmach/elf.h b/src/libmach/elf.h
t@@ -218,6 +218,7 @@ struct Elf
         ElfSect        *dynsym;
         ElfSect        *dynstr;
         ElfSect        *bss;
+        ulong        dynamic;                /* offset to elf dynamic crap */
 
         int                (*coreregs)(Elf*, ElfNote*, uchar**);
 };
t@@ -227,7 +228,10 @@ Elf*        elfinit(int);
 ElfSect *elfsection(Elf*, char*);
 void        elfclose(Elf*);
 int        elfsym(Elf*, int, ElfSym*);
+int        elfsymlookup(Elf*, char*, ulong*);
 int        elfmap(Elf*, ElfSect*);
 
 int        coreregslinux386(Elf*, ElfNote*, uchar**);
 int        coreregsfreebsd386(Elf*, ElfNote*, uchar**);
+int        corecmdlinux386(Elf*, ElfNote*, char**);
+int        corecmdfreebsd386(Elf*, ElfNote*, char**);
diff --git a/src/libmach/elfcorefreebsd386.c b/src/libmach/elfcorefreebsd386.c
t@@ -6,6 +6,7 @@
 
 typedef struct Lreg Lreg;
 typedef struct Status Status;
+typedef struct Psinfo Psinfo;
 
 struct Lreg
 {
t@@ -32,14 +33,22 @@ struct Lreg
 
 struct Status
 {
-    u32int                version;        /* Version number of struct (1) */
-    u32int                statussz;        /* sizeof(prstatus_t) (1) */
-    u32int                gregsetsz;        /* sizeof(gregset_t) (1) */
-    u32int                fpregsetsz;        /* sizeof(fpregset_t) (1) */
-    u32int                osreldate;        /* Kernel version (1) */
-    u32int                cursig;        /* Current signal (1) */
-    u32int                pid;                /* Process ID (1) */
-    Lreg                reg;                /* General purpose registers (1) */
+        u32int                version;        /* Version number of struct (1) */
+        u32int                statussz;        /* sizeof(prstatus_t) (1) */
+        u32int                gregsetsz;        /* sizeof(gregset_t) (1) */
+        u32int                fpregsetsz;        /* sizeof(fpregset_t) (1) */
+        u32int                osreldate;        /* Kernel version (1) */
+        u32int                cursig;        /* Current signal (1) */
+        u32int                pid;                /* Process ID (1) */
+        Lreg                reg;                /* General purpose registers (1) */
+};
+
+struct Psinfo
+{
+        u32int        version;
+        u32int        size;
+        char        name[17];
+        char        psargs[81];
 };
 
 int
t@@ -87,3 +96,25 @@ coreregsfreebsd386(Elf *elf, ElfNote *note, uchar **up)
         return sizeof(Ureg);
 }
 
+int
+corecmdfreebsd386(Elf *elf, ElfNote *note, char **pp)
+{
+        char *t;
+        Psinfo *p;
+
+        *pp = nil;
+        if(note->descsz < sizeof(Psinfo)){
+                werrstr("elf psinfo note too small");
+                return -1;
+        }
+        p = (Psinfo*)note->desc;
+        print("elf name %s\nelf args %s\n", p->name, p->psargs);
+        t = malloc(80+1);
+        if(t == nil)
+                return -1;
+        memmove(t, p->psargs, 80);
+        t[80] = 0;
+        *pp = t;
+        return 0;
+}
+
diff --git a/src/libmach/elfcorelinux386.c b/src/libmach/elfcorelinux386.c
t@@ -6,30 +6,10 @@
 
 typedef struct Lreg Lreg;
 typedef struct Status Status;
-
-struct Lreg
-{
-        u32int        ebx;
-        u32int        ecx;
-        u32int        edx;
-        u32int        esi;
-        u32int        edi;
-        u32int        ebp;
-        u32int        eax;
-        u32int        ds;
-        u32int        es;
-        u32int        fs;
-        u32int        gs;
-        u32int        origeax;
-        u32int        eip;
-        u32int        cs;
-        u32int        eflags;
-        u32int        esp;
-        u32int        ss;
-};
+typedef struct Psinfo Psinfo;
 
 /*
- * Lreg is 64-bit aligned within status, so we shouldn't 
+ * UregLinux386 is 64-bit aligned within status, so we shouldn't 
  * have any packing problems. 
  */
 struct Status
t@@ -48,15 +28,32 @@ struct Status
         u32int        stime[2];
         u32int        cutime[2];
         u32int        cstime[2];
-        Lreg        reg;
+        UregLinux386        reg;
         u32int        fpvalid;
 };
 
+struct Psinfo
+{
+        char state;
+        char sname;
+        char zomb;
+        char nice;
+        u32int flag;
+        u16int uid;
+        u16int gid;
+        u32int pid;
+        u32int ppid;
+        u32int pgrp;
+        u32int sid;
+        char fname[16];
+        char psargs[80];
+};
+
 int
 coreregslinux386(Elf *elf, ElfNote *note, uchar **up)
 {
         Status *s;
-        Lreg *l;
+        UregLinux386 *l;
         Ureg *u;
 
         if(note->descsz < sizeof(Status)){
t@@ -65,31 +62,31 @@ coreregslinux386(Elf *elf, ElfNote *note, uchar **up)
         }
         s = (Status*)note->desc;
         l = &s->reg;
-        u = malloc(sizeof(Ureg));
-        if(u == nil)
+        if((u = _linux2ureg386(l)) == nil)
                 return -1;
-
-        /* no byte order problems - just copying and rearranging */
-        u->di = l->edi;
-        u->si = l->esi;
-        u->bp = l->ebp;
-        u->nsp = l->esp;
-        u->bx = l->ebx;
-        u->dx = l->edx;
-        u->cx = l->ecx;
-        u->ax = l->eax;
-        u->gs = l->gs;
-        u->fs = l->fs;
-        u->es = l->es;
-        u->ds = l->ds;
-        u->trap = ~0; // l->trapno;
-        u->ecode = ~0; // l->err;
-        u->pc = l->eip;
-        u->cs = l->cs;
-        u->flags = l->eflags;
-        u->sp = l->esp;
-        u->ss = l->ss;
         *up = (uchar*)u;
         return sizeof(Ureg);
 }
 
+int
+corecmdlinux386(Elf *elf, ElfNote *note, char **pp)
+{
+        char *t;
+        Psinfo *p;
+
+        *pp = nil;
+        if(note->descsz < sizeof(Psinfo)){
+                werrstr("elf psinfo note too small");
+                return -1;
+        }
+        p = (Psinfo*)note->desc;
+        print("elf name %s\nelf args %s\n", p->fname, p->psargs);
+        t = malloc(80+1);
+        if(t == nil)
+                return -1;
+        memmove(t, p->psargs, 80);
+        t[80] = 0;
+        *pp = t;
+        return 0;
+}
+
diff --git a/src/libmach/frame.c b/src/libmach/frame.c
t@@ -79,7 +79,7 @@ stacktrace(Map *map, Regs *regs, Tracer trace)
                 lr.oldregs = regs;
                 lr.val = cur;
                 lr.map = map;
-                if((i = unwindframe(map, &lr.r, next)) >= 0)
+                if((i = unwindframe(map, &lr.r, next, sp)) >= 0)
                         nextpc = next[ipc];
                 else
                         nextpc = ~(ulong)0;
diff --git a/src/libmach/mach386.c b/src/libmach/mach386.c
t@@ -29,7 +29,7 @@ static        int        i386hexinst(Map*, ulong, char*, int);
 static        int        i386das(Map*, ulong, char, char*, int);
 static        int        i386instlen(Map*, ulong);
 static        char        *i386windregs[];
-static        int        i386unwind(Map*, Regs*, ulong*);
+static        int        i386unwind(Map*, Regs*, ulong*, Symbol*);
 
 static        Regdesc i386reglist[] = {
         {"DI",                REGOFF(di),        RINT, 'X'},
t@@ -128,14 +128,37 @@ static char *i386windregs[] = {
         0,
 };
 
+/*
+ * The wrapper code around Linux system calls 
+ * saves AX on the stack before calling some calls
+ * (at least, __libc_nanosleep), when running in 
+ * threaded programs. 
+ */
+static void
+syscallhack(Map *map, Regs *regs, int *spoff)
+{
+        ulong pc;
+        char buf[60];
+
+        rget(regs, "PC", &pc);
+        if(i386das(map, pc-2, 0, buf, sizeof buf) != 2 || strncmp(buf, "INTB\t$", 6) != 0)
+                return;
+        if(i386das(map, pc, 0, buf, sizeof buf) != 2 || strcmp(buf, "MOVL\tDX,BX") != 0)
+                return;
+        if(i386das(map, pc+2, 0, buf, sizeof buf) != 3 || strcmp(buf, "XCHGL\tAX,0(SP)") != 0)
+                return;
+        *spoff += 4;        
+}
+
 static int
-i386unwind(Map *map, Regs *regs, ulong *next)
+i386unwind(Map *map, Regs *regs, ulong *next, Symbol *sym)
 {
-        int isp, ipc, ibp;
-        ulong bp;
+        int i, isp, ipc, ibp, havebp, n, spoff, off[9];
+        ulong pc;
         u32int v;
+        char buf[60], *p;
 
-        /* No symbol information, use frame pointer and do the best we can. */
+//print("i386unwind %s\n", sym ? sym->name : nil);
         isp = windindex("SP");
         ipc = windindex("PC");
         ibp = windindex("BP");
t@@ -144,19 +167,85 @@ i386unwind(Map *map, Regs *regs, ulong *next)
                 return -1;
         }
 
-        bp = next[ibp];
+        /*
+         * Disassemble entry to figure out
+         * where values have been saved.
+         * Perhaps should disassemble exit path
+         * instead -- a random walk on the code
+         * should suffice to get us to a RET.
+         */
+        if(sym){
+                pc = sym->loc.addr;
+//print("startpc %lux\n", pc);
+                memset(off, 0xff, sizeof off);
+                spoff = 0;
+                havebp = 0;
+                for(;;){
+                        if((n = i386das(map, pc, 0, buf, sizeof buf)) < 0)
+                                break;
+//print("%s\n", buf);
+                        pc += n;
+                        if(strncmp(buf, "PUSHL\t", 6) == 0){
+                                spoff += 4;
+                                if((i = windindex(buf+6)) >= 0)
+                                        off[i] = spoff;
+                        }else if(strcmp(buf, "MOVL\tSP,BP") == 0 && spoff == 4 && off[ibp] == 4){
+                                havebp = 1;
+                        }else if(strncmp(buf, "SUBL\t$", 6) == 0){
+                                if((p = strrchr(buf, ',')) && strcmp(p, ",SP") == 0){
+//print("spoff %s\n", buf+6);
+                                        spoff += strtol(buf+6, 0, 16);
+                                }
+                                break;
+                        }else if(strncmp(buf, "XORL\t", 5) == 0 || strncmp(buf, "MOVL\t", 5) == 0){
+                                /*
+                                 * Hope these are rescheduled non-prologue instructions
+                                 * like XORL AX, AX or MOVL $0x3, AX and thus ignorable.
+                                 */
+                        }else
+                                break;
+                }
 
-        if(get4(map, bp, &v) < 0)
-                return -1;
-        next[ibp] = v;
+                syscallhack(map, regs, &spoff);
+
+                if(havebp){
+//print("havebp\n");
+                        rget(regs, "BP", &next[isp]);
+                        get4(map, next[isp], &v);
+                        next[ibp] = v;
+                        next[isp] += 4;
+                }else{
+                        rget(regs, "SP", &next[isp]);
+//print("old sp %lux + %d\n", next[isp], spoff);
+                        next[isp] += spoff;
+                }
+                for(i=0; i %lux\n", next[isp], v);
+                next[ipc] = v;
+                next[isp] += 4;
+                return 0;
+        }
 
-        if(get4(map, bp+4, &v) < 0)
+        /*
+         * Rely on bp chaining
+         */
+        if(rget(regs, "BP", &next[isp]) < 0
+        || get4(map, next[isp], &v) < 0)
+                return -1;
+        next[ibp] = v;
+        next[isp] += 4;
+        if(get4(map, next[isp], &v) < 0)
                 return -1;
         next[ipc] = v;
-
-        return 0;        
+        next[isp] += 4;
+        return 0;
 }
 
 //static        char        STARTSYM[] =        "_main";
t@@ -1766,8 +1855,10 @@ pea(Instr *ip)
         else {
                 if (ip->base < 0)
                         immediate(ip, ip->disp);
-                else
+                else {
+                        bprint(ip, "%lux", ip->disp);
                         bprint(ip,"(%s%s)", ANAME(ip), reg[(uchar)ip->base]);
+                }
         }
         if (ip->index >= 0)
                 bprint(ip,"(%s%s*%d)", ANAME(ip), reg[(uchar)ip->index], 1<ss);
diff --git a/src/libmach/machpower.c b/src/libmach/machpower.c
t@@ -1337,7 +1337,7 @@ static char *powerwindregs[] =
 };
 
 static int
-powerunwind(Map *map, Regs *regs, ulong *next)
+powerunwind(Map *map, Regs *regs, ulong *next, Symbol *sym)
 {
         /*
          * This is tremendously hard.  The best we're going to
diff --git a/src/libmach/mkfile b/src/libmach/mkfile
t@@ -4,6 +4,7 @@ LIB=libmach.a
 
 OFILES=\
         $SYSNAME.$O\
+        cmdline.$O\
         crack.$O\
         crackelf.$O\
         crackmacho.$O\
t@@ -17,6 +18,7 @@ OFILES=\
         dwarfpc.$O\
         dwarfpubnames.$O\
         elf.$O\
+        elfdl386.$O\
         elfcorefreebsd386.$O\
         elfcorelinux386.$O\
         frame.$O\
t@@ -57,6 +59,13 @@ dwarfdump: dwarfdump.o $LIBDIR/$LIB
 nm: nm.o $LIBDIR/$LIB
         $LD -o $target $prereq -l9
 
+t: t.o $LIBDIR/$LIB
+        $LD -o $target $prereq -l9 -lthread_db
+
+elfnm: elfnm.o $LIBDIR/$LIB
+        $LD -o $target $prereq -l9
+
+
 SunOS.$O: nosys.c
 Darwin.$O: nosys.c
 OpenBSD.$O: nosys.c
diff --git a/src/libmach/sym.c b/src/libmach/sym.c
t@@ -50,6 +50,23 @@ _delhdr(Fhdr *h)
         h->next = nil;
 }
 
+Fhdr*
+findhdr(char *name)
+{
+        int len, plen;
+        Fhdr *p;
+
+        len = strlen(name);
+        for(p=fhdrlist; p; p=p->next){
+                plen = strlen(p->filename);
+                if(plen >= len)
+                if(strcmp(p->filename+plen-len, name) == 0)
+                if(plen == len || p->filename[plen-len-1] == '/')
+                        return p;
+        }
+        return nil;
+}
+
 int
 pc2file(ulong pc, char *file, uint nfile, ulong *line)
 {
t@@ -354,14 +371,14 @@ findlsym(Symbol *s1, Loc loc, Symbol *s2)
 }
 
 int
-unwindframe(Map *map, Regs *regs, ulong *next)
+unwindframe(Map *map, Regs *regs, ulong *next, Symbol *sym)
 {
         Fhdr *p;
 
         for(p=fhdrlist; p; p=p->next)
-                if(p->unwind && p->unwind(p, map, regs, next) >= 0)
+                if(p->unwind && p->unwind(p, map, regs, next, sym) >= 0)
                         return 0;
-        if(mach->unwind && mach->unwind(map, regs, next) >= 0)
+        if(mach->unwind && mach->unwind(map, regs, next, sym) >= 0)
                 return 0;
         return -1;
 }
diff --git a/src/libmach/symdwarf.c b/src/libmach/symdwarf.c
t@@ -13,7 +13,7 @@ static int        dwarfindexlsym(Fhdr*, Symbol*, uint, Symbol*);
 static int        dwarffindlsym(Fhdr*, Symbol*, Loc, Symbol*);
 static void        dwarfsyminit(Fhdr*);
 static int        dwarftosym(Fhdr*, Dwarf*, DwarfSym*, Symbol*, int);
-static int        _dwarfunwind(Fhdr *fhdr, Map *map, Regs *regs, ulong *next);
+static int        _dwarfunwind(Fhdr *fhdr, Map *map, Regs *regs, ulong *next, Symbol*);
 
 int
 symdwarf(Fhdr *hdr)
t@@ -396,7 +396,7 @@ dwarfexprfmt(Fmt *fmt)
 #endif
 
 static int
-_dwarfunwind(Fhdr *fhdr, Map *map, Regs *regs, ulong *next)
+_dwarfunwind(Fhdr *fhdr, Map *map, Regs *regs, ulong *next, Symbol *sym)
 {
         char *name;
         int i, j;
diff --git a/src/libmach/symelf.c b/src/libmach/symelf.c
t@@ -53,6 +53,7 @@ elfsyminit(Fhdr *fp)
                 p = &elf->prog[i];
                 if(p->type != ElfProgDynamic)
                         continue;
+                elf->dynamic = p->vaddr;
                 memset(&sym, 0, sizeof sym);
                 sym.name = "_DYNAMIC";
                 sym.loc = locaddr(p->vaddr);
t@@ -65,6 +66,23 @@ elfsyminit(Fhdr *fp)
 }
 
 int
+elfsymlookup(Elf *elf, char *name, ulong *addr)
+{
+        int i;
+        ElfSym esym;
+
+        for(i=0; elfsym(elf, i, &esym) >= 0; i++){
+                if(esym.name == nil)
+                        continue;
+                if(strcmp(esym.name, name) == 0){
+                        *addr = esym.value;
+                        return 0;
+                }
+        }
+        return -1;
+}
+
+int
 symelf(Fhdr *fhdr)
 {
         int ret;
diff --git a/src/libmach/ureg386.h b/src/libmach/ureg386.h
t@@ -29,6 +29,7 @@ struct UregLinux386
         ulong        ecx;
         ulong        edx;
         ulong        esi;
+        ulong        edi;
         ulong        ebp;
         ulong        eax;
         ulong        xds;
t@@ -43,3 +44,5 @@ struct UregLinux386
         ulong        xss;
 };
 
+Ureg *_linux2ureg386(UregLinux386*);
+