URI: 
       tbetter unwinding for 386. - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 1cc215aaf92a6cf3cea436f2c215a84839fd59bc
   DIR parent cdf1805191ba4ab5b8fbb1697a95fe0d32e25ee6
  HTML Author: rsc <devnull@localhost>
       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(-)
       ---
   DIR diff --git a/src/libmach/Linux.c b/src/libmach/Linux.c
       t@@ -18,10 +18,12 @@
        #include <sys/ptrace.h>
        #include <sys/types.h>
        #include <sys/wait.h>
       +#include <sys/procfs.h>
        #include <signal.h>
        #include <errno.h>
        #include <libc.h>
        #include <mach.h>
       +#include <elf.h>
        #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; i<map->nseg; i++)
       -                while(i<map->nseg && 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; i<nattached; i++)
                        if(attachedpids[i]==pid)
       -                        goto already;
       +                        return 0;
                if(nattached == nelem(attachedpids)){
                        werrstr("attached to too many processes");
                        return -1;
       t@@ -86,15 +73,42 @@ mapproc(int pid, Map *map, Regs **rp)
                        return -1;
                }
                attachedpids[nattached++] = pid;
       +        return 0;
       +}
       +
       +void
       +unmapproc(Map *map)
       +{
       +        int i;
       +
       +        if(map == nil)
       +                return;
       +        for(i=0; i<map->nseg; i++)
       +                while(i<map->nseg && 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; i<n; i+=4){
                        if(isr){
                                errno = 0;
       -                        u = ptrace(PTRACE_PEEKDATA, seg->pid, 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;
       +}
   DIR 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
   DIR 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**);
   DIR 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;
       +}
       +
   DIR 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;
       +}
       +
   DIR 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;
   DIR 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<nelem(off); i++)
       +                        if(off[i] != -1){
       +                                get4(map, next[isp]-off[i], &v);
       +                                next[i] = v;
       +                        }
        
       -        next[isp] = bp+4;
       +                if(get4(map, next[isp], &v) < 0)
       +                        return -1;
       +//print("new pc %lux => %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<<ip->ss);
   DIR 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
   DIR 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
   DIR 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;
        }
   DIR 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;
   DIR 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;
   DIR 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*);
       +