ref: ea344d4512a08ee0cd09735fe3a57ca44dc4b2b1
dir: /sys/src/libmach/tdb.c/
#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;
}