static void testDisas(const char *pszSub, uint8_t const *pabInstrs, uintptr_t uEndPtr, DISCPUMODE enmDisCpuMode) { RTTestISub(pszSub); size_t const cbInstrs = uEndPtr - (uintptr_t)pabInstrs; for (size_t off = 0; off < cbInstrs;) { uint32_t const cErrBefore = RTTestIErrorCount(); uint32_t cb = 1; DISSTATE Dis; char szOutput[256] = {0}; int rc = DISInstrToStr(&pabInstrs[off], enmDisCpuMode, &Dis, &cb, szOutput, sizeof(szOutput)); RTTESTI_CHECK_RC(rc, VINF_SUCCESS); RTTESTI_CHECK(cb == Dis.cbInstr); RTTESTI_CHECK(cb > 0); RTTESTI_CHECK(cb <= 16); RTStrStripR(szOutput); RTTESTI_CHECK(szOutput[0]); if (szOutput[0]) { char *pszBytes = strchr(szOutput, '['); RTTESTI_CHECK(pszBytes); if (pszBytes) { RTTESTI_CHECK(pszBytes[-1] == ' '); RTTESTI_CHECK(RT_C_IS_XDIGIT(pszBytes[1])); RTTESTI_CHECK(pszBytes[cb * 3] == ']'); RTTESTI_CHECK(pszBytes[cb * 3 + 1] == ' '); size_t cch = strlen(szOutput); RTTESTI_CHECK(szOutput[cch - 1] != ','); } } if (cErrBefore != RTTestIErrorCount()) RTTestIFailureDetails("rc=%Rrc, off=%#x (%u) cbInstr=%u enmDisCpuMode=%d\n", rc, off, Dis.cbInstr, enmDisCpuMode); RTTestIPrintf(RTTESTLVL_ALWAYS, "%s\n", szOutput); /* Check with size-only. */ uint32_t cbOnly = 1; DISSTATE DisOnly; rc = DISInstrWithPrefetchedBytes((uintptr_t)&pabInstrs[off], enmDisCpuMode, 0 /*fFilter - none */, Dis.abInstr, Dis.cbCachedInstr, NULL, NULL, &DisOnly, &cbOnly); RTTESTI_CHECK_RC(rc, VINF_SUCCESS); RTTESTI_CHECK(cbOnly == DisOnly.cbInstr); RTTESTI_CHECK_MSG(cbOnly == cb, ("%#x vs %#x\n", cbOnly, cb)); off += cb; } }
/** * Disassembles a block of memory. * * @returns VBox status code. * @param argv0 Program name (for errors and warnings). * @param enmCpuMode The cpu mode to disassemble in. * @param uAddress The address we're starting to disassemble at. * @param uHighlightAddr The address of the instruction that should be * highlighted. Pass UINT64_MAX to keep quiet. * @param pbFile Where to start disassemble. * @param cbFile How much to disassemble. * @param enmStyle The assembly output style. * @param fListing Whether to print in a listing like mode. * @param enmUndefOp How to deal with undefined opcodes. */ static int MyDisasmBlock(const char *argv0, DISCPUMODE enmCpuMode, uint64_t uAddress, uint64_t uHighlightAddr, uint8_t *pbFile, size_t cbFile, ASMSTYLE enmStyle, bool fListing, UNDEFOPHANDLING enmUndefOp) { /* * Initialize the CPU context. */ MYDISSTATE State; State.uAddress = uAddress; State.pbInstr = pbFile; State.cbInstr = 0; State.enmUndefOp = enmUndefOp; State.rc = VINF_SUCCESS; State.cbLeft = cbFile; State.pbNext = pbFile; State.uNextAddr = uAddress; void (*pfnFormatter)(PMYDISSTATE pState); switch (enmStyle) { case kAsmStyle_Default: pfnFormatter = MyDisasDefaultFormatter; break; case kAsmStyle_yasm: RTPrintf(" BITS %d\n", enmCpuMode == DISCPUMODE_16BIT ? 16 : enmCpuMode == DISCPUMODE_32BIT ? 32 : 64); pfnFormatter = MyDisasYasmFormatter; break; case kAsmStyle_masm: pfnFormatter = MyDisasMasmFormatter; break; default: AssertFailedReturn(VERR_INTERNAL_ERROR); } /* * The loop. */ int rcRet = VINF_SUCCESS; while (State.cbLeft > 0) { /* * Disassemble it. */ State.cbInstr = 0; State.cbLeft += State.pbNext - State.pbInstr; State.uNextAddr = State.uAddress; State.pbNext = State.pbInstr; int rc = DISInstrToStrWithReader(State.uAddress, enmCpuMode, MyDisasInstrRead, &State, &State.Dis, &State.cbInstr, State.szLine, sizeof(State.szLine)); if ( RT_SUCCESS(rc) || ( ( rc == VERR_DIS_INVALID_OPCODE || rc == VERR_DIS_GEN_FAILURE) && State.enmUndefOp == kUndefOp_DefineByte)) { State.fUndefOp = rc == VERR_DIS_INVALID_OPCODE || rc == VERR_DIS_GEN_FAILURE || State.Dis.pCurInstr->uOpcode == OP_INVALID || State.Dis.pCurInstr->uOpcode == OP_ILLUD2 || ( State.enmUndefOp == kUndefOp_DefineByte && !MyDisasIsValidInstruction(&State.Dis)); if (State.fUndefOp && State.enmUndefOp == kUndefOp_DefineByte) { if (!State.cbInstr) { State.Dis.abInstr[0] = 0; State.Dis.pfnReadBytes(&State.Dis, 0, 1, 1); State.cbInstr = 1; } RTPrintf(" db"); for (unsigned off = 0; off < State.cbInstr; off++) RTPrintf(off ? ", %03xh" : " %03xh", State.Dis.abInstr[off]); RTPrintf(" ; %s\n", State.szLine); } else if (!State.fUndefOp && State.enmUndefOp == kUndefOp_All) { RTPrintf("%s: error at %#RX64: unexpected valid instruction (op=%d)\n", argv0, State.uAddress, State.Dis.pCurInstr->uOpcode); pfnFormatter(&State); rcRet = VERR_GENERAL_FAILURE; } else if (State.fUndefOp && State.enmUndefOp == kUndefOp_Fail) { RTPrintf("%s: error at %#RX64: undefined opcode (op=%d)\n", argv0, State.uAddress, State.Dis.pCurInstr->uOpcode); pfnFormatter(&State); rcRet = VERR_GENERAL_FAILURE; } else { /* Use db for odd encodings that we can't make the assembler use. */ if ( State.enmUndefOp == kUndefOp_DefineByte && DISFormatYasmIsOddEncoding(&State.Dis)) { RTPrintf(" db"); for (unsigned off = 0; off < State.cbInstr; off++) RTPrintf(off ? ", %03xh" : " %03xh", State.Dis.abInstr[off]); RTPrintf(" ; "); } pfnFormatter(&State); } } else { State.cbInstr = State.pbNext - State.pbInstr; if (!State.cbLeft) RTPrintf("%s: error at %#RX64: read beyond the end (%Rrc)\n", argv0, State.uAddress, rc); else if (State.cbInstr) RTPrintf("%s: error at %#RX64: %Rrc cbInstr=%d\n", argv0, State.uAddress, rc, State.cbInstr); else { RTPrintf("%s: error at %#RX64: %Rrc cbInstr=%d!\n", argv0, State.uAddress, rc, State.cbInstr); if (rcRet == VINF_SUCCESS) rcRet = rc; break; } } /* Highlight this instruction? */ if (uHighlightAddr - State.uAddress < State.cbInstr) RTPrintf("; ^^^^^^^^^^^^^^^^^^^^^\n"); /* Check that the size-only mode returns the smae size on success. */ if (RT_SUCCESS(rc)) { uint32_t cbInstrOnly = 32; uint8_t abInstr[sizeof(State.Dis.abInstr)]; memcpy(abInstr, State.Dis.abInstr, sizeof(State.Dis.abInstr)); int rcOnly = DISInstrWithPrefetchedBytes(State.uAddress, enmCpuMode, 0 /*fFilter - none */, abInstr, State.Dis.cbCachedInstr, MyDisasInstrRead, &State, &State.Dis, &cbInstrOnly); if ( rcOnly != rc || cbInstrOnly != State.cbInstr) { RTPrintf("; Instruction size only check failed rc=%Rrc cbInstrOnly=%#x exepcted %Rrc and %#x\n", rcOnly, cbInstrOnly, rc, State.cbInstr); rcRet = VERR_GENERAL_FAILURE; break; } } /* next */ State.uAddress += State.cbInstr; State.pbInstr += State.cbInstr; } return rcRet; }