xref: /inferno-os/appl/cmd/mdb.b (revision 3f1f06c5d12b24c4061e5123acabf72348ff45a2)
1implement Mdb;
2
3include "sys.m";
4	sys: Sys;
5	stderr: ref Sys->FD;
6	print, sprint: import sys;
7
8include "draw.m";
9include "string.m";
10	str: String;
11include "bufio.m";
12	bufio: Bufio;
13	Iobuf: import bufio;
14include "dis.m";
15	dis: Dis;
16	Inst, Type, Data, Link, Mod: import dis;
17	XMAGIC: import Dis;
18	MUSTCOMPILE, DONTCOMPILE: import Dis;
19	AMP, AFP, AIMM, AXXX, AIND, AMASK: import Dis;
20	ARM, AXNON, AXIMM, AXINF, AXINM: import Dis;
21	DEFB, DEFW, DEFS, DEFF, DEFA, DIND, DAPOP, DEFL: import Dis;
22disfile: string;
23m: ref Mod;
24
25Mdb: module
26{
27	init: fn(nil: ref Draw->Context, argv: list of string);
28};
29
30mfd: ref Sys->FD;
31dot := 0;
32lastaddr := 0;
33count := 1;
34
35atoi(s: string): int
36{
37        b := 10;
38        if(s == nil)
39                return 0;
40        if(s[0] == '0') {
41                b = 8;
42                s = s[1:];
43                if(s == nil)
44                        return 0;
45                if(s[0] == 'x' || s[0] == 'X') {
46                        b = 16;
47                        s = s[1:];
48                }
49        }
50        n: int;
51        (n, nil) = str->toint(s, b);
52        return n;
53}
54
55eatws(s: string): string
56{
57	for (i := 0; i < len s; i++)
58		if (s[i] != ' ' && s[i] != '\t')
59			return s[i:];
60	return nil;
61}
62
63eatnum(s: string): string
64{
65	if(len s == 0)
66		return s;
67	while(gotnum(s) || gotalpha(s))
68		s = s[1:];
69	return s;
70}
71
72gotnum(s: string): int
73{
74	if(len s == 0)
75		return 0;
76	if(s[0] >= '0' && s[0] <= '9')
77		return 1;
78	else
79		return 0;
80}
81
82gotalpha(s: string): int
83{
84	if(len s == 0)
85		return 0;
86	if((s[0] >= 'a' && s[0] <= 'z') || (s[0] >= 'A' && s[0] <= 'Z'))
87		return 1;
88	else
89		return 0;
90}
91
92getexpr(s: string): (string, int, int)
93{
94	ov: int;
95	v := 0;
96	op := '+';
97	for(;;) {
98		ov = v;
99		s = eatws(s);
100		if(s == nil)
101			return (nil, 0, 0);
102		if(s[0] == '.' || s[0] == '+' || s[0] == '^') {
103			v = dot;
104			s = s[1:];
105		} else if(s[0] == '"') {
106			v = lastaddr;
107			s = s[1:];
108		} else if(s[0] == '(') {
109			(s, v, nil) = getexpr(s[1:]);
110			s = s[1:];
111		} else if(gotnum(s)) {
112			v = atoi(s);
113			s = eatnum(s);
114		} else
115			return (s, 0, 0);
116		case op {
117		'+' => v = ov+v;
118		'-' => v = ov-v;
119		'*' => v = ov*v;
120		'%' => v = ov/v;
121		'&' => v = ov&v;
122		'|' => v = ov|v;
123		}
124		if(s == nil)
125			return (nil, v, 1);
126		case s[0] {
127		'+' or '-' or '*' or '%' or '&' or '|' =>
128			op = s[0]; s = s[1:];
129		* =>
130			return (eatws(s), v, 1);
131		}
132	}
133}
134
135lastcmd := "";
136
137docmd(s: string)
138{
139	ok: int;
140	n: int;
141	s = eatws(s);
142	(s, n, ok) = getexpr(s);
143	if(ok) {
144		dot = n;
145		lastaddr = n;
146	}
147	count = 1;
148	if(s != nil && s[0] == ',') {
149		(s, n, ok) = getexpr(s[1:]);
150		if(ok)
151			count = n;
152	}
153	if(s == nil && (s = lastcmd) == nil)
154		return;
155	lastcmd = s;
156	cmd := s[0];
157	case cmd {
158	'?' or '/' =>
159		case s[1] {
160		'w' =>
161			writemem(2, s[2:]);
162		'W' =>
163			writemem(4, s[2:]);
164		'i' =>
165			das();
166		* =>
167			dumpmem(s[1:], cmd);
168		}
169	'$' =>
170		case s[1] {
171		'D' =>
172			desc();
173		'h' =>
174			hdr();
175		'l' =>
176			link();
177		'i' =>
178			imports();
179		'd' =>
180			dat();
181		'H' =>
182			handlers();
183		's' =>
184			if(m != nil)
185				print("%s\n", m.srcpath);
186		}
187	'=' =>
188		dumpmem(s[1:], cmd);
189	* =>
190		sys->fprint(stderr, "invalid cmd: %c\n", cmd);
191	}
192}
193
194octal(n: int, d: int): string
195{
196	s: string;
197	do {
198		s = string (n%8) + s;
199		n /= 8;
200	} while(d-- > 1);
201	return "0" + s;
202}
203
204printable(c: int): string
205{
206	case c {
207	32 to 126 =>
208		return sprint("%c", c);
209	'\n' =>
210		return "\\n";
211	'\r' =>
212		return "\\r";
213	'\b' =>
214		return "\\b";
215	'\a' =>
216		return "\\a";
217	'\v' =>
218		return "\\v";
219	* =>
220		return sprint("\\x%2.2x", c);
221	}
222
223}
224
225dumpmem(s: string, t: int)
226{
227	n := 0;
228	c := count;
229	while(c-- > 0) for(p:=0; p<len s; p++) {
230		fmt := s[p];
231		case fmt {
232		'b' or 'c' or 'C' =>
233			n = 1;
234		'x' or 'd' or 'u' or 'o' =>
235			n = 2;
236		'X' or 'D' or 'U' or 'O' =>
237			n = 4;
238		's' or 'S' or 'r' or 'R' =>
239			print("'%c' format not yet supported\n", fmt);
240			continue;
241		'n' =>
242			print("\n");
243			continue;
244		'+' =>
245			dot++;
246			continue;
247		'-' =>
248			dot--;
249			continue;
250		'^' =>
251			dot -= n;
252			continue;
253		* =>
254			print("unknown format '%c'\n", fmt);
255			continue;
256		}
257		b := array[n] of byte;
258		v: int;
259		if(t == '=')
260			v = dot;
261		else {
262			sys->seek(mfd, big dot, Sys->SEEKSTART);
263			sys->read(mfd, b, len b);
264			v = 0;
265			for(i := 0; i < n; i++)
266				v |= int b[i] << (8*i);
267		}
268		case fmt {
269		'c' => print("%c", v);
270		'C' => print("%s", printable(v));
271		'b' => print("%#2.2ux ", v);
272		'x' => print("%#4.4ux ", v);
273		'X' => print("%#8.8ux ", v);
274		'd' => print("%-4d ", v);
275		'D' => print("%-8d ", v);
276		'u' => print("%-4ud ", v);
277		'U' => print("%-8ud ", v);
278		'o' => print("%s ", octal(v, 6));
279		'O' => print("%s ", octal(v, 11));
280		}
281		if(t != '=')
282			dot += n;
283	}
284	print("\n");
285}
286
287writemem(n: int, s: string)
288{
289	v: int;
290	ok: int;
291	s = eatws(s);
292	sys->seek(mfd, big dot, Sys->SEEKSTART);
293	for(;;) {
294		(s, v, ok) = getexpr(s);
295		if(!ok)
296			return;
297		b := array[n] of byte;
298		for(i := 0; i < n; i++)
299			b[i] = byte (v >> (8*i));
300		if (sys->write(mfd, b, len b) != len b)
301			sys->fprint(stderr, "mdb: write error: %r\n");
302	}
303}
304
305usage()
306{
307	sys->fprint(stderr, "usage: mdb [-w] file [command]\n");
308	raise "fail:usage";
309}
310
311writeable := 0;
312
313init(nil: ref Draw->Context, argv: list of string)
314{
315	sys = load Sys Sys->PATH;
316	stderr = sys->fildes(2);
317	str = load String String->PATH;
318	if (str == nil) {
319		sys->fprint(stderr, "mdb: cannot load %s: %r\n", String->PATH);
320		raise "fail:bad module";
321	}
322	bufio = load Bufio Bufio->PATH;
323	if (bufio == nil) {
324		sys->fprint(stderr, "mdb: cannot load %s: %r\n", Bufio->PATH);
325		raise "fail:bad module";
326	}
327	dis = load Dis Dis->PATH;
328	dis->init();
329
330	if (len argv < 2)
331		usage();
332	if (argv != nil)
333		argv = tl argv;
334	if (argv != nil && len hd argv && (hd argv)[0] == '-') {
335		if (hd argv != "-w")
336			usage();
337		writeable = 1;
338		argv = tl argv;
339	}
340	if (argv == nil)
341		usage();
342	fname := hd argv;
343	argv = tl argv;
344	cmd := "";
345	if(argv != nil)
346		cmd = hd argv;
347
348	oflags := Sys->OREAD;
349	if (writeable)
350		oflags = Sys->ORDWR;
351	mfd = sys->open(fname, oflags);
352	if(mfd == nil) {
353		sys->fprint(stderr, "mdb: cannot open %s: %r\n", fname);
354		raise "fail:cannot open";
355	}
356	(m, nil) = dis->loadobj(fname);
357
358	if(cmd != nil)
359		docmd(cmd);
360	else {
361		stdin := bufio->fopen(sys->fildes(0), Sys->OREAD);
362		while ((s := stdin.gets('\n')) != nil) {
363			if (s[len s -1] == '\n')
364				s = s[0:len s - 1];
365			docmd(s);
366		}
367	}
368}
369
370link()
371{
372	if(m == nil || m.magic == 0)
373		return;
374
375	for(i := 0; i < m.lsize; i++) {
376		l := m.links[i];
377		print("	link %d,%d, 0x%ux, \"%s\"\n",
378					l.desc, l.pc, l.sig, l.name);
379	}
380}
381
382imports()
383{
384	if(m == nil || m.magic == 0)
385		return;
386
387	mi := m.imports;
388	for(i := 0; i < len mi; i++) {
389		a := mi[i];
390		for(j := 0; j < len a; j++) {
391			ai := a[j];
392			print("	import 0x%ux, \"%s\"\n", ai.sig, ai.name);
393		}
394	}
395}
396
397handlers()
398{
399	if(m == nil || m.magic == 0)
400		return;
401
402	hs := m.handlers;
403	for(i := 0; i < len hs; i++) {
404		h := hs[i];
405		tt := -1;
406		for(j := 0; j < len m.types; j++) {
407			if(h.t == m.types[j]) {
408				tt = j;
409				break;
410			}
411		}
412		print("	%d-%d, o=%d, e=%d t=%d\n", h.pc1, h.pc2, h.eoff, h.ne, tt);
413		et := h.etab;
414		for(j = 0; j < len et; j++) {
415			e := et[j];
416			if(e.s == nil)
417				print("		%d	*\n", e.pc);
418			else
419				print("		%d	\"%s\"\n", e.pc, e.s);
420		}
421	}
422}
423
424desc()
425{
426	if(m == nil || m.magic == 0)
427		return;
428
429	for(i := 0; i < m.tsize; i++) {
430		h := m.types[i];
431		s := sprint("	desc $%d, %d, \"", i, h.size);
432		for(j := 0; j < h.np; j++)
433			s += sprint("%.2ux", int h.map[j]);
434		s += "\"\n";
435		print("%s", s);
436	}
437}
438
439hdr()
440{
441	if(m == nil || m.magic == 0)
442		return;
443	s := sprint("%.8ux Version %d Dis VM\n", m.magic, m.magic - XMAGIC + 1);
444	s += sprint("%.8ux Runtime flags %s\n", m.rt, rtflag(m.rt));
445	s += sprint("%8d bytes per stack extent\n\n", m.ssize);
446
447
448	s += sprint("%8d instructions\n", m.isize);
449	s += sprint("%8d data size\n", m.dsize);
450	s += sprint("%8d heap type descriptors\n", m.tsize);
451	s += sprint("%8d link directives\n", m.lsize);
452	s += sprint("%8d entry pc\n", m.entry);
453	s += sprint("%8d entry type descriptor\n\n", m.entryt);
454
455	if(m.sign == nil)
456		s += "Module is Insecure\n";
457	print("%s", s);
458}
459
460rtflag(flag: int): string
461{
462	if(flag == 0)
463		return "";
464
465	s := "[";
466
467	if(flag & MUSTCOMPILE)
468		s += "MustCompile";
469	if(flag & DONTCOMPILE) {
470		if(flag & MUSTCOMPILE)
471			s += "|";
472		s += "DontCompile";
473	}
474	s[len s] = ']';
475
476	return s;
477}
478
479das()
480{
481	if(m == nil || m.magic == 0)
482		return;
483
484	for(i := dot;  count-- > 0 && i < m.isize; i++) {
485		if(i % 10 == 0)
486			print("#%d\n", i);
487		print("\t%s\n", dis->inst2s(m.inst[i]));
488	}
489}
490
491dat()
492{
493	if(m == nil || m.magic == 0)
494		return;
495	print("	var @mp, %d\n", m.types[0].size);
496
497	s := "";
498	for(d := m.data; d != nil; d = tl d) {
499		pick dat := hd d {
500		Bytes =>
501			s = sprint("\tbyte @mp+%d", dat.off);
502			for(n := 0; n < dat.n; n++)
503				s += sprint(",%d", int dat.bytes[n]);
504		Words =>
505			s = sprint("\tword @mp+%d", dat.off);
506			for(n := 0; n < dat.n; n++)
507				s += sprint(",%d", dat.words[n]);
508		String =>
509			s = sprint("\tstring @mp+%d, \"%s\"", dat.off, mapstr(dat.str));
510		Reals =>
511			s = sprint("\treal @mp+%d", dat.off);
512			for(n := 0; n < dat.n; n++)
513				s += sprint(", %g", dat.reals[n]);
514			break;
515		Array =>
516			s = sprint("\tarray @mp+%d,$%d,%d", dat.off, dat.typex, dat.length);
517		Aindex =>
518			s = sprint("\tindir @mp+%d,%d", dat.off, dat.index);
519		Arestore =>
520			s = "\tapop";
521			break;
522		Bigs =>
523			s = sprint("\tlong @mp+%d", dat.off);
524			for(n := 0; n < dat.n; n++)
525				s += sprint(", %bd", dat.bigs[n]);
526		}
527		print("%s\n", s);
528	}
529}
530
531mapstr(s: string): string
532{
533	for(i := 0; i < len s; i++)
534		if(s[i] == '\n')
535			s = s[0:i] + "\\n" + s[i+1:];
536	return s;
537}
538