implement Mdb; include "sys.m"; sys: Sys; stderr: ref Sys->FD; print, sprint: import sys; include "draw.m"; include "string.m"; str: String; include "bufio.m"; bufio: Bufio; Iobuf: import bufio; include "dis.m"; dis: Dis; Inst, Type, Data, Link, Mod: import dis; XMAGIC: import Dis; MUSTCOMPILE, DONTCOMPILE: import Dis; AMP, AFP, AIMM, AXXX, AIND, AMASK: import Dis; ARM, AXNON, AXIMM, AXINF, AXINM: import Dis; DEFB, DEFW, DEFS, DEFF, DEFA, DIND, DAPOP, DEFL: import Dis; disfile: string; m: ref Mod; Mdb: module { init: fn(nil: ref Draw->Context, argv: list of string); }; mfd: ref Sys->FD; dot := 0; lastaddr := 0; count := 1; atoi(s: string): int { b := 10; if(s == nil) return 0; if(s[0] == '0') { b = 8; s = s[1:]; if(s == nil) return 0; if(s[0] == 'x' || s[0] == 'X') { b = 16; s = s[1:]; } } n: int; (n, nil) = str->toint(s, b); return n; } eatws(s: string): string { for (i := 0; i < len s; i++) if (s[i] != ' ' && s[i] != '\t') return s[i:]; return nil; } eatnum(s: string): string { if(len s == 0) return s; while(gotnum(s) || gotalpha(s)) s = s[1:]; return s; } gotnum(s: string): int { if(len s == 0) return 0; if(s[0] >= '0' && s[0] <= '9') return 1; else return 0; } gotalpha(s: string): int { if(len s == 0) return 0; if((s[0] >= 'a' && s[0] <= 'z') || (s[0] >= 'A' && s[0] <= 'Z')) return 1; else return 0; } getexpr(s: string): (string, int, int) { ov: int; v := 0; op := '+'; for(;;) { ov = v; s = eatws(s); if(s == nil) return (nil, 0, 0); if(s[0] == '.' || s[0] == '+' || s[0] == '^') { v = dot; s = s[1:]; } else if(s[0] == '"') { v = lastaddr; s = s[1:]; } else if(s[0] == '(') { (s, v, nil) = getexpr(s[1:]); s = s[1:]; } else if(gotnum(s)) { v = atoi(s); s = eatnum(s); } else return (s, 0, 0); case op { '+' => v = ov+v; '-' => v = ov-v; '*' => v = ov*v; '%' => v = ov/v; '&' => v = ov&v; '|' => v = ov|v; } if(s == nil) return (nil, v, 1); case s[0] { '+' or '-' or '*' or '%' or '&' or '|' => op = s[0]; s = s[1:]; * => return (eatws(s), v, 1); } } } lastcmd := ""; docmd(s: string) { ok: int; n: int; s = eatws(s); (s, n, ok) = getexpr(s); if(ok) { dot = n; lastaddr = n; } count = 1; if(s != nil && s[0] == ',') { (s, n, ok) = getexpr(s[1:]); if(ok) count = n; } if(s == nil && (s = lastcmd) == nil) return; lastcmd = s; cmd := s[0]; case cmd { '?' or '/' => case s[1] { 'w' => writemem(2, s[2:]); 'W' => writemem(4, s[2:]); 'i' => das(); * => dumpmem(s[1:], cmd); } '$' => case s[1] { 'D' => desc(); 'h' => hdr(); 'l' => link(); 'i' => imports(); 'd' => dat(); 'H' => handlers(); 's' => if(m != nil) print("%s\n", m.srcpath); } '=' => dumpmem(s[1:], cmd); * => sys->fprint(stderr, "invalid cmd: %c\n", cmd); } } octal(n: int, d: int): string { s: string; do { s = string (n%8) + s; n /= 8; } while(d-- > 1); return "0" + s; } printable(c: int): string { case c { 32 to 126 => return sprint("%c", c); '\n' => return "\\n"; '\r' => return "\\r"; '\b' => return "\\b"; '\a' => return "\\a"; '\v' => return "\\v"; * => return sprint("\\x%2.2x", c); } } dumpmem(s: string, t: int) { n := 0; c := count; while(c-- > 0) for(p:=0; p n = 1; 'x' or 'd' or 'u' or 'o' => n = 2; 'X' or 'D' or 'U' or 'O' => n = 4; 's' or 'S' or 'r' or 'R' => print("'%c' format not yet supported\n", fmt); continue; 'n' => print("\n"); continue; '+' => dot++; continue; '-' => dot--; continue; '^' => dot -= n; continue; * => print("unknown format '%c'\n", fmt); continue; } b := array[n] of byte; v: int; if(t == '=') v = dot; else { sys->seek(mfd, big dot, Sys->SEEKSTART); sys->read(mfd, b, len b); v = 0; for(i := 0; i < n; i++) v |= int b[i] << (8*i); } case fmt { 'c' => print("%c", v); 'C' => print("%s", printable(v)); 'b' => print("%#2.2ux ", v); 'x' => print("%#4.4ux ", v); 'X' => print("%#8.8ux ", v); 'd' => print("%-4d ", v); 'D' => print("%-8d ", v); 'u' => print("%-4ud ", v); 'U' => print("%-8ud ", v); 'o' => print("%s ", octal(v, 6)); 'O' => print("%s ", octal(v, 11)); } if(t != '=') dot += n; } print("\n"); } writemem(n: int, s: string) { v: int; ok: int; s = eatws(s); sys->seek(mfd, big dot, Sys->SEEKSTART); for(;;) { (s, v, ok) = getexpr(s); if(!ok) return; b := array[n] of byte; for(i := 0; i < n; i++) b[i] = byte (v >> (8*i)); if (sys->write(mfd, b, len b) != len b) sys->fprint(stderr, "mdb: write error: %r\n"); } } usage() { sys->fprint(stderr, "usage: mdb [-w] file [command]\n"); raise "fail:usage"; } writeable := 0; init(nil: ref Draw->Context, argv: list of string) { sys = load Sys Sys->PATH; stderr = sys->fildes(2); str = load String String->PATH; if (str == nil) { sys->fprint(stderr, "mdb: cannot load %s: %r\n", String->PATH); raise "fail:bad module"; } bufio = load Bufio Bufio->PATH; if (bufio == nil) { sys->fprint(stderr, "mdb: cannot load %s: %r\n", Bufio->PATH); raise "fail:bad module"; } dis = load Dis Dis->PATH; dis->init(); if (len argv < 2) usage(); if (argv != nil) argv = tl argv; if (argv != nil && len hd argv && (hd argv)[0] == '-') { if (hd argv != "-w") usage(); writeable = 1; argv = tl argv; } if (argv == nil) usage(); fname := hd argv; argv = tl argv; cmd := ""; if(argv != nil) cmd = hd argv; oflags := Sys->OREAD; if (writeable) oflags = Sys->ORDWR; mfd = sys->open(fname, oflags); if(mfd == nil) { sys->fprint(stderr, "mdb: cannot open %s: %r\n", fname); raise "fail:cannot open"; } (m, nil) = dis->loadobj(fname); if(cmd != nil) docmd(cmd); else { stdin := bufio->fopen(sys->fildes(0), Sys->OREAD); while ((s := stdin.gets('\n')) != nil) { if (s[len s -1] == '\n') s = s[0:len s - 1]; docmd(s); } } } link() { if(m == nil || m.magic == 0) return; for(i := 0; i < m.lsize; i++) { l := m.links[i]; print(" link %d,%d, 0x%ux, \"%s\"\n", l.desc, l.pc, l.sig, l.name); } } imports() { if(m == nil || m.magic == 0) return; mi := m.imports; for(i := 0; i < len mi; i++) { a := mi[i]; for(j := 0; j < len a; j++) { ai := a[j]; print(" import 0x%ux, \"%s\"\n", ai.sig, ai.name); } } } handlers() { if(m == nil || m.magic == 0) return; hs := m.handlers; for(i := 0; i < len hs; i++) { h := hs[i]; tt := -1; for(j := 0; j < len m.types; j++) { if(h.t == m.types[j]) { tt = j; break; } } print(" %d-%d, o=%d, e=%d t=%d\n", h.pc1, h.pc2, h.eoff, h.ne, tt); et := h.etab; for(j = 0; j < len et; j++) { e := et[j]; if(e.s == nil) print(" %d *\n", e.pc); else print(" %d \"%s\"\n", e.pc, e.s); } } } desc() { if(m == nil || m.magic == 0) return; for(i := 0; i < m.tsize; i++) { h := m.types[i]; s := sprint(" desc $%d, %d, \"", i, h.size); for(j := 0; j < h.np; j++) s += sprint("%.2ux", int h.map[j]); s += "\"\n"; print("%s", s); } } hdr() { if(m == nil || m.magic == 0) return; s := sprint("%.8ux Version %d Dis VM\n", m.magic, m.magic - XMAGIC + 1); s += sprint("%.8ux Runtime flags %s\n", m.rt, rtflag(m.rt)); s += sprint("%8d bytes per stack extent\n\n", m.ssize); s += sprint("%8d instructions\n", m.isize); s += sprint("%8d data size\n", m.dsize); s += sprint("%8d heap type descriptors\n", m.tsize); s += sprint("%8d link directives\n", m.lsize); s += sprint("%8d entry pc\n", m.entry); s += sprint("%8d entry type descriptor\n\n", m.entryt); if(m.sign == nil) s += "Module is Insecure\n"; print("%s", s); } rtflag(flag: int): string { if(flag == 0) return ""; s := "["; if(flag & MUSTCOMPILE) s += "MustCompile"; if(flag & DONTCOMPILE) { if(flag & MUSTCOMPILE) s += "|"; s += "DontCompile"; } s[len s] = ']'; return s; } das() { if(m == nil || m.magic == 0) return; for(i := dot; count-- > 0 && i < m.isize; i++) { if(i % 10 == 0) print("#%d\n", i); print("\t%s\n", dis->inst2s(m.inst[i])); } } dat() { if(m == nil || m.magic == 0) return; print(" var @mp, %d\n", m.types[0].size); s := ""; for(d := m.data; d != nil; d = tl d) { pick dat := hd d { Bytes => s = sprint("\tbyte @mp+%d", dat.off); for(n := 0; n < dat.n; n++) s += sprint(",%d", int dat.bytes[n]); Words => s = sprint("\tword @mp+%d", dat.off); for(n := 0; n < dat.n; n++) s += sprint(",%d", dat.words[n]); String => s = sprint("\tstring @mp+%d, \"%s\"", dat.off, mapstr(dat.str)); Reals => s = sprint("\treal @mp+%d", dat.off); for(n := 0; n < dat.n; n++) s += sprint(", %g", dat.reals[n]); break; Array => s = sprint("\tarray @mp+%d,$%d,%d", dat.off, dat.typex, dat.length); Aindex => s = sprint("\tindir @mp+%d,%d", dat.off, dat.index); Arestore => s = "\tapop"; break; Bigs => s = sprint("\tlong @mp+%d", dat.off); for(n := 0; n < dat.n; n++) s += sprint(", %bd", dat.bigs[n]); } print("%s\n", s); } } mapstr(s: string): string { for(i := 0; i < len s; i++) if(s[i] == '\n') s = s[0:i] + "\\n" + s[i+1:]; return s; }