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