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.
This commit is contained in:
Jacob Moody 2023-09-20 18:09:33 +00:00
parent 9d9da40fd2
commit 2e8a4a690e
5 changed files with 993 additions and 2 deletions

View file

@ -1269,6 +1269,8 @@ format(char *mnemonic, Instr *i, char *f)
*i->curr = 0;
}
extern Machdata thumbmach;
static int
printins(Map *map, uvlong pc, char *buf, int n)
{
@ -1286,7 +1288,8 @@ printins(Map *map, uvlong pc, char *buf, int n)
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 @@ armdas(Map *map, uvlong pc, char *buf, int n)
{
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)
@ -1310,6 +1316,9 @@ arminstlen(Map *map, uvlong pc)
{
Instr i;
if(thumbpclookup(pc))
return thumbmach.instsize(map, pc);
if(decode(map, pc, &i) < 0)
return -1;
return 4;

View file

@ -66,6 +66,7 @@ extern Mach m68020;
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 @@ crackhdr(int fd, Fhdr *fp)
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 @@ adotout(int fd, Fhdr *fp, ExecHdr *hp)
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 @@ commonboot(Fhdr *fp)
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;

View file

@ -10,6 +10,7 @@ FILES=\
access\
machdata\
setmach\
t\
v\
k\
u\
@ -21,6 +22,7 @@ FILES=\
7\
8\
9\
tdb\
vdb\
kdb\
udb\

129
sys/src/libmach/t.c Normal file
View file

@ -0,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
}

840
sys/src/libmach/tdb.c Normal file
View file

@ -0,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;
}