summaryrefslogtreecommitdiff
path: root/utils/libmach/tdb.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/libmach/tdb.c')
-rw-r--r--utils/libmach/tdb.c839
1 files changed, 839 insertions, 0 deletions
diff --git a/utils/libmach/tdb.c b/utils/libmach/tdb.c
new file mode 100644
index 00000000..8eddc43a
--- /dev/null
+++ b/utils/libmach/tdb.c
@@ -0,0 +1,839 @@
+#include <lib9.h>
+#include <bio.h>
+#include "mach.h"
+
+static int debug = 0;
+
+typedef struct Instr Instr;
+struct Instr
+{
+ Map *map;
+ ulong w;
+ ulong addr;
+ uchar op; /* super opcode */
+
+ uchar rd;
+ uchar rn;
+ uchar rs;
+
+ long imm; /* imm */
+
+ char* curr; /* fill point in buffer */
+ char* end; /* end of buffer */
+ char* err; /* error message */
+};
+
+typedef struct Opcode Opcode;
+struct Opcode
+{
+ char* o;
+ void (*fmt)(Opcode*, Instr*);
+ ulong (*foll)(Map*, Rgetter, Instr*, ulong);
+ char* a;
+};
+
+static void format(char*, Instr*, char*);
+static char FRAMENAME[] = ".frame";
+
+/*
+ * Thumb-specific debugger interface
+ */
+
+static char *thumbexcep(Map*, Rgetter);
+static int thumbfoll(Map*, ulong, Rgetter, ulong*);
+static int thumbinst(Map*, ulong, char, char*, int);
+static int thumbdas(Map*, ulong, char*, int);
+static int thumbinstlen(Map*, ulong);
+
+/*
+ * Debugger interface
+ */
+Machdata thumbmach =
+{
+ {0x0, 0xE8}, /* break point */
+ 2, /* break point size */
+
+ leswab, /* short to local byte order */
+ leswal, /* long to local byte order */
+ leswav, /* long to local byte order */
+ risctrace, /* C traceback */
+ riscframe, /* Frame finder */
+ thumbexcep, /* print exception */
+ 0, /* breakpoint fixup */
+ 0, /* single precision float printer */
+ 0, /* double precision float printer */
+ thumbfoll, /* following addresses */
+ thumbinst, /* print instruction */
+ thumbdas, /* dissembler */
+ thumbinstlen, /* instruction size */
+};
+
+static void thumbrrh(Opcode *, Instr *);
+static void thumbbcc(Opcode *, Instr *);
+static void thumbb(Opcode *, Instr *);
+static void thumbbl(Opcode *, Instr *);
+
+static char*
+thumbexcep(Map *map, Rgetter rget)
+{
+ long c;
+
+ c = (*rget)(map, "TYPE");
+ switch (c&0x1f) {
+ case 0x11:
+ return "Fiq interrupt";
+ case 0x12:
+ return "Mirq interrupt";
+ case 0x13:
+ return "SVC/SWI Exception";
+ case 0x17:
+ return "Prefetch Abort/Data Abort";
+ case 0x18:
+ return "Data Abort";
+ case 0x1b:
+ return "Undefined instruction/Breakpoint";
+ case 0x1f:
+ return "Sys trap";
+ default:
+ return "Undefined trap";
+ }
+}
+
+static
+char* cond[16] =
+{
+ "EQ", "NE", "CS", "CC",
+ "MI", "PL", "VS", "VC",
+ "HI", "LS", "GE", "LT",
+ "GT", "LE", "\0", "NV"
+};
+
+#define B(h, l) bits(ins, h, l)
+
+static int
+bits(int i, int h, int l)
+{
+ if(h < l)
+ print("h < l in bits");
+ return (i&(((1<<(h-l+1))-1)<<l))>>l;
+}
+
+int
+thumbclass(long w)
+{
+ int o;
+ int ins = w;
+
+ if(ins&0xffff0000)
+ return 3+2+2+4+16+4+1+8+6+2+2+2+4+1+1+1+2;
+ o = B(15, 13);
+ switch(o){
+ case 0:
+ o = B(12, 11);
+ switch(o){
+ case 0:
+ case 1:
+ case 2:
+ return B(12, 11);
+ case 3:
+ if(B(10, 10) == 0)
+ return 3+B(9, 9);
+ else
+ return 3+2+B(9, 9);
+ }
+ case 1:
+ return 3+2+2+B(12, 11);
+ case 2:
+ o = B(12, 10);
+ if(o == 0)
+ return 3+2+2+4+B(9, 6);
+ if(o == 1){
+ o = B(9, 8);
+ if(o == 3)
+ return 3+2+2+4+16+B(9, 8);
+ return 3+2+2+4+16+B(9, 8);
+ }
+ if(o == 2 || o == 3)
+ return 3+2+2+4+16+4;
+ return 3+2+2+4+16+4+1+B(11, 9);
+ case 3:
+ return 3+2+2+4+16+4+1+8+B(12, 11);
+ case 4:
+ if(B(12, 12) == 0)
+ return 3+2+2+4+16+4+1+8+4+B(11, 11);
+ return 3+2+2+4+16+4+1+8+6+B(11, 11);
+ case 5:
+ if(B(12, 12) == 0)
+ return 3+2+2+4+16+4+1+8+6+2+B(11, 11);
+ if(B(11, 8) == 0)
+ return 3+2+2+4+16+4+1+8+6+2+2+B(7, 7);
+ return 3+2+2+4+16+4+1+8+6+2+2+2+B(11, 11);
+ case 6:
+ if(B(12, 12) == 0)
+ return 3+2+2+4+16+4+1+8+6+2+2+2+2+B(11, 11);
+ if(B(11, 8) == 0xf)
+ return 3+2+2+4+16+4+1+8+6+2+2+2+4;
+ return 3+2+2+4+16+4+1+8+6+2+2+2+4+1;
+ case 7:
+ o = B(12, 11);
+ switch(o){
+ case 0:
+ return 3+2+2+4+16+4+1+8+6+2+2+2+4+1+1;
+ case 1:
+ return 3+2+2+4+16+4+1+8+6+2+2+2+4+1+1+1+2;
+ case 2:
+ return 3+2+2+4+16+4+1+8+6+2+2+2+4+1+1+1;
+ case 3:
+ return 3+2+2+4+16+4+1+8+6+2+2+2+4+1+1+1+1;
+ }
+ }
+ return 0;
+}
+
+static int
+decode(Map *map, ulong pc, Instr *i)
+{
+ ushort w;
+
+ if(get2(map, pc, &w) < 0) {
+ werrstr("can't read instruction: %r");
+ return -1;
+ }
+ i->w = w;
+ i->addr = pc;
+ i->op = thumbclass(w);
+ i->map = map;
+ return 1;
+}
+
+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
+plocal(Instr *i)
+{
+ char *reg;
+ Symbol s;
+ char *fn;
+ int class;
+ int offset;
+
+ if(!findsym(i->addr, CTEXT, &s)) {
+ if(debug)fprint(2,"fn not found @%lux: %r\n", i->addr);
+ return 0;
+ }
+ fn = s.name;
+ if (!findlocal(&s, FRAMENAME, &s)) {
+ if(debug)fprint(2,"%s.%s not found @%s: %r\n", fn, FRAMENAME, s.name);
+ return 0;
+ }
+ if(s.value > i->imm) {
+ class = CAUTO;
+ offset = s.value-i->imm;
+ reg = "(SP)";
+ } else {
+ class = CPARAM;
+ offset = i->imm-s.value-4;
+ reg = "(FP)";
+ }
+ if(!getauto(&s, offset, class, &s)) {
+ if(debug)fprint(2,"%s %s not found @%ux: %r\n", fn,
+ class == CAUTO ? " auto" : "param", offset);
+ return 0;
+ }
+ bprint(i, "%s%c%d%s", s.name, class == CPARAM ? '+' : '-', s.value, reg);
+ return 1;
+}
+
+/*
+ * Print value v as name[+offset]
+ */
+static int
+gsymoff(char *buf, int n, long v, int space)
+{
+ Symbol s;
+ int r;
+ long delta;
+
+ r = delta = 0; /* to shut compiler up */
+ if (v) {
+ r = findsym(v, space, &s);
+ if (r)
+ delta = v-s.value;
+ if (delta < 0)
+ delta = -delta;
+ }
+ if (v == 0 || r == 0 || delta >= 4096)
+ return snprint(buf, n, "#%lux", v);
+ if (strcmp(s.name, ".string") == 0)
+ return snprint(buf, n, "#%lux", v);
+ if (!delta)
+ return snprint(buf, n, "%s", s.name);
+ if (s.type != 't' && s.type != 'T')
+ return snprint(buf, n, "%s+%lux", s.name, v-s.value);
+ else
+ return snprint(buf, n, "#%lux", v);
+}
+
+static int
+thumbcondpass(Map *map, Rgetter rget, uchar cond)
+{
+ ulong psr;
+ uchar n;
+ uchar z;
+ uchar c;
+ uchar v;
+
+ psr = rget(map, "PSR");
+ n = (psr >> 31) & 1;
+ z = (psr >> 30) & 1;
+ c = (psr >> 29) & 1;
+ v = (psr >> 28) & 1;
+
+ switch(cond) {
+ case 0: return z;
+ case 1: return !z;
+ case 2: return c;
+ case 3: return !c;
+ case 4: return n;
+ case 5: return !n;
+ case 6: return v;
+ case 7: return !v;
+ case 8: return c && !z;
+ case 9: return !c || z;
+ case 10: return n == v;
+ case 11: return n != v;
+ case 12: return !z && (n == v);
+ case 13: return z && (n != v);
+ case 14: return 1;
+ case 15: return 0;
+ }
+ return 0;
+}
+
+static ulong
+thumbfbranch(Map *map, Rgetter rget, Instr *i, ulong pc)
+{
+ char buf[8];
+
+ if(i->op == 30){ // BX
+ thumbrrh(nil, i);
+ sprint(buf, "R%ud", i->rn);
+ return rget(map, buf)&~1; // clear T bit
+ }
+ if(i->op == 57){ // Bcc
+ thumbbcc(nil, i);
+ if(thumbcondpass(map, rget, (i->w >> 8) & 0xf))
+ return i->imm;
+ return pc+2;
+ }
+ if(i->op == 58){ // B
+ thumbb(nil, i);
+ return i->imm;
+ }
+ if(i->op == 60){ // BL
+ thumbbl(nil, i);
+ return i->imm;
+ }
+ print("bad thumbfbranch call");
+ return 0;
+}
+
+static ulong
+thumbfmov(Map *map, Rgetter rget, Instr *i, ulong pc)
+{
+ char buf[8];
+ ulong rd;
+
+ thumbrrh(nil, i);
+ rd = i->rd;
+ if(rd != 15)
+ return pc+2;
+ sprint(buf, "R%ud", i->rn);
+ return rget(map, buf);
+}
+
+static ulong
+thumbfadd(Map *map, Rgetter rget, Instr *i, ulong pc)
+{
+ char buf[8];
+ ulong rd, v;
+
+ thumbrrh(nil, i);
+ rd = i->rd;
+ if(rd != 15)
+ return pc+2;
+ sprint(buf, "R%ud", i->rn);
+ v = rget(map, buf);
+ sprint(buf, "R15");
+ return rget(map, buf) + v;
+}
+
+static void
+thumbshift(Opcode *o, Instr *i)
+{
+ int ins = i->w;
+
+ i->rd = B(2, 0);
+ i->rn = B(5, 3);
+ i->imm = B(10, 6);
+ format(o->o, i, o->a);
+}
+
+static void
+thumbrrr(Opcode *o, Instr *i)
+{
+ int ins = i->w;
+
+ i->rd = B(2, 0);
+ i->rn = B(5, 3);
+ i->rs = B(8, 6);
+ format(o->o, i, o->a);
+}
+
+static void
+thumbirr(Opcode *o, Instr *i)
+{
+ int ins = i->w;
+
+ i->rd = B(2, 0);
+ i->rn = B(5, 3);
+ i->imm = B(8, 6);
+ format(o->o, i, o->a);
+}
+
+static void
+thumbir(Opcode *o, Instr *i)
+{
+ int ins = i->w;
+
+ i->rd = B(10, 8);
+ i->imm = B(7, 0);
+ format(o->o, i, o->a);
+}
+
+static void
+thumbrr(Opcode *o, Instr *i)
+{
+ int ins = i->w;
+
+ i->rd = B(2, 0);
+ i->rn = B(5, 3);
+ format(o->o, i, o->a);
+}
+
+static void
+thumbrrh(Opcode *o, Instr *i)
+{
+ int ins = i->w;
+
+ i->rd = B(2, 0);
+ i->rn = B(5, 3);
+ if(B(6, 6))
+ i->rn += 8;
+ if(B(7, 7))
+ i->rd += 8;
+ if(o != nil){
+ if(i->w == 0x46b7 || i->w == 0x46f7 || i->w == 0x4730 || i->w == 0x4770) // mov r6, pc or mov lr, pc or bx r6 or bx lr
+ format("RET", i, "");
+ else
+ format(o->o, i, o->a);
+ }
+}
+
+static void
+thumbpcrel(Opcode *o, Instr *i)
+{
+ int ins = i->w;
+
+ i->rn = 15;
+ i->rd = B(10, 8);
+ i->imm = 4*(B(7, 0)+1);
+ if(i->addr & 3)
+ i->imm -= 2;
+ format(o->o, i, o->a);
+}
+
+static void
+thumbmovirr(Opcode *o, Instr *i)
+{
+ int ins = i->w;
+
+ i->rd = B(2, 0);
+ i->rn = B(5, 3);
+ i->imm = B(10, 6);
+ if(strcmp(o->o, "MOVW") == 0)
+ i->imm *= 4;
+ else if(strncmp(o->o, "MOVH", 4) == 0)
+ i->imm *= 2;
+ format(o->o, i, o->a);
+}
+
+static void
+thumbmovsp(Opcode *o, Instr *i)
+{
+ int ins = i->w;
+
+ i->rn = 13;
+ i->rd = B(10, 8);
+ i->imm = 4*B(7, 0);
+ format(o->o, i, o->a);
+}
+
+static void
+thumbaddsppc(Opcode *o, Instr *i)
+{
+ int ins = i->w;
+
+ i->rd = B(10, 8);
+ i->imm = 4*B(7, 0);
+ if(i->op == 48)
+ i->imm += 4;
+ format(o->o, i, o->a);
+}
+
+static void
+thumbaddsp(Opcode *o, Instr *i)
+{
+ int ins = i->w;
+
+ i->imm = 4*B(6, 0);
+ format(o->o, i, o->a);
+}
+
+static void
+thumbswi(Opcode *o, Instr *i)
+{
+ int ins = i->w;
+
+ i->imm = B(7, 0);
+ format(o->o, i, o->a);
+}
+
+static void
+thumbbcc(Opcode *o, Instr *i)
+{
+ int off, ins = i->w;
+
+ off = B(7, 0);
+ if(off & 0x80)
+ off |= 0xffffff00;
+ i->imm = i->addr + 2*off + 4;
+ if(o != nil)
+ format(o->o, i, o->a);
+}
+
+static void
+thumbb(Opcode *o, Instr *i)
+{
+ int off, ins = i->w;
+
+ off = B(10, 0);
+ if(off & 0x400)
+ off |= 0xfffff800;
+ i->imm = i->addr + 2*off + 4;
+ if(o != nil)
+ format(o->o, i, o->a);
+}
+
+static void
+thumbbl(Opcode *o, Instr *i)
+{
+ int off, h, ins = i->w;
+ static int reglink;
+
+ h = B(11, 11);
+ off = B(10, 0);
+ if(h == 0){
+ if(off & 0x400)
+ off |= 0xfffff800;
+ i->imm = i->addr + (off<<12) + 4;
+ reglink = i->imm;
+ }
+ else{
+ i->imm = reglink + 2*off;
+ }
+ if(o != nil)
+ format(o->o, i, o->a);
+}
+
+static void
+thumbregs(Opcode *o, Instr *i)
+{
+ int ins = i->w;
+
+ if(i->op == 52 || i->op == 53)
+ i->rd = 13;
+ else
+ i->rd = B(10, 8);
+ i->imm = B(7, 0);
+ format(o->o, i, o->a);
+}
+
+static void
+thumbunk(Opcode *o, Instr *i)
+{
+ format(o->o, i, o->a);
+}
+
+static Opcode opcodes[] =
+{
+ "LSL", thumbshift, 0, "$#%i,R%n,R%d", // 0
+ "LSR", thumbshift, 0, "$#%i,R%n,R%d", // 1
+ "ASR", thumbshift, 0, "$#%i,R%n,R%d", // 2
+ "ADD", thumbrrr, 0, "R%s,R%n,R%d", // 3
+ "SUB", thumbrrr, 0, "R%s,R%n,R%d", // 4
+ "ADD", thumbirr, 0, "$#%i,R%n,R%d", // 5
+ "SUB", thumbirr, 0, "$#%i,R%n,R%d", // 6
+ "MOVW", thumbir, 0, "$#%i,R%d", // 7
+ "CMP", thumbir, 0, "$#%i,R%d", // 8
+ "ADD", thumbir, 0, "$#%i,R%d,R%d", // 9
+ "SUB", thumbir, 0, "$#%i,R%d,R%d", // 10
+ "AND", thumbrr, 0, "R%n,R%d,R%d", // 11
+ "EOR", thumbrr, 0, "R%n,R%d,R%d", // 12
+ "LSL", thumbrr, 0, "R%n,R%d,R%d", // 13
+ "LSR", thumbrr, 0, "R%n,R%d,R%d", // 14
+ "ASR", thumbrr, 0, "R%n,R%d,R%d", // 15
+ "ADC", thumbrr, 0, "R%n,R%d,R%d", // 16
+ "SBC", thumbrr, 0, "R%n,R%d,R%d", // 17
+ "ROR", thumbrr, 0, "R%n,R%d,R%d", // 18
+ "TST", thumbrr, 0, "R%n,R%d", // 19
+ "NEG", thumbrr, 0, "R%n,R%d", // 20
+ "CMP", thumbrr, 0, "R%n,R%d", // 21
+ "CMPN", thumbrr, 0, "R%n,R%d", // 22
+ "OR", thumbrr, 0, "R%n,R%d,R%d", // 23
+ "MUL", thumbrr, 0, "R%n,R%d,R%d", // 24
+ "BITC", thumbrr, 0, "R%n,R%d,R%d", // 25
+ "MOVN", thumbrr, 0, "R%n,R%d", // 26
+ "ADD", thumbrrh, thumbfadd, "R%n,R%d,R%d", // 27
+ "CMP", thumbrrh, 0, "R%n,R%d", // 28
+ "MOVW", thumbrrh, thumbfmov, "R%n,R%d", // 29
+ "BX", thumbrrh, thumbfbranch, "R%n", // 30
+ "MOVW", thumbpcrel, 0, "$%I,R%d", // 31
+ "MOVW", thumbrrr, 0, "R%d, [R%s,R%n]", // 32
+ "MOVH", thumbrrr, 0, "R%d, [R%s,R%n]", // 33
+ "MOVB", thumbrrr, 0, "R%d, [R%s,R%n]", // 34
+ "MOVB", thumbrrr, 0, "[R%s,R%n],R%d", // 35
+ "MOVW", thumbrrr, 0, "[R%s,R%n],R%d", // 36
+ "MOVHU", thumbrrr, 0, "[R%s,R%n],R%d", // 37
+ "MOVBU", thumbrrr, 0, "[R%s,R%n],R%d", // 38
+ "MOVH", thumbrrr, 0, "[R%s,R%n],R%d", // 39
+ "MOVW", thumbmovirr, 0, "R%d,%I", // 40
+ "MOVW", thumbmovirr, 0, "%I,R%d", // 41
+ "MOVB", thumbmovirr, 0, "R%d,%I", // 42
+ "MOVBU", thumbmovirr, 0, "$%I,R%d", // 43
+ "MOVH", thumbmovirr, 0, "R%d,%I", // 44
+ "MOVHU", thumbmovirr, 0, "%I,R%d", // 45
+ "MOVW", thumbmovsp, 0, "R%d,%I", // 46
+ "MOVW", thumbmovsp, 0, "%I,R%d", // 47
+ "ADD", thumbaddsppc,0, "$#%i,PC,R%d", // 48
+ "ADD", thumbaddsppc,0, "$#%i,SP,R%d", // 49
+ "ADD", thumbaddsp, 0, "$#%i,SP,SP", // 50
+ "SUB", thumbaddsp, 0, "$#%i,SP,SP", // 51
+ "PUSH", thumbregs, 0, "R%d, %r", // 52
+ "POP", thumbregs, 0, "R%d, %r", // 53
+ "STMIA", thumbregs, 0, "R%d, %r", // 54
+ "LDMIA", thumbregs, 0, "R%d, %r", // 55
+ "SWI", thumbswi, 0, "$#%i", // 56
+ "B%c", thumbbcc, thumbfbranch, "%b", // 57
+ "B", thumbb, thumbfbranch, "%b", // 58
+ "BL", thumbbl, 0, "", // 59
+ "BL", thumbbl, thumbfbranch, "%b", // 60
+ "UNK", thumbunk, 0, "", // 61
+};
+
+static void
+gaddr(Instr *i)
+{
+ *i->curr++ = '$';
+ i->curr += gsymoff(i->curr, i->end-i->curr, i->imm, CANY);
+}
+
+static void
+format(char *mnemonic, Instr *i, char *f)
+{
+ int j, k, m, n;
+ int g;
+ char *fmt;
+ int ins = i->w;
+
+ if(mnemonic)
+ format(0, i, mnemonic);
+ if(f == 0)
+ return;
+ if(mnemonic)
+ if(i->curr < i->end)
+ *i->curr++ = '\t';
+ for ( ; *f && i->curr < i->end; f++) {
+ if(*f != '%') {
+ *i->curr++ = *f;
+ continue;
+ }
+ switch (*++f) {
+
+ case 'c': /*Bcc */
+ bprint(i, "%s", cond[B(11, 8)]);
+ break;
+
+ case 's':
+ bprint(i, "%d", i->rs);
+ break;
+
+ case 'n':
+ bprint(i, "%d", i->rn);
+ break;
+
+ case 'd':
+ bprint(i, "%d", i->rd);
+ break;
+
+ case 'i':
+ bprint(i, "%lux", i->imm);
+ break;
+
+ case 'b':
+ i->curr += symoff(i->curr, i->end-i->curr,
+ i->imm, CTEXT);
+ break;
+
+ case 'I':
+ if (i->rn == 13) {
+ if (plocal(i))
+ break;
+ }
+ g = 0;
+ fmt = "#%lx(R%d)";
+ if (i->rn == 15) {
+ /* convert load of offset(PC) to a load immediate */
+ if (get4(i->map, i->addr + i->imm, &i->imm) > 0)
+ {
+ g = 1;
+ fmt = "";
+ }
+ }
+ if (mach->sb)
+ {
+ if (i->rn == 12)
+ {
+ i->imm += mach->sb;
+ g = 1;
+ fmt = "-SB(SB)";
+ }
+ }
+ if (g)
+ {
+ gaddr(i);
+ bprint(i, fmt, i->rn);
+ }
+ else
+ bprint(i, fmt, i->imm, i->rn);
+ break;
+
+ case 'r':
+ n = i->imm&0xff;
+ j = 0;
+ k = 0;
+ while(n) {
+ m = j;
+ while(n&0x1) {
+ j++;
+ n >>= 1;
+ }
+ if(j != m) {
+ if(k)
+ bprint(i, ",");
+ if(j == m+1)
+ bprint(i, "R%d", m);
+ else
+ bprint(i, "R%d-R%d", m, j-1);
+ k = 1;
+ }
+ j++;
+ n >>= 1;
+ }
+ break;
+
+ case '\0':
+ *i->curr++ = '%';
+ return;
+
+ default:
+ bprint(i, "%%%c", *f);
+ break;
+ }
+ }
+ *i->curr = 0;
+}
+
+static int
+printins(Map *map, ulong pc, char *buf, int n)
+{
+ Instr i;
+
+ i.curr = buf;
+ i.end = buf+n-1;
+ if(decode(map, pc, &i) < 0)
+ return -1;
+
+ (*opcodes[i.op].fmt)(&opcodes[i.op], &i);
+ return 2;
+}
+
+static int
+thumbinst(Map *map, ulong pc, char modifier, char *buf, int n)
+{
+ USED(modifier);
+ return printins(map, pc, buf, n);
+}
+
+static int
+thumbdas(Map *map, ulong pc, char *buf, int n)
+{
+ Instr i;
+
+ i.curr = buf;
+ i.end = buf+n;
+ if(decode(map, pc, &i) < 0)
+ return -1;
+ if(i.end-i.curr > 8)
+ i.curr = _hexify(buf, i.w, 7);
+ *i.curr = 0;
+ return 2;
+}
+
+static int
+thumbinstlen(Map *map, ulong pc)
+{
+ Instr i;
+
+ if(decode(map, pc, &i) < 0)
+ return -1;
+ return 2;
+}
+
+static int
+thumbfoll(Map *map, ulong pc, Rgetter rget, ulong *foll)
+{
+ ulong d;
+ Instr i;
+
+ if(decode(map, pc, &i) < 0)
+ return -1;
+
+ if(opcodes[i.op].foll) {
+ d = (*opcodes[i.op].foll)(map, rget, &i, pc);
+ if(d == -1)
+ return -1;
+ } else
+ d = pc+2;
+
+ foll[0] = d;
+ return 1;
+}