summaryrefslogtreecommitdiffstats
path: root/src/VBox/VMM/VMMR3/DBGFDisas.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/VMM/VMMR3/DBGFDisas.cpp
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/VMM/VMMR3/DBGFDisas.cpp')
-rw-r--r--src/VBox/VMM/VMMR3/DBGFDisas.cpp798
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;
+}
+