db_disasm.c revision 1.14 1 /* $NetBSD: db_disasm.c,v 1.14 2005/05/31 00:40:17 chs Exp $ */
2 /*
3 * Copyright (c) 1996 Ludd, University of Lule}, Sweden.
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to Ludd by
7 * Bertram Barth.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed at Ludd, University of
20 * Lule}, Sweden and its contributors.
21 * 4. The name of the author may not be used to endorse or promote products
22 * derived from this software without specific prior written permission
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: db_disasm.c,v 1.14 2005/05/31 00:40:17 chs Exp $");
38
39 #include <sys/param.h>
40 #include <sys/proc.h>
41 #include <sys/reboot.h>
42 #include <sys/systm.h>
43
44 #include <machine/db_machdep.h>
45 #include <ddb/db_sym.h>
46 #include <ddb/db_variables.h>
47 #include <ddb/db_interface.h>
48 #include <ddb/db_output.h>
49
50 #include <vax/vax/db_disasm.h>
51
52 #ifdef VMS_MODE
53 #define DEFERRED '@'
54 #define LITERAL '#'
55 #else
56 #define DEFERRED '*'
57 #define LITERAL '$'
58 #endif
59 /*
60 * disassembling vax instructions works as follows:
61 *
62 * 1. get first byte as opcode (check for two-byte opcodes!)
63 * 2. lookup in op-table for mnemonic and operand-list
64 * 2.a store the mnemonic
65 * 3. for each operand in list: get the size/type
66 * 3.a evaluate addressing mode for this operand
67 * 3.b store each operand(s)
68 * 4. db_printf the opcode and the (value of the) operands
69 * 5. return the start of the next instruction
70 *
71 * - if jump/branch calculate (and display) the target-address
72 */
73
74 /*
75 #define BROKEN_DB_REGS
76 */
77 #ifdef BROKEN_DB_REGS
78 struct { /* Due to order and contents of db_regs[], we can't */
79 char *name; /* use this array to extract register-names. */
80 void *valuep; /* eg. "psl" vs "pc", "pc" vs "sp" */
81 } my_db_regs[16] = {
82 { "r0", NULL },
83 { "r1", NULL },
84 { "r2", NULL },
85 { "r3", NULL },
86 { "r4", NULL },
87 { "r5", NULL },
88 { "r6", NULL },
89 { "r7", NULL },
90 { "r8", NULL },
91 { "r9", NULL },
92 { "r10", NULL },
93 { "r11", NULL },
94 { "ap", NULL }, /* aka "r12" */
95 { "fp", NULL }, /* aka "r13" */
96 { "sp", NULL }, /* aka "r14" */
97 { "pc", NULL }, /* aka "r15" */
98 };
99 #else
100 #define my_db_regs db_regs
101 #endif
102
103 typedef struct {
104 char dasm[256]; /* disassebled instruction as text */
105 char *curp; /* pointer into result */
106 char *ppc; /* pseudo PC */
107 int opc; /* op-code */
108 char *argp; /* pointer into argument-list */
109 int itype; /* instruction-type, eg. branch, call, unspec */
110 int atype; /* argument-type, eg. byte, long, address */
111 int off; /* offset specified by last argument */
112 int addr; /* address specified by last argument */
113 } inst_buffer;
114
115 #define ITYPE_INVALID -1
116 #define ITYPE_UNSPEC 0
117 #define ITYPE_BRANCH 1
118 #define ITYPE_CALL 2
119
120 int get_byte __P((inst_buffer * ib));
121 int get_word __P((inst_buffer * ib));
122 int get_long __P((inst_buffer * ib));
123
124 int get_opcode __P((inst_buffer * ib));
125 int get_operands __P((inst_buffer * ib));
126 int get_operand __P((inst_buffer * ib, int size));
127
128 void add_char __P((inst_buffer * ib, int c));
129 void add_str __P((inst_buffer * ib, const char *s));
130 void add_int __P((inst_buffer * ib, int i));
131 void add_xint __P((inst_buffer * ib, int i));
132 void add_sym __P((inst_buffer * ib, int i));
133 void add_off __P((inst_buffer * ib, int i));
134
135 #define err_print printf
136
137 /*
138 * Disassemble instruction at 'loc'. 'altfmt' specifies an
139 * (optional) alternate format (altfmt for vax: don't assume
140 * that each external label is a procedure entry mask).
141 * Return address of start of next instruction.
142 * Since this function is used by 'examine' and by 'step'
143 * "next instruction" does NOT mean the next instruction to
144 * be executed but the 'linear' next instruction.
145 */
146 db_addr_t
147 db_disasm(loc, altfmt)
148 db_addr_t loc;
149 boolean_t altfmt;
150 {
151 db_expr_t diff;
152 db_sym_t sym;
153 const char *symname;
154
155 inst_buffer ib;
156
157 bzero(&ib, sizeof(ib));
158 ib.ppc = (void *) loc;
159 ib.curp = ib.dasm;
160
161 if (!altfmt) { /* ignore potential entry masks in altfmt */
162 diff = INT_MAX;
163 symname = NULL;
164 sym = db_search_symbol(loc, DB_STGY_PROC, &diff);
165 db_symbol_values(sym, &symname, 0);
166
167 if (symname && !diff) { /* symbol at loc */
168 db_printf("function \"%s()\", entry-mask 0x%x\n\t\t",
169 symname, (unsigned short) get_word(&ib));
170 ib.ppc += 2;
171 }
172 }
173 get_opcode(&ib);
174 get_operands(&ib);
175 db_printf("%s\n", ib.dasm);
176
177 return ((u_int) ib.ppc);
178 }
179
180 int
181 get_opcode(ib)
182 inst_buffer *ib;
183 {
184 ib->opc = get_byte(ib);
185 if (ib->opc >> 2 == 0x3F) { /* two byte op-code */
186 ib->opc = ib->opc << 8;
187 ib->opc += get_byte(ib);
188 }
189 switch (ib->opc) {
190 case 0xFA: /* CALLG */
191 case 0xFB: /* CALLS */
192 case 0xFC: /* XFC */
193 ib->itype = ITYPE_CALL;
194 break;
195 case 0x16: /* JSB */
196 case 0x17: /* JMP */
197 ib->itype = ITYPE_BRANCH;
198 break;
199 default:
200 ib->itype = ITYPE_UNSPEC;
201 }
202 if (ib->opc < 0 || ib->opc > 0xFF) {
203 add_str(ib, "invalid or two-byte opcode ");
204 add_xint(ib, ib->opc);
205 ib->itype = ITYPE_INVALID;
206 } else {
207 add_str(ib, vax_inst[ib->opc].mnemonic);
208 add_char(ib, '\t');
209 }
210 return (ib->opc);
211 }
212
213 int
214 get_operands(ib)
215 inst_buffer *ib;
216 {
217 int aa = 0; /* absolute address mode ? */
218 int size;
219
220 if (ib->opc < 0 || ib->opc > 0xFF) {
221 /* invalid or two-byte opcode */
222 ib->argp = NULL;
223 return (-1);
224 }
225 ib->argp = vax_inst[ib->opc].argdesc;
226
227 while (*ib->argp) {
228 switch (*ib->argp) {
229
230 case 'b': /* branch displacement */
231 switch (*(++ib->argp)) {
232 case 'b':
233 ib->off = (signed char) get_byte(ib);
234 break;
235 case 'w':
236 ib->off = (short) get_word(ib);
237 break;
238 case 'l':
239 ib->off = get_long(ib);
240 break;
241 default:
242 err_print("XXX eror\n");
243 }
244 /* add_int(ib, ib->off); */
245 ib->addr = (u_int) ib->ppc + ib->off;
246 add_off(ib, ib->addr);
247 break;
248
249 case 'a': /* absolute addressing mode */
250 aa = 1; /* do not break here ! */
251
252 default:
253 switch (*(++ib->argp)) {
254 case 'b': /* Byte */
255 size = SIZE_BYTE;
256 break;
257 case 'w': /* Word */
258 size = SIZE_WORD;
259 break;
260 case 'l': /* Long-Word */
261 case 'f': /* F_Floating */
262 size = SIZE_LONG;
263 break;
264 case 'q': /* Quad-Word */
265 case 'd': /* D_Floating */
266 case 'g': /* G_Floating */
267 size = SIZE_QWORD;
268 break;
269 case 'o': /* Octa-Word */
270 case 'h': /* H_Floating */
271 size = SIZE_OWORD;
272 break;
273 default:
274 err_print("invalid op-type %X (%c) found.\n",
275 *ib->argp, *ib->argp);
276 size = 0;
277 }
278 if (aa) {
279 /* get the address */
280 ib->addr = get_operand(ib, size);
281 add_sym(ib, ib->addr);
282 } else {
283 /* get the operand */
284 ib->addr = get_operand(ib, size);
285 add_off(ib, ib->addr);
286 }
287 }
288
289 if (!*ib->argp || !*++ib->argp)
290 break;
291 if (*ib->argp++ == ',') {
292 add_char(ib, ',');
293 add_char(ib, ' ');
294 } else {
295 err_print("XXX error\n");
296 add_char(ib, '\0');
297 return (-1);
298 }
299 }
300
301 add_char(ib, '\0');
302 return (0);
303 }
304
305 int
306 get_operand(ib, size)
307 inst_buffer *ib;
308 int size;
309 {
310 int c = get_byte(ib);
311 int mode = c >> 4;
312 int reg = c & 0x0F;
313 int lit = c & 0x3F;
314 int tmp = 0;
315 char buf[16];
316
317 switch (mode) {
318 case 0: /* literal */
319 case 1: /* literal */
320 case 2: /* literal */
321 case 3: /* literal */
322 add_char(ib, LITERAL);
323 add_int(ib, lit);
324 tmp = lit;
325 break;
326
327 case 4: /* indexed */
328 sprintf(buf, "[%s]", my_db_regs[reg].name);
329 get_operand(ib, 0);
330 add_str(ib, buf);
331 break;
332
333 case 5: /* register */
334 add_str(ib, (char *)my_db_regs[reg].name);
335 break;
336
337 case 6: /* register deferred */
338 add_char(ib, '(');
339 add_str(ib, (char *)my_db_regs[reg].name);
340 add_char(ib, ')');
341 break;
342
343 case 7: /* autodecrement */
344 add_char(ib, '-');
345 add_char(ib, '(');
346 add_str(ib, (char *)my_db_regs[reg].name);
347 add_char(ib, ')');
348 if (reg == 0x0F) { /* pc is not allowed in this mode */
349 err_print("autodecrement not allowd for PC.\n");
350 }
351 break;
352
353 case 9: /* autoincrement deferred */
354 add_char(ib, DEFERRED);
355 if (reg == 0x0F) { /* pc: immediate deferred */
356 /*
357 * addresses are always longwords!
358 */
359 tmp = get_long(ib);
360 add_off(ib, tmp);
361 break;
362 }
363 /* fall through */
364 case 8: /* autoincrement */
365 if (reg == 0x0F) { /* pc: immediate ==> special syntax */
366 switch (size) {
367 case SIZE_BYTE:
368 tmp = (signed char) get_byte(ib);
369 break;
370 case SIZE_WORD:
371 tmp = (signed short) get_word(ib);
372 break;
373 case SIZE_LONG:
374 tmp = get_long(ib);
375 break;
376 default:
377 err_print("illegal op-type %d\n", size);
378 tmp = -1;
379 }
380 if (mode == 8)
381 add_char(ib, LITERAL);
382 add_int(ib, tmp);
383 break;
384 }
385 add_char(ib, '(');
386 add_str(ib, (char *)my_db_regs[reg].name);
387 add_char(ib, ')');
388 add_char(ib, '+');
389 break;
390
391 case 11: /* byte displacement deferred/ relative deferred */
392 add_char(ib, DEFERRED);
393 case 10: /* byte displacement / relative mode */
394 tmp = (signed char) get_byte(ib);
395 if (reg == 0x0F) {
396 add_off(ib, (u_int) ib->ppc + tmp);
397 break;
398 }
399 /* add_str (ib, "b^"); */
400 add_int(ib, tmp);
401 add_char(ib, '(');
402 add_str(ib, (char *)my_db_regs[reg].name);
403 add_char(ib, ')');
404 break;
405
406 case 13: /* word displacement deferred */
407 add_char(ib, DEFERRED);
408 case 12: /* word displacement */
409 tmp = (signed short) get_word(ib);
410 if (reg == 0x0F) {
411 add_off(ib, (u_int) ib->ppc + tmp);
412 break;
413 }
414 /* add_str (ib, "w^"); */
415 add_int(ib, tmp);
416 add_char(ib, '(');
417 add_str(ib, (char *)my_db_regs[reg].name);
418 add_char(ib, ')');
419 break;
420
421 case 15: /* long displacement referred */
422 add_char(ib, DEFERRED);
423 case 14: /* long displacement */
424 tmp = get_long(ib);
425 if (reg == 0x0F) {
426 add_off(ib, (u_int) ib->ppc + tmp);
427 break;
428 }
429 /* add_str (ib, "l^"); */
430 add_int(ib, tmp);
431 add_char(ib, '(');
432 add_str(ib, (char *)my_db_regs[reg].name);
433 add_char(ib, ')');
434 break;
435
436 default:
437 err_print("can\'t evaluate operand (%02X).\n", lit);
438 break;
439 }
440
441 return (0);
442 }
443
444 int
445 get_byte(ib)
446 inst_buffer *ib;
447 {
448 return ((unsigned char) *(ib->ppc++));
449 }
450
451 int
452 get_word(ib)
453 inst_buffer *ib;
454 {
455 int tmp;
456 char *p = (void *) &tmp;
457 *p++ = get_byte(ib);
458 *p++ = get_byte(ib);
459 return (tmp);
460 }
461
462 int
463 get_long(ib)
464 inst_buffer *ib;
465 {
466 int tmp;
467 char *p = (void *) &tmp;
468 *p++ = get_byte(ib);
469 *p++ = get_byte(ib);
470 *p++ = get_byte(ib);
471 *p++ = get_byte(ib);
472 return (tmp);
473 }
474
475 void
476 add_char(ib, c)
477 inst_buffer *ib;
478 int c;
479 {
480 *ib->curp++ = c;
481 }
482
483 void
484 add_str(ib, s)
485 inst_buffer *ib;
486 const char *s;
487 {
488 while ((*ib->curp++ = *s++));
489 *--ib->curp = '\0';
490 }
491
492 void
493 add_int(ib, i)
494 inst_buffer *ib;
495 int i;
496 {
497 char buf[32];
498 if (i < 100 && i > -100)
499 sprintf(buf, "%d", i);
500 else
501 sprintf(buf, "0x%x", i);
502 add_str(ib, buf);
503 }
504
505 void
506 add_xint(ib, val)
507 inst_buffer *ib;
508 int val;
509 {
510 char buf[32];
511 sprintf(buf, "0x%x", val);
512 add_str(ib, buf);
513 }
514
515 void
516 add_sym(ib, loc)
517 inst_buffer *ib;
518 int loc;
519 {
520 db_expr_t diff;
521 db_sym_t sym;
522 const char *symname;
523
524 if (!loc)
525 return;
526
527 diff = INT_MAX;
528 symname = NULL;
529 sym = db_search_symbol(loc, DB_STGY_ANY, &diff);
530 db_symbol_values(sym, &symname, 0);
531
532 if (symname && !diff) {
533 /* add_char(ib, '<'); */
534 add_str(ib, symname);
535 /* add_char(ib, '>'); */
536 }
537 else
538 add_xint(ib, loc);
539 }
540
541 void
542 add_off(ib, loc)
543 inst_buffer *ib;
544 int loc;
545 {
546 db_expr_t diff;
547 db_sym_t sym;
548 const char *symname;
549
550 if (!loc)
551 return;
552
553 diff = INT_MAX;
554 symname = NULL;
555 sym = db_search_symbol(loc, DB_STGY_ANY, &diff);
556 db_symbol_values(sym, &symname, 0);
557
558 if (symname) {
559 /* add_char(ib, '<'); */
560 add_str(ib, symname);
561 if (diff) {
562 add_char(ib, '+');
563 add_xint(ib, diff);
564 }
565 /* add_char(ib, '>'); */
566 }
567 else
568 add_xint(ib, loc);
569 }
570