ref: f88a55e79b5bf656e7f9578d1318a955b9a4963a
dir: /sys/src/libmach/jdb.c/
#include <u.h> #include <libc.h> #include <bio.h> #include <mach.h> #include "jc/j.out.h" static char *riscvexcep(Map*, Rgetter); /* * RISCV-specific debugger interface */ typedef struct Instr Instr; struct Instr { Map *map; ulong w; uvlong addr; char *fmt; int n; int op; int aop; int func3; int func7; char rs1, rs2, rs3, rd; char rv64; long imm; char* curr; /* fill point in buffer */ char* end; /* end of buffer */ }; typedef struct Optab Optab; struct Optab { int func7; int op[8]; }; typedef struct Opclass Opclass; struct Opclass { char *fmt; Optab tab[4]; }; /* Major opcodes */ enum { OLOAD, OLOAD_FP, Ocustom_0, OMISC_MEM, OOP_IMM, OAUIPC, OOP_IMM_32, O48b, OSTORE, OSTORE_FP, Ocustom_1, OAMO, OOP, OLUI, OOP_32, O64b, OMADD, OMSUB, ONMSUB, ONMADD, OOP_FP, Ores_0, Ocustom_2, O48b_2, OBRANCH, OJALR, Ores_1, OJAL, OSYSTEM, Ores_2, Ocustom_3, O80b }; /* copy anames from compiler */ static #include "jc/enam.c" static Opclass opOLOAD = { "a,d", 0, AMOVB, AMOVH, AMOVW, AMOV, AMOVBU, AMOVHU, AMOVWU, 0, }; static Opclass opOLOAD_FP = { "a,fd", 0, 0, 0, AMOVF, AMOVD, 0, 0, 0, 0, }; static Opclass opOMISC_MEM = { "", 0, AFENCE, AFENCE_I,0, 0, 0, 0, 0, 0, }; static Opclass opOOP_IMM = { "$i,s,d", 0x20, 0, 0, 0, 0, 0, ASRA, 0, 0, 0, AADD, ASLL, ASLT, ASLTU, AXOR, ASRL, AOR, AAND, }; static Opclass opOAUIPC = { "$i(PC),d", 0, ALUI, ALUI, ALUI, ALUI, ALUI, ALUI, ALUI, ALUI, }; static Opclass opOOP_IMM_32 = { "$i,s,d", 0x20, 0, 0, 0, 0, 0, ASRAW, 0, 0, 0, AADDW, ASLLW, 0, 0, 0, ASRLW, 0, 0, }; static Opclass opOSTORE = { "2,a", 0, AMOVB, AMOVH, AMOVW, AMOV, 0, 0, 0, 0, }; static Opclass opOSTORE_FP = { "f2,a", 0, 0, 0, AMOVF, AMOVD, 0, 0, 0, 0, }; static Opclass opOAMO = { "7,2,s,d", 0x04, 0, 0, ASWAP_W,ASWAP_D,0, 0, 0, 0, 0x08, 0, 0, ALR_W, ALR_D, 0, 0, 0, 0, 0x0C, 0, 0, ASC_W, ASC_D, 0, 0, 0, 0, 0, 0, 0, AAMO_W, AAMO_D, 0, 0, 0, 0, }; static Opclass opOOP = { "2,s,d", 0x01, AMUL, AMULH, AMULHSU,AMULHU, ADIV, ADIVU, AREM, AREMU, 0x20, ASUB, 0, 0, 0, 0, ASRA, 0, 0, 0, AADD, ASLL, ASLT, ASLTU, AXOR, ASRL, AOR, AAND, }; static Opclass opOLUI = { "$i,d", 0, ALUI, ALUI, ALUI, ALUI, ALUI, ALUI, ALUI, ALUI, }; static Opclass opOOP_32 = { "2,s,d", 0x01, AMULW, 0, 0, 0, ADIVW, ADIVUW, AREMW, AREMUW, 0x20, ASUBW, 0, 0, 0, 0, ASRAW, 0, 0, 0, AADDW, ASLLW, 0, 0, 0, ASRLW, 0, 0, }; static Opclass opOBRANCH = { "2,s,p", 0, ABEQ, ABNE, 0, 0, ABLT, ABGE, ABLTU, ABGEU, }; static Opclass opOJALR = { "d,a", 0, AJALR, AJALR, AJALR, AJALR, AJALR, AJALR, AJALR, AJALR, }; static Opclass opOJAL = { "d,p", 0, AJAL, AJAL, AJAL, AJAL, AJAL, AJAL, AJAL, AJAL, }; static Opclass opOSYSTEM = { "", 0, ASYS, ACSRRW, ACSRRS, ACSRRC, 0, ACSRRWI,ACSRRSI,ACSRRCI, }; static char fmtcsr[] = "c,s,d"; static char fmtcsri[] = "c,js,d"; static char *fmtOSYSTEM[8] = { "$i", fmtcsr, fmtcsr, fmtcsr, "", fmtcsri, fmtcsri, fmtcsri, }; static Opclass opOOP_FP = { "fs,fd", 0x0, AADDF, ASUBF, AMULF, ADIVF, AMOVF, 0, 0, 0, 0x1, AMOVDF, 0, 0, 0, 0, 0, 0, 0, 0x2, ACMPLEF,ACMPLTF,ACMPEQF,0, 0, 0, 0, 0, 0x3, AMOVFW, 0, AMOVFV, 0, AMOVWF, AMOVUF, AMOVVF, AMOVUVF, }; static Opclass opOOP_DP = { "f2,fs,fd", 0x0, AADDD, ASUBD, AMULD, ADIVD, AMOVD, 0, 0, 0, 0x1, AMOVFD, 0, 0, 0, 0, 0, 0, 0, 0x2, ACMPLED,ACMPLTD,ACMPEQD,0, 0, 0, 0, 0, 0x3, AMOVDW, 0, AMOVDV, 0, AMOVWD, AMOVUD, AMOVVD, AMOVUVD, }; typedef struct Compclass Compclass; struct Compclass { char *fmt; uchar immbits[18]; }; static Compclass rv32compressed[0x2E] = { /* 00-07 ([1:0] = 0) ([15:13] = 0-7) */ {"ADDI4SPN $i,d", 22, 6, 5, 11, 12, 7, 8, 9, 10}, /* 12:5 → 5:4|9:6|2|3 */ {"FLD a,fd", 24, 10, 11, 12, 5, 6}, /* 12:10|6:5 → 5:3|7:6 */ {"LW a,d", 25, 6, 10, 11, 12, 5}, /* 12:10|6:5 → 5:2|6 */ {"FLW a,fd", 25, 6, 10, 11, 12, 5}, /* 12:10|6:5 → 5:2|6 rv32 */ {"? ", 0}, {"FSD f2,a", 24, 10, 11, 12, 5, 6}, /* 12:10|6:5 → 5:3|7:6 */ {"SW 2,a", 25, 6, 10, 11, 12, 5}, /* 12:10|6:5 → 5:2|6 */ {"FSW f2,a", 25, 6, 10, 11, 12, 5}, /* 12:10|6:5 → 5:2|6 rv32 */ /* 08-0F ([1:0] = 1) ([15:13] = 0-7 not 4) */ {"ADDI $i,d", ~26, 2, 3, 4, 5, 6, 12}, /* 12|6:2 → * 5:0 */ {"JAL p", ~20, 3, 4, 5, 11, 2, 7, 6, 9, 10, 8, 12}, /* 12:2 → * 11|4|9:8|10|6|7|3:1|5 rv32 D*/ {"LI $i,d", ~26, 2, 3, 4, 5, 6, 12}, /* 12|6:2 → * 5:0 */ {"LUI $i,d", ~14, 2, 3, 4, 5, 6, 12}, /* 12|6:2 → * 17:12 */ {"? ", 0}, {"J p", ~20, 3, 4, 5, 11, 2, 7, 6, 9, 10, 8, 12}, /* 12:2 → * 11|4|9:8|10|6|7|3:1|5 */ {"BEQZ s,p", ~23, 3, 4, 10, 11, 2, 5, 6, 12}, /* 12:10|6:2 → * 8|4|3|7:6|2:1|5 */ {"BNEZ s,p", ~23, 3, 4, 10, 11, 2, 5, 6, 12}, /* 12:10|6:2 → * 8|4|3|7:6|2:1|5 */ /* 10-17 ([1:0] = 2) ([15:13] = 0-7 not 4) */ {"SLLI $i,d", 26, 2, 3, 4, 5, 6, 12}, /* 12|6:2 → 5:0 */ {"FLDSP i,fd", 23, 5, 6, 12, 2, 3, 4}, /* 12|6:2 → 5:3|8:6 */ {"LWSP i,d", 24, 4, 5, 6, 12, 2, 3}, /* 12|6:2 → 5:2|7:6 */ {"FLWSP i,fd", 24, 4, 5, 6, 12, 2, 3}, /* 12|6:2 → 5:2|7:6 rv32 */ {"? ", 0}, {"FSDSP f2,$i", 23, 10, 11, 12, 7, 8, 9}, /* 12:7 → 5:3|8:6 */ {"SWSP 2,$i", 24, 9, 10, 11, 12, 7, 8}, /* 12:7 → 5:2|7:6 */ {"FSWSP f2,$i", 24, 9, 10, 11, 12, 7, 8}, /* 12:7 → 5:2|7:6 rv32 */ /* 18-1A ([1:0] = 1) ([15:13] = 4) ([11:10] = 0-2) */ {"SRLI $i,d", 26, 2, 3, 4, 5, 6, 12}, /* 12|6:2 → 5:0 */ {"SRAI $i,d", 26, 2, 3, 4, 5, 6, 12}, /* 12|6:2 → 5:0 */ {"ANDI $i,d", ~26, 2, 3, 4, 5, 6, 12}, /* 12|6:2 → * 5:0 */ /* 1B-22 ([1:0] = 1) ([15:13] = 4) ([11:10] = 3) ([12] = 0-1) ([6:5] = 0-3) */ {"SUB 2,d", 0}, {"XOR 2,d", 0}, {"OR 2,d", 0}, {"AND 2,d", 0}, {"SUBW 2,d", 0}, /* rv64 */ {"ADDW 2,d", 0}, /* rv64 */ {"? ", 0}, {"? ", 0}, /* 23-26 ([1:0] = 2) ([15:13] = 4) ([12] = 0-1) ((rs2 != 0) = 0-1) */ {"JR s", 0}, {"MV 2,d", 0}, {"JALR s", 0}, {"ADD 2,d", 0}, /* 27-27 ([1:0] = 1) ([15:13] = 3) ( rd = 2) */ {"ADDI16SP $i", ~22, 6, 2, 5, 3, 4, 12}, /* 12|6:2 → * 9|4|6|8:7|5 */ /* 28-2C rv64 alternates */ {"LD a,d", 24, 10, 11, 12, 5, 6}, /* 12:10|6:5 → 5:3|7:6 */ {"SD 2,a", 24, 10, 11, 12, 5, 6}, /* 12:10|6:5 → 5:3|7:6 */ {"ADDIW $i,d", ~26, 2, 3, 4, 5, 6, 12}, /* 12|6:2 → * 5:0 */ {"LDSP i,d", 23, 5, 6, 12, 2, 3, }, /* 12|6:2 → 5:3|8:6 */ {"SDSP 2,i", 23, 10, 11, 12, 7, 8, 9}, /* 12:7 → 5:3|8:6 */ /* 2D-2D C.ADD with (rd = 0) */ {"EBREAK", 0 } }; /* map major opcodes to opclass table */ static Opclass *opclass[32] = { [OLOAD] &opOLOAD, [OLOAD_FP] &opOLOAD_FP, [OMISC_MEM] &opOMISC_MEM, [OOP_IMM] &opOOP_IMM, [OAUIPC] &opOAUIPC, [OOP_IMM_32] &opOOP_IMM_32, [OSTORE] &opOSTORE, [OSTORE_FP] &opOSTORE_FP, [OAMO] &opOAMO, [OOP] &opOOP, [OLUI] &opOLUI, [OOP_FP] &opOOP_FP, [OOP_32] &opOOP_32, [OBRANCH] &opOBRANCH, [OJALR] &opOJALR, [OJAL] &opOJAL, [OSYSTEM] &opOSYSTEM, }; /* * Print value v as name[+offset] */ static int gsymoff(char *buf, int n, uvlong 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, "#%llux", v); if (strcmp(s.name, ".string") == 0) return snprint(buf, n, "#%llux", v); if (!delta) return snprint(buf, n, "%s", s.name); return snprint(buf, n, "%s+%llux", s.name, v-s.value); } #pragma varargck argpos bprint 2 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 void format(Instr *i, char *opcode, char *f) { int c; long imm; char reg; reg = 'R'; if(opcode != nil){ bprint(i, "%s", opcode); if(f == 0) return; bprint(i, "\t"); }else bprint(i, "C."); for(; (c = *f); f++){ switch(c){ default: bprint(i, "%c", c); break; case ' ': bprint(i, "\t"); break; case 'f': reg = 'F'; break; case 'j': reg = '$'; break; case 's': bprint(i, "%c%d", reg, i->rs1); reg = 'R'; break; case '2': bprint(i, "%c%d", reg, i->rs2); reg = 'R'; break; case '3': bprint(i, "%c%d", reg, i->rs3); break; case 'd': bprint(i, "%c%d", reg, i->rd); reg = 'R'; break; case 'i': imm = i->imm; if(imm < 0) bprint(i, "-%lux", -imm); else bprint(i, "%lux", imm); break; case 'p': i->curr += gsymoff(i->curr, i->end-i->curr, i->addr + i->imm, CANY); break; case 'a': if(i->rs1 == REGSB && mach->sb){ i->curr += gsymoff(i->curr, i->end-i->curr, i->imm+mach->sb, CANY); bprint(i, "(SB)"); break; } bprint(i, "%lx(R%d)", i->imm, i->rs1); break; case '7': bprint(i, "%ux", i->func7); break; case 'c': bprint(i, "CSR(%lx)", i->imm&0xFFF); break; } } } static int badinst(Instr *i) { format(i, "???", 0); return 4; } static long immshuffle(uint w, uchar *p) { int shift, i; ulong imm; shift = *p++; imm = 0; while((i = *p++) != 0){ imm >>= 1; if((w>>i) & 0x01) imm |= (1<<31); } if(shift & 0x80) imm = (long)imm >> (shift ^ 0xFF); else imm >>= shift; return imm; } static int decompress(Instr *i) { ushort w; int op, aop; Compclass *cop; w = i->w; i->n = 2; i->func3 = (w>>13)&0x7; op = w&0x3; i->op = op; switch(op){ case 0: i->rd = 8 + ((w>>2)&0x7); i->rs1 = 8 + ((w>>7)&0x7); i->rs2 = i->rd; break; case 1: i->rd = (w>>7)&0x1F; if((i->func3&0x4) != 0) i->rd = 8 + (i->rd&0x7); i->rs1 = i->rd; i->rs2 = 8 + ((w>>2)&0x7); break; case 2: i->rd = (w>>7)&0x1F; i->rs1 = i->rd; i->rs2 = (w>>2)&0x1F; } aop = (op << 3) + i->func3; if((aop & 0x7) == 4){ switch(op){ case 1: aop = 0x18 + ((w>>10) & 0x3); if(aop == 0x1B) aop += ((w>>10) & 0x4) + ((w>>5) & 0x3); break; case 2: aop = 0x23 + ((w>>11) & 0x2) + (i->rs2 != 0); if(aop == 0x26 && i->rd == 0) aop = 0x2D; break; } } if(aop == 0x0B && i->rd == 2) aop = 0x27; if(i->rv64) switch(aop){ case 0x03: aop = 0x28; break; case 0x07: aop = 0x29; break; case 0x09: aop = 0x2A; break; case 0x13: aop = 0x2B; break; case 0x17: aop = 0x2C; break; } i->aop = aop; cop = &rv32compressed[aop]; i->fmt = cop->fmt; i->imm = immshuffle(w, cop->immbits); return 2; } static int decode(Map *map, uvlong pc, Instr *i) { ulong w; int op; if(get4(map, pc, &w) < 0) { werrstr("can't read instruction: %r"); return -1; } i->addr = pc; i->map = map; if((w&0x3) != 3){ i->w = w & 0xFFFF; return decompress(i); } i->w = w; i->n = 4; op = (w&0x7F); i->op = op; i->func3 = (w>>12)&0x7; i->func7 = (w>>25)&0x7F; i->rs1 = (w>>15)&0x1F; i->rs2 = (w>>20)&0x1F; i->rs3 = (w>>27)&0x1F; i->rd = (w>>7)&0x1F; #define FIELD(hi,lo,off) (w>>(lo-off))&(((1<<(hi-lo+1))-1)<<off) #define LFIELD(hi,lo,off) (w<<(off-lo))&(((1<<(hi-lo+1))-1)<<off) #define SFIELD(lo,off) ((long)(w&((~0)<<lo))>>(lo-off)) switch(op>>2) { case OSTORE: /* S-type */ case OSTORE_FP: i->imm = SFIELD(25,5) | FIELD(11,7,0); break; case OBRANCH: /* B-type */ i->imm = SFIELD(31,12) | LFIELD(7,7,11) | FIELD(30,25,5) | FIELD(11,8,1); break; case OOP_IMM: /* I-type */ case OOP_IMM_32: if(i->func3 == 1 || i->func3 == 5){ /* special case ASL/ASR */ i->imm = FIELD(25,20,0); break; } /* fall through */ case OLOAD: case OLOAD_FP: case OMISC_MEM: case OJALR: case OSYSTEM: i->imm = SFIELD(20,0); break; case OAUIPC: /* U-type */ case OLUI: i->imm = SFIELD(12,12); break; case OJAL: /* J-type */ i->imm = SFIELD(31,20) | FIELD(19,12,12) | FIELD(20,20,11) | FIELD(30,21,1); break; } return 4; } static int pseudo(Instr *i, int aop) { char *op; switch(aop){ case AJAL: if(i->rd == 0){ format(i, "JMP", "p"); return 1; } break; case AJALR: if(i->rd == 0){ format(i, "JMP", "a"); return 1; } break; case AADD: if((i->op>>2) == OOP_IMM){ op = i->rv64 ? "MOV" : "MOVW"; if(i->rs1 == 0) format(i, op, "$i,d"); else if(i->rs1 == REGSB && mach->sb && i->rd != REGSB) format(i, op, "$a,d"); else if(i->imm == 0) format(i, op, "s,d"); else break; return 1; } break; case ASYS: switch(i->imm){ case 0: format(i, "ECALL", nil); return 1; case 1: format(i, "EBREAK", nil); return 1; } } return 0; } static int mkinstr(Instr *i) { Opclass *oc; Optab *o; char *fmt; int aop; if((i->op&0x3) != 0x3){ format(i, nil, i->fmt); return 2; } oc = opclass[i->op>>2]; if(oc == 0) return badinst(i); fmt = oc->fmt; if(oc == &opOSYSTEM) fmt = fmtOSYSTEM[i->func3]; if(oc == &opOOP_FP){ if(i->func7 & 1) oc = &opOOP_DP; o = &oc->tab[i->func7>>5]; switch(o->func7){ case 0: fmt = "f2,fs,fd"; /* fall through */ default: aop = o->op[(i->func7>>2)&0x7]; if((i->func7&~1) == 0x10){ if(i->func3 == 0 && i->rs1 == i->rs2) fmt = "fs,fd"; else aop = 0; } break; case 2: aop = o->op[i->func3]; break; case 3: if(i->func7 & 0x10) return badinst(i); aop = o->op[(i->func7>>1)&0x4 | (i->rs2&0x3)]; if(i->func7 & 0x8) fmt = "s,fd"; else fmt = "fs,d"; break; } if(aop == 0) return badinst(i); format(i, anames[aop], fmt); return 4; } o = oc->tab; while(o->func7 != 0 && (i->func7 != o->func7 || o->op[i->func3] == 0)) o++; if((aop = o->op[i->func3]) == 0) return badinst(i); if(pseudo(i, aop)) return 4; format(i, anames[aop], fmt); return 4; } static int riscvdas(Map *map, uvlong pc, char modifier, char *buf, int n) { Instr i; USED(modifier); i.rv64 = 0; i.curr = buf; i.end = buf+n; if(decode(map, pc, &i) < 0) return -1; return mkinstr(&i); } static int riscv64das(Map *map, uvlong pc, char modifier, char *buf, int n) { Instr i; USED(modifier); i.rv64 = 1; i.curr = buf; i.end = buf+n; if(decode(map, pc, &i) < 0) return -1; return mkinstr(&i); } static int riscvhexinst(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 > 2*i.n) i.curr = _hexify(buf, i.w, 2*i.n - 1); *i.curr = 0; return i.n; } static int riscvinstlen(Map *map, uvlong pc) { Instr i; return decode(map, pc, &i); } static char* riscvexcep(Map*, Rgetter) { return "Trap"; } static int riscvfoll(Map *map, uvlong pc, Rgetter rget, uvlong *foll) { Instr i; char buf[8]; int len; len = decode(map, pc, &i); if(len < 0) return -1; foll[0] = pc + len; if(len == 2){ switch(i.aop){ case 0x0D: /* C.J */ case 0x0E: /* C.BEQZ */ case 0x0F: /* C.BNEZ */ foll[1] = pc + i.imm; return 2; case 0x09: /* C.JAL */ foll[0] = pc + i.imm; break; case 0x23: /* C.JR */ case 0x25: /* C.JALR */ sprint(buf, "R%d", i.rs1); foll[0] = (*rget)(map, buf); break; } return 1; } switch(i.op>>2) { case OBRANCH: foll[1] = pc + i.imm; return 2; case OJAL: foll[0] = pc + i.imm; break; case OJALR: sprint(buf, "R%d", i.rd); foll[0] = (*rget)(map, buf); break; } return 1; } /* * Debugger interface */ Machdata riscvmach = { {0x02, 0x90}, /* 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 */ riscvexcep, /* print exception */ 0, /* breakpoint fixup */ leieeesftos, /* single precision float printer */ leieeedftos, /* double precision float printer */ riscvfoll, /* following addresses */ riscvdas, /* symbolic disassembly */ riscvhexinst, /* hex disassembly */ riscvinstlen, /* instruction size */ }; Machdata riscv64mach = { {0x02, 0x90}, /* 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 */ riscvexcep, /* print exception */ 0, /* breakpoint fixup */ leieeesftos, /* single precision float printer */ leieeedftos, /* double precision float printer */ riscvfoll, /* following addresses */ riscv64das, /* symbolic disassembly */ riscvhexinst, /* hex disassembly */ riscvinstlen, /* instruction size */ };