db_disasm.c revision 1.20 1 /* $NetBSD: db_disasm.c,v 1.20 2012/02/02 14:29:25 matt 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.20 2012/02/02 14:29:25 matt 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 const struct { /* Due to order and contents of db_regs[], we can't */
79 const 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 const 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 static inline int get_byte(inst_buffer * ib);
121 static inline int get_word(inst_buffer * ib);
122 static inline int get_long(inst_buffer * ib);
123
124 static int get_opcode(inst_buffer * ib);
125 static int get_operands(inst_buffer * ib);
126 static int get_operand(inst_buffer * ib, int size);
127
128 static inline void add_char(inst_buffer * ib, char c);
129 static inline void add_str(inst_buffer * ib, const char *s);
130 static void add_int(inst_buffer * ib, int i);
131 static void add_xint(inst_buffer * ib, int i);
132 static void add_sym(inst_buffer * ib, int i);
133 static void add_off(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(db_addr_t loc, bool altfmt)
148 {
149 db_expr_t diff;
150 db_sym_t sym;
151 const char *symname;
152
153 inst_buffer ib;
154
155 memset(&ib, 0, sizeof(ib));
156 ib.ppc = (void *) loc;
157 ib.curp = ib.dasm;
158
159 if (!altfmt) { /* ignore potential entry masks in altfmt */
160 diff = INT_MAX;
161 symname = NULL;
162 sym = db_search_symbol(loc, DB_STGY_PROC, &diff);
163 db_symbol_values(sym, &symname, 0);
164
165 if (symname && !diff) { /* symbol at loc */
166 db_printf("function \"%s()\", entry-mask 0x%x\n\t\t",
167 symname, (unsigned short) get_word(&ib));
168 ib.ppc += 2;
169 }
170 }
171 get_opcode(&ib);
172 get_operands(&ib);
173 db_printf("%s\n", ib.dasm);
174
175 return ((u_int) ib.ppc);
176 }
177
178 int
179 get_opcode(inst_buffer *ib)
180 {
181 ib->opc = get_byte(ib);
182 if (ib->opc >> 2 == 0x3F) { /* two byte op-code */
183 ib->opc = ib->opc << 8;
184 ib->opc += get_byte(ib);
185 }
186 switch (ib->opc) {
187 case 0xFA: /* CALLG */
188 case 0xFB: /* CALLS */
189 case 0xFC: /* XFC */
190 ib->itype = ITYPE_CALL;
191 break;
192 case 0x16: /* JSB */
193 case 0x17: /* JMP */
194 ib->itype = ITYPE_BRANCH;
195 break;
196 default:
197 ib->itype = ITYPE_UNSPEC;
198 }
199 if (ib->opc < 0 || ib->opc > 0xFF) {
200 add_str(ib, "invalid or two-byte opcode ");
201 add_xint(ib, ib->opc);
202 ib->itype = ITYPE_INVALID;
203 } else {
204 add_str(ib, vax_inst[ib->opc].mnemonic);
205 add_char(ib, '\t');
206 }
207 return (ib->opc);
208 }
209
210 int
211 get_operands(inst_buffer *ib)
212 {
213 int aa = 0; /* absolute address mode ? */
214 int size;
215
216 if (ib->opc < 0 || ib->opc > 0xFF) {
217 /* invalid or two-byte opcode */
218 ib->argp = NULL;
219 return (-1);
220 }
221 ib->argp = vax_inst[ib->opc].argdesc;
222 if (ib->argp == NULL)
223 return 0;
224
225 while (*ib->argp) {
226 switch (*ib->argp) {
227
228 case 'b': /* branch displacement */
229 switch (*(++ib->argp)) {
230 case 'b':
231 ib->off = (signed char) get_byte(ib);
232 break;
233 case 'w':
234 ib->off = (short) get_word(ib);
235 break;
236 case 'l':
237 ib->off = get_long(ib);
238 break;
239 default:
240 err_print("XXX eror\n");
241 }
242 /* add_int(ib, ib->off); */
243 ib->addr = (u_int) ib->ppc + ib->off;
244 add_off(ib, ib->addr);
245 break;
246
247 case 'a': /* absolute addressing mode */
248 aa = 1; /* do not break here ! */
249
250 default:
251 switch (*(++ib->argp)) {
252 case 'b': /* Byte */
253 size = SIZE_BYTE;
254 break;
255 case 'w': /* Word */
256 size = SIZE_WORD;
257 break;
258 case 'l': /* Long-Word */
259 case 'f': /* F_Floating */
260 size = SIZE_LONG;
261 break;
262 case 'q': /* Quad-Word */
263 case 'd': /* D_Floating */
264 case 'g': /* G_Floating */
265 size = SIZE_QWORD;
266 break;
267 case 'o': /* Octa-Word */
268 case 'h': /* H_Floating */
269 size = SIZE_OWORD;
270 break;
271 default:
272 err_print("invalid op-type %X (%c) found.\n",
273 *ib->argp, *ib->argp);
274 size = 0;
275 }
276 if (aa) {
277 /* get the address */
278 ib->addr = get_operand(ib, size);
279 add_sym(ib, ib->addr);
280 } else {
281 /* get the operand */
282 ib->addr = get_operand(ib, size);
283 add_off(ib, ib->addr);
284 }
285 }
286
287 if (!*ib->argp || !*++ib->argp)
288 break;
289 if (*ib->argp++ == ',') {
290 add_char(ib, ',');
291 add_char(ib, ' ');
292 } else {
293 err_print("XXX error\n");
294 add_char(ib, '\0');
295 return (-1);
296 }
297 }
298
299 add_char(ib, '\0');
300 return (0);
301 }
302
303 int
304 get_operand(inst_buffer *ib, int size)
305 {
306 int c = get_byte(ib);
307 int mode = c >> 4;
308 int reg = c & 0x0F;
309 int lit = c & 0x3F;
310 int tmp = 0;
311 char buf[16];
312
313 switch (mode) {
314 case 0: /* literal */
315 case 1: /* literal */
316 case 2: /* literal */
317 case 3: /* literal */
318 add_char(ib, LITERAL);
319 add_int(ib, lit);
320 tmp = lit;
321 break;
322
323 case 4: /* indexed */
324 sprintf(buf, "[%s]", my_db_regs[reg].name);
325 get_operand(ib, 0);
326 add_str(ib, buf);
327 break;
328
329 case 5: /* register */
330 add_str(ib, my_db_regs[reg].name);
331 break;
332
333 case 6: /* register deferred */
334 add_char(ib, '(');
335 add_str(ib, my_db_regs[reg].name);
336 add_char(ib, ')');
337 break;
338
339 case 7: /* autodecrement */
340 add_char(ib, '-');
341 add_char(ib, '(');
342 add_str(ib, my_db_regs[reg].name);
343 add_char(ib, ')');
344 if (reg == 0x0F) { /* pc is not allowed in this mode */
345 err_print("autodecrement not allowd for PC.\n");
346 }
347 break;
348
349 case 9: /* autoincrement deferred */
350 add_char(ib, DEFERRED);
351 if (reg == 0x0F) { /* pc: immediate deferred */
352 /*
353 * addresses are always longwords!
354 */
355 tmp = get_long(ib);
356 add_off(ib, tmp);
357 break;
358 }
359 /* fall through */
360 case 8: /* autoincrement */
361 if (reg == 0x0F) { /* pc: immediate ==> special syntax */
362 switch (size) {
363 case SIZE_BYTE:
364 tmp = (signed char) get_byte(ib);
365 break;
366 case SIZE_WORD:
367 tmp = (signed short) get_word(ib);
368 break;
369 case SIZE_LONG:
370 tmp = get_long(ib);
371 break;
372 default:
373 err_print("illegal op-type %d\n", size);
374 tmp = -1;
375 }
376 if (mode == 8)
377 add_char(ib, LITERAL);
378 add_int(ib, tmp);
379 break;
380 }
381 add_char(ib, '(');
382 add_str(ib, my_db_regs[reg].name);
383 add_char(ib, ')');
384 add_char(ib, '+');
385 break;
386
387 case 11: /* byte displacement deferred/ relative deferred */
388 add_char(ib, DEFERRED);
389 case 10: /* byte displacement / relative mode */
390 tmp = (signed char) get_byte(ib);
391 if (reg == 0x0F) {
392 add_off(ib, (u_int) ib->ppc + tmp);
393 break;
394 }
395 /* add_str (ib, "b^"); */
396 add_int(ib, tmp);
397 add_char(ib, '(');
398 add_str(ib, my_db_regs[reg].name);
399 add_char(ib, ')');
400 break;
401
402 case 13: /* word displacement deferred */
403 add_char(ib, DEFERRED);
404 case 12: /* word displacement */
405 tmp = (signed short) get_word(ib);
406 if (reg == 0x0F) {
407 add_off(ib, (u_int) ib->ppc + tmp);
408 break;
409 }
410 /* add_str (ib, "w^"); */
411 add_int(ib, tmp);
412 add_char(ib, '(');
413 add_str(ib, my_db_regs[reg].name);
414 add_char(ib, ')');
415 break;
416
417 case 15: /* long displacement referred */
418 add_char(ib, DEFERRED);
419 case 14: /* long displacement */
420 tmp = get_long(ib);
421 if (reg == 0x0F) {
422 add_off(ib, (u_int) ib->ppc + tmp);
423 break;
424 }
425 /* add_str (ib, "l^"); */
426 add_int(ib, tmp);
427 add_char(ib, '(');
428 add_str(ib, my_db_regs[reg].name);
429 add_char(ib, ')');
430 break;
431
432 default:
433 err_print("can\'t evaluate operand (%02X).\n", lit);
434 break;
435 }
436
437 return (0);
438 }
439
440 int
441 get_byte(inst_buffer *ib)
442 {
443 return ((unsigned char) *(ib->ppc++));
444 }
445
446 int
447 get_word(inst_buffer *ib)
448 {
449 int tmp = *(uint16_t *)ib->ppc;
450 ib->ppc += 2;
451 return tmp;
452 }
453
454 int
455 get_long(inst_buffer *ib)
456 {
457 int tmp = *(int *)ib->ppc;
458 ib->ppc += 4;
459 return (tmp);
460 }
461
462 void
463 add_char(inst_buffer *ib, char c)
464 {
465 *ib->curp++ = c;
466 }
467
468 void
469 add_str(inst_buffer *ib, const char *s)
470 {
471 while ((*ib->curp++ = *s++));
472 --ib->curp;
473 }
474
475 void
476 add_int(inst_buffer *ib, int i)
477 {
478 char buf[32];
479 if (i < 100 && i > -100)
480 sprintf(ib->curp, "%d", i);
481 else
482 sprintf(buf, "0x%x", i);
483 add_str(ib, buf);
484 }
485
486 void
487 add_xint(inst_buffer *ib, int val)
488 {
489 char buf[32];
490 sprintf(buf, "0x%x", val);
491 add_str(ib, buf);
492 }
493
494 void
495 add_sym(inst_buffer *ib, int loc)
496 {
497 db_expr_t diff;
498 db_sym_t sym;
499 const char *symname;
500
501 if (!loc)
502 return;
503
504 diff = INT_MAX;
505 symname = NULL;
506 sym = db_search_symbol(loc, DB_STGY_ANY, &diff);
507 db_symbol_values(sym, &symname, 0);
508
509 if (symname && !diff) {
510 /* add_char(ib, '<'); */
511 add_str(ib, symname);
512 /* add_char(ib, '>'); */
513 } else
514 add_xint(ib, loc);
515 }
516
517 void
518 add_off(inst_buffer *ib, 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) {
533 /* add_char(ib, '<'); */
534 add_str(ib, symname);
535 if (diff) {
536 add_char(ib, '+');
537 add_xint(ib, diff);
538 }
539 /* add_char(ib, '>'); */
540 } else
541 add_xint(ib, loc);
542 }
543