1 /* $NetBSD: db_disasm.c,v 1.16 2005/12/11 12:19:36 christos 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.16 2005/12/11 12:19:36 christos 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 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 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, my_db_regs[reg].name); 335 break; 336 337 case 6: /* register deferred */ 338 add_char(ib, '('); 339 add_str(ib, 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, 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, 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, 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, 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, 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