static int dis_i386_instrlen(dis_handle_t *dhp, uint64_t pc) { if (dis_disassemble(dhp, pc, NULL, 0) != 0) return (-1); return (dhp->dh_addr - pc); }
/* * Return the previous instruction. On x86, we have no choice except to * disassemble everything from the start of the symbol, and stop when we have * reached our instruction address. If we're not in the middle of a known * symbol, then we return the same address to indicate failure. */ static uint64_t dis_i386_previnstr(dis_handle_t *dhp, uint64_t pc, int n) { uint64_t *hist, addr, start; int cur, nseen; uint64_t res = pc; if (n <= 0) return (pc); if (dhp->dh_lookup(dhp->dh_data, pc, NULL, 0, &start, NULL) != 0 || start == pc) return (res); hist = dis_zalloc(sizeof (uint64_t) * n); for (cur = 0, nseen = 0, addr = start; addr < pc; addr = dhp->dh_addr) { hist[cur] = addr; cur = (cur + 1) % n; nseen++; /* if we cannot make forward progress, give up */ if (dis_disassemble(dhp, addr, NULL, 0) != 0) goto done; } if (addr != pc) { /* * We scanned past %pc, but didn't find an instruction that * started at %pc. This means that either the caller specified * an invalid address, or we ran into something other than code * during our scan. Virtually any combination of bytes can be * construed as a valid Intel instruction, so any non-code bytes * we encounter will have thrown off the scan. */ goto done; } res = hist[(cur + n - MIN(n, nseen)) % n]; done: dis_free(hist, sizeof (uint64_t) * n); return (res); }
/* * 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; } }