static int instr_size(dis_handle_t *dhp, uint8_t *ins, unsigned int i, size_t size) { text_t t; t.data = ins; t.size = size; dis_set_data(dhp, &t); return (dis_instrlen(dhp, i)); }
/* * The main disassembly routine. Given a fixed-sized buffer and starting * address, disassemble the data using the supplied target and libdisasm handle. */ void dis_data(dis_tgt_t *tgt, dis_handle_t *dhp, uint64_t addr, void *data, size_t datalen) { dis_buffer_t db = { 0 }; char buf[BUFSIZE]; char symbuf[BUFSIZE]; const char *symbol; const char *last_symbol; off_t symoffset; int i; int bytesperline; size_t symsize; int isfunc; size_t symwidth = 0; int ret; int insz = insn_size(dhp); db.db_tgt = tgt; db.db_data = data; db.db_addr = addr; db.db_size = datalen; dis_set_data(dhp, &db); if ((bytesperline = dis_max_instrlen(dhp)) > 6) bytesperline = 6; symbol = NULL; while (addr < db.db_addr + db.db_size) { ret = dis_disassemble(dhp, addr, buf, BUFSIZE); if (ret != 0 && insz > 0) { /* * Since we know instructions are fixed size, we * always know the address of the next instruction */ (void) snprintf(buf, sizeof (buf), "*** invalid opcode ***"); db.db_nextaddr = addr + insz; } else if (ret != 0) { off_t next; (void) snprintf(buf, sizeof (buf), "*** invalid opcode ***"); /* * On architectures with variable sized instructions * we have no way to figure out where the next * instruction starts if we encounter an invalid * instruction. Instead we print the rest of the * instruction stream as hex until we reach the * next valid symbol in the section. */ if ((next = dis_tgt_next_symbol(tgt, addr)) == 0) { db.db_nextaddr = db.db_addr + db.db_size; } else { if (next > db.db_size) db.db_nextaddr = db.db_addr + db.db_size; else db.db_nextaddr = addr + next; } } /* * Print out the line as: * * address: bytes text * * If there are more than 6 bytes in any given instruction, * spread the bytes across two lines. We try to get symbolic * information for the address, but if that fails we print out * the numeric address instead. * * We try to keep the address portion of the text aligned at * MINSYMWIDTH characters. If we are disassembling a function * with a long name, this can be annoying. So we pick a width * based on the maximum width that the current symbol can be. * This at least produces text aligned within each function. */ last_symbol = symbol; symbol = dis_tgt_lookup(tgt, addr, &symoffset, 1, &symsize, &isfunc); if (symbol == NULL) { symbol = dis_find_section(tgt, addr, &symoffset); symsize = symoffset; } if (symbol != last_symbol) getsymname(addr, symbol, symsize, symbuf, sizeof (symbuf)); symwidth = MAX(symwidth, strlen(symbuf)); getsymname(addr, symbol, symoffset, symbuf, sizeof (symbuf)); /* * If we've crossed a new function boundary, print out the * function name on a blank line. */ if (!g_quiet && symoffset == 0 && symbol != NULL && isfunc) (void) printf("%s()\n", symbol); (void) printf(" %s:%*s ", symbuf, symwidth - strlen(symbuf), ""); /* print bytes */ for (i = 0; i < MIN(bytesperline, (db.db_nextaddr - addr)); i++) { int byte = *((uchar_t *)data + (addr - db.db_addr) + i); if (g_flags & DIS_OCTAL) (void) printf("%03o ", byte); else (void) printf("%02x ", byte); } /* trailing spaces for missing bytes */ for (; i < bytesperline; i++) { if (g_flags & DIS_OCTAL) (void) printf(" "); else (void) printf(" "); } /* contents of disassembly */ (void) printf(" %s", buf); /* excess bytes that spill over onto subsequent lines */ for (; i < db.db_nextaddr - addr; i++) { int byte = *((uchar_t *)data + (addr - db.db_addr) + i); if (i % bytesperline == 0) (void) printf("\n %*s ", symwidth, ""); if (g_flags & DIS_OCTAL) (void) printf("%03o ", byte); else (void) printf("%02x ", byte); } (void) printf("\n"); addr = db.db_nextaddr; } }