aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/libmach/FreeBSD.c43
-rw-r--r--src/libmach/Linux.c450
-rw-r--r--src/libmach/Notes13
-rw-r--r--src/libmach/Notes.stab166
-rw-r--r--src/libmach/crack.c91
-rw-r--r--src/libmach/crackelf.c342
-rw-r--r--src/libmach/crackmacho.c198
-rw-r--r--src/libmach/dwarf.h460
-rw-r--r--src/libmach/dwarf386.c24
-rw-r--r--src/libmach/dwarfabbrev.c129
-rw-r--r--src/libmach/dwarfaranges.c63
-rw-r--r--src/libmach/dwarfcfa.c391
-rw-r--r--src/libmach/dwarfdump.c138
-rw-r--r--src/libmach/dwarfeval.c62
-rw-r--r--src/libmach/dwarfget.c217
-rw-r--r--src/libmach/dwarfinfo.c646
-rw-r--r--src/libmach/dwarfopen.c107
-rw-r--r--src/libmach/dwarfpc.c338
-rw-r--r--src/libmach/dwarfpubnames.c76
-rw-r--r--src/libmach/elf.c405
-rw-r--r--src/libmach/elf.h233
-rw-r--r--src/libmach/elfcore.h142
-rw-r--r--src/libmach/elfcorefreebsd386.c89
-rw-r--r--src/libmach/elfcorelinux386.c95
-rw-r--r--src/libmach/elfdump.c133
-rw-r--r--src/libmach/elfdynsym.c6
-rw-r--r--src/libmach/fpformat.c65
-rw-r--r--src/libmach/frame.c130
-rw-r--r--src/libmach/hexify.c20
-rw-r--r--src/libmach/ieee.c169
-rw-r--r--src/libmach/loc.c253
-rw-r--r--src/libmach/localaddr.c56
-rw-r--r--src/libmach/mach.c30
-rw-r--r--src/libmach/mach386.c1786
-rw-r--r--src/libmach/macho.c128
-rw-r--r--src/libmach/macho.h71
-rw-r--r--src/libmach/machocorepower.c173
-rw-r--r--src/libmach/machodump.c93
-rw-r--r--src/libmach/machpower.c1409
-rw-r--r--src/libmach/map.c306
-rw-r--r--src/libmach/mkfile58
-rw-r--r--src/libmach/nm.c289
-rw-r--r--src/libmach/regs.c59
-rw-r--r--src/libmach/stabs.c54
-rw-r--r--src/libmach/stabs.h117
-rw-r--r--src/libmach/swap.c118
-rw-r--r--src/libmach/sym.c478
-rw-r--r--src/libmach/symdwarf.c466
-rw-r--r--src/libmach/symelf.c103
-rw-r--r--src/libmach/symmacho.c50
-rw-r--r--src/libmach/symstabs.c401
-rw-r--r--src/libmach/ureg386.h45
-rw-r--r--src/libmach/uregpower.h54
53 files changed, 12038 insertions, 0 deletions
diff --git a/src/libmach/FreeBSD.c b/src/libmach/FreeBSD.c
new file mode 100644
index 00000000..2838b392
--- /dev/null
+++ b/src/libmach/FreeBSD.c
@@ -0,0 +1,43 @@
+/*
+ * process interface for FreeBSD
+ */
+
+#include <u.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <errno.h>
+#include <libc.h>
+#include <mach.h>
+#include "ureg386.h"
+
+void
+unmapproc(Map*)
+{
+}
+
+int
+mapproc(int, Map*, Regs**)
+{
+}
+
+int
+detachproc(int)
+{
+}
+
+int
+procnotes(int, char***)
+{
+}
+
+int
+ctlproc(int, char*)
+{
+}
+
+char*
+proctextfile(int)
+{
+}
diff --git a/src/libmach/Linux.c b/src/libmach/Linux.c
new file mode 100644
index 00000000..62796adb
--- /dev/null
+++ b/src/libmach/Linux.c
@@ -0,0 +1,450 @@
+/*
+ * process interface for Linux.
+ *
+ * Uses ptrace for registers and data,
+ * /proc for some process status.
+ * There's not much point to worrying about
+ * byte order here -- using ptrace means
+ * we're running on the architecture we're debugging,
+ * unless truly weird stuff is going on.
+ *
+ * It is tempting to use /proc/%d/mem along with
+ * the sp and pc in the stat file to get a stack trace
+ * without attaching to the program, but unfortunately
+ * you can't read the mem file unless you've attached.
+ */
+
+#include <u.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <errno.h>
+#include <libc.h>
+#include <mach.h>
+#include "ureg386.h"
+
+Mach *machcpu = &mach386;
+
+typedef struct PtraceRegs PtraceRegs;
+
+struct PtraceRegs
+{
+ Regs r;
+ int pid;
+};
+
+static int ptracerw(Map*, Seg*, ulong, void*, uint, int);
+static int ptraceregrw(Regs*, char*, ulong*, int);
+
+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(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0)
+ if(ptrace(PTRACE_PEEKUSER, pid, 0, 0) < 0)
+ if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0){
+ werrstr("ptrace attach %d: %r", pid);
+ return -1;
+ }
+
+ if(ctlproc(pid, "waitstop") < 0){
+ ptrace(PTRACE_DETACH, pid, 0, 0);
+ return -1;
+ }
+
+ memset(&s, 0, sizeof s);
+ s.base = 0;
+ s.size = 0xFFFFFFFF;
+ s.offset = 0;
+ s.name = "data";
+ s.file = nil;
+ s.rw = ptracerw;
+ s.pid = pid;
+ if(addseg(map, s) < 0)
+ return -1;
+
+ if((r = mallocz(sizeof(PtraceRegs), 1)) == nil)
+ return -1;
+ r->r.rw = ptraceregrw;
+ r->pid = pid;
+ *rp = (Regs*)r;
+ return 0;
+}
+
+int
+detachproc(int pid)
+{
+ return ptrace(PTRACE_DETACH, pid, 0, 0);
+}
+
+static int
+ptracerw(Map *map, Seg *seg, ulong addr, void *v, uint n, int isr)
+{
+ 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);
+ if(errno)
+ goto ptraceerr;
+ if(n-i >= 4)
+ *(u32int*)((char*)v+i) = u;
+ else{
+ *(u32int*)buf = u;
+ memmove((char*)v+i, buf, n-i);
+ }
+ }else{
+ if(n-i >= 4)
+ u = *(u32int*)((char*)v+i);
+ else{
+ errno = 0;
+ u = ptrace(PTRACE_PEEKDATA, seg->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)
+ goto ptraceerr;
+ }
+ }
+ return 0;
+
+ptraceerr:
+ werrstr("ptrace: %r");
+ return -1;
+}
+
+static char* linuxregs[] = {
+ "BX",
+ "CX",
+ "DX",
+ "SI",
+ "DI",
+ "BP",
+ "AX",
+ "DS",
+ "ES",
+ "FS",
+ "GS",
+ "OAX",
+ "PC",
+ "CS",
+ "EFLAGS",
+ "SP",
+ "SS",
+};
+
+static ulong
+reg2linux(char *reg)
+{
+ int i;
+
+ for(i=0; i<nelem(linuxregs); i++)
+ if(strcmp(linuxregs[i], reg) == 0)
+ return 4*i;
+ return ~(ulong)0;
+}
+
+static int
+ptraceregrw(Regs *regs, char *name, ulong *val, int isr)
+{
+ int pid;
+ ulong addr;
+ u32int u;
+
+ pid = ((PtraceRegs*)regs)->pid;
+ addr = reg2linux(name);
+ if(~addr == 0){
+ if(isr){
+ *val = ~(ulong)0;
+ return 0;
+ }
+ werrstr("register not available");
+ return -1;
+ }
+ if(isr){
+ errno = 0;
+ u = ptrace(PTRACE_PEEKUSER, pid, addr, 0);
+ if(errno)
+ goto ptraceerr;
+ *val = u;
+ }else{
+ u = *val;
+ if(ptrace(PTRACE_POKEUSER, pid, addr, &u) < 0)
+ goto ptraceerr;
+ }
+ return 0;
+
+ptraceerr:
+ werrstr("ptrace: %r");
+ return -1;
+}
+
+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';
+}
+
+/* /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;
+}
+
+#undef waitpid
+
+int
+ctlproc(int pid, char *msg)
+{
+ int p, status;
+
+ 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, "waitstop") == 0){
+ waitstop:
+ if(isstopped(pid))
+ return 0;
+ for(;;){
+ p = waitpid(pid, &status, WUNTRACED);
+ if(p <= 0)
+ return -1;
+ 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 buf;
+ if(access(pbuf, AEXIST) >= 0)
+ return pbuf;
+ return nil;
+}
+
+
+#if 0
+ snprint(buf, sizeof buf, "/proc/%d/maps", pid);
+ if((b = Bopen(buf, OREAD)) == nil){
+ werrstr("open %s: %r", buf);
+ return -1;
+ }
+
+/*
+ 08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm
+ 08056000-08058000 rw-p 0000d000 03:0c 64593 /usr/sbin/gpm
+ 08058000-0805b000 rwxp 00000000 00:00 0
+ 40000000-40013000 r-xp 00000000 03:0c 4165 /lib/ld-2.2.4.so
+ 40013000-40015000 rw-p 00012000 03:0c 4165 /lib/ld-2.2.4.so
+ 4001f000-40135000 r-xp 00000000 03:0c 45494 /lib/libc-2.2.4.so
+ 40135000-4013e000 rw-p 00115000 03:0c 45494 /lib/libc-2.2.4.so
+ 4013e000-40142000 rw-p 00000000 00:00 0
+ bffff000-c0000000 rwxp 00000000 00:00 0
+*/
+
+ file = nil;
+ while((p = Brdline(b, '\n')) != nil){
+ p[Blinelen(b)-1] = 0;
+ memset(f, 0, sizeof f);
+ if((nf = getfields(p, f, 6, 1, " ")) < 5)
+ continue;
+ base = strtoul(f[0], &p, 16);
+ if(*p != '-')
+ continue;
+ end = strtoul(p+1, &p, 16);
+ if(*p != 0)
+ continue;
+ offset = strtoul(f[2], &p, 16);
+ if(*p != 0)
+ continue;
+ if(nf == 6)
+ file = f[5];
+ zero = atoi(f[4]) == 0;
+ print("%lux-%lux %lux %s %s\n", base, end, offset, zero ? "data" : "text", file ? file : "");
+ s.base = base;
+ s.size = end - base;
+ s.offset = offset;
+ s.name = zero ? "data" : "text";
+ s.file = strdup(file);
+ s.rw = ptracerw;
+ s.pid = pid;
+ if(addseg(map, s) < 0){
+ Bterm(b);
+ ptrace(PTRACE_DETACH, pid, 0, 0);
+ return -1;
+ }
+ }
+ Bterm(b);
+#endif
+
diff --git a/src/libmach/Notes b/src/libmach/Notes
new file mode 100644
index 00000000..ea8a808c
--- /dev/null
+++ b/src/libmach/Notes
@@ -0,0 +1,13 @@
+
+TODO ===============
+
+Move 386 crap out of symdwarf.c, symstabs.c.
+Implement line2pc in dwarf.c.
+Parse ELF .dynamic section for a few more symbols.
+
+Add stabs support to acidtypes.
+Handle multiple symbol files better in acid.
+Write acid code to walk dynamic linking tables.
+
+Write lax command-line parsing code for acid, db.
+
diff --git a/src/libmach/Notes.stab b/src/libmach/Notes.stab
new file mode 100644
index 00000000..78046f50
--- /dev/null
+++ b/src/libmach/Notes.stab
@@ -0,0 +1,166 @@
+stabs
+
+N_MAIN with name "main" to identify entry function
+N_SO source file. might be preceded by dir ending in /
+ value is code ptr
+ empty string means source done
+N_SOL source include file, value = text addr of where this starts
+N_SLINE start of source line
+ no name
+ desc = line number
+ value = code addr for that line
+
+N_FUN (36) function def
+ 'F' global 'f' local
+ value = addr
+ desc = line number of def
+ return type is number after :
+
+ nil name marks end of function
+
+constants
+
+ c= XXX p. 15
+
+N_LSYM (128) local variable
+ :type-number
+ value = offset from fp
+ :ptype means parameter passed in reg but stored as variable
+
+N_GSYM (32) global variable
+ addr not given (use external symbol)
+ :type
+
+N_RSYM register value
+
+N_STSYM(38)/N_FUN/N_LCSYM(40) data/text/bss
+ static varibale 'S' file static 'V' procedure static
+ :Stype :Vtype
+
+N_PSYM (160) parameter
+ :ptype
+ value=offset from fp
+ desc = line number of decl
+
+ register params followed by an N_RSYM with same name and :rtype.
+
+skip types
+
+type (a,b) means a=file number b=type number
+
+N_BINCL/N_EINCL begin/end include
+N_EXCL - same effect as earlier guy
+
+
+
+=============
+
+type crap
+
+
+
+ name:symbol_opt typeinfo
+
+typeinfo ::= typenum | typenum = attr* typedef
+
+typenum ::= integer | '(' integer ',' integer ')'
+
+attr ::= @ attrtext ;
+
+attrtext ::= 'a' integer (alignment)
+ | 'p' integer (pointer class)
+ | 'P' (packed type)
+ | 's' integer (size of type in bits)
+ | 'S' (string instead of array of chars)
+
+typedef ::= typeinfo
+ | 'b' ('u' | 's') 'c'? width; offset; nbits; (builtin, signed/unsigned, char/not, width in bytes, offset & nbits of type)
+ | 'w' (aix wide char type, not used)
+ | 'R' fptype; bytes; (fptype 1=32-bit, 2=64-bit, 3=complex, 4=complex16, 5=complex32, 6=long double)
+ | 'g' typeinfo ';' nbits (aix floating, not used)
+ | 'c' typeinfo ';' nbits (aix complex, not used)
+ | -1 int32
+ | -2 char8
+ | -3 int16
+ | -4 int32 (long)
+ | -5 uchar8
+ | -6 schar8
+ | -7 uint16
+ | -8 uint32
+ | -9 uint32
+ | -10 ulong32
+ | -11 void
+ | -12 float
+ | -13 double
+ | -14 long double
+ | -15 int32
+ | -16 bool32
+ | -17 short real
+ | -18 real
+ | -19 stringptr
+ | -20 character8
+ | -21 logical*1 8
+ | -22 logical*2 16
+ | -23 logical*4 32
+ | -24 logical 32
+ | -25 complex (two single)
+ | -26 complex (two double)
+ | -27 integer*1 8 signed
+ | -28 integer*2 16 signed
+ | -29 integer*4 32 signed
+ | -30 wchar 16 wide char
+ | -31 int64
+ | -32 uint64
+ | -33 logical*8 64
+ | -34 integer*8 64 signed
+ | 'b' typeinfo ';' bytes (ibm, no idea)
+ | 'B' typeinfo (volatile typref)
+ | 'd' typeinfo (file of typeref)
+ | 'k' typeinfo (const typeref)
+ | 'M' typeinfo ';' length (multiple instance type, fortran)
+ | 'S' typeinfo (set, typeref must have small number of values)
+ | '*' typeinfo (pointer to typeref)
+ | 'x' ('s'|'u'|'e') name ':' (struct, union, enum reference. name can have '::' in it)
+ | 'r' typeinfo ';' low ';' high ';' (subrange. typeref can be type being defined for base types!)
+ low and high are bounds
+ if bound is octal power of two, it's a big negative number
+ | ('a'|'P') indextypedef arraytypeinfo (array, index should be range type)
+ indextype is type definition not typeinfo (need not say typenum=)
+ P means packed array
+ | 'A' arraytypeinfo (open array (no index bounds))
+ | 'D' dims ';' typeinfo (dims-dimensional dynamic array)
+ | 'E' dims ';' typeinfo (subarray of N-dimensional array)
+ | 'n' typeinfo ';' bytes (max length string)
+ | 'z' typeinfo ';' bytes (no idea what difference is from 'n')
+ | 'N' (pascal stringptr)
+ | 'e' (name ':' bigint ',')* ';' (enum listing)
+ | ('s'|'u') bytes (name ':' type ',' bitoffset ',' bitsize ';')* ';' (struct/union defn)
+ tag is given as name in stabs entry, with 'T' symbol
+ | 'f' typeinfo ';' (function returning type)
+ | 'f' rettypeinfo ',' paramcount ';' (typeinfo ',' (0|1) ';')* ';'
+ | 'p' paramcount ';' (typeinfo ',' (0|1) ';')* ';'
+ | 'F' rettypeinfo ',' paramcount ';' (name ':' typeinfo ',' (0|1) ';')* ';'
+ | 'R' paramcount ';' (name ':' typeinfo ',' (0|1) ';')* ';'
+ (the 0 or 1 is pass-by-reference vs pass-by-value)
+ (the 0 or 1 is pass-by-reference vs pass-by-value)
+
+bound ::=
+ 'A' offset (bound is on stack by ref at offset offset from arg list)
+ | 'T' offset (bound is on stack by val at offset offset from arg list)
+ | 'a' regnum (bound passed by reference in register)
+ | 't' regnum (bound passed by value in register)
+ | 'J' (no bound)
+ | bigint
+
+bigint ::= '-'? decimal
+ | 0 octal
+ | -1
+
+C++
+
+symbol 'Tt' means typename + tag in one stab
+
+names can have ::, as in foo::bar::baz::t1
+
+t16 unknown type just like void
+t17 vtable record type
diff --git a/src/libmach/crack.c b/src/libmach/crack.c
new file mode 100644
index 00000000..75651897
--- /dev/null
+++ b/src/libmach/crack.c
@@ -0,0 +1,91 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+static struct
+{
+ ulong magic;
+ int (*fn)(int, Fhdr*);
+} cracktab[] = {
+ 0x7F454C46, crackelf,
+ 0xFEEDFACE, crackmacho,
+};
+
+Fhdr*
+crackhdr(char *name, int mode)
+{
+ uchar buf[4];
+ ulong magic;
+ int i, fd;
+ Fhdr *hdr;
+
+ if((fd = open(name, mode)) < 0)
+ return nil;
+
+ if(seek(fd, 0, 0) < 0 || readn(fd, buf, 4) != 4){
+ close(fd);
+ return nil;
+ }
+
+ hdr = mallocz(sizeof(Fhdr), 1);
+ if(hdr == nil){
+ close(fd);
+ return nil;
+ }
+ hdr->filename = strdup(name);
+ magic = beload4(buf);
+ werrstr("magic doesn't match");
+ for(i=0; i<nelem(cracktab); i++)
+ if(cracktab[i].magic == magic && seek(fd, 0, 0) == 0 && cracktab[i].fn(fd, hdr) >= 0){
+ _addhdr(hdr);
+ return hdr;
+ }
+ werrstr("unknown file type: %r");
+ free(hdr);
+ close(fd);
+ return nil;
+}
+
+void
+uncrackhdr(Fhdr *hdr)
+{
+ close(hdr->fd);
+ _delhdr(hdr);
+ free(hdr);
+}
+
+int
+mapfile(Fhdr *fp, ulong base, Map *map, Regs **regs)
+{
+ if(fp == nil){
+ werrstr("no file");
+ return -1;
+ }
+ if(map == nil){
+ werrstr("no map");
+ return -1;
+ }
+ if(fp->map == nil){
+ werrstr("cannot load map for this file type");
+ return -1;
+ }
+ return fp->map(fp, base, map, regs);
+}
+
+void
+unmapfile(Fhdr *fp, Map *map)
+{
+ int i;
+
+ if(map == nil || fp == nil)
+ return;
+
+ for(i=0; i<map->nseg; i++){
+ while(i<map->nseg && map->seg[i].fd == fp->fd){
+ map->nseg--;
+ memmove(&map->seg[i], &map->seg[i+1],
+ (map->nseg-i)*sizeof(map->seg[0]));
+ }
+ }
+}
diff --git a/src/libmach/crackelf.c b/src/libmach/crackelf.c
new file mode 100644
index 00000000..0d15804b
--- /dev/null
+++ b/src/libmach/crackelf.c
@@ -0,0 +1,342 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "elf.h"
+#include "dwarf.h"
+
+static int mapelf(Fhdr *fp, ulong base, Map *map, Regs**);
+static int mapcoreregs(Fhdr *fp, Map *map, Regs**);
+
+static struct
+{
+ uint etype;
+ uint mtype;
+ Mach *mach;
+ char *name;
+} mtab[] =
+{ /* Font Tab 4 */
+ ElfMachSparc, MSPARC, nil, "sparc",
+ ElfMach386, M386, &mach386, "386",
+ ElfMachMips, MMIPS, nil, "mips",
+ ElfMachArm, MARM, nil, "arm",
+ ElfMachPower, MPOWER, nil, "powerpc",
+ ElfMachPower64, MNONE, nil, "powerpc64",
+};
+
+static struct
+{
+ uint etype;
+ uint atype;
+ char *aname;
+} atab[] =
+{ /* Font Tab 4 */
+ ElfAbiSystemV, ALINUX, "linux", /* [sic] */
+ ElfAbiLinux, ALINUX, "linux",
+ ElfAbiFreeBSD, AFREEBSD, "freebsd",
+};
+
+static struct
+{
+ uint mtype;
+ uint atype;
+ int (*coreregs)(Elf*, ElfNote*, uchar**);
+} ctab[] =
+{ /* Font Tab 4 */
+ M386, ALINUX, coreregslinux386,
+ M386, ANONE, coreregslinux386, /* [sic] */
+ M386, AFREEBSD, coreregsfreebsd386,
+};
+
+int
+crackelf(int fd, Fhdr *fp)
+{
+ int i, havetext, havedata;
+ Elf *elf;
+ ElfProg *p;
+ ElfSect *s1, *s2;
+
+ if((elf = elfinit(fd)) == nil)
+ return -1;
+
+ fp->fd = fd;
+ fp->elf = elf;
+ fp->dwarf = dwarfopen(elf); /* okay to fail */
+ fp->syminit = symelf;
+
+ if((s1 = elfsection(elf, ".stab")) != nil && s1->link!=0 && s1->link < elf->nsect){
+ s2 = &elf->sect[s1->link];
+ if(elfmap(elf, s1) >= 0 && elfmap(elf, s2) >= 0){
+ fp->stabs.stabbase = s1->base;
+ fp->stabs.stabsize = s1->size;
+ fp->stabs.strbase = s2->base;
+ fp->stabs.strsize = s2->size;
+ fp->stabs.e2 = elf->hdr.e2;
+ fp->stabs.e4 = elf->hdr.e4;
+ }
+ }
+
+ for(i=0; i<nelem(mtab); i++){
+ if(elf->hdr.machine != mtab[i].etype)
+ continue;
+ fp->mach = mtab[i].mach;
+ fp->mname = mtab[i].name;
+ fp->mtype = mtab[i].mtype;
+ break;
+ }
+ if(i == nelem(mtab)){
+ werrstr("unsupported machine type %d", elf->hdr.machine);
+ goto err;
+ }
+
+ if(mach == nil)
+ mach = fp->mach;
+
+ fp->aname = "unknown";
+ for(i=0; i<nelem(atab); i++){
+ if(elf->hdr.abi != atab[i].etype)
+ continue;
+ fp->atype = atab[i].atype;
+ fp->aname = atab[i].aname;
+ break;
+ }
+
+ switch(elf->hdr.type){
+ default:
+ werrstr("unknown file type %d", elf->hdr.type);
+ goto err;
+ case ElfTypeExecutable:
+ fp->ftype = FEXEC;
+ fp->fname = "executable";
+ break;
+ case ElfTypeRelocatable:
+ fp->ftype = FRELOC;
+ fp->fname = "relocatable";
+ break;
+ case ElfTypeSharedObject:
+ fp->ftype = FSHOBJ;
+ fp->fname = "shared object";
+ break;
+ case ElfTypeCore:
+ fp->ftype = FCORE;
+ fp->fname = "core dump";
+ break;
+ }
+
+ fp->map = mapelf;
+
+ if(fp->ftype == FCORE){
+ for(i=0; i<nelem(ctab); i++){
+ if(ctab[i].atype != fp->atype
+ || ctab[i].mtype != fp->mtype)
+ continue;
+ elf->coreregs = ctab[i].coreregs;
+ break;
+ }
+ return 0;
+ }
+
+ fp->entry = elf->hdr.entry;
+
+ /* First r-x section we find is the text and initialized data */
+ /* First rw- section we find is the r/w data */
+ havetext = 0;
+ havedata = 0;
+ for(i=0; i<elf->nprog; i++){
+ p = &elf->prog[i];
+ if(p->type != ElfProgLoad)
+ continue;
+ if(!havetext && p->flags == (ElfProgFlagRead|ElfProgFlagExec) && p->align >= mach->pgsize){
+ havetext = 1;
+ fp->txtaddr = p->vaddr;
+ fp->txtsz = p->memsz;
+ fp->txtoff = p->offset;
+ }
+ if(!havedata && p->flags == (ElfProgFlagRead|ElfProgFlagWrite) && p->align >= mach->pgsize){
+ havedata = 1;
+ fp->dataddr = p->vaddr;
+ fp->datsz = p->filesz;
+ fp->datoff = p->offset;
+ fp->bsssz = p->memsz - p->filesz;
+ }
+ }
+ if(!havetext){
+ werrstr("did not find text segment in elf binary");
+ goto err;
+ }
+ if(!havedata){
+ werrstr("did not find data segment in elf binary");
+ goto err;
+ }
+ return 0;
+
+err:
+ elfclose(elf);
+ return -1;
+}
+
+static int
+mapelf(Fhdr *fp, ulong base, Map *map, Regs **regs)
+{
+ int i;
+ Elf *elf;
+ ElfProg *p;
+ ulong sz;
+ ulong lim;
+ Seg s;
+
+ elf = fp->elf;
+ if(elf == nil){
+ werrstr("not an elf file");
+ return -1;
+ }
+
+ for(i=0; i<elf->nprog; i++){
+ p = &elf->prog[i];
+ if(p->type != ElfProgLoad)
+ continue;
+ if(p->align < mach->pgsize)
+ continue;
+ if(p->filesz){
+ memset(&s, 0, sizeof s);
+ s.file = fp->filename;
+ s.fd = fp->fd;
+ if(fp->ftype == FCORE)
+ s.name = "core";
+ else if(p->flags == 5)
+ s.name = "text";
+ else
+ s.name = "data";
+ s.base = base+p->vaddr;
+ s.size = p->filesz;
+ s.offset = p->offset;
+ if(addseg(map, s) < 0)
+ return -1;
+ }
+ /*
+ * If memsz > filesz, we're supposed to zero fill.
+ * Core files have zeroed sections where the pages
+ * can be filled in from the text file, so if this is a core
+ * we only fill in that which isn't yet mapped.
+ */
+ if(fp->ftype == FCORE){
+ sz = p->filesz;
+ while(sz < p->memsz){
+ if(addrtoseg(map, base+p->vaddr+sz, &s) < 0){
+ lim = base + p->vaddr + p->memsz;
+ if(addrtosegafter(map, base+p->vaddr+sz, &s) >= 0 && s.base < lim)
+ lim = s.base;
+ memset(&s, 0, sizeof s);
+ s.name = "zero";
+ s.base = base + p->vaddr + sz;
+ s.size = lim - s.base;
+ s.offset = p->offset;
+ if(addseg(map, s) < 0)
+ return -1;
+ }else
+ sz = (s.base+s.size) - (base + p->vaddr);
+ }
+ }else{
+ if(p->filesz < p->memsz){
+ memset(&s, 0, sizeof s);
+ s.name = "zero";
+ s.base = base + p->vaddr + p->filesz;
+ s.size = p->memsz - p->filesz;
+ if(addseg(map, s) < 0)
+ return -1;
+ }
+ }
+ }
+
+ if(fp->ftype == FCORE){
+ if(mapcoreregs(fp, map, regs) < 0)
+ fprint(2, "warning: reading core regs: %r");
+ }
+
+ return 0;
+}
+
+static int
+unpacknote(Elf *elf, uchar *a, uchar *ea, ElfNote *note, uchar **pa)
+{
+ if(a+12 > ea)
+ return -1;
+ note->namesz = elf->hdr.e4(a);
+ note->descsz = elf->hdr.e4(a+4);
+ note->type = elf->hdr.e4(a+8);
+ a += 12;
+ note->name = (char*)a;
+/* XXX fetch alignment constants from elsewhere */
+ a += (note->namesz+3)&~3;
+ note->desc = (uchar*)a;
+ a += (note->descsz+3)&~3;
+ if(a > ea)
+ return -1;
+ *pa = a;
+ return 0;
+}
+
+static int
+mapcoreregs(Fhdr *fp, Map *map, Regs **rp)
+{
+ int i;
+ uchar *a, *sa, *ea, *uregs;
+ uint n;
+ ElfNote note;
+ ElfProg *p;
+ Elf *elf;
+ UregRegs *r;
+
+ elf = fp->elf;
+ if(elf->coreregs == nil){
+ werrstr("cannot parse %s %s cores", fp->mname, fp->aname);
+ return -1;
+ }
+
+ for(i=0; i<elf->nprog; i++){
+ p = &elf->prog[i];
+ if(p->type != ElfProgNote)
+ continue;
+ n = p->filesz;
+ a = malloc(n);
+ if(a == nil)
+ return -1;
+ if(seek(fp->fd, p->offset, 0) < 0 || readn(fp->fd, a, n) != n){
+ free(a);
+ continue;
+ }
+ sa = a;
+ ea = a+n;
+ while(a < ea){
+ note.offset = (a-sa) + p->offset;
+ if(unpacknote(elf, a, ea, &note, &a) < 0)
+ break;
+ switch(note.type){
+ case ElfNotePrStatus:
+ if((n = (*elf->coreregs)(elf, &note, &uregs)) < 0){
+ free(sa);
+ return -1;
+ }
+ free(sa);
+ if((r = mallocz(sizeof(*r), 1)) == nil){
+ free(uregs);
+ return -1;
+ }
+ r->r.rw = _uregrw;
+ r->ureg = uregs;
+ *rp = &r->r;
+ return 0;
+ case ElfNotePrFpreg:
+ case ElfNotePrPsinfo:
+ case ElfNotePrTaskstruct:
+ case ElfNotePrAuxv:
+ case ElfNotePrXfpreg:
+ break;
+ }
+ // fprint(2, "0x%lux note %s/%lud %p\n", note.offset, note.name, note.type, note.desc);
+ }
+ free(sa);
+ }
+ fprint(2, "could not find registers in core file\n");
+ return -1;
+}
+
diff --git a/src/libmach/crackmacho.c b/src/libmach/crackmacho.c
new file mode 100644
index 00000000..bfc0fd62
--- /dev/null
+++ b/src/libmach/crackmacho.c
@@ -0,0 +1,198 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "macho.h"
+
+static int mapmacho(Fhdr *fp, ulong base, Map *map, Regs**);
+
+static struct
+{
+ uint etype;
+ uint mtype;
+ Mach *mach;
+ char *name;
+ int (*coreregs)(Macho*, uchar**);
+} mtab[] =
+{
+ MachoCpuPower, MPOWER, &machpower, "powerpc", coreregsmachopower,
+};
+
+static uchar*
+load(int fd, ulong off, int size)
+{
+ uchar *a;
+
+ a = malloc(size);
+ if(a == nil)
+ return nil;
+ if(seek(fd, off, 0) < 0 || readn(fd, a, size) != size){
+ free(a);
+ return nil;
+ }
+ return a;
+}
+
+int
+crackmacho(int fd, Fhdr *fp)
+{
+ int i;
+ Macho *m;
+
+ if((m = machoinit(fd)) == nil)
+ return -1;
+
+ fp->fd = fd;
+ fp->macho = m;
+
+ for(i=0; i<nelem(mtab); i++){
+ if(m->cputype != mtab[i].etype)
+ continue;
+ fp->mach = mtab[i].mach;
+ fp->mtype = mtab[i].mtype;
+ fp->mname = mtab[i].name;
+ m->coreregs = mtab[i].coreregs;
+ break;
+ }
+ if(i == nelem(mtab)){
+ werrstr("unsupported cpu type %ud", m->cputype);
+ goto err;
+ }
+
+ fp->atype = AMACH;
+ fp->aname = "mach";
+
+ if(mach == nil)
+ mach = fp->mach;
+
+ switch(m->filetype){
+ default:
+ werrstr("unsupported macho file type %lud", m->filetype);
+ goto err;
+ case MachoFileObject:
+ fp->ftype = FOBJ;
+ fp->fname = "object";
+ break;
+ case MachoFileExecutable:
+ fp->ftype = FEXEC;
+ fp->fname = "executable";
+ break;
+ case MachoFileFvmlib:
+ fp->ftype = FSHLIB;
+ fp->fname = "shared library";
+ break;
+ case MachoFileCore:
+ fp->ftype = FCORE;
+ fp->fname = "core";
+ break;
+ case MachoFilePreload:
+ fp->ftype = FBOOT;
+ fp->fname = "preloaded executable";
+ break;
+ }
+
+ fp->txtaddr = fp->dataddr = 0;
+ fp->txtsz = fp->datsz = 0;
+ for(i=0; i<m->ncmd; i++){
+ if(m->cmd[i].type != MachoCmdSegment)
+ continue;
+ if(strcmp(m->cmd[i].seg.name, "__TEXT") == 0){
+ fp->txtaddr = m->cmd[i].seg.vmaddr;
+ fp->txtsz = m->cmd[i].seg.vmsize;
+ fp->txtoff = m->cmd[i].seg.fileoff;
+ }
+ if(strcmp(m->cmd[i].seg.name, "__DATA") == 0){
+ fp->dataddr = m->cmd[i].seg.vmaddr;
+ fp->datsz = m->cmd[i].seg.filesz;
+ fp->datoff = m->cmd[i].seg.fileoff;
+ fp->bsssz = m->cmd[i].seg.vmsize - fp->datsz;
+ }
+ }
+
+ fp->map = mapmacho;
+ fp->syminit = symmacho;
+
+ for(i=0; i<m->ncmd; i++)
+ if(m->cmd[i].type == MachoCmdSymtab)
+ break;
+ if(i < m->ncmd){
+ fp->stabs.stabbase = load(fp->fd, m->cmd[i].sym.symoff, m->cmd[i].sym.nsyms*16);
+ fp->stabs.stabsize = m->cmd[i].sym.nsyms*16;
+ fp->stabs.strbase = load(fp->fd, m->cmd[i].sym.stroff, m->cmd[i].sym.strsize);
+ if(fp->stabs.stabbase == nil || fp->stabs.strbase == nil){
+ fp->stabs.stabbase = nil;
+ fp->stabs.strbase = nil;
+ }else{
+ fp->stabs.strsize = m->cmd[i].sym.strsize;
+ fp->stabs.e2 = (m->e4==beload4 ? beload2 : leload2);
+ fp->stabs.e4 = m->e4;
+ }
+ }
+
+ return 0;
+
+err:
+ machoclose(m);
+ return -1;
+}
+
+static int
+mapmacho(Fhdr *fp, ulong base, Map *map, Regs **rp)
+{
+ int i, n;
+ uchar *u;
+ Macho *m;
+ MachoCmd *c;
+ Seg s;
+ UregRegs *r;
+
+ m = fp->macho;
+ if(m == nil){
+ werrstr("not a macho file");
+ return -1;
+ }
+
+ for(i=0; i<m->ncmd; i++){
+ c = &m->cmd[i];
+ if(c->type != MachoCmdSegment)
+ continue;
+ if(c->seg.filesz){
+ memset(&s, 0, sizeof s);
+ s.file = fp->filename;
+ s.fd = fp->fd;
+ if(fp->ftype == FCORE)
+ s.name = "core";
+ else if(strcmp(c->seg.name, "__DATA") == 0)
+ s.name = "data";
+ else
+ s.name = "text";
+ s.base = base+c->seg.vmaddr;
+ s.size = c->seg.filesz;
+ s.offset = c->seg.fileoff;
+ if(addseg(map, s) < 0)
+ return -1;
+ }
+ if(c->seg.filesz < c->seg.vmsize){
+ memset(&s, 0, sizeof s);
+ s.name = "zero";
+ s.base = base + c->seg.vmaddr + c->seg.filesz;
+ s.size = c->seg.vmsize - c->seg.filesz;
+ if(addseg(map, s) < 0)
+ return -1;
+ }
+ }
+
+ if(fp->ftype == FCORE && m->coreregs){
+ n = m->coreregs(m, &u);
+ if(n < 0){
+ fprint(2, "mapping registers: %r\n");
+ goto noregs;
+ }
+ if((r = mallocz(sizeof *r, 1)) == nil)
+ return -1;
+ r->r.rw = _uregrw;
+ r->ureg = u;
+ *rp = &r->r;
+ }
+noregs:
+ return 0;
+}
diff --git a/src/libmach/dwarf.h b/src/libmach/dwarf.h
new file mode 100644
index 00000000..08365239
--- /dev/null
+++ b/src/libmach/dwarf.h
@@ -0,0 +1,460 @@
+typedef struct Dwarf Dwarf;
+typedef struct DwarfAttrs DwarfAttrs;
+typedef struct DwarfBlock DwarfBlock;
+typedef struct DwarfBuf DwarfBuf;
+typedef struct DwarfExpr DwarfExpr;
+typedef struct DwarfSym DwarfSym;
+typedef union DwarfVal DwarfVal;
+
+enum
+{
+ TagArrayType = 0x01,
+ TagClassType = 0x02,
+ TagEntryPoint = 0x03,
+ TagEnumerationType = 0x04,
+ TagFormalParameter = 0x05,
+ TagImportedDeclaration = 0x08,
+ TagLabel = 0x0A,
+ TagLexDwarfBlock = 0x0B,
+ TagMember = 0x0D,
+ TagPointerType = 0x0F,
+ TagReferenceType = 0x10,
+ TagCompileUnit = 0x11,
+ TagStringType = 0x12,
+ TagStructType = 0x13,
+ TagSubroutineType = 0x15,
+ TagTypedef = 0x16,
+ TagUnionType = 0x17,
+ TagUnspecifiedParameters = 0x18,
+ TagVariant = 0x19,
+ TagCommonDwarfBlock = 0x1A,
+ TagCommonInclusion = 0x1B,
+ TagInheritance = 0x1C,
+ TagInlinedSubroutine = 0x1D,
+ TagModule = 0x1E,
+ TagPtrToMemberType = 0x1F,
+ TagSetType = 0x20,
+ TagSubrangeType = 0x21,
+ TagWithStmt = 0x22,
+ TagAccessDeclaration = 0x23,
+ TagBaseType = 0x24,
+ TagCatchDwarfBlock = 0x25,
+ TagConstType = 0x26,
+ TagConstant = 0x27,
+ TagEnumerator = 0x28,
+ TagFileType = 0x29,
+ TagFriend = 0x2A,
+ TagNamelist = 0x2B,
+ TagNamelistItem = 0x2C,
+ TagPackedType = 0x2D,
+ TagSubprogram = 0x2E,
+ TagTemplateTypeParameter = 0x2F,
+ TagTemplateValueParameter = 0x30,
+ TagThrownType = 0x31,
+ TagTryDwarfBlock = 0x32,
+ TagVariantPart = 0x33,
+ TagVariable = 0x34,
+ TagVolatileType = 0x35,
+ TagDwarfProcedure = 0x36,
+ TagRestrictType = 0x37,
+ TagInterfaceType = 0x38,
+ TagNamespace = 0x39,
+ TagImportedModule = 0x3A,
+ TagUnspecifiedType = 0x3B,
+ TagPartialUnit = 0x3C,
+ TagImportedUnit = 0x3D,
+ TagMutableType = 0x3E,
+
+ TypeAddress = 0x01,
+ TypeBoolean = 0x02,
+ TypeComplexFloat = 0x03,
+ TypeFloat = 0x04,
+ TypeSigned = 0x05,
+ TypeSignedChar = 0x06,
+ TypeUnsigned = 0x07,
+ TypeUnsignedChar = 0x08,
+ TypeImaginaryFloat = 0x09,
+
+ AccessPublic = 0x01,
+ AccessProtected = 0x02,
+ AccessPrivate = 0x03,
+
+ VisLocal = 0x01,
+ VisExported = 0x02,
+ VisQualified = 0x03,
+
+ VirtNone = 0x00,
+ VirtVirtual = 0x01,
+ VirtPureVirtual = 0x02,
+
+ LangC89 = 0x0001,
+ LangC = 0x0002,
+ LangAda83 = 0x0003,
+ LangCplusplus = 0x0004,
+ LangCobol74 = 0x0005,
+ LangCobol85 = 0x0006,
+ LangFortran77 = 0x0007,
+ LangFortran90 = 0x0008,
+ LangPascal83 = 0x0009,
+ LangModula2 = 0x000A,
+ LangJava = 0x000B,
+ LangC99 = 0x000C,
+ LangAda95 = 0x000D,
+ LangFortran95 = 0x000E,
+ LangPLI = 0x000F,
+ // 0x8000-0xFFFF reserved
+
+ IdCaseSensitive = 0x00,
+ IdCaseUpper = 0x01,
+ IdCaseLower = 0x02,
+ IdCaseInsensitive = 0x03,
+
+ CallingNormal = 0x01,
+ CallingProgram = 0x02,
+ CallingNocall = 0x03,
+ // 0x40-0xFF reserved
+
+ InNone = 0x00,
+ InInlined = 0x01,
+ InDeclaredNotInlined = 0x02,
+ InDeclaredInlined = 0x03,
+
+ OrderRowMajor = 0x00,
+ OrderColumnMajor = 0x01,
+
+ DiscLabel = 0x00,
+ DiscRange = 0x01,
+
+ TReference = 1<<0,
+ TBlock = 1<<1,
+ TConstant = 1<<2,
+ TString = 1<<3,
+ TFlag = 1<<4,
+ TAddress = 1<<5,
+
+ OpAddr = 0x03, // 1 op, const addr
+ OpDeref = 0x06,
+ OpConst1u = 0x08, // 1 op, 1 byte const
+ OpConst1s = 0x09, // " signed
+ OpConst2u = 0x0A, // 1 op, 2 byte const
+ OpConst2s = 0x0B, // " signed
+ OpConst4u = 0x0C, // 1 op, 4 byte const
+ OpConst4s = 0x0D, // " signed
+ OpConst8u = 0x0E, // 1 op, 8 byte const
+ OpConst8s = 0x0F, // " signed
+ OpConstu = 0x10, // 1 op, LEB128 const
+ OpConsts = 0x11, // " signed
+ OpDup = 0x12,
+ OpDrop = 0x13,
+ OpOver = 0x14,
+ OpPick = 0x15, // 1 op, 1 byte stack index
+ OpSwap = 0x16,
+ OpRot = 0x17,
+ OpXderef = 0x18,
+ OpAbs = 0x19,
+ OpAnd = 0x1A,
+ OpDiv = 0x1B,
+ OpMinus = 0x1C,
+ OpMod = 0x1D,
+ OpMul = 0x1E,
+ OpNeg = 0x1F,
+ OpNot = 0x20,
+ OpOr = 0x21,
+ OpPlus = 0x22,
+ OpPlusUconst = 0x23, // 1 op, ULEB128 addend
+ OpShl = 0x24,
+ OpShr = 0x25,
+ OpShra = 0x26,
+ OpXor = 0x27,
+ OpSkip = 0x2F, // 1 op, signed 2-byte constant
+ OpBra = 0x28, // 1 op, signed 2-byte constant
+ OpEq = 0x29,
+ OpGe = 0x2A,
+ OpGt = 0x2B,
+ OpLe = 0x2C,
+ OpLt = 0x2D,
+ OpNe = 0x2E,
+ OpLit0 = 0x30,
+ // OpLitN = OpLit0 + N for N = 0..31
+ OpReg0 = 0x50,
+ // OpRegN = OpReg0 + N for N = 0..31
+ OpBreg0 = 0x70, // 1 op, signed LEB128 constant
+ // OpBregN = OpBreg0 + N for N = 0..31
+ OpRegx = 0x90, // 1 op, ULEB128 register
+ OpFbreg = 0x91, // 1 op, SLEB128 offset
+ OpBregx = 0x92, // 2 op, ULEB128 reg, SLEB128 off
+ OpPiece = 0x93, // 1 op, ULEB128 size of piece
+ OpDerefSize = 0x94, // 1-byte size of data retrieved
+ OpXderefSize = 0x95, // 1-byte size of data retrieved
+ OpNop = 0x96,
+ // next four new in Dwarf v3
+ OpPushObjAddr = 0x97,
+ OpCall2 = 0x98, // 2-byte offset of DIE
+ OpCall4 = 0x99, // 4-byte offset of DIE
+ OpCallRef = 0x9A, // 4- or 8- byte offset of DIE
+ // 0xE0-0xFF reserved for user-specific
+};
+
+struct DwarfBlock
+{
+ uchar *data;
+ ulong len;
+};
+
+/* not for consumer use */
+struct DwarfBuf
+{
+ Dwarf *d;
+ uchar *p;
+ uchar *ep;
+ uint addrsize;
+};
+
+union DwarfVal
+{
+ char *s;
+ ulong c;
+ ulong r;
+ DwarfBlock b;
+};
+
+struct DwarfAttrs
+{
+ ulong tag;
+ uchar haskids;
+
+ /* whether we have it, along with type */
+ struct {
+ uchar abstractorigin;
+ uchar accessibility;
+ uchar addrclass;
+ uchar basetypes;
+ uchar bitoffset;
+ uchar bitsize;
+ uchar bytesize;
+ uchar calling;
+ uchar commonref;
+ uchar compdir;
+ uchar constvalue;
+ uchar containingtype;
+ uchar count;
+ uchar datamemberloc;
+ uchar declcolumn;
+ uchar declfile;
+ uchar declline;
+ uchar defaultvalue;
+ uchar discr;
+ uchar discrlist;
+ uchar discrvalue;
+ uchar encoding;
+ uchar framebase;
+ uchar friend;
+ uchar highpc;
+ uchar identifiercase;
+ uchar import;
+ uchar inlined;
+ uchar isartificial;
+ uchar isdeclaration;
+ uchar isexternal;
+ uchar isoptional;
+ uchar isprototyped;
+ uchar isvarparam;
+ uchar language;
+ uchar location;
+ uchar lowerbound;
+ uchar lowpc;
+ uchar macroinfo;
+ uchar name;
+ uchar namelistitem;
+ uchar ordering;
+ uchar priority;
+ uchar producer;
+ uchar ranges;
+ uchar returnaddr;
+ uchar segment;
+ uchar sibling;
+ uchar specification;
+ uchar startscope;
+ uchar staticlink;
+ uchar stmtlist;
+ uchar stridesize;
+ uchar stringlength;
+ uchar type;
+ uchar upperbound;
+ uchar uselocation;
+ uchar virtuality;
+ uchar visibility;
+ uchar vtableelemloc;
+ } have;
+
+ ulong abstractorigin;
+ ulong accessibility;
+ ulong addrclass;
+ ulong basetypes;
+ ulong bitoffset;
+ ulong bitsize;
+ ulong bytesize;
+ ulong calling;
+ ulong commonref;
+ char* compdir;
+ DwarfVal constvalue;
+ ulong containingtype;
+ ulong count;
+ DwarfVal datamemberloc;
+ ulong declcolumn;
+ ulong declfile;
+ ulong declline;
+ ulong defaultvalue;
+ ulong discr;
+ DwarfBlock discrlist;
+ ulong discrvalue;
+ ulong encoding;
+ DwarfVal framebase;
+ ulong friend;
+ ulong highpc;
+ ulong identifiercase;
+ ulong import;
+ ulong inlined;
+ uchar isartificial;
+ uchar isdeclaration;
+ uchar isexternal;
+ uchar isoptional;
+ uchar isprototyped;
+ uchar isvarparam;
+ ulong language;
+ DwarfVal location;
+ ulong lowerbound;
+ ulong lowpc;
+ ulong macroinfo;
+ char* name;
+ DwarfBlock namelistitem;
+ ulong ordering;
+ ulong priority;
+ char* producer;
+ ulong ranges;
+ DwarfVal returnaddr;
+ DwarfVal segment;
+ ulong sibling;
+ ulong specification;
+ ulong startscope;
+ DwarfVal staticlink;
+ ulong stmtlist;
+ ulong stridesize;
+ DwarfVal stringlength;
+ ulong type;
+ ulong upperbound;
+ DwarfVal uselocation;
+ ulong virtuality;
+ ulong visibility;
+ DwarfVal vtableelemloc;
+};
+
+enum
+{
+ RuleUndef,
+ RuleSame,
+ RuleCfaOffset,
+ RuleRegister,
+ RuleRegOff,
+ RuleLocation,
+};
+struct DwarfExpr
+{
+ int type;
+ long offset;
+ ulong reg;
+ DwarfBlock loc;
+};
+
+struct DwarfSym
+{
+ DwarfAttrs attrs;
+
+/* not for consumer use... */
+ DwarfBuf b;
+ ulong unit;
+ uint uoff;
+ ulong aoff;
+ int depth;
+ int allunits;
+ ulong nextunit;
+};
+
+
+Dwarf *dwarfopen(Elf *elf);
+void dwarfclose(Dwarf*);
+int dwarfaddrtounit(Dwarf*, ulong, ulong*);
+int dwarflookupfn(Dwarf*, ulong, ulong, DwarfSym*);
+int dwarflookupname(Dwarf*, char*, DwarfSym*);
+int dwarflookupnameinunit(Dwarf*, ulong, char*, DwarfSym*);
+int dwarflookupsubname(Dwarf*, DwarfSym*, char*, DwarfSym*);
+int dwarflookuptag(Dwarf*, ulong, ulong, DwarfSym*);
+int dwarfenumunit(Dwarf*, ulong, DwarfSym*);
+int dwarfseeksym(Dwarf*, ulong, ulong, DwarfSym*);
+int dwarfenum(Dwarf*, DwarfSym*);
+int dwarfnextsym(Dwarf*, DwarfSym*, int);
+int dwarfpctoline(Dwarf*, ulong, char**, char**, char**, ulong*, ulong*, ulong*);
+int dwarfunwind(Dwarf*, ulong, DwarfExpr*, DwarfExpr*, DwarfExpr*, int);
+ulong dwarfget1(DwarfBuf*);
+ulong dwarfget2(DwarfBuf*);
+ulong dwarfget4(DwarfBuf*);
+uvlong dwarfget8(DwarfBuf*);
+ulong dwarfget128(DwarfBuf*);
+long dwarfget128s(DwarfBuf*);
+ulong dwarfgetaddr(DwarfBuf*);
+int dwarfgetn(DwarfBuf*, uchar*, int);
+uchar *dwarfgetnref(DwarfBuf*, ulong);
+char *dwarfgetstring(DwarfBuf*);
+
+
+typedef struct DwarfAbbrev DwarfAbbrev;
+typedef struct DwarfAttr DwarfAttr;
+
+struct DwarfAttr
+{
+ ulong name;
+ ulong form;
+};
+
+struct DwarfAbbrev
+{
+ ulong num;
+ ulong tag;
+ uchar haskids;
+ DwarfAttr *attr;
+ int nattr;
+};
+
+struct Dwarf
+{
+ Elf *elf;
+ int fd;
+ char **reg;
+ int nreg;
+ int addrsize;
+ DwarfBlock abbrev;
+ DwarfBlock aranges;
+ DwarfBlock frame;
+ DwarfBlock info;
+ DwarfBlock line;
+ DwarfBlock pubnames;
+ DwarfBlock pubtypes;
+ DwarfBlock ranges;
+ DwarfBlock str;
+
+ /* little cache */
+ struct {
+ DwarfAbbrev *a;
+ int na;
+ ulong off;
+ } acache;
+};
+
+DwarfAbbrev *dwarfgetabbrev(Dwarf*, ulong, ulong);
+
+int dwarfgetinfounit(Dwarf*, ulong, DwarfBlock*);
+
+extern int dwarf386nregs;
+extern char *dwarf386regs[];
+extern char *dwarf386fp;
+
diff --git a/src/libmach/dwarf386.c b/src/libmach/dwarf386.c
new file mode 100644
index 00000000..d5cc9b71
--- /dev/null
+++ b/src/libmach/dwarf386.c
@@ -0,0 +1,24 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "elf.h"
+#include "dwarf.h"
+
+char*
+dwarf386regs[] =
+{
+ "AX",
+ "CX",
+ "DX",
+ "BX",
+ "SP",
+ "BP",
+ "SI",
+ "DI",
+ "LR",
+ "CFA",
+};
+
+int dwarf386nregs = 10;
+
+
diff --git a/src/libmach/dwarfabbrev.c b/src/libmach/dwarfabbrev.c
new file mode 100644
index 00000000..2db418dd
--- /dev/null
+++ b/src/libmach/dwarfabbrev.c
@@ -0,0 +1,129 @@
+/*
+ * Dwarf abbreviation parsing code.
+ *
+ * The convention here is that calling dwarfgetabbrevs relinquishes
+ * access to any abbrevs returned previously. Will have to add
+ * explicit reference counting if this turns out not to be acceptable.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "elf.h"
+#include "dwarf.h"
+
+static int parseabbrevs(Dwarf*, ulong, DwarfAbbrev*, DwarfAttr*, int*, int*);
+DwarfAbbrev *dwarfgetabbrev(Dwarf*, ulong, ulong);
+
+static int
+loadabbrevs(Dwarf *d, ulong off, DwarfAbbrev **aa)
+{
+ int nattr, nabbrev;
+ DwarfAbbrev *abbrev;
+ DwarfAttr *attr;
+
+ if(d->acache.off == off && d->acache.na){
+ *aa = d->acache.a;
+ return d->acache.na;
+ }
+
+ /* two passes - once to count, then allocate, then a second to copy */
+ if(parseabbrevs(d, off, nil, nil, &nabbrev, &nattr) < 0)
+ return -1;
+
+ abbrev = malloc(nabbrev*sizeof(DwarfAbbrev) + nattr*sizeof(DwarfAttr));
+ attr = (DwarfAttr*)(abbrev+nabbrev);
+
+ if(parseabbrevs(d, off, abbrev, attr, nil, nil) < 0){
+ free(abbrev);
+ return -1;
+ }
+
+ free(d->acache.a);
+ d->acache.a = abbrev;
+ d->acache.na = nabbrev;
+ d->acache.off = off;
+
+ *aa = abbrev;
+ return nabbrev;
+}
+
+static int
+parseabbrevs(Dwarf *d, ulong off, DwarfAbbrev *abbrev, DwarfAttr *attr, int *pnabbrev, int *pnattr)
+{
+ int i, nabbrev, nattr, haskids;
+ ulong num, tag, name, form;
+ DwarfBuf b;
+
+ if(off >= d->abbrev.len){
+ werrstr("bad abbrev section offset 0x%lux >= 0x%lux\n", off, d->abbrev.len);
+ return -1;
+ }
+
+ memset(&b, 0, sizeof b);
+ b.p = d->abbrev.data + off;
+ b.ep = d->abbrev.data + d->abbrev.len;
+
+ nabbrev = 0;
+ nattr = 0;
+ for(;;){
+ if(b.p == nil){
+ werrstr("malformed abbrev data");
+ return -1;
+ }
+ num = dwarfget128(&b);
+ if(num == 0)
+ break;
+ tag = dwarfget128(&b);
+ haskids = dwarfget1(&b);
+ for(i=0;; i++){
+ name = dwarfget128(&b);
+ form = dwarfget128(&b);
+ if(name == 0 && form == 0)
+ break;
+ if(attr){
+ attr[i].name = name;
+ attr[i].form = form;
+ }
+ }
+ if(abbrev){
+ abbrev->num = num;
+ abbrev->tag = tag;
+ abbrev->haskids = haskids;
+ abbrev->attr = attr;
+ abbrev->nattr = i;
+ abbrev++;
+ attr += i;
+ }
+ nabbrev++;
+ nattr += i;
+ }
+ if(pnabbrev)
+ *pnabbrev = nabbrev;
+ if(pnattr)
+ *pnattr = nattr;
+ return 0;
+}
+
+static DwarfAbbrev*
+findabbrev(DwarfAbbrev *a, int na, ulong num)
+{
+ int i;
+
+ for(i=0; i<na; i++)
+ if(a[i].num == num)
+ return &a[i];
+ return nil;
+}
+
+DwarfAbbrev*
+dwarfgetabbrev(Dwarf *d, ulong off, ulong num)
+{
+ DwarfAbbrev *a;
+ int na;
+
+ if((na = loadabbrevs(d, off, &a)) < 0)
+ return nil;
+ return findabbrev(a, na, num);
+}
+
diff --git a/src/libmach/dwarfaranges.c b/src/libmach/dwarfaranges.c
new file mode 100644
index 00000000..21299588
--- /dev/null
+++ b/src/libmach/dwarfaranges.c
@@ -0,0 +1,63 @@
+/*
+ * Dwarf address ranges parsing code.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "elf.h"
+#include "dwarf.h"
+
+int
+dwarfaddrtounit(Dwarf *d, ulong addr, ulong *unit)
+{
+ DwarfBuf b;
+ int segsize, i;
+ ulong len, id, off, base, size;
+ uchar *start, *end;
+
+ memset(&b, 0, sizeof b);
+ b.d = d;
+ b.p = d->aranges.data;
+ b.ep = b.p + d->aranges.len;
+
+ while(b.p < b.ep){
+ start = b.p;
+ len = dwarfget4(&b);
+ if((id = dwarfget2(&b)) != 2){
+ if(b.p == nil){
+ underflow:
+ werrstr("buffer underflow reading address ranges header");
+ }else
+ werrstr("bad dwarf version 0x%lux in address ranges header", id);
+ return -1;
+ }
+ off = dwarfget4(&b);
+ b.addrsize = dwarfget1(&b);
+ if(d->addrsize == 0)
+ d->addrsize = b.addrsize;
+ segsize = dwarfget1(&b);
+ USED(segsize); /* what am i supposed to do with this? */
+ if(b.p == nil)
+ goto underflow;
+ if((i = (b.p-start) % (2*b.addrsize)) != 0)
+ b.p += 2*b.addrsize - i;
+ end = start+4+len;
+ while(b.p!=nil && b.p<end){
+ base = dwarfgetaddr(&b);
+ size = dwarfgetaddr(&b);
+ if(b.p == nil)
+ goto underflow;
+ if(base <= addr && addr < base+size){
+ *unit = off;
+ return 0;
+ }
+ }
+ if(b.p == nil)
+ goto underflow;
+ b.p = end;
+ }
+ werrstr("address 0x%lux is not listed in dwarf debugging symbols", addr);
+ return -1;
+}
+
diff --git a/src/libmach/dwarfcfa.c b/src/libmach/dwarfcfa.c
new file mode 100644
index 00000000..de342cd7
--- /dev/null
+++ b/src/libmach/dwarfcfa.c
@@ -0,0 +1,391 @@
+/*
+ * Dwarf call frame unwinding.
+ *
+ * The call frame unwinding values are encoded using a state machine
+ * like the pc<->line mapping, but it's a different machine.
+ * The expressions to generate the old values are similar in function to the
+ * ``dwarf expressions'' used for locations in the code, but of course not
+ * the same encoding.
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "elf.h"
+#include "dwarf.h"
+
+#define trace 0
+
+typedef struct State State;
+struct State
+{
+ ulong loc;
+ ulong endloc;
+ ulong iquantum;
+ ulong dquantum;
+ char *augmentation;
+ int version;
+ ulong rareg;
+ DwarfBuf init;
+ DwarfExpr *cfa;
+ DwarfExpr *ra;
+ DwarfExpr *r;
+ DwarfExpr *initr;
+ int nr;
+ DwarfExpr **stack;
+ int nstack;
+};
+
+static int findfde(Dwarf*, ulong, State*, DwarfBuf*);
+static int dexec(DwarfBuf*, State*, int);
+
+int
+dwarfunwind(Dwarf *d, ulong pc, DwarfExpr *cfa, DwarfExpr *ra, DwarfExpr *r, int nr)
+{
+ int i, ret;
+ DwarfBuf fde, b;
+ DwarfExpr *initr;
+ State s;
+
+ initr = mallocz(nr*sizeof(initr[0]), 1);
+ if(initr == 0)
+ return -1;
+
+ memset(&s, 0, sizeof s);
+ s.loc = 0;
+ s.cfa = cfa;
+ s.ra = ra;
+ s.r = r;
+ s.nr = nr;
+
+ if(findfde(d, pc, &s, &fde) < 0){
+ free(initr);
+ return -1;
+ }
+
+ memset(r, 0, nr*sizeof(r[0]));
+ for(i=0; i<nr; i++)
+ r[i].type = RuleSame;
+ if(trace) fprint(2, "s.init %p-%p, fde %p-%p\n", s.init.p, s.init.ep, fde.p, fde.ep);
+ b = s.init;
+ if(dexec(&b, &s, 0) < 0)
+ goto err;
+
+ s.initr = initr;
+ memmove(initr, r, nr*sizeof(initr[0]));
+
+ if(trace) fprint(2, "s.loc 0x%lux pc 0x%lux\n", s.loc, pc);
+ while(s.loc < pc){
+ if(trace) fprint(2, "s.loc 0x%lux pc 0x%lux\n", s.loc, pc);
+ if(dexec(&fde, &s, 1) < 0)
+ goto err;
+ }
+ *ra = s.r[s.rareg];
+
+ ret = 0;
+ goto out;
+
+err:
+ ret = -1;
+out:
+ free(initr);
+ for(i=0; i<s.nstack; i++)
+ free(s.stack[i]);
+ free(s.stack);
+ return ret;
+}
+
+/*
+ * XXX This turns out to be much more expensive than the actual
+ * running of the machine in dexec. It probably makes sense to
+ * cache the last 10 or so fde's we've found, since stack traces
+ * will keep asking for the same info over and over.
+ */
+static int
+findfde(Dwarf *d, ulong pc, State *s, DwarfBuf *fde)
+{
+ static int nbad;
+ char *aug;
+ uchar *next;
+ int i, vers;
+ ulong len, id, base, size;
+ DwarfBuf b;
+
+ b.d = d;
+ b.p = d->frame.data;
+ b.ep = b.p + d->frame.len;
+ b.addrsize = d->addrsize;
+ if(b.addrsize == 0)
+ b.addrsize = 4; /* where should i find this? */
+
+ for(; b.p < b.ep; b.p = next){
+ if((i = (b.p - d->frame.data) % b.addrsize))
+ b.p += b.addrsize - i;
+ len = dwarfget4(&b);
+ if(len > b.ep-b.p){
+ werrstr("bad length in cie/fde header");
+ return -1;
+ }
+ next = b.p+len;
+ id = dwarfget4(&b);
+ if(id == 0xFFFFFFFF){ /* CIE */
+ vers = dwarfget1(&b);
+ if(vers != 1 && vers != 2 && vers != 3){
+ if(++nbad == 1)
+ fprint(2, "unknown cie version %d (wanted 1-3)\n", vers);
+ continue;
+ }
+ aug = dwarfgetstring(&b);
+ if(aug && *aug){
+ if(++nbad == 1)
+ fprint(2, "unknown augmentation: %s\n", aug);
+ continue;
+ }
+ s->iquantum = dwarfget128(&b);
+ s->dquantum = dwarfget128s(&b);
+ s->rareg = dwarfget128(&b);
+ if(s->rareg > s->nr){
+ werrstr("return address is register %d but only have %d registers",
+ s->rareg, s->nr);
+ return -1;
+ }
+ s->init.p = b.p;
+ s->init.ep = next;
+ }else{ /* FDE */
+ base = dwarfgetaddr(&b);
+ size = dwarfgetaddr(&b);
+ fde->p = b.p;
+ fde->ep = next;
+ s->loc = base;
+ s->endloc = base+size;
+ if(base <= pc && pc < base+size)
+ return 0;
+ }
+ }
+ werrstr("cannot find call frame information for pc 0x%lux", pc);
+ return -1;
+
+}
+
+static int
+checkreg(State *s, long r)
+{
+ if(r < 0 || r >= s->nr){
+ werrstr("bad register number 0x%lux", r);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+dexec(DwarfBuf *b, State *s, int locstop)
+{
+ int c;
+ long arg1, arg2;
+ DwarfExpr *e, **p;
+
+ for(;;){
+ if(b->p == b->ep){
+ if(s->initr)
+ s->loc = s->endloc;
+ return 0;
+ }
+ c = dwarfget1(b);
+ if(b->p == nil){
+ werrstr("ran out of instructions during cfa program");
+ if(trace) fprint(2, "%r\n");
+ return -1;
+ }
+ if(trace) fprint(2, "+ loc=0x%lux op 0x%ux ", s->loc, c);
+ switch(c>>6){
+ case 1: /* advance location */
+ arg1 = c&0x3F;
+ advance:
+ if(trace) fprint(2, "loc += %ld\n", arg1*s->iquantum);
+ s->loc += arg1 * s->iquantum;
+ if(locstop)
+ return 0;
+ continue;
+
+ case 2: /* offset rule */
+ arg1 = c&0x3F;
+ arg2 = dwarfget128(b);
+ offset:
+ if(trace) fprint(2, "r%ld += %ld\n", arg1, arg2*s->dquantum);
+ if(checkreg(s, arg1) < 0)
+ return -1;
+ s->r[arg1].type = RuleCfaOffset;
+ s->r[arg1].offset = arg2 * s->dquantum;
+ continue;
+
+ case 3: /* restore initial setting */
+ arg1 = c&0x3F;
+ restore:
+ if(trace) fprint(2, "r%ld = init\n", arg1);
+ if(checkreg(s, arg1) < 0)
+ return -1;
+ s->r[arg1] = s->initr[arg1];
+ continue;
+ }
+
+ switch(c){
+ case 0: /* nop */
+ if(trace) fprint(2, "nop\n");
+ continue;
+
+ case 0x01: /* set location */
+ s->loc = dwarfgetaddr(b);
+ if(trace) fprint(2, "loc = 0x%lux\n", s->loc);
+ if(locstop)
+ return 0;
+ continue;
+
+ case 0x02: /* advance loc1 */
+ arg1 = dwarfget1(b);
+ goto advance;
+
+ case 0x03: /* advance loc2 */
+ arg1 = dwarfget2(b);
+ goto advance;
+
+ case 0x04: /* advance loc4 */
+ arg1 = dwarfget4(b);
+ goto advance;
+
+ case 0x05: /* offset extended */
+ arg1 = dwarfget128(b);
+ arg2 = dwarfget128(b);
+ goto offset;
+
+ case 0x06: /* restore extended */
+ arg1 = dwarfget128(b);
+ goto restore;
+
+ case 0x07: /* undefined */
+ arg1 = dwarfget128(b);
+ if(trace) fprint(2, "r%ld = undef\n", arg1);
+ if(checkreg(s, arg1) < 0)
+ return -1;
+ s->r[arg1].type = RuleUndef;
+ continue;
+
+ case 0x08: /* same value */
+ arg1 = dwarfget128(b);
+ if(trace) fprint(2, "r%ld = same\n", arg1);
+ if(checkreg(s, arg1) < 0)
+ return -1;
+ s->r[arg1].type = RuleSame;
+ continue;
+
+ case 0x09: /* register */
+ arg1 = dwarfget128(b);
+ arg2 = dwarfget128(b);
+ if(trace) fprint(2, "r%ld = r%ld\n", arg1, arg2);
+ if(checkreg(s, arg1) < 0 || checkreg(s, arg2) < 0)
+ return -1;
+ s->r[arg1].type = RuleRegister;
+ s->r[arg1].reg = arg2;
+ continue;
+
+ case 0x0A: /* remember state */
+ e = malloc(s->nr*sizeof(e[0]));
+ if(trace) fprint(2, "push\n");
+ if(e == nil)
+ return -1;
+ p = realloc(s->stack, (s->nstack+1)*sizeof(s->stack[0]));
+ if(p == nil){
+ free(e);
+ return -1;
+ }
+ s->stack[s->nstack++] = e;
+ memmove(e, s->r, s->nr*sizeof(e[0]));
+ continue;
+
+ case 0x0B: /* restore state */
+ if(trace) fprint(2, "pop\n");
+ if(s->nstack == 0){
+ werrstr("restore state underflow");
+ return -1;
+ }
+ e = s->stack[s->nstack-1];
+ memmove(s->r, e, s->nr*sizeof(e[0]));
+ p = realloc(s->stack, (s->nstack-1)*sizeof(s->stack[0]));
+ if(p == nil)
+ return -1;
+ free(e);
+ s->nstack--;
+ continue;
+
+ case 0x0C: /* def cfa */
+ arg1 = dwarfget128(b);
+ arg2 = dwarfget128(b);
+ defcfa:
+ if(trace) fprint(2, "cfa %ld(r%ld)\n", arg2, arg1);
+ if(checkreg(s, arg1) < 0)
+ return -1;
+ s->cfa->type = RuleRegOff;
+ s->cfa->reg = arg1;
+ s->cfa->offset = arg2;
+ continue;
+
+ case 0x0D: /* def cfa register */
+ arg1 = dwarfget128(b);
+ if(trace) fprint(2, "cfa reg r%ld\n", arg1);
+ if(s->cfa->type != RuleRegOff){
+ werrstr("change CFA register but CFA not in register+offset form");
+ return -1;
+ }
+ if(checkreg(s, arg1) < 0)
+ return -1;
+ s->cfa->reg = arg1;
+ continue;
+
+ case 0x0E: /* def cfa offset */
+ arg1 = dwarfget128(b);
+ cfaoffset:
+ if(trace) fprint(2, "cfa off %ld\n", arg1);
+ if(s->cfa->type != RuleRegOff){
+ werrstr("change CFA offset but CFA not in register+offset form");
+ return -1;
+ }
+ s->cfa->offset = arg1;
+ continue;
+
+ case 0x0F: /* def cfa expression */
+ if(trace) fprint(2, "cfa expr\n");
+ s->cfa->type = RuleLocation;
+ s->cfa->loc.len = dwarfget128(b);
+ s->cfa->loc.data = dwarfgetnref(b, s->cfa->loc.len);
+ continue;
+
+ case 0x10: /* def reg expression */
+ arg1 = dwarfget128(b);
+ if(trace) fprint(2, "reg expr r%ld\n", arg1);
+ if(checkreg(s, arg1) < 0)
+ return -1;
+ s->r[arg1].type = RuleLocation;
+ s->r[arg1].loc.len = dwarfget128(b);
+ s->r[arg1].loc.data = dwarfgetnref(b, s->r[arg1].loc.len);
+ continue;
+
+ case 0x11: /* offset extended */
+ arg1 = dwarfget128(b);
+ arg2 = dwarfget128s(b);
+ goto offset;
+
+ case 0x12: /* cfa sf */
+ arg1 = dwarfget128(b);
+ arg2 = dwarfget128s(b);
+ goto defcfa;
+
+ case 0x13: /* cfa offset sf */
+ arg1 = dwarfget128s(b);
+ goto cfaoffset;
+
+ default: /* unknown */
+ werrstr("unknown opcode 0x%ux in cfa program", c);
+ return -1;
+ }
+ }
+ return -1; /* not reached */
+}
+
diff --git a/src/libmach/dwarfdump.c b/src/libmach/dwarfdump.c
new file mode 100644
index 00000000..47c38c37
--- /dev/null
+++ b/src/libmach/dwarfdump.c
@@ -0,0 +1,138 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "elf.h"
+#include "dwarf.h"
+
+void printrules(Dwarf *d, ulong pc);
+int exprfmt(Fmt*);
+
+void
+usage(void)
+{
+ fprint(2, "usage: dwarfdump file\n");
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ int c;
+ Elf *elf;
+ Dwarf *d;
+ DwarfSym s;
+ char *cdir, *dir, *file;
+ ulong line, mtime, length;
+
+ ARGBEGIN{
+ default:
+ usage();
+ }ARGEND
+
+ if(argc != 1)
+ usage();
+
+ fmtinstall('R', exprfmt);
+ fmtinstall('H', encodefmt);
+
+ if((elf = elfopen(argv[0])) == nil)
+ sysfatal("elfopen %s: %r", argv[0]);
+ if((d=dwarfopen(elf)) == nil)
+ sysfatal("dwarfopen: %r");
+
+ if(dwarfenum(d, &s) < 0)
+ sysfatal("dwarfenumall: %r");
+
+ while(dwarfnextsym(d, &s, 1) == 1){
+ switch(s.attrs.tag){
+ case TagCompileUnit:
+ print("compileunit %s\n", s.attrs.name);
+ break;
+ case TagSubprogram:
+ c = 't';
+ goto sym;
+ case TagVariable:
+ c = 'd';
+ goto sym;
+ case TagConstant:
+ c = 'c';
+ goto sym;
+ case TagFormalParameter:
+ if(!s.attrs.name)
+ break;
+ c = 'p';
+ sym:
+ if(s.attrs.isexternal)
+ c += 'A' - 'a';
+ print("%c %s", c, s.attrs.name);
+ if(s.attrs.have.lowpc)
+ print(" 0x%lux-0x%lux", s.attrs.lowpc, s.attrs.highpc);
+ switch(s.attrs.have.location){
+ case TBlock:
+ print(" @ %.*H", s.attrs.location.b.len, s.attrs.location.b.data);
+ break;
+ case TConstant:
+ print(" @ 0x%lux", s.attrs.location.c);
+ break;
+ }
+ if(s.attrs.have.ranges)
+ print(" ranges@0x%lux", s.attrs.ranges);
+ print("\n");
+ if(s.attrs.have.lowpc){
+ if(dwarfpctoline(d, s.attrs.lowpc, &cdir, &dir, &file, &line, &mtime, &length) < 0)
+ print("\tcould not find source: %r\n");
+ else
+ print("\t%s/%s/%s:%lud mtime=%lud length=%lud\n",
+ cdir, dir, file, line, mtime, length);
+
+ if(0) printrules(d, s.attrs.lowpc);
+ if(0) printrules(d, (s.attrs.lowpc+s.attrs.highpc)/2);
+ }
+ break;
+ }
+ }
+ exits(0);
+}
+
+void
+printrules(Dwarf *d, ulong pc)
+{
+ int i;
+ DwarfExpr r[10];
+ DwarfExpr cfa, ra;
+
+ if(dwarfunwind(d, pc, &cfa, &ra, r, nelem(r)) < 0)
+ print("\tcannot unwind from pc 0x%lux: %r\n", pc);
+
+ print("\tpc=0x%lux cfa=%R ra=%R", pc, &cfa, &ra);
+ for(i=0; i<nelem(r); i++)
+ if(r[i].type != RuleSame)
+ print(" r%d=%R", i, &r[i]);
+ print("\n");
+}
+
+int
+exprfmt(Fmt *fmt)
+{
+ DwarfExpr *e;
+
+ if((e = va_arg(fmt->args, DwarfExpr*)) == nil)
+ return fmtstrcpy(fmt, "<nil>");
+
+ switch(e->type){
+ case RuleUndef:
+ return fmtstrcpy(fmt, "undef");
+ case RuleSame:
+ return fmtstrcpy(fmt, "same");
+ case RuleCfaOffset:
+ return fmtprint(fmt, "%ld(cfa)", e->offset);
+ case RuleRegister:
+ return fmtprint(fmt, "r%ld", e->reg);
+ case RuleRegOff:
+ return fmtprint(fmt, "%ld(r%ld)", e->offset, e->reg);
+ case RuleLocation:
+ return fmtprint(fmt, "l.%.*H", e->loc.len, e->loc.data);
+ default:
+ return fmtprint(fmt, "?%d", e->type);
+ }
+}
diff --git a/src/libmach/dwarfeval.c b/src/libmach/dwarfeval.c
new file mode 100644
index 00000000..0da7273a
--- /dev/null
+++ b/src/libmach/dwarfeval.c
@@ -0,0 +1,62 @@
+ OpAddr = 0x03, // 1 op, const addr
+ OpDeref = 0x06,
+ OpConst1u = 0x08, // 1 op, 1 byte const
+ OpConst1s = 0x09, // " signed
+ OpConst2u = 0x0A // 1 op, 2 byte const
+ OpConst2s = 0x0B, // " signed
+ OpConst4u = 0x0C, // 1 op, 4 byte const
+ OpConst4s = 0x0D, // " signed
+ OpConst8u = 0x0E, // 1 op, 8 byte const
+ OpConst8s = 0x0F, // " signed
+ OpConstu = 0x10, // 1 op, LEB128 const
+ OpConsts = 0x11, // " signed
+ OpDup = 0x12,
+ OpDrop = 0x13,
+ OpOver = 0x14,
+ OpPick = 0x15, // 1 op, 1 byte stack index
+ OpSwap = 0x16,
+ OpRot = 0x17,
+ OpXderef = 0x18,
+ OpAbs = 0x19,
+ OpAnd = 0x1A,
+ OpDiv = 0x1B,
+ OpMinus = 0x1C,
+ OpMod = 0x1D,
+ OpMul = 0x1E,
+ OpNeg = 0x1F,
+ OpNot = 0x20,
+ OpOr = 0x21,
+ OpPlus = 0x22,
+ OpPlusUconst = 0x23, // 1 op, ULEB128 addend
+ OpShl = 0x24,
+ OpShr = 0x25,
+ OpShra = 0x26,
+ OpXor = 0x27,
+ OpSkip = 0x2F, // 1 op, signed 2-byte constant
+ OpBra = 0x28, // 1 op, signed 2-byte constant
+ OpEq = 0x29,
+ OpGe = 0x2A,
+ OpGt = 0x2B,
+ OpLe = 0x2C,
+ OpLt = 0x2D,
+ OpNe = 0x2E,
+ OpLit0 = 0x30,
+ // OpLitN = OpLit0 + N for N = 0..31
+ OpReg0 = 0x50,
+ // OpRegN = OpReg0 + N for N = 0..31
+ OpBreg0 = 0x70, // 1 op, signed LEB128 constant
+ // OpBregN = OpBreg0 + N for N = 0..31
+ OpRegx = 0x90, // 1 op, ULEB128 register
+ OpFbreg = 0x91, // 1 op, SLEB128 offset
+ OpBregx = 0x92, // 2 op, ULEB128 reg, SLEB128 off
+ OpPiece = 0x93, // 1 op, ULEB128 size of piece
+ OpDerefSize = 0x94, // 1-byte size of data retrieved
+ OpXderefSize = 0x95, // 1-byte size of data retrieved
+ OpNop = 0x96,
+ // next four new in Dwarf v3
+ OpPushObjAddr = 0x97,
+ OpCall2 = 0x98, // 2-byte offset of DIE
+ OpCall4 = 0x99, // 4-byte offset of DIE
+ OpCallRef = 0x9A, // 4- or 8- byte offset of DIE
+ // 0xE0-0xFF reserved for user-specific
+
diff --git a/src/libmach/dwarfget.c b/src/libmach/dwarfget.c
new file mode 100644
index 00000000..da8558c4
--- /dev/null
+++ b/src/libmach/dwarfget.c
@@ -0,0 +1,217 @@
+/*
+ * Dwarf data format parsing routines.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "elf.h"
+#include "dwarf.h"
+
+ulong
+dwarfget1(DwarfBuf *b)
+{
+ if(b->p==nil || b->p+1 > b->ep){
+ b->p = nil;
+ return 0;
+ }
+ return *b->p++;
+}
+
+int
+dwarfgetn(DwarfBuf *b, uchar *a, int n)
+{
+ if(b->p==nil || b->p+n > b->ep){
+ b->p = nil;
+ memset(a, 0, n);
+ return -1;
+ }
+ memmove(a, b->p, n);
+ b->p += n;
+ return 0;
+}
+
+uchar*
+dwarfgetnref(DwarfBuf *b, ulong n)
+{
+ uchar *p;
+
+ if(b->p==nil || b->p+n > b->ep){
+ b->p = nil;
+ return nil;
+ }
+ p = b->p;
+ b->p += n;
+ return p;
+}
+
+char*
+dwarfgetstring(DwarfBuf *b)
+{
+ char *s;
+
+ if(b->p == nil)
+ return nil;
+ s = (char*)b->p;
+ while(b->p < b->ep && *b->p)
+ b->p++;
+ if(b->p >= b->ep){
+ b->p = nil;
+ return nil;
+ }
+ b->p++;
+ return s;
+}
+
+void
+dwarfskip(DwarfBuf *b, int n)
+{
+ if(b->p==nil || b->p+n > b->ep)
+ b->p = nil;
+ else
+ b->p += n;
+}
+
+ulong
+dwarfget2(DwarfBuf *b)
+{
+ ulong v;
+
+ if(b->p==nil || b->p+2 > b->ep){
+ b->p = nil;
+ return 0;
+ }
+ v = b->d->elf->hdr.e2(b->p);
+ b->p += 2;
+ return v;
+}
+
+ulong
+dwarfget4(DwarfBuf *b)
+{
+ ulong v;
+
+ if(b->p==nil || b->p+4 > b->ep){
+ b->p = nil;
+ return 0;
+ }
+ v = b->d->elf->hdr.e4(b->p);
+ b->p += 4;
+ return v;
+}
+
+uvlong
+dwarfget8(DwarfBuf *b)
+{
+ uvlong v;
+
+ if(b->p==nil || b->p+8 > b->ep){
+ b->p = nil;
+ return 0;
+ }
+ v = b->d->elf->hdr.e8(b->p);
+ b->p += 8;
+ return v;
+}
+
+ulong
+dwarfgetaddr(DwarfBuf *b)
+{
+ static int nbad;
+
+ if(b->addrsize == 0)
+ b->addrsize = b->d->addrsize;
+
+ switch(b->addrsize){
+ case 1:
+ return dwarfget1(b);
+ case 2:
+ return dwarfget2(b);
+ case 4:
+ return dwarfget4(b);
+ case 8:
+ return dwarfget8(b);
+ default:
+ if(++nbad == 1)
+ fprint(2, "dwarf: unexpected address size %lud in dwarfgetaddr\n", b->addrsize);
+ b->p = nil;
+ return 0;
+ }
+}
+
+int n1, n2, n3, n4, n5;
+
+/* An inline function picks off the calls to dwarfget128 for 1-byte encodings,
+ * more than by far the common case (99.999% on most binaries!). */
+ulong
+dwarfget128(DwarfBuf *b)
+{
+ static int nbad;
+ ulong c, d;
+
+ if(b->p == nil)
+ return 0;
+ c = *b->p++;
+ if(!(c&0x80))
+{n1++;
+ return c;
+}
+ d = *b->p++;
+ c |= (d&0x7F)<<7;
+ if(!(d&0x80))
+{n2++;
+ return c;
+}
+ d = *b->p++;
+ c |= (d&0x7F)<<14;
+ if(!(d&0x80))
+{n3++;
+ return c;
+}
+ d = *b->p++;
+ c |= (d&0x7F)<<21;
+ if(!(d&0x80))
+{n4++;
+ return c;
+}
+ d = *b->p++;
+ c |= (d&0x7F)<<28;
+ if(!(d&0x80))
+{n5++;
+ return c;
+}
+ while(b->p<b->ep && *b->p&0x80)
+ b->p++;
+ if(++nbad == 1)
+ fprint(2, "dwarf: overflow during parsing of uleb128 integer\n");
+ return c;
+}
+
+long
+dwarfget128s(DwarfBuf *b)
+{
+ int nb, c;
+ ulong v;
+ static int nbad;
+
+ v = 0;
+ nb = 0;
+ if(b->p==nil)
+ return 0;
+ while(b->p<b->ep){
+ c = *b->p++;
+ v |= (c & 0x7F)<<nb;
+ nb += 7;
+ if(!(c&0x80))
+ break;
+ }
+ if(v&(1<<(nb-1)))
+ v |= ~(((ulong)1<<nb)-1);
+ if(nb > 8*sizeof(ulong)){
+ if(0)
+ if(++nbad == 1)
+ fprint(2, "dwarf: overflow during parsing of sleb128 integer: got %d bits\n", nb);
+ }
+ return v;
+}
+
diff --git a/src/libmach/dwarfinfo.c b/src/libmach/dwarfinfo.c
new file mode 100644
index 00000000..79065bba
--- /dev/null
+++ b/src/libmach/dwarfinfo.c
@@ -0,0 +1,646 @@
+/*
+ * Dwarf info parse and search.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "elf.h"
+#include "dwarf.h"
+
+enum
+{
+ DwarfAttrSibling = 0x01,
+ DwarfAttrLocation = 0x02,
+ DwarfAttrName = 0x03,
+ DwarfAttrOrdering = 0x09,
+ DwarfAttrByteSize = 0x0B,
+ DwarfAttrBitOffset = 0x0C,
+ DwarfAttrBitSize = 0x0D,
+ DwarfAttrStmtList = 0x10,
+ DwarfAttrLowpc = 0x11,
+ DwarfAttrHighpc = 0x12,
+ DwarfAttrLanguage = 0x13,
+ DwarfAttrDiscr = 0x15,
+ DwarfAttrDiscrValue = 0x16,
+ DwarfAttrVisibility = 0x17,
+ DwarfAttrImport = 0x18,
+ DwarfAttrStringLength = 0x19,
+ DwarfAttrCommonRef = 0x1A,
+ DwarfAttrCompDir = 0x1B,
+ DwarfAttrConstValue = 0x1C,
+ DwarfAttrContainingType = 0x1D,
+ DwarfAttrDefaultValue = 0x1E,
+ DwarfAttrInline = 0x20,
+ DwarfAttrIsOptional = 0x21,
+ DwarfAttrLowerBound = 0x22,
+ DwarfAttrProducer = 0x25,
+ DwarfAttrPrototyped = 0x27,
+ DwarfAttrReturnAddr = 0x2A,
+ DwarfAttrStartScope = 0x2C,
+ DwarfAttrStrideSize = 0x2E,
+ DwarfAttrUpperBound = 0x2F,
+ DwarfAttrAbstractOrigin = 0x31,
+ DwarfAttrAccessibility = 0x32,
+ DwarfAttrAddrClass = 0x33,
+ DwarfAttrArtificial = 0x34,
+ DwarfAttrBaseTypes = 0x35,
+ DwarfAttrCalling = 0x36,
+ DwarfAttrCount = 0x37,
+ DwarfAttrDataMemberLoc = 0x38,
+ DwarfAttrDeclColumn = 0x39,
+ DwarfAttrDeclFile = 0x3A,
+ DwarfAttrDeclLine = 0x3B,
+ DwarfAttrDeclaration = 0x3C,
+ DwarfAttrDiscrList = 0x3D,
+ DwarfAttrEncoding = 0x3E,
+ DwarfAttrExternal = 0x3F,
+ DwarfAttrFrameBase = 0x40,
+ DwarfAttrFriend = 0x41,
+ DwarfAttrIdentifierCase = 0x42,
+ DwarfAttrMacroInfo = 0x43,
+ DwarfAttrNamelistItem = 0x44,
+ DwarfAttrPriority = 0x45,
+ DwarfAttrSegment = 0x46,
+ DwarfAttrSpecification = 0x47,
+ DwarfAttrStaticLink = 0x48,
+ DwarfAttrType = 0x49,
+ DwarfAttrUseLocation = 0x4A,
+ DwarfAttrVarParam = 0x4B,
+ DwarfAttrVirtuality = 0x4C,
+ DwarfAttrVtableElemLoc = 0x4D,
+ DwarfAttrAllocated = 0x4E,
+ DwarfAttrAssociated = 0x4F,
+ DwarfAttrDataLocation = 0x50,
+ DwarfAttrStride = 0x51,
+ DwarfAttrEntrypc = 0x52,
+ DwarfAttrUseUTF8 = 0x53,
+ DwarfAttrExtension = 0x54,
+ DwarfAttrRanges = 0x55,
+ DwarfAttrTrampoline = 0x56,
+ DwarfAttrCallColumn = 0x57,
+ DwarfAttrCallFile = 0x58,
+ DwarfAttrCallLine = 0x59,
+ DwarfAttrDescription = 0x5A,
+ DwarfAttrMax,
+
+ FormAddr = 0x01,
+ FormDwarfBlock2 = 0x03,
+ FormDwarfBlock4 = 0x04,
+ FormData2 = 0x05,
+ FormData4 = 0x06,
+ FormData8 = 0x07,
+ FormString = 0x08,
+ FormDwarfBlock = 0x09,
+ FormDwarfBlock1 = 0x0A,
+ FormData1 = 0x0B,
+ FormFlag = 0x0C,
+ FormSdata = 0x0D,
+ FormStrp = 0x0E,
+ FormUdata = 0x0F,
+ FormRefAddr = 0x10,
+ FormRef1 = 0x11,
+ FormRef2 = 0x12,
+ FormRef4 = 0x13,
+ FormRef8 = 0x14,
+ FormRefUdata = 0x15,
+ FormIndirect = 0x16,
+};
+
+static int parseattrs(DwarfBuf*, ulong, DwarfAbbrev*, DwarfAttrs*);
+static int getulong(DwarfBuf*, int, ulong, ulong*, int*);
+static int getuchar(DwarfBuf*, int, uchar*);
+static int getstring(DwarfBuf*, int, char**);
+static int getblock(DwarfBuf*, int, DwarfBlock*);
+static int skipform(DwarfBuf*, int);
+static int constblock(Dwarf*, DwarfBlock*, ulong*);
+
+int
+dwarflookupnameinunit(Dwarf *d, ulong unit, char *name, DwarfSym *s)
+{
+ if(dwarfenumunit(d, unit, s) < 0)
+ return -1;
+
+ dwarfnextsym(d, s, 1); /* s is now the CompileUnit */
+ if(dwarfnextsym(d, s, 1) == 1){ /* s is now the first child of the compile unit */
+ do{
+ if(s->attrs.name && strcmp(s->attrs.name, name) == 0)
+ return 0;
+ }while(dwarfnextsym(d, s, 0) == 1);
+ }
+ werrstr("symbol '%s' not found", name);
+ return -1;
+}
+
+
+int
+dwarflookupsubname(Dwarf *d, DwarfSym *parent, char *name, DwarfSym *s)
+{
+ *s = *parent;
+ dwarfnextsym(d, s, 1);
+ if(s->depth == parent->depth+1)
+ do{
+ if(s->attrs.name && strcmp(s->attrs.name, name) == 0)
+ return 0;
+ }while(dwarfnextsym(d, s, 0) == 1);
+ werrstr("symbol '%s' not found", name);
+ return -1;
+}
+
+int
+dwarflookuptag(Dwarf *d, ulong unit, ulong tag, DwarfSym *s)
+{
+ if(dwarfenumunit(d, unit, s) < 0)
+ return -1;
+
+ dwarfnextsym(d, s, 1); /* s is now the CompileUnit */
+ if(s->attrs.tag == tag)
+ return 0;
+
+ if(dwarfnextsym(d, s, 1) == 1){ /* s is now the first child of the compile unit */
+ do{
+ if(s->attrs.tag == tag)
+ return 0;
+ }while(dwarfnextsym(d, s, 0) == 1);
+ }
+ werrstr("symbol with tag 0x%lux not found", tag);
+ return -1;
+}
+
+int
+dwarfseeksym(Dwarf *d, ulong unit, ulong off, DwarfSym *s)
+{
+ if(dwarfenumunit(d, unit, s) < 0)
+ return -1;
+ s->b.p = d->info.data + unit + off;
+ if(dwarfnextsym(d, s, 1) != 1)
+ return -1;
+ return 0;
+}
+
+int
+dwarflookupfn(Dwarf *d, ulong unit, ulong pc, DwarfSym *s)
+{
+ if(dwarfenumunit(d, unit, s) < 0)
+ return -1;
+
+ if(dwarfnextsym(d, s, 1) != 1)
+ return -1;
+ /* s is now the CompileUnit */
+
+ if(dwarfnextsym(d, s, 1) == 1){ /* s is now the first child of the compile unit */
+ do{
+ if(s->attrs.tag != TagSubprogram)
+ continue;
+ if(s->attrs.lowpc <= pc && pc < s->attrs.highpc)
+ return 0;
+ }while(dwarfnextsym(d, s, 0) == 1);
+ }
+ werrstr("fn containing pc 0x%lux not found", pc);
+ return -1;
+}
+
+int
+dwarfenumunit(Dwarf *d, ulong unit, DwarfSym *s)
+{
+ int i;
+ ulong aoff, len;
+
+ if(unit >= d->info.len){
+ werrstr("dwarf unit address 0x%lux >= 0x%lux out of range", unit, d->info.len);
+ return -1;
+ }
+ memset(s, 0, sizeof *s);
+ memset(&s->b, 0, sizeof s->b);
+ s->b.d = d;
+ s->b.p = d->info.data + unit;
+ s->b.ep = d->info.data + d->info.len;
+ len = dwarfget4(&s->b);
+ s->nextunit = unit + 4 + len;
+
+ if(s->b.ep - s->b.p < len){
+ badheader:
+ werrstr("bad dwarf unit header at unit 0x%lux", unit);
+ return -1;
+ }
+ s->b.ep = s->b.p+len;
+ if((i=dwarfget2(&s->b)) != 2)
+ goto badheader;
+ aoff = dwarfget4(&s->b);
+ s->b.addrsize = dwarfget1(&s->b);
+ if(d->addrsize == 0)
+ d->addrsize = s->b.addrsize;
+ if(s->b.p == nil)
+ goto badheader;
+
+ s->aoff = aoff;
+ s->unit = unit;
+ s->depth = 0;
+ return 0;
+}
+
+int
+dwarfenum(Dwarf *d, DwarfSym *s)
+{
+ if(dwarfenumunit(d, 0, s) < 0)
+ return -1;
+ s->allunits = 1;
+ return 0;
+}
+
+static int
+_dwarfnextsym(Dwarf *d, DwarfSym *s)
+{
+ ulong num;
+ DwarfAbbrev *a;
+
+ if(s->attrs.haskids)
+ s->depth++;
+top:
+ if(s->b.p >= s->b.ep){
+ if(s->allunits && s->nextunit < d->info.len){
+ if(dwarfenumunit(d, s->nextunit, s) < 0)
+ return -1;
+ s->allunits = 1;
+ goto top;
+ }
+ return 0;
+ }
+
+ s->uoff = s->b.p - (d->info.data+s->unit);
+ num = dwarfget128(&s->b);
+ if(num == 0){
+ if(s->depth == 0)
+ return 0;
+ if(s->depth > 0)
+ s->depth--;
+ goto top;
+ }
+
+ a = dwarfgetabbrev(d, s->aoff, num);
+ if(a == nil){
+ fprint(2, "getabbrev %ud: %r\n", num);
+ return -1;
+ }
+ if(parseattrs(&s->b, s->unit, a, &s->attrs) < 0)
+ return -1;
+ return 1;
+}
+
+int
+dwarfnextsym(Dwarf *d, DwarfSym *s, int recurse)
+{
+ int r;
+ int depth;
+ ulong sib;
+
+ if(recurse)
+ return _dwarfnextsym(d, s);
+
+ depth = s->depth;
+ if(s->attrs.have.sibling){
+ sib = s->attrs.sibling;
+ if(sib < d->info.len && d->info.data+sib >= s->b.p)
+ s->b.p = d->info.data+sib;
+ s->attrs.haskids = 0;
+ }
+
+ do{
+ r = _dwarfnextsym(d, s);
+ if(r <= 0)
+ return r;
+ }while(s->depth != depth);
+ if(s->depth < depth)
+ return 0;
+ return 1;
+}
+
+typedef struct Parse Parse;
+struct Parse {
+ int name;
+ int off;
+ int haveoff;
+ int type;
+};
+
+#define OFFSET(x) offsetof(DwarfAttrs, x), offsetof(DwarfAttrs, have.x)
+
+static Parse plist[] = { /* Font Tab 4 */
+ DwarfAttrAbstractOrigin, OFFSET(abstractorigin), TReference,
+ DwarfAttrAccessibility, OFFSET(accessibility), TConstant,
+ DwarfAttrAddrClass, OFFSET(addrclass), TConstant,
+ DwarfAttrArtificial, OFFSET(isartificial), TFlag,
+ DwarfAttrBaseTypes, OFFSET(basetypes), TReference,
+ DwarfAttrBitOffset, OFFSET(bitoffset), TConstant,
+ DwarfAttrBitSize, OFFSET(bitsize), TConstant,
+ DwarfAttrByteSize, OFFSET(bytesize), TConstant,
+ DwarfAttrCalling, OFFSET(calling), TConstant,
+ DwarfAttrCommonRef, OFFSET(commonref), TReference,
+ DwarfAttrCompDir, OFFSET(compdir), TString,
+ DwarfAttrConstValue, OFFSET(constvalue), TString|TConstant|TBlock,
+ DwarfAttrContainingType, OFFSET(containingtype), TReference,
+ DwarfAttrCount, OFFSET(count), TConstant|TReference,
+ DwarfAttrDataMemberLoc, OFFSET(datamemberloc), TBlock|TConstant|TReference,
+ DwarfAttrDeclColumn, OFFSET(declcolumn), TConstant,
+ DwarfAttrDeclFile, OFFSET(declfile), TConstant,
+ DwarfAttrDeclLine, OFFSET(declline), TConstant,
+ DwarfAttrDeclaration, OFFSET(isdeclaration), TFlag,
+ DwarfAttrDefaultValue, OFFSET(defaultvalue), TReference,
+ DwarfAttrDiscr, OFFSET(discr), TReference,
+ DwarfAttrDiscrList, OFFSET(discrlist), TBlock,
+ DwarfAttrDiscrValue, OFFSET(discrvalue), TConstant,
+ DwarfAttrEncoding, OFFSET(encoding), TConstant,
+ DwarfAttrExternal, OFFSET(isexternal), TFlag,
+ DwarfAttrFrameBase, OFFSET(framebase), TBlock|TConstant,
+ DwarfAttrFriend, OFFSET(friend), TReference,
+ DwarfAttrHighpc, OFFSET(highpc), TAddress,
+ DwarfAttrIdentifierCase, OFFSET(identifiercase), TConstant,
+ DwarfAttrImport, OFFSET(import), TReference,
+ DwarfAttrInline, OFFSET(inlined), TConstant,
+ DwarfAttrIsOptional, OFFSET(isoptional), TFlag,
+ DwarfAttrLanguage, OFFSET(language), TConstant,
+ DwarfAttrLocation, OFFSET(location), TBlock|TConstant,
+ DwarfAttrLowerBound, OFFSET(lowerbound), TConstant|TReference,
+ DwarfAttrLowpc, OFFSET(lowpc), TAddress,
+ DwarfAttrMacroInfo, OFFSET(macroinfo), TConstant,
+ DwarfAttrName, OFFSET(name), TString,
+ DwarfAttrNamelistItem, OFFSET(namelistitem), TBlock,
+ DwarfAttrOrdering, OFFSET(ordering), TConstant,
+ DwarfAttrPriority, OFFSET(priority), TReference,
+ DwarfAttrProducer, OFFSET(producer), TString,
+ DwarfAttrPrototyped, OFFSET(isprototyped), TFlag,
+ DwarfAttrRanges, OFFSET(ranges), TReference,
+ DwarfAttrReturnAddr, OFFSET(returnaddr), TBlock|TConstant,
+ DwarfAttrSegment, OFFSET(segment), TBlock|TConstant,
+ DwarfAttrSibling, OFFSET(sibling), TReference,
+ DwarfAttrSpecification, OFFSET(specification), TReference,
+ DwarfAttrStartScope, OFFSET(startscope), TConstant,
+ DwarfAttrStaticLink, OFFSET(staticlink), TBlock|TConstant,
+ DwarfAttrStmtList, OFFSET(stmtlist), TConstant,
+ DwarfAttrStrideSize, OFFSET(stridesize), TConstant,
+ DwarfAttrStringLength, OFFSET(stringlength), TBlock|TConstant,
+ DwarfAttrType, OFFSET(type), TReference,
+ DwarfAttrUpperBound, OFFSET(upperbound), TConstant|TReference,
+ DwarfAttrUseLocation, OFFSET(uselocation), TBlock|TConstant,
+ DwarfAttrVarParam, OFFSET(isvarparam), TFlag,
+ DwarfAttrVirtuality, OFFSET(virtuality), TConstant,
+ DwarfAttrVisibility, OFFSET(visibility), TConstant,
+ DwarfAttrVtableElemLoc, OFFSET(vtableelemloc), TBlock|TReference,
+};
+
+static Parse ptab[DwarfAttrMax];
+
+static int
+parseattrs(DwarfBuf *b, ulong unit, DwarfAbbrev *a, DwarfAttrs *attrs)
+{
+ int i, f, n, got;
+ static int nbad;
+ void *v;
+
+ /* initialize ptab first time through for quick access */
+ if(ptab[DwarfAttrName].name != DwarfAttrName)
+ for(i=0; i<nelem(plist); i++)
+ ptab[plist[i].name] = plist[i];
+
+ memset(attrs, 0, sizeof *attrs);
+ attrs->tag = a->tag;
+ attrs->haskids = a->haskids;
+
+ for(i=0; i<a->nattr; i++){
+ n = a->attr[i].name;
+ f = a->attr[i].form;
+ if(n < 0 || n >= nelem(ptab) || ptab[n].name==0){
+ if(++nbad == 1)
+ fprint(2, "dwarf parse attrs: unexpected attribute name 0x%ux\n", n);
+ return -1;
+ }
+ v = (char*)attrs + ptab[n].off;
+ got = 0;
+ if(f == FormIndirect)
+ f = dwarfget128(b);
+ if((ptab[n].type&(TConstant|TReference|TAddress))
+ && getulong(b, f, unit, v, &got) >= 0)
+ ;
+ else if((ptab[n].type&TFlag) && getuchar(b, f, v) >= 0)
+ got = TFlag;
+ else if((ptab[n].type&TString) && getstring(b, f, v) >= 0)
+ got = TString;
+ else if((ptab[n].type&TBlock) && getblock(b, f, v) >= 0)
+ got = TBlock;
+ else{
+ if(skipform(b, f) < 0){
+ if(++nbad == 1)
+ fprint(2, "dwarf parse attrs: cannot skip form %d\n", f);
+ return -1;
+ }
+ }
+ if(got == TBlock && (ptab[n].type&TConstant))
+ got = constblock(b->d, v, v);
+ *((uchar*)attrs+ptab[n].haveoff) = got;
+ }
+ return 0;
+}
+
+static int
+getulong(DwarfBuf *b, int form, ulong unit, ulong *u, int *type)
+{
+ static int nbad;
+ uvlong uv;
+
+ switch(form){
+ default:
+ return -1;
+
+ /* addresses */
+ case FormAddr:
+ *type = TAddress;
+ *u = dwarfgetaddr(b);
+ return 0;
+
+ /* references */
+ case FormRefAddr:
+ /* absolute ref in .debug_info */
+ *type = TReference;
+ *u = dwarfgetaddr(b);
+ return 0;
+ case FormRef1:
+ *u = dwarfget1(b);
+ goto relativeref;
+ case FormRef2:
+ *u = dwarfget2(b);
+ goto relativeref;
+ case FormRef4:
+ *u = dwarfget4(b);
+ goto relativeref;
+ case FormRef8:
+ *u = dwarfget8(b);
+ goto relativeref;
+ case FormRefUdata:
+ *u = dwarfget128(b);
+ relativeref:
+ *u += unit;
+ *type = TReference;
+ return 0;
+
+ /* constants */
+ case FormData1:
+ *u = dwarfget1(b);
+ goto constant;
+ case FormData2:
+ *u = dwarfget2(b);
+ goto constant;
+ case FormData4:
+ *u = dwarfget4(b);
+ goto constant;
+ case FormData8:
+ uv = dwarfget8(b);
+ *u = uv;
+ if(uv != *u && ++nbad == 1)
+ fprint(2, "dwarf: truncating 64-bit attribute constants\n");
+ goto constant;
+ case FormSdata:
+ *u = dwarfget128s(b);
+ goto constant;
+ case FormUdata:
+ *u = dwarfget128(b);
+ constant:
+ *type = TConstant;
+ return 0;
+ }
+}
+
+static int
+getuchar(DwarfBuf *b, int form, uchar *u)
+{
+ switch(form){
+ default:
+ return -1;
+
+ case FormFlag:
+ *u = dwarfget1(b);
+ return 0;
+ }
+}
+
+static int
+getstring(DwarfBuf *b, int form, char **s)
+{
+ static int nbad;
+ ulong u;
+
+ switch(form){
+ default:
+ return -1;
+
+ case FormString:
+ *s = dwarfgetstring(b);
+ return 0;
+
+ case FormStrp:
+ u = dwarfget4(b);
+ if(u >= b->d->str.len){
+ if(++nbad == 1)
+ fprint(2, "dwarf: bad string pointer 0x%lux in attribute\n", u);
+ /* don't return error - maybe can proceed */
+ *s = nil;
+ }else
+ *s = b->d->str.data + u;
+ return 0;
+
+ }
+}
+
+static int
+getblock(DwarfBuf *b, int form, DwarfBlock *bl)
+{
+ ulong n;
+
+ switch(form){
+ default:
+ return -1;
+ case FormDwarfBlock:
+ n = dwarfget128(b);
+ goto copyn;
+ case FormDwarfBlock1:
+ n = dwarfget1(b);
+ goto copyn;
+ case FormDwarfBlock2:
+ n = dwarfget2(b);
+ goto copyn;
+ case FormDwarfBlock4:
+ n = dwarfget4(b);
+ copyn:
+ bl->data = dwarfgetnref(b, n);
+ bl->len = n;
+ if(bl->data == nil)
+ return -1;
+ return 0;
+ }
+}
+
+static int
+constblock(Dwarf *d, DwarfBlock *bl, ulong *pval)
+{
+ DwarfBuf b;
+
+ memset(&b, 0, sizeof b);
+ b.p = bl->data;
+ b.ep = bl->data+bl->len;
+ b.d = d;
+
+ switch(dwarfget1(&b)){
+ case OpAddr:
+ *pval = dwarfgetaddr(&b);
+ return TConstant;
+ case OpConst1u:
+ *pval = dwarfget1(&b);
+ return TConstant;
+ case OpConst1s:
+ *pval = (schar)dwarfget1(&b);
+ return TConstant;
+ case OpConst2u:
+ *pval = dwarfget2(&b);
+ return TConstant;
+ case OpConst2s:
+ *pval = (s16int)dwarfget2(&b);
+ return TConstant;
+ case OpConst4u:
+ *pval = dwarfget4(&b);
+ return TConstant;
+ case OpConst4s:
+ *pval = (s32int)dwarfget4(&b);
+ return TConstant;
+ case OpConst8u:
+ *pval = (u64int)dwarfget8(&b);
+ return TConstant;
+ case OpConst8s:
+ *pval = (s64int)dwarfget8(&b);
+ return TConstant;
+ case OpConstu:
+ *pval = dwarfget128(&b);
+ return TConstant;
+ case OpConsts:
+ *pval = dwarfget128s(&b);
+ return TConstant;
+ case OpPlusUconst:
+ *pval = dwarfget128(&b);
+ return TConstant;
+ default:
+ return TBlock;
+ }
+}
+
+/* last resort */
+static int
+skipform(DwarfBuf *b, int form)
+{
+ int type;
+ DwarfVal val;
+
+ if(getulong(b, form, 0, &val.c, &type) < 0
+ && getuchar(b, form, (uchar*)&val) < 0
+ && getstring(b, form, &val.s) < 0
+ && getblock(b, form, &val.b) < 0)
+ return -1;
+ return 0;
+}
diff --git a/src/libmach/dwarfopen.c b/src/libmach/dwarfopen.c
new file mode 100644
index 00000000..6122ae68
--- /dev/null
+++ b/src/libmach/dwarfopen.c
@@ -0,0 +1,107 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "elf.h"
+#include "dwarf.h"
+
+static int
+readblock(int fd, DwarfBlock *b, ulong off, ulong len)
+{
+ b->data = malloc(len);
+ if(b->data == nil)
+ return -1;
+ if(seek(fd, off, 0) < 0 || readn(fd, b->data, len) != len){
+ free(b->data);
+ b->data = nil;
+ return -1;
+ }
+ b->len = len;
+ return 0;
+}
+
+static int
+findsection(Elf *elf, char *name, ulong *off, ulong *len)
+{
+ ElfSect *s;
+
+ if((s = elfsection(elf, name)) == nil)
+ return -1;
+ *off = s->offset;
+ *len = s->size;
+ return s - elf->sect;
+}
+
+static int
+loadsection(Elf *elf, char *name, DwarfBlock *b)
+{
+ ulong off, len;
+
+ if(findsection(elf, name, &off, &len) < 0)
+ return -1;
+ return readblock(elf->fd, b, off, len);
+}
+
+Dwarf*
+dwarfopen(Elf *elf)
+{
+ Dwarf *d;
+
+ if(elf == nil){
+ werrstr("nil elf passed to dwarfopen");
+ return nil;
+ }
+
+ d = mallocz(sizeof(Dwarf), 1);
+ if(d == nil)
+ return nil;
+
+ d->elf = elf;
+ if(loadsection(elf, ".debug_abbrev", &d->abbrev) < 0
+ || loadsection(elf, ".debug_aranges", &d->aranges) < 0
+ || loadsection(elf, ".debug_frame", &d->frame) < 0
+ || loadsection(elf, ".debug_line", &d->line) < 0
+ || loadsection(elf, ".debug_pubnames", &d->pubnames) < 0
+ || loadsection(elf, ".debug_ranges", &d->ranges) < 0
+ || loadsection(elf, ".debug_str", &d->str) < 0
+ || loadsection(elf, ".debug_info", &d->info) < 0)
+ goto err;
+
+ /* make this a table once there are more */
+ switch(d->elf->hdr.machine){
+ case ElfMach386:
+ d->reg = dwarf386regs;
+ d->nreg = dwarf386nregs;
+ break;
+ default:
+ werrstr("unsupported machine");
+ goto err;
+ }
+
+ return d;
+
+err:
+ free(d->abbrev.data);
+ free(d->aranges.data);
+ free(d->frame.data);
+ free(d->line.data);
+ free(d->pubnames.data);
+ free(d->ranges.data);
+ free(d->str.data);
+ free(d->info.data);
+ free(d);
+ return nil;
+}
+
+void
+dwarfclose(Dwarf *d)
+{
+ free(d->abbrev.data);
+ free(d->aranges.data);
+ free(d->frame.data);
+ free(d->line.data);
+ free(d->pubnames.data);
+ free(d->ranges.data);
+ free(d->str.data);
+ free(d->info.data);
+ free(d);
+}
diff --git a/src/libmach/dwarfpc.c b/src/libmach/dwarfpc.c
new file mode 100644
index 00000000..165b3aa0
--- /dev/null
+++ b/src/libmach/dwarfpc.c
@@ -0,0 +1,338 @@
+/*
+ * Dwarf pc to source line conversion.
+ *
+ * Maybe should do the reverse here, but what should the interface look like?
+ * One possibility is to use the Plan 9 line2addr interface:
+ *
+ * long line2addr(ulong line, ulong basepc)
+ *
+ * which returns the smallest pc > basepc with line number line (ignoring file name).
+ *
+ * The encoding may be small, but it sure isn't simple!
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "elf.h"
+#include "dwarf.h"
+
+#define trace 0
+
+enum
+{
+ Isstmt = 1<<0,
+ BasicDwarfBlock = 1<<1,
+ EndSequence = 1<<2,
+ PrologueEnd = 1<<3,
+ EpilogueBegin = 1<<4,
+};
+
+typedef struct State State;
+struct State
+{
+ ulong addr;
+ ulong file;
+ ulong line;
+ ulong column;
+ ulong flags;
+ ulong isa;
+};
+
+int
+dwarfpctoline(Dwarf *d, ulong pc, char **cdir, char **dir, char **file, ulong *line, ulong *mtime, ulong *length)
+{
+ uchar *prog, *opcount, *end;
+ ulong off, unit, len, vers, x, start;
+ int i, first, op, a, l, quantum, isstmt, linebase, linerange, opcodebase, nf;
+ char *files, *dirs, *s;
+ DwarfBuf b;
+ DwarfSym sym;
+ State emit, cur, reset;
+ uchar **f, **newf;
+
+ f = nil;
+
+ if(dwarfaddrtounit(d, pc, &unit) < 0
+ || dwarflookuptag(d, unit, TagCompileUnit, &sym) < 0)
+ return -1;
+
+ if(!sym.attrs.have.stmtlist){
+ werrstr("no line mapping information for 0x%lux", pc);
+ return -1;
+ }
+ off = sym.attrs.stmtlist;
+ if(off >= d->line.len){
+ fprint(2, "bad stmtlist\n");
+ goto bad;
+ }
+
+ if(trace) fprint(2, "unit 0x%lux stmtlist 0x%lux\n", unit, sym.attrs.stmtlist);
+
+ memset(&b, 0, sizeof b);
+ b.d = d;
+ b.p = d->line.data + off;
+ b.ep = b.p + d->line.len;
+ b.addrsize = sym.b.addrsize; /* should i get this from somewhere else? */
+
+ len = dwarfget4(&b);
+ if(b.p==nil || b.p+len > b.ep || b.p+len < b.p){
+ fprint(2, "bad len\n");
+ goto bad;
+ }
+
+ b.ep = b.p+len;
+ vers = dwarfget2(&b);
+ if(vers != 2){
+ werrstr("bad dwarf version 0x%lux", vers);
+ return -1;
+ }
+
+ len = dwarfget4(&b);
+ if(b.p==nil || b.p+len > b.ep || b.p+len < b.p){
+ fprint(2, "another bad len\n");
+ goto bad;
+ }
+ prog = b.p+len;
+
+ quantum = dwarfget1(&b);
+ isstmt = dwarfget1(&b);
+ linebase = (schar)dwarfget1(&b);
+ linerange = (schar)dwarfget1(&b);
+ opcodebase = dwarfget1(&b);
+
+ opcount = b.p-1;
+ dwarfgetnref(&b, opcodebase-1);
+ if(b.p == nil){
+ fprint(2, "bad opcode chart\n");
+ goto bad;
+ }
+
+ /* just skip the files and dirs for now; we'll come back */
+ dirs = b.p;
+ while(b.p!=nil && *b.p!=0)
+ dwarfgetstring(&b);
+ dwarfget1(&b);
+
+ files = b.p;
+ while(b.p!=nil && *b.p!=0){
+ dwarfgetstring(&b);
+ dwarfget128(&b);
+ dwarfget128(&b);
+ dwarfget128(&b);
+ }
+ dwarfget1(&b);
+
+ /* move on to the program */
+ if(b.p == nil || b.p > prog){
+ fprint(2, "bad header\n");
+ goto bad;
+ }
+ b.p = prog;
+
+ reset.addr = 0;
+ reset.file = 1;
+ reset.line = 1;
+ reset.column = 0;
+ reset.flags = isstmt ? Isstmt : 0;
+ reset.isa = 0;
+
+ cur = reset;
+ emit = reset;
+ nf = 0;
+ start = 0;
+ if(trace) fprint(2, "program @ %lud ... %.*H opbase = %d\n", b.p - d->line.data, b.ep-b.p, b.p, opcodebase);
+ first = 1;
+ while(b.p != nil){
+ op = dwarfget1(&b);
+ if(trace) fprint(2, "\tline %lud, addr 0x%lux, op %d %.10H", cur.line, cur.addr, op, b.p);
+ if(op >= opcodebase){
+ a = (op - opcodebase) / linerange;
+ l = (op - opcodebase) % linerange + linebase;
+ cur.line += l;
+ cur.addr += a * quantum;
+ if(trace) fprint(2, " +%d,%d\n", a, l);
+ emit:
+ if(first){
+ if(cur.addr > pc){
+ werrstr("found wrong line mapping 0x%lux for pc 0x%lux", cur.addr, pc);
+ goto out;
+ }
+ first = 0;
+ start = cur.addr;
+ }
+ if(cur.addr > pc)
+ break;
+ if(b.p == nil){
+ werrstr("buffer underflow in line mapping");
+ goto out;
+ }
+ emit = cur;
+ if(emit.flags & EndSequence){
+ werrstr("found wrong line mapping 0x%lux-0x%lux for pc 0x%lux", start, cur.addr, pc);
+ goto out;
+ }
+ cur.flags &= ~(BasicDwarfBlock|PrologueEnd|EpilogueBegin);
+ }else{
+ switch(op){
+ case 0: /* extended op code */
+ if(trace) fprint(2, " ext");
+ len = dwarfget128(&b);
+ end = b.p+len;
+ if(b.p == nil || end > b.ep || end < b.p || len < 1)
+ goto bad;
+ switch(dwarfget1(&b)){
+ case 1: /* end sequence */
+ if(trace) fprint(2, " end\n");
+ cur.flags |= EndSequence;
+ goto emit;
+ case 2: /* set address */
+ cur.addr = dwarfgetaddr(&b);
+ if(trace) fprint(2, " set pc 0x%lux\n", cur.addr);
+ break;
+ case 3: /* define file */
+ newf = realloc(f, (nf+1)*sizeof(f[0]));
+ if(newf == nil)
+ goto out;
+ f[nf++] = b.p;
+ s = dwarfgetstring(&b);
+ dwarfget128(&b);
+ dwarfget128(&b);
+ dwarfget128(&b);
+ if(trace) fprint(2, " def file %s\n", s);
+ break;
+ }
+ if(b.p == nil || b.p > end)
+ goto bad;
+ b.p = end;
+ break;
+ case 1: /* emit */
+ if(trace) fprint(2, " emit\n");
+ goto emit;
+ case 2: /* advance pc */
+ a = dwarfget128(&b);
+ if(trace) fprint(2, " advance pc + %lud\n", a*quantum);
+ cur.addr += a * quantum;
+ break;
+ case 3: /* advance line */
+ l = dwarfget128s(&b);
+ if(trace) fprint(2, " advance line + %ld\n", l);
+ cur.line += l;
+ break;
+ case 4: /* set file */
+ if(trace) fprint(2, " set file\n");
+ cur.file = dwarfget128s(&b);
+ break;
+ case 5: /* set column */
+ if(trace) fprint(2, " set column\n");
+ cur.column = dwarfget128(&b);
+ break;
+ case 6: /* negate stmt */
+ if(trace) fprint(2, " negate stmt\n");
+ cur.flags ^= Isstmt;
+ break;
+ case 7: /* set basic block */
+ if(trace) fprint(2, " set basic block\n");
+ cur.flags |= BasicDwarfBlock;
+ break;
+ case 8: /* const add pc */
+ a = (255 - opcodebase) / linerange * quantum;
+ if(trace) fprint(2, " const add pc + %d\n", a);
+ cur.addr += a;
+ break;
+ case 9: /* fixed advance pc */
+ a = dwarfget2(&b);
+ if(trace) fprint(2, " fixed advance pc + %d\n", a);
+ cur.addr += a;
+ break;
+ case 10: /* set prologue end */
+ if(trace) fprint(2, " set prologue end\n");
+ cur.flags |= PrologueEnd;
+ break;
+ case 11: /* set epilogue begin */
+ if(trace) fprint(2, " set epilogue begin\n");
+ cur.flags |= EpilogueBegin;
+ break;
+ case 12: /* set isa */
+ if(trace) fprint(2, " set isa\n");
+ cur.isa = dwarfget128(&b);
+ break;
+ default: /* something new - skip it */
+ if(trace) fprint(2, " unknown %d\n", opcount[op]);
+ for(i=0; i<opcount[op]; i++)
+ dwarfget128(&b);
+ break;
+ }
+ }
+ }
+ if(b.p == nil)
+ goto bad;
+
+ /* finally! the data we seek is in "emit" */
+
+ if(emit.file == 0){
+ werrstr("invalid file index in mapping data");
+ goto out;
+ }
+ if(line)
+ *line = emit.line;
+
+ /* skip over first emit.file-2 guys */
+ b.p = files;
+ for(i=emit.file-1; i > 0 && b.p!=nil && *b.p!=0; i--){
+ dwarfgetstring(&b);
+ dwarfget128(&b);
+ dwarfget128(&b);
+ dwarfget128(&b);
+ }
+ if(b.p == nil){
+ werrstr("problem parsing file data second time (cannot happen)");
+ goto bad;
+ }
+ if(*b.p == 0){
+ if(i >= nf){
+ werrstr("bad file index in mapping data");
+ goto bad;
+ }
+ b.p = f[i];
+ }
+ s = dwarfgetstring(&b);
+ if(file)
+ *file = s;
+ i = dwarfget128(&b); /* directory */
+ x = dwarfget128(&b);
+ if(mtime)
+ *mtime = x;
+ x = dwarfget128(&b);
+ if(length)
+ *length = x;
+
+ /* fetch dir name */
+ if(cdir)
+ *cdir = sym.attrs.compdir;
+
+ if(dir){
+ if(i == 0)
+ *dir = nil;
+ else{
+ b.p = dirs;
+ for(i--; i>0 && b.p!=nil && *b.p!=0; i--)
+ dwarfgetstring(&b);
+ if(b.p==nil || *b.p==0){
+ werrstr("bad directory reference in line mapping");
+ goto out; /* can only happen with bad dir index */
+ }
+ *dir = dwarfgetstring(&b);
+ }
+ }
+
+ /* free at last, free at last */
+ free(f);
+ return 0;
+
+bad:
+ werrstr("corrupted line mapping for 0x%lux", pc);
+out:
+ free(f);
+ return -1;
+}
diff --git a/src/libmach/dwarfpubnames.c b/src/libmach/dwarfpubnames.c
new file mode 100644
index 00000000..b09d2866
--- /dev/null
+++ b/src/libmach/dwarfpubnames.c
@@ -0,0 +1,76 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "elf.h"
+#include "dwarf.h"
+
+static int
+_dwarfnametounit(Dwarf *d, char *name, DwarfBlock *bl, DwarfSym *s)
+{
+ int vers;
+ ulong len, unit, off;
+ uchar *next;
+ char *str;
+ DwarfBuf b;
+
+ b.d = d;
+ b.p = bl->data;
+ b.ep = b.p + bl->len;
+
+ while(b.p < b.ep){
+ len = dwarfget4(&b);
+ if(len > b.ep-b.p){
+ werrstr("bad length in dwarf name header");
+ return -1;
+ }
+ next = b.p + len;
+ vers = dwarfget2(&b);
+ if(vers != 1 && vers != 2){
+ werrstr("bad version %d in dwarf name header", vers);
+ return -1;
+ }
+ unit = dwarfget4(&b);
+ dwarfget4(&b); /* unit length */
+ while(b.p < next){
+ off = dwarfget4(&b);
+ if(off == 0)
+ break;
+ str = dwarfgetstring(&b);
+ if(strcmp(str, name) == 0){
+ if(dwarfenumunit(d, unit, s) < 0)
+ return -1;
+ if(unit + off >= s->b.ep - d->info.data){
+ werrstr("bad offset in name entry");
+ return -1;
+ }
+ s->b.p = d->info.data + unit + off;
+ if(dwarfnextsym(d, s, 1) < 0)
+ return -1;
+ if(s->attrs.name==nil || strcmp(s->attrs.name, name)!=0){
+ werrstr("unexpected name %#q in lookup for %#q", s->attrs.name, name);
+ return -1;
+ }
+ return 0;
+ }
+ }
+ b.p = next;
+ }
+ werrstr("unknown name '%s'", name);
+ return -1;
+}
+
+int
+dwarflookupname(Dwarf *d, char *name, DwarfSym *sym)
+{
+ return _dwarfnametounit(d, name, &d->pubnames, sym);
+}
+
+/*
+
+int
+dwarflookuptype(Dwarf *d, char *name, DwarfSym *sym)
+{
+ return _dwarfnametounit(d, name, &d->pubtypes, sym);
+}
+
+ */
diff --git a/src/libmach/elf.c b/src/libmach/elf.c
new file mode 100644
index 00000000..6a387fd3
--- /dev/null
+++ b/src/libmach/elf.c
@@ -0,0 +1,405 @@
+/*
+ * Parse 32-bit ELF files.
+ * Copyright (c) 2004 Russ Cox. See LICENSE.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "elf.h"
+
+typedef struct ElfHdrBytes ElfHdrBytes;
+typedef struct ElfSectBytes ElfSectBytes;
+typedef struct ElfProgBytes ElfProgBytes;
+typedef struct ElfSymBytes ElfSymBytes;
+
+struct ElfHdrBytes
+{
+ uchar ident[16];
+ uchar type[2];
+ uchar machine[2];
+ uchar version[4];
+ uchar entry[4];
+ uchar phoff[4];
+ uchar shoff[4];
+ uchar flags[4];
+ uchar ehsize[2];
+ uchar phentsize[2];
+ uchar phnum[2];
+ uchar shentsize[2];
+ uchar shnum[2];
+ uchar shstrndx[2];
+};
+
+struct ElfSectBytes
+{
+ uchar name[4];
+ uchar type[4];
+ uchar flags[4];
+ uchar addr[4];
+ uchar offset[4];
+ uchar size[4];
+ uchar link[4];
+ uchar info[4];
+ uchar align[4];
+ uchar entsize[4];
+};
+
+struct ElfSymBytes
+{
+ uchar name[4];
+ uchar value[4];
+ uchar size[4];
+ uchar info; /* top4: bind, bottom4: type */
+ uchar other;
+ uchar shndx[2];
+};
+
+struct ElfProgBytes
+{
+ uchar type[4];
+ uchar offset[4];
+ uchar vaddr[4];
+ uchar paddr[4];
+ uchar filesz[4];
+ uchar memsz[4];
+ uchar flags[4];
+ uchar align[4];
+};
+
+uchar ElfMagic[4] = { 0x7F, 'E', 'L', 'F' };
+
+static void unpackhdr(ElfHdr*, ElfHdrBytes*);
+static void unpackprog(ElfHdr*, ElfProg*, ElfProgBytes*);
+static void unpacksect(ElfHdr*, ElfSect*, ElfSectBytes*);
+
+static char *elftypes[] = {
+ "none",
+ "relocatable",
+ "executable",
+ "shared object",
+ "core",
+};
+
+char*
+elftype(int t)
+{
+ if(t < 0 || t >= nelem(elftypes))
+ return "unknown";
+ return elftypes[t];
+}
+
+static char *elfmachs[] = {
+ "none",
+ "32100",
+ "sparc",
+ "386",
+ "68000",
+ "88000",
+ "486",
+ "860",
+ "MIPS",
+};
+
+char*
+elfmachine(int t)
+{
+ if(t < 0 || t >= nelem(elfmachs))
+ return "unknown";
+ return elfmachs[t];
+}
+
+Elf*
+elfopen(char *name)
+{
+ int fd;
+ Elf *e;
+
+ if((fd = open(name, OREAD)) < 0)
+ return nil;
+ if((e = elfinit(fd)) == nil)
+ close(fd);
+ return e;
+}
+
+Elf*
+elfinit(int fd)
+{
+ int i;
+ Elf *e;
+ ElfHdr *h;
+ ElfHdrBytes hdrb;
+ ElfProgBytes progb;
+ ElfSectBytes sectb;
+ ElfSect *s;
+
+ e = mallocz(sizeof(Elf), 1);
+ if(e == nil)
+ return nil;
+ e->fd = fd;
+
+ /*
+ * parse header
+ */
+ seek(fd, 0, 0);
+ if(readn(fd, &hdrb, sizeof hdrb) != sizeof hdrb)
+ goto err;
+ h = &e->hdr;
+ unpackhdr(h, &hdrb);
+ if(h->class != ElfClass32){
+ werrstr("bad ELF class - not 32-bit");
+ goto err;
+ }
+ if(h->encoding != ElfDataLsb && h->encoding != ElfDataMsb){
+ werrstr("bad ELF encoding - not LSB, MSB");
+ goto err;
+ }
+ if(hdrb.ident[6] != h->version){
+ werrstr("bad ELF encoding - version mismatch %02ux and %08ux",
+ (uint)hdrb.ident[6], (uint)h->version);
+ goto err;
+ }
+
+ /*
+ * the prog+section info is almost always small - just load it into memory.
+ */
+ e->nprog = h->phnum;
+ e->prog = mallocz(sizeof(ElfProg)*e->nprog, 1);
+ for(i=0; i<e->nprog; i++){
+ if(seek(fd, h->phoff+i*h->phentsize, 0) < 0
+ || readn(fd, &progb, sizeof progb) != sizeof progb)
+ goto err;
+ unpackprog(h, &e->prog[i], &progb);
+ }
+
+ e->nsect = h->shnum;
+ if(e->nsect == 0)
+ goto nosects;
+ e->sect = mallocz(sizeof(ElfSect)*e->nsect, 1);
+ for(i=0; i<e->nsect; i++){
+ if(seek(fd, h->shoff+i*h->shentsize, 0) < 0
+ || readn(fd, &sectb, sizeof sectb) != sizeof sectb)
+ goto err;
+ unpacksect(h, &e->sect[i], &sectb);
+ }
+
+ if(h->shstrndx >= e->nsect){
+ fprint(2, "warning: bad string section index %d >= %d", h->shstrndx, e->nsect);
+ h->shnum = 0;
+ e->nsect = 0;
+ goto nosects;
+ }
+ s = &e->sect[h->shstrndx];
+ if(elfmap(e, s) < 0)
+ goto err;
+
+ for(i=0; i<e->nsect; i++)
+ if(e->sect[i].name)
+ e->sect[i].name = s->base + (ulong)e->sect[i].name;
+
+ e->symtab = elfsection(e, ".symtab");
+ if(e->symtab){
+ if(e->symtab->link >= e->nsect)
+ e->symtab = nil;
+ else{
+ e->symstr = &e->sect[e->symtab->link];
+ e->nsymtab = e->symtab->size / sizeof(ElfSymBytes);
+ }
+ }
+ e->dynsym = elfsection(e, ".dynsym");
+ if(e->dynsym){
+ if(e->dynsym->link >= e->nsect)
+ e->dynsym = nil;
+ else{
+ e->dynstr = &e->sect[e->dynsym->link];
+ e->ndynsym = e->dynsym->size / sizeof(ElfSymBytes);
+ }
+ }
+
+ e->bss = elfsection(e, ".bss");
+
+nosects:
+ return e;
+
+err:
+ free(e->sect);
+ free(e->prog);
+ free(e->shstrtab);
+ free(e);
+ return nil;
+}
+
+void
+elfclose(Elf *elf)
+{
+ int i;
+
+ for(i=0; i<elf->nsect; i++)
+ free(elf->sect[i].base);
+ free(elf->sect);
+ free(elf->prog);
+ free(elf->shstrtab);
+ free(elf);
+}
+
+static void
+unpackhdr(ElfHdr *h, ElfHdrBytes *b)
+{
+ u16int (*e2)(uchar*);
+ u32int (*e4)(uchar*);
+ u64int (*e8)(uchar*);
+
+ memmove(h->magic, b->ident, 4);
+ h->class = b->ident[4];
+ h->encoding = b->ident[5];
+ switch(h->encoding){
+ case ElfDataLsb:
+ e2 = leload2;
+ e4 = leload4;
+ e8 = leload8;
+ break;
+ case ElfDataMsb:
+ e2 = beload2;
+ e4 = beload4;
+ e8 = beload8;
+ break;
+ default:
+ return;
+ }
+ h->abi = b->ident[7];
+ h->abiversion = b->ident[8];
+
+ h->e2 = e2;
+ h->e4 = e4;
+ h->e8 = e8;
+
+ h->type = e2(b->type);
+ h->machine = e2(b->machine);
+ h->version = e4(b->version);
+ h->entry = e4(b->entry);
+ h->phoff = e4(b->phoff);
+ h->shoff = e4(b->shoff);
+ h->flags = e4(b->flags);
+ h->ehsize = e2(b->ehsize);
+ h->phentsize = e2(b->phentsize);
+ h->phnum = e2(b->phnum);
+ h->shentsize = e2(b->shentsize);
+ h->shnum = e2(b->shnum);
+ h->shstrndx = e2(b->shstrndx);
+}
+
+static void
+unpackprog(ElfHdr *h, ElfProg *p, ElfProgBytes *b)
+{
+ u32int (*e4)(uchar*);
+
+ e4 = h->e4;
+ p->type = e4(b->type);
+ p->offset = e4(b->offset);
+ p->vaddr = e4(b->vaddr);
+ p->paddr = e4(b->paddr);
+ p->filesz = e4(b->filesz);
+ p->memsz = e4(b->memsz);
+ p->flags = e4(b->flags);
+ p->align = e4(b->align);
+}
+
+static void
+unpacksect(ElfHdr *h, ElfSect *s, ElfSectBytes *b)
+{
+ u32int (*e4)(uchar*);
+
+ e4 = h->e4;
+ s->name = (char*)e4(b->name);
+ s->type = e4(b->type);
+ s->flags = e4(b->flags);
+ s->addr = e4(b->addr);
+ s->offset = e4(b->offset);
+ s->size = e4(b->size);
+ s->link = e4(b->link);
+ s->info = e4(b->info);
+ s->align = e4(b->align);
+ s->entsize = e4(b->entsize);
+}
+
+ElfSect*
+elfsection(Elf *elf, char *name)
+{
+ int i;
+
+ for(i=0; i<elf->nsect; i++){
+ if(elf->sect[i].name == name)
+ return &elf->sect[i];
+ if(elf->sect[i].name && name
+ && strcmp(elf->sect[i].name, name) == 0)
+ return &elf->sect[i];
+ }
+ werrstr("elf section '%s' not found", name);
+ return nil;
+}
+
+int
+elfmap(Elf *elf, ElfSect *sect)
+{
+ if(sect->base)
+ return 0;
+ if((sect->base = malloc(sect->size)) == nil)
+ return -1;
+ werrstr("short read");
+ if(seek(elf->fd, sect->offset, 0) < 0
+ || readn(elf->fd, sect->base, sect->size) != sect->size){
+ free(sect->base);
+ sect->base = nil;
+ return -1;
+ }
+ return 0;
+}
+
+int
+elfsym(Elf *elf, int i, ElfSym *sym)
+{
+ ElfSect *symtab, *strtab;
+ uchar *p;
+ char *s;
+ ulong x;
+
+ if(i < 0){
+ werrstr("bad index %d in elfsym", i);
+ return -1;
+ }
+
+ if(i < elf->nsymtab){
+ symtab = elf->symtab;
+ strtab = elf->symstr;
+ extract:
+ if(elfmap(elf, symtab) < 0 || elfmap(elf, strtab) < 0)
+ return -1;
+ p = symtab->base + i * sizeof(ElfSymBytes);
+ s = strtab->base;
+ x = elf->hdr.e4(p);
+ if(x >= strtab->size){
+ werrstr("bad symbol name offset 0x%lux", x);
+ return -1;
+ }
+ sym->name = s + x;
+ sym->value = elf->hdr.e4(p+4);
+ sym->size = elf->hdr.e4(p+8);
+ x = p[12];
+ sym->bind = x>>4;
+ sym->type = x & 0xF;
+ sym->other = p[13];
+ sym->shndx = elf->hdr.e2(p+14);
+ return 0;
+ }
+ i -= elf->nsymtab;
+ if(i < elf->ndynsym){
+ symtab = elf->dynsym;
+ strtab = elf->dynstr;
+ goto extract;
+ }
+ /* i -= elf->ndynsym */
+
+ werrstr("symbol index out of range");
+ return -1;
+}
+
diff --git a/src/libmach/elf.h b/src/libmach/elf.h
new file mode 100644
index 00000000..6ed239bc
--- /dev/null
+++ b/src/libmach/elf.h
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2004 Russ Cox. See LICENSE.
+ */
+
+/* /home/rsc/papers/elfXXelf.pdf */
+
+typedef struct Elf Elf;
+typedef struct ElfHdr ElfHdr;
+typedef struct ElfSect ElfSect;
+typedef struct ElfProg ElfProg;
+typedef struct ElfNote ElfNote;
+typedef struct ElfSym ElfSym;
+
+enum
+{
+ ElfClassNone = 0,
+ ElfClass32,
+ ElfClass64,
+
+ ElfDataNone = 0,
+ ElfDataLsb,
+ ElfDataMsb,
+
+ ElfTypeNone = 0,
+ ElfTypeRelocatable,
+ ElfTypeExecutable,
+ ElfTypeSharedObject,
+ ElfTypeCore,
+ /* 0xFF00 - 0xFFFF reserved for processor-specific types */
+
+ ElfMachNone = 0,
+ ElfMach32100, /* AT&T WE 32100 */
+ ElfMachSparc, /* SPARC */
+ ElfMach386, /* Intel 80386 */
+ ElfMach68000, /* Motorola 68000 */
+ ElfMach88000, /* Motorola 88000 */
+ ElfMach486, /* Intel 80486, no longer used */
+ ElfMach860, /* Intel 80860 */
+ ElfMachMips, /* MIPS RS3000 */
+ ElfMachS370, /* IBM System/370 */
+ ElfMachMipsLe, /* MIPS RS3000 LE */
+ ElfMachParisc = 15, /* HP PA RISC */
+ ElfMachVpp500 = 17, /* Fujitsu VPP500 */
+ ElfMachSparc32Plus, /* SPARC V8+ */
+ ElfMach960, /* Intel 80960 */
+ ElfMachPower, /* PowerPC */
+ ElfMachPower64, /* PowerPC 64 */
+ ElfMachS390, /* IBM System/390 */
+ ElfMachV800 = 36, /* NEC V800 */
+ ElfMachFr20, /* Fujitsu FR20 */
+ ElfMachRh32, /* TRW RH-32 */
+ ElfMachRce, /* Motorola RCE */
+ ElfMachArm, /* ARM */
+ ElfMachAlpha, /* Digital Alpha */
+ ElfMachSH, /* Hitachi SH */
+ ElfMachSparc9, /* SPARC V9 */
+ /* and the list goes on... */
+
+ ElfAbiNone = 0,
+ ElfAbiSystemV = 0, /* [sic] */
+ ElfAbiHPUX,
+ ElfAbiNetBSD,
+ ElfAbiLinux,
+ ElfAbiSolaris = 6,
+ ElfAbiAix,
+ ElfAbiIrix,
+ ElfAbiFreeBSD,
+ ElfAbiTru64,
+ ElfAbiModesto,
+ ElfAbiOpenBSD,
+ ElfAbiARM = 97,
+ ElfAbiEmbedded = 255,
+
+ /* some of sections 0xFF00 - 0xFFFF reserved for various things */
+ ElfSectNone = 0,
+ ElfSectProgbits,
+ ElfSectSymtab,
+ ElfSectStrtab,
+ ElfSectRela,
+ ElfSectHash,
+ ElfSectDynamic,
+ ElfSectNote,
+ ElfSectNobits,
+ ElfSectRel,
+ ElfSectShlib,
+ ElfSectDynsym,
+
+ ElfSectFlagWrite = 0x1,
+ ElfSectFlagAlloc = 0x2,
+ ElfSectFlagExec = 0x4,
+ /* 0xF0000000 are reserved for processor specific */
+
+ ElfSymBindLocal = 0,
+ ElfSymBindGlobal,
+ ElfSymBindWeak,
+ /* 13-15 reserved */
+
+ ElfSymTypeNone = 0,
+ ElfSymTypeObject,
+ ElfSymTypeFunc,
+ ElfSymTypeSection,
+ ElfSymTypeFile,
+ /* 13-15 reserved */
+
+ ElfSymShnNone = 0,
+ ElfSymShnAbs = 0xFFF1,
+ ElfSymShnCommon = 0xFFF2,
+ /* 0xFF00-0xFF1F reserved for processors */
+ /* 0xFF20-0xFF3F reserved for operating systems */
+
+ ElfProgNone = 0,
+ ElfProgLoad,
+ ElfProgDynamic,
+ ElfProgInterp,
+ ElfProgNote,
+ ElfProgShlib,
+ ElfProgPhdr,
+
+ ElfProgFlagExec = 0x1,
+ ElfProgFlagWrite = 0x2,
+ ElfProgFlagRead = 0x4,
+
+ ElfNotePrStatus = 1,
+ ElfNotePrFpreg = 2,
+ ElfNotePrPsinfo = 3,
+ ElfNotePrTaskstruct = 4,
+ ElfNotePrAuxv = 6,
+ ElfNotePrXfpreg = 0x46e62b7f, /* for gdb/386 */
+};
+
+struct ElfHdr
+{
+ uchar magic[4];
+ uchar class;
+ uchar encoding;
+ uchar version;
+ uchar abi;
+ uchar abiversion;
+ u32int type;
+ u32int machine;
+ u32int entry;
+ u32int phoff;
+ u32int shoff;
+ u32int flags;
+ u32int ehsize;
+ u32int phentsize;
+ u32int phnum;
+ u32int shentsize;
+ u32int shnum;
+ u32int shstrndx;
+ u16int (*e2)(uchar*);
+ u32int (*e4)(uchar*);
+ u64int (*e8)(uchar*);
+};
+
+struct ElfSect
+{
+ char *name;
+ u32int type;
+ u32int flags;
+ u32int addr;
+ u32int offset;
+ u32int size;
+ u32int link;
+ u32int info;
+ u32int align;
+ u32int entsize;
+ uchar *base;
+};
+
+struct ElfProg
+{
+ u32int type;
+ u32int offset;
+ u32int vaddr;
+ u32int paddr;
+ u32int filesz;
+ u32int memsz;
+ u32int flags;
+ u32int align;
+};
+
+struct ElfNote
+{
+ u32int namesz;
+ u32int descsz;
+ u32int type;
+ char *name;
+ uchar *desc;
+ u32int offset; /* in-memory only */
+};
+
+struct ElfSym
+{
+ char* name;
+ u32int value;
+ u32int size;
+ uchar bind;
+ uchar type;
+ uchar other;
+ u16int shndx;
+};
+
+struct Elf
+{
+ int fd;
+ ElfHdr hdr;
+ ElfSect *sect;
+ uint nsect;
+ ElfProg *prog;
+ uint nprog;
+ char *shstrtab;
+
+ int nsymtab;
+ ElfSect *symtab;
+ ElfSect *symstr;
+ int ndynsym;
+ ElfSect *dynsym;
+ ElfSect *dynstr;
+ ElfSect *bss;
+
+ int (*coreregs)(Elf*, ElfNote*, uchar**);
+};
+
+Elf* elfopen(char*);
+Elf* elfinit(int);
+ElfSect *elfsection(Elf*, char*);
+void elfclose(Elf*);
+int elfsym(Elf*, int, ElfSym*);
+int elfmap(Elf*, ElfSect*);
+
+int coreregslinux386(Elf*, ElfNote*, uchar**);
+int coreregsfreebsd386(Elf*, ElfNote*, uchar**);
diff --git a/src/libmach/elfcore.h b/src/libmach/elfcore.h
new file mode 100644
index 00000000..f1e953fb
--- /dev/null
+++ b/src/libmach/elfcore.h
@@ -0,0 +1,142 @@
+/* Copyright (c) 2002, 2003 William Josephson */
+
+enum {
+ CoremapMagic = 0xba5eba11,
+ CoremapMax = 128,
+};
+#undef MAXCOMLEN
+#define MAXCOMLEN 16
+#define PRSTATUS_VERSION 1 /* Current version of prstatus_t */
+#define PRPSINFO_VERSION 1 /* Current version of prpsinfo_t */
+#define PRARGSZ 80 /* Maximum argument bytes saved */
+
+
+typedef struct Coremap Coremap;
+typedef struct CoremapItem CoremapItem;
+typedef struct CoremapHeader CoremapHeader;
+typedef struct ElfNote ElfNote;
+typedef struct Reg386 Reg386;
+typedef struct PrStatus386 PrStatus386;
+typedef struct PrPsinfo PrPsinfo;
+
+struct CoremapHeader {
+ u32int magic;
+ u32int counter;
+ u32int maxelem;
+};
+
+struct CoremapItem {
+ u32int address;
+ u32int size;
+};
+
+struct Coremap {
+ CoremapHeader header;
+ CoremapItem map[CoremapMax];
+};
+
+struct ElfNote {
+ u32int namesz;
+ u32int descsz;
+ u32int type;
+ char *name;
+ uchar *desc;
+ u32int offset; /* in-memory only */
+};
+
+enum
+{
+ NotePrStatus = 1,
+ NotePrFpreg = 2,
+ NotePrPsinfo = 3,
+ NotePrTaskstruct = 4,
+ NotePrAuxv = 6,
+ NotePrXfpreg = 0x46e62b7f, /* according to gdb */
+};
+#if 0
+struct Reg386
+{
+ u32int fs;
+ u32int es;
+ u32int ds;
+ u32int edi;
+ u32int esi;
+ u32int ebp;
+ u32int isp;
+ u32int ebx;
+ u32int edx;
+ u32int ecx;
+ u32int eax;
+ u32int trapno;
+ u32int err;
+ u32int eip;
+ u32int cs;
+ u32int eflags;
+ u32int esp;
+ u32int ss;
+ u32int gs;
+};
+#endif
+
+struct Reg386
+{
+ 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;
+};
+
+#if 0
+struct PrStatus386
+{
+ 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) */
+ int osreldate; /* Kernel version (1) */
+ int cursig; /* Current signal (1) */
+ pid_t pid; /* Process ID (1) */
+ Reg386 reg; /* General purpose registers (1) */
+};
+#endif
+
+struct PrPsinfo
+{
+ int version; /* Version number of struct (1) */
+ u32int psinfosz; /* sizeof(prpsinfo_t) (1) */
+ char fname[MAXCOMLEN+1]; /* Command name, null terminated (1) */
+ char psargs[PRARGSZ+1]; /* Arguments, null terminated (1) */
+};
+
+struct PrStatus386
+{
+ u32int signo;
+ u32int code;
+ u32int errno;
+ u32int cursig;
+ u32int sigpend;
+ u32int sighold;
+ u32int pid;
+ u32int ppid;
+ u32int pgrp;
+ u32int sid;
+ u32int utime[2];
+ u32int stime[2];
+ u32int cutime[2];
+ u32int cstime[2];
+ Reg386 reg;
+ u32int fpvalid;
+};
diff --git a/src/libmach/elfcorefreebsd386.c b/src/libmach/elfcorefreebsd386.c
new file mode 100644
index 00000000..2ff746de
--- /dev/null
+++ b/src/libmach/elfcorefreebsd386.c
@@ -0,0 +1,89 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "elf.h"
+#include "ureg386.h"
+
+typedef struct Lreg Lreg;
+typedef struct Status Status;
+
+struct Lreg
+{
+ u32int fs;
+ u32int es;
+ u32int ds;
+ u32int edi;
+ u32int esi;
+ u32int ebp;
+ u32int isp;
+ u32int ebx;
+ u32int edx;
+ u32int ecx;
+ u32int eax;
+ u32int trapno;
+ u32int err;
+ u32int eip;
+ u32int cs;
+ u32int eflags;
+ u32int esp;
+ u32int ss;
+ u32int gs;
+};
+
+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) */
+};
+
+int
+coreregsfreebsd386(Elf *elf, ElfNote *note, uchar **up)
+{
+ Status *s;
+ Lreg *l;
+ Ureg *u;
+
+ if(note->descsz < sizeof(Status)){
+ werrstr("elf status note too small");
+ return -1;
+ }
+ s = (Status*)note->desc;
+ if(s->version != 1){
+ werrstr("unknown status version %ud", (uint)s->version);
+ return -1;
+ }
+ l = &s->reg;
+ u = malloc(sizeof(Ureg));
+ if(u == 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 = l->trapno;
+ u->ecode = 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);
+}
+
diff --git a/src/libmach/elfcorelinux386.c b/src/libmach/elfcorelinux386.c
new file mode 100644
index 00000000..f6a0234c
--- /dev/null
+++ b/src/libmach/elfcorelinux386.c
@@ -0,0 +1,95 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "elf.h"
+#include "ureg386.h"
+
+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;
+};
+
+/*
+ * Lreg is 64-bit aligned within status, so we shouldn't
+ * have any packing problems.
+ */
+struct Status
+{
+ u32int signo;
+ u32int code;
+ u32int errno;
+ u32int cursig;
+ u32int sigpend;
+ u32int sighold;
+ u32int pid;
+ u32int ppid;
+ u32int pgrp;
+ u32int sid;
+ u32int utime[2];
+ u32int stime[2];
+ u32int cutime[2];
+ u32int cstime[2];
+ Lreg reg;
+ u32int fpvalid;
+};
+
+int
+coreregslinux386(Elf *elf, ElfNote *note, uchar **up)
+{
+ Status *s;
+ Lreg *l;
+ Ureg *u;
+
+ if(note->descsz < sizeof(Status)){
+ werrstr("elf status note too small");
+ return -1;
+ }
+ s = (Status*)note->desc;
+ l = &s->reg;
+ u = malloc(sizeof(Ureg));
+ if(u == 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);
+}
+
diff --git a/src/libmach/elfdump.c b/src/libmach/elfdump.c
new file mode 100644
index 00000000..e96f818d
--- /dev/null
+++ b/src/libmach/elfdump.c
@@ -0,0 +1,133 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+#include "elf.h"
+#include "stabs.h"
+
+void
+usage(void)
+{
+ fprint(2, "usage: elf file list\n");
+ fprint(2, " elf file syms\n");
+ fprint(2, " elf file prog n\n");
+ fprint(2, " elf file sect n\n");
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ int i, n, nn;
+ char buf[512];
+ ulong off, len;
+ Elf *elf;
+ ElfProg *p;
+ ElfSect *s;
+
+ ARGBEGIN{
+ default:
+ usage();
+ }ARGEND
+
+ if(argc < 2)
+ usage();
+
+ if((elf = elfopen(argv[0])) == nil)
+ sysfatal("elfopen %s: %r", argv[0]);
+
+ if(strcmp(argv[1], "syms") == 0){
+ ElfSym sym;
+ for(i=0; elfsym(elf, i, &sym) >= 0; i++){
+ print("%s 0x%lux +%lud bind %d type %d other %d shndx 0x%ux\n",
+ sym.name, (ulong)sym.value, (ulong)sym.size,
+ sym.bind, sym.type, sym.other, (uint)sym.shndx);
+ }
+ }
+ else if(strcmp(argv[1], "stabs") == 0){
+ ElfSect *s1, *s2;
+ Stab stabs;
+ StabSym sym;
+
+ if((s1 = elfsection(elf, ".stab")) == nil)
+ sysfatal("no stabs");
+ if(s1->link==0 || s1->link >= elf->nsect)
+ sysfatal("bad stabstr %d", s1->link);
+ s2 = &elf->sect[s1->link];
+ if(elfmap(elf, s1) < 0 || elfmap(elf, s2) < 0)
+ sysfatal("elfmap");
+ stabs.stabbase = s1->base;
+ stabs.stabsize = s1->size;
+ stabs.strbase = s2->base;
+ stabs.strsize = s2->size;
+ stabs.e2 = elf->hdr.e2;
+ stabs.e4 = elf->hdr.e4;
+ print("%ud %ud\n", stabs.stabsize, stabs.strsize);
+ for(i=0; stabsym(&stabs, i, &sym) >= 0; i++)
+ print("%s type 0x%x other %d desc %d value 0x%lux\n",
+ sym.name, sym.type, sym.other, (int)sym.desc, (ulong)sym.value);
+ fprint(2, "err at %d: %r\n", i);
+ }
+ else if(strcmp(argv[1], "list") == 0){
+ if(argc != 2)
+ usage();
+ print("elf %s %s v%d entry 0x%08lux phoff 0x%lux shoff 0x%lux flags 0x%lux\n",
+ elftype(elf->hdr.type), elfmachine(elf->hdr.machine),
+ elf->hdr.version, elf->hdr.entry, elf->hdr.phoff, elf->hdr.shoff,
+ elf->hdr.flags);
+ print("\tehsize %d phentsize %d phnum %d shentsize %d shnum %d shstrndx %d\n",
+ elf->hdr.ehsize, elf->hdr.phentsize, elf->hdr.phnum, elf->hdr.shentsize,
+ elf->hdr.shnum, elf->hdr.shstrndx);
+ for(i=0; i<elf->nprog; i++){
+ p = &elf->prog[i];
+ print("prog %d type %d offset 0x%08lux vaddr 0x%08lux paddr 0x%08lux filesz 0x%08lux memsz 0x%08lux flags 0x%08lux align 0x%08lux\n",
+ i, p->type, p->offset, p->vaddr, p->paddr,
+ p->filesz, p->memsz, p->flags, p->align);
+ }
+ for(i=0; i<elf->nsect; i++){
+ s = &elf->sect[i];
+ print("sect %d %s type %d flags 0x%lux addr 0x%08lux offset 0x%08lux size 0x%08lux link 0x%lux info 0x%lux align 0x%lux entsize 0x%lux\n",
+ i, s->name, s->type, s->flags, s->addr, s->offset, s->size, s->link, s->info,
+ s->align, s->entsize);
+ }
+ }
+ else if(strcmp(argv[1], "prog") == 0){
+ if(argc != 3)
+ usage();
+ i = atoi(argv[2]);
+ if(i < 0 || i >= elf->nprog)
+ sysfatal("bad prog number");
+ off = elf->prog[i].offset;
+ len = elf->prog[i].filesz;
+ fprint(2, "prog %d offset 0x%lux size 0x%lux\n", i, off, len);
+ copy:
+ seek(elf->fd, off, 0);
+ for(n=0; n<len; n+=nn){
+ nn = sizeof buf;
+ if(nn > len-n)
+ nn = len-n;
+ nn = read(elf->fd, buf, nn);
+ if(nn == 0)
+ break;
+ if(nn < 0)
+ sysfatal("read error");
+ write(1, buf, nn);
+ }
+ if(n < len)
+ fprint(2, "early eof\n");
+ }
+ else if(strcmp(argv[1], "sect") == 0){
+ if(argc != 3)
+ usage();
+ i = atoi(argv[2]);
+ if(i < 0 || i >= elf->nsect)
+ sysfatal("bad section number");
+ off = elf->sect[i].offset;
+ len = elf->sect[i].size;
+ fprint(2, "section %d offset 0x%lux size 0x%lux\n", i, off, len);
+ goto copy;
+ }
+ else
+ usage();
+ exits(0);
+}
diff --git a/src/libmach/elfdynsym.c b/src/libmach/elfdynsym.c
new file mode 100644
index 00000000..06bb41a6
--- /dev/null
+++ b/src/libmach/elfdynsym.c
@@ -0,0 +1,6 @@
+#include <u.h>
+#include <libc.h>
+#include "elf.h"
+
+int
+elfsym(Elf *elf, ElfSect *symtab, ElfSect *strtab, int ndx, ElfSym * \ No newline at end of file
diff --git a/src/libmach/fpformat.c b/src/libmach/fpformat.c
new file mode 100644
index 00000000..a94a096a
--- /dev/null
+++ b/src/libmach/fpformat.c
@@ -0,0 +1,65 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+/*
+ * Format floating point registers
+ *
+ * Register codes in format field:
+ * 'X' - print as 32-bit hexadecimal value
+ * 'F' - 64-bit double register when modif == 'F'; else 32-bit single reg
+ * 'f' - 32-bit ieee float
+ * '8' - big endian 80-bit ieee extended float
+ * '3' - little endian 80-bit ieee extended float with hole in bytes 8&9
+ */
+int
+fpformat(Map *map, Regdesc *rp, char *buf, uint n, uint modif)
+{
+ char reg[12];
+ u32int r;
+
+ switch(rp->format)
+ {
+ case 'X':
+ if (get4(map, rp->offset, &r) < 0)
+ return -1;
+ snprint(buf, n, "%lux", r);
+ break;
+ case 'F': /* first reg of double reg pair */
+ if (modif == 'F')
+ if ((rp->format=='F') || (((rp+1)->flags&RFLT) && (rp+1)->format == 'f')) {
+ if (get1(map, rp->offset, (uchar *)reg, 8) < 0)
+ return -1;
+ mach->ftoa64(buf, n, reg);
+ if (rp->format == 'F')
+ return 1;
+ return 2;
+ }
+ /* treat it like 'f' */
+ if (get1(map, rp->offset, (uchar *)reg, 4) < 0)
+ return -1;
+ mach->ftoa32(buf, n, reg);
+ break;
+ case 'f': /* 32 bit float */
+ if (get1(map, rp->offset, (uchar *)reg, 4) < 0)
+ return -1;
+ mach->ftoa32(buf, n, reg);
+ break;
+ case '3': /* little endian ieee 80 with hole in bytes 8&9 */
+ if (get1(map, rp->offset, (uchar *)reg, 10) < 0)
+ return -1;
+ memmove(reg+10, reg+8, 2); /* open hole */
+ memset(reg+8, 0, 2); /* fill it */
+ leieeeftoa80(buf, n, reg);
+ break;
+ case '8': /* big-endian ieee 80 */
+ if (get1(map, rp->offset, (uchar *)reg, 10) < 0)
+ return -1;
+ beieeeftoa80(buf, n, reg);
+ break;
+ default: /* unknown */
+ break;
+ }
+ return 1;
+}
diff --git a/src/libmach/frame.c b/src/libmach/frame.c
new file mode 100644
index 00000000..6e448519
--- /dev/null
+++ b/src/libmach/frame.c
@@ -0,0 +1,130 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+typedef struct LocRegs LocRegs;
+struct LocRegs
+{
+ Regs r;
+ Regs *oldregs;
+ Map *map;
+ ulong *val;
+};
+
+static int
+locregrw(Regs *regs, char *name, ulong *val, int isr)
+{
+ int i;
+ LocRegs *lr;
+
+ lr = (LocRegs*)regs;
+ i = windindex(name);
+ if(i == -1)
+ return lr->oldregs->rw(lr->oldregs, name, val, isr);
+ if(isr){
+ *val = lr->val[i];
+ return 0;
+ }else{
+ werrstr("saved registers are immutable");
+ return -1;
+ }
+}
+
+int
+stacktrace(Map *map, Regs *regs, Tracer trace)
+{
+ char *rname;
+ int i, ipc, ret;
+ ulong nextpc, pc, v;
+ ulong *cur, *next;
+ LocRegs lr;
+ Symbol s, *sp;
+
+ /*
+ * Allocate location arrays.
+ */
+ ret = -1;
+ cur = malloc(mach->nwindreg*sizeof(cur[0]));
+ next = malloc(mach->nwindreg*sizeof(cur[0]));
+ if(cur==nil || next==nil)
+ goto out;
+
+ /*
+ * Initialize current registers using regs.
+ */
+ if(rget(regs, mach->pc, &pc) < 0){
+ werrstr("cannot fetch initial pc: %r");
+ goto out;
+ }
+
+ for(i=0; i<mach->nwindreg; i++){
+ rname = mach->windreg[i];
+ if(rget(regs, rname, &v) < 0)
+ v = ~(ulong)0;
+ cur[i] = v;
+ }
+
+ ipc = windindex(mach->pc);
+ ret = 0;
+
+ /* set up cur[i]==next[i] for unwindframe */
+ memmove(next, cur, mach->nwindreg*sizeof(next[0]));
+ for(;;){
+ sp = &s;
+ if(findsym(locaddr(pc), CTEXT, &s) < 0)
+ sp = nil;
+
+ lr.r.rw = locregrw;
+ lr.oldregs = regs;
+ lr.val = cur;
+ lr.map = map;
+ if((i = unwindframe(map, &lr.r, next)) >= 0)
+ nextpc = next[ipc];
+ else
+ nextpc = ~(ulong)0;
+ if((*trace)(map, &lr.r, pc, nextpc, sp, ++ret) <= 0)
+ break;
+ if(i < 0)
+ break;
+ if(sp && strcmp(sp->name, "main") == 0)
+ break;
+ pc = nextpc;
+ memmove(cur, next, mach->nwindreg*sizeof(cur[0]));
+ }
+
+out:
+ free(cur);
+ free(next);
+ return ret;
+}
+
+int
+windindex(char *reg)
+{
+ char **p;
+ int i;
+
+ p = mach->windreg;
+ for(i=0; i<mach->nwindreg; i++)
+ if(strcmp(p[i], reg) == 0)
+ return i;
+ werrstr("%s is not a winding register", reg);
+ return -1;
+}
+
+Loc*
+windreglocs(void)
+{
+ int i;
+ Loc *loc;
+
+ loc = malloc(mach->nwindreg*sizeof(loc[0]));
+ if(loc == nil)
+ return nil;
+ for(i=0; i<mach->nwindreg; i++){
+ loc[i].type = LREG;
+ loc[i].reg = mach->windreg[i];
+ }
+ return loc;
+}
diff --git a/src/libmach/hexify.c b/src/libmach/hexify.c
new file mode 100644
index 00000000..5f3ed50e
--- /dev/null
+++ b/src/libmach/hexify.c
@@ -0,0 +1,20 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+char *
+_hexify(char *buf, ulong p, int zeros)
+{
+ ulong d;
+
+ d = p/16;
+ if(d)
+ buf = _hexify(buf, d, zeros-1);
+ else
+ while(zeros--)
+ *buf++ = '0';
+ *buf++ = "0123456789abcdef"[p&0x0f];
+ return buf;
+}
+
diff --git a/src/libmach/ieee.c b/src/libmach/ieee.c
new file mode 100644
index 00000000..0d756d21
--- /dev/null
+++ b/src/libmach/ieee.c
@@ -0,0 +1,169 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+/*
+ * These routines assume that if the number is representable
+ * in IEEE floating point, it will be representable in the native
+ * double format. Naive but workable, probably.
+ */
+int
+ieeeftoa64(char *buf, uint n, u32int h, u32int l)
+{
+ double fr;
+ int exp;
+
+ if (n <= 0)
+ return 0;
+
+
+ if(h & (1L<<31)){
+ *buf++ = '-';
+ h &= ~(1L<<31);
+ }else
+ *buf++ = ' ';
+ n--;
+ if(l == 0 && h == 0)
+ return snprint(buf, n, "0.");
+ exp = (h>>20) & ((1L<<11)-1L);
+ if(exp == 0)
+ return snprint(buf, n, "DeN(%.8lux%.8lux)", h, l);
+ if(exp == ((1L<<11)-1L)){
+ if(l==0 && (h&((1L<<20)-1L)) == 0)
+ return snprint(buf, n, "Inf");
+ else
+ return snprint(buf, n, "NaN(%.8lux%.8lux)", h&((1L<<20)-1L), l);
+ }
+ exp -= (1L<<10) - 2L;
+ fr = l & ((1L<<16)-1L);
+ fr /= 1L<<16;
+ fr += (l>>16) & ((1L<<16)-1L);
+ fr /= 1L<<16;
+ fr += (h & (1L<<20)-1L) | (1L<<20);
+ fr /= 1L<<21;
+ fr = ldexp(fr, exp);
+ return snprint(buf, n, "%.18g", fr);
+}
+
+int
+ieeeftoa32(char *buf, uint n, u32int h)
+{
+ double fr;
+ int exp;
+
+ if (n <= 0)
+ return 0;
+
+ if(h & (1L<<31)){
+ *buf++ = '-';
+ h &= ~(1L<<31);
+ }else
+ *buf++ = ' ';
+ n--;
+ if(h == 0)
+ return snprint(buf, n, "0.");
+ exp = (h>>23) & ((1L<<8)-1L);
+ if(exp == 0)
+ return snprint(buf, n, "DeN(%.8lux)", h);
+ if(exp == ((1L<<8)-1L)){
+ if((h&((1L<<23)-1L)) == 0)
+ return snprint(buf, n, "Inf");
+ else
+ return snprint(buf, n, "NaN(%.8lux)", h&((1L<<23)-1L));
+ }
+ exp -= (1L<<7) - 2L;
+ fr = (h & ((1L<<23)-1L)) | (1L<<23);
+ fr /= 1L<<24;
+ fr = ldexp(fr, exp);
+ return snprint(buf, n, "%.9g", fr);
+}
+
+int
+beieeeftoa32(char *buf, uint n, void *s)
+{
+ return ieeeftoa32(buf, n, beswap4(*(u32int*)s));
+}
+
+int
+beieeeftoa64(char *buf, uint n, void *s)
+{
+ return ieeeftoa64(buf, n, beswap4(*(u32int*)s), beswap4(((u32int*)(s))[1]));
+}
+
+int
+leieeeftoa32(char *buf, uint n, void *s)
+{
+ return ieeeftoa32(buf, n, leswap4(*(u32int*)s));
+}
+
+int
+leieeeftoa64(char *buf, uint n, void *s)
+{
+ return ieeeftoa64(buf, n, leswap4(((u32int*)(s))[1]), leswap4(*(u32int*)s));
+}
+
+/* packed in 12 bytes, with s[2]==s[3]==0; mantissa starts at s[4]*/
+int
+beieeeftoa80(char *buf, uint n, void *s)
+{
+ uchar *reg = (uchar*)s;
+ int i;
+ ulong x;
+ uchar ieee[8+8]; /* room for slop */
+ uchar *p, *q;
+
+ memset(ieee, 0, sizeof(ieee));
+ /* sign */
+ if(reg[0] & 0x80)
+ ieee[0] |= 0x80;
+
+ /* exponent */
+ x = ((reg[0]&0x7F)<<8) | reg[1];
+ if(x == 0) /* number is ±0 */
+ goto done;
+ if(x == 0x7FFF){
+ if(memcmp(reg+4, ieee+1, 8) == 0){ /* infinity */
+ x = 2047;
+ }else{ /* NaN */
+ x = 2047;
+ ieee[7] = 0x1; /* make sure */
+ }
+ ieee[0] |= x>>4;
+ ieee[1] |= (x&0xF)<<4;
+ goto done;
+ }
+ x -= 0x3FFF; /* exponent bias */
+ x += 1023;
+ if(x >= (1<<11) || ((reg[4]&0x80)==0 && x!=0))
+ return snprint(buf, n, "not in range");
+ ieee[0] |= x>>4;
+ ieee[1] |= (x&0xF)<<4;
+
+ /* mantissa */
+ p = reg+4;
+ q = ieee+1;
+ for(i=0; i<56; i+=8, p++, q++){ /* move one byte */
+ x = (p[0]&0x7F) << 1;
+ if(p[1] & 0x80)
+ x |= 1;
+ q[0] |= x>>4;
+ q[1] |= (x&0xF)<<4;
+ }
+ done:
+ return beieeeftoa64(buf, n, (void*)ieee);
+}
+
+
+int
+leieeeftoa80(char *buf, uint n, void *s)
+{
+ int i;
+ char *cp;
+ char b[12];
+
+ cp = (char*) s;
+ for(i=0; i<12; i++)
+ b[11-i] = *cp++;
+ return beieeeftoa80(buf, n, b);
+}
diff --git a/src/libmach/loc.c b/src/libmach/loc.c
new file mode 100644
index 00000000..47bcf3d9
--- /dev/null
+++ b/src/libmach/loc.c
@@ -0,0 +1,253 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+int
+locfmt(Fmt *fmt)
+{
+ Loc l;
+
+ l = va_arg(fmt->args, Loc);
+ switch(l.type){
+ default:
+ return fmtprint(fmt, "<loc%d>", l.type);
+ case LCONST:
+ return fmtprint(fmt, "0x%lux", l.addr);
+ case LADDR:
+ return fmtprint(fmt, "*0x%lux", l.addr);
+ case LOFFSET:
+ return fmtprint(fmt, "%ld(%s)", l.offset, l.reg);
+ case LREG:
+ return fmtprint(fmt, "%s", l.reg);
+ }
+}
+
+int
+loccmp(Loc *a, Loc *b)
+{
+ int i;
+
+ if(a->type < b->type)
+ return -1;
+ if(a->type > b->type)
+ return 1;
+ switch(a->type){
+ default:
+ return 0;
+ case LADDR:
+ if(a->addr < b->addr)
+ return -1;
+ if(a->addr > b->addr)
+ return 1;
+ return 0;
+ case LOFFSET:
+ i = strcmp(a->reg, b->reg);
+ if(i != 0)
+ return i;
+ if(a->offset < b->offset)
+ return -1;
+ if(a->offset > b->offset)
+ return 1;
+ return 0;
+ case LREG:
+ return strcmp(a->reg, b->reg);
+ }
+}
+
+int
+lget1(Map *map, Regs *regs, Loc loc, uchar *a, uint n)
+{
+ if(locsimplify(map, regs, loc, &loc) < 0)
+ return -1;
+ if(loc.type == LADDR)
+ return get1(map, loc.addr, a, n);
+ /* could do more here - i'm lazy */
+ werrstr("bad location for lget1");
+ return -1;
+}
+
+int
+lget2(Map *map, Regs *regs, Loc loc, u16int *u)
+{
+ ulong ul;
+
+ if(locsimplify(map, regs, loc, &loc) < 0)
+ return -1;
+ if(loc.type == LADDR)
+ return get2(map, loc.addr, u);
+ if(loc.type == LCONST){
+ *u = loc.addr;
+ return 0;
+ }
+ if(loc.type == LREG){
+ if(rget(regs, loc.reg, &ul) < 0)
+ return -1;
+ *u = ul;
+ return 0;
+ }
+ werrstr("bad location for lget2");
+ return -1;
+}
+
+int
+lget4(Map *map, Regs *regs, Loc loc, u32int *u)
+{
+ ulong ul;
+
+ if(locsimplify(map, regs, loc, &loc) < 0)
+ return -1;
+ if(loc.type == LADDR)
+ return get4(map, loc.addr, u);
+ if(loc.type == LCONST){
+ *u = loc.addr;
+ return 0;
+ }
+ if(loc.type == LREG){
+ if(rget(regs, loc.reg, &ul) < 0)
+ return -1;
+ *u = ul;
+ return 0;
+ }
+ werrstr("bad location for lget4");
+ return -1;
+}
+
+int
+lget8(Map *map, Regs *regs, Loc loc, u64int *u)
+{
+ ulong ul;
+
+ if(locsimplify(map, regs, loc, &loc) < 0)
+ return -1;
+ if(loc.type == LADDR)
+ return get8(map, loc.addr, u);
+ if(loc.type == LCONST){
+ *u = loc.addr;
+ return 0;
+ }
+ if(loc.type == LREG){
+ if(rget(regs, loc.reg, &ul) < 0)
+ return -1;
+ *u = ul;
+ return 0;
+ }
+ werrstr("bad location for lget8");
+ return -1;
+}
+
+int
+lput1(Map *map, Regs *regs, Loc loc, uchar *a, uint n)
+{
+ if(locsimplify(map, regs, loc, &loc) < 0)
+ return -1;
+ if(loc.type == LADDR)
+ return put1(map, loc.addr, a, n);
+ /* could do more here - i'm lazy */
+ werrstr("bad location for lput1");
+ return -1;
+}
+
+int
+lput2(Map *map, Regs *regs, Loc loc, u16int u)
+{
+ if(locsimplify(map, regs, loc, &loc) < 0)
+ return -1;
+ if(loc.type == LADDR)
+ return put2(map, loc.addr, u);
+ if(loc.type == LREG)
+ return rput(regs, loc.reg, u);
+ werrstr("bad location for lput2");
+ return -1;
+}
+
+int
+lput4(Map *map, Regs *regs, Loc loc, u32int u)
+{
+ if(locsimplify(map, regs, loc, &loc) < 0)
+ return -1;
+ if(loc.type == LADDR)
+ return put4(map, loc.addr, u);
+ if(loc.type == LREG)
+ return rput(regs, loc.reg, u);
+ werrstr("bad location for lput4");
+ return -1;
+}
+
+int
+lput8(Map *map, Regs *regs, Loc loc, u64int u)
+{
+ if(locsimplify(map, regs, loc, &loc) < 0)
+ return -1;
+ if(loc.type == LADDR)
+ return put8(map, loc.addr, u);
+ if(loc.type == LREG)
+ return rput(regs, loc.reg, u);
+ werrstr("bad location for lput8");
+ return -1;
+}
+
+Loc
+locaddr(ulong addr)
+{
+ Loc l;
+
+ l.type = LADDR;
+ l.addr = addr;
+ return l;
+}
+
+Loc
+locindir(char *reg, long offset)
+{
+ Loc l;
+
+ l.type = LOFFSET;
+ l.reg = reg;
+ l.offset = offset;
+ return l;
+}
+
+Loc
+locconst(ulong con)
+{
+ Loc l;
+
+ l.type = LCONST;
+ l.addr = con;
+ return l;
+}
+
+Loc
+locnone(void)
+{
+ Loc l;
+
+ l.type = LNONE;
+ return l;
+}
+
+Loc
+locreg(char *reg)
+{
+ Loc l;
+
+ l.type = LREG;
+ l.reg = reg;
+ return l;
+}
+
+int
+locsimplify(Map *map, Regs *regs, Loc loc, Loc *newloc)
+{
+ ulong u;
+
+ if(loc.type == LOFFSET){
+ if(rget(regs, loc.reg, &u) < 0)
+ return -1;
+ *newloc = locaddr(u + loc.offset);
+ }else
+ *newloc = loc;
+ return 0;
+}
+
diff --git a/src/libmach/localaddr.c b/src/libmach/localaddr.c
new file mode 100644
index 00000000..1b1dea73
--- /dev/null
+++ b/src/libmach/localaddr.c
@@ -0,0 +1,56 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+/*
+ * XXX could remove the rock by hiding it in a special regs.
+ * That would still be sleazy but would be thread-safe.
+ */
+
+static struct {
+ int found;
+ int nframe;
+ Loc l;
+ char *fn;
+ char *var;
+} rock;
+
+static int
+ltrace(Map *map, Regs *regs, ulong pc, ulong nextpc, Symbol *sym, int depth)
+{
+ ulong v;
+ Symbol s1;
+
+ USED(pc);
+ USED(nextpc);
+ USED(depth);
+
+ if(sym==nil || strcmp(sym->name, rock.fn) != 0)
+ return ++rock.nframe < 40;
+ if(lookuplsym(sym, rock.var, &s1) < 0)
+ return 0;
+ if(locsimplify(map, regs, s1.loc, &rock.l) < 0)
+ return 0;
+ if(rock.l.type == LREG && rget(regs, rock.l.reg, &v) >= 0)
+ rock.l = locconst(v);
+ if(rock.l.type != LADDR && rock.l.type != LCONST)
+ return 0;
+ rock.found = 1;
+ return 0;
+}
+
+int
+localaddr(Map *map, Regs *regs, char *fn, char *var, ulong *val)
+{
+ rock.found = 0;
+ rock.nframe = 0;
+ rock.fn = fn;
+ rock.var = var;
+ stacktrace(map, regs, ltrace);
+ if(rock.found){
+ *val = rock.l.addr;
+ return 0;
+ }
+ return -1;
+}
diff --git a/src/libmach/mach.c b/src/libmach/mach.c
new file mode 100644
index 00000000..89a88f16
--- /dev/null
+++ b/src/libmach/mach.c
@@ -0,0 +1,30 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+Mach *mach;
+
+extern Mach mach386;
+extern Mach machpower;
+
+static Mach *machs[] =
+{
+ &mach386,
+ &machpower,
+};
+
+Mach*
+machbyname(char *name)
+{
+ int i;
+
+ for(i=0; i<nelem(machs); i++)
+ if(strcmp(machs[i]->name, name) == 0){
+ mach = machs[i];
+ return machs[i];
+ }
+ werrstr("machine '%s' not found", name);
+ return nil;
+}
+
diff --git a/src/libmach/mach386.c b/src/libmach/mach386.c
new file mode 100644
index 00000000..5f336ce7
--- /dev/null
+++ b/src/libmach/mach386.c
@@ -0,0 +1,1786 @@
+/*
+ * 386 definition
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+#include "ureg386.h"
+
+#define REGOFF(x) (ulong)(&((struct Ureg *) 0)->x)
+
+#define REGSIZE sizeof(struct Ureg)
+#define FP_CTL(x) (REGSIZE+4*(x))
+#define FP_REG(x) (FP_CTL(7)+10*(x))
+#define FPREGSIZE (6*4+8*10)
+
+/*
+ * i386-specific debugger interface
+ */
+
+static char *i386excep(Map*, Regs*);
+
+/*
+static int i386trace(Map*, ulong, ulong, ulong, Tracer);
+static ulong i386frame(Map*, ulong, ulong, ulong, ulong);
+*/
+static int i386foll(Map*, Regs*, ulong, ulong*);
+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 Regdesc i386reglist[] = {
+ {"DI", REGOFF(di), RINT, 'X'},
+ {"SI", REGOFF(si), RINT, 'X'},
+ {"BP", REGOFF(bp), RINT, 'X'},
+ {"BX", REGOFF(bx), RINT, 'X'},
+ {"DX", REGOFF(dx), RINT, 'X'},
+ {"CX", REGOFF(cx), RINT, 'X'},
+ {"AX", REGOFF(ax), RINT, 'X'},
+ {"GS", REGOFF(gs), RINT, 'X'},
+ {"FS", REGOFF(fs), RINT, 'X'},
+ {"ES", REGOFF(es), RINT, 'X'},
+ {"DS", REGOFF(ds), RINT, 'X'},
+ {"TRAP", REGOFF(trap), RINT, 'X'},
+ {"ECODE", REGOFF(ecode), RINT, 'X'},
+ {"PC", REGOFF(pc), RINT, 'X'},
+ {"CS", REGOFF(cs), RINT, 'X'},
+ {"EFLAGS", REGOFF(flags), RINT, 'X'},
+ {"SP", REGOFF(sp), RINT, 'X'},
+ {"SS", REGOFF(ss), RINT, 'X'},
+
+ {"E0", FP_CTL(0), RFLT, 'X'},
+ {"E1", FP_CTL(1), RFLT, 'X'},
+ {"E2", FP_CTL(2), RFLT, 'X'},
+ {"E3", FP_CTL(3), RFLT, 'X'},
+ {"E4", FP_CTL(4), RFLT, 'X'},
+ {"E5", FP_CTL(5), RFLT, 'X'},
+ {"E6", FP_CTL(6), RFLT, 'X'},
+ {"F0", FP_REG(7), RFLT, '3'},
+ {"F1", FP_REG(6), RFLT, '3'},
+ {"F2", FP_REG(5), RFLT, '3'},
+ {"F3", FP_REG(4), RFLT, '3'},
+ {"F4", FP_REG(3), RFLT, '3'},
+ {"F5", FP_REG(2), RFLT, '3'},
+ {"F6", FP_REG(1), RFLT, '3'},
+ {"F7", FP_REG(0), RFLT, '3'},
+ { 0 }
+};
+
+Mach mach386 =
+{
+ "386",
+ M386, /* machine type */
+ i386reglist, /* register list */
+ REGSIZE, /* size of registers in bytes */
+ FPREGSIZE, /* size of fp registers in bytes */
+ "PC", /* name of PC */
+ "SP", /* name of SP */
+ "BP", /* name of FP */
+ 0, /* link register */
+ "setSB", /* static base register name (bogus anyways) */
+ 0, /* static base register value */
+ 0x1000, /* page size */
+ 0x80100000, /* kernel base */
+ 0, /* kernel text mask */
+ 1, /* quantization of pc */
+ 4, /* szaddr */
+ 4, /* szreg */
+ 4, /* szfloat */
+ 8, /* szdouble */
+
+ i386windregs, /* locations unwound in stack trace */
+ 9,
+
+ {0xCC, 0, 0, 0}, /* break point: INT 3 */
+ 1, /* break point size */
+
+ i386foll, /* following addresses */
+ i386excep, /* print exception */
+ i386unwind, /* stack unwind */
+
+ leswap2, /* convert short to local byte order */
+ leswap4, /* convert long to local byte order */
+ leswap8, /* convert vlong to local byte order */
+ leieeeftoa32, /* single precision float pointer */
+ leieeeftoa64, /* double precision float pointer */
+ leieeeftoa80, /* long double precision floating point */
+
+ i386das, /* dissembler */
+ i386das, /* plan9-format disassembler */
+ 0, /* commercial disassembler */
+ i386hexinst, /* print instruction */
+ i386instlen, /* instruction size calculation */
+};
+
+static char *i386windregs[] = {
+ "PC",
+ "SP",
+ "BP",
+ "AX",
+ "CX",
+ "DX",
+ "BX",
+ "SI",
+ "DI",
+ 0,
+};
+
+static int
+i386unwind(Map *map, Regs *regs, ulong *next)
+{
+ int isp, ipc, ibp;
+ ulong bp;
+ u32int v;
+
+ /* No symbol information, use frame pointer and do the best we can. */
+ isp = windindex("SP");
+ ipc = windindex("PC");
+ ibp = windindex("BP");
+ if(isp < 0 || ipc < 0 || ibp < 0){
+ werrstr("i386unwind: cannot happen");
+ return -1;
+ }
+
+ bp = next[ibp];
+
+ if(get4(map, bp, &v) < 0)
+ return -1;
+ next[ibp] = v;
+
+ next[isp] = bp+4;
+
+ if(get4(map, bp+4, &v) < 0)
+ return -1;
+ next[ipc] = v;
+
+ return 0;
+}
+
+//static char STARTSYM[] = "_main";
+//static char PROFSYM[] = "_mainp";
+static char FRAMENAME[] = ".frame";
+static char *excname[] =
+{
+[0] "divide error",
+[1] "debug exception",
+[4] "overflow",
+[5] "bounds check",
+[6] "invalid opcode",
+[7] "math coprocessor emulation",
+[8] "double fault",
+[9] "math coprocessor overrun",
+[10] "invalid TSS",
+[11] "segment not present",
+[12] "stack exception",
+[13] "general protection violation",
+[14] "page fault",
+[16] "math coprocessor error",
+[24] "clock",
+[25] "keyboard",
+[27] "modem status",
+[28] "serial line status",
+[30] "floppy disk",
+[36] "mouse",
+[37] "math coprocessor",
+[38] "hard disk",
+[64] "system call",
+};
+
+static char*
+i386excep(Map *map, Regs *regs)
+{
+ ulong c;
+ ulong pc;
+ static char buf[16];
+
+ if(rget(regs, "TRAP", &c) < 0)
+ return "no trap register";
+
+ if(c > 64 || excname[c] == 0) {
+ if (c == 3) {
+ if (rget(regs, "PC", &pc) >= 0)
+ if (get1(map, pc, (uchar*)buf, mach->bpsize) > 0)
+ if (memcmp(buf, mach->bpinst, mach->bpsize) == 0)
+ return "breakpoint";
+ }
+ sprint(buf, "exception %ld", c);
+ return buf;
+ } else
+ return excname[c];
+}
+
+ /* I386/486 - Disassembler and related functions */
+
+/*
+ * an instruction
+ */
+typedef struct Instr Instr;
+struct Instr
+{
+ uchar mem[1+1+1+1+2+1+1+4+4]; /* raw instruction */
+ ulong addr; /* address of start of instruction */
+ int n; /* number of bytes in instruction */
+ char *prefix; /* instr prefix */
+ char *segment; /* segment override */
+ uchar jumptype; /* set to the operand type for jump/ret/call */
+ char osize; /* 'W' or 'L' */
+ char asize; /* address size 'W' or 'L' */
+ uchar mod; /* bits 6-7 of mod r/m field */
+ uchar reg; /* bits 3-5 of mod r/m field */
+ char ss; /* bits 6-7 of SIB */
+ char index; /* bits 3-5 of SIB */
+ char base; /* bits 0-2 of SIB */
+ short seg; /* segment of far address */
+ ulong disp; /* displacement */
+ ulong imm; /* immediate */
+ ulong imm2; /* second immediate operand */
+ char *curr; /* fill level in output buffer */
+ char *end; /* end of output buffer */
+ char *err; /* error message */
+};
+
+ /* 386 register (ha!) set */
+enum{
+ AX=0,
+ CX,
+ DX,
+ BX,
+ SP,
+ BP,
+ SI,
+ DI,
+};
+ /* Operand Format codes */
+/*
+%A - address size register modifier (!asize -> 'E')
+%C - Control register CR0/CR1/CR2
+%D - Debug register DR0/DR1/DR2/DR3/DR6/DR7
+%I - second immediate operand
+%O - Operand size register modifier (!osize -> 'E')
+%T - Test register TR6/TR7
+%S - size code ('W' or 'L')
+%X - Weird opcode: OSIZE == 'W' => "CBW"; else => "CWDE"
+%d - displacement 16-32 bits
+%e - effective address - Mod R/M value
+%f - floating point register F0-F7 - from Mod R/M register
+%g - segment register
+%i - immediate operand 8-32 bits
+%p - PC-relative - signed displacement in immediate field
+%r - Reg from Mod R/M
+%x - Weird opcode: OSIZE == 'W' => "CWD"; else => "CDQ"
+*/
+
+typedef struct Optable Optable;
+struct Optable
+{
+ char operand[2];
+ void *proto; /* actually either (char*) or (Optable*) */
+};
+ /* Operand decoding codes */
+enum {
+ Ib = 1, /* 8-bit immediate - (no sign extension)*/
+ Ibs, /* 8-bit immediate (sign extended) */
+ Jbs, /* 8-bit sign-extended immediate in jump or call */
+ Iw, /* 16-bit immediate -> imm */
+ Iw2, /* 16-bit immediate -> imm2 */
+ Iwd, /* Operand-sized immediate (no sign extension)*/
+ Awd, /* Address offset */
+ Iwds, /* Operand-sized immediate (sign extended) */
+ RM, /* Word or long R/M field with register (/r) */
+ RMB, /* Byte R/M field with register (/r) */
+ RMOP, /* Word or long R/M field with op code (/digit) */
+ RMOPB, /* Byte R/M field with op code (/digit) */
+ RMR, /* R/M register only (mod = 11) */
+ RMM, /* R/M memory only (mod = 0/1/2) */
+ R0, /* Base reg of Mod R/M is literal 0x00 */
+ R1, /* Base reg of Mod R/M is literal 0x01 */
+ FRMOP, /* Floating point R/M field with opcode */
+ FRMEX, /* Extended floating point R/M field with opcode */
+ JUMP, /* Jump or Call flag - no operand */
+ RET, /* Return flag - no operand */
+ OA, /* literal 0x0a byte */
+ PTR, /* Seg:Displacement addr (ptr16:16 or ptr16:32) */
+ AUX, /* Multi-byte op code - Auxiliary table */
+ PRE, /* Instr Prefix */
+ SEG, /* Segment Prefix */
+ OPOVER, /* Operand size override */
+ ADDOVER, /* Address size override */
+};
+
+static Optable optab0F00[8]=
+{
+[0x00] 0,0, "MOVW LDT,%e",
+[0x01] 0,0, "MOVW TR,%e",
+[0x02] 0,0, "MOVW %e,LDT",
+[0x03] 0,0, "MOVW %e,TR",
+[0x04] 0,0, "VERR %e",
+[0x05] 0,0, "VERW %e",
+};
+
+static Optable optab0F01[8]=
+{
+[0x00] 0,0, "MOVL GDTR,%e",
+[0x01] 0,0, "MOVL IDTR,%e",
+[0x02] 0,0, "MOVL %e,GDTR",
+[0x03] 0,0, "MOVL %e,IDTR",
+[0x04] 0,0, "MOVW MSW,%e", /* word */
+[0x06] 0,0, "MOVW %e,MSW", /* word */
+};
+
+static Optable optab0FBA[8]=
+{
+[0x04] Ib,0, "BT%S %i,%e",
+[0x05] Ib,0, "BTS%S %i,%e",
+[0x06] Ib,0, "BTR%S %i,%e",
+[0x07] Ib,0, "BTC%S %i,%e",
+};
+
+static Optable optab0F[256]=
+{
+[0x00] RMOP,0, optab0F00,
+[0x01] RMOP,0, optab0F01,
+[0x02] RM,0, "LAR %e,%r",
+[0x03] RM,0, "LSL %e,%r",
+[0x06] 0,0, "CLTS",
+[0x08] 0,0, "INVD",
+[0x09] 0,0, "WBINVD",
+[0x20] RMR,0, "MOVL %C,%e",
+[0x21] RMR,0, "MOVL %D,%e",
+[0x22] RMR,0, "MOVL %e,%C",
+[0x23] RMR,0, "MOVL %e,%D",
+[0x24] RMR,0, "MOVL %T,%e",
+[0x26] RMR,0, "MOVL %e,%T",
+[0x30] 0,0, "WRMSR",
+[0x31] 0,0, "RDTSC",
+[0x32] 0,0, "RDMSR",
+[0x42] RM,0, "CMOVC %e,%r", /* CF */
+[0x43] RM,0, "CMOVNC %e,%r", /* ¬ CF */
+[0x44] RM,0, "CMOVZ %e,%r", /* ZF */
+[0x45] RM,0, "CMOVNZ %e,%r", /* ¬ ZF */
+[0x46] RM,0, "CMOVBE %e,%r", /* CF ∨ ZF */
+[0x47] RM,0, "CMOVA %e,%r", /* ¬CF ∧ ¬ZF */
+[0x48] RM,0, "CMOVS %e,%r", /* SF */
+[0x49] RM,0, "CMOVNS %e,%r", /* ¬ SF */
+[0x4A] RM,0, "CMOVP %e,%r", /* PF */
+[0x4B] RM,0, "CMOVNP %e,%r", /* ¬ PF */
+[0x4C] RM,0, "CMOVLT %e,%r", /* LT ≡ OF ≠ SF */
+[0x4D] RM,0, "CMOVGE %e,%r", /* GE ≡ ZF ∨ SF */
+[0x4E] RM,0, "CMOVLE %e,%r", /* LE ≡ ZF ∨ LT */
+[0x4F] RM,0, "CMOVGT %e,%r", /* GT ≡ ¬ZF ∧ GE */
+[0x80] Iwds,0, "JOS %p",
+[0x81] Iwds,0, "JOC %p",
+[0x82] Iwds,0, "JCS %p",
+[0x83] Iwds,0, "JCC %p",
+[0x84] Iwds,0, "JEQ %p",
+[0x85] Iwds,0, "JNE %p",
+[0x86] Iwds,0, "JLS %p",
+[0x87] Iwds,0, "JHI %p",
+[0x88] Iwds,0, "JMI %p",
+[0x89] Iwds,0, "JPL %p",
+[0x8a] Iwds,0, "JPS %p",
+[0x8b] Iwds,0, "JPC %p",
+[0x8c] Iwds,0, "JLT %p",
+[0x8d] Iwds,0, "JGE %p",
+[0x8e] Iwds,0, "JLE %p",
+[0x8f] Iwds,0, "JGT %p",
+[0x90] RMB,0, "SETOS %e",
+[0x91] RMB,0, "SETOC %e",
+[0x92] RMB,0, "SETCS %e",
+[0x93] RMB,0, "SETCC %e",
+[0x94] RMB,0, "SETEQ %e",
+[0x95] RMB,0, "SETNE %e",
+[0x96] RMB,0, "SETLS %e",
+[0x97] RMB,0, "SETHI %e",
+[0x98] RMB,0, "SETMI %e",
+[0x99] RMB,0, "SETPL %e",
+[0x9a] RMB,0, "SETPS %e",
+[0x9b] RMB,0, "SETPC %e",
+[0x9c] RMB,0, "SETLT %e",
+[0x9d] RMB,0, "SETGE %e",
+[0x9e] RMB,0, "SETLE %e",
+[0x9f] RMB,0, "SETGT %e",
+[0xa0] 0,0, "PUSHL FS",
+[0xa1] 0,0, "POPL FS",
+[0xa2] 0,0, "CPUID",
+[0xa3] RM,0, "BT%S %r,%e",
+[0xa4] RM,Ib, "SHLD%S %r,%i,%e",
+[0xa5] RM,0, "SHLD%S %r,CL,%e",
+[0xa8] 0,0, "PUSHL GS",
+[0xa9] 0,0, "POPL GS",
+[0xab] RM,0, "BTS%S %r,%e",
+[0xac] RM,Ib, "SHRD%S %r,%i,%e",
+[0xad] RM,0, "SHRD%S %r,CL,%e",
+[0xaf] RM,0, "IMUL%S %e,%r",
+[0xb2] RMM,0, "LSS %e,%r",
+[0xb3] RM,0, "BTR%S %r,%e",
+[0xb4] RMM,0, "LFS %e,%r",
+[0xb5] RMM,0, "LGS %e,%r",
+[0xb6] RMB,0, "MOVBZX %e,%R",
+[0xb7] RM,0, "MOVWZX %e,%R",
+[0xba] RMOP,0, optab0FBA,
+[0xbb] RM,0, "BTC%S %e,%r",
+[0xbc] RM,0, "BSF%S %e,%r",
+[0xbd] RM,0, "BSR%S %e,%r",
+[0xbe] RMB,0, "MOVBSX %e,%R",
+[0xbf] RM,0, "MOVWSX %e,%R",
+};
+
+static Optable optab80[8]=
+{
+[0x00] Ib,0, "ADDB %i,%e",
+[0x01] Ib,0, "ORB %i,%e",
+[0x02] Ib,0, "ADCB %i,%e",
+[0x03] Ib,0, "SBBB %i,%e",
+[0x04] Ib,0, "ANDB %i,%e",
+[0x05] Ib,0, "SUBB %i,%e",
+[0x06] Ib,0, "XORB %i,%e",
+[0x07] Ib,0, "CMPB %e,%i",
+};
+
+static Optable optab81[8]=
+{
+[0x00] Iwd,0, "ADD%S %i,%e",
+[0x01] Iwd,0, "OR%S %i,%e",
+[0x02] Iwd,0, "ADC%S %i,%e",
+[0x03] Iwd,0, "SBB%S %i,%e",
+[0x04] Iwd,0, "AND%S %i,%e",
+[0x05] Iwd,0, "SUB%S %i,%e",
+[0x06] Iwd,0, "XOR%S %i,%e",
+[0x07] Iwd,0, "CMP%S %e,%i",
+};
+
+static Optable optab83[8]=
+{
+[0x00] Ibs,0, "ADD%S %i,%e",
+[0x01] Ibs,0, "OR%S %i,%e",
+[0x02] Ibs,0, "ADC%S %i,%e",
+[0x03] Ibs,0, "SBB%S %i,%e",
+[0x04] Ibs,0, "AND%S %i,%e",
+[0x05] Ibs,0, "SUB%S %i,%e",
+[0x06] Ibs,0, "XOR%S %i,%e",
+[0x07] Ibs,0, "CMP%S %e,%i",
+};
+
+static Optable optabC0[8] =
+{
+[0x00] Ib,0, "ROLB %i,%e",
+[0x01] Ib,0, "RORB %i,%e",
+[0x02] Ib,0, "RCLB %i,%e",
+[0x03] Ib,0, "RCRB %i,%e",
+[0x04] Ib,0, "SHLB %i,%e",
+[0x05] Ib,0, "SHRB %i,%e",
+[0x07] Ib,0, "SARB %i,%e",
+};
+
+static Optable optabC1[8] =
+{
+[0x00] Ib,0, "ROL%S %i,%e",
+[0x01] Ib,0, "ROR%S %i,%e",
+[0x02] Ib,0, "RCL%S %i,%e",
+[0x03] Ib,0, "RCR%S %i,%e",
+[0x04] Ib,0, "SHL%S %i,%e",
+[0x05] Ib,0, "SHR%S %i,%e",
+[0x07] Ib,0, "SAR%S %i,%e",
+};
+
+static Optable optabD0[8] =
+{
+[0x00] 0,0, "ROLB %e",
+[0x01] 0,0, "RORB %e",
+[0x02] 0,0, "RCLB %e",
+[0x03] 0,0, "RCRB %e",
+[0x04] 0,0, "SHLB %e",
+[0x05] 0,0, "SHRB %e",
+[0x07] 0,0, "SARB %e",
+};
+
+static Optable optabD1[8] =
+{
+[0x00] 0,0, "ROL%S %e",
+[0x01] 0,0, "ROR%S %e",
+[0x02] 0,0, "RCL%S %e",
+[0x03] 0,0, "RCR%S %e",
+[0x04] 0,0, "SHL%S %e",
+[0x05] 0,0, "SHR%S %e",
+[0x07] 0,0, "SAR%S %e",
+};
+
+static Optable optabD2[8] =
+{
+[0x00] 0,0, "ROLB CL,%e",
+[0x01] 0,0, "RORB CL,%e",
+[0x02] 0,0, "RCLB CL,%e",
+[0x03] 0,0, "RCRB CL,%e",
+[0x04] 0,0, "SHLB CL,%e",
+[0x05] 0,0, "SHRB CL,%e",
+[0x07] 0,0, "SARB CL,%e",
+};
+
+static Optable optabD3[8] =
+{
+[0x00] 0,0, "ROL%S CL,%e",
+[0x01] 0,0, "ROR%S CL,%e",
+[0x02] 0,0, "RCL%S CL,%e",
+[0x03] 0,0, "RCR%S CL,%e",
+[0x04] 0,0, "SHL%S CL,%e",
+[0x05] 0,0, "SHR%S CL,%e",
+[0x07] 0,0, "SAR%S CL,%e",
+};
+
+static Optable optabD8[8+8] =
+{
+[0x00] 0,0, "FADDF %e,F0",
+[0x01] 0,0, "FMULF %e,F0",
+[0x02] 0,0, "FCOMF %e,F0",
+[0x03] 0,0, "FCOMFP %e,F0",
+[0x04] 0,0, "FSUBF %e,F0",
+[0x05] 0,0, "FSUBRF %e,F0",
+[0x06] 0,0, "FDIVF %e,F0",
+[0x07] 0,0, "FDIVRF %e,F0",
+[0x08] 0,0, "FADDD %f,F0",
+[0x09] 0,0, "FMULD %f,F0",
+[0x0a] 0,0, "FCOMD %f,F0",
+[0x0b] 0,0, "FCOMPD %f,F0",
+[0x0c] 0,0, "FSUBD %f,F0",
+[0x0d] 0,0, "FSUBRD %f,F0",
+[0x0e] 0,0, "FDIVD %f,F0",
+[0x0f] 0,0, "FDIVRD %f,F0",
+};
+/*
+ * optabD9 and optabDB use the following encoding:
+ * if (0 <= modrm <= 2) instruction = optabDx[modrm&0x07];
+ * else instruction = optabDx[(modrm&0x3f)+8];
+ *
+ * the instructions for MOD == 3, follow the 8 instructions
+ * for the other MOD values stored at the front of the table.
+ */
+static Optable optabD9[64+8] =
+{
+[0x00] 0,0, "FMOVF %e,F0",
+[0x02] 0,0, "FMOVF F0,%e",
+[0x03] 0,0, "FMOVFP F0,%e",
+[0x04] 0,0, "FLDENV%S %e",
+[0x05] 0,0, "FLDCW %e",
+[0x06] 0,0, "FSTENV%S %e",
+[0x07] 0,0, "FSTCW %e",
+[0x08] 0,0, "FMOVD F0,F0", /* Mod R/M = 11xx xxxx*/
+[0x09] 0,0, "FMOVD F1,F0",
+[0x0a] 0,0, "FMOVD F2,F0",
+[0x0b] 0,0, "FMOVD F3,F0",
+[0x0c] 0,0, "FMOVD F4,F0",
+[0x0d] 0,0, "FMOVD F5,F0",
+[0x0e] 0,0, "FMOVD F6,F0",
+[0x0f] 0,0, "FMOVD F7,F0",
+[0x10] 0,0, "FXCHD F0,F0",
+[0x11] 0,0, "FXCHD F1,F0",
+[0x12] 0,0, "FXCHD F2,F0",
+[0x13] 0,0, "FXCHD F3,F0",
+[0x14] 0,0, "FXCHD F4,F0",
+[0x15] 0,0, "FXCHD F5,F0",
+[0x16] 0,0, "FXCHD F6,F0",
+[0x17] 0,0, "FXCHD F7,F0",
+[0x18] 0,0, "FNOP",
+[0x28] 0,0, "FCHS",
+[0x29] 0,0, "FABS",
+[0x2c] 0,0, "FTST",
+[0x2d] 0,0, "FXAM",
+[0x30] 0,0, "FLD1",
+[0x31] 0,0, "FLDL2T",
+[0x32] 0,0, "FLDL2E",
+[0x33] 0,0, "FLDPI",
+[0x34] 0,0, "FLDLG2",
+[0x35] 0,0, "FLDLN2",
+[0x36] 0,0, "FLDZ",
+[0x38] 0,0, "F2XM1",
+[0x39] 0,0, "FYL2X",
+[0x3a] 0,0, "FPTAN",
+[0x3b] 0,0, "FPATAN",
+[0x3c] 0,0, "FXTRACT",
+[0x3d] 0,0, "FPREM1",
+[0x3e] 0,0, "FDECSTP",
+[0x3f] 0,0, "FNCSTP",
+[0x40] 0,0, "FPREM",
+[0x41] 0,0, "FYL2XP1",
+[0x42] 0,0, "FSQRT",
+[0x43] 0,0, "FSINCOS",
+[0x44] 0,0, "FRNDINT",
+[0x45] 0,0, "FSCALE",
+[0x46] 0,0, "FSIN",
+[0x47] 0,0, "FCOS",
+};
+
+static Optable optabDA[8+8] =
+{
+[0x00] 0,0, "FADDL %e,F0",
+[0x01] 0,0, "FMULL %e,F0",
+[0x02] 0,0, "FCOML %e,F0",
+[0x03] 0,0, "FCOMLP %e,F0",
+[0x04] 0,0, "FSUBL %e,F0",
+[0x05] 0,0, "FSUBRL %e,F0",
+[0x06] 0,0, "FDIVL %e,F0",
+[0x07] 0,0, "FDIVRL %e,F0",
+[0x0d] R1,0, "FUCOMPP",
+};
+
+static Optable optabDB[8+64] =
+{
+[0x00] 0,0, "FMOVL %e,F0",
+[0x02] 0,0, "FMOVL F0,%e",
+[0x03] 0,0, "FMOVLP F0,%e",
+[0x05] 0,0, "FMOVX %e,F0",
+[0x07] 0,0, "FMOVXP F0,%e",
+[0x2a] 0,0, "FCLEX",
+[0x2b] 0,0, "FINIT",
+};
+
+static Optable optabDC[8+8] =
+{
+[0x00] 0,0, "FADDD %e,F0",
+[0x01] 0,0, "FMULD %e,F0",
+[0x02] 0,0, "FCOMD %e,F0",
+[0x03] 0,0, "FCOMDP %e,F0",
+[0x04] 0,0, "FSUBD %e,F0",
+[0x05] 0,0, "FSUBRD %e,F0",
+[0x06] 0,0, "FDIVD %e,F0",
+[0x07] 0,0, "FDIVRD %e,F0",
+[0x08] 0,0, "FADDD F0,%f",
+[0x09] 0,0, "FMULD F0,%f",
+[0x0c] 0,0, "FSUBRD F0,%f",
+[0x0d] 0,0, "FSUBD F0,%f",
+[0x0e] 0,0, "FDIVRD F0,%f",
+[0x0f] 0,0, "FDIVD F0,%f",
+};
+
+static Optable optabDD[8+8] =
+{
+[0x00] 0,0, "FMOVD %e,F0",
+[0x02] 0,0, "FMOVD F0,%e",
+[0x03] 0,0, "FMOVDP F0,%e",
+[0x04] 0,0, "FRSTOR%S %e",
+[0x06] 0,0, "FSAVE%S %e",
+[0x07] 0,0, "FSTSW %e",
+[0x08] 0,0, "FFREED %f",
+[0x0a] 0,0, "FMOVD %f,F0",
+[0x0b] 0,0, "FMOVDP %f,F0",
+[0x0c] 0,0, "FUCOMD %f,F0",
+[0x0d] 0,0, "FUCOMDP %f,F0",
+};
+
+static Optable optabDE[8+8] =
+{
+[0x00] 0,0, "FADDW %e,F0",
+[0x01] 0,0, "FMULW %e,F0",
+[0x02] 0,0, "FCOMW %e,F0",
+[0x03] 0,0, "FCOMWP %e,F0",
+[0x04] 0,0, "FSUBW %e,F0",
+[0x05] 0,0, "FSUBRW %e,F0",
+[0x06] 0,0, "FDIVW %e,F0",
+[0x07] 0,0, "FDIVRW %e,F0",
+[0x08] 0,0, "FADDDP F0,%f",
+[0x09] 0,0, "FMULDP F0,%f",
+[0x0b] R1,0, "FCOMPDP",
+[0x0c] 0,0, "FSUBRDP F0,%f",
+[0x0d] 0,0, "FSUBDP F0,%f",
+[0x0e] 0,0, "FDIVRDP F0,%f",
+[0x0f] 0,0, "FDIVDP F0,%f",
+};
+
+static Optable optabDF[8+8] =
+{
+[0x00] 0,0, "FMOVW %e,F0",
+[0x02] 0,0, "FMOVW F0,%e",
+[0x03] 0,0, "FMOVWP F0,%e",
+[0x04] 0,0, "FBLD %e",
+[0x05] 0,0, "FMOVL %e,F0",
+[0x06] 0,0, "FBSTP %e",
+[0x07] 0,0, "FMOVLP F0,%e",
+[0x0c] R0,0, "FSTSW %OAX",
+};
+
+static Optable optabF6[8] =
+{
+[0x00] Ib,0, "TESTB %i,%e",
+[0x02] 0,0, "NOTB %e",
+[0x03] 0,0, "NEGB %e",
+[0x04] 0,0, "MULB AL,%e",
+[0x05] 0,0, "IMULB AL,%e",
+[0x06] 0,0, "DIVB AL,%e",
+[0x07] 0,0, "IDIVB AL,%e",
+};
+
+static Optable optabF7[8] =
+{
+[0x00] Iwd,0, "TEST%S %i,%e",
+[0x02] 0,0, "NOT%S %e",
+[0x03] 0,0, "NEG%S %e",
+[0x04] 0,0, "MUL%S %OAX,%e",
+[0x05] 0,0, "IMUL%S %OAX,%e",
+[0x06] 0,0, "DIV%S %OAX,%e",
+[0x07] 0,0, "IDIV%S %OAX,%e",
+};
+
+static Optable optabFE[8] =
+{
+[0x00] 0,0, "INCB %e",
+[0x01] 0,0, "DECB %e",
+};
+
+static Optable optabFF[8] =
+{
+[0x00] 0,0, "INC%S %e",
+[0x01] 0,0, "DEC%S %e",
+[0x02] JUMP,0, "CALL* %e",
+[0x03] JUMP,0, "CALLF* %e",
+[0x04] JUMP,0, "JMP* %e",
+[0x05] JUMP,0, "JMPF* %e",
+[0x06] 0,0, "PUSHL %e",
+};
+
+static Optable optable[256] =
+{
+[0x00] RMB,0, "ADDB %r,%e",
+[0x01] RM,0, "ADD%S %r,%e",
+[0x02] RMB,0, "ADDB %e,%r",
+[0x03] RM,0, "ADD%S %e,%r",
+[0x04] Ib,0, "ADDB %i,AL",
+[0x05] Iwd,0, "ADD%S %i,%OAX",
+[0x06] 0,0, "PUSHL ES",
+[0x07] 0,0, "POPL ES",
+[0x08] RMB,0, "ORB %r,%e",
+[0x09] RM,0, "OR%S %r,%e",
+[0x0a] RMB,0, "ORB %e,%r",
+[0x0b] RM,0, "OR%S %e,%r",
+[0x0c] Ib,0, "ORB %i,AL",
+[0x0d] Iwd,0, "OR%S %i,%OAX",
+[0x0e] 0,0, "PUSHL CS",
+[0x0f] AUX,0, optab0F,
+[0x10] RMB,0, "ADCB %r,%e",
+[0x11] RM,0, "ADC%S %r,%e",
+[0x12] RMB,0, "ADCB %e,%r",
+[0x13] RM,0, "ADC%S %e,%r",
+[0x14] Ib,0, "ADCB %i,AL",
+[0x15] Iwd,0, "ADC%S %i,%OAX",
+[0x16] 0,0, "PUSHL SS",
+[0x17] 0,0, "POPL SS",
+[0x18] RMB,0, "SBBB %r,%e",
+[0x19] RM,0, "SBB%S %r,%e",
+[0x1a] RMB,0, "SBBB %e,%r",
+[0x1b] RM,0, "SBB%S %e,%r",
+[0x1c] Ib,0, "SBBB %i,AL",
+[0x1d] Iwd,0, "SBB%S %i,%OAX",
+[0x1e] 0,0, "PUSHL DS",
+[0x1f] 0,0, "POPL DS",
+[0x20] RMB,0, "ANDB %r,%e",
+[0x21] RM,0, "AND%S %r,%e",
+[0x22] RMB,0, "ANDB %e,%r",
+[0x23] RM,0, "AND%S %e,%r",
+[0x24] Ib,0, "ANDB %i,AL",
+[0x25] Iwd,0, "AND%S %i,%OAX",
+[0x26] SEG,0, "ES:",
+[0x27] 0,0, "DAA",
+[0x28] RMB,0, "SUBB %r,%e",
+[0x29] RM,0, "SUB%S %r,%e",
+[0x2a] RMB,0, "SUBB %e,%r",
+[0x2b] RM,0, "SUB%S %e,%r",
+[0x2c] Ib,0, "SUBB %i,AL",
+[0x2d] Iwd,0, "SUB%S %i,%OAX",
+[0x2e] SEG,0, "CS:",
+[0x2f] 0,0, "DAS",
+[0x30] RMB,0, "XORB %r,%e",
+[0x31] RM,0, "XOR%S %r,%e",
+[0x32] RMB,0, "XORB %e,%r",
+[0x33] RM,0, "XOR%S %e,%r",
+[0x34] Ib,0, "XORB %i,AL",
+[0x35] Iwd,0, "XOR%S %i,%OAX",
+[0x36] SEG,0, "SS:",
+[0x37] 0,0, "AAA",
+[0x38] RMB,0, "CMPB %r,%e",
+[0x39] RM,0, "CMP%S %r,%e",
+[0x3a] RMB,0, "CMPB %e,%r",
+[0x3b] RM,0, "CMP%S %e,%r",
+[0x3c] Ib,0, "CMPB %i,AL",
+[0x3d] Iwd,0, "CMP%S %i,%OAX",
+[0x3e] SEG,0, "DS:",
+[0x3f] 0,0, "AAS",
+[0x40] 0,0, "INC%S %OAX",
+[0x41] 0,0, "INC%S %OCX",
+[0x42] 0,0, "INC%S %ODX",
+[0x43] 0,0, "INC%S %OBX",
+[0x44] 0,0, "INC%S %OSP",
+[0x45] 0,0, "INC%S %OBP",
+[0x46] 0,0, "INC%S %OSI",
+[0x47] 0,0, "INC%S %ODI",
+[0x48] 0,0, "DEC%S %OAX",
+[0x49] 0,0, "DEC%S %OCX",
+[0x4a] 0,0, "DEC%S %ODX",
+[0x4b] 0,0, "DEC%S %OBX",
+[0x4c] 0,0, "DEC%S %OSP",
+[0x4d] 0,0, "DEC%S %OBP",
+[0x4e] 0,0, "DEC%S %OSI",
+[0x4f] 0,0, "DEC%S %ODI",
+[0x50] 0,0, "PUSH%S %OAX",
+[0x51] 0,0, "PUSH%S %OCX",
+[0x52] 0,0, "PUSH%S %ODX",
+[0x53] 0,0, "PUSH%S %OBX",
+[0x54] 0,0, "PUSH%S %OSP",
+[0x55] 0,0, "PUSH%S %OBP",
+[0x56] 0,0, "PUSH%S %OSI",
+[0x57] 0,0, "PUSH%S %ODI",
+[0x58] 0,0, "POP%S %OAX",
+[0x59] 0,0, "POP%S %OCX",
+[0x5a] 0,0, "POP%S %ODX",
+[0x5b] 0,0, "POP%S %OBX",
+[0x5c] 0,0, "POP%S %OSP",
+[0x5d] 0,0, "POP%S %OBP",
+[0x5e] 0,0, "POP%S %OSI",
+[0x5f] 0,0, "POP%S %ODI",
+[0x60] 0,0, "PUSHA%S",
+[0x61] 0,0, "POPA%S",
+[0x62] RMM,0, "BOUND %e,%r",
+[0x63] RM,0, "ARPL %r,%e",
+[0x64] SEG,0, "FS:",
+[0x65] SEG,0, "GS:",
+[0x66] OPOVER,0, "",
+[0x67] ADDOVER,0, "",
+[0x68] Iwd,0, "PUSH%S %i",
+[0x69] RM,Iwd, "IMUL%S %e,%i,%r",
+[0x6a] Ib,0, "PUSH%S %i",
+[0x6b] RM,Ibs, "IMUL%S %e,%i,%r",
+[0x6c] 0,0, "INSB DX,(%ODI)",
+[0x6d] 0,0, "INS%S DX,(%ODI)",
+[0x6e] 0,0, "OUTSB (%ASI),DX",
+[0x6f] 0,0, "OUTS%S (%ASI),DX",
+[0x70] Jbs,0, "JOS %p",
+[0x71] Jbs,0, "JOC %p",
+[0x72] Jbs,0, "JCS %p",
+[0x73] Jbs,0, "JCC %p",
+[0x74] Jbs,0, "JEQ %p",
+[0x75] Jbs,0, "JNE %p",
+[0x76] Jbs,0, "JLS %p",
+[0x77] Jbs,0, "JHI %p",
+[0x78] Jbs,0, "JMI %p",
+[0x79] Jbs,0, "JPL %p",
+[0x7a] Jbs,0, "JPS %p",
+[0x7b] Jbs,0, "JPC %p",
+[0x7c] Jbs,0, "JLT %p",
+[0x7d] Jbs,0, "JGE %p",
+[0x7e] Jbs,0, "JLE %p",
+[0x7f] Jbs,0, "JGT %p",
+[0x80] RMOPB,0, optab80,
+[0x81] RMOP,0, optab81,
+[0x83] RMOP,0, optab83,
+[0x84] RMB,0, "TESTB %r,%e",
+[0x85] RM,0, "TEST%S %r,%e",
+[0x86] RMB,0, "XCHGB %r,%e",
+[0x87] RM,0, "XCHG%S %r,%e",
+[0x88] RMB,0, "MOVB %r,%e",
+[0x89] RM,0, "MOV%S %r,%e",
+[0x8a] RMB,0, "MOVB %e,%r",
+[0x8b] RM,0, "MOV%S %e,%r",
+[0x8c] RM,0, "MOVW %g,%e",
+[0x8d] RM,0, "LEA %e,%r",
+[0x8e] RM,0, "MOVW %e,%g",
+[0x8f] RM,0, "POP%S %e",
+[0x90] 0,0, "NOP",
+[0x91] 0,0, "XCHG %OCX,%OAX",
+[0x92] 0,0, "XCHG %ODX,%OAX",
+[0x93] 0,0, "XCHG %OBX,%OAX",
+[0x94] 0,0, "XCHG %OSP,%OAX",
+[0x95] 0,0, "XCHG %OBP,%OAX",
+[0x96] 0,0, "XCHG %OSI,%OAX",
+[0x97] 0,0, "XCHG %ODI,%OAX",
+[0x98] 0,0, "%X", /* miserable CBW or CWDE */
+[0x99] 0,0, "%x", /* idiotic CWD or CDQ */
+[0x9a] PTR,0, "CALL%S %d",
+[0x9b] 0,0, "WAIT",
+[0x9c] 0,0, "PUSHF",
+[0x9d] 0,0, "POPF",
+[0x9e] 0,0, "SAHF",
+[0x9f] 0,0, "LAHF",
+[0xa0] Awd,0, "MOVB %i,AL",
+[0xa1] Awd,0, "MOV%S %i,%OAX",
+[0xa2] Awd,0, "MOVB AL,%i",
+[0xa3] Awd,0, "MOV%S %OAX,%i",
+[0xa4] 0,0, "MOVSB (%ASI),(%ADI)",
+[0xa5] 0,0, "MOVS%S (%ASI),(%ADI)",
+[0xa6] 0,0, "CMPSB (%ASI),(%ADI)",
+[0xa7] 0,0, "CMPS%S (%ASI),(%ADI)",
+[0xa8] Ib,0, "TESTB %i,AL",
+[0xa9] Iwd,0, "TEST%S %i,%OAX",
+[0xaa] 0,0, "STOSB AL,(%ADI)",
+[0xab] 0,0, "STOS%S %OAX,(%ADI)",
+[0xac] 0,0, "LODSB (%ASI),AL",
+[0xad] 0,0, "LODS%S (%ASI),%OAX",
+[0xae] 0,0, "SCASB (%ADI),AL",
+[0xaf] 0,0, "SCAS%S (%ADI),%OAX",
+[0xb0] Ib,0, "MOVB %i,AL",
+[0xb1] Ib,0, "MOVB %i,CL",
+[0xb2] Ib,0, "MOVB %i,DL",
+[0xb3] Ib,0, "MOVB %i,BL",
+[0xb4] Ib,0, "MOVB %i,AH",
+[0xb5] Ib,0, "MOVB %i,CH",
+[0xb6] Ib,0, "MOVB %i,DH",
+[0xb7] Ib,0, "MOVB %i,BH",
+[0xb8] Iwd,0, "MOV%S %i,%OAX",
+[0xb9] Iwd,0, "MOV%S %i,%OCX",
+[0xba] Iwd,0, "MOV%S %i,%ODX",
+[0xbb] Iwd,0, "MOV%S %i,%OBX",
+[0xbc] Iwd,0, "MOV%S %i,%OSP",
+[0xbd] Iwd,0, "MOV%S %i,%OBP",
+[0xbe] Iwd,0, "MOV%S %i,%OSI",
+[0xbf] Iwd,0, "MOV%S %i,%ODI",
+[0xc0] RMOPB,0, optabC0,
+[0xc1] RMOP,0, optabC1,
+[0xc2] Iw,0, "RET %i",
+[0xc3] RET,0, "RET",
+[0xc4] RM,0, "LES %e,%r",
+[0xc5] RM,0, "LDS %e,%r",
+[0xc6] RMB,Ib, "MOVB %i,%e",
+[0xc7] RM,Iwd, "MOV%S %i,%e",
+[0xc8] Iw2,Ib, "ENTER %i,%I", /* loony ENTER */
+[0xc9] RET,0, "LEAVE", /* bizarre LEAVE */
+[0xca] Iw,0, "RETF %i",
+[0xcb] RET,0, "RETF",
+[0xcc] 0,0, "INT 3",
+[0xcd] Ib,0, "INTB %i",
+[0xce] 0,0, "INTO",
+[0xcf] 0,0, "IRET",
+[0xd0] RMOPB,0, optabD0,
+[0xd1] RMOP,0, optabD1,
+[0xd2] RMOPB,0, optabD2,
+[0xd3] RMOP,0, optabD3,
+[0xd4] OA,0, "AAM",
+[0xd5] OA,0, "AAD",
+[0xd7] 0,0, "XLAT",
+[0xd8] FRMOP,0, optabD8,
+[0xd9] FRMEX,0, optabD9,
+[0xda] FRMOP,0, optabDA,
+[0xdb] FRMEX,0, optabDB,
+[0xdc] FRMOP,0, optabDC,
+[0xdd] FRMOP,0, optabDD,
+[0xde] FRMOP,0, optabDE,
+[0xdf] FRMOP,0, optabDF,
+[0xe0] Jbs,0, "LOOPNE %p",
+[0xe1] Jbs,0, "LOOPE %p",
+[0xe2] Jbs,0, "LOOP %p",
+[0xe3] Jbs,0, "JCXZ %p",
+[0xe4] Ib,0, "INB %i,AL",
+[0xe5] Ib,0, "IN%S %i,%OAX",
+[0xe6] Ib,0, "OUTB AL,%i",
+[0xe7] Ib,0, "OUT%S %OAX,%i",
+[0xe8] Iwds,0, "CALL %p",
+[0xe9] Iwds,0, "JMP %p",
+[0xea] PTR,0, "JMP %d",
+[0xeb] Jbs,0, "JMP %p",
+[0xec] 0,0, "INB DX,AL",
+[0xed] 0,0, "IN%S DX,%OAX",
+[0xee] 0,0, "OUTB AL,DX",
+[0xef] 0,0, "OUT%S %OAX,DX",
+[0xf0] PRE,0, "LOCK",
+[0xf2] PRE,0, "REPNE",
+[0xf3] PRE,0, "REP",
+[0xf4] 0,0, "HALT",
+[0xf5] 0,0, "CMC",
+[0xf6] RMOPB,0, optabF6,
+[0xf7] RMOP,0, optabF7,
+[0xf8] 0,0, "CLC",
+[0xf9] 0,0, "STC",
+[0xfa] 0,0, "CLI",
+[0xfb] 0,0, "STI",
+[0xfc] 0,0, "CLD",
+[0xfd] 0,0, "STD",
+[0xfe] RMOPB,0, optabFE,
+[0xff] RMOP,0, optabFF,
+};
+
+/*
+ * get a byte of the instruction
+ */
+static int
+igetc(Map * map, Instr *ip, uchar *c)
+{
+ if(ip->n+1 > sizeof(ip->mem)){
+ werrstr("instruction too long");
+ return -1;
+ }
+ if (get1(map, ip->addr+ip->n, c, 1) < 0) {
+ werrstr("can't read instruction: %r");
+ return -1;
+ }
+ ip->mem[ip->n++] = *c;
+ return 1;
+}
+
+/*
+ * get two bytes of the instruction
+ */
+static int
+igets(Map *map, Instr *ip, ushort *sp)
+{
+ uchar c;
+ ushort s;
+
+ if (igetc(map, ip, &c) < 0)
+ return -1;
+ s = c;
+ if (igetc(map, ip, &c) < 0)
+ return -1;
+ s |= (c<<8);
+ *sp = s;
+ return 1;
+}
+
+/*
+ * get 4 bytes of the instruction
+ */
+static int
+igetl(Map *map, Instr *ip, ulong *lp)
+{
+ ushort s;
+ long l;
+
+ if (igets(map, ip, &s) < 0)
+ return -1;
+ l = s;
+ if (igets(map, ip, &s) < 0)
+ return -1;
+ l |= (s<<16);
+ *lp = l;
+ return 1;
+}
+
+static int
+getdisp(Map *map, Instr *ip, int mod, int rm, int code)
+{
+ uchar c;
+ ushort s;
+
+ if (mod > 2)
+ return 1;
+ if (mod == 1) {
+ if (igetc(map, ip, &c) < 0)
+ return -1;
+ if (c&0x80)
+ ip->disp = c|0xffffff00;
+ else
+ ip->disp = c&0xff;
+ } else if (mod == 2 || rm == code) {
+ if (ip->asize == 'E') {
+ if (igetl(map, ip, &ip->disp) < 0)
+ return -1;
+ } else {
+ if (igets(map, ip, &s) < 0)
+ return -1;
+ if (s&0x8000)
+ ip->disp = s|0xffff0000;
+ else
+ ip->disp = s;
+ }
+ if (mod == 0)
+ ip->base = -1;
+ }
+ return 1;
+}
+
+static int
+modrm(Map *map, Instr *ip, uchar c)
+{
+ uchar rm, mod;
+
+ mod = (c>>6)&3;
+ rm = c&7;
+ ip->mod = mod;
+ ip->base = rm;
+ ip->reg = (c>>3)&7;
+ if (mod == 3) /* register */
+ return 1;
+ if (ip->asize == 0) { /* 16-bit mode */
+ switch(rm)
+ {
+ case 0:
+ ip->base = BX; ip->index = SI;
+ break;
+ case 1:
+ ip->base = BX; ip->index = DI;
+ break;
+ case 2:
+ ip->base = BP; ip->index = SI;
+ break;
+ case 3:
+ ip->base = BP; ip->index = DI;
+ break;
+ case 4:
+ ip->base = SI;
+ break;
+ case 5:
+ ip->base = DI;
+ break;
+ case 6:
+ ip->base = BP;
+ break;
+ case 7:
+ ip->base = BX;
+ break;
+ default:
+ break;
+ }
+ return getdisp(map, ip, mod, rm, 6);
+ }
+ if (rm == 4) { /* scummy sib byte */
+ if (igetc(map, ip, &c) < 0)
+ return -1;
+ ip->ss = (c>>6)&0x03;
+ ip->index = (c>>3)&0x07;
+ if (ip->index == 4)
+ ip->index = -1;
+ ip->base = c&0x07;
+ return getdisp(map, ip, mod, ip->base, 5);
+ }
+ return getdisp(map, ip, mod, rm, 5);
+}
+
+static Optable *
+mkinstr(Map *map, Instr *ip, ulong pc)
+{
+ int i, n;
+ uchar c;
+ ushort s;
+ Optable *op, *obase;
+ char buf[128];
+
+ memset(ip, 0, sizeof(*ip));
+ ip->base = -1;
+ ip->index = -1;
+ if(0) /* asstype == AI8086) */
+ ip->osize = 'W';
+ else {
+ ip->osize = 'L';
+ ip->asize = 'E';
+ }
+ ip->addr = pc;
+ if (igetc(map, ip, &c) < 0)
+ return 0;
+ obase = optable;
+newop:
+ op = &obase[c];
+ if (op->proto == 0) {
+badop:
+ n = snprint(buf, sizeof(buf), "opcode: ??");
+ for (i = 0; i < ip->n && n < sizeof(buf)-3; i++, n+=2)
+ _hexify(buf+n, ip->mem[i], 1);
+ strcpy(buf+n, "??");
+ werrstr(buf);
+ return 0;
+ }
+ for(i = 0; i < 2 && op->operand[i]; i++) {
+ switch(op->operand[i])
+ {
+ case Ib: /* 8-bit immediate - (no sign extension)*/
+ if (igetc(map, ip, &c) < 0)
+ return 0;
+ ip->imm = c&0xff;
+ break;
+ case Jbs: /* 8-bit jump immediate (sign extended) */
+ if (igetc(map, ip, &c) < 0)
+ return 0;
+ if (c&0x80)
+ ip->imm = c|0xffffff00;
+ else
+ ip->imm = c&0xff;
+ ip->jumptype = Jbs;
+ break;
+ case Ibs: /* 8-bit immediate (sign extended) */
+ if (igetc(map, ip, &c) < 0)
+ return 0;
+ if (c&0x80)
+ if (ip->osize == 'L')
+ ip->imm = c|0xffffff00;
+ else
+ ip->imm = c|0xff00;
+ else
+ ip->imm = c&0xff;
+ break;
+ case Iw: /* 16-bit immediate -> imm */
+ if (igets(map, ip, &s) < 0)
+ return 0;
+ ip->imm = s&0xffff;
+ ip->jumptype = Iw;
+ break;
+ case Iw2: /* 16-bit immediate -> in imm2*/
+ if (igets(map, ip, &s) < 0)
+ return 0;
+ ip->imm2 = s&0xffff;
+ break;
+ case Iwd: /* Operand-sized immediate (no sign extension)*/
+ if (ip->osize == 'L') {
+ if (igetl(map, ip, &ip->imm) < 0)
+ return 0;
+ } else {
+ if (igets(map, ip, &s)< 0)
+ return 0;
+ ip->imm = s&0xffff;
+ }
+ break;
+ case Awd: /* Address-sized immediate (no sign extension)*/
+ if (ip->asize == 'E') {
+ if (igetl(map, ip, &ip->imm) < 0)
+ return 0;
+ } else {
+ if (igets(map, ip, &s)< 0)
+ return 0;
+ ip->imm = s&0xffff;
+ }
+ break;
+ case Iwds: /* Operand-sized immediate (sign extended) */
+ if (ip->osize == 'L') {
+ if (igetl(map, ip, &ip->imm) < 0)
+ return 0;
+ } else {
+ if (igets(map, ip, &s)< 0)
+ return 0;
+ if (s&0x8000)
+ ip->imm = s|0xffff0000;
+ else
+ ip->imm = s&0xffff;
+ }
+ ip->jumptype = Iwds;
+ break;
+ case OA: /* literal 0x0a byte */
+ if (igetc(map, ip, &c) < 0)
+ return 0;
+ if (c != 0x0a)
+ goto badop;
+ break;
+ case R0: /* base register must be R0 */
+ if (ip->base != 0)
+ goto badop;
+ break;
+ case R1: /* base register must be R1 */
+ if (ip->base != 1)
+ goto badop;
+ break;
+ case RMB: /* R/M field with byte register (/r)*/
+ if (igetc(map, ip, &c) < 0)
+ return 0;
+ if (modrm(map, ip, c) < 0)
+ return 0;
+ ip->osize = 'B';
+ break;
+ case RM: /* R/M field with register (/r) */
+ if (igetc(map, ip, &c) < 0)
+ return 0;
+ if (modrm(map, ip, c) < 0)
+ return 0;
+ break;
+ case RMOPB: /* R/M field with op code (/digit) */
+ if (igetc(map, ip, &c) < 0)
+ return 0;
+ if (modrm(map, ip, c) < 0)
+ return 0;
+ c = ip->reg; /* secondary op code */
+ obase = (Optable*)op->proto;
+ ip->osize = 'B';
+ goto newop;
+ case RMOP: /* R/M field with op code (/digit) */
+ if (igetc(map, ip, &c) < 0)
+ return 0;
+ if (modrm(map, ip, c) < 0)
+ return 0;
+ c = ip->reg;
+ obase = (Optable*)op->proto;
+ goto newop;
+ case FRMOP: /* FP R/M field with op code (/digit) */
+ if (igetc(map, ip, &c) < 0)
+ return 0;
+ if (modrm(map, ip, c) < 0)
+ return 0;
+ if ((c&0xc0) == 0xc0)
+ c = ip->reg+8; /* 16 entry table */
+ else
+ c = ip->reg;
+ obase = (Optable*)op->proto;
+ goto newop;
+ case FRMEX: /* Extended FP R/M field with op code (/digit) */
+ if (igetc(map, ip, &c) < 0)
+ return 0;
+ if (modrm(map, ip, c) < 0)
+ return 0;
+ if ((c&0xc0) == 0xc0)
+ c = (c&0x3f)+8; /* 64-entry table */
+ else
+ c = ip->reg;
+ obase = (Optable*)op->proto;
+ goto newop;
+ case RMR: /* R/M register only (mod = 11) */
+ if (igetc(map, ip, &c) < 0)
+ return 0;
+ if ((c&0xc0) != 0xc0) {
+ werrstr("invalid R/M register: %x", c);
+ return 0;
+ }
+ if (modrm(map, ip, c) < 0)
+ return 0;
+ break;
+ case RMM: /* R/M register only (mod = 11) */
+ if (igetc(map, ip, &c) < 0)
+ return 0;
+ if ((c&0xc0) == 0xc0) {
+ werrstr("invalid R/M memory mode: %x", c);
+ return 0;
+ }
+ if (modrm(map, ip, c) < 0)
+ return 0;
+ break;
+ case PTR: /* Seg:Displacement addr (ptr16:16 or ptr16:32) */
+ if (ip->osize == 'L') {
+ if (igetl(map, ip, &ip->disp) < 0)
+ return 0;
+ } else {
+ if (igets(map, ip, &s)< 0)
+ return 0;
+ ip->disp = s&0xffff;
+ }
+ if (igets(map, ip, (ushort*)&ip->seg) < 0)
+ return 0;
+ ip->jumptype = PTR;
+ break;
+ case AUX: /* Multi-byte op code - Auxiliary table */
+ obase = (Optable*)op->proto;
+ if (igetc(map, ip, &c) < 0)
+ return 0;
+ goto newop;
+ case PRE: /* Instr Prefix */
+ ip->prefix = (char*)op->proto;
+ if (igetc(map, ip, &c) < 0)
+ return 0;
+ goto newop;
+ case SEG: /* Segment Prefix */
+ ip->segment = (char*)op->proto;
+ if (igetc(map, ip, &c) < 0)
+ return 0;
+ goto newop;
+ case OPOVER: /* Operand size override */
+ ip->osize = 'W';
+ if (igetc(map, ip, &c) < 0)
+ return 0;
+ goto newop;
+ case ADDOVER: /* Address size override */
+ ip->asize = 0;
+ if (igetc(map, ip, &c) < 0)
+ return 0;
+ goto newop;
+ case JUMP: /* mark instruction as JUMP or RET */
+ case RET:
+ ip->jumptype = op->operand[i];
+ break;
+ default:
+ werrstr("bad operand type %d", op->operand[i]);
+ return 0;
+ }
+ }
+ return op;
+}
+
+static void
+bprint(Instr *ip, char *fmt, ...)
+{
+ va_list arg;
+
+ va_start(arg, fmt);
+ ip->curr = vseprint(ip->curr, ip->end, fmt, arg);
+ va_end(arg);
+}
+
+/*
+ * if we want to call 16 bit regs AX,BX,CX,...
+ * and 32 bit regs EAX,EBX,ECX,... then
+ * change the defs of ANAME and ONAME to:
+ * #define ANAME(ip) ((ip->asize == 'E' ? "E" : "")
+ * #define ONAME(ip) ((ip)->osize == 'L' ? "E" : "")
+ */
+#define ANAME(ip) ""
+#define ONAME(ip) ""
+
+static char *reg[] = {
+[AX] "AX",
+[CX] "CX",
+[DX] "DX",
+[BX] "BX",
+[SP] "SP",
+[BP] "BP",
+[SI] "SI",
+[DI] "DI",
+};
+
+static char *breg[] = { "AL", "CL", "DL", "BL", "AH", "CH", "DH", "BH" };
+static char *sreg[] = { "ES", "CS", "SS", "DS", "FS", "GS" };
+
+static void
+plocal(Instr *ip)
+{
+ Symbol s;
+ char *name;
+ Loc l, li;
+
+ l.type = LOFFSET;
+ l.offset = ip->disp;
+ if(ip->base == SP)
+ l.reg = "SP";
+ else
+ l.reg = "BP";
+
+ li.type = LADDR;
+ li.addr = ip->addr;
+ if(findsym(li, CTEXT, &s) < 0)
+ goto raw;
+
+ name = nil;
+ if(ip->base==SP && lookuplsym(&s, FRAMENAME, &s) >= 0){
+ /* translate stack offset to offset from plan 9 frame pointer */
+ /* XXX not sure how to do this */
+ }
+
+ if(name==nil && findlsym(&s, l, &s) >= 0)
+ name = s.name;
+
+ if(name)
+ bprint(ip, "%s+", name);
+
+raw:
+ bprint(ip, "%lx(%s)", l.offset, l.reg);
+}
+
+static int
+isjmp(Instr *ip)
+{
+ switch(ip->jumptype){
+ case Iwds:
+ case Jbs:
+ case JUMP:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * This is too smart for its own good, but it really is nice
+ * to have accurate translations when debugging, and it
+ * helps us identify which code is different in binaries that
+ * are changed on sources.
+ */
+static int
+issymref(Instr *ip, Symbol *s, long w, long val)
+{
+ Symbol next, tmp;
+ long isstring, size;
+
+ if (isjmp(ip))
+ return 1;
+ if (s->class==CTEXT && w==0)
+ return 1;
+ if (s->class==CDATA) {
+ /* use first bss symbol (or "end") rather than edata */
+ if (s->name[0]=='e' && strcmp(s->name, "edata") == 0){
+ if((indexsym(s->index+1, &tmp) && loccmp(&tmp.loc, &s->loc)==0)
+ || (indexsym(s->index-1, &tmp) && loccmp(&tmp.loc, &s->loc)==0))
+ *s = tmp;
+ }
+ if (w == 0)
+ return 1;
+ for (next=*s; next.loc.addr==s->loc.addr; next=tmp)
+ if (!indexsym(next.index+1, &tmp))
+ break;
+ size = next.loc.addr - s->loc.addr;
+ if (w >= size)
+ return 0;
+ if (w > size-w)
+ w = size-w;
+ /* huge distances are usually wrong except in .string */
+ isstring = (s->name[0]=='.' && strcmp(s->name, ".string") == 0);
+ if (w > 8192 && !isstring)
+ return 0;
+ /* medium distances are tricky - look for constants */
+ /* near powers of two */
+ if ((val&(val-1)) == 0 || (val&(val+1)) == 0)
+ return 0;
+ return 1;
+ }
+ return 0;
+}
+
+static void
+immediate(Instr *ip, long val)
+{
+ Symbol s;
+ long w;
+ Loc l;
+
+ l.type = LADDR;
+ l.addr = val;
+ if (findsym(l, CANY, &s) >= 0) {
+ w = val - s.loc.addr;
+ if (w < 0)
+ w = -w;
+ if (issymref(ip, &s, w, val)) {
+ if (w)
+ bprint(ip, "%s+%lux(SB)", s.name, w);
+ else
+ bprint(ip, "%s(SB)", s.name);
+ return;
+ }
+ if (s.class==CDATA && indexsym(s.index+1, &s) >= 0) {
+ w = s.loc.addr - val;
+ if (w < 0)
+ w = -w;
+ if (w < 4096) {
+ bprint(ip, "%s-%lux(SB)", s.name, w);
+ return;
+ }
+ }
+ }
+ bprint(ip, "%lux", val);
+}
+
+static void
+pea(Instr *ip)
+{
+ if (ip->mod == 3) {
+ if (ip->osize == 'B')
+ bprint(ip, breg[(uchar)ip->base]);
+ else
+ bprint(ip, "%s%s", ANAME(ip), reg[(uchar)ip->base]);
+ return;
+ }
+ if (ip->segment)
+ bprint(ip, ip->segment);
+ if (ip->asize == 'E' && (ip->base == SP || ip->base == BP))
+ plocal(ip);
+ else {
+ if (ip->base < 0)
+ immediate(ip, ip->disp);
+ else
+ 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);
+}
+
+static void
+prinstr(Instr *ip, char *fmt)
+{
+ if (ip->prefix)
+ bprint(ip, "%s ", ip->prefix);
+ for (; *fmt && ip->curr < ip->end; fmt++) {
+ if (*fmt != '%')
+ *ip->curr++ = *fmt;
+ else switch(*++fmt)
+ {
+ case '%':
+ *ip->curr++ = '%';
+ break;
+ case 'A':
+ bprint(ip, "%s", ANAME(ip));
+ break;
+ case 'C':
+ bprint(ip, "CR%d", ip->reg);
+ break;
+ case 'D':
+ if (ip->reg < 4 || ip->reg == 6 || ip->reg == 7)
+ bprint(ip, "DR%d",ip->reg);
+ else
+ bprint(ip, "???");
+ break;
+ case 'I':
+ bprint(ip, "$");
+ immediate(ip, ip->imm2);
+ break;
+ case 'O':
+ bprint(ip,"%s", ONAME(ip));
+ break;
+ case 'i':
+ bprint(ip, "$");
+ immediate(ip,ip->imm);
+ break;
+ case 'R':
+ bprint(ip, "%s%s", ONAME(ip), reg[ip->reg]);
+ break;
+ case 'S':
+ bprint(ip, "%c", ip->osize);
+ break;
+ case 'T':
+ if (ip->reg == 6 || ip->reg == 7)
+ bprint(ip, "TR%d",ip->reg);
+ else
+ bprint(ip, "???");
+ break;
+ case 'X':
+ if (ip->osize == 'L')
+ bprint(ip,"CWDE");
+ else
+ bprint(ip, "CBW");
+ break;
+ case 'd':
+ bprint(ip,"%lux:%lux",ip->seg,ip->disp);
+ break;
+ case 'e':
+ pea(ip);
+ break;
+ case 'f':
+ bprint(ip, "F%d", ip->base);
+ break;
+ case 'g':
+ if (ip->reg < 6)
+ bprint(ip,"%s",sreg[ip->reg]);
+ else
+ bprint(ip,"???");
+ break;
+ case 'p':
+ immediate(ip, ip->imm+ip->addr+ip->n);
+ break;
+ case 'r':
+ if (ip->osize == 'B')
+ bprint(ip,"%s",breg[ip->reg]);
+ else
+ bprint(ip, reg[ip->reg]);
+ break;
+ case 'x':
+ if (ip->osize == 'L')
+ bprint(ip,"CDQ");
+ else
+ bprint(ip, "CWD");
+ break;
+ default:
+ bprint(ip, "%%%c", *fmt);
+ break;
+ }
+ }
+ *ip->curr = 0; /* there's always room for 1 byte */
+}
+
+static int
+i386das(Map *map, ulong pc, char modifier, char *buf, int n)
+{
+ Instr instr;
+ Optable *op;
+
+ USED(modifier);
+ op = mkinstr(map, &instr, pc);
+ if (op == 0) {
+ errstr(buf, n);
+ return -1;
+ }
+ instr.curr = buf;
+ instr.end = buf+n-1;
+ prinstr(&instr, op->proto);
+ return instr.n;
+}
+
+static int
+i386hexinst(Map *map, ulong pc, char *buf, int n)
+{
+ Instr instr;
+ int i;
+
+ if (mkinstr(map, &instr, pc) == 0) {
+ errstr(buf, n);
+ return -1;
+ }
+ for(i = 0; i < instr.n && n > 2; i++) {
+ _hexify(buf, instr.mem[i], 1);
+ buf += 2;
+ n -= 2;
+ }
+ *buf = 0;
+ return instr.n;
+}
+
+static int
+i386instlen(Map *map, ulong pc)
+{
+ Instr i;
+
+ if (mkinstr(map, &i, pc))
+ return i.n;
+ return -1;
+}
+
+static int
+i386foll(Map *map, Regs *regs, ulong pc, ulong *foll)
+{
+ Instr i;
+ Optable *op;
+ ushort s;
+ ulong addr;
+ u32int l;
+ int n;
+
+ op = mkinstr(map, &i, pc);
+ if (!op)
+ return -1;
+
+ n = 0;
+
+ switch(i.jumptype) {
+ case RET: /* RETURN or LEAVE */
+ case Iw: /* RETURN */
+ if (strcmp(op->proto, "LEAVE") == 0) {
+ if (lget4(map, regs, locindir("BP", 0), &l) < 0)
+ return -1;
+ } else if (lget4(map, regs, locindir(mach->sp, 0), &l) < 0)
+ return -1;
+ foll[0] = l;
+ return 1;
+ case Iwds: /* pc relative JUMP or CALL*/
+ case Jbs: /* pc relative JUMP or CALL */
+ foll[0] = pc+i.imm+i.n;
+ n = 1;
+ break;
+ case PTR: /* seg:displacement JUMP or CALL */
+ foll[0] = (i.seg<<4)+i.disp;
+ return 1;
+ case JUMP: /* JUMP or CALL EA */
+
+ if(i.mod == 3) {
+ if (rget(regs, reg[(uchar)i.base], &foll[0]) < 0)
+ return -1;
+ return 1;
+ }
+ /* calculate the effective address */
+ addr = i.disp;
+ if (i.base >= 0) {
+ if (lget4(map, regs, locindir(reg[(uchar)i.base], 0), &l) < 0)
+ return -1;
+ addr += l;
+ }
+ if (i.index >= 0) {
+ if (lget4(map, regs, locindir(reg[(uchar)i.index], 0), &l) < 0)
+ return -1;
+ addr += l*(1<<i.ss);
+ }
+ /* now retrieve a seg:disp value at that address */
+ if (get2(map, addr, &s) < 0) /* seg */
+ return -1;
+ foll[0] = s<<4;
+ addr += 2;
+ if (i.asize == 'L') {
+ if (get4(map, addr, &l) < 0) /* disp32 */
+ return -1;
+ foll[0] += l;
+ } else { /* disp16 */
+ if (get2(map, addr, &s) < 0)
+ return -1;
+ foll[0] += s;
+ }
+ return 1;
+ default:
+ break;
+ }
+ if (strncmp(op->proto,"JMP", 3) == 0 || strncmp(op->proto,"CALL", 4) == 0)
+ return 1;
+ foll[n++] = pc+i.n;
+ return n;
+}
diff --git a/src/libmach/macho.c b/src/libmach/macho.c
new file mode 100644
index 00000000..641db598
--- /dev/null
+++ b/src/libmach/macho.c
@@ -0,0 +1,128 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "macho.h"
+
+/*
+http://www.channelu.com/NeXT/NeXTStep/3.3/nd/DevTools/14_MachO/MachO.htmld/
+*/
+
+Macho*
+machoopen(char *name)
+{
+ int fd;
+ Macho *m;
+
+ if((fd = open(name, OREAD)) < 0)
+ return nil;
+ m = machoinit(fd);
+ if(m == nil)
+ close(fd);
+ return m;
+}
+
+static int
+unpackseg(uchar *p, Macho *m, MachoCmd *c, uint type, uint sz)
+{
+ u32int (*e4)(uchar*);
+
+ e4 = m->e4;
+
+ c->type = type;
+ c->size = sz;
+ switch(type){
+ default:
+ return -1;
+ case MachoCmdSegment:
+ if(sz < 56)
+ return -1;
+ strecpy(c->seg.name, c->seg.name+sizeof c->seg.name, (char*)p+8);
+ c->seg.vmaddr = e4(p+24);
+ c->seg.vmsize = e4(p+28);
+ c->seg.fileoff = e4(p+32);
+ c->seg.filesz = e4(p+36);
+ c->seg.maxprot = e4(p+40);
+ c->seg.initprot = e4(p+44);
+ c->seg.nsect = e4(p+48);
+ c->seg.flags = e4(p+52);
+ break;
+ case MachoCmdSymtab:
+ if(sz < 24)
+ return -1;
+ c->sym.symoff = e4(p+8);
+ c->sym.nsyms = e4(p+12);
+ c->sym.stroff = e4(p+16);
+ c->sym.strsize = e4(p+20);
+ break;
+ }
+ return 0;
+}
+
+
+Macho*
+machoinit(int fd)
+{
+ int i;
+ uchar hdr[7*4], *cmdp;
+ u32int (*e4)(uchar*);
+ ulong ncmd, cmdsz, ty, sz, off;
+ Macho *m;
+
+ if(seek(fd, 0, 0) < 0 || readn(fd, hdr, sizeof hdr) != sizeof hdr)
+ return nil;
+
+ if(beload4(hdr) == 0xFEEDFACE)
+ e4 = beload4;
+ else if(leload4(hdr) == 0xFEEDFACE)
+ e4 = leload4;
+ else{
+ werrstr("bad magic - not mach-o file");
+ return nil;
+ }
+
+ ncmd = e4(hdr+4*4);
+ cmdsz = e4(hdr+5*4);
+ if(ncmd > 0x10000 || cmdsz >= 0x01000000){
+ werrstr("implausible mach-o header ncmd=%lud cmdsz=%lud", ncmd, cmdsz);
+ return nil;
+ }
+
+ m = mallocz(sizeof(*m)+ncmd*sizeof(MachoCmd)+cmdsz, 1);
+ if(m == nil)
+ return nil;
+
+ m->fd = fd;
+ m->e4 = e4;
+ m->cputype = e4(hdr+1*4);
+ m->subcputype = e4(hdr+2*4);
+ m->filetype = e4(hdr+3*4);
+ m->ncmd = ncmd;
+ m->flags = e4(hdr+6*4);
+
+ m->cmd = (MachoCmd*)(m+1);
+ off = sizeof hdr;
+ cmdp = (uchar*)(m->cmd+ncmd);
+ if(readn(fd, cmdp, cmdsz) != cmdsz){
+ werrstr("reading cmds: %r");
+ free(m);
+ return nil;
+ }
+
+ for(i=0; i<ncmd; i++){
+ ty = e4(cmdp);
+ sz = e4(cmdp+4);
+ m->cmd[i].off = off;
+ unpackseg(cmdp, m, &m->cmd[i], ty, sz);
+ cmdp += sz;
+ off += sz;
+ }
+
+ return m;
+}
+
+void
+machoclose(Macho *m)
+{
+ close(m->fd);
+ free(m);
+}
diff --git a/src/libmach/macho.h b/src/libmach/macho.h
new file mode 100644
index 00000000..377e0d2b
--- /dev/null
+++ b/src/libmach/macho.h
@@ -0,0 +1,71 @@
+typedef struct Macho Macho;
+typedef struct MachoCmd MachoCmd;
+
+enum
+{
+ MachoCpuVax = 1,
+ MachoCpu68000 = 6,
+ MachoCpu386 = 7,
+ MachoCpuMips = 8,
+ MachoCpu98000 = 10,
+ MachoCpuHppa = 11,
+ MachoCpuArm = 12,
+ MachoCpu88000 = 13,
+ MachoCpuSparc = 14,
+ MachoCpu860 = 15,
+ MachoCpuAlpha = 16,
+ MachoCpuPower = 18,
+
+ MachoCmdSegment = 1,
+ MachoCmdSymtab = 2,
+ MachoCmdSymseg = 3,
+ MachoCmdThread = 4,
+
+ MachoFileObject = 1,
+ MachoFileExecutable = 2,
+ MachoFileFvmlib = 3,
+ MachoFileCore = 4,
+ MachoFilePreload = 5,
+};
+
+struct MachoCmd
+{
+ int type;
+ ulong off;
+ ulong size;
+ struct {
+ char name[16+1];
+ ulong vmaddr;
+ ulong vmsize;
+ ulong fileoff;
+ ulong filesz;
+ ulong maxprot;
+ ulong initprot;
+ ulong nsect;
+ ulong flags;
+ } seg;
+ struct {
+ ulong symoff;
+ ulong nsyms;
+ ulong stroff;
+ ulong strsize;
+ } sym;
+};
+
+struct Macho
+{
+ int fd;
+ uint cputype;
+ uint subcputype;
+ ulong filetype;
+ ulong flags;
+ MachoCmd *cmd;
+ uint ncmd;
+ u32int (*e4)(uchar*);
+ int (*coreregs)(Macho*, uchar**);
+};
+
+Macho *machoopen(char*);
+Macho *machoinit(int);
+void machoclose(Macho*);
+int coreregsmachopower(Macho*, uchar**);
diff --git a/src/libmach/machocorepower.c b/src/libmach/machocorepower.c
new file mode 100644
index 00000000..0eb3761d
--- /dev/null
+++ b/src/libmach/machocorepower.c
@@ -0,0 +1,173 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "macho.h"
+#include "uregpower.h"
+
+enum
+{
+ ThreadState = 1,
+ FloatState,
+ ExceptionState,
+ VectorState,
+ ThreadState64,
+ ExceptionState64,
+ ThreadStateNone,
+};
+
+typedef struct Lreg Lreg;
+typedef struct Lflt Lflt;
+typedef struct Lexc Lexc;
+
+struct Lreg
+{
+ u32int srr0;
+ u32int srr1;
+ u32int r0;
+ u32int r1;
+ u32int r2;
+ u32int r3;
+ u32int r4;
+ u32int r5;
+ u32int r6;
+ u32int r7;
+ u32int r8;
+ u32int r9;
+ u32int r10;
+ u32int r11;
+ u32int r12;
+ u32int r13;
+ u32int r14;
+ u32int r15;
+ u32int r16;
+ u32int r17;
+ u32int r18;
+ u32int r19;
+ u32int r20;
+ u32int r21;
+ u32int r22;
+ u32int r23;
+ u32int r24;
+ u32int r25;
+ u32int r26;
+ u32int r27;
+ u32int r28;
+ u32int r29;
+ u32int r30;
+ u32int r31;
+
+ u32int cr;
+ u32int xer;
+ u32int lr;
+ u32int ctr;
+ u32int mq;
+
+ u32int vrsave;
+};
+
+struct Lflt
+{
+ u32int fpregs[32*2]; /* 32 doubles */
+ u32int fpscr[2];
+
+};
+
+struct Lexc
+{
+ u32int dar;
+ u32int dsisr;
+ u32int exception;
+ u32int pad0;
+ u32int pad1[4];
+};
+
+static void
+lreg2ureg(Lreg *l, Ureg *u)
+{
+ u->pc = l->srr0;
+ u->srr1 = l->srr1;
+ u->lr = l->lr;
+ u->cr = l->cr;
+ u->xer = l->xer;
+ u->ctr = l->ctr;
+ u->vrsave = l->vrsave;
+ memmove(&u->r0, &l->r0, 32*4);
+}
+
+static void
+lexc2ureg(Lexc *l, Ureg *u)
+{
+ u->cause = l->exception;
+ u->dar = l->dar;
+ u->dsisr = l->dsisr;
+}
+
+static uchar*
+load(int fd, ulong off, int size)
+{
+ uchar *a;
+
+ a = malloc(size);
+ if(a == nil)
+ return nil;
+ if(seek(fd, off, 0) < 0 || readn(fd, a, size) != size){
+ free(a);
+ return nil;
+ }
+ return a;
+}
+
+int
+coreregsmachopower(Macho *m, uchar **up)
+{
+ int i, havereg, haveexc;
+ uchar *a, *p, *nextp;
+ Ureg *u;
+ ulong flavor, count;
+ MachoCmd *c;
+
+ *up = nil;
+ for(i=0; i<m->ncmd; i++)
+ if(m->cmd[i].type == MachoCmdThread)
+ break;
+ if(i == m->ncmd){
+ werrstr("no registers found");
+ return -1;
+ }
+
+ c = &m->cmd[i];
+ a = load(m->fd, c->off, c->size);
+ if(a == nil)
+ return -1;
+
+ if((u = mallocz(sizeof(Ureg), 1)) == nil){
+ free(a);
+ return -1;
+ }
+
+ havereg = haveexc = 0;
+ for(p=a+8; p<a+c->size; p=nextp){
+ flavor = m->e4(p);
+ count = m->e4(p+4);
+ nextp = p+8+count*4;
+ if(flavor == ThreadState && count*4 == sizeof(Lreg)){
+ havereg = 1;
+ lreg2ureg((Lreg*)(p+8), u);
+ }
+ if(flavor == ExceptionState && count*4 == sizeof(Lexc)){
+ haveexc = 1;
+ lexc2ureg((Lexc*)(p+8), u);
+ }
+ }
+ free(a);
+ if(!havereg){
+ werrstr("no registers found");
+ free(u);
+ return -1;
+ }
+ if(!haveexc)
+ fprint(2, "warning: no exception state in core file registers\n");
+ *up = (uchar*)u;
+ return sizeof(*u);
+}
+
diff --git a/src/libmach/machodump.c b/src/libmach/machodump.c
new file mode 100644
index 00000000..181061aa
--- /dev/null
+++ b/src/libmach/machodump.c
@@ -0,0 +1,93 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "stabs.h"
+#include "macho.h"
+
+void
+usage(void)
+{
+ fprint(2, "usage: machodump file list\n");
+ fprint(2, " machodump file stabs\n");
+ exits("usage");
+}
+
+uchar*
+load(int fd, ulong off, int size)
+{
+ uchar *a;
+
+ a = malloc(size);
+print("malloc %d -> %p\n", size, a);
+ if(a == nil)
+ sysfatal("malloc: %r");
+ if(seek(fd, off, 0) < 0)
+ sysfatal("seek %lud: %r", off);
+ if(readn(fd, a, size) != size)
+ sysfatal("readn: %r");
+ return a;
+}
+
+void
+main(int argc, char **argv)
+{
+ int i;
+ Macho *m;
+
+ ARGBEGIN{
+ default:
+ usage();
+ }ARGEND
+
+ if(argc < 2)
+ usage();
+
+ if((m = machoopen(argv[0])) == nil)
+ sysfatal("machoopen %s: %r", argv[0]);
+
+ if(strcmp(argv[1], "stabs") == 0){
+ Stab stabs;
+ StabSym sym;
+
+ for(i=0; i<m->ncmd; i++){
+ if(m->cmd[i].type == MachoCmdSymtab){
+ stabs.stabbase = load(m->fd, m->cmd[i].sym.symoff, m->cmd[i].sym.nsyms*16);
+ stabs.stabsize = m->cmd[i].sym.nsyms*16;
+ stabs.strbase = load(m->fd, m->cmd[i].sym.stroff, m->cmd[i].sym.strsize);
+ stabs.strsize = m->cmd[i].sym.strsize;
+ stabs.e4 = m->e4;
+ stabs.e2 = (m->e4 == beload4 ? beload2 : leload2);
+ print("cmd%d: %p %ud %p %ud\n", i, stabs.stabbase, stabs.stabsize, stabs.strbase, stabs.strsize);
+ for(i=0; stabsym(&stabs, i, &sym) >= 0; i++)
+ print("%s type 0x%x other %d desc %d value 0x%lux\n",
+ sym.name, sym.type, sym.other, (int)sym.desc, (ulong)sym.value);
+ print("err at %d: %r\n", i);
+ }
+ }
+ }
+ else if(strcmp(argv[1], "list") == 0){
+ print("macho cpu %ud sub %ud filetype %lud flags %lud\n",
+ m->cputype, m->subcputype, m->filetype, m->flags);
+ for(i=0; i<m->ncmd; i++){
+ switch(m->cmd[i].type){
+ case MachoCmdSymtab:
+ print("cmd%d: symtab %lud+%lud %lud+%lud\n", i,
+ m->cmd[i].sym.symoff, m->cmd[i].sym.nsyms,
+ m->cmd[i].sym.stroff, m->cmd[i].sym.strsize);
+ break;
+ case MachoCmdSegment:
+ print("cmd%d: segment %s vm 0x%lux+0x%lux file 0x%lux+0x%lux prot 0x%lux/0x%lux ns %d flags 0x%lux\n", i,
+ m->cmd[i].seg.name, m->cmd[i].seg.vmaddr, m->cmd[i].seg.vmsize,
+ m->cmd[i].seg.fileoff, m->cmd[i].seg.filesz, m->cmd[i].seg.maxprot,
+ m->cmd[i].seg.initprot, m->cmd[i].seg.nsect, m->cmd[i].seg.flags);
+ break;
+ default:
+ print("cmd%d: type %d offset %lud\n", i, m->cmd[i].type, m->cmd[i].off);
+ break;
+ }
+ }
+ }
+ else
+ usage();
+ exits(0);
+}
diff --git a/src/libmach/machpower.c b/src/libmach/machpower.c
new file mode 100644
index 00000000..d223f4f5
--- /dev/null
+++ b/src/libmach/machpower.c
@@ -0,0 +1,1409 @@
+/*
+ * PowerPC definition
+ * forsyth@plan9.cs.york.ac.uk
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "uregpower.h"
+#include <mach.h>
+
+/*
+ * PowerPC-specific debugger interface
+ * forsyth@plan9.cs.york.ac.uk
+ */
+
+static char *powerexcep(Map*, Regs*);
+static int powerfoll(Map*, Regs*, ulong, ulong*);
+static int powerdas(Map*, ulong, char, char*, int);
+static int powerinstlen(Map*, ulong);
+static int powerhexinst(Map*, ulong, char*, int);
+
+static char *excname[] =
+{
+ "reserved 0",
+ "system reset",
+ "machine check",
+ "data access",
+ "instruction access",
+ "external interrupt",
+ "alignment",
+ "program exception",
+ "floating-point unavailable",
+ "decrementer",
+ "i/o controller interface error",
+ "reserved B",
+ "system call",
+ "trace trap",
+ "floating point assist",
+ "reserved",
+ "ITLB miss",
+ "DTLB load miss",
+ "DTLB store miss",
+ "instruction address breakpoint"
+ "SMI interrupt"
+ "reserved 15",
+ "reserved 16",
+ "reserved 17",
+ "reserved 18",
+ "reserved 19",
+ "reserved 1A",
+ /* the following are made up on a program exception */
+ "floating point exception", /* FPEXC */
+ "illegal instruction",
+ "privileged instruction",
+ "trap",
+ "illegal operation",
+};
+
+static char*
+powerexcep(Map *map, Regs *regs)
+{
+ ulong c;
+ static char buf[32];
+
+ if(rget(regs, "CAUSE", &c) < 0)
+ return "no cause register";
+ c >>= 8;
+ if(c < nelem(excname))
+ return excname[c];
+ sprint(buf, "unknown trap #%lux", c);
+ return buf;
+}
+
+/*
+ * disassemble PowerPC opcodes
+ */
+
+#define REGSP 1 /* should come from q.out.h, but there's a clash */
+#define REGSB 2
+
+//static char FRAMENAME[] = ".frame";
+
+static Map *mymap;
+
+/*
+ * ibm conventions for these: bit 0 is top bit
+ * from table 10-1
+ */
+typedef struct {
+ uchar aa; /* bit 30 */
+ uchar crba; /* bits 11-15 */
+ uchar crbb; /* bits 16-20 */
+ long bd; /* bits 16-29 */
+ uchar crfd; /* bits 6-8 */
+ uchar crfs; /* bits 11-13 */
+ uchar bi; /* bits 11-15 */
+ uchar bo; /* bits 6-10 */
+ uchar crbd; /* bits 6-10 */
+ union {
+ short d; /* bits 16-31 */
+ short simm;
+ ushort uimm;
+ };
+ uchar fm; /* bits 7-14 */
+ uchar fra; /* bits 11-15 */
+ uchar frb; /* bits 16-20 */
+ uchar frc; /* bits 21-25 */
+ uchar frs; /* bits 6-10 */
+ uchar frd; /* bits 6-10 */
+ uchar crm; /* bits 12-19 */
+ long li; /* bits 6-29 || b'00' */
+ uchar lk; /* bit 31 */
+ uchar mb; /* bits 21-25 */
+ uchar me; /* bits 26-30 */
+ uchar nb; /* bits 16-20 */
+ uchar op; /* bits 0-5 */
+ uchar oe; /* bit 21 */
+ uchar ra; /* bits 11-15 */
+ uchar rb; /* bits 16-20 */
+ uchar rc; /* bit 31 */
+ union {
+ uchar rs; /* bits 6-10 */
+ uchar rd;
+ };
+ uchar sh; /* bits 16-20 */
+ ushort spr; /* bits 11-20 */
+ uchar to; /* bits 6-10 */
+ uchar imm; /* bits 16-19 */
+ ushort xo; /* bits 21-30, 22-30, 26-30, or 30 (beware) */
+ long immediate;
+ long w0;
+ long w1;
+ ulong addr; /* pc of instruction */
+ short target;
+ char *curr; /* current fill level in output buffer */
+ char *end; /* end of buffer */
+ int size; /* number of longs in instr */
+ char *err; /* errmsg */
+} Instr;
+
+#define IBF(v,a,b) (((ulong)(v)>>(32-(b)-1)) & ~(~0L<<(((b)-(a)+1))))
+#define IB(v,b) IBF((v),(b),(b))
+
+static void
+bprint(Instr *i, char *fmt, ...)
+{
+ va_list arg;
+
+ va_start(arg, fmt);
+ i->curr = vseprint(i->curr, i->end, fmt, arg);
+ va_end(arg);
+}
+
+static int
+decode(ulong pc, Instr *i)
+{
+ u32int w;
+
+ if (get4(mymap, pc, &w) < 0) {
+ werrstr("can't read instruction: %r");
+ return -1;
+ }
+ i->aa = IB(w, 30);
+ i->crba = IBF(w, 11, 15);
+ i->crbb = IBF(w, 16, 20);
+ i->bd = IBF(w, 16, 29)<<2;
+ if(i->bd & 0x8000)
+ i->bd |= ~0L<<16;
+ i->crfd = IBF(w, 6, 8);
+ i->crfs = IBF(w, 11, 13);
+ i->bi = IBF(w, 11, 15);
+ i->bo = IBF(w, 6, 10);
+ i->crbd = IBF(w, 6, 10);
+ i->uimm = IBF(w, 16, 31); /* also d, simm */
+ i->fm = IBF(w, 7, 14);
+ i->fra = IBF(w, 11, 15);
+ i->frb = IBF(w, 16, 20);
+ i->frc = IBF(w, 21, 25);
+ i->frs = IBF(w, 6, 10);
+ i->frd = IBF(w, 6, 10);
+ i->crm = IBF(w, 12, 19);
+ i->li = IBF(w, 6, 29)<<2;
+ if(IB(w, 6))
+ i->li |= ~0<<25;
+ i->lk = IB(w, 31);
+ i->mb = IBF(w, 21, 25);
+ i->me = IBF(w, 26, 30);
+ i->nb = IBF(w, 16, 20);
+ i->op = IBF(w, 0, 5);
+ i->oe = IB(w, 21);
+ i->ra = IBF(w, 11, 15);
+ i->rb = IBF(w, 16, 20);
+ i->rc = IB(w, 31);
+ i->rs = IBF(w, 6, 10); /* also rd */
+ i->sh = IBF(w, 16, 20);
+ i->spr = IBF(w, 11, 20);
+ i->to = IBF(w, 6, 10);
+ i->imm = IBF(w, 16, 19);
+ i->xo = IBF(w, 21, 30); /* bits 21-30, 22-30, 26-30, or 30 (beware) */
+ i->immediate = i->simm;
+ if(i->op == 15)
+ i->immediate <<= 16;
+ i->w0 = w;
+ i->target = -1;
+ i->addr = pc;
+ i->size = 1;
+ return 1;
+}
+
+static int
+mkinstr(ulong pc, Instr *i)
+{
+ Instr x;
+
+ if(decode(pc, i) < 0)
+ return -1;
+ /*
+ * combine ADDIS/ORI (CAU/ORIL) into MOVW
+ */
+ if (i->op == 15 && i->ra==0) {
+ if(decode(pc+4, &x) < 0)
+ return -1;
+ if (x.op == 24 && x.rs == x.ra && x.ra == i->rd) {
+ i->immediate |= (x.immediate & 0xFFFF);
+ i->w1 = x.w0;
+ i->target = x.rd;
+ i->size++;
+ return 1;
+ }
+ }
+ return 1;
+}
+
+static int
+plocal(Instr *i)
+{
+ Symbol s;
+ Loc l, li;
+
+ l.type = LOFFSET;
+ l.offset = i->immediate;
+ l.reg = "SP";
+
+ li.type = LADDR;
+ li.addr = i->addr;
+ if (findsym(li, CTEXT, &s)<0 || findlsym(&s, l, &s)<0)
+ return -1;
+ bprint(i, "%s%+ld(SP)", s.name, (long)i->immediate);
+ return 0;
+}
+
+static int
+pglobal(Instr *i, long off, int anyoff, char *reg)
+{
+ Symbol s, s2;
+ u32int off1;
+ Loc l;
+
+ l.type = LADDR;
+ l.addr = off;
+ if(findsym(l, CANY, &s)>=0 && s.loc.type==LADDR &&
+ s.loc.addr-off < 4096 &&
+ (s.class == CDATA || s.class == CTEXT)) {
+ if(off==s.loc.addr && s.name[0]=='$'){
+ off1 = 0;
+ get4(mymap, s.loc.addr, &off1);
+ l.addr = off1;
+ if(off1 && findsym(l, CANY, &s2)>=0 && s2.loc.type==LADDR && s2.loc.addr == off1){
+ bprint(i, "$%s%s", s2.name, reg);
+ return 1;
+ }
+ }
+ bprint(i, "%s", s.name);
+ if (s.loc.addr != off)
+ bprint(i, "+%lux", off-s.loc.addr);
+ bprint(i, reg);
+ return 1;
+ }
+ if(!anyoff)
+ return 0;
+ bprint(i, "%lux%s", off, reg);
+ return 1;
+}
+
+static void
+address(Instr *i)
+{
+ if (i->ra == REGSP && plocal(i) >= 0)
+ return;
+ if (i->ra == REGSB && mach->sb && pglobal(i, mach->sb+i->immediate, 0, "(SB)") >= 0)
+ return;
+ if(i->simm < 0)
+ bprint(i, "-%lx(R%d)", -i->simm, i->ra);
+ else
+ bprint(i, "%lux(R%d)", i->immediate, i->ra);
+}
+
+static char *tcrbits[] = {"LT", "GT", "EQ", "VS"};
+static char *fcrbits[] = {"GE", "LE", "NE", "VC"};
+
+typedef struct Opcode Opcode;
+
+struct Opcode {
+ uchar op;
+ ushort xo;
+ ushort xomask;
+ char *mnemonic;
+ void (*f)(Opcode *, Instr *);
+ char *ken;
+ int flags;
+};
+
+static void format(char *, Instr *, char *);
+
+static void
+branch(Opcode *o, Instr *i)
+{
+ char buf[8];
+ int bo, bi;
+
+ bo = i->bo & ~1; /* ignore prediction bit */
+ if(bo==4 || bo==12 || bo==20) { /* simple forms */
+ if(bo != 20) {
+ bi = i->bi&3;
+ sprint(buf, "B%s%%L", bo==12? tcrbits[bi]: fcrbits[bi]);
+ format(buf, i, 0);
+ bprint(i, "\t");
+ if(i->bi > 4)
+ bprint(i, "CR(%d),", i->bi/4);
+ } else
+ format("BR%L\t", i, 0);
+ if(i->op == 16)
+ format(0, i, "%J");
+ else if(i->op == 19 && i->xo == 528)
+ format(0, i, "(CTR)");
+ else if(i->op == 19 && i->xo == 16)
+ format(0, i, "(LR)");
+ } else
+ format(o->mnemonic, i, o->ken);
+}
+
+static void
+addi(Opcode *o, Instr *i)
+{
+ if (i->op==14 && i->ra == 0)
+ format("MOVW", i, "%i,R%d");
+ else if (i->ra == REGSB) {
+ bprint(i, "MOVW\t$");
+ address(i);
+ bprint(i, ",R%d", i->rd);
+ } else if(i->op==14 && i->simm < 0) {
+ bprint(i, "SUB\t$%d,R%d", -i->simm, i->ra);
+ if(i->rd != i->ra)
+ bprint(i, ",R%d", i->rd);
+ } else if(i->ra == i->rd) {
+ format(o->mnemonic, i, "%i");
+ bprint(i, ",R%d", i->rd);
+ } else
+ format(o->mnemonic, i, o->ken);
+}
+
+static void
+addis(Opcode *o, Instr *i)
+{
+ long v;
+
+ v = i->immediate;
+ if (i->op==15 && i->ra == 0)
+ bprint(i, "MOVW\t$%lux,R%d", v, i->rd);
+ else if (i->op==15 && i->ra == REGSB) {
+ bprint(i, "MOVW\t$");
+ address(i);
+ bprint(i, ",R%d", i->rd);
+ } else if(i->op==15 && v < 0) {
+ bprint(i, "SUB\t$%d,R%d", -v, i->ra);
+ if(i->rd != i->ra)
+ bprint(i, ",R%d", i->rd);
+ } else {
+ format(o->mnemonic, i, 0);
+ bprint(i, "\t$%ld,R%d", v, i->ra);
+ if(i->rd != i->ra)
+ bprint(i, ",R%d", i->rd);
+ }
+}
+
+static void
+andi(Opcode *o, Instr *i)
+{
+ if (i->ra == i->rs)
+ format(o->mnemonic, i, "%I,R%d");
+ else
+ format(o->mnemonic, i, o->ken);
+}
+
+static void
+gencc(Opcode *o, Instr *i)
+{
+ format(o->mnemonic, i, o->ken);
+}
+
+static void
+gen(Opcode *o, Instr *i)
+{
+ format(o->mnemonic, i, o->ken);
+ if (i->rc)
+ bprint(i, " [illegal Rc]");
+}
+
+static void
+ldx(Opcode *o, Instr *i)
+{
+ if(i->ra == 0)
+ format(o->mnemonic, i, "(R%b),R%d");
+ else
+ format(o->mnemonic, i, "(R%b+R%a),R%d");
+ if(i->rc)
+ bprint(i, " [illegal Rc]");
+}
+
+static void
+stx(Opcode *o, Instr *i)
+{
+ if(i->ra == 0)
+ format(o->mnemonic, i, "R%d,(R%b)");
+ else
+ format(o->mnemonic, i, "R%d,(R%b+R%a)");
+ if(i->rc && i->xo != 150)
+ bprint(i, " [illegal Rc]");
+}
+
+static void
+fldx(Opcode *o, Instr *i)
+{
+ if(i->ra == 0)
+ format(o->mnemonic, i, "(R%b),F%d");
+ else
+ format(o->mnemonic, i, "(R%b+R%a),F%d");
+ if(i->rc)
+ bprint(i, " [illegal Rc]");
+}
+
+static void
+fstx(Opcode *o, Instr *i)
+{
+ if(i->ra == 0)
+ format(o->mnemonic, i, "F%d,(R%b)");
+ else
+ format(o->mnemonic, i, "F%d,(R%b+R%a)");
+ if(i->rc)
+ bprint(i, " [illegal Rc]");
+}
+
+static void
+dcb(Opcode *o, Instr *i)
+{
+ if(i->ra == 0)
+ format(o->mnemonic, i, "(R%b)");
+ else
+ format(o->mnemonic, i, "(R%b+R%a)");
+ if(i->rd)
+ bprint(i, " [illegal Rd]");
+ if(i->rc)
+ bprint(i, " [illegal Rc]");
+}
+
+static void
+lw(Opcode *o, Instr *i, char r)
+{
+ bprint(i, "%s\t", o->mnemonic);
+ address(i);
+ bprint(i, ",%c%d", r, i->rd);
+}
+
+static void
+load(Opcode *o, Instr *i)
+{
+ lw(o, i, 'R');
+}
+
+static void
+fload(Opcode *o, Instr *i)
+{
+ lw(o, i, 'F');
+}
+
+static void
+sw(Opcode *o, Instr *i, char r)
+{
+ char *m;
+ Symbol s;
+ Loc l;
+
+ m = o->mnemonic;
+ if (i->rs == REGSP) {
+ l.type = LADDR;
+ l.addr = i->addr;
+ if (findsym(l, CTEXT, &s)>=0) {
+ l.type = LOFFSET;
+ l.reg = "SP";
+ l.offset = i->immediate;
+ if (findlsym(&s, l, &s) >= 0) {
+ bprint(i, "%s\t%c%d,%s-%d(SP)", m, r, i->rd,
+ s.name, i->immediate);
+ return;
+ }
+ }
+ }
+ if (i->rs == REGSB && mach->sb) {
+ bprint(i, "%s\t%c%d,", m, r, i->rd);
+ address(i);
+ return;
+ }
+ if (r == 'F')
+ format(m, i, "F%d,%l");
+ else
+ format(m, i, o->ken);
+}
+
+static void
+store(Opcode *o, Instr *i)
+{
+ sw(o, i, 'R');
+}
+
+static void
+fstore(Opcode *o, Instr *i)
+{
+ sw(o, i, 'F');
+}
+
+static void
+shifti(Opcode *o, Instr *i)
+{
+ if (i->ra == i->rs)
+ format(o->mnemonic, i, "$%k,R%a");
+ else
+ format(o->mnemonic, i, o->ken);
+}
+
+static void
+shift(Opcode *o, Instr *i)
+{
+ if (i->ra == i->rs)
+ format(o->mnemonic, i, "R%b,R%a");
+ else
+ format(o->mnemonic, i, o->ken);
+}
+
+static void
+add(Opcode *o, Instr *i)
+{
+ if (i->rd == i->ra)
+ format(o->mnemonic, i, "R%b,R%d");
+ else if (i->rd == i->rb)
+ format(o->mnemonic, i, "R%a,R%d");
+ else
+ format(o->mnemonic, i, o->ken);
+}
+
+static void
+sub(Opcode *o, Instr *i)
+{
+ format(o->mnemonic, i, 0);
+ bprint(i, "\t");
+ if(i->op == 31) {
+ bprint(i, "\tR%d,R%d", i->ra, i->rb); /* subtract Ra from Rb */
+ if(i->rd != i->rb)
+ bprint(i, ",R%d", i->rd);
+ } else
+ bprint(i, "\tR%d,$%d,R%d", i->ra, i->simm, i->rd);
+}
+
+#define div qdiv
+
+static void
+div(Opcode *o, Instr *i)
+{
+ format(o->mnemonic, i, 0);
+ if(i->op == 31)
+ bprint(i, "\tR%d,R%d", i->rb, i->ra);
+ else
+ bprint(i, "\t$%d,R%d", i->simm, i->ra);
+ if(i->ra != i->rd)
+ bprint(i, ",R%d", i->rd);
+}
+
+static void
+and(Opcode *o, Instr *i)
+{
+ if (i->op == 31) {
+ /* Rb,Rs,Ra */
+ if (i->ra == i->rs)
+ format(o->mnemonic, i, "R%b,R%a");
+ else if (i->ra == i->rb)
+ format(o->mnemonic, i, "R%s,R%a");
+ else
+ format(o->mnemonic, i, o->ken);
+ } else {
+ /* imm,Rs,Ra */
+ if (i->ra == i->rs)
+ format(o->mnemonic, i, "%I,R%a");
+ else
+ format(o->mnemonic, i, o->ken);
+ }
+}
+
+static void
+or(Opcode *o, Instr *i)
+{
+ if (i->op == 31) {
+ /* Rb,Rs,Ra */
+ if (i->rs == 0 && i->ra == 0 && i->rb == 0)
+ format("NOP", i, 0);
+ else if (i->rs == i->rb)
+ format("MOVW", i, "R%b,R%a");
+ else
+ and(o, i);
+ } else
+ and(o, i);
+}
+
+static void
+shifted(Opcode *o, Instr *i)
+{
+ format(o->mnemonic, i, 0);
+ bprint(i, "\t$%lux,", (ulong)i->uimm<<16);
+ if (i->rs == i->ra)
+ bprint(i, "R%d", i->ra);
+ else
+ bprint(i, "R%d,R%d", i->rs, i->ra);
+}
+
+static void
+neg(Opcode *o, Instr *i)
+{
+ if (i->rd == i->ra)
+ format(o->mnemonic, i, "R%d");
+ else
+ format(o->mnemonic, i, o->ken);
+}
+
+static char ir2[] = "R%a,R%d"; /* reverse of IBM order */
+static char ir3[] = "R%b,R%a,R%d";
+static char ir3r[] = "R%a,R%b,R%d";
+static char il3[] = "R%b,R%s,R%a";
+static char il2u[] = "%I,R%a,R%d";
+static char il3s[] = "$%k,R%s,R%a";
+static char il2[] = "R%s,R%a";
+static char icmp3[] = "R%a,R%b,%D";
+static char cr3op[] = "%b,%a,%d";
+static char ir2i[] = "%i,R%a,R%d";
+static char fp2[] = "F%b,F%d";
+static char fp3[] = "F%b,F%a,F%d";
+static char fp3c[] = "F%c,F%a,F%d";
+static char fp4[] = "F%a,F%c,F%b,F%d";
+static char fpcmp[] = "F%a,F%b,%D";
+static char ldop[] = "%l,R%d";
+static char stop[] = "R%d,%l";
+static char fldop[] = "%l,F%d";
+static char fstop[] = "F%d,%l";
+static char rlim[] = "R%b,R%s,$%z,R%a";
+static char rlimi[] = "$%k,R%s,$%z,R%a";
+
+#define OEM IBF(~0,22,30)
+#define FP4 IBF(~0,26,30)
+#define ALL (~0)
+/*
+notes:
+ 10-26: crfD = rD>>2; rD&3 mbz
+ also, L bit (bit 10) mbz or selects 64-bit operands
+*/
+
+static Opcode opcodes[] = {
+ {31, 360, OEM, "ABS%V%C", 0, ir2}, /* POWER */
+
+ {31, 266, OEM, "ADD%V%C", add, ir3},
+ {31, 10, OEM, "ADDC%V%C", add, ir3},
+ {31, 138, OEM, "ADDE%V%C", add, ir3},
+ {14, 0, 0, "ADD", addi, ir2i},
+ {12, 0, 0, "ADDC", addi, ir2i},
+ {13, 0, 0, "ADDCCC", addi, ir2i},
+ {15, 0, 0, "ADD", addis, 0},
+ {31, 234, OEM, "ADDME%V%C", gencc, ir2},
+ {31, 202, OEM, "ADDZE%V%C", gencc, ir2},
+
+ {31, 28, ALL, "AND%C", and, il3},
+ {31, 60, ALL, "ANDN%C", and, il3},
+ {28, 0, 0, "ANDCC", andi, il2u},
+ {29, 0, 0, "ANDCC", shifted, 0},
+
+ {18, 0, 0, "B%L", gencc, "%j"},
+ {16, 0, 0, "BC%L", branch, "%d,%a,%J"},
+ {19, 528, ALL, "BC%L", branch, "%d,%a,(CTR)"},
+ {19, 16, ALL, "BC%L", branch, "%d,%a,(LR)"},
+
+ {31, 531, ALL, "CLCS", gen, ir2}, /* POWER */
+
+ {31, 0, ALL, "CMP", 0, icmp3},
+ {11, 0, 0, "CMP", 0, "R%a,%i,%D"},
+ {31, 32, ALL, "CMPU", 0, icmp3},
+ {10, 0, 0, "CMPU", 0, "R%a,%I,%D"},
+
+ {31, 26, ALL, "CNTLZ%C", gencc, ir2},
+
+ {19, 257, ALL, "CRAND", gen, cr3op},
+ {19, 129, ALL, "CRANDN", gen, cr3op},
+ {19, 289, ALL, "CREQV", gen, cr3op},
+ {19, 225, ALL, "CRNAND", gen, cr3op},
+ {19, 33, ALL, "CRNOR", gen, cr3op},
+ {19, 449, ALL, "CROR", gen, cr3op},
+ {19, 417, ALL, "CRORN", gen, cr3op},
+ {19, 193, ALL, "CRXOR", gen, cr3op},
+
+ {31, 86, ALL, "DCBF", dcb, 0},
+ {31, 470, ALL, "DCBI", dcb, 0},
+ {31, 54, ALL, "DCBST", dcb, 0},
+ {31, 278, ALL, "DCBT", dcb, 0},
+ {31, 246, ALL, "DCBTST", dcb, 0},
+ {31, 1014, ALL, "DCBZ", dcb, 0},
+
+ {31, 331, OEM, "DIV%V%C", div, ir3}, /* POWER */
+ {31, 363, OEM, "DIVS%V%C", div, ir3}, /* POWER */
+ {31, 491, OEM, "DIVW%V%C", div, ir3},
+ {31, 459, OEM, "DIVWU%V%C", div, ir3},
+
+ {31, 264, OEM, "DOZ%V%C", gencc, ir3r}, /* POWER */
+ {9, 0, 0, "DOZ", gen, ir2i}, /* POWER */
+
+ {31, 310, ALL, "ECIWX", ldx, 0},
+ {31, 438, ALL, "ECOWX", stx, 0},
+ {31, 854, ALL, "EIEIO", gen, 0},
+
+ {31, 284, ALL, "EQV%C", gencc, il3},
+
+ {31, 954, ALL, "EXTSB%C", gencc, il2},
+ {31, 922, ALL, "EXTSH%C", gencc, il2},
+
+ {63, 264, ALL, "FABS%C", gencc, fp2},
+ {63, 21, ALL, "FADD%C", gencc, fp3},
+ {59, 21, ALL, "FADDS%C", gencc, fp3},
+ {63, 32, ALL, "FCMPO", gen, fpcmp},
+ {63, 0, ALL, "FCMPU", gen, fpcmp},
+ {63, 14, ALL, "FCTIW%C", gencc, fp2},
+ {63, 15, ALL, "FCTIWZ%C", gencc, fp2},
+ {63, 18, ALL, "FDIV%C", gencc, fp3},
+ {59, 18, ALL, "FDIVS%C", gencc, fp3},
+ {63, 29, FP4, "FMADD%C", gencc, fp4},
+ {59, 29, FP4, "FMADDS%C", gencc, fp4},
+ {63, 72, ALL, "FMOVD%C", gencc, fp2},
+ {63, 28, FP4, "FMSUB%C", gencc, fp4},
+ {59, 28, FP4, "FMSUBS%C", gencc, fp4},
+ {63, 25, FP4, "FMUL%C", gencc, fp3c},
+ {59, 25, FP4, "FMULS%C", gencc, fp3c},
+ {63, 136, ALL, "FNABS%C", gencc, fp2},
+ {63, 40, ALL, "FNEG%C", gencc, fp2},
+ {63, 31, FP4, "FNMADD%C", gencc, fp4},
+ {59, 31, FP4, "FNMADDS%C", gencc, fp4},
+ {63, 30, FP4, "FNMSUB%C", gencc, fp4},
+ {59, 30, FP4, "FNMSUBS%C", gencc, fp4},
+ {63, 12, ALL, "FRSP%C", gencc, fp2},
+ {63, 20, FP4, "FSUB%C", gencc, fp3},
+ {59, 20, FP4, "FSUBS%C", gencc, fp3},
+
+ {31, 982, ALL, "ICBI", dcb, 0},
+ {19, 150, ALL, "ISYNC", gen, 0},
+
+ {34, 0, 0, "MOVBZ", load, ldop},
+ {35, 0, 0, "MOVBZU", load, ldop},
+ {31, 119, ALL, "MOVBZU", ldx, 0},
+ {31, 87, ALL, "MOVBZ", ldx, 0},
+ {50, 0, 0, "FMOVD", fload, fldop},
+ {51, 0, 0, "FMOVDU", fload, fldop},
+ {31, 631, ALL, "FMOVDU", fldx, 0},
+ {31, 599, ALL, "FMOVD", fldx, 0},
+ {48, 0, 0, "FMOVS", load, fldop},
+ {49, 0, 0, "FMOVSU", load, fldop},
+ {31, 567, ALL, "FMOVSU", fldx, 0},
+ {31, 535, ALL, "FMOVS", fldx, 0},
+ {42, 0, 0, "MOVH", load, ldop},
+ {43, 0, 0, "MOVHU", load, ldop},
+ {31, 375, ALL, "MOVHU", ldx, 0},
+ {31, 343, ALL, "MOVH", ldx, 0},
+ {31, 790, ALL, "MOVHBR", ldx, 0},
+ {40, 0, 0, "MOVHZ", load, ldop},
+ {41, 0, 0, "MOVHZU", load, ldop},
+ {31, 311, ALL, "MOVHZU", ldx, 0},
+ {31, 279, ALL, "MOVHZ", ldx, 0},
+ {46, 0, 0, "MOVMW", load, ldop},
+ {31, 277, ALL, "LSCBX%C", ldx, 0}, /* POWER */
+ {31, 597, ALL, "LSW", gen, "(R%a),$%n,R%d"},
+ {31, 533, ALL, "LSW", ldx, 0},
+ {31, 20, ALL, "LWAR", ldx, 0},
+ {31, 534, ALL, "MOVWBR", ldx, 0},
+ {32, 0, 0, "MOVW", load, ldop},
+ {33, 0, 0, "MOVWU", load, ldop},
+ {31, 55, ALL, "MOVWU", ldx, 0},
+ {31, 23, ALL, "MOVW", ldx, 0},
+
+ {31, 29, ALL, "MASKG%C", gencc, "R%s:R%b,R%d"}, /* POWER */
+ {31, 541, ALL, "MASKIR%C", gencc, "R%s,R%b,R%a"}, /* POWER */
+
+ {19, 0, ALL, "MOVFL", gen, "%S,%D"},
+ {63, 64, ALL, "MOVCRFS", gen, "%S,%D"},
+ {31, 512, ALL, "MOVW", gen, "XER,%D"},
+ {31, 19, ALL, "MOVW", gen, "CR,R%d"},
+
+ {63, 583, ALL, "MOVW%C", gen, "FPSCR, F%d"}, /* mffs */
+ {31, 83, ALL, "MOVW", gen, "MSR,R%d"},
+ {31, 339, ALL, "MOVW", gen, "%P,R%d"},
+ {31, 595, ALL, "MOVW", gen, "SEG(%a),R%d"},
+ {31, 659, ALL, "MOVW", gen, "SEG(R%b),R%d"},
+ {31, 144, ALL, "MOVFL", gen, "R%s,%m,CR"},
+ {63, 70, ALL, "MTFSB0%C", gencc, "%D"},
+ {63, 38, ALL, "MTFSB1%C", gencc, "%D"},
+ {63, 711, ALL, "MOVFL%C", gencc, "F%b,%M,FPSCR"}, /* mtfsf */
+ {63, 134, ALL, "MOVFL%C", gencc, "%K,%D"},
+ {31, 146, ALL, "MOVW", gen, "R%s,MSR"},
+ {31, 467, ALL, "MOVW", gen, "R%s,%P"},
+ {31, 210, ALL, "MOVW", gen, "R%s,SEG(%a)"},
+ {31, 242, ALL, "MOVW", gen, "R%s,SEG(R%b)"},
+
+ {31, 107, OEM, "MUL%V%C", gencc, ir3}, /* POWER */
+ {31, 75, ALL, "MULHW%C", gencc, ir3}, /* POWER */
+ {31, 11, ALL, "MULHWU%C", gencc, ir3}, /* POWER */
+
+ {31, 235, OEM, "MULLW%V%C", gencc, ir3},
+ {7, 0, 0, "MULLW", div, "%i,R%a,R%d"},
+
+ {31, 488, OEM, "NABS%V%C", neg, ir2}, /* POWER */
+
+ {31, 476, ALL, "NAND%C", gencc, il3},
+ {31, 104, OEM, "NEG%V%C", neg, ir2},
+ {31, 124, ALL, "NOR%C", gencc, il3},
+ {31, 444, ALL, "OR%C", or, il3},
+ {31, 412, ALL, "ORN%C", or, il3},
+ {24, 0, 0, "OR", and, "%I,R%d,R%a"},
+ {25, 0, 0, "OR", shifted, 0},
+
+ {19, 50, ALL, "RFI", gen, 0},
+
+ {22, 0, 0, "RLMI%C", gencc, rlim}, /* POWER */
+ {20, 0, 0, "RLWMI%C", gencc, rlimi},
+ {21, 0, 0, "RLWNM%C", gencc, rlimi},
+ {23, 0, 0, "RLWNM%C", gencc, rlim},
+
+ {31, 537, ALL, "RRIB%C", gencc, il3}, /* POWER */
+
+ {17, 1, ALL, "SYSCALL", gen, 0},
+
+ {31, 153, ALL, "SLE%C", shift, il3}, /* POWER */
+ {31, 217, ALL, "SLEQ%C", shift, il3}, /* POWER */
+ {31, 184, ALL, "SLQ%C", shifti, il3s}, /* POWER */
+ {31, 248, ALL, "SLLQ%C", shifti, il3s}, /* POWER */
+ {31, 216, ALL, "SLLQ%C", shift, il3}, /* POWER */
+ {31, 152, ALL, "SLQ%C", shift, il3}, /* POWER */
+
+ {31, 24, ALL, "SLW%C", shift, il3},
+
+ {31, 920, ALL, "SRAQ%C", shift, il3}, /* POWER */
+ {31, 952, ALL, "SRAQ%C", shifti, il3s}, /* POWER */
+
+ {31, 792, ALL, "SRAW%C", shift, il3},
+ {31, 824, ALL, "SRAW%C", shifti, il3s},
+
+ {31, 665, ALL, "SRE%C", shift, il3}, /* POWER */
+ {31, 921, ALL, "SREA%C", shift, il3}, /* POWER */
+ {31, 729, ALL, "SREQ%C", shift, il3}, /* POWER */
+ {31, 696, ALL, "SRQ%C", shifti, il3s}, /* POWER */
+ {31, 760, ALL, "SRLQ%C", shifti, il3s}, /* POWER */
+ {31, 728, ALL, "SRLQ%C", shift, il3}, /* POWER */
+ {31, 664, ALL, "SRQ%C", shift, il3}, /* POWER */
+
+ {31, 536, ALL, "SRW%C", shift, il3},
+
+ {38, 0, 0, "MOVB", store, stop},
+ {39, 0, 0, "MOVBU", store, stop},
+ {31, 247, ALL, "MOVBU", stx, 0},
+ {31, 215, ALL, "MOVB", stx, 0},
+ {54, 0, 0, "FMOVD", fstore, fstop},
+ {55, 0, 0, "FMOVDU", fstore, fstop},
+ {31, 759, ALL, "FMOVDU", fstx, 0},
+ {31, 727, ALL, "FMOVD", fstx, 0},
+ {52, 0, 0, "FMOVS", fstore, fstop},
+ {53, 0, 0, "FMOVSU", fstore, fstop},
+ {31, 695, ALL, "FMOVSU", fstx, 0},
+ {31, 663, ALL, "FMOVS", fstx, 0},
+ {44, 0, 0, "MOVH", store, stop},
+ {31, 918, ALL, "MOVHBR", stx, 0},
+ {45, 0, 0, "MOVHU", store, stop},
+ {31, 439, ALL, "MOVHU", stx, 0},
+ {31, 407, ALL, "MOVH", stx, 0},
+ {47, 0, 0, "MOVMW", store, stop},
+ {31, 725, ALL, "STSW", gen, "R%d,$%n,(R%a)"},
+ {31, 661, ALL, "STSW", stx, 0},
+ {36, 0, 0, "MOVW", store, stop},
+ {31, 662, ALL, "MOVWBR", stx, 0},
+ {31, 150, ALL, "STWCCC", stx, 0},
+ {37, 0, 0, "MOVWU", store, stop},
+ {31, 183, ALL, "MOVWU", stx, 0},
+ {31, 151, ALL, "MOVW", stx, 0},
+
+ {31, 40, OEM, "SUB%V%C", sub, ir3},
+ {31, 8, OEM, "SUBC%V%C", sub, ir3},
+ {31, 136, OEM, "SUBE%V%C", sub, ir3},
+ {8, 0, 0, "SUBC", gen, "R%a,%i,R%d"},
+ {31, 232, OEM, "SUBME%V%C", sub, ir2},
+ {31, 200, OEM, "SUBZE%V%C", sub, ir2},
+
+ {31, 598, ALL, "SYNC", gen, 0},
+ {31, 370, ALL, "TLBIA", gen, 0},
+ {31, 306, ALL, "TLBIE", gen, "R%b"},
+ {31, 1010, ALL, "TLBLI", gen, "R%b"},
+ {31, 978, ALL, "TLBLD", gen, "R%b"},
+ {31, 4, ALL, "TW", gen, "%d,R%a,R%b"},
+ {3, 0, 0, "TW", gen, "%d,R%a,%i"},
+
+ {31, 316, ALL, "XOR", and, il3},
+ {26, 0, 0, "XOR", and, il2u},
+ {27, 0, 0, "XOR", shifted, 0},
+
+ {0},
+};
+
+typedef struct Spr Spr;
+struct Spr {
+ int n;
+ char *name;
+};
+
+static Spr sprname[] = {
+ {0, "MQ"},
+ {1, "XER"},
+ {268, "TBL"},
+ {269, "TBU"},
+ {8, "LR"},
+ {9, "CTR"},
+ {528, "IBAT0U"},
+ {529, "IBAT0L"},
+ {530, "IBAT1U"},
+ {531, "IBAT1L"},
+ {532, "IBAT2U"},
+ {533, "IBAT2L"},
+ {534, "IBAT3U"},
+ {535, "IBAT3L"},
+ {536, "DBAT0U"},
+ {537, "DBAT0L"},
+ {538, "DBAT1U"},
+ {539, "DBAT1L"},
+ {540, "DBAT2U"},
+ {541, "DBAT2L"},
+ {542, "DBAT3U"},
+ {543, "DBAT3L"},
+ {25, "SDR1"},
+ {19, "DAR"},
+ {272, "SPRG0"},
+ {273, "SPRG1"},
+ {274, "SPRG2"},
+ {275, "SPRG3"},
+ {18, "DSISR"},
+ {26, "SRR0"},
+ {27, "SRR1"},
+ {284, "TBLW"},
+ {285, "TBUW"},
+ {22, "DEC"},
+ {282, "EAR"},
+ {1008, "HID0"},
+ {1009, "HID1"},
+ {976, "DMISS"},
+ {977, "DCMP"},
+ {978, "HASH1"},
+ {979, "HASH2"},
+ {980, "IMISS"},
+ {981, "ICMP"},
+ {982, "RPA"},
+ {1010, "IABR"},
+ {0,0},
+};
+
+static void
+format(char *mnemonic, Instr *i, char *f)
+{
+ int n, s;
+ ulong mask;
+
+ if (mnemonic)
+ format(0, i, mnemonic);
+ if (f == 0)
+ return;
+ if (mnemonic)
+ bprint(i, "\t");
+ for ( ; *f; f++) {
+ if (*f != '%') {
+ bprint(i, "%c", *f);
+ continue;
+ }
+ switch (*++f) {
+ case 'V':
+ if(i->oe)
+ bprint(i, "V");
+ break;
+
+ case 'C':
+ if(i->rc)
+ bprint(i, "CC");
+ break;
+
+ case 'a':
+ bprint(i, "%d", i->ra);
+ break;
+
+ case 'b':
+ bprint(i, "%d", i->rb);
+ break;
+
+ case 'c':
+ bprint(i, "%d", i->frc);
+ break;
+
+ case 'd':
+ case 's':
+ bprint(i, "%d", i->rd);
+ break;
+
+ case 'S':
+ if(i->ra & 3)
+ bprint(i, "CR(INVAL:%d)", i->ra);
+ else if(i->op == 63)
+ bprint(i, "FPSCR(%d)", i->crfs);
+ else
+ bprint(i, "CR(%d)", i->crfs);
+ break;
+
+ case 'D':
+ if(i->rd & 3)
+ bprint(i, "CR(INVAL:%d)", i->rd);
+ else if(i->op == 63)
+ bprint(i, "FPSCR(%d)", i->crfd);
+ else
+ bprint(i, "CR(%d)", i->crfd);
+ break;
+
+ case 'l':
+ if(i->simm < 0)
+ bprint(i, "-%lx(R%d)", -i->simm, i->ra);
+ else
+ bprint(i, "%lx(R%d)", i->simm, i->ra);
+ break;
+
+ case 'i':
+ bprint(i, "$%ld", i->simm);
+ break;
+
+ case 'I':
+ bprint(i, "$%lx", i->uimm);
+ break;
+
+ case 'w':
+ bprint(i, "[%lux]", i->w0);
+ break;
+
+ case 'P':
+ n = ((i->spr&0x1f)<<5)|((i->spr>>5)&0x1f);
+ for(s=0; sprname[s].name; s++)
+ if(sprname[s].n == n)
+ break;
+ if(sprname[s].name) {
+ if(s < 10)
+ bprint(i, sprname[s].name);
+ else
+ bprint(i, "SPR(%s)", sprname[s].name);
+ } else
+ bprint(i, "SPR(%d)", n);
+ break;
+
+ case 'n':
+ bprint(i, "%d", i->nb==0? 32: i->nb); /* eg, pg 10-103 */
+ break;
+
+ case 'm':
+ bprint(i, "%lx", i->crm);
+ break;
+
+ case 'M':
+ bprint(i, "%lx", i->fm);
+ break;
+
+ case 'z':
+ if(i->mb <= i->me)
+ mask = ((ulong)~0L>>i->mb) & (~0L<<(31-i->me));
+ else
+ mask = ~(((ulong)~0L>>(i->me+1)) & (~0L<<(31-(i->mb-1))));
+ bprint(i, "%lux", mask);
+ break;
+
+ case 'k':
+ bprint(i, "%d", i->sh);
+ break;
+
+ case 'K':
+ bprint(i, "$%x", i->imm);
+ break;
+
+ case 'L':
+ if(i->lk)
+ bprint(i, "L");
+ break;
+
+ case 'j':
+ if(i->aa)
+ pglobal(i, i->li, 1, "(SB)");
+ else
+ pglobal(i, i->addr+i->li, 1, "");
+ break;
+
+ case 'J':
+ if(i->aa)
+ pglobal(i, i->bd, 1, "(SB)");
+ else
+ pglobal(i, i->addr+i->bd, 1, "");
+ break;
+
+ case '\0':
+ bprint(i, "%%");
+ return;
+
+ default:
+ bprint(i, "%%%c", *f);
+ break;
+ }
+ }
+}
+
+static int
+printins(Map *map, ulong pc, char *buf, int n)
+{
+ Instr i;
+ Opcode *o;
+
+ mymap = map;
+ memset(&i, 0, sizeof(i));
+ i.curr = buf;
+ i.end = buf+n-1;
+ if(mkinstr(pc, &i) < 0)
+ return -1;
+ for(o = opcodes; o->mnemonic != 0; o++)
+ if(i.op == o->op && (i.xo & o->xomask) == o->xo) {
+ if (o->f)
+ (*o->f)(o, &i);
+ else
+ format(o->mnemonic, &i, o->ken);
+ return i.size*4;
+ }
+ bprint(&i, "unknown %lux", i.w0);
+ return i.size*4;
+}
+
+static int
+powerdas(Map *map, ulong pc, char modifier, char *buf, int n)
+{
+ USED(modifier);
+ return printins(map, pc, buf, n);
+}
+
+static int
+powerhexinst(Map *map, ulong pc, char *buf, int n)
+{
+ Instr instr;
+
+ mymap = map;
+ memset(&instr, 0, sizeof(instr));
+ instr.curr = buf;
+ instr.end = buf+n-1;
+ if (mkinstr(pc, &instr) < 0)
+ return -1;
+ if (instr.end-instr.curr > 8)
+ instr.curr = _hexify(instr.curr, instr.w0, 7);
+ if (instr.end-instr.curr > 9 && instr.size == 2) {
+ *instr.curr++ = ' ';
+ instr.curr = _hexify(instr.curr, instr.w1, 7);
+ }
+ *instr.curr = 0;
+ return instr.size*4;
+}
+
+static int
+powerinstlen(Map *map, ulong pc)
+{
+ Instr i;
+
+ mymap = map;
+ if (mkinstr(pc, &i) < 0)
+ return -1;
+ return i.size*4;
+}
+
+static int
+powerfoll(Map *map, Regs *regs, ulong pc, ulong *foll)
+{
+ char *reg;
+ Instr i;
+
+ mymap = map;
+ if (mkinstr(pc, &i) < 0)
+ return -1;
+ foll[0] = pc+4;
+ foll[1] = pc+4;
+ switch(i.op) {
+ default:
+ return 1;
+
+ case 18: /* branch */
+ foll[0] = i.li;
+ if(!i.aa)
+ foll[0] += pc;
+ break;
+
+ case 16: /* conditional branch */
+ foll[0] = i.bd;
+ if(!i.aa)
+ foll[0] += pc;
+ break;
+
+ case 19: /* conditional branch to register */
+ if(i.xo == 528)
+ reg = "CTR";
+ else if(i.xo == 16)
+ reg = "LR";
+ else
+ return 1; /* not a branch */
+ if(rget(regs, reg, &foll[0]) < 0)
+ return -1;
+ break;
+ }
+ if(i.lk)
+ return 2;
+ return 1;
+}
+
+#define REGOFF(x) (ulong) (&((struct Ureg *) 0)->x)
+
+#define SP REGOFF(r1)
+#define PC REGOFF(pc)
+#define R3 REGOFF(r3) /* return reg */
+#define LR REGOFF(lr)
+#define R31 REGOFF(r31)
+#define FP_REG(x) (R31+4+8*(x))
+
+#define REGSIZE sizeof(struct Ureg)
+#define FPREGSIZE (8*33)
+
+Regdesc powerreglist[] =
+{
+ {"CAUSE", REGOFF(cause), RINT|RRDONLY, 'X'},
+ {"SRR1", REGOFF(srr1), RINT|RRDONLY, 'X'},
+ {"PC", REGOFF(pc), RINT, 'X'},
+ {"LR", REGOFF(lr), RINT, 'X'},
+ {"CR", REGOFF(cr), RINT, 'X'},
+ {"XER", REGOFF(xer), RINT, 'X'},
+ {"CTR", REGOFF(ctr), RINT, 'X'},
+ {"PC", PC, RINT, 'X'},
+ {"SP", SP, RINT, 'X'},
+ {"R0", REGOFF(r0), RINT, 'X'},
+ /* R1 is SP */
+ {"R2", REGOFF(r2), RINT, 'X'},
+ {"R3", REGOFF(r3), RINT, 'X'},
+ {"R4", REGOFF(r4), RINT, 'X'},
+ {"R5", REGOFF(r5), RINT, 'X'},
+ {"R6", REGOFF(r6), RINT, 'X'},
+ {"R7", REGOFF(r7), RINT, 'X'},
+ {"R8", REGOFF(r8), RINT, 'X'},
+ {"R9", REGOFF(r9), RINT, 'X'},
+ {"R10", REGOFF(r10), RINT, 'X'},
+ {"R11", REGOFF(r11), RINT, 'X'},
+ {"R12", REGOFF(r12), RINT, 'X'},
+ {"R13", REGOFF(r13), RINT, 'X'},
+ {"R14", REGOFF(r14), RINT, 'X'},
+ {"R15", REGOFF(r15), RINT, 'X'},
+ {"R16", REGOFF(r16), RINT, 'X'},
+ {"R17", REGOFF(r17), RINT, 'X'},
+ {"R18", REGOFF(r18), RINT, 'X'},
+ {"R19", REGOFF(r19), RINT, 'X'},
+ {"R20", REGOFF(r20), RINT, 'X'},
+ {"R21", REGOFF(r21), RINT, 'X'},
+ {"R22", REGOFF(r22), RINT, 'X'},
+ {"R23", REGOFF(r23), RINT, 'X'},
+ {"R24", REGOFF(r24), RINT, 'X'},
+ {"R25", REGOFF(r25), RINT, 'X'},
+ {"R26", REGOFF(r26), RINT, 'X'},
+ {"R27", REGOFF(r27), RINT, 'X'},
+ {"R28", REGOFF(r28), RINT, 'X'},
+ {"R29", REGOFF(r29), RINT, 'X'},
+ {"R30", REGOFF(r30), RINT, 'X'},
+ {"R31", REGOFF(r31), RINT, 'X'},
+ {"VRSAVE", REGOFF(vrsave), RINT, 'X'},
+ {"F0", FP_REG(0), RFLT, 'F'},
+ {"F1", FP_REG(1), RFLT, 'F'},
+ {"F2", FP_REG(2), RFLT, 'F'},
+ {"F3", FP_REG(3), RFLT, 'F'},
+ {"F4", FP_REG(4), RFLT, 'F'},
+ {"F5", FP_REG(5), RFLT, 'F'},
+ {"F6", FP_REG(6), RFLT, 'F'},
+ {"F7", FP_REG(7), RFLT, 'F'},
+ {"F8", FP_REG(8), RFLT, 'F'},
+ {"F9", FP_REG(9), RFLT, 'F'},
+ {"F10", FP_REG(10), RFLT, 'F'},
+ {"F11", FP_REG(11), RFLT, 'F'},
+ {"F12", FP_REG(12), RFLT, 'F'},
+ {"F13", FP_REG(13), RFLT, 'F'},
+ {"F14", FP_REG(14), RFLT, 'F'},
+ {"F15", FP_REG(15), RFLT, 'F'},
+ {"F16", FP_REG(16), RFLT, 'F'},
+ {"F17", FP_REG(17), RFLT, 'F'},
+ {"F18", FP_REG(18), RFLT, 'F'},
+ {"F19", FP_REG(19), RFLT, 'F'},
+ {"F20", FP_REG(20), RFLT, 'F'},
+ {"F21", FP_REG(21), RFLT, 'F'},
+ {"F22", FP_REG(22), RFLT, 'F'},
+ {"F23", FP_REG(23), RFLT, 'F'},
+ {"F24", FP_REG(24), RFLT, 'F'},
+ {"F25", FP_REG(25), RFLT, 'F'},
+ {"F26", FP_REG(26), RFLT, 'F'},
+ {"F27", FP_REG(27), RFLT, 'F'},
+ {"F28", FP_REG(28), RFLT, 'F'},
+ {"F29", FP_REG(29), RFLT, 'F'},
+ {"F30", FP_REG(30), RFLT, 'F'},
+ {"F31", FP_REG(31), RFLT, 'F'},
+ {"FPSCR", FP_REG(32)+4, RFLT, 'X'},
+ { 0 }
+};
+
+static char *powerwindregs[] =
+{
+ "PC",
+ "SP",
+ "LR",
+ 0,
+};
+
+static int
+powerunwind(Map *map, Regs *regs, ulong *next)
+{
+ /*
+ * This is tremendously hard. The best we're going to
+ * do without better debugger support is trace through
+ * the stack frame links and pull the link registers out of 8(R1).
+ * Anything more requires knowing which registers got saved,
+ * and the compiler appears not to record that. Gdb appears
+ * to disassemble the function prologues in order to figure
+ * this out.
+ */
+ ulong b[2];
+
+ // evaluate lr
+ // if in this function, no good - go to saved one.
+ // set next[sp] to *cur[sp]
+ // set next[pc] to lr
+ // set next[lr] to lr
+ //
+ werrstr("powerunwind not implemented");
+ return -1;
+}
+
+ /* the machine description */
+Mach machpower =
+{
+ "power",
+ MPOWER, /* machine type */
+ powerreglist, /* register set */
+ REGSIZE, /* number of bytes in register set */
+ FPREGSIZE, /* number of bytes in FP register set */
+ "PC", /* name of PC */
+ "SP", /* name of SP */
+ 0, /* name of FP */
+ "LR", /* name of link register */
+ "setSB", /* static base register name */
+ 0, /* value */
+ 0x1000, /* page size */
+ 0x80000000, /* kernel base */
+ 0, /* kernel text mask */
+ 4, /* quantization of pc */
+ 4, /* szaddr */
+ 4, /* szreg */
+ 4, /* szfloat */
+ 8, /* szdouble */
+
+ powerwindregs, /* locations unwound in stack trace */
+ 3,
+
+ {0x02, 0x8F, 0xFF, 0xFF}, /* break point */ /* BUG */
+ 4,
+
+ powerfoll, /* following addresses */
+ powerexcep, /* print exception */
+ powerunwind, /* stack unwind */
+
+ beswap2, /* convert short to local byte order */
+ beswap4, /* convert long to local byte order */
+ beswap8, /* convert vlong to local byte order */
+ beieeeftoa32, /* single precision float pointer */
+ beieeeftoa64, /* double precision float pointer */
+ beieeeftoa80, /* long double precision floating point */
+
+ powerdas, /* dissembler */
+ powerdas, /* plan9-format disassembler */
+ 0, /* commercial disassembler */
+ powerhexinst, /* print instruction */
+ powerinstlen, /* instruction size calculation */
+};
+
diff --git a/src/libmach/map.c b/src/libmach/map.c
new file mode 100644
index 00000000..144b7042
--- /dev/null
+++ b/src/libmach/map.c
@@ -0,0 +1,306 @@
+/*
+ * File map routines
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+static int fdrw(Map*, Seg*, ulong, void*, uint, int);
+static int zerorw(Map*, Seg*, ulong, void*, uint, int);
+static int mrw(Map*, ulong, void*, uint, int);
+static int datarw(Map*, Seg*, ulong, void*, uint, int);
+
+Map*
+allocmap(void)
+{
+ return mallocz(sizeof(Map), 1);
+}
+
+void
+freemap(Map *map)
+{
+ if(map == nil)
+ return;
+ free(map->seg);
+ free(map);
+}
+
+int
+addseg(Map *map, Seg seg)
+{
+ Seg *ss;
+
+ if(map == 0){
+ werrstr("invalid map");
+ return -1;
+ }
+
+ ss = realloc(map->seg, (map->nseg+1)*sizeof(ss[0]));
+ if(ss == nil)
+ return -1;
+ map->seg = ss;
+ if(seg.rw == nil){
+ if(seg.name && strcmp(seg.name, "zero") == 0)
+ seg.rw = zerorw;
+ else if(seg.p)
+ seg.rw = datarw;
+ else
+ seg.rw = fdrw;
+ }
+ map->seg[map->nseg] = seg;
+ return map->nseg++;
+}
+
+int
+findseg(Map *map, char *name, char *file)
+{
+ int i;
+
+ if(map == 0)
+ return -1;
+ for(i=0; i<map->nseg; i++){
+ if(name && (!map->seg[i].name || strcmp(map->seg[i].name, name) != 0))
+ continue;
+ if(file && (!map->seg[i].file || strcmp(map->seg[i].file, file) != 0))
+ continue;
+ return i;
+ }
+ werrstr("segment %s in %s not found", name, file);
+ return -1;
+}
+
+int
+addrtoseg(Map *map, ulong addr, Seg *sp)
+{
+ int i;
+ Seg *s;
+
+ if(map == nil){
+ werrstr("no map");
+ return -1;
+ }
+ for(i=map->nseg-1; i>=0; i--){
+ s = &map->seg[i];
+ if(s->base <= addr && addr-s->base < s->size){
+ if(sp)
+ *sp = *s;
+ return i;
+ }
+ }
+ werrstr("address 0x%lux is not mapped", addr);
+ return -1;
+}
+
+int
+addrtosegafter(Map *map, ulong addr, Seg *sp)
+{
+ int i;
+ Seg *s, *best;
+ ulong bdist;
+
+ if(map == nil){
+ werrstr("no map");
+ return -1;
+ }
+
+ /*
+ * If segments were sorted this would be easier,
+ * but since segments may overlap, sorting also
+ * requires splitting and rejoining, and that's just
+ * too complicated.
+ */
+ best = nil;
+ bdist = 0;
+ for(i=map->nseg-1; i>=0; i--){
+ s = &map->seg[i];
+ if(s->base > addr){
+ if(best==nil || s->base-addr < bdist){
+ bdist = s->base - addr;
+ best = s;
+ }
+ }
+ }
+ if(best){
+ if(sp)
+ *sp = *best;
+ return best-map->seg;
+ }
+ werrstr("nothing mapped after address 0x%lux", addr);
+ return -1;
+}
+
+void
+removeseg(Map *map, int i)
+{
+ if(map == nil)
+ return;
+ if(i < 0 || i >= map->nseg)
+ return;
+ memmove(&map->seg[i], &map->seg[i+1], (map->nseg-(i+1))*sizeof(Seg));
+ map->nseg--;
+}
+
+int
+get1(Map *map, ulong addr, uchar *a, uint n)
+{
+ return mrw(map, addr, a, n, 1);
+}
+
+int
+get2(Map *map, ulong addr, u16int *u)
+{
+ u16int v;
+
+ if(mrw(map, addr, &v, 2, 1) < 0)
+ return -1;
+ *u = mach->swap2(v);
+ return 2;
+}
+
+int
+get4(Map *map, ulong addr, u32int *u)
+{
+ u32int v;
+
+ if(mrw(map, addr, &v, 4, 1) < 0)
+ return -1;
+ *u = mach->swap4(v);
+ return 4;
+}
+
+int
+get8(Map *map, ulong addr, u64int *u)
+{
+ u64int v;
+
+ if(mrw(map, addr, &v, 4, 1) < 0)
+ return -1;
+ *u = mach->swap8(v);
+ return 8;
+}
+
+int
+put1(Map *map, ulong addr, uchar *a, uint n)
+{
+ return mrw(map, addr, a, n, 0);
+}
+
+int
+put2(Map *map, ulong addr, u16int u)
+{
+ u = mach->swap2(u);
+ return mrw(map, addr, &u, 2, 0);
+}
+
+int
+put4(Map *map, ulong addr, u32int u)
+{
+ u = mach->swap4(u);
+ return mrw(map, addr, &u, 4, 0);
+}
+
+int
+put8(Map *map, ulong addr, u64int u)
+{
+ u = mach->swap8(u);
+ return mrw(map, addr, &u, 8, 0);
+}
+
+static Seg*
+reloc(Map *map, ulong addr, uint n, ulong *off, uint *nn)
+{
+ int i;
+ ulong o;
+
+ if(map == nil){
+ werrstr("invalid map");
+ return nil;
+ }
+
+ for(i=map->nseg-1; i>=0; i--){
+ if(map->seg[i].base <= addr){
+ o = addr - map->seg[i].base;
+ if(o >= map->seg[i].size)
+ continue;
+ if(o+n > map->seg[i].size)
+ *nn = map->seg[i].size - o;
+ else
+ *nn = n;
+ *off = o;
+ return &map->seg[i];
+ }
+ }
+ werrstr("address 0x%lux not mapped", addr);
+ return nil;
+}
+
+static int
+mrw(Map *map, ulong addr, void *a, uint n, int r)
+{
+ uint nn;
+ uint tot;
+ Seg *s;
+ ulong off;
+
+ for(tot=0; tot<n; tot+=nn){
+ s = reloc(map, addr+tot, n-tot, &off, &nn);
+ if(s == nil)
+ return -1;
+ if(s->rw(map, s, off, a, nn, r) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+static int
+fdrw(Map *map, Seg *seg, ulong addr, void *a, uint n, int r)
+{
+ int nn;
+ uint tot;
+ ulong off;
+
+ USED(map);
+ off = seg->offset + addr;
+ for(tot=0; tot<n; tot+=nn){
+ if(r)
+ nn = pread(seg->fd, a, n-tot, off+tot);
+ else
+ nn = pwrite(seg->fd, a, n-tot, off+tot);
+ if(nn < 0)
+ return -1;
+ if(nn == 0){
+ werrstr("partial %s at address 0x%lux in %s",
+ r ? "read" : "write", off+tot, seg->file);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int
+zerorw(Map *map, Seg *seg, ulong addr, void *a, uint n, int r)
+{
+ USED(map);
+ USED(seg);
+ USED(addr);
+
+ if(r==0){
+ werrstr("cannot write zero segment");
+ return -1;
+ }
+ memset(a, 0, n);
+ return 0;
+}
+
+static int
+datarw(Map *map, Seg *seg, ulong addr, void *a, uint n, int r)
+{
+ USED(map);
+
+ if(r)
+ memmove(a, seg->p+addr, n);
+ else
+ memmove(seg->p+addr, a, n);
+ return 0;
+}
diff --git a/src/libmach/mkfile b/src/libmach/mkfile
new file mode 100644
index 00000000..a690a000
--- /dev/null
+++ b/src/libmach/mkfile
@@ -0,0 +1,58 @@
+<$PLAN9/src/mkhdr
+
+LIB=libmach.a
+
+OFILES=\
+ $SYSNAME.$O\
+ crack.$O\
+ crackelf.$O\
+ crackmacho.$O\
+ dwarf386.$O\
+ dwarfabbrev.$O\
+ dwarfaranges.$O\
+ dwarfcfa.$O\
+ dwarfget.$O\
+ dwarfinfo.$O\
+ dwarfopen.$O\
+ dwarfpc.$O\
+ dwarfpubnames.$O\
+ elf.$O\
+ elfcorefreebsd386.$O\
+ elfcorelinux386.$O\
+ frame.$O\
+ fpformat.$O\
+ hexify.$O\
+ ieee.$O\
+ loc.$O\
+ localaddr.$O\
+ mach.$O\
+ mach386.$O\
+ macho.$O\
+ machocorepower.$O\
+ machpower.$O\
+ map.$O\
+ regs.$O\
+ stabs.$O\
+ swap.$O\
+ sym.$O\
+ symdwarf.$O\
+ symelf.$O\
+ symmacho.$O\
+ symstabs.$O\
+
+HFILES=mach.h
+
+<$PLAN9/src/mksyslib
+CFLAGS=$CFLAGS -I.
+
+elfdump: elfdump.o $LIBDIR/$LIB
+ $LD -o $target $prereq -l9
+
+machodump: machodump.o $LIBDIR/$LIB
+ $LD -o $target $prereq -l9
+
+dwarfdump: dwarfdump.o $LIBDIR/$LIB
+ $LD -o $target $prereq -l9
+
+nm: nm.o $LIBDIR/$LIB
+ $LD -o $target $prereq -l9
diff --git a/src/libmach/nm.c b/src/libmach/nm.c
new file mode 100644
index 00000000..b5e369cb
--- /dev/null
+++ b/src/libmach/nm.c
@@ -0,0 +1,289 @@
+/*
+ * nm.c -- drive nm
+ */
+#include <u.h>
+#include <libc.h>
+#include <ar.h>
+#include <bio.h>
+#include <mach.h>
+
+enum{
+ CHUNK = 256 /* must be power of 2 */
+};
+
+char *errs; /* exit status */
+char *filename; /* current file */
+char symname[]="__.SYMDEF"; /* table of contents file name */
+int multifile; /* processing multiple files */
+int aflag;
+int gflag;
+int hflag;
+int nflag;
+int sflag;
+int uflag;
+
+Symbol **fnames; /* file path translation table */
+Symbol **symptr;
+int nsym;
+Biobuf bout;
+
+int cmp(void*, void*);
+void error(char*, ...);
+void execsyms(int);
+void psym(Symbol*, void*);
+void printsyms(Symbol**, long);
+void doar(Biobuf*);
+void dofile(Biobuf*);
+void zenter(Symbol*);
+
+void
+main(int argc, char *argv[])
+{
+ int i;
+ Biobuf *bin;
+
+ Binit(&bout, 1, OWRITE);
+ argv0 = argv[0];
+ ARGBEGIN {
+ case 'a': aflag = 1; break;
+ case 'g': gflag = 1; break;
+ case 'h': hflag = 1; break;
+ case 'n': nflag = 1; break;
+ case 's': sflag = 1; break;
+ case 'u': uflag = 1; break;
+ } ARGEND
+ if (argc > 1)
+ multifile++;
+ for(i=0; i<argc; i++){
+ filename = argv[i];
+ bin = Bopen(filename, OREAD);
+ if(bin == 0){
+ error("cannot open %s", filename);
+ continue;
+ }
+ if (isar(bin))
+ doar(bin);
+ else{
+ Bseek(bin, 0, 0);
+ dofile(bin);
+ }
+ Bterm(bin);
+ }
+ exits(errs);
+}
+
+/*
+ * read an archive file,
+ * processing the symbols for each intermediate file in it.
+ */
+void
+doar(Biobuf *bp)
+{
+ int offset, size, obj;
+ char membername[SARNAME];
+
+ multifile = 1;
+ for (offset = Boffset(bp);;offset += size) {
+ size = nextar(bp, offset, membername);
+ if (size < 0) {
+ error("phase error on ar header %ld", offset);
+ return;
+ }
+ if (size == 0)
+ return;
+ if (strcmp(membername, symname) == 0)
+ continue;
+ obj = objtype(bp, 0);
+ if (obj < 0) {
+ error("inconsistent file %s in %s",
+ membername, filename);
+ return;
+ }
+ if (!readar(bp, obj, offset+size, 1)) {
+ error("invalid symbol reference in file %s",
+ membername);
+ return;
+ }
+ filename = membername;
+ nsym=0;
+ objtraverse(psym, 0);
+ printsyms(symptr, nsym);
+ }
+}
+
+/*
+ * process symbols in a file
+ */
+void
+dofile(Biobuf *bp)
+{
+ int obj;
+
+ obj = objtype(bp, 0);
+ if (obj < 0)
+ execsyms(Bfildes(bp));
+ else
+ if (readobj(bp, obj)) {
+ nsym = 0;
+ objtraverse(psym, 0);
+ printsyms(symptr, nsym);
+ }
+}
+
+/*
+ * comparison routine for sorting the symbol table
+ * this screws up on 'z' records when aflag == 1
+ */
+int
+cmp(void *vs, void *vt)
+{
+ Symbol **s, **t;
+
+ s = vs;
+ t = vt;
+ if(nflag)
+ if((*s)->value < (*t)->value)
+ return -1;
+ else
+ return (*s)->value > (*t)->value;
+ return strcmp((*s)->name, (*t)->name);
+}
+/*
+ * enter a symbol in the table of filename elements
+ */
+void
+zenter(Symbol *s)
+{
+ static int maxf = 0;
+
+ if (s->value > maxf) {
+ maxf = (s->value+CHUNK-1) &~ (CHUNK-1);
+ fnames = realloc(fnames, (maxf+1)*sizeof(*fnames));
+ if(fnames == 0) {
+ error("out of memory", argv0);
+ exits("memory");
+ }
+ }
+ fnames[s->value] = s;
+}
+
+/*
+ * get the symbol table from an executable file, if it has one
+ */
+void
+execsyms(int fd)
+{
+ Fhdr f;
+ Symbol *s;
+ long n;
+
+ seek(fd, 0, 0);
+ if (crackhdr(fd, &f) == 0) {
+ error("Can't read header for %s", filename);
+ return;
+ }
+ if (syminit(fd, &f) < 0)
+ return;
+ s = symbase(&n);
+ nsym = 0;
+ while(n--)
+ psym(s++, 0);
+
+ printsyms(symptr, nsym);
+}
+
+void
+psym(Symbol *s, void* p)
+{
+ USED(p);
+ switch(s->type) {
+ case 'T':
+ case 'L':
+ case 'D':
+ case 'B':
+ if (uflag)
+ return;
+ if (!aflag && ((s->name[0] == '.' || s->name[0] == '$')))
+ return;
+ break;
+ case 'b':
+ case 'd':
+ case 'l':
+ case 't':
+ if (uflag || gflag)
+ return;
+ if (!aflag && ((s->name[0] == '.' || s->name[0] == '$')))
+ return;
+ break;
+ case 'U':
+ if (gflag)
+ return;
+ break;
+ case 'Z':
+ if (!aflag)
+ return;
+ break;
+ case 'm':
+ case 'f': /* we only see a 'z' when the following is true*/
+ if(!aflag || uflag || gflag)
+ return;
+ if (strcmp(s->name, ".frame"))
+ zenter(s);
+ break;
+ case 'a':
+ case 'p':
+ case 'z':
+ default:
+ if(!aflag || uflag || gflag)
+ return;
+ break;
+ }
+ symptr = realloc(symptr, (nsym+1)*sizeof(Sym*));
+ if (symptr == 0) {
+ error("out of memory");
+ exits("memory");
+ }
+ symptr[nsym++] = s;
+}
+
+void
+printsyms(Symbol **symptr, long nsym)
+{
+ Symbol *s;
+ char *cp;
+ char path[512];
+
+ if(!sflag)
+ qsort(symptr, nsym, sizeof(*symptr), cmp);
+ while (nsym-- > 0) {
+ s = *symptr++;
+ if (multifile && !hflag)
+ Bprint(&bout, "%s:", filename);
+ if (s->type == 'z') {
+ fileelem(fnames, (uchar *) s->name, path, 512);
+ cp = path;
+ } else
+ cp = s->name;
+ if (s->value || s->type == 'a' || s->type == 'p')
+ Bprint(&bout, "%8lux %c %s\n", s->value, s->type, cp);
+ else
+ Bprint(&bout, " %c %s\n", s->type, cp);
+ }
+}
+
+void
+error(char *fmt, ...)
+{
+ Fmt f;
+ char buf[128];
+ va_list arg;
+
+ fmtfdinit(&f, 2, buf, sizeof buf);
+ fmtprint(&f, "%s: ", argv0);
+ va_start(arg, fmt);
+ fmtvprint(&f, fmt, arg);
+ va_end(arg);
+ fmtprint(&f, "\n");
+ fmtfdflush(&f);
+ errs = "errors";
+}
diff --git a/src/libmach/regs.c b/src/libmach/regs.c
new file mode 100644
index 00000000..43c04786
--- /dev/null
+++ b/src/libmach/regs.c
@@ -0,0 +1,59 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+
+int
+rput(Regs *regs, char *name, ulong u)
+{
+ if(regs == nil){
+ werrstr("registers not mapped");
+ return -1;
+ }
+ return regs->rw(regs, name, &u, 0);
+}
+
+int
+rget(Regs *regs, char *name, ulong *u)
+{
+ if(regs == nil){
+ *u = ~(ulong)0;
+ werrstr("registers not mapped");
+ return -1;
+ }
+ return regs->rw(regs, name, u, 1);
+}
+
+int
+_uregrw(Regs *regs, char *name, ulong *u, int isr)
+{
+ Regdesc *r;
+ uchar *ureg;
+
+ if(!isr){
+ werrstr("cannot write registers");
+ return -1;
+ }
+
+ if((r = regdesc(name)) == nil)
+ return -1;
+ ureg = ((UregRegs*)regs)->ureg + r->offset;
+
+ switch(r->format){
+ default:
+ case 'X':
+ *u = mach->swap4(*(u32int*)ureg);
+ return 0;
+ }
+}
+
+Regdesc*
+regdesc(char *name)
+{
+ Regdesc *r;
+
+ for(r=mach->reglist; r->name; r++)
+ if(strcmp(r->name, name) == 0)
+ return r;
+ return nil;
+}
+
diff --git a/src/libmach/stabs.c b/src/libmach/stabs.c
new file mode 100644
index 00000000..d34bb864
--- /dev/null
+++ b/src/libmach/stabs.c
@@ -0,0 +1,54 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "stabs.h"
+
+/*
+http://sources.redhat.com/gdb/onlinedocs/stabs.html
+*/
+
+int
+stabsym(Stab *stabs, int i, StabSym *sym)
+{
+ uchar *p;
+ ulong x;
+
+ if(stabs == nil){
+ werrstr("no stabs");
+ return -1;
+ }
+ if(stabs->e2==nil || stabs->e4==nil){
+ werrstr("no data extractors");
+ return -1;
+ }
+
+ if(i >= stabs->stabsize/12){
+ werrstr("stabs index out of range");
+ return -1;
+ }
+
+ p = stabs->stabbase+i*12;
+ x = stabs->e4(p);
+ if(x == 0)
+ sym->name = nil;
+ else if(x < stabs->strsize)
+ sym->name = stabs->strbase+x;
+ else{
+ werrstr("bad stabs string index");
+ return -1;
+ }
+
+ /*
+ * In theory, if name ends with a backslash,
+ * it continues into the next entry. We could
+ * rewrite these in place and then zero the next
+ * few entries, but let's wait until we run across
+ * some system that generates these.
+ */
+ sym->type = p[4];
+ sym->other = p[5];
+ sym->desc = stabs->e2(p+6);
+ sym->value = stabs->e4(p+8);
+ return 0;
+}
+
diff --git a/src/libmach/stabs.h b/src/libmach/stabs.h
new file mode 100644
index 00000000..ad67cfe6
--- /dev/null
+++ b/src/libmach/stabs.h
@@ -0,0 +1,117 @@
+typedef struct StabSym StabSym;
+typedef struct Stab Stab; /* defined in mach.h */
+
+struct StabSym
+{
+ char *name;
+ uchar type;
+ uchar other;
+ u16int desc;
+ u32int value;
+};
+
+enum
+{
+ EXT = 0x01,
+
+ N_UNDEF = 0x00,
+ N_ABS = 0x02,
+ N_TEXT = 0x04,
+ N_DATA = 0x06,
+ N_BSS = 0x08,
+ N_INDR = 0x0A,
+ N_FN_SEQ = 0x0C,
+ N_WEAKU = 0x0D,
+ N_WEAKA = 0x0E,
+ N_WEAKT = 0x0F,
+ N_WEAKD = 0x10,
+ N_WEAKB = 0x11,
+ N_COMM = 0x12,
+ N_SETA = 0x14,
+ N_SETT = 0x16,
+
+ N_GSYM = 0x20,
+ N_FNAME = 0x22,
+ N_FUN = 0x24,
+ N_STSYM = 0x26,
+ N_LCSYM = 0x28,
+ N_MAIN = 0x2A,
+ N_ROSYM = 0x2C,
+ N_PC = 0x30,
+ N_NSYMS = 0x32,
+ N_NOMAP = 0x34,
+ N_OBJ = 0x38,
+ N_OPT = 0x3C,
+ N_RSYM = 0x40,
+ N_M2C = 0x42,
+ N_SLINE = 0x44,
+ N_DSLINE = 0x46,
+ N_BSLINE = 0x48,
+ N_BROWS = 0x48,
+ N_DEFD = 0x4A,
+ N_FLINE = 0x4C,
+ N_EHDECL = 0x50,
+ N_MOD2 = 0x50,
+ N_CATCH = 0x54,
+ N_SSYM = 0x60,
+ N_ENDM = 0x62,
+ N_SO = 0x64,
+ N_ALIAS = 0x6C,
+ N_LSYM = 0x80,
+ N_BINCL = 0x82,
+ N_SOL = 0x84,
+ N_PSYM = 0xA0,
+ N_EINCL = 0xA2,
+ N_ENTRY = 0xA4,
+ N_LBRAC = 0xC0,
+ N_EXCL = 0xC2,
+ N_SCOPE = 0xC4,
+ N_RBRAC = 0xE0,
+ N_BCOMM = 0xE2,
+ N_ECOMM = 0xE4,
+ N_ECOML = 0xE8,
+ N_WITH = 0xEA,
+ N_LENG = 0xFE
+};
+
+/*
+ symbol descriptors
+
+[(0-9\-] variable on stack
+: C++ nested symbol
+a parameter by reference
+b based variable
+c constant
+C conformant array bound
+ name of caught exception (N_CATCH)
+d fp register variable
+D fp parameter
+f file scope function
+F global function
+G global variable
+i register parameter?
+I nested procedure
+J nested function
+L label name
+m module
+p arg list parameter
+pP
+pF
+P register param (N_PSYM)
+ proto of ref fun (N_FUN)
+Q static procedure
+R register param
+r register variable
+S file scope variable
+s local variable
+t type name
+T sue tag
+v param by reference
+V procedure scope static variable
+x conformant array
+X function return variable
+
+*/
+
+int stabsym(Stab*, int, StabSym*);
+
diff --git a/src/libmach/swap.c b/src/libmach/swap.c
new file mode 100644
index 00000000..b6558234
--- /dev/null
+++ b/src/libmach/swap.c
@@ -0,0 +1,118 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+/*
+ * big-endian short
+ */
+u16int
+beswap2(u16int s)
+{
+ uchar *p;
+
+ p = (uchar*)&s;
+ return (p[0]<<8) | p[1];
+}
+
+/*
+ * big-endian long
+ */
+u32int
+beswap4(u32int l)
+{
+ uchar *p;
+
+ p = (uchar*)&l;
+ return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
+}
+
+/*
+ * big-endian vlong
+ */
+u64int
+beswap8(u64int v)
+{
+ uchar *p;
+
+ p = (uchar*)&v;
+ return ((u64int)p[0]<<56) | ((u64int)p[1]<<48) | ((u64int)p[2]<<40)
+ | ((u64int)p[3]<<32) | ((u64int)p[4]<<24)
+ | ((u64int)p[5]<<16) | ((u64int)p[6]<<8)
+ | (u64int)p[7];
+}
+
+/*
+ * little-endian short
+ */
+u16int
+leswap2(u16int s)
+{
+ uchar *p;
+
+ p = (uchar*)&s;
+ return (p[1]<<8) | p[0];
+}
+
+/*
+ * little-endian long
+ */
+u32int
+leswap4(u32int l)
+{
+ uchar *p;
+
+ p = (uchar*)&l;
+ return (p[3]<<24) | (p[2]<<16) | (p[1]<<8) | p[0];
+}
+
+/*
+ * little-endian vlong
+ */
+u64int
+leswap8(u64int v)
+{
+ uchar *p;
+
+ p = (uchar*)&v;
+ return ((u64int)p[7]<<56) | ((u64int)p[6]<<48) | ((u64int)p[5]<<40)
+ | ((u64int)p[4]<<32) | ((u64int)p[3]<<24)
+ | ((u64int)p[2]<<16) | ((u64int)p[1]<<8)
+ | (u64int)p[0];
+}
+
+u16int
+leload2(uchar *b)
+{
+ return b[0] | (b[1]<<8);
+}
+
+u32int
+leload4(uchar *b)
+{
+ return b[0] | (b[1]<<8) | (b[2]<<16) | (b[3]<<24);
+}
+
+u64int
+leload8(uchar *b)
+{
+ return leload4(b) | ((uvlong)leload4(b+4) << 32);
+}
+
+u16int
+beload2(uchar *b)
+{
+ return (b[0]<<8) | b[1];
+}
+
+u32int
+beload4(uchar *b)
+{
+ return (b[0]<<24) | (b[1]<<16) | (b[2]<<8) | b[3];
+}
+
+u64int
+beload8(uchar *b)
+{
+ return ((uvlong)beload4(b) << 32) | beload4(b+4);
+}
diff --git a/src/libmach/sym.c b/src/libmach/sym.c
new file mode 100644
index 00000000..7953019b
--- /dev/null
+++ b/src/libmach/sym.c
@@ -0,0 +1,478 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+int machdebug = 0;
+
+Fhdr *fhdrlist;
+static Fhdr *last;
+
+static void
+relocsym(Symbol *dst, Symbol *src, ulong base)
+{
+ if(dst != src)
+ *dst = *src;
+ if(dst->loc.type == LADDR)
+ dst->loc.addr += base;
+ if(dst->hiloc.type == LADDR)
+ dst->hiloc.addr += base;
+}
+
+void
+_addhdr(Fhdr *h)
+{
+ h->next = nil;
+ if(fhdrlist == nil){
+ fhdrlist = h;
+ last = h;
+ }else{
+ last->next = h;
+ last = h;
+ }
+}
+
+void
+_delhdr(Fhdr *h)
+{
+ Fhdr *p;
+
+ if(h == fhdrlist)
+ fhdrlist = h->next;
+ else{
+ for(p=fhdrlist; p && p->next!=h; p=p->next)
+ ;
+ if(p)
+ p->next = h->next;
+ if(p->next == nil)
+ last = p;
+ }
+ h->next = nil;
+}
+
+int
+pc2file(ulong pc, char *file, uint nfile, ulong *line)
+{
+ Fhdr *p;
+
+ for(p=fhdrlist; p; p=p->next)
+ if(p->pc2file && p->pc2file(p, pc-p->base, file, nfile, line) >= 0)
+ return 0;
+ werrstr("no source file for 0x%lux", pc);
+ return -1;
+}
+
+int
+pc2line(ulong pc, ulong *line)
+{
+ char tmp[10]; /* just in case */
+ return pc2file(pc, tmp, sizeof tmp, line);
+}
+
+int
+file2pc(char *file, ulong line, ulong *addr)
+{
+ Fhdr *p;
+
+ for(p=fhdrlist; p; p=p->next)
+ if(p->file2pc && p->file2pc(p, file, line, addr) >= 0){
+ *addr += p->base;
+ return 0;
+ }
+ werrstr("no instructions at %s:%lud", file, line);
+ return -1;
+}
+
+int
+line2pc(ulong basepc, ulong line, ulong *pc)
+{
+ Fhdr *p;
+
+ for(p=fhdrlist; p; p=p->next)
+ if(p->line2pc && p->line2pc(p, basepc-p->base, line, pc) >= 0){
+ *pc += p->base;
+ return 0;
+ }
+ werrstr("no instructions on line %lud", line);
+ return -1;
+}
+
+int
+fnbound(ulong pc, ulong *bounds)
+{
+ Fhdr *p;
+ Loc l;
+ Symbol *s;
+
+ for(p=fhdrlist; p; p=p->next){
+ l = locaddr(pc - p->base);
+ if((s = ffindsym(p, l, CANY)) != nil){
+ if(s->loc.type != LADDR){
+ werrstr("function %s has weird location %L", s->name, s->loc);
+ return -1;
+ }
+ bounds[0] = s->loc.addr + p->base;
+ if(s->hiloc.type != LADDR){
+ werrstr("can't find upper bound for function %s", s->name);
+ return -1;
+ }
+ bounds[1] = s->hiloc.addr + p->base;
+ return 0;
+ }
+ }
+ werrstr("no function contains 0x%lux", pc);
+ return -1;
+}
+
+int
+fileline(ulong pc, char *a, uint n)
+{
+ ulong line;
+
+ if(pc2file(pc, a, n, &line) < 0)
+ return -1;
+ seprint(a+strlen(a), a+n, ":%lud", line);
+ return 0;
+}
+
+Symbol*
+flookupsym(Fhdr *fhdr, char *name)
+{
+ Symbol **a, *t;
+ uint n, m;
+ int i;
+
+ a = fhdr->byname;
+ n = fhdr->nsym;
+ if(a == nil)
+ return nil;
+
+ while(n > 0){
+ m = n/2;
+ t = a[m];
+ i = strcmp(name, t->name);
+ if(i < 0)
+ n = m;
+ else if(i > 0){
+ n -= m+1;
+ a += m+1;
+ }else{
+ /* found! */
+ m += a - fhdr->byname;
+ a = fhdr->byname;
+ assert(strcmp(name, a[m]->name) == 0);
+ while(m > 0 && strcmp(name, a[m-1]->name) == 0)
+ m--;
+ return a[m];
+ }
+ }
+ return nil;
+}
+
+int
+lookupsym(char *fn, char *var, Symbol *s)
+{
+ Symbol *t, s1;
+ Fhdr *p;
+ char *nam;
+
+ nam = fn ? fn : var;
+ if(nam == nil)
+ return -1;
+ t = nil;
+ for(p=fhdrlist; p; p=p->next)
+ if((t=flookupsym(p, nam)) != nil){
+ relocsym(&s1, t, p->base);
+ break;
+ }
+ if(t == nil)
+ goto err;
+ if(fn && var)
+ return lookuplsym(&s1, var, s);
+ *s = s1;
+ return 0;
+
+err:
+ werrstr("unknown symbol %s%s%s", fn ? fn : "",
+ fn && var ? ":" : "", var ? var : "");
+ return -1;
+}
+
+int
+findexsym(Fhdr *fp, uint i, Symbol *s)
+{
+ if(i >= fp->nsym)
+ return -1;
+ relocsym(s, &fp->sym[i], fp->base);
+ return 0;
+}
+
+int
+indexsym(uint ndx, Symbol *s)
+{
+ uint t;
+ Fhdr *p;
+
+ for(p=fhdrlist; p; p=p->next){
+ t = p->nsym;
+ if(t < ndx)
+ ndx -= t;
+ else{
+ relocsym(s, &p->sym[ndx], p->base);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+Symbol*
+ffindsym(Fhdr *fhdr, Loc loc, uint class)
+{
+ Symbol *a, *t;
+ int n, i, hi, lo;
+ int cmp;
+
+ a = fhdr->sym;
+ n = fhdr->nsym;
+ if(a == nil || n <= 0)
+ return nil;
+
+ /*
+ * We have a list of possibly duplicate locations in a.
+ * We want to find the largest index i such that
+ * a[i] <= loc. This cannot be done with a simple
+ * binary search. Instead we binary search to find
+ * where the location should be.
+ */
+ lo = 0;
+ hi = n;
+ while(lo < hi){
+ i = (lo+hi)/2;
+ cmp = loccmp(&loc, &a[i].loc);
+ if(cmp < 0) /* loc < a[i].loc */
+ hi = i;
+ if(cmp > 0) /* loc > a[i].loc */
+ lo = i+1;
+ if(cmp == 0)
+ goto found;
+ }
+
+ /* found position where value would go, but not there -- go back one */
+ if(lo == 0)
+ return nil;
+ i = lo-1;
+
+found:
+ /*
+ * might be in a run of all-the-same -- go back to beginning of run.
+ * if runs were long, could binary search for a[i].loc instead.
+ */
+ while(i > 0 && loccmp(&a[i-1].loc, &a[i].loc) == 0)
+ i--;
+
+ t = &a[i];
+ if(t->hiloc.type && loccmp(&loc, &t->hiloc) >= 0)
+ return nil;
+ if(class != CANY && class != t->class)
+ return nil;
+ return t;
+}
+
+int
+findsym(Loc loc, uint class, Symbol *s)
+{
+ Fhdr *p, *bestp;
+ Symbol *t, *best;
+ long bestd, d;
+ Loc l;
+
+ l = loc;
+ best = nil;
+ bestp = nil;
+ bestd = 0;
+ for(p=fhdrlist; p; p=p->next){
+ if(l.type == LADDR)
+ l.addr = loc.addr - p->base;
+ if((t = ffindsym(p, l, CANY)) != nil){
+ d = l.addr - t->loc.addr;
+ if(d < 4096)
+ if(best == nil || d < bestd){
+ best = t;
+ bestp = p;
+ bestd = d;
+ }
+ }
+ }
+ if(best){
+ if(class != CANY && class != best->class)
+ goto err;
+ relocsym(s, best, bestp->base);
+ return 0;
+ }
+err:
+ werrstr("could not find symbol at %L", loc);
+ return -1;
+}
+
+int
+lookuplsym(Symbol *s1, char *name, Symbol *s2)
+{
+ Fhdr *p;
+
+ p = s1->fhdr;
+ if(p->lookuplsym && p->lookuplsym(p, s1, name, s2) >= 0){
+ relocsym(s2, s2, p->base);
+ return 0;
+ }
+ return -1;
+}
+
+int
+indexlsym(Symbol *s1, uint ndx, Symbol *s2)
+{
+ Fhdr *p;
+
+ p = s1->fhdr;
+ if(p->indexlsym && p->indexlsym(p, s1, ndx, s2) >= 0){
+ relocsym(s2, s2, p->base);
+ return 0;
+ }
+ return -1;
+}
+
+int
+findlsym(Symbol *s1, Loc loc, Symbol *s2)
+{
+ Fhdr *p;
+
+ p = s1->fhdr;
+ if(p->findlsym && p->findlsym(p, s1, loc, s2) >= 0){
+ relocsym(s2, s2, p->base);
+ return 0;
+ }
+ return -1;
+}
+
+int
+unwindframe(Map *map, Regs *regs, ulong *next)
+{
+ Fhdr *p;
+
+ for(p=fhdrlist; p; p=p->next)
+ if(p->unwind && p->unwind(p, map, regs, next) >= 0)
+ return 0;
+ if(mach->unwind && mach->unwind(map, regs, next) >= 0)
+ return 0;
+ return -1;
+}
+
+int
+symoff(char *a, uint n, ulong addr, uint class)
+{
+ Loc l;
+ Symbol s;
+
+ l.type = LADDR;
+ l.addr = addr;
+ if(findsym(l, class, &s) < 0 || addr-s.loc.addr >= 4096){
+ snprint(a, n, "%lux", addr);
+ return -1;
+ }
+ if(addr != s.loc.addr)
+ snprint(a, n, "%s+%ld", s.name, addr-s.loc.addr);
+ else
+ snprint(a, n, "%s", s.name);
+ return 0;
+}
+
+/* location, class, name */
+static int
+byloccmp(const void *va, const void *vb)
+{
+ int i;
+ Symbol *a, *b;
+
+ a = (Symbol*)va;
+ b = (Symbol*)vb;
+ i = loccmp(&a->loc, &b->loc);
+ if(i != 0)
+ return i;
+ i = a->class - b->class;
+ if(i != 0)
+ return i;
+ return strcmp(a->name, b->name);
+}
+
+/* name, location, class */
+static int
+bynamecmp(const void *va, const void *vb)
+{
+ int i;
+ Symbol *a, *b;
+
+ a = *(Symbol**)va;
+ b = *(Symbol**)vb;
+ i = strcmp(a->name, b->name);
+ if(i != 0)
+ return i;
+ i = loccmp(&a->loc, &b->loc);
+ if(i != 0)
+ return i;
+ return a->class - b->class;
+}
+
+int
+syminit(Fhdr *hdr)
+{
+ int i;
+ Symbol *r, *w, *es;
+
+ if(hdr->syminit == nil){
+ werrstr("no debugging symbols");
+ return -1;
+ }
+ if(hdr->syminit(hdr) < 0)
+ return -1;
+
+ qsort(hdr->sym, hdr->nsym, sizeof(hdr->sym[0]), byloccmp);
+ es = hdr->sym+hdr->nsym;
+ for(r=w=hdr->sym; r<es; r++){
+ if(w > hdr->sym
+ && strcmp((w-1)->name, r->name) ==0
+ && loccmp(&(w-1)->loc, &r->loc) == 0){
+ /* skip it */
+ }else
+ *w++ = *r;
+ }
+ hdr->nsym = w - hdr->sym;
+
+ hdr->byname = malloc(hdr->nsym*sizeof(hdr->byname[0]));
+ if(hdr->byname == nil){
+ fprint(2, "could not allocate table to sort by location\n");
+ }else{
+ for(i=0; i<hdr->nsym; i++)
+ hdr->byname[i] = &hdr->sym[i];
+ qsort(hdr->byname, hdr->nsym, sizeof(hdr->byname[0]), bynamecmp);
+ }
+ return 0;
+}
+
+Symbol*
+addsym(Fhdr *fp, Symbol *sym)
+{
+ Symbol *s;
+
+ if(fp->nsym%128 == 0){
+ s = realloc(fp->sym, (fp->nsym+128)*sizeof(fp->sym[0]));
+ if(s == nil)
+ return nil;
+ fp->sym = s;
+ }
+ if(machdebug)
+ fprint(2, "sym %s %c %L\n", sym->name, sym->type, sym->loc);
+ sym->fhdr = fp;
+ s = &fp->sym[fp->nsym++];
+ *s = *sym;
+ return s;
+}
+
diff --git a/src/libmach/symdwarf.c b/src/libmach/symdwarf.c
new file mode 100644
index 00000000..8ced09f4
--- /dev/null
+++ b/src/libmach/symdwarf.c
@@ -0,0 +1,466 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+#include "elf.h"
+#include "dwarf.h"
+
+static void dwarfsymclose(Fhdr*);
+static int dwarfpc2file(Fhdr*, ulong, char*, uint, ulong*);
+static int dwarfline2pc(Fhdr*, ulong, ulong, ulong*);
+static int dwarflookuplsym(Fhdr*, Symbol*, char*, Symbol*);
+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);
+
+int
+symdwarf(Fhdr *hdr)
+{
+ if(hdr->dwarf == nil){
+ werrstr("no dwarf debugging symbols");
+ return -1;
+ }
+
+ hdr->symclose = dwarfsymclose;
+ hdr->pc2file = dwarfpc2file;
+ hdr->line2pc = dwarfline2pc;
+ hdr->lookuplsym = dwarflookuplsym;
+ hdr->indexlsym = dwarfindexlsym;
+ hdr->findlsym = dwarffindlsym;
+ hdr->unwind = _dwarfunwind;
+ dwarfsyminit(hdr);
+
+ return 0;
+}
+
+static void
+dwarfsymclose(Fhdr *hdr)
+{
+ dwarfclose(hdr->dwarf);
+ hdr->dwarf = nil;
+}
+
+static int
+dwarfpc2file(Fhdr *fhdr, ulong pc, char *buf, uint nbuf, ulong *line)
+{
+ char *cdir, *dir, *file;
+
+ if(dwarfpctoline(fhdr->dwarf, pc, &cdir, &dir, &file, line, nil, nil) < 0)
+ return -1;
+
+ if(file[0] == '/' || (dir==nil && cdir==nil))
+ strecpy(buf, buf+nbuf, file);
+ else if((dir && dir[0] == '/') || cdir==nil)
+ snprint(buf, nbuf, "%s/%s", dir, file);
+ else
+ snprint(buf, nbuf, "%s/%s/%s", cdir, dir ? dir : "", file);
+ cleanname(buf);
+ return 0;;
+}
+
+static int
+dwarfline2pc(Fhdr *fhdr, ulong basepc, ulong line, ulong *pc)
+{
+ werrstr("dwarf line2pc not implemented");
+ return -1;
+}
+
+static uint
+typesize(Dwarf *dwarf, ulong unit, ulong tref, char *name)
+{
+ DwarfSym ds;
+
+top:
+ if(dwarfseeksym(dwarf, unit, tref-unit, &ds) < 0){
+ cannot:
+ fprint(2, "warning: cannot compute size of parameter %s (%lud %lud: %r)\n",
+ name, unit, tref);
+ return 0;
+ }
+
+ if(ds.attrs.have.bytesize)
+ return ds.attrs.bytesize;
+
+ switch(ds.attrs.tag){
+ case TagVolatileType:
+ case TagRestrictType:
+ case TagTypedef:
+ if(ds.attrs.have.type != TReference)
+ goto cannot;
+ tref = ds.attrs.type;
+ goto top;
+ }
+
+ goto cannot;
+}
+
+static int
+roundup(int s, int n)
+{
+ return (s+n-1)&~(n-1);
+}
+
+static int
+dwarflenum(Fhdr *fhdr, Symbol *p, char *name, uint j, Loc l, Symbol *s)
+{
+ int depth, bpoff;
+ DwarfSym ds;
+ Symbol s1;
+
+ if(p == nil)
+ return -1;
+
+ if(dwarfseeksym(fhdr->dwarf, p->u.dwarf.unit, p->u.dwarf.uoff, &ds) < 0)
+ return -1;
+
+ ds.depth = 1;
+ depth = 1;
+
+ bpoff = 8;
+ while(dwarfnextsym(fhdr->dwarf, &ds, 1) == 1 && depth < ds.depth){
+ if(ds.attrs.tag != TagVariable){
+ if(ds.attrs.tag != TagFormalParameter
+ && ds.attrs.tag != TagUnspecifiedParameters)
+ continue;
+ if(ds.depth != depth+1)
+ continue;
+ }
+ if(dwarftosym(fhdr, fhdr->dwarf, &ds, &s1, 1) < 0)
+ continue;
+ /* XXX move this out once there is another architecture */
+ /*
+ * gcc tells us the registers where the parameters might be
+ * held for an instruction or two. use the parameter list to
+ * recompute the actual stack locations.
+ */
+ if(fhdr->mtype == M386)
+ if(ds.attrs.tag==TagFormalParameter || ds.attrs.tag==TagUnspecifiedParameters){
+ if(s1.loc.type==LOFFSET
+ && strcmp(s1.loc.reg, "BP")==0
+ && s1.loc.offset >= 8)
+ bpoff = s1.loc.offset;
+ else{
+ s1.loc.type = LOFFSET;
+ s1.loc.reg = "BP";
+ s1.loc.offset = bpoff;
+ }
+ if(ds.attrs.tag == TagFormalParameter){
+ if(ds.attrs.have.type)
+ bpoff += roundup(typesize(fhdr->dwarf, p->u.dwarf.unit, ds.attrs.type, s1.name), 4);
+ else
+ fprint(2, "warning: cannot compute size of parameter %s\n", s1.name);
+ }
+ }
+ if(name){
+ if(strcmp(ds.attrs.name, name) != 0)
+ continue;
+ }else if(l.type){
+ if(loccmp(&s1.loc, &l) != 0)
+ continue;
+ }else{
+ if(j-- > 0)
+ continue;
+ }
+ *s = s1;
+ return 0;
+ }
+ return -1;
+}
+
+static Loc zl;
+
+static int
+dwarflookuplsym(Fhdr *fhdr, Symbol *p, char *name, Symbol *s)
+{
+ return dwarflenum(fhdr, p, name, 0, zl, s);
+}
+
+static int
+dwarfindexlsym(Fhdr *fhdr, Symbol *p, uint i, Symbol *s)
+{
+ return dwarflenum(fhdr, p, nil, i, zl, s);
+}
+
+static int
+dwarffindlsym(Fhdr *fhdr, Symbol *p, Loc l, Symbol *s)
+{
+ return dwarflenum(fhdr, p, nil, 0, l, s);
+}
+
+static void
+dwarfsyminit(Fhdr *fp)
+{
+ Dwarf *d;
+ DwarfSym s;
+ Symbol sym;
+
+ d = fp->dwarf;
+ if(dwarfenum(d, &s) < 0)
+ return;
+
+ while(dwarfnextsym(d, &s, s.depth!=1) == 1){
+ if(s.depth != 1)
+ continue;
+ if(s.attrs.name == nil)
+ continue;
+ switch(s.attrs.tag){
+ case TagSubprogram:
+ case TagVariable:
+ if(dwarftosym(fp, d, &s, &sym, 0) < 0)
+ continue;
+ addsym(fp, &sym);
+ }
+ }
+}
+
+static char*
+regname(Dwarf *d, int i)
+{
+ if(i < 0 || i >= d->nreg)
+ return nil;
+ return d->reg[i];
+}
+
+static int
+dwarftosym(Fhdr *fp, Dwarf *d, DwarfSym *ds, Symbol *s, int infn)
+{
+ DwarfBuf buf;
+ DwarfBlock b;
+
+ memset(s, 0, sizeof *s);
+ s->u.dwarf.uoff = ds->uoff;
+ s->u.dwarf.unit = ds->unit;
+ switch(ds->attrs.tag){
+ default:
+ return -1;
+ case TagUnspecifiedParameters:
+ ds->attrs.name = "...";
+ s->type = 'p';
+ goto sym;
+ case TagFormalParameter:
+ s->type = 'p';
+ s->class = CPARAM;
+ goto sym;
+ case TagSubprogram:
+ s->type = 't';
+ s->class = CTEXT;
+ goto sym;
+ case TagVariable:
+ if(infn){
+ s->type = 'a';
+ s->class = CAUTO;
+ }else{
+ s->type = 'd';
+ s->class = CDATA;
+ }
+ sym:
+ s->name = ds->attrs.name;
+ if(ds->attrs.have.lowpc){
+ s->loc.type = LADDR;
+ s->loc.addr = ds->attrs.lowpc;
+ if(ds->attrs.have.highpc){
+ s->hiloc.type = LADDR;
+ s->hiloc.addr = ds->attrs.highpc;
+ }
+ }else if(ds->attrs.have.location == TConstant){
+ s->loc.type = LADDR;
+ s->loc.addr = ds->attrs.location.c;
+ }else if(ds->attrs.have.location == TBlock){
+ b = ds->attrs.location.b;
+ if(b.len == 0)
+ return -1;
+ buf.p = b.data+1;
+ buf.ep = b.data+b.len;
+ buf.d = d;
+ buf.addrsize = 0;
+ if(b.data[0]==OpAddr){
+ if(b.len != 5)
+ return -1;
+ s->loc.type = LADDR;
+ s->loc.addr = dwarfgetaddr(&buf);
+ }else if(OpReg0 <= b.data[0] && b.data[0] < OpReg0+0x20){
+ if(b.len != 1 || (s->loc.reg = regname(d, b.data[0]-OpReg0)) == nil)
+ return -1;
+ s->loc.type = LREG;
+ }else if(OpBreg0 <= b.data[0] && b.data[0] < OpBreg0+0x20){
+ s->loc.type = LOFFSET;
+ s->loc.reg = regname(d, b.data[0]-0x70);
+ s->loc.offset = dwarfget128s(&buf);
+ if(s->loc.reg == nil)
+ return -1;
+ }else if(b.data[0] == OpRegx){
+ s->loc.type = LREG;
+ s->loc.reg = regname(d, dwarfget128(&buf));
+ if(s->loc.reg == nil)
+ return -1;
+ }else if(b.data[0] == OpFbreg){
+ s->loc.type = LOFFSET;
+ s->loc.reg = mach->fp;
+ s->loc.offset = dwarfget128s(&buf);
+ }else if(b.data[0] == OpBregx){
+ s->loc.type = LOFFSET;
+ s->loc.reg = regname(d, dwarfget128(&buf));
+ s->loc.offset = dwarfget128s(&buf);
+ if(s->loc.reg == nil)
+ return -1;
+ }else
+ s->loc.type = LNONE;
+ if(buf.p != buf.ep)
+ s->loc.type = LNONE;
+ }else
+ return -1;
+ if(ds->attrs.isexternal)
+ s->type += 'A' - 'a';
+ if(ds->attrs.tag==TagVariable && s->loc.type==LADDR && s->loc.addr>=fp->dataddr+fp->datsz)
+ s->type += 'b' - 'd';
+ s->fhdr = fp;
+ return 0;
+ }
+}
+
+static int
+dwarfeval(Dwarf *d, Map *map, Regs *regs, ulong cfa, int rno, DwarfExpr e, ulong *u)
+{
+ int i;
+ u32int u4;
+ ulong uu;
+
+ switch(e.type){
+ case RuleUndef:
+ *u = 0;
+ return 0;
+ case RuleSame:
+ if(rno == -1){
+ werrstr("pc cannot be `same'");
+ return -1;
+ }
+ return rget(regs, regname(d, rno), u);
+ case RuleRegister:
+ if((i = windindex(regname(d, e.reg))) < 0)
+ return -1;
+ return rget(regs, regname(d, i), u);
+ case RuleCfaOffset:
+ if(cfa == 0){
+ werrstr("unknown cfa");
+ return -1;
+ }
+ if(get4(map, cfa + e.offset, &u4) < 0)
+ return -1;
+ *u = u4;
+ return 0;
+ case RuleRegOff:
+ if(rget(regs, regname(d, e.reg), &uu) < 0)
+ return -1;
+ if(get4(map, uu+e.offset, &u4) < 0)
+ return -1;
+ *u = u4;
+ return 0;
+ case RuleLocation:
+ werrstr("not evaluating dwarf loc expressions");
+ return -1;
+ }
+ werrstr("not reached in dwarfeval");
+ return -1;
+}
+
+#if 0
+static int
+dwarfexprfmt(Fmt *fmt)
+{
+ DwarfExpr *e;
+
+ if((e = va_arg(fmt->args, DwarfExpr*)) == nil)
+ return fmtstrcpy(fmt, "<nil>");
+
+ switch(e->type){
+ case RuleUndef:
+ return fmtstrcpy(fmt, "undef");
+ case RuleSame:
+ return fmtstrcpy(fmt, "same");
+ case RuleCfaOffset:
+ return fmtprint(fmt, "%ld(cfa)", e->offset);
+ case RuleRegister:
+ return fmtprint(fmt, "r%ld", e->reg);
+ case RuleRegOff:
+ return fmtprint(fmt, "%ld(r%ld)", e->offset, e->reg);
+ case RuleLocation:
+ return fmtprint(fmt, "l.%.*H", e->loc.len, e->loc.data);
+ default:
+ return fmtprint(fmt, "?%d", e->type);
+ }
+}
+#endif
+
+static int
+_dwarfunwind(Fhdr *fhdr, Map *map, Regs *regs, ulong *next)
+{
+ char *name;
+ int i, j;
+ ulong cfa, pc, u;
+ Dwarf *d;
+ DwarfExpr *e, epc, ecfa;
+
+
+ /*
+ * Use dwarfunwind to tell us what to do.
+ */
+ d = fhdr->dwarf;
+ e = malloc(d->nreg*sizeof(e[0]));
+ if(e == nil)
+ return -1;
+ if(rget(regs, mach->pc, &pc) < 0)
+ goto err;
+ if(dwarfunwind(d, pc, &ecfa, &epc, e, d->nreg) < 0)
+ goto err;
+
+ /*
+ * Compute CFA.
+ */
+ switch(ecfa.type){
+ default:
+ werrstr("invalid call-frame-address in _dwarfunwind");
+ goto err;
+ case RuleRegister:
+ ecfa.offset = 0;
+ case RuleRegOff:
+ if((name = regname(d, ecfa.reg)) == nil){
+ werrstr("invalid call-frame-address register %d", (int)ecfa.reg);
+ goto err;
+ }
+ if(rget(regs, name, &cfa) < 0){
+ werrstr("fetching %s for call-frame-address: %r", name);
+ goto err;
+ }
+ cfa += ecfa.offset;
+ }
+
+ /*
+ * Compute registers.
+ */
+ for(i=0; i<d->nreg; i++){
+ j = windindex(d->reg[i]);
+ if(j == -1)
+ continue;
+ if(dwarfeval(d, map, regs, cfa, i, e[i], &u) < 0)
+ u = ~(ulong)0;
+ next[j] = u;
+ }
+
+ /*
+ * Compute caller pc
+ */
+ if(dwarfeval(d, map, regs, cfa, -1, epc, &u) < 0){
+ werrstr("computing caller %s: %r", mach->pc);
+ goto err;
+ }
+ next[windindex(mach->pc)] = u;
+ free(e);
+ return 0;
+
+err:
+ free(e);
+ return -1;
+}
+
diff --git a/src/libmach/symelf.c b/src/libmach/symelf.c
new file mode 100644
index 00000000..7babbd49
--- /dev/null
+++ b/src/libmach/symelf.c
@@ -0,0 +1,103 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "elf.h"
+
+static int
+elfsyminit(Fhdr *fp)
+{
+ int i, onlyundef;
+ Elf *elf;
+ Symbol sym;
+ ElfSym esym;
+ ElfProg *p;
+
+ elf = fp->elf;
+
+ onlyundef = fp->nsym > 0;
+ for(i=0; elfsym(elf, i, &esym) >= 0; i++){
+ if(esym.name == nil)
+ continue;
+ if(onlyundef && esym.shndx != ElfSymShnNone)
+ continue;
+ if(esym.type != ElfSymTypeObject && esym.type != ElfSymTypeFunc)
+ continue;
+ if(strchr(esym.name, '@'))
+ continue;
+ memset(&sym, 0, sizeof sym);
+ sym.name = esym.name;
+ sym.loc.type = LADDR;
+ sym.loc.addr = esym.value;
+ if(esym.size){
+ sym.hiloc.type = LADDR;
+ sym.hiloc.addr = esym.value+esym.size;
+ }
+ sym.fhdr = fp;
+ if(esym.type==ElfSymTypeObject){
+ sym.class = CDATA;
+ sym.type = 'D';
+ if(&elf->sect[esym.shndx] == elf->bss)
+ sym.type = 'B';
+ }else if(esym.type==ElfSymTypeFunc){
+ sym.class = CTEXT;
+ sym.type = 'T';
+ }
+ if(esym.shndx == ElfSymShnNone)
+ sym.type = 'U';
+ if(esym.bind==ElfSymBindLocal)
+ sym.type += 'a' - 'A';
+ addsym(fp, &sym);
+ }
+
+ for(i=0; i<elf->nprog; i++){
+ p = &elf->prog[i];
+ if(p->type != ElfProgDynamic)
+ continue;
+ memset(&sym, 0, sizeof sym);
+ sym.name = "_DYNAMIC";
+ sym.loc = locaddr(p->vaddr);
+ sym.hiloc = locaddr(p->vaddr+p->filesz);
+ sym.type = 'D';
+ sym.class = CDATA;
+ addsym(fp, &sym);
+ }
+ return 0;
+}
+
+int
+symelf(Fhdr *fhdr)
+{
+ int ret;
+
+ ret = -1;
+
+ /* try dwarf */
+ if(fhdr->dwarf){
+ if(machdebug)
+ fprint(2, "dwarf symbols...\n");
+ if(symdwarf(fhdr) < 0)
+ fprint(2, "initializing dwarf: %r");
+ else
+ ret = 0;
+ }
+
+ /* try stabs */
+ if(fhdr->stabs.stabbase){
+ if(machdebug)
+ fprint(2, "stabs symbols...\n");
+ if(symstabs(fhdr) < 0)
+ fprint(2, "initializing stabs: %r");
+ else
+ ret = 0;
+ }
+
+ if(machdebug)
+ fprint(2, "elf symbols...\n");
+
+ if(elfsyminit(fhdr) < 0)
+ fprint(2, "initializing elf: %r");
+ else
+ ret = 0;
+ return ret;
+}
+
diff --git a/src/libmach/symmacho.c b/src/libmach/symmacho.c
new file mode 100644
index 00000000..674190ff
--- /dev/null
+++ b/src/libmach/symmacho.c
@@ -0,0 +1,50 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "macho.h"
+
+#if 0
+static int
+machosyminit(Fhdr *fp)
+{
+ /* XXX should parse dynamic symbol table here */
+ return 0;
+}
+#endif
+
+int
+symmacho(Fhdr *fp)
+{
+ int ret;
+ Macho *m;
+
+ m = fp->macho;
+ if(m == nil){
+ werrstr("not a macho");
+ return -1;
+ }
+
+ ret = -1;
+
+ if(machdebug)
+ fprint(2, "macho symbols...\n");
+
+/*
+ if(machosyminit(fp) < 0)
+ fprint(2, "initializing macho symbols: %r\n");
+ else
+ ret = 0;
+*/
+
+ if(fp->stabs.stabbase){
+ if(machdebug)
+ fprint(2, "stabs symbols...\n");
+
+ if(symstabs(fp) < 0)
+ fprint(2, "initializing stabs: %r");
+ else
+ ret = 0;
+ }
+ return ret;
+}
+
diff --git a/src/libmach/symstabs.c b/src/libmach/symstabs.c
new file mode 100644
index 00000000..8c1ddac8
--- /dev/null
+++ b/src/libmach/symstabs.c
@@ -0,0 +1,401 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "stabs.h"
+
+static int
+strcmpcolon(char *a, char *bcolon)
+{
+ int i, len;
+ char *p;
+
+ p = strchr(bcolon, ':');
+ if(p == nil)
+ return strcmp(a, bcolon);
+ len = p-bcolon;
+ i = strncmp(a, bcolon, len);
+ if(i)
+ return i;
+ if(a[len] == 0)
+ return 0;
+ return 1;
+}
+
+static int
+stabcvtsym(StabSym *stab, Symbol *sym, char *dir, char *file, int i)
+{
+ char *p;
+
+ /*
+ * Zero out the : to avoid allocating a new name string.
+ * The type info can be found by looking past the NUL.
+ * This is going to get us in trouble...
+ */
+ if((p = strchr(stab->name, ':')) != nil)
+ *p++ = 0;
+ else
+ p = stab->name+strlen(stab->name)+1;
+
+ sym->name = stab->name;
+ sym->u.stabs.dir = dir;
+ sym->u.stabs.file = file;
+ sym->u.stabs.i = i;
+ switch(stab->type){
+ default:
+ return -1;
+ case N_FUN:
+ sym->class = CTEXT;
+ switch(*p){
+ default:
+ return -1;
+ case 'F': /* global function */
+ sym->type = 'T';
+ break;
+ case 'Q': /* static procedure */
+ case 'f': /* static function */
+ case 'I': /* nested procedure */
+ case 'J': /* nested function */
+ sym->type = 't';
+ break;
+ }
+ sym->loc.type = LADDR;
+ sym->loc.addr = stab->value;
+ break;
+ case N_GSYM:
+ case N_PSYM:
+ case N_LSYM:
+ case N_LCSYM:
+ sym->class = CDATA;
+ sym->loc.type = LADDR;
+ sym->loc.addr = stab->value;
+ switch(*p){
+ default:
+ return -1;
+ case 'S': /* file-scope static variable */
+ sym->type = 'd';
+ break;
+ case 'G': /* global variable */
+ sym->type = 'D';
+ sym->loc.type = LNONE;
+ break;
+ case 'r': /* register variable */
+ sym->class = CAUTO;
+ sym->type = 'a';
+ sym->loc.type = LREG;
+ sym->loc.reg = "XXX";
+ break;
+ case 's': /* local variable */
+ sym->class = CAUTO;
+ sym->type = 'a';
+ sym->loc.type = LOFFSET;
+ sym->loc.offset = stab->value;
+ sym->loc.reg = "XXX";
+ break;
+ case 'a': /* by reference */
+ case 'D': /* f.p. parameter */
+ case 'i': /* register parameter */
+ case 'p': /* "normal" parameter */
+ case 'P': /* register parameter */
+ case 'v': /* by reference */
+ case 'X': /* function return variable */
+ sym->class = CPARAM;
+ sym->type = 'p';
+ if(*p == 'i'){
+ sym->loc.type = LREG;
+ sym->loc.reg = "XXX";
+ }else{
+ sym->loc.type = LOFFSET;
+ sym->loc.offset = stab->value;
+ sym->loc.reg = "XXX";
+ }
+ break;
+ }
+ break;
+ }
+ return 0;
+}
+
+static int
+stabssyminit(Fhdr *fp)
+{
+ int i;
+ char *dir, *file;
+ Stab *stabs;
+ StabSym sym, lastfun;
+ Symbol s, *fun;
+ char **inc, **xinc;
+ int ninc, minc;
+ int locals, autos, params;
+
+ stabs = &fp->stabs;
+ if(stabs == nil){
+ werrstr("no stabs info");
+ return -1;
+ }
+
+ dir = nil;
+ file = nil;
+ inc = nil;
+ fun = nil;
+ ninc = 0;
+ minc = 0;
+ locals = 0;
+ params = 0;
+ autos = 0;
+ memset(&lastfun, 0, sizeof lastfun);
+ for(i=0; stabsym(stabs, i, &sym)>=0; i++){
+ switch(sym.type){
+ case N_SO:
+ if(sym.name == nil || *sym.name == 0){
+ file = nil;
+ break;
+ }
+ if(sym.name[strlen(sym.name)-1] == '/')
+ dir = sym.name;
+ else
+ file = sym.name;
+ break;
+ case N_BINCL:
+ if(ninc >= minc){
+ xinc = realloc(inc, (ninc+32)*sizeof(inc[0]));
+ if(xinc){
+ memset(xinc+ninc, 0, 32*sizeof(inc[0]));
+ inc = xinc;
+ }
+ ninc += 32;
+ }
+ if(ninc < minc)
+ inc[ninc] = sym.name;
+ ninc++;
+ break;
+ case N_EINCL:
+ if(ninc > 0)
+ ninc--;
+ break;
+ case N_EXCL:
+ /* condensed include - same effect as previous BINCL/EINCL pair */
+ break;
+ case N_GSYM: /* global variable */
+ /* only includes type, so useless for now */
+ break;
+ case N_FUN:
+ if(sym.name == nil){
+ /* marks end of function */
+ if(fun){
+ fun->hiloc.type = LADDR;
+ fun->hiloc.addr = fun->loc.addr + sym.value;
+ }
+ break;
+ }
+ if(fun && lastfun.value==sym.value && lastfun.name==sym.name){
+ fun->u.stabs.locals = i;
+ break;
+ }
+ /* create new symbol, add it */
+ lastfun = sym;
+ fun = nil;
+ if(stabcvtsym(&sym, &s, dir, file, i) < 0)
+ continue;
+ if((fun = addsym(fp, &s)) == nil)
+ goto err;
+ locals = 0;
+ params = 0;
+ autos = 0;
+ break;
+ case N_PSYM:
+ case N_LSYM:
+ case N_LCSYM:
+ if(fun){
+ if(fun->u.stabs.frameptr == -1){
+ /*
+ * Try to distinguish functions with a real frame pointer
+ * from functions with a virtual frame pointer, based on
+ * whether the first parameter is in the right location and
+ * whether the autos have negative offsets.
+ *
+ * This heuristic works most of the time. On the 386, we
+ * cannot distinguish between a v. function with no autos
+ * but a frame of size 4 and a f.p. function with no autos and
+ * no frame. Anything else we'll get right.
+ *
+ * Another way to go about this would be to have
+ * mach-specific functions to inspect the function
+ * prologues when we're not sure. What we have
+ * already should be enough, though.
+ */
+ if(params==0 && sym.type == N_PSYM){
+ if(sym.value != 8 && sym.value >= 4){
+ /* XXX 386 specific, but let's find another system before generalizing */
+ fun->u.stabs.frameptr = 0;
+ fun->u.stabs.framesize = sym.value - 4;
+ }
+ }else if(sym.type == N_LSYM){
+ if(sym.value >= 0){
+ fun->u.stabs.frameptr = 0;
+ if(params)
+ fun->u.stabs.framesize = 8 - 4;
+ }else
+ fun->u.stabs.frameptr = 1;
+ }
+ }
+ if(sym.type == N_PSYM)
+ params++;
+ if(sym.type == N_LSYM)
+ autos++;
+ }
+ break;
+
+ case N_STSYM: /* static file-scope variable */
+ /* create new symbol, add it */
+ if(stabcvtsym(&sym, &s, dir, file, i) < 0)
+ continue;
+ if(addsym(fp, &s) < 0)
+ goto err;
+ break;
+ }
+ }
+ free(inc);
+ return 0;
+
+err:
+ free(inc);
+ return -1;
+}
+
+static int
+stabspc2file(Fhdr *fhdr, ulong pc, char *buf, uint nbuf, ulong *pline)
+{
+ int i;
+ Symbol *s;
+ StabSym ss;
+ ulong line, basepc;
+ Loc l;
+
+ l.type = LADDR;
+ l.addr = pc;
+ if((s = ffindsym(fhdr, l, CTEXT)) == nil
+ || stabsym(&fhdr->stabs, s->u.stabs.i, &ss) < 0)
+ return -1;
+
+ line = ss.desc;
+ basepc = ss.value;
+ for(i=s->u.stabs.i+1; stabsym(&fhdr->stabs, i, &ss) >= 0; i++){
+ if(ss.type == N_FUN && ss.name == nil)
+ break;
+ if(ss.type == N_SLINE){
+ if(basepc+ss.value > pc)
+ break;
+ else
+ line = ss.desc;
+ }
+ }
+ *pline = line;
+ if(s->u.stabs.dir)
+ snprint(buf, nbuf, "%s%s", s->u.stabs.dir, s->u.stabs.file);
+ else
+ snprint(buf, nbuf, "%s", s->u.stabs.file);
+ return 0;
+}
+
+static int
+stabsline2pc(Fhdr *fhdr, ulong startpc, ulong line, ulong *pc)
+{
+ int i, trigger;
+ Symbol *s;
+ StabSym ss;
+ ulong basepc;
+ Loc l;
+
+ l.type = LADDR;
+ l.addr = startpc;
+ if((s = ffindsym(fhdr, l, CTEXT)) == nil)
+ return -1;
+
+ trigger = 0;
+ line = ss.desc;
+ basepc = ss.value;
+ for(i=s->u.stabs.i+1; stabsym(&fhdr->stabs, i, &ss) >= 0; i++){
+ if(ss.type == N_FUN)
+ basepc = ss.value;
+ if(ss.type == N_SLINE){
+ if(basepc+ss.value >= startpc)
+ trigger = 1;
+ if(trigger && ss.desc >= line){
+ *pc = basepc+ss.value;
+ return 0;
+ }
+ }
+ }
+ return -1;
+}
+
+static int
+stabslenum(Fhdr *fhdr, Symbol *p, char *name, uint j, Loc l, Symbol *s)
+{
+ int i;
+ StabSym ss;
+
+ for(i=p->u.stabs.locals; stabsym(&fhdr->stabs, i, &ss)>=0; i++){
+ if(ss.type == N_FUN && ss.name == nil)
+ break;
+ switch(ss.type){
+ case N_PSYM:
+ case N_LSYM:
+ case N_LCSYM:
+ if(name){
+ if(strcmpcolon(name, ss.name) != 0)
+ break;
+ }else if(l.type){
+ /* wait for now */
+ }else{
+ if(j-- > 0)
+ break;
+ }
+ if(stabcvtsym(&ss, s, p->u.stabs.dir, p->u.stabs.file, i) < 0)
+ return -1;
+ if(s->loc.type == LOFFSET){
+ if(p->u.stabs.frameptr == 0)
+ s->loc.reg = mach->sp;
+ else
+ s->loc.reg = mach->fp;
+ }
+ if(l.type && loccmp(&l, &s->loc) != 0)
+ break;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static Loc zl;
+
+static int
+stabslookuplsym(Fhdr *fhdr, Symbol *p, char *name, Symbol *s)
+{
+ return stabslenum(fhdr, p, name, 0, zl, s);
+}
+
+static int
+stabsindexlsym(Fhdr *fhdr, Symbol *p, uint i, Symbol *s)
+{
+ return stabslenum(fhdr, p, nil, i, zl, s);
+}
+
+static int
+stabsfindlsym(Fhdr *fhdr, Symbol *p, Loc l, Symbol *s)
+{
+ return stabslenum(fhdr, p, nil, 0, l, s);
+}
+
+int
+symstabs(Fhdr *fp)
+{
+ if(stabssyminit(fp) < 0)
+ return -1;
+ fp->pc2file = stabspc2file;
+ fp->line2pc = stabsline2pc;
+ fp->lookuplsym = stabslookuplsym;
+ fp->indexlsym = stabsindexlsym;
+ fp->findlsym = stabsfindlsym;
+ return 0;
+}
diff --git a/src/libmach/ureg386.h b/src/libmach/ureg386.h
new file mode 100644
index 00000000..961ef6d0
--- /dev/null
+++ b/src/libmach/ureg386.h
@@ -0,0 +1,45 @@
+typedef struct Ureg Ureg;
+struct Ureg
+{
+ ulong di; /* general registers */
+ ulong si; /* ... */
+ ulong bp; /* ... */
+ ulong nsp;
+ ulong bx; /* ... */
+ ulong dx; /* ... */
+ ulong cx; /* ... */
+ ulong ax; /* ... */
+ ulong gs; /* data segments */
+ ulong fs; /* ... */
+ ulong es; /* ... */
+ ulong ds; /* ... */
+ ulong trap; /* trap type */
+ ulong ecode; /* error code (or zero) */
+ ulong pc; /* pc */
+ ulong cs; /* old context */
+ ulong flags; /* old flags */
+ ulong sp;
+ ulong ss; /* old stack segment */
+};
+
+typedef struct UregLinux386 UregLinux386;
+struct UregLinux386
+{
+ ulong ebx;
+ ulong ecx;
+ ulong edx;
+ ulong esi;
+ ulong ebp;
+ ulong eax;
+ ulong xds;
+ ulong xes;
+ ulong xfs;
+ ulong xgs;
+ ulong origeax;
+ ulong eip;
+ ulong xcs;
+ ulong eflags;
+ ulong esp;
+ ulong xss;
+};
+
diff --git a/src/libmach/uregpower.h b/src/libmach/uregpower.h
new file mode 100644
index 00000000..0e98d19d
--- /dev/null
+++ b/src/libmach/uregpower.h
@@ -0,0 +1,54 @@
+typedef struct Ureg Ureg;
+
+struct Ureg
+{
+/* 0*/ ulong cause;
+/* 4*/ ulong srr1; /* aka status */
+/* 8*/ ulong pc; /* SRR0 */
+/* 12*/ ulong pad;
+/* 16*/ ulong lr;
+/* 20*/ ulong cr;
+/* 24*/ ulong xer;
+/* 28*/ ulong ctr;
+/* 32*/ ulong r0;
+/* 36*/ ulong r1; /* aka sp */
+/* 40*/ ulong r2;
+/* 44*/ ulong r3;
+/* 48*/ ulong r4;
+/* 52*/ ulong r5;
+/* 56*/ ulong r6;
+/* 60*/ ulong r7;
+/* 64*/ ulong r8;
+/* 68*/ ulong r9;
+/* 72*/ ulong r10;
+/* 76*/ ulong r11;
+/* 80*/ ulong r12;
+/* 84*/ ulong r13;
+/* 88*/ ulong r14;
+/* 92*/ ulong r15;
+/* 96*/ ulong r16;
+/*100*/ ulong r17;
+/*104*/ ulong r18;
+/*108*/ ulong r19;
+/*112*/ ulong r20;
+/*116*/ ulong r21;
+/*120*/ ulong r22;
+/*124*/ ulong r23;
+/*128*/ ulong r24;
+/*132*/ ulong r25;
+/*136*/ ulong r26;
+/*140*/ ulong r27;
+/*144*/ ulong r28;
+/*148*/ ulong r29;
+/*152*/ ulong r30;
+/*156*/ ulong r31;
+/*160*/ ulong dcmp;
+/*164*/ ulong icmp;
+/*168*/ ulong dmiss;
+/*172*/ ulong imiss;
+/*176*/ ulong hash1;
+/*180*/ ulong hash2;
+/*184*/ ulong dar;
+/*188*/ ulong dsisr;
+/*192*/ ulong vrsave;
+};