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