diff options
Diffstat (limited to 'src/VBox/VMM/VMMR3/DBGFDisas.cpp')
-rw-r--r-- | src/VBox/VMM/VMMR3/DBGFDisas.cpp | 798 |
1 files changed, 798 insertions, 0 deletions
diff --git a/src/VBox/VMM/VMMR3/DBGFDisas.cpp b/src/VBox/VMM/VMMR3/DBGFDisas.cpp new file mode 100644 index 00000000..96ad6ab3 --- /dev/null +++ b/src/VBox/VMM/VMMR3/DBGFDisas.cpp @@ -0,0 +1,798 @@ +/* $Id: DBGFDisas.cpp $ */ +/** @file + * DBGF - Debugger Facility, Disassembler. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_DBGF +#include <VBox/vmm/dbgf.h> +#include <VBox/vmm/selm.h> +#include <VBox/vmm/mm.h> +#include <VBox/vmm/hm.h> +#include <VBox/vmm/pgm.h> +#include <VBox/vmm/cpum.h> +#include "DBGFInternal.h" +#include <VBox/dis.h> +#include <VBox/err.h> +#include <VBox/param.h> +#include <VBox/vmm/vm.h> +#include <VBox/vmm/uvm.h> + +#include <VBox/log.h> +#include <iprt/assert.h> +#include <iprt/string.h> +#include <iprt/alloca.h> +#include <iprt/ctype.h> + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Structure used when disassembling and instructions in DBGF. + * This is used so the reader function can get the stuff it needs. + */ +typedef struct +{ + /** The core structure. */ + DISCPUSTATE Cpu; + /** The cross context VM structure. */ + PVM pVM; + /** The cross context virtual CPU structure. */ + PVMCPU pVCpu; + /** The address space for resolving symbol. */ + RTDBGAS hDbgAs; + /** Pointer to the first byte in the segment. */ + RTGCUINTPTR GCPtrSegBase; + /** Pointer to the byte after the end of the segment. (might have wrapped!) */ + RTGCUINTPTR GCPtrSegEnd; + /** The size of the segment minus 1. */ + RTGCUINTPTR cbSegLimit; + /** The guest paging mode. */ + PGMMODE enmMode; + /** Pointer to the current page - R3 Ptr. */ + void const *pvPageR3; + /** Pointer to the current page - GC Ptr. */ + RTGCPTR GCPtrPage; + /** Pointer to the next instruction (relative to GCPtrSegBase). */ + RTGCUINTPTR GCPtrNext; + /** The lock information that PGMPhysReleasePageMappingLock needs. */ + PGMPAGEMAPLOCK PageMapLock; + /** Whether the PageMapLock is valid or not. */ + bool fLocked; + /** 64 bits mode or not. */ + bool f64Bits; +} DBGFDISASSTATE, *PDBGFDISASSTATE; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static FNDISREADBYTES dbgfR3DisasInstrRead; + + + +/** + * Calls the disassembler with the proper reader functions and such for disa + * + * @returns VBox status code. + * @param pVM The cross context VM structure. + * @param pVCpu The cross context virtual CPU structure. + * @param pSelInfo The selector info. + * @param enmMode The guest paging mode. + * @param fFlags DBGF_DISAS_FLAGS_XXX. + * @param GCPtr The GC pointer (selector offset). + * @param pState The disas CPU state. + */ +static int dbgfR3DisasInstrFirst(PVM pVM, PVMCPU pVCpu, PDBGFSELINFO pSelInfo, PGMMODE enmMode, + RTGCPTR GCPtr, uint32_t fFlags, PDBGFDISASSTATE pState) +{ + pState->GCPtrSegBase = pSelInfo->GCPtrBase; + pState->GCPtrSegEnd = pSelInfo->cbLimit + 1 + (RTGCUINTPTR)pSelInfo->GCPtrBase; + pState->cbSegLimit = pSelInfo->cbLimit; + pState->enmMode = enmMode; + pState->GCPtrPage = 0; + pState->pvPageR3 = NULL; + pState->hDbgAs = DBGF_AS_GLOBAL; + pState->pVM = pVM; + pState->pVCpu = pVCpu; + pState->fLocked = false; + pState->f64Bits = enmMode >= PGMMODE_AMD64 && pSelInfo->u.Raw.Gen.u1Long; + + DISCPUMODE enmCpuMode; + switch (fFlags & DBGF_DISAS_FLAGS_MODE_MASK) + { + default: + AssertFailed(); + RT_FALL_THRU(); + case DBGF_DISAS_FLAGS_DEFAULT_MODE: + enmCpuMode = pState->f64Bits + ? DISCPUMODE_64BIT + : pSelInfo->u.Raw.Gen.u1DefBig + ? DISCPUMODE_32BIT + : DISCPUMODE_16BIT; + break; + case DBGF_DISAS_FLAGS_16BIT_MODE: + case DBGF_DISAS_FLAGS_16BIT_REAL_MODE: + enmCpuMode = DISCPUMODE_16BIT; + break; + case DBGF_DISAS_FLAGS_32BIT_MODE: + enmCpuMode = DISCPUMODE_32BIT; + break; + case DBGF_DISAS_FLAGS_64BIT_MODE: + enmCpuMode = DISCPUMODE_64BIT; + break; + } + + uint32_t cbInstr; + int rc = DISInstrWithReader(GCPtr, + enmCpuMode, + dbgfR3DisasInstrRead, + &pState->Cpu, + &pState->Cpu, + &cbInstr); + if (RT_SUCCESS(rc)) + { + pState->GCPtrNext = GCPtr + cbInstr; + return VINF_SUCCESS; + } + + /* cleanup */ + if (pState->fLocked) + { + PGMPhysReleasePageMappingLock(pVM, &pState->PageMapLock); + pState->fLocked = false; + } + return rc; +} + + +#if 0 +/** + * Calls the disassembler for disassembling the next instruction. + * + * @returns VBox status code. + * @param pState The disas CPU state. + */ +static int dbgfR3DisasInstrNext(PDBGFDISASSTATE pState) +{ + uint32_t cbInstr; + int rc = DISInstr(&pState->Cpu, (void *)pState->GCPtrNext, 0, &cbInstr, NULL); + if (RT_SUCCESS(rc)) + { + pState->GCPtrNext = GCPtr + cbInstr; + return VINF_SUCCESS; + } + return rc; +} +#endif + + +/** + * Done with the disassembler state, free associated resources. + * + * @param pState The disas CPU state ++. + */ +static void dbgfR3DisasInstrDone(PDBGFDISASSTATE pState) +{ + if (pState->fLocked) + { + PGMPhysReleasePageMappingLock(pState->pVM, &pState->PageMapLock); + pState->fLocked = false; + } +} + + +/** + * @callback_method_impl{FNDISREADBYTES} + * + * @remarks The source is relative to the base address indicated by + * DBGFDISASSTATE::GCPtrSegBase. + */ +static DECLCALLBACK(int) dbgfR3DisasInstrRead(PDISCPUSTATE pDis, uint8_t offInstr, uint8_t cbMinRead, uint8_t cbMaxRead) +{ + PDBGFDISASSTATE pState = (PDBGFDISASSTATE)pDis; + for (;;) + { + RTGCUINTPTR GCPtr = pDis->uInstrAddr + offInstr + pState->GCPtrSegBase; + + /* + * Need to update the page translation? + */ + if ( !pState->pvPageR3 + || (GCPtr >> GUEST_PAGE_SHIFT) != (pState->GCPtrPage >> GUEST_PAGE_SHIFT)) + { + int rc = VINF_SUCCESS; + + /* translate the address */ + pState->GCPtrPage = GCPtr & ~(RTGCPTR)GUEST_PAGE_OFFSET_MASK; + if (pState->fLocked) + PGMPhysReleasePageMappingLock(pState->pVM, &pState->PageMapLock); + if (pState->enmMode <= PGMMODE_PROTECTED) + rc = PGMPhysGCPhys2CCPtrReadOnly(pState->pVM, pState->GCPtrPage, &pState->pvPageR3, &pState->PageMapLock); + else + rc = PGMPhysGCPtr2CCPtrReadOnly(pState->pVCpu, pState->GCPtrPage, &pState->pvPageR3, &pState->PageMapLock); + if (RT_SUCCESS(rc)) + pState->fLocked = true; + else + { + pState->fLocked = false; + pState->pvPageR3 = NULL; + return rc; + } + } + + /* + * Check the segment limit. + */ + if (!pState->f64Bits && pDis->uInstrAddr + offInstr > pState->cbSegLimit) + return VERR_OUT_OF_SELECTOR_BOUNDS; + + /* + * Calc how much we can read, maxing out the read. + */ + uint32_t cb = GUEST_PAGE_SIZE - (GCPtr & GUEST_PAGE_OFFSET_MASK); + if (!pState->f64Bits) + { + RTGCUINTPTR cbSeg = pState->GCPtrSegEnd - GCPtr; + if (cb > cbSeg && cbSeg) + cb = cbSeg; + } + if (cb > cbMaxRead) + cb = cbMaxRead; + + /* + * Read and advance, + */ + memcpy(&pDis->abInstr[offInstr], (char *)pState->pvPageR3 + (GCPtr & GUEST_PAGE_OFFSET_MASK), cb); + offInstr += (uint8_t)cb; + if (cb >= cbMinRead) + { + pDis->cbCachedInstr = offInstr; + return VINF_SUCCESS; + } + cbMaxRead -= (uint8_t)cb; + cbMinRead -= (uint8_t)cb; + } +} + + +/** + * @callback_method_impl{FNDISGETSYMBOL} + */ +static DECLCALLBACK(int) dbgfR3DisasGetSymbol(PCDISCPUSTATE pDis, uint32_t u32Sel, RTUINTPTR uAddress, + char *pszBuf, size_t cchBuf, RTINTPTR *poff, void *pvUser) +{ + PDBGFDISASSTATE pState = (PDBGFDISASSTATE)pDis; + PCDBGFSELINFO pSelInfo = (PCDBGFSELINFO)pvUser; + + /* + * Address conversion + */ + DBGFADDRESS Addr; + int rc; + /* Start with CS. */ + if ( DIS_FMT_SEL_IS_REG(u32Sel) + ? DIS_FMT_SEL_GET_REG(u32Sel) == DISSELREG_CS + : pSelInfo->Sel == DIS_FMT_SEL_GET_VALUE(u32Sel)) + rc = DBGFR3AddrFromSelInfoOff(pState->pVM->pUVM, &Addr, pSelInfo, uAddress); + /* In long mode everything but FS and GS is easy. */ + else if ( pState->Cpu.uCpuMode == DISCPUMODE_64BIT + && DIS_FMT_SEL_IS_REG(u32Sel) + && DIS_FMT_SEL_GET_REG(u32Sel) != DISSELREG_GS + && DIS_FMT_SEL_GET_REG(u32Sel) != DISSELREG_FS) + { + DBGFR3AddrFromFlat(pState->pVM->pUVM, &Addr, uAddress); + rc = VINF_SUCCESS; + } + /* Here's a quick hack to catch patch manager SS relative access. */ + else if ( DIS_FMT_SEL_IS_REG(u32Sel) + && DIS_FMT_SEL_GET_REG(u32Sel) == DISSELREG_SS + && pSelInfo->GCPtrBase == 0 + && pSelInfo->cbLimit >= UINT32_MAX) + { + DBGFR3AddrFromFlat(pState->pVM->pUVM, &Addr, uAddress); + rc = VINF_SUCCESS; + } + else + { + /** @todo implement a generic solution here. */ + rc = VERR_SYMBOL_NOT_FOUND; + } + + /* + * If we got an address, try resolve it into a symbol. + */ + if (RT_SUCCESS(rc)) + { + RTDBGSYMBOL Sym; + RTGCINTPTR off; + rc = DBGFR3AsSymbolByAddr(pState->pVM->pUVM, pState->hDbgAs, &Addr, + RTDBGSYMADDR_FLAGS_LESS_OR_EQUAL | RTDBGSYMADDR_FLAGS_SKIP_ABS_IN_DEFERRED, + &off, &Sym, NULL /*phMod*/); + if (RT_SUCCESS(rc)) + { + /* + * Return the symbol and offset. + */ + size_t cchName = strlen(Sym.szName); + if (cchName >= cchBuf) + cchName = cchBuf - 1; + memcpy(pszBuf, Sym.szName, cchName); + pszBuf[cchName] = '\0'; + + *poff = off; + } + } + return rc; +} + + +/** + * Disassembles the one instruction according to the specified flags and + * address, internal worker executing on the EMT of the specified virtual CPU. + * + * @returns VBox status code. + * @param pVM The cross context VM structure. + * @param pVCpu The cross context virtual CPU structure. + * @param Sel The code selector. This used to determine the 32/16 bit ness and + * calculation of the actual instruction address. + * @param pGCPtr Pointer to the variable holding the code address + * relative to the base of Sel. + * @param fFlags Flags controlling where to start and how to format. + * A combination of the DBGF_DISAS_FLAGS_* \#defines. + * @param pszOutput Output buffer. + * @param cbOutput Size of the output buffer. + * @param pcbInstr Where to return the size of the instruction. + * @param pDisState Where to store the disassembler state into. + */ +static DECLCALLBACK(int) +dbgfR3DisasInstrExOnVCpu(PVM pVM, PVMCPU pVCpu, RTSEL Sel, PRTGCPTR pGCPtr, uint32_t fFlags, + char *pszOutput, uint32_t cbOutput, uint32_t *pcbInstr, PDBGFDISSTATE pDisState) +{ + VMCPU_ASSERT_EMT(pVCpu); + RTGCPTR GCPtr = *pGCPtr; + int rc; + + /* + * Get the Sel and GCPtr if fFlags requests that. + */ + PCCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu); + PCCPUMSELREG pSRegCS = NULL; + if (fFlags & DBGF_DISAS_FLAGS_CURRENT_GUEST) + { + Sel = pCtx->cs.Sel; + pSRegCS = &pCtx->cs; + GCPtr = pCtx->rip; + } + /* + * Check if the selector matches the guest CS, use the hidden + * registers from that if they are valid. Saves time and effort. + */ + else + { + if (pCtx->cs.Sel == Sel && Sel != DBGF_SEL_FLAT) + pSRegCS = &pCtx->cs; + else + pCtx = NULL; + } + + /* + * Read the selector info - assume no stale selectors and nasty stuff like that. + * + * Note! We CANNOT load invalid hidden selector registers since that would + * mean that log/debug statements or the debug will influence the + * guest state and make things behave differently. + */ + DBGFSELINFO SelInfo; + const PGMMODE enmMode = PGMGetGuestMode(pVCpu); + bool fRealModeAddress = false; + + if ( pSRegCS + && CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, pSRegCS)) + { + SelInfo.Sel = Sel; + SelInfo.SelGate = 0; + SelInfo.GCPtrBase = pSRegCS->u64Base; + SelInfo.cbLimit = pSRegCS->u32Limit; + SelInfo.fFlags = PGMMODE_IS_LONG_MODE(enmMode) + ? DBGFSELINFO_FLAGS_LONG_MODE + : enmMode != PGMMODE_REAL && !pCtx->eflags.Bits.u1VM + ? DBGFSELINFO_FLAGS_PROT_MODE + : DBGFSELINFO_FLAGS_REAL_MODE; + + SelInfo.u.Raw.au32[0] = 0; + SelInfo.u.Raw.au32[1] = 0; + SelInfo.u.Raw.Gen.u16LimitLow = 0xffff; + SelInfo.u.Raw.Gen.u4LimitHigh = 0xf; + SelInfo.u.Raw.Gen.u1Present = pSRegCS->Attr.n.u1Present; + SelInfo.u.Raw.Gen.u1Granularity = pSRegCS->Attr.n.u1Granularity;; + SelInfo.u.Raw.Gen.u1DefBig = pSRegCS->Attr.n.u1DefBig; + SelInfo.u.Raw.Gen.u1Long = pSRegCS->Attr.n.u1Long; + SelInfo.u.Raw.Gen.u1DescType = pSRegCS->Attr.n.u1DescType; + SelInfo.u.Raw.Gen.u4Type = pSRegCS->Attr.n.u4Type; + fRealModeAddress = !!(SelInfo.fFlags & DBGFSELINFO_FLAGS_REAL_MODE); + } + else if (Sel == DBGF_SEL_FLAT) + { + SelInfo.Sel = Sel; + SelInfo.SelGate = 0; + SelInfo.GCPtrBase = 0; + SelInfo.cbLimit = ~(RTGCUINTPTR)0; + SelInfo.fFlags = PGMMODE_IS_LONG_MODE(enmMode) + ? DBGFSELINFO_FLAGS_LONG_MODE + : enmMode != PGMMODE_REAL + ? DBGFSELINFO_FLAGS_PROT_MODE + : DBGFSELINFO_FLAGS_REAL_MODE; + SelInfo.u.Raw.au32[0] = 0; + SelInfo.u.Raw.au32[1] = 0; + SelInfo.u.Raw.Gen.u16LimitLow = 0xffff; + SelInfo.u.Raw.Gen.u4LimitHigh = 0xf; + + pSRegCS = &CPUMQueryGuestCtxPtr(pVCpu)->cs; + if (CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, pSRegCS)) + { + /* Assume the current CS defines the execution mode. */ + SelInfo.u.Raw.Gen.u1Present = pSRegCS->Attr.n.u1Present; + SelInfo.u.Raw.Gen.u1Granularity = pSRegCS->Attr.n.u1Granularity;; + SelInfo.u.Raw.Gen.u1DefBig = pSRegCS->Attr.n.u1DefBig; + SelInfo.u.Raw.Gen.u1Long = pSRegCS->Attr.n.u1Long; + SelInfo.u.Raw.Gen.u1DescType = pSRegCS->Attr.n.u1DescType; + SelInfo.u.Raw.Gen.u4Type = pSRegCS->Attr.n.u4Type; + } + else + { + pSRegCS = NULL; + SelInfo.u.Raw.Gen.u1Present = 1; + SelInfo.u.Raw.Gen.u1Granularity = 1; + SelInfo.u.Raw.Gen.u1DefBig = 1; + SelInfo.u.Raw.Gen.u1DescType = 1; + SelInfo.u.Raw.Gen.u4Type = X86_SEL_TYPE_EO; + } + } + else if ( (pCtx && pCtx->eflags.Bits.u1VM) + || enmMode == PGMMODE_REAL + || (fFlags & DBGF_DISAS_FLAGS_MODE_MASK) == DBGF_DISAS_FLAGS_16BIT_REAL_MODE) + { /* V86 mode or real mode - real mode addressing */ + SelInfo.Sel = Sel; + SelInfo.SelGate = 0; + SelInfo.GCPtrBase = Sel * 16; + SelInfo.cbLimit = ~(RTGCUINTPTR)0; + SelInfo.fFlags = DBGFSELINFO_FLAGS_REAL_MODE; + SelInfo.u.Raw.au32[0] = 0; + SelInfo.u.Raw.au32[1] = 0; + SelInfo.u.Raw.Gen.u16LimitLow = 0xffff; + SelInfo.u.Raw.Gen.u4LimitHigh = 0xf; + SelInfo.u.Raw.Gen.u1Present = 1; + SelInfo.u.Raw.Gen.u1Granularity = 1; + SelInfo.u.Raw.Gen.u1DefBig = 0; /* 16 bits */ + SelInfo.u.Raw.Gen.u1DescType = 1; + SelInfo.u.Raw.Gen.u4Type = X86_SEL_TYPE_EO; + fRealModeAddress = true; + } + else + { + rc = SELMR3GetSelectorInfo(pVCpu, Sel, &SelInfo); + if (RT_FAILURE(rc)) + { + RTStrPrintf(pszOutput, cbOutput, "Sel=%04x -> %Rrc\n", Sel, rc); + return rc; + } + } + + /* + * Disassemble it. + */ + DBGFDISASSTATE State; + rc = dbgfR3DisasInstrFirst(pVM, pVCpu, &SelInfo, enmMode, GCPtr, fFlags, &State); + if (RT_FAILURE(rc)) + { + if (State.Cpu.cbCachedInstr) + RTStrPrintf(pszOutput, cbOutput, "Disas -> %Rrc; %.*Rhxs\n", rc, (size_t)State.Cpu.cbCachedInstr, State.Cpu.abInstr); + else + RTStrPrintf(pszOutput, cbOutput, "Disas -> %Rrc\n", rc); + return rc; + } + + /* + * Format it. + */ + char szBuf[512]; + DISFormatYasmEx(&State.Cpu, szBuf, sizeof(szBuf), + DIS_FMT_FLAGS_RELATIVE_BRANCH, + fFlags & DBGF_DISAS_FLAGS_NO_SYMBOLS ? NULL : dbgfR3DisasGetSymbol, + &SelInfo); + + /* + * Print it to the user specified buffer. + */ + size_t cch; + if (fFlags & DBGF_DISAS_FLAGS_NO_BYTES) + { + if (fFlags & DBGF_DISAS_FLAGS_NO_ADDRESS) + cch = RTStrPrintf(pszOutput, cbOutput, "%s", szBuf); + else if (fRealModeAddress) + cch = RTStrPrintf(pszOutput, cbOutput, "%04x:%04x %s", Sel, (unsigned)GCPtr, szBuf); + else if (Sel == DBGF_SEL_FLAT) + { + if (enmMode >= PGMMODE_AMD64) + cch = RTStrPrintf(pszOutput, cbOutput, "%RGv %s", GCPtr, szBuf); + else + cch = RTStrPrintf(pszOutput, cbOutput, "%08RX32 %s", (uint32_t)GCPtr, szBuf); + } + else + { + if (enmMode >= PGMMODE_AMD64) + cch = RTStrPrintf(pszOutput, cbOutput, "%04x:%RGv %s", Sel, GCPtr, szBuf); + else + cch = RTStrPrintf(pszOutput, cbOutput, "%04x:%08RX32 %s", Sel, (uint32_t)GCPtr, szBuf); + } + } + else + { + uint32_t cbInstr = State.Cpu.cbInstr; + uint8_t const *pabInstr = State.Cpu.abInstr; + if (fFlags & DBGF_DISAS_FLAGS_NO_ADDRESS) + cch = RTStrPrintf(pszOutput, cbOutput, "%.*Rhxs%*s %s", + cbInstr, pabInstr, cbInstr < 8 ? (8 - cbInstr) * 3 : 0, "", + szBuf); + else if (fRealModeAddress) + cch = RTStrPrintf(pszOutput, cbOutput, "%04x:%04x %.*Rhxs%*s %s", + Sel, (unsigned)GCPtr, + cbInstr, pabInstr, cbInstr < 8 ? (8 - cbInstr) * 3 : 0, "", + szBuf); + else if (Sel == DBGF_SEL_FLAT) + { + if (enmMode >= PGMMODE_AMD64) + cch = RTStrPrintf(pszOutput, cbOutput, "%RGv %.*Rhxs%*s %s", + GCPtr, + cbInstr, pabInstr, cbInstr < 8 ? (8 - cbInstr) * 3 : 0, "", + szBuf); + else + cch = RTStrPrintf(pszOutput, cbOutput, "%08RX32 %.*Rhxs%*s %s", + (uint32_t)GCPtr, + cbInstr, pabInstr, cbInstr < 8 ? (8 - cbInstr) * 3 : 0, "", + szBuf); + } + else + { + if (enmMode >= PGMMODE_AMD64) + cch = RTStrPrintf(pszOutput, cbOutput, "%04x:%RGv %.*Rhxs%*s %s", + Sel, GCPtr, + cbInstr, pabInstr, cbInstr < 8 ? (8 - cbInstr) * 3 : 0, "", + szBuf); + else + cch = RTStrPrintf(pszOutput, cbOutput, "%04x:%08RX32 %.*Rhxs%*s %s", + Sel, (uint32_t)GCPtr, + cbInstr, pabInstr, cbInstr < 8 ? (8 - cbInstr) * 3 : 0, "", + szBuf); + } + } + + if (pcbInstr) + *pcbInstr = State.Cpu.cbInstr; + + if (pDisState) + { + pDisState->pCurInstr = State.Cpu.pCurInstr; + pDisState->cbInstr = State.Cpu.cbInstr; + pDisState->Param1 = State.Cpu.Param1; + pDisState->Param2 = State.Cpu.Param2; + pDisState->Param3 = State.Cpu.Param3; + pDisState->Param4 = State.Cpu.Param4; + } + + dbgfR3DisasInstrDone(&State); + return VINF_SUCCESS; +} + + +/** + * Disassembles the one instruction according to the specified flags and address + * returning part of the disassembler state. + * + * @returns VBox status code. + * @param pUVM The user mode VM handle. + * @param idCpu The ID of virtual CPU. + * @param pAddr The code address. + * @param fFlags Flags controlling where to start and how to format. + * A combination of the DBGF_DISAS_FLAGS_* \#defines. + * @param pszOutput Output buffer. This will always be properly + * terminated if @a cbOutput is greater than zero. + * @param cbOutput Size of the output buffer. + * @param pDisState The disassembler state to fill in. + * + * @remarks May have to switch to the EMT of the virtual CPU in order to do + * address conversion. + */ +DECLHIDDEN(int) dbgfR3DisasInstrStateEx(PUVM pUVM, VMCPUID idCpu, PDBGFADDRESS pAddr, uint32_t fFlags, + char *pszOutput, uint32_t cbOutput, PDBGFDISSTATE pDisState) +{ + AssertReturn(cbOutput > 0, VERR_INVALID_PARAMETER); + *pszOutput = '\0'; + UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE); + PVM pVM = pUVM->pVM; + VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); + AssertReturn(idCpu < pUVM->cCpus, VERR_INVALID_CPU_ID); + AssertReturn(!(fFlags & ~DBGF_DISAS_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER); + AssertReturn((fFlags & DBGF_DISAS_FLAGS_MODE_MASK) <= DBGF_DISAS_FLAGS_64BIT_MODE, VERR_INVALID_PARAMETER); + + /* + * Optimize the common case where we're called on the EMT of idCpu since + * we're using this all the time when logging. + */ + int rc; + PVMCPU pVCpu = VMMGetCpu(pVM); + if ( pVCpu + && pVCpu->idCpu == idCpu) + rc = dbgfR3DisasInstrExOnVCpu(pVM, pVCpu, pAddr->Sel, &pAddr->off, fFlags, pszOutput, cbOutput, NULL, pDisState); + else + rc = VMR3ReqPriorityCallWait(pVM, idCpu, (PFNRT)dbgfR3DisasInstrExOnVCpu, 9, + pVM, VMMGetCpuById(pVM, idCpu), pAddr->Sel, &pAddr->off, fFlags, pszOutput, cbOutput, NULL, pDisState); + return rc; +} + +/** + * Disassembles the one instruction according to the specified flags and address. + * + * @returns VBox status code. + * @param pUVM The user mode VM handle. + * @param idCpu The ID of virtual CPU. + * @param Sel The code selector. This used to determine the 32/16 bit ness and + * calculation of the actual instruction address. + * @param GCPtr The code address relative to the base of Sel. + * @param fFlags Flags controlling where to start and how to format. + * A combination of the DBGF_DISAS_FLAGS_* \#defines. + * @param pszOutput Output buffer. This will always be properly + * terminated if @a cbOutput is greater than zero. + * @param cbOutput Size of the output buffer. + * @param pcbInstr Where to return the size of the instruction. + * + * @remarks May have to switch to the EMT of the virtual CPU in order to do + * address conversion. + */ +VMMR3DECL(int) DBGFR3DisasInstrEx(PUVM pUVM, VMCPUID idCpu, RTSEL Sel, RTGCPTR GCPtr, uint32_t fFlags, + char *pszOutput, uint32_t cbOutput, uint32_t *pcbInstr) +{ + AssertReturn(cbOutput > 0, VERR_INVALID_PARAMETER); + *pszOutput = '\0'; + UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE); + PVM pVM = pUVM->pVM; + VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); + AssertReturn(idCpu < pUVM->cCpus, VERR_INVALID_CPU_ID); + AssertReturn(!(fFlags & ~DBGF_DISAS_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER); + AssertReturn((fFlags & DBGF_DISAS_FLAGS_MODE_MASK) <= DBGF_DISAS_FLAGS_64BIT_MODE, VERR_INVALID_PARAMETER); + + /* + * Optimize the common case where we're called on the EMT of idCpu since + * we're using this all the time when logging. + */ + int rc; + PVMCPU pVCpu = VMMGetCpu(pVM); + if ( pVCpu + && pVCpu->idCpu == idCpu) + rc = dbgfR3DisasInstrExOnVCpu(pVM, pVCpu, Sel, &GCPtr, fFlags, pszOutput, cbOutput, pcbInstr, NULL); + else + rc = VMR3ReqPriorityCallWait(pVM, idCpu, (PFNRT)dbgfR3DisasInstrExOnVCpu, 9, + pVM, VMMGetCpuById(pVM, idCpu), Sel, &GCPtr, fFlags, pszOutput, cbOutput, pcbInstr, NULL); + return rc; +} + + +/** + * Disassembles the current guest context instruction. + * All registers and data will be displayed. Addresses will be attempted resolved to symbols. + * + * @returns VBox status code. + * @param pVCpu The cross context virtual CPU structure. + * @param pszOutput Output buffer. This will always be properly + * terminated if @a cbOutput is greater than zero. + * @param cbOutput Size of the output buffer. + * @thread EMT(pVCpu) + */ +VMMR3_INT_DECL(int) DBGFR3DisasInstrCurrent(PVMCPU pVCpu, char *pszOutput, uint32_t cbOutput) +{ + AssertReturn(cbOutput > 0, VERR_INVALID_PARAMETER); + *pszOutput = '\0'; + Assert(VMCPU_IS_EMT(pVCpu)); + + RTGCPTR GCPtr = 0; + return dbgfR3DisasInstrExOnVCpu(pVCpu->pVMR3, pVCpu, 0, &GCPtr, + DBGF_DISAS_FLAGS_CURRENT_GUEST | DBGF_DISAS_FLAGS_DEFAULT_MODE + | DBGF_DISAS_FLAGS_ANNOTATE_PATCHED, + pszOutput, cbOutput, NULL, NULL); +} + + +/** + * Disassembles the current guest context instruction and writes it to the log. + * All registers and data will be displayed. Addresses will be attempted resolved to symbols. + * + * @returns VBox status code. + * @param pVCpu The cross context virtual CPU structure. + * @param pszPrefix Short prefix string to the disassembly string. (optional) + * @thread EMT(pVCpu) + */ +VMMR3DECL(int) DBGFR3DisasInstrCurrentLogInternal(PVMCPU pVCpu, const char *pszPrefix) +{ + char szBuf[256]; + szBuf[0] = '\0'; + int rc = DBGFR3DisasInstrCurrent(pVCpu, &szBuf[0], sizeof(szBuf)); + if (RT_FAILURE(rc)) + RTStrPrintf(szBuf, sizeof(szBuf), "DBGFR3DisasInstrCurrentLog failed with rc=%Rrc\n", rc); + if (pszPrefix && *pszPrefix) + { + if (pVCpu->CTX_SUFF(pVM)->cCpus > 1) + RTLogPrintf("%s-CPU%u: %s\n", pszPrefix, pVCpu->idCpu, szBuf); + else + RTLogPrintf("%s: %s\n", pszPrefix, szBuf); + } + else + RTLogPrintf("%s\n", szBuf); + return rc; +} + + + +/** + * Disassembles the specified guest context instruction and writes it to the log. + * Addresses will be attempted resolved to symbols. + * + * @returns VBox status code. + * @param pVCpu The cross context virtual CPU structure of the calling + * EMT. + * @param Sel The code selector. This used to determine the 32/16 + * bit-ness and calculation of the actual instruction + * address. + * @param GCPtr The code address relative to the base of Sel. + * @param pszPrefix Short prefix string to the disassembly string. + * (optional) + * @thread EMT(pVCpu) + */ +VMMR3DECL(int) DBGFR3DisasInstrLogInternal(PVMCPU pVCpu, RTSEL Sel, RTGCPTR GCPtr, const char *pszPrefix) +{ + Assert(VMCPU_IS_EMT(pVCpu)); + + char szBuf[256]; + RTGCPTR GCPtrTmp = GCPtr; + int rc = dbgfR3DisasInstrExOnVCpu(pVCpu->pVMR3, pVCpu, Sel, &GCPtrTmp, DBGF_DISAS_FLAGS_DEFAULT_MODE, + &szBuf[0], sizeof(szBuf), NULL, NULL); + if (RT_FAILURE(rc)) + RTStrPrintf(szBuf, sizeof(szBuf), "DBGFR3DisasInstrLog(, %RTsel, %RGv) failed with rc=%Rrc\n", Sel, GCPtr, rc); + if (pszPrefix && *pszPrefix) + { + if (pVCpu->CTX_SUFF(pVM)->cCpus > 1) + RTLogPrintf("%s-CPU%u: %s\n", pszPrefix, pVCpu->idCpu, szBuf); + else + RTLogPrintf("%s: %s\n", pszPrefix, szBuf); + } + else + RTLogPrintf("%s\n", szBuf); + return rc; +} + |