ref: 2e8a4a690e1c5a2cf515432e73cd24ab2a9446a7
parent: 9d9da40fd2320e36b4fa2e0060783603b2f15062
author: Jacob Moody <moody@posixcafe.org>
date: Wed Sep 20 14:09:33 EDT 2023
libmach: thumb support Most of this code is imported from inferno with some modifications. This allows us to disassemble thumb and mixed thumb/arm binaries. Source line lookup is still not correct for either thumb or mixed binaries due to bugs in tl compounded with the lack of support for variable size mach.pcquant in libmach.
--- a/sys/src/libmach/5db.c
+++ b/sys/src/libmach/5db.c
@@ -1269,6 +1269,8 @@
*i->curr = 0;
}
+extern Machdata thumbmach;
+
static int
printins(Map *map, uvlong pc, char *buf, int n)
{
@@ -1286,7 +1288,8 @@
static int
arminst(Map *map, uvlong pc, char modifier, char *buf, int n)
{
- USED(modifier);
+ if(thumbpclookup(pc))
+ return thumbmach.das(map, pc, modifier, buf, n);
return printins(map, pc, buf, n);
}
@@ -1295,6 +1298,9 @@
{
Instr i;
+ if(thumbpclookup(pc))
+ return thumbmach.hexinst(map, pc, buf, n);
+
i.curr = buf;
i.end = buf+n;
if(decode(map, pc, &i) < 0)
@@ -1309,6 +1315,9 @@
arminstlen(Map *map, uvlong pc)
{
Instr i;
+
+ if(thumbpclookup(pc))
+ return thumbmach.instsize(map, pc);
if(decode(map, pc, &i) < 0)
return -1;
--- a/sys/src/libmach/executable.c
+++ b/sys/src/libmach/executable.c
@@ -66,6 +66,7 @@
extern Mach mi386;
extern Mach mamd64;
extern Mach marm;
+extern Mach mthumb;
extern Mach marm64;
extern Mach mpower;
extern Mach mpower64;
@@ -303,8 +304,14 @@
seek(fd, mp->hsize, 0); /* seek to end of header */
break;
}
- if(mp->magic == 0)
+ switch(mp->magic){
+ case E_MAGIC:
+ thumbpctab(fd, fp);
+ break;
+ case 0:
werrstr("unknown header type");
+ break;
+ }
return ret;
}
@@ -341,6 +348,9 @@
static void
commonboot(Fhdr *fp)
{
+ /* arm needs to check for both arm and thumb */
+ if(fp->type == FARM && (fp->entry & mthumb.ktmask))
+ goto FTHUM;
if (!(fp->entry & mach->ktmask))
return;
@@ -355,6 +365,7 @@
fp->name = "386 plan 9 boot image";
fp->dataddr = _round(fp->txtaddr+fp->txtsz, mach->pgsize);
break;
+ FTHUM:
case FARM:
fp->type = FARMB;
fp->txtaddr = (u32int)fp->entry;
--- a/sys/src/libmach/mkfile
+++ b/sys/src/libmach/mkfile
@@ -10,6 +10,7 @@
access\
machdata\
setmach\
+ t\
v\
k\
u\
@@ -21,6 +22,7 @@
7\
8\
9\
+ tdb\
vdb\
kdb\
udb\
--- /dev/null
+++ b/sys/src/libmach/t.c
@@ -1,0 +1,129 @@
+/*
+ * thumb definition
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+#pragma pack on
+#include "/arm/include/ureg.h"
+#pragma pack off
+
+#define REGOFF(x) (uintptr)(&((struct Ureg *) 0)->x)
+
+#define SP REGOFF(r13)
+#define PC REGOFF(pc)
+
+#define REGSIZE sizeof(struct Ureg)
+
+Reglist thumbreglist[] =
+{
+ {"LINK", REGOFF(link), RINT|RRDONLY, 'X'},
+ {"TYPE", REGOFF(type), RINT|RRDONLY, 'X'},
+ {"PSR", REGOFF(psr), RINT|RRDONLY, 'X'},
+ {"PC", PC, RINT, 'X'},
+ {"SP", SP, RINT, 'X'},
+ {"R15", PC, RINT, 'X'},
+ {"R14", REGOFF(r14), RINT, 'X'},
+ {"R13", REGOFF(r13), RINT, 'X'},
+ {"R12", REGOFF(r12), RINT, 'X'},
+ {"R11", REGOFF(r11), RINT, 'X'},
+ {"R10", REGOFF(r10), RINT, 'X'},
+ {"R9", REGOFF(r9), RINT, 'X'},
+ {"R8", REGOFF(r8), RINT, 'X'},
+ {"R7", REGOFF(r7), RINT, 'X'},
+ {"R6", REGOFF(r6), RINT, 'X'},
+ {"R5", REGOFF(r5), RINT, 'X'},
+ {"R4", REGOFF(r4), RINT, 'X'},
+ {"R3", REGOFF(r3), RINT, 'X'},
+ {"R2", REGOFF(r2), RINT, 'X'},
+ {"R1", REGOFF(r1), RINT, 'X'},
+ {"R0", REGOFF(r0), RINT, 'X'},
+ { 0 }
+};
+
+ /* the machine description */
+Mach mthumb =
+{
+ "thumb",
+ MARM, /* machine type */
+ thumbreglist, /* register set */
+ REGSIZE, /* register set size */
+ 0, /* fp register set size */
+ "PC", /* name of PC */
+ "SP", /* name of SP */
+ "R15", /* name of link register */
+ "setR12", /* static base register name */
+ 0, /* static base register value */
+ 0x1000, /* page size */
+ 0x80000000, /* kernel base */
+ 0x88000000, /* kernel text mask */
+ 0x7FFFFFFF, /* stack top */
+ 2, /* quantization of pc */
+ 4, /* szaddr */
+ 4, /* szreg */
+ 4, /* szfloat */
+ 8, /* szdouble */
+};
+
+typedef struct pcentry pcentry;
+
+struct pcentry{
+ long start;
+ long stop;
+};
+
+static pcentry *pctab;
+static int npctab = 0;
+
+void
+thumbpctab(int fd, Fhdr *fp)
+{
+ long n;
+ uchar c[8];
+ pcentry *tab;
+
+ n = seek(fd, 0, 2)-(fp->lnpcoff+fp->lnpcsz);
+ if(n == 0)
+ return;
+ pctab = malloc(n);
+ if(pctab == nil)
+ sysfatal("could not alloc thumbpctab");
+ tab = pctab;
+ seek(fd, fp->lnpcoff+fp->lnpcsz, 0);
+ while(readn(fd, c, sizeof(c)) == sizeof(c)){
+ tab->start = fp->txtaddr + (long)((c[0]<<24)|(c[1]<<16)|(c[2]<<8)|c[3]);
+ tab->stop = fp->txtaddr + (long)((c[4]<<24)|(c[5]<<16)|(c[6]<<8)|c[7]);
+ tab++;
+ }
+ npctab = n/sizeof(c);
+}
+
+int
+thumbpclookup(uvlong pc)
+{
+ uvlong l, u, m;
+ pcentry *tab = pctab;
+
+ if(npctab == 0)
+ return 0;
+
+ l = 0;
+ u = npctab-1;
+ while(l < u){
+ m = (l+u)/2;
+ if(pc < tab[m].start){
+ if(m == 0)
+ return 0;
+ u = m-1;
+ }
+ else if(pc > tab[m].stop)
+ l = m+1;
+ else
+ l = u = m;
+ }
+ if(l == u && u < npctab && tab[u].start <= pc && pc <= tab[u].stop)
+ return 1; // thumb
+ return 0; // arm
+}
--- /dev/null
+++ b/sys/src/libmach/tdb.c
@@ -1,0 +1,840 @@
+#include <u.h>
+#include <libc.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*);
+ uvlong (*foll)(Map*, Rgetter, Instr*, uvlong);
+ 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*, uvlong, Rgetter, uvlong*);
+static int thumbinst(Map*, uvlong, char, char*, int);
+static int thumbdas(Map*, uvlong, char*, int);
+static int thumbinstlen(Map*, uvlong);
+
+/*
+ * 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)
+{
+ uvlong c;
+
+ c = (*rget)(map, "TYPE");
+ switch ((int)c&0x1f) {
+ case 0x11:
+ return "Fiq interrupt";
+ case 0x12:
+ return "Mirq interrupt";
+ case 0x13:
+ return "SVC/SWI Exception";
+ case 0x17:
+ return "Prefetch Abort/Breakpoint";
+ 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, uvlong 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+%llux", 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 uvlong
+thumbfbranch(Map *map, Rgetter rget, Instr *i, uvlong 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 uvlong
+thumbfmov(Map *map, Rgetter rget, Instr *i, uvlong 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 uvlong
+thumbfadd(Map *map, Rgetter rget, Instr *i, uvlong 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, (ulong*)&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, uvlong 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, uvlong pc, char modifier, char *buf, int n)
+{
+ USED(modifier);
+ return printins(map, pc, buf, n);
+}
+
+static int
+thumbdas(Map *map, uvlong 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, uvlong pc)
+{
+ Instr i;
+
+ if(decode(map, pc, &i) < 0)
+ return -1;
+ return 2;
+}
+
+static int
+thumbfoll(Map *map, uvlong pc, Rgetter rget, uvlong *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;
+}