/* $Id: DBGFR3Flow.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */ /** @file * DBGF - Debugger Facility, Control Flow Graph Interface (CFG). */ /* * Copyright (C) 2016-2022 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 . * * SPDX-License-Identifier: GPL-3.0-only */ /** @page pg_dbgf_cfg DBGFR3Flow - Control Flow Graph Interface * * The control flow graph interface provides an API to disassemble * guest code providing the result in a control flow graph. */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #define LOG_GROUP LOG_GROUP_DBGF #include #include "DBGFInternal.h" #include #include #include #include #include #include #include #include #include #include #include #include /********************************************************************************************************************************* * Structures and Typedefs * *********************************************************************************************************************************/ /** * Internal control flow graph state. */ typedef struct DBGFFLOWINT { /** Reference counter. */ uint32_t volatile cRefs; /** Internal reference counter for basic blocks. */ uint32_t volatile cRefsBb; /** Flags during creation. */ uint32_t fFlags; /** List of all basic blocks. */ RTLISTANCHOR LstFlowBb; /** List of identified branch tables. */ RTLISTANCHOR LstBranchTbl; /** Number of basic blocks in this control flow graph. */ uint32_t cBbs; /** Number of branch tables in this control flow graph. */ uint32_t cBranchTbls; /** Number of call instructions in this control flow graph. */ uint32_t cCallInsns; /** The lowest addres of a basic block. */ DBGFADDRESS AddrLowest; /** The highest address of a basic block. */ DBGFADDRESS AddrHighest; /** String cache for disassembled instructions. */ RTSTRCACHE hStrCacheInstr; } DBGFFLOWINT; /** Pointer to an internal control flow graph state. */ typedef DBGFFLOWINT *PDBGFFLOWINT; /** * Instruction record */ typedef struct DBGFFLOWBBINSTR { /** Instruction address. */ DBGFADDRESS AddrInstr; /** Size of instruction. */ uint32_t cbInstr; /** Disassembled instruction string. */ const char *pszInstr; } DBGFFLOWBBINSTR; /** Pointer to an instruction record. */ typedef DBGFFLOWBBINSTR *PDBGFFLOWBBINSTR; /** * A branch table identified by the graph processor. */ typedef struct DBGFFLOWBRANCHTBLINT { /** Node for the list of branch tables. */ RTLISTNODE NdBranchTbl; /** The owning control flow graph. */ PDBGFFLOWINT pFlow; /** Reference counter. */ uint32_t volatile cRefs; /** The general register index holding the bracnh table base. */ uint8_t idxGenRegBase; /** Start address of the branch table. */ DBGFADDRESS AddrStart; /** Number of valid entries in the branch table. */ uint32_t cSlots; /** The addresses contained in the branch table - variable in size. */ DBGFADDRESS aAddresses[1]; } DBGFFLOWBRANCHTBLINT; /** Pointer to a branch table structure. */ typedef DBGFFLOWBRANCHTBLINT *PDBGFFLOWBRANCHTBLINT; /** * Internal control flow graph basic block state. */ typedef struct DBGFFLOWBBINT { /** Node for the list of all basic blocks. */ RTLISTNODE NdFlowBb; /** The control flow graph the basic block belongs to. */ PDBGFFLOWINT pFlow; /** Reference counter. */ uint32_t volatile cRefs; /** Basic block end type. */ DBGFFLOWBBENDTYPE enmEndType; /** Start address of this basic block. */ DBGFADDRESS AddrStart; /** End address of this basic block. */ DBGFADDRESS AddrEnd; /** Address of the block succeeding. * This is valid for conditional jumps * (the other target is referenced by AddrEnd+1) and * unconditional jumps (not ret, iret, etc.) except * if we can't infer the jump target (jmp *eax for example). */ DBGFADDRESS AddrTarget; /** The indirect branch table identified for indirect branches. */ PDBGFFLOWBRANCHTBLINT pFlowBranchTbl; /** Last status error code if DBGF_FLOW_BB_F_INCOMPLETE_ERR is set. */ int rcError; /** Error message if DBGF_FLOW_BB_F_INCOMPLETE_ERR is set. */ char *pszErr; /** Flags for this basic block. */ uint32_t fFlags; /** Number of instructions in this basic block. */ uint32_t cInstr; /** Maximum number of instruction records for this basic block. */ uint32_t cInstrMax; /** Instruction records, variable in size. */ DBGFFLOWBBINSTR aInstr[1]; } DBGFFLOWBBINT; /** Pointer to an internal control flow graph basic block state. */ typedef DBGFFLOWBBINT *PDBGFFLOWBBINT; /** * Control flow graph iterator state. */ typedef struct DBGFFLOWITINT { /** Pointer to the control flow graph (holding a reference). */ PDBGFFLOWINT pFlow; /** Next basic block to return. */ uint32_t idxBbNext; /** Array of basic blocks sorted by the specified order - variable in size. */ PDBGFFLOWBBINT apBb[1]; } DBGFFLOWITINT; /** Pointer to the internal control flow graph iterator state. */ typedef DBGFFLOWITINT *PDBGFFLOWITINT; /** * Control flow graph branch table iterator state. */ typedef struct DBGFFLOWBRANCHTBLITINT { /** Pointer to the control flow graph (holding a reference). */ PDBGFFLOWINT pFlow; /** Next branch table to return. */ uint32_t idxTblNext; /** Array of branch table pointers sorted by the specified order - variable in size. */ PDBGFFLOWBRANCHTBLINT apBranchTbl[1]; } DBGFFLOWBRANCHTBLITINT; /** Pointer to the internal control flow graph branch table iterator state. */ typedef DBGFFLOWBRANCHTBLITINT *PDBGFFLOWBRANCHTBLITINT; /********************************************************************************************************************************* * Internal Functions * *********************************************************************************************************************************/ static uint32_t dbgfR3FlowBbReleaseInt(PDBGFFLOWBBINT pFlowBb, bool fMayDestroyFlow); static void dbgfR3FlowBranchTblDestroy(PDBGFFLOWBRANCHTBLINT pFlowBranchTbl); /** * Checks whether both addresses are equal. * * @returns true if both addresses point to the same location, false otherwise. * @param pAddr1 First address. * @param pAddr2 Second address. */ static bool dbgfR3FlowAddrEqual(PDBGFADDRESS pAddr1, PDBGFADDRESS pAddr2) { return pAddr1->Sel == pAddr2->Sel && pAddr1->off == pAddr2->off; } /** * Checks whether the first given address is lower than the second one. * * @returns true if both addresses point to the same location, false otherwise. * @param pAddr1 First address. * @param pAddr2 Second address. */ static bool dbgfR3FlowAddrLower(PDBGFADDRESS pAddr1, PDBGFADDRESS pAddr2) { return pAddr1->Sel == pAddr2->Sel && pAddr1->off < pAddr2->off; } /** * Checks whether the given basic block and address intersect. * * @returns true if they intersect, false otherwise. * @param pFlowBb The basic block to check. * @param pAddr The address to check for. */ static bool dbgfR3FlowAddrIntersect(PDBGFFLOWBBINT pFlowBb, PDBGFADDRESS pAddr) { return (pFlowBb->AddrStart.Sel == pAddr->Sel) && (pFlowBb->AddrStart.off <= pAddr->off) && (pFlowBb->AddrEnd.off >= pAddr->off); } /** * Returns the distance of the two given addresses. * * @returns Distance of the addresses. * @param pAddr1 The first address. * @param pAddr2 The second address. */ static RTGCUINTPTR dbgfR3FlowAddrGetDistance(PDBGFADDRESS pAddr1, PDBGFADDRESS pAddr2) { if (pAddr1->Sel == pAddr2->Sel) { if (pAddr1->off >= pAddr2->off) return pAddr1->off - pAddr2->off; else return pAddr2->off - pAddr1->off; } else AssertFailed(); return 0; } /** * Creates a new basic block. * * @returns Pointer to the basic block on success or NULL if out of memory. * @param pThis The control flow graph. * @param pAddrStart The start of the basic block. * @param fFlowBbFlags Additional flags for this bascic block. * @param cInstrMax Maximum number of instructions this block can hold initially. */ static PDBGFFLOWBBINT dbgfR3FlowBbCreate(PDBGFFLOWINT pThis, PDBGFADDRESS pAddrStart, uint32_t fFlowBbFlags, uint32_t cInstrMax) { PDBGFFLOWBBINT pFlowBb = (PDBGFFLOWBBINT)RTMemAllocZ(RT_UOFFSETOF_DYN(DBGFFLOWBBINT, aInstr[cInstrMax])); if (RT_LIKELY(pFlowBb)) { RTListInit(&pFlowBb->NdFlowBb); pFlowBb->cRefs = 1; pFlowBb->enmEndType = DBGFFLOWBBENDTYPE_INVALID; pFlowBb->pFlow = pThis; pFlowBb->fFlags = DBGF_FLOW_BB_F_EMPTY | fFlowBbFlags; pFlowBb->AddrStart = *pAddrStart; pFlowBb->AddrEnd = *pAddrStart; pFlowBb->rcError = VINF_SUCCESS; pFlowBb->pszErr = NULL; pFlowBb->cInstr = 0; pFlowBb->cInstrMax = cInstrMax; pFlowBb->pFlowBranchTbl = NULL; ASMAtomicIncU32(&pThis->cRefsBb); } return pFlowBb; } /** * Creates an empty branch table with the given size. * * @returns Pointer to the empty branch table on success or NULL if out of memory. * @param pThis The control flow graph. * @param pAddrStart The start of the branch table. * @param idxGenRegBase The general register index holding the base address. * @param cSlots Number of slots the table has. */ static PDBGFFLOWBRANCHTBLINT dbgfR3FlowBranchTblCreate(PDBGFFLOWINT pThis, PDBGFADDRESS pAddrStart, uint8_t idxGenRegBase, uint32_t cSlots) { PDBGFFLOWBRANCHTBLINT pBranchTbl = (PDBGFFLOWBRANCHTBLINT)RTMemAllocZ(RT_UOFFSETOF_DYN(DBGFFLOWBRANCHTBLINT, aAddresses[cSlots])); if (RT_LIKELY(pBranchTbl)) { RTListInit(&pBranchTbl->NdBranchTbl); pBranchTbl->pFlow = pThis; pBranchTbl->idxGenRegBase = idxGenRegBase; pBranchTbl->AddrStart = *pAddrStart; pBranchTbl->cSlots = cSlots; pBranchTbl->cRefs = 1; } return pBranchTbl; } /** * Destroys a control flow graph. * * @returns nothing. * @param pThis The control flow graph to destroy. */ static void dbgfR3FlowDestroy(PDBGFFLOWINT pThis) { /* Defer destruction if there are still basic blocks referencing us. */ PDBGFFLOWBBINT pFlowBb; PDBGFFLOWBBINT pFlowBbNext; RTListForEachSafe(&pThis->LstFlowBb, pFlowBb, pFlowBbNext, DBGFFLOWBBINT, NdFlowBb) { dbgfR3FlowBbReleaseInt(pFlowBb, false /*fMayDestroyFlow*/); } Assert(!pThis->cRefs); if (!pThis->cRefsBb) { /* Destroy the branch tables. */ PDBGFFLOWBRANCHTBLINT pTbl = NULL; PDBGFFLOWBRANCHTBLINT pTblNext = NULL; RTListForEachSafe(&pThis->LstBranchTbl, pTbl, pTblNext, DBGFFLOWBRANCHTBLINT, NdBranchTbl) { dbgfR3FlowBranchTblDestroy(pTbl); } RTStrCacheDestroy(pThis->hStrCacheInstr); RTMemFree(pThis); } } /** * Destroys a basic block. * * @returns nothing. * @param pFlowBb The basic block to destroy. * @param fMayDestroyFlow Flag whether the control flow graph container * should be destroyed when there is nothing referencing it. */ static void dbgfR3FlowBbDestroy(PDBGFFLOWBBINT pFlowBb, bool fMayDestroyFlow) { PDBGFFLOWINT pThis = pFlowBb->pFlow; RTListNodeRemove(&pFlowBb->NdFlowBb); pThis->cBbs--; for (uint32_t idxInstr = 0; idxInstr < pFlowBb->cInstr; idxInstr++) RTStrCacheRelease(pThis->hStrCacheInstr, pFlowBb->aInstr[idxInstr].pszInstr); uint32_t cRefsBb = ASMAtomicDecU32(&pThis->cRefsBb); RTMemFree(pFlowBb); if (!cRefsBb && !pThis->cRefs && fMayDestroyFlow) dbgfR3FlowDestroy(pThis); } /** * Destroys a given branch table. * * @returns nothing. * @param pFlowBranchTbl The flow branch table to destroy. */ static void dbgfR3FlowBranchTblDestroy(PDBGFFLOWBRANCHTBLINT pFlowBranchTbl) { RTListNodeRemove(&pFlowBranchTbl->NdBranchTbl); RTMemFree(pFlowBranchTbl); } /** * Internal basic block release worker. * * @returns New reference count of the released basic block, on 0 * it is destroyed. * @param pFlowBb The basic block to release. * @param fMayDestroyFlow Flag whether the control flow graph container * should be destroyed when there is nothing referencing it. */ static uint32_t dbgfR3FlowBbReleaseInt(PDBGFFLOWBBINT pFlowBb, bool fMayDestroyFlow) { uint32_t cRefs = ASMAtomicDecU32(&pFlowBb->cRefs); AssertMsg(cRefs < _1M, ("%#x %p %d\n", cRefs, pFlowBb, pFlowBb->enmEndType)); if (cRefs == 0) dbgfR3FlowBbDestroy(pFlowBb, fMayDestroyFlow); return cRefs; } /** * Links the given basic block into the control flow graph. * * @returns nothing. * @param pThis The control flow graph to link into. * @param pFlowBb The basic block to link. */ DECLINLINE(void) dbgfR3FlowLink(PDBGFFLOWINT pThis, PDBGFFLOWBBINT pFlowBb) { RTListAppend(&pThis->LstFlowBb, &pFlowBb->NdFlowBb); pThis->cBbs++; } /** * Links the given branch table into the control flow graph. * * @returns nothing. * @param pThis The control flow graph to link into. * @param pBranchTbl The branch table to link. */ DECLINLINE(void) dbgfR3FlowBranchTblLink(PDBGFFLOWINT pThis, PDBGFFLOWBRANCHTBLINT pBranchTbl) { RTListAppend(&pThis->LstBranchTbl, &pBranchTbl->NdBranchTbl); pThis->cBranchTbls++; } /** * Returns the first unpopulated basic block of the given control flow graph. * * @returns The first unpopulated control flow graph or NULL if not found. * @param pThis The control flow graph. */ DECLINLINE(PDBGFFLOWBBINT) dbgfR3FlowGetUnpopulatedBb(PDBGFFLOWINT pThis) { PDBGFFLOWBBINT pFlowBb; RTListForEach(&pThis->LstFlowBb, pFlowBb, DBGFFLOWBBINT, NdFlowBb) { if (pFlowBb->fFlags & DBGF_FLOW_BB_F_EMPTY) return pFlowBb; } return NULL; } /** * Returns the branch table with the given address if it exists. * * @returns Pointer to the branch table record or NULL if not found. * @param pThis The control flow graph. * @param pAddrTbl The branch table address. */ DECLINLINE(PDBGFFLOWBRANCHTBLINT) dbgfR3FlowBranchTblFindByAddr(PDBGFFLOWINT pThis, PDBGFADDRESS pAddrTbl) { PDBGFFLOWBRANCHTBLINT pTbl; RTListForEach(&pThis->LstBranchTbl, pTbl, DBGFFLOWBRANCHTBLINT, NdBranchTbl) { if (dbgfR3FlowAddrEqual(&pTbl->AddrStart, pAddrTbl)) return pTbl; } return NULL; } /** * Sets the given error status for the basic block. * * @returns nothing. * @param pFlowBb The basic block causing the error. * @param rcError The error to set. * @param pszFmt Format string of the error description. * @param ... Arguments for the format string. */ static void dbgfR3FlowBbSetError(PDBGFFLOWBBINT pFlowBb, int rcError, const char *pszFmt, ...) { va_list va; va_start(va, pszFmt); Assert(!(pFlowBb->fFlags & DBGF_FLOW_BB_F_INCOMPLETE_ERR)); pFlowBb->fFlags |= DBGF_FLOW_BB_F_INCOMPLETE_ERR; pFlowBb->fFlags &= ~DBGF_FLOW_BB_F_EMPTY; pFlowBb->rcError = rcError; pFlowBb->pszErr = RTStrAPrintf2V(pszFmt, va); va_end(va); } /** * Checks whether the given control flow graph contains a basic block * with the given start address. * * @returns true if there is a basic block with the start address, false otherwise. * @param pThis The control flow graph. * @param pAddr The address to check for. */ static bool dbgfR3FlowHasBbWithStartAddr(PDBGFFLOWINT pThis, PDBGFADDRESS pAddr) { PDBGFFLOWBBINT pFlowBb; RTListForEach(&pThis->LstFlowBb, pFlowBb, DBGFFLOWBBINT, NdFlowBb) { if (dbgfR3FlowAddrEqual(&pFlowBb->AddrStart, pAddr)) return true; } return false; } /** * Splits a given basic block into two at the given address. * * @returns VBox status code. * @param pThis The control flow graph. * @param pFlowBb The basic block to split. * @param pAddr The address to split at. */ static int dbgfR3FlowBbSplit(PDBGFFLOWINT pThis, PDBGFFLOWBBINT pFlowBb, PDBGFADDRESS pAddr) { int rc = VINF_SUCCESS; uint32_t idxInstrSplit; /* If the block is empty it will get populated later so there is nothing to split, * same if the start address equals. */ if ( pFlowBb->fFlags & DBGF_FLOW_BB_F_EMPTY || dbgfR3FlowAddrEqual(&pFlowBb->AddrStart, pAddr)) return VINF_SUCCESS; /* Find the instruction to split at. */ for (idxInstrSplit = 1; idxInstrSplit < pFlowBb->cInstr; idxInstrSplit++) if (dbgfR3FlowAddrEqual(&pFlowBb->aInstr[idxInstrSplit].AddrInstr, pAddr)) break; Assert(idxInstrSplit > 0); /* * Given address might not be on instruction boundary, this is not supported * so far and results in an error. */ if (idxInstrSplit < pFlowBb->cInstr) { /* Create new basic block. */ uint32_t cInstrNew = pFlowBb->cInstr - idxInstrSplit; PDBGFFLOWBBINT pFlowBbNew = dbgfR3FlowBbCreate(pThis, &pFlowBb->aInstr[idxInstrSplit].AddrInstr, 0 /*fFlowBbFlags*/, cInstrNew); if (pFlowBbNew) { /* Move instructions over. */ pFlowBbNew->cInstr = cInstrNew; pFlowBbNew->AddrEnd = pFlowBb->AddrEnd; pFlowBbNew->enmEndType = pFlowBb->enmEndType; pFlowBbNew->AddrTarget = pFlowBb->AddrTarget; pFlowBbNew->fFlags = pFlowBb->fFlags & ~DBGF_FLOW_BB_F_ENTRY; pFlowBbNew->pFlowBranchTbl = pFlowBb->pFlowBranchTbl; pFlowBb->pFlowBranchTbl = NULL; /* Move any error to the new basic block and clear them in the old basic block. */ pFlowBbNew->rcError = pFlowBb->rcError; pFlowBbNew->pszErr = pFlowBb->pszErr; pFlowBb->rcError = VINF_SUCCESS; pFlowBb->pszErr = NULL; pFlowBb->fFlags &= ~DBGF_FLOW_BB_F_INCOMPLETE_ERR; memcpy(&pFlowBbNew->aInstr[0], &pFlowBb->aInstr[idxInstrSplit], cInstrNew * sizeof(DBGFFLOWBBINSTR)); pFlowBb->cInstr = idxInstrSplit; pFlowBb->enmEndType = DBGFFLOWBBENDTYPE_UNCOND; pFlowBb->AddrEnd = pFlowBb->aInstr[idxInstrSplit-1].AddrInstr; pFlowBb->AddrTarget = pFlowBbNew->AddrStart; DBGFR3AddrAdd(&pFlowBb->AddrEnd, pFlowBb->aInstr[idxInstrSplit-1].cbInstr - 1); RT_BZERO(&pFlowBb->aInstr[idxInstrSplit], cInstrNew * sizeof(DBGFFLOWBBINSTR)); dbgfR3FlowLink(pThis, pFlowBbNew); } else rc = VERR_NO_MEMORY; } else AssertFailedStmt(rc = VERR_INVALID_STATE); /** @todo Proper status code. */ return rc; } /** * Makes sure there is an successor at the given address splitting already existing * basic blocks if they intersect. * * @returns VBox status code. * @param pThis The control flow graph. * @param pAddrSucc The guest address the new successor should start at. * @param fNewBbFlags Flags for the new basic block. * @param pBranchTbl Branch table candidate for this basic block. */ static int dbgfR3FlowBbSuccessorAdd(PDBGFFLOWINT pThis, PDBGFADDRESS pAddrSucc, uint32_t fNewBbFlags, PDBGFFLOWBRANCHTBLINT pBranchTbl) { PDBGFFLOWBBINT pFlowBb; RTListForEach(&pThis->LstFlowBb, pFlowBb, DBGFFLOWBBINT, NdFlowBb) { /* * The basic block must be split if it intersects with the given address * and the start address does not equal the given one. */ if (dbgfR3FlowAddrIntersect(pFlowBb, pAddrSucc)) return dbgfR3FlowBbSplit(pThis, pFlowBb, pAddrSucc); } int rc = VINF_SUCCESS; pFlowBb = dbgfR3FlowBbCreate(pThis, pAddrSucc, fNewBbFlags, 10); if (pFlowBb) { pFlowBb->pFlowBranchTbl = pBranchTbl; dbgfR3FlowLink(pThis, pFlowBb); } else rc = VERR_NO_MEMORY; return rc; } /** * Returns whether the parameter indicates an indirect branch. * * @returns Flag whether this is an indirect branch. * @param pDisParam The parameter from the disassembler. */ DECLINLINE(bool) dbgfR3FlowBranchTargetIsIndirect(PDISOPPARAM pDisParam) { bool fIndirect = true; if ( pDisParam->fUse & (DISUSE_IMMEDIATE8 | DISUSE_IMMEDIATE16 | DISUSE_IMMEDIATE32 | DISUSE_IMMEDIATE64) || pDisParam->fUse & (DISUSE_IMMEDIATE8_REL | DISUSE_IMMEDIATE16_REL | DISUSE_IMMEDIATE32_REL | DISUSE_IMMEDIATE64_REL)) fIndirect = false; return fIndirect; } /** * Resolves the direct branch target address if possible from the given instruction address * and instruction parameter. * * @returns VBox status code. * @param pUVM The usermode VM handle. * @param idCpu CPU id for resolving the address. * @param pDisParam The parameter from the disassembler. * @param pAddrInstr The instruction address. * @param cbInstr Size of instruction in bytes. * @param fRelJmp Flag whether this is a reltive jump. * @param pAddrJmpTarget Where to store the address to the jump target on success. */ static int dbgfR3FlowQueryDirectBranchTarget(PUVM pUVM, VMCPUID idCpu, PDISOPPARAM pDisParam, PDBGFADDRESS pAddrInstr, uint32_t cbInstr, bool fRelJmp, PDBGFADDRESS pAddrJmpTarget) { int rc = VINF_SUCCESS; Assert(!dbgfR3FlowBranchTargetIsIndirect(pDisParam)); /* Relative jumps are always from the beginning of the next instruction. */ *pAddrJmpTarget = *pAddrInstr; DBGFR3AddrAdd(pAddrJmpTarget, cbInstr); if (fRelJmp) { RTGCINTPTR iRel = 0; if (pDisParam->fUse & DISUSE_IMMEDIATE8_REL) iRel = (int8_t)pDisParam->uValue; else if (pDisParam->fUse & DISUSE_IMMEDIATE16_REL) iRel = (int16_t)pDisParam->uValue; else if (pDisParam->fUse & DISUSE_IMMEDIATE32_REL) iRel = (int32_t)pDisParam->uValue; else if (pDisParam->fUse & DISUSE_IMMEDIATE64_REL) iRel = (int64_t)pDisParam->uValue; else AssertFailedStmt(rc = VERR_NOT_SUPPORTED); if (iRel < 0) DBGFR3AddrSub(pAddrJmpTarget, -iRel); else DBGFR3AddrAdd(pAddrJmpTarget, iRel); } else { if (pDisParam->fUse & (DISUSE_IMMEDIATE8 | DISUSE_IMMEDIATE16 | DISUSE_IMMEDIATE32 | DISUSE_IMMEDIATE64)) { if (DBGFADDRESS_IS_FLAT(pAddrInstr)) DBGFR3AddrFromFlat(pUVM, pAddrJmpTarget, pDisParam->uValue); else DBGFR3AddrFromSelOff(pUVM, idCpu, pAddrJmpTarget, pAddrInstr->Sel, pDisParam->uValue); } else AssertFailedStmt(rc = VERR_INVALID_STATE); } return rc; } /** * Returns the CPU mode based on the given assembler flags. * * @returns CPU mode. * @param pUVM The user mode VM handle. * @param idCpu CPU id for disassembling. * @param fFlagsDisasm The flags used for disassembling. */ static CPUMMODE dbgfR3FlowGetDisasCpuMode(PUVM pUVM, VMCPUID idCpu, uint32_t fFlagsDisasm) { CPUMMODE enmMode = CPUMMODE_INVALID; uint32_t fDisasMode = fFlagsDisasm & DBGF_DISAS_FLAGS_MODE_MASK; if (fDisasMode == DBGF_DISAS_FLAGS_DEFAULT_MODE) enmMode = DBGFR3CpuGetMode(pUVM, idCpu); else if ( fDisasMode == DBGF_DISAS_FLAGS_16BIT_MODE || fDisasMode == DBGF_DISAS_FLAGS_16BIT_REAL_MODE) enmMode = CPUMMODE_REAL; else if (fDisasMode == DBGF_DISAS_FLAGS_32BIT_MODE) enmMode = CPUMMODE_PROTECTED; else if (fDisasMode == DBGF_DISAS_FLAGS_64BIT_MODE) enmMode = CPUMMODE_LONG; else AssertFailed(); return enmMode; } /** * Searches backwards in the given basic block starting the given instruction index for * a mov instruction with the given register as the target where the constant looks like * a pointer. * * @returns Flag whether a candidate was found. * @param pFlowBb The basic block containing the indirect branch. * @param idxRegTgt The general register the mov targets. * @param cbPtr The pointer size to look for. * @param pUVM The user mode VM handle. * @param idCpu CPU id for disassembling. * @param fFlagsDisasm The flags to use for disassembling. * @param pidxInstrStart The instruction index to start searching for on input, * The last instruction evaluated on output. * @param pAddrDest Where to store the candidate address on success. */ static bool dbgfR3FlowSearchMovWithConstantPtrSizeBackwards(PDBGFFLOWBBINT pFlowBb, uint8_t idxRegTgt, uint32_t cbPtr, PUVM pUVM, VMCPUID idCpu, uint32_t fFlagsDisasm, uint32_t *pidxInstrStart, PDBGFADDRESS pAddrDest) { bool fFound = false; uint32_t idxInstrCur = *pidxInstrStart; uint32_t cInstrCheck = idxInstrCur + 1; for (;;) { /** @todo Avoid to disassemble again. */ PDBGFFLOWBBINSTR pInstr = &pFlowBb->aInstr[idxInstrCur]; DBGFDISSTATE DisState; char szOutput[_4K]; int rc = dbgfR3DisasInstrStateEx(pUVM, idCpu, &pInstr->AddrInstr, fFlagsDisasm, &szOutput[0], sizeof(szOutput), &DisState); if (RT_SUCCESS(rc)) { if ( DisState.pCurInstr->uOpcode == OP_MOV && (DisState.Param1.fUse & (DISUSE_REG_GEN16 | DISUSE_REG_GEN32 | DISUSE_REG_GEN64)) && DisState.Param1.Base.idxGenReg == idxRegTgt /*&& DisState.Param1.cb == cbPtr*/ && DisState.Param2.cb == cbPtr && (DisState.Param2.fUse & (DISUSE_IMMEDIATE16 | DISUSE_IMMEDIATE32 | DISUSE_IMMEDIATE64))) { /* Found possible candidate. */ fFound = true; if (DBGFADDRESS_IS_FLAT(&pInstr->AddrInstr)) DBGFR3AddrFromFlat(pUVM, pAddrDest, DisState.Param2.uValue); else DBGFR3AddrFromSelOff(pUVM, idCpu, pAddrDest, pInstr->AddrInstr.Sel, DisState.Param2.uValue); break; } } else break; cInstrCheck--; if (!cInstrCheck) break; idxInstrCur--; } *pidxInstrStart = idxInstrCur; return fFound; } /** * Verifies the given branch table candidate and adds it to the control flow graph on success. * * @returns VBox status code. * @param pThis The flow control graph. * @param pFlowBb The basic block causing the indirect branch. * @param pAddrBranchTbl Address of the branch table location. * @param idxGenRegBase The general register holding the base address. * @param cbPtr Guest pointer size. * @param pUVM The user mode VM handle. * @param idCpu CPU id for disassembling. * * @todo Handle branch tables greater than 4KB (lazy coder). */ static int dbgfR3FlowBranchTblVerifyAdd(PDBGFFLOWINT pThis, PDBGFFLOWBBINT pFlowBb, PDBGFADDRESS pAddrBranchTbl, uint8_t idxGenRegBase, uint32_t cbPtr, PUVM pUVM, VMCPUID idCpu) { int rc = VINF_SUCCESS; PDBGFFLOWBRANCHTBLINT pBranchTbl = dbgfR3FlowBranchTblFindByAddr(pThis, pAddrBranchTbl); if (!pBranchTbl) { uint32_t cSlots = 0; uint8_t abBuf[_4K]; rc = DBGFR3MemRead(pUVM, idCpu, pAddrBranchTbl, &abBuf[0], sizeof(abBuf)); if (RT_SUCCESS(rc)) { uint8_t *pbBuf = &abBuf[0]; while (pbBuf < &abBuf[0] + sizeof(abBuf)) { DBGFADDRESS AddrDest; RTGCUINTPTR GCPtr = cbPtr == sizeof(uint64_t) ? *(uint64_t *)pbBuf : cbPtr == sizeof(uint32_t) ? *(uint32_t *)pbBuf : *(uint16_t *)pbBuf; pbBuf += cbPtr; if (DBGFADDRESS_IS_FLAT(pAddrBranchTbl)) DBGFR3AddrFromFlat(pUVM, &AddrDest, GCPtr); else DBGFR3AddrFromSelOff(pUVM, idCpu, &AddrDest, pAddrBranchTbl->Sel, GCPtr); if (dbgfR3FlowAddrGetDistance(&AddrDest, &pFlowBb->AddrEnd) > _512K) break; cSlots++; } /* If there are any slots use it. */ if (cSlots) { pBranchTbl = dbgfR3FlowBranchTblCreate(pThis, pAddrBranchTbl, idxGenRegBase, cSlots); if (pBranchTbl) { /* Get the addresses. */ for (unsigned i = 0; i < cSlots && RT_SUCCESS(rc); i++) { RTGCUINTPTR GCPtr = cbPtr == sizeof(uint64_t) ? *(uint64_t *)&abBuf[i * cbPtr] : cbPtr == sizeof(uint32_t) ? *(uint32_t *)&abBuf[i * cbPtr] : *(uint16_t *)&abBuf[i * cbPtr]; if (DBGFADDRESS_IS_FLAT(pAddrBranchTbl)) DBGFR3AddrFromFlat(pUVM, &pBranchTbl->aAddresses[i], GCPtr); else DBGFR3AddrFromSelOff(pUVM, idCpu, &pBranchTbl->aAddresses[i], pAddrBranchTbl->Sel, GCPtr); rc = dbgfR3FlowBbSuccessorAdd(pThis, &pBranchTbl->aAddresses[i], DBGF_FLOW_BB_F_BRANCH_TABLE, pBranchTbl); } dbgfR3FlowBranchTblLink(pThis, pBranchTbl); } else rc = VERR_NO_MEMORY; } } } if (pBranchTbl) pFlowBb->pFlowBranchTbl = pBranchTbl; return rc; } /** * Checks whether the location for the branch target candidate contains a valid code address. * * @returns VBox status code. * @param pThis The flow control graph. * @param pFlowBb The basic block causing the indirect branch. * @param pAddrBranchTgt Address of the branch target location. * @param idxGenRegBase The general register holding the address of the location. * @param cbPtr Guest pointer size. * @param pUVM The user mode VM handle. * @param idCpu CPU id for disassembling. * @param fBranchTbl Flag whether this is a possible branch table containing multiple * targets. */ static int dbgfR3FlowCheckBranchTargetLocation(PDBGFFLOWINT pThis, PDBGFFLOWBBINT pFlowBb, PDBGFADDRESS pAddrBranchTgt, uint8_t idxGenRegBase, uint32_t cbPtr, PUVM pUVM, VMCPUID idCpu, bool fBranchTbl) { int rc = VINF_SUCCESS; if (!fBranchTbl) { union { uint16_t u16Val; uint32_t u32Val; uint64_t u64Val; } uVal; rc = DBGFR3MemRead(pUVM, idCpu, pAddrBranchTgt, &uVal, cbPtr); if (RT_SUCCESS(rc)) { DBGFADDRESS AddrTgt; RTGCUINTPTR GCPtr = cbPtr == sizeof(uint64_t) ? uVal.u64Val : cbPtr == sizeof(uint32_t) ? uVal.u32Val : uVal.u16Val; if (DBGFADDRESS_IS_FLAT(pAddrBranchTgt)) DBGFR3AddrFromFlat(pUVM, &AddrTgt, GCPtr); else DBGFR3AddrFromSelOff(pUVM, idCpu, &AddrTgt, pAddrBranchTgt->Sel, GCPtr); if (dbgfR3FlowAddrGetDistance(&AddrTgt, &pFlowBb->AddrEnd) <= _128K) { /* Finish the basic block. */ pFlowBb->AddrTarget = AddrTgt; rc = dbgfR3FlowBbSuccessorAdd(pThis, &AddrTgt, (pFlowBb->fFlags & DBGF_FLOW_BB_F_BRANCH_TABLE), pFlowBb->pFlowBranchTbl); } else rc = VERR_NOT_FOUND; } } else rc = dbgfR3FlowBranchTblVerifyAdd(pThis, pFlowBb, pAddrBranchTgt, idxGenRegBase, cbPtr, pUVM, idCpu); return rc; } /** * Tries to resolve the indirect branch. * * @returns VBox status code. * @param pThis The flow control graph. * @param pFlowBb The basic block causing the indirect branch. * @param pUVM The user mode VM handle. * @param idCpu CPU id for disassembling. * @param pDisParam The parameter from the disassembler. * @param fFlagsDisasm Flags for the disassembler. */ static int dbgfR3FlowTryResolveIndirectBranch(PDBGFFLOWINT pThis, PDBGFFLOWBBINT pFlowBb, PUVM pUVM, VMCPUID idCpu, PDISOPPARAM pDisParam, uint32_t fFlagsDisasm) { Assert(dbgfR3FlowBranchTargetIsIndirect(pDisParam)); uint32_t cbPtr = 0; CPUMMODE enmMode = dbgfR3FlowGetDisasCpuMode(pUVM, idCpu, fFlagsDisasm); switch (enmMode) { case CPUMMODE_REAL: cbPtr = sizeof(uint16_t); break; case CPUMMODE_PROTECTED: cbPtr = sizeof(uint32_t); break; case CPUMMODE_LONG: cbPtr = sizeof(uint64_t); break; default: AssertMsgFailed(("Invalid CPU mode %u\n", enmMode)); } if (pDisParam->fUse & DISUSE_BASE) { uint8_t idxRegBase = pDisParam->Base.idxGenReg; /* Check that the used register size and the pointer size match. */ if ( ((pDisParam->fUse & DISUSE_REG_GEN16) && cbPtr == sizeof(uint16_t)) || ((pDisParam->fUse & DISUSE_REG_GEN32) && cbPtr == sizeof(uint32_t)) || ((pDisParam->fUse & DISUSE_REG_GEN64) && cbPtr == sizeof(uint64_t))) { /* * Search all instructions backwards until a move to the used general register * is detected with a constant using the pointer size. */ uint32_t idxInstrStart = pFlowBb->cInstr - 1 - 1; /* Don't look at the branch. */ bool fCandidateFound = false; bool fBranchTbl = RT_BOOL(pDisParam->fUse & DISUSE_INDEX); DBGFADDRESS AddrBranchTgt; do { fCandidateFound = dbgfR3FlowSearchMovWithConstantPtrSizeBackwards(pFlowBb, idxRegBase, cbPtr, pUVM, idCpu, fFlagsDisasm, &idxInstrStart, &AddrBranchTgt); if (fCandidateFound) { /* Check that the address is not too far away from the instruction address. */ RTGCUINTPTR offPtr = dbgfR3FlowAddrGetDistance(&AddrBranchTgt, &pFlowBb->AddrEnd); if (offPtr <= 20 * _1M) { /* Read the content at the address and check that it is near this basic block too. */ int rc = dbgfR3FlowCheckBranchTargetLocation(pThis, pFlowBb, &AddrBranchTgt, idxRegBase, cbPtr, pUVM, idCpu, fBranchTbl); if (RT_SUCCESS(rc)) break; fCandidateFound = false; } if (idxInstrStart > 0) idxInstrStart--; } } while (idxInstrStart > 0 && !fCandidateFound); } else dbgfR3FlowBbSetError(pFlowBb, VERR_INVALID_STATE, "The base register size and selected pointer size do not match (fUse=%#x cbPtr=%u)", pDisParam->fUse, cbPtr); } return VINF_SUCCESS; } /** * Tries to resolve the indirect branch. * * @returns VBox status code. * @param pThis The flow control graph. * @param pFlowBb The basic block causing the indirect branch. * @param pUVM The user mode VM handle. * @param idCpu CPU id for disassembling. * @param pDisParam The parameter from the disassembler. * @param fFlagsDisasm Flags for the disassembler. */ static int dbgfR3FlowBbCheckBranchTblCandidate(PDBGFFLOWINT pThis, PDBGFFLOWBBINT pFlowBb, PUVM pUVM, VMCPUID idCpu, PDISOPPARAM pDisParam, uint32_t fFlagsDisasm) { int rc = VINF_SUCCESS; Assert(pFlowBb->fFlags & DBGF_FLOW_BB_F_BRANCH_TABLE && pFlowBb->pFlowBranchTbl); uint32_t cbPtr = 0; CPUMMODE enmMode = dbgfR3FlowGetDisasCpuMode(pUVM, idCpu, fFlagsDisasm); switch (enmMode) { case CPUMMODE_REAL: cbPtr = sizeof(uint16_t); break; case CPUMMODE_PROTECTED: cbPtr = sizeof(uint32_t); break; case CPUMMODE_LONG: cbPtr = sizeof(uint64_t); break; default: AssertMsgFailed(("Invalid CPU mode %u\n", enmMode)); } if (pDisParam->fUse & DISUSE_BASE) { uint8_t idxRegBase = pDisParam->Base.idxGenReg; /* Check that the used register size and the pointer size match. */ if ( ((pDisParam->fUse & DISUSE_REG_GEN16) && cbPtr == sizeof(uint16_t)) || ((pDisParam->fUse & DISUSE_REG_GEN32) && cbPtr == sizeof(uint32_t)) || ((pDisParam->fUse & DISUSE_REG_GEN64) && cbPtr == sizeof(uint64_t))) { if (idxRegBase != pFlowBb->pFlowBranchTbl->idxGenRegBase) { /* Try to find the new branch table. */ pFlowBb->pFlowBranchTbl = NULL; rc = dbgfR3FlowTryResolveIndirectBranch(pThis, pFlowBb, pUVM, idCpu, pDisParam, fFlagsDisasm); } /** @todo else check that the base register is not modified in this basic block. */ } else dbgfR3FlowBbSetError(pFlowBb, VERR_INVALID_STATE, "The base register size and selected pointer size do not match (fUse=%#x cbPtr=%u)", pDisParam->fUse, cbPtr); } else dbgfR3FlowBbSetError(pFlowBb, VERR_INVALID_STATE, "The instruction does not use a register"); return rc; } /** * Processes and fills one basic block. * * @returns VBox status code. * @param pUVM The user mode VM handle. * @param idCpu CPU id for disassembling. * @param pThis The control flow graph to populate. * @param pFlowBb The basic block to fill. * @param cbDisasmMax The maximum amount to disassemble. * @param fFlags Combination of DBGF_DISAS_FLAGS_*. */ static int dbgfR3FlowBbProcess(PUVM pUVM, VMCPUID idCpu, PDBGFFLOWINT pThis, PDBGFFLOWBBINT pFlowBb, uint32_t cbDisasmMax, uint32_t fFlags) { int rc = VINF_SUCCESS; uint32_t cbDisasmLeft = cbDisasmMax ? cbDisasmMax : UINT32_MAX; DBGFADDRESS AddrDisasm = pFlowBb->AddrEnd; Assert(pFlowBb->fFlags & DBGF_FLOW_BB_F_EMPTY); /* * Disassemble instruction by instruction until we get a conditional or * unconditional jump or some sort of return. */ while ( cbDisasmLeft && RT_SUCCESS(rc)) { DBGFDISSTATE DisState; char szOutput[_4K]; /* * Before disassembling we have to check whether the address belongs * to another basic block and stop here. */ if ( !(pFlowBb->fFlags & DBGF_FLOW_BB_F_EMPTY) && dbgfR3FlowHasBbWithStartAddr(pThis, &AddrDisasm)) { pFlowBb->AddrTarget = AddrDisasm; pFlowBb->enmEndType = DBGFFLOWBBENDTYPE_UNCOND; break; } rc = dbgfR3DisasInstrStateEx(pUVM, idCpu, &AddrDisasm, fFlags, &szOutput[0], sizeof(szOutput), &DisState); if (RT_SUCCESS(rc)) { if ( pThis->fFlags & DBGF_FLOW_CREATE_F_CALL_INSN_SEPARATE_BB && DisState.pCurInstr->uOpcode == OP_CALL && !(pFlowBb->fFlags & DBGF_FLOW_BB_F_EMPTY)) { /* * If the basic block is not empty, the basic block is terminated and the successor is added * which will contain the call instruction. */ pFlowBb->AddrTarget = AddrDisasm; pFlowBb->enmEndType = DBGFFLOWBBENDTYPE_UNCOND; rc = dbgfR3FlowBbSuccessorAdd(pThis, &AddrDisasm, (pFlowBb->fFlags & DBGF_FLOW_BB_F_BRANCH_TABLE), pFlowBb->pFlowBranchTbl); if (RT_FAILURE(rc)) dbgfR3FlowBbSetError(pFlowBb, rc, "Adding successor blocks failed with %Rrc", rc); break; } pFlowBb->fFlags &= ~DBGF_FLOW_BB_F_EMPTY; cbDisasmLeft -= DisState.cbInstr; if (pFlowBb->cInstr == pFlowBb->cInstrMax) { /* Reallocate. */ RTListNodeRemove(&pFlowBb->NdFlowBb); PDBGFFLOWBBINT pFlowBbNew = (PDBGFFLOWBBINT)RTMemRealloc(pFlowBb, RT_UOFFSETOF_DYN(DBGFFLOWBBINT, aInstr[pFlowBb->cInstrMax + 10])); if (pFlowBbNew) { pFlowBbNew->cInstrMax += 10; pFlowBb = pFlowBbNew; } else rc = VERR_NO_MEMORY; RTListAppend(&pThis->LstFlowBb, &pFlowBb->NdFlowBb); } if (RT_SUCCESS(rc)) { PDBGFFLOWBBINSTR pInstr = &pFlowBb->aInstr[pFlowBb->cInstr]; pInstr->AddrInstr = AddrDisasm; pInstr->cbInstr = DisState.cbInstr; pInstr->pszInstr = RTStrCacheEnter(pThis->hStrCacheInstr, &szOutput[0]); pFlowBb->cInstr++; pFlowBb->AddrEnd = AddrDisasm; DBGFR3AddrAdd(&pFlowBb->AddrEnd, pInstr->cbInstr - 1); DBGFR3AddrAdd(&AddrDisasm, pInstr->cbInstr); /* * Check control flow instructions and create new basic blocks * marking the current one as complete. */ if (DisState.pCurInstr->fOpType & DISOPTYPE_CONTROLFLOW) { uint16_t uOpc = DisState.pCurInstr->uOpcode; if (uOpc == OP_CALL) pThis->cCallInsns++; if ( uOpc == OP_RETN || uOpc == OP_RETF || uOpc == OP_IRET || uOpc == OP_SYSEXIT || uOpc == OP_SYSRET) pFlowBb->enmEndType = DBGFFLOWBBENDTYPE_EXIT; else if (uOpc == OP_JMP) { Assert(DisState.pCurInstr->fOpType & DISOPTYPE_UNCOND_CONTROLFLOW); if (dbgfR3FlowBranchTargetIsIndirect(&DisState.Param1)) { pFlowBb->enmEndType = DBGFFLOWBBENDTYPE_UNCOND_INDIRECT_JMP; if (pFlowBb->fFlags & DBGF_FLOW_BB_F_BRANCH_TABLE) { Assert(pThis->fFlags & DBGF_FLOW_CREATE_F_TRY_RESOLVE_INDIRECT_BRANCHES); /* * This basic block was already discovered by parsing a jump table and * there should be a candidate for the branch table. Check whether it uses the * same branch table. */ rc = dbgfR3FlowBbCheckBranchTblCandidate(pThis, pFlowBb, pUVM, idCpu, &DisState.Param1, fFlags); } else { if (pThis->fFlags & DBGF_FLOW_CREATE_F_TRY_RESOLVE_INDIRECT_BRANCHES) rc = dbgfR3FlowTryResolveIndirectBranch(pThis, pFlowBb, pUVM, idCpu, &DisState.Param1, fFlags); else dbgfR3FlowBbSetError(pFlowBb, VERR_NOT_SUPPORTED, "Detected indirect branch and resolving it not being enabled"); } } else { pFlowBb->enmEndType = DBGFFLOWBBENDTYPE_UNCOND_JMP; /* Create one new basic block with the jump target address. */ rc = dbgfR3FlowQueryDirectBranchTarget(pUVM, idCpu, &DisState.Param1, &pInstr->AddrInstr, pInstr->cbInstr, RT_BOOL(DisState.pCurInstr->fOpType & DISOPTYPE_RELATIVE_CONTROLFLOW), &pFlowBb->AddrTarget); if (RT_SUCCESS(rc)) rc = dbgfR3FlowBbSuccessorAdd(pThis, &pFlowBb->AddrTarget, (pFlowBb->fFlags & DBGF_FLOW_BB_F_BRANCH_TABLE), pFlowBb->pFlowBranchTbl); } } else if (uOpc != OP_CALL) { Assert(DisState.pCurInstr->fOpType & DISOPTYPE_COND_CONTROLFLOW); pFlowBb->enmEndType = DBGFFLOWBBENDTYPE_COND; /* * Create two new basic blocks, one with the jump target address * and one starting after the current instruction. */ rc = dbgfR3FlowBbSuccessorAdd(pThis, &AddrDisasm, (pFlowBb->fFlags & DBGF_FLOW_BB_F_BRANCH_TABLE), pFlowBb->pFlowBranchTbl); if (RT_SUCCESS(rc)) { rc = dbgfR3FlowQueryDirectBranchTarget(pUVM, idCpu, &DisState.Param1, &pInstr->AddrInstr, pInstr->cbInstr, RT_BOOL(DisState.pCurInstr->fOpType & DISOPTYPE_RELATIVE_CONTROLFLOW), &pFlowBb->AddrTarget); if (RT_SUCCESS(rc)) rc = dbgfR3FlowBbSuccessorAdd(pThis, &pFlowBb->AddrTarget, (pFlowBb->fFlags & DBGF_FLOW_BB_F_BRANCH_TABLE), pFlowBb->pFlowBranchTbl); } } else if (pThis->fFlags & DBGF_FLOW_CREATE_F_CALL_INSN_SEPARATE_BB) { pFlowBb->enmEndType = DBGFFLOWBBENDTYPE_UNCOND; pFlowBb->fFlags |= DBGF_FLOW_BB_F_CALL_INSN; /* Add new basic block coming after the call instruction. */ rc = dbgfR3FlowBbSuccessorAdd(pThis, &AddrDisasm, (pFlowBb->fFlags & DBGF_FLOW_BB_F_BRANCH_TABLE), pFlowBb->pFlowBranchTbl); if ( RT_SUCCESS(rc) && !dbgfR3FlowBranchTargetIsIndirect(&DisState.Param1)) { /* Resolve the branch target. */ rc = dbgfR3FlowQueryDirectBranchTarget(pUVM, idCpu, &DisState.Param1, &pInstr->AddrInstr, pInstr->cbInstr, RT_BOOL(DisState.pCurInstr->fOpType & DISOPTYPE_RELATIVE_CONTROLFLOW), &pFlowBb->AddrTarget); if (RT_SUCCESS(rc)) pFlowBb->fFlags |= DBGF_FLOW_BB_F_CALL_INSN_TARGET_KNOWN; } } if (RT_FAILURE(rc)) dbgfR3FlowBbSetError(pFlowBb, rc, "Adding successor blocks failed with %Rrc", rc); /* Quit disassembling. */ if ( ( uOpc != OP_CALL || (pThis->fFlags & DBGF_FLOW_CREATE_F_CALL_INSN_SEPARATE_BB)) || RT_FAILURE(rc)) break; } } else dbgfR3FlowBbSetError(pFlowBb, rc, "Increasing basic block failed with %Rrc", rc); } else dbgfR3FlowBbSetError(pFlowBb, rc, "Disassembling the instruction failed with %Rrc", rc); } return VINF_SUCCESS; } /** * Populate all empty basic blocks. * * @returns VBox status code. * @param pUVM The user mode VM handle. * @param idCpu CPU id for disassembling. * @param pThis The control flow graph to populate. * @param cbDisasmMax The maximum amount to disassemble. * @param fFlags Combination of DBGF_DISAS_FLAGS_*. */ static int dbgfR3FlowPopulate(PUVM pUVM, VMCPUID idCpu, PDBGFFLOWINT pThis, uint32_t cbDisasmMax, uint32_t fFlags) { int rc = VINF_SUCCESS; PDBGFFLOWBBINT pFlowBb = dbgfR3FlowGetUnpopulatedBb(pThis); while (pFlowBb != NULL) { rc = dbgfR3FlowBbProcess(pUVM, idCpu, pThis, pFlowBb, cbDisasmMax, fFlags); if (RT_FAILURE(rc)) break; pFlowBb = dbgfR3FlowGetUnpopulatedBb(pThis); } return rc; } /** * Creates a new control flow graph from the given start address. * * @returns VBox status code. * @param pUVM The user mode VM handle. * @param idCpu CPU id for disassembling. * @param pAddressStart Where to start creating the control flow graph. * @param cbDisasmMax Limit the amount of bytes to disassemble, 0 for no limit. * @param fFlagsFlow Combination of DBGF_FLOW_CREATE_F_* to control the creation of the flow graph. * @param fFlagsDisasm Combination of DBGF_DISAS_FLAGS_* controlling the style of the disassembled * instructions. * @param phFlow Where to store the handle to the control flow graph on success. */ VMMR3DECL(int) DBGFR3FlowCreate(PUVM pUVM, VMCPUID idCpu, PDBGFADDRESS pAddressStart, uint32_t cbDisasmMax, uint32_t fFlagsFlow, uint32_t fFlagsDisasm, PDBGFFLOW phFlow) { 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); AssertPtrReturn(pAddressStart, VERR_INVALID_POINTER); AssertReturn(!(fFlagsDisasm & ~DBGF_DISAS_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER); AssertReturn((fFlagsDisasm & DBGF_DISAS_FLAGS_MODE_MASK) <= DBGF_DISAS_FLAGS_64BIT_MODE, VERR_INVALID_PARAMETER); /* Create the control flow graph container. */ int rc = VINF_SUCCESS; PDBGFFLOWINT pThis = (PDBGFFLOWINT)RTMemAllocZ(sizeof(DBGFFLOWINT)); if (RT_LIKELY(pThis)) { rc = RTStrCacheCreate(&pThis->hStrCacheInstr, "DBGFFLOW"); if (RT_SUCCESS(rc)) { pThis->cRefs = 1; pThis->cRefsBb = 0; pThis->cBbs = 0; pThis->cBranchTbls = 0; pThis->cCallInsns = 0; pThis->fFlags = fFlagsFlow; RTListInit(&pThis->LstFlowBb); RTListInit(&pThis->LstBranchTbl); /* Create the entry basic block and start the work. */ PDBGFFLOWBBINT pFlowBb = dbgfR3FlowBbCreate(pThis, pAddressStart, DBGF_FLOW_BB_F_ENTRY, 10); if (RT_LIKELY(pFlowBb)) { dbgfR3FlowLink(pThis, pFlowBb); rc = dbgfR3FlowPopulate(pUVM, idCpu, pThis, cbDisasmMax, fFlagsDisasm); if (RT_SUCCESS(rc)) { *phFlow = pThis; return VINF_SUCCESS; } } else rc = VERR_NO_MEMORY; } ASMAtomicDecU32(&pThis->cRefs); dbgfR3FlowDestroy(pThis); } else rc = VERR_NO_MEMORY; return rc; } /** * Retains the control flow graph handle. * * @returns Current reference count. * @param hFlow The control flow graph handle to retain. */ VMMR3DECL(uint32_t) DBGFR3FlowRetain(DBGFFLOW hFlow) { PDBGFFLOWINT pThis = hFlow; AssertPtrReturn(pThis, UINT32_MAX); uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs); AssertMsg(cRefs > 1 && cRefs < _1M, ("%#x %p\n", cRefs, pThis)); return cRefs; } /** * Releases the control flow graph handle. * * @returns Current reference count, on 0 the control flow graph will be destroyed. * @param hFlow The control flow graph handle to release. */ VMMR3DECL(uint32_t) DBGFR3FlowRelease(DBGFFLOW hFlow) { PDBGFFLOWINT pThis = hFlow; if (!pThis) return 0; AssertPtrReturn(pThis, UINT32_MAX); uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs); AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pThis)); if (cRefs == 0) dbgfR3FlowDestroy(pThis); return cRefs; } /** * Queries the basic block denoting the entry point into the control flow graph. * * @returns VBox status code. * @param hFlow The control flow graph handle. * @param phFlowBb Where to store the basic block handle on success. */ VMMR3DECL(int) DBGFR3FlowQueryStartBb(DBGFFLOW hFlow, PDBGFFLOWBB phFlowBb) { PDBGFFLOWINT pThis = hFlow; AssertPtrReturn(pThis, VERR_INVALID_HANDLE); PDBGFFLOWBBINT pFlowBb; RTListForEach(&pThis->LstFlowBb, pFlowBb, DBGFFLOWBBINT, NdFlowBb) { if (pFlowBb->fFlags & DBGF_FLOW_BB_F_ENTRY) { *phFlowBb = pFlowBb; return VINF_SUCCESS; } } AssertFailed(); /* Should never get here. */ return VERR_INTERNAL_ERROR; } /** * Queries a basic block in the given control flow graph which covers the given * address. * * @returns VBox status code. * @retval VERR_NOT_FOUND if there is no basic block intersecting with the address. * @param hFlow The control flow graph handle. * @param pAddr The address to look for. * @param phFlowBb Where to store the basic block handle on success. */ VMMR3DECL(int) DBGFR3FlowQueryBbByAddress(DBGFFLOW hFlow, PDBGFADDRESS pAddr, PDBGFFLOWBB phFlowBb) { PDBGFFLOWINT pThis = hFlow; AssertPtrReturn(pThis, VERR_INVALID_HANDLE); AssertPtrReturn(pAddr, VERR_INVALID_POINTER); AssertPtrReturn(phFlowBb, VERR_INVALID_POINTER); PDBGFFLOWBBINT pFlowBb; RTListForEach(&pThis->LstFlowBb, pFlowBb, DBGFFLOWBBINT, NdFlowBb) { if (dbgfR3FlowAddrIntersect(pFlowBb, pAddr)) { DBGFR3FlowBbRetain(pFlowBb); *phFlowBb = pFlowBb; return VINF_SUCCESS; } } return VERR_NOT_FOUND; } /** * Queries a branch table in the given control flow graph by the given address. * * @returns VBox status code. * @retval VERR_NOT_FOUND if there is no branch table with the given address. * @param hFlow The control flow graph handle. * @param pAddr The address of the branch table. * @param phFlowBranchTbl Where to store the handle to branch table on success. * * @note Call DBGFR3FlowBranchTblRelease() when the handle is not required anymore. */ VMMR3DECL(int) DBGFR3FlowQueryBranchTblByAddress(DBGFFLOW hFlow, PDBGFADDRESS pAddr, PDBGFFLOWBRANCHTBL phFlowBranchTbl) { PDBGFFLOWINT pThis = hFlow; AssertPtrReturn(pThis, VERR_INVALID_HANDLE); AssertPtrReturn(pAddr, VERR_INVALID_POINTER); AssertPtrReturn(phFlowBranchTbl, VERR_INVALID_POINTER); PDBGFFLOWBRANCHTBLINT pBranchTbl = dbgfR3FlowBranchTblFindByAddr(pThis, pAddr); if (pBranchTbl) { DBGFR3FlowBranchTblRetain(pBranchTbl); *phFlowBranchTbl = pBranchTbl; return VINF_SUCCESS; } return VERR_NOT_FOUND; } /** * Returns the number of basic blcoks inside the control flow graph. * * @returns Number of basic blocks. * @param hFlow The control flow graph handle. */ VMMR3DECL(uint32_t) DBGFR3FlowGetBbCount(DBGFFLOW hFlow) { PDBGFFLOWINT pThis = hFlow; AssertPtrReturn(pThis, 0); return pThis->cBbs; } /** * Returns the number of branch tables inside the control flow graph. * * @returns Number of basic blocks. * @param hFlow The control flow graph handle. */ VMMR3DECL(uint32_t) DBGFR3FlowGetBranchTblCount(DBGFFLOW hFlow) { PDBGFFLOWINT pThis = hFlow; AssertPtrReturn(pThis, 0); return pThis->cBranchTbls; } /** * Returns the number of call instructions encountered in the given * control flow graph. * * @returns Number of call instructions. * @param hFlow The control flow graph handle. */ VMMR3DECL(uint32_t) DBGFR3FlowGetCallInsnCount(DBGFFLOW hFlow) { PDBGFFLOWINT pThis = hFlow; AssertPtrReturn(pThis, 0); return pThis->cCallInsns; } /** * Retains the basic block handle. * * @returns Current reference count. * @param hFlowBb The basic block handle to retain. */ VMMR3DECL(uint32_t) DBGFR3FlowBbRetain(DBGFFLOWBB hFlowBb) { PDBGFFLOWBBINT pFlowBb = hFlowBb; AssertPtrReturn(pFlowBb, UINT32_MAX); uint32_t cRefs = ASMAtomicIncU32(&pFlowBb->cRefs); AssertMsg(cRefs > 1 && cRefs < _1M, ("%#x %p %d\n", cRefs, pFlowBb, pFlowBb->enmEndType)); return cRefs; } /** * Releases the basic block handle. * * @returns Current reference count, on 0 the basic block will be destroyed. * @param hFlowBb The basic block handle to release. */ VMMR3DECL(uint32_t) DBGFR3FlowBbRelease(DBGFFLOWBB hFlowBb) { PDBGFFLOWBBINT pFlowBb = hFlowBb; if (!pFlowBb) return 0; return dbgfR3FlowBbReleaseInt(pFlowBb, true /* fMayDestroyFlow */); } /** * Returns the start address of the basic block. * * @returns Pointer to DBGF adress containing the start address of the basic block. * @param hFlowBb The basic block handle. * @param pAddrStart Where to store the start address of the basic block. */ VMMR3DECL(PDBGFADDRESS) DBGFR3FlowBbGetStartAddress(DBGFFLOWBB hFlowBb, PDBGFADDRESS pAddrStart) { PDBGFFLOWBBINT pFlowBb = hFlowBb; AssertPtrReturn(pFlowBb, NULL); AssertPtrReturn(pAddrStart, NULL); *pAddrStart = pFlowBb->AddrStart; return pAddrStart; } /** * Returns the end address of the basic block (inclusive). * * @returns Pointer to DBGF adress containing the end address of the basic block. * @param hFlowBb The basic block handle. * @param pAddrEnd Where to store the end address of the basic block. */ VMMR3DECL(PDBGFADDRESS) DBGFR3FlowBbGetEndAddress(DBGFFLOWBB hFlowBb, PDBGFADDRESS pAddrEnd) { PDBGFFLOWBBINT pFlowBb = hFlowBb; AssertPtrReturn(pFlowBb, NULL); AssertPtrReturn(pAddrEnd, NULL); *pAddrEnd = pFlowBb->AddrEnd; return pAddrEnd; } /** * Returns the address the last instruction in the basic block branches to. * * @returns Pointer to DBGF adress containing the branch address of the basic block. * @param hFlowBb The basic block handle. * @param pAddrTarget Where to store the branch address of the basic block. * * @note This is only valid for unconditional or conditional branches, or for a basic block * containing only a call instruction when DBGF_FLOW_CREATE_F_CALL_INSN_SEPARATE_BB was given * during creation and the branch target could be deduced as indicated by the DBGF_FLOW_BB_F_CALL_INSN_TARGET_KNOWN * flag for the basic block. This method will assert for every other basic block type. * @note For indirect unconditional branches using a branch table this will return the start address * of the branch table. */ VMMR3DECL(PDBGFADDRESS) DBGFR3FlowBbGetBranchAddress(DBGFFLOWBB hFlowBb, PDBGFADDRESS pAddrTarget) { PDBGFFLOWBBINT pFlowBb = hFlowBb; AssertPtrReturn(pFlowBb, NULL); AssertPtrReturn(pAddrTarget, NULL); AssertReturn( pFlowBb->enmEndType == DBGFFLOWBBENDTYPE_UNCOND_JMP || pFlowBb->enmEndType == DBGFFLOWBBENDTYPE_COND || pFlowBb->enmEndType == DBGFFLOWBBENDTYPE_UNCOND_INDIRECT_JMP || ( pFlowBb->enmEndType == DBGFFLOWBBENDTYPE_UNCOND && (pFlowBb->fFlags & DBGF_FLOW_BB_F_CALL_INSN_TARGET_KNOWN)), NULL); if ( pFlowBb->enmEndType == DBGFFLOWBBENDTYPE_UNCOND_INDIRECT_JMP && pFlowBb->pFlowBranchTbl) *pAddrTarget = pFlowBb->pFlowBranchTbl->AddrStart; else *pAddrTarget = pFlowBb->AddrTarget; return pAddrTarget; } /** * Returns the address of the next block following this one in the instruction stream. * (usually end address + 1). * * @returns Pointer to DBGF adress containing the following address of the basic block. * @param hFlowBb The basic block handle. * @param pAddrFollow Where to store the following address of the basic block. * * @note This is only valid for conditional branches and if the last instruction in the * given basic block doesn't change the control flow but the blocks were split * because the successor is referenced by multiple other blocks as an entry point. */ VMMR3DECL(PDBGFADDRESS) DBGFR3FlowBbGetFollowingAddress(DBGFFLOWBB hFlowBb, PDBGFADDRESS pAddrFollow) { PDBGFFLOWBBINT pFlowBb = hFlowBb; AssertPtrReturn(pFlowBb, NULL); AssertPtrReturn(pAddrFollow, NULL); AssertReturn( pFlowBb->enmEndType == DBGFFLOWBBENDTYPE_UNCOND || pFlowBb->enmEndType == DBGFFLOWBBENDTYPE_COND, NULL); *pAddrFollow = pFlowBb->AddrEnd; DBGFR3AddrAdd(pAddrFollow, 1); return pAddrFollow; } /** * Returns the type of the last instruction in the basic block. * * @returns Last instruction type. * @param hFlowBb The basic block handle. */ VMMR3DECL(DBGFFLOWBBENDTYPE) DBGFR3FlowBbGetType(DBGFFLOWBB hFlowBb) { PDBGFFLOWBBINT pFlowBb = hFlowBb; AssertPtrReturn(pFlowBb, DBGFFLOWBBENDTYPE_INVALID); return pFlowBb->enmEndType; } /** * Get the number of instructions contained in the basic block. * * @returns Number of instructions in the basic block. * @param hFlowBb The basic block handle. */ VMMR3DECL(uint32_t) DBGFR3FlowBbGetInstrCount(DBGFFLOWBB hFlowBb) { PDBGFFLOWBBINT pFlowBb = hFlowBb; AssertPtrReturn(pFlowBb, 0); return pFlowBb->cInstr; } /** * Get flags for the given basic block. * * @returns Combination of DBGF_FLOW_BB_F_* * @param hFlowBb The basic block handle. */ VMMR3DECL(uint32_t) DBGFR3FlowBbGetFlags(DBGFFLOWBB hFlowBb) { PDBGFFLOWBBINT pFlowBb = hFlowBb; AssertPtrReturn(pFlowBb, 0); return pFlowBb->fFlags; } /** * Queries the branch table used if the given basic block ends with an indirect branch * and has a branch table referenced. * * @returns VBox status code. * @param hFlowBb The basic block handle. * @param phBranchTbl Where to store the branch table handle on success. * * @note Release the branch table reference with DBGFR3FlowBranchTblRelease() when not required * anymore. */ VMMR3DECL(int) DBGFR3FlowBbQueryBranchTbl(DBGFFLOWBB hFlowBb, PDBGFFLOWBRANCHTBL phBranchTbl) { PDBGFFLOWBBINT pFlowBb = hFlowBb; AssertPtrReturn(pFlowBb, VERR_INVALID_HANDLE); AssertReturn(pFlowBb->enmEndType == DBGFFLOWBBENDTYPE_UNCOND_INDIRECT_JMP, VERR_INVALID_STATE); AssertPtrReturn(pFlowBb->pFlowBranchTbl, VERR_INVALID_STATE); AssertPtrReturn(phBranchTbl, VERR_INVALID_POINTER); DBGFR3FlowBranchTblRetain(pFlowBb->pFlowBranchTbl); *phBranchTbl = pFlowBb->pFlowBranchTbl; return VINF_SUCCESS; } /** * Returns the error status and message if the given basic block has an error. * * @returns VBox status code of the error for the basic block. * @param hFlowBb The basic block handle. * @param ppszErr Where to store the pointer to the error message - optional. */ VMMR3DECL(int) DBGFR3FlowBbQueryError(DBGFFLOWBB hFlowBb, const char **ppszErr) { PDBGFFLOWBBINT pFlowBb = hFlowBb; AssertPtrReturn(pFlowBb, VERR_INVALID_HANDLE); if (ppszErr) *ppszErr = pFlowBb->pszErr; return pFlowBb->rcError; } /** * Store the disassembled instruction as a string in the given output buffer. * * @returns VBox status code. * @param hFlowBb The basic block handle. * @param idxInstr The instruction to query. * @param pAddrInstr Where to store the guest instruction address on success, optional. * @param pcbInstr Where to store the instruction size on success, optional. * @param ppszInstr Where to store the pointer to the disassembled instruction string, optional. */ VMMR3DECL(int) DBGFR3FlowBbQueryInstr(DBGFFLOWBB hFlowBb, uint32_t idxInstr, PDBGFADDRESS pAddrInstr, uint32_t *pcbInstr, const char **ppszInstr) { PDBGFFLOWBBINT pFlowBb = hFlowBb; AssertPtrReturn(pFlowBb, VERR_INVALID_POINTER); AssertReturn(idxInstr < pFlowBb->cInstr, VERR_INVALID_PARAMETER); if (pAddrInstr) *pAddrInstr = pFlowBb->aInstr[idxInstr].AddrInstr; if (pcbInstr) *pcbInstr = pFlowBb->aInstr[idxInstr].cbInstr; if (ppszInstr) *ppszInstr = pFlowBb->aInstr[idxInstr].pszInstr; return VINF_SUCCESS; } /** * Queries the successors of the basic block. * * @returns VBox status code. * @param hFlowBb The basic block handle. * @param phFlowBbFollow Where to store the handle to the basic block following * this one (optional). * @param phFlowBbTarget Where to store the handle to the basic block being the * branch target for this one (optional). */ VMMR3DECL(int) DBGFR3FlowBbQuerySuccessors(DBGFFLOWBB hFlowBb, PDBGFFLOWBB phFlowBbFollow, PDBGFFLOWBB phFlowBbTarget) { PDBGFFLOWBBINT pFlowBb = hFlowBb; AssertPtrReturn(pFlowBb, VERR_INVALID_POINTER); if ( phFlowBbFollow && ( pFlowBb->enmEndType == DBGFFLOWBBENDTYPE_UNCOND || pFlowBb->enmEndType == DBGFFLOWBBENDTYPE_COND)) { DBGFADDRESS AddrStart = pFlowBb->AddrEnd; DBGFR3AddrAdd(&AddrStart, 1); int rc = DBGFR3FlowQueryBbByAddress(pFlowBb->pFlow, &AddrStart, phFlowBbFollow); AssertRC(rc); } if ( phFlowBbTarget && ( pFlowBb->enmEndType == DBGFFLOWBBENDTYPE_UNCOND_JMP || pFlowBb->enmEndType == DBGFFLOWBBENDTYPE_COND)) { int rc = DBGFR3FlowQueryBbByAddress(pFlowBb->pFlow, &pFlowBb->AddrTarget, phFlowBbTarget); AssertRC(rc); } return VINF_SUCCESS; } /** * Returns the number of basic blocks referencing this basic block as a target. * * @returns Number of other basic blocks referencing this one. * @param hFlowBb The basic block handle. * * @note If the given basic block references itself (loop, etc.) this will be counted as well. */ VMMR3DECL(uint32_t) DBGFR3FlowBbGetRefBbCount(DBGFFLOWBB hFlowBb) { PDBGFFLOWBBINT pFlowBb = hFlowBb; AssertPtrReturn(pFlowBb, 0); uint32_t cRefsBb = 0; PDBGFFLOWBBINT pFlowBbCur; RTListForEach(&pFlowBb->pFlow->LstFlowBb, pFlowBbCur, DBGFFLOWBBINT, NdFlowBb) { if (pFlowBbCur->fFlags & DBGF_FLOW_BB_F_INCOMPLETE_ERR) continue; if ( pFlowBbCur->enmEndType == DBGFFLOWBBENDTYPE_UNCOND || pFlowBbCur->enmEndType == DBGFFLOWBBENDTYPE_COND) { DBGFADDRESS AddrStart = pFlowBb->AddrEnd; DBGFR3AddrAdd(&AddrStart, 1); if (dbgfR3FlowAddrEqual(&pFlowBbCur->AddrStart, &AddrStart)) cRefsBb++; } if ( ( pFlowBbCur->enmEndType == DBGFFLOWBBENDTYPE_UNCOND_JMP || pFlowBbCur->enmEndType == DBGFFLOWBBENDTYPE_COND) && dbgfR3FlowAddrEqual(&pFlowBbCur->AddrStart, &pFlowBb->AddrTarget)) cRefsBb++; } return cRefsBb; } /** * Returns the basic block handles referencing the given basic block. * * @returns VBox status code. * @retval VERR_BUFFER_OVERFLOW if the array can't hold all the basic blocks. * @param hFlowBb The basic block handle. * @param paFlowBbRef Pointer to the array containing the referencing basic block handles on success. * @param cRef Number of entries in the given array. */ VMMR3DECL(int) DBGFR3FlowBbGetRefBb(DBGFFLOWBB hFlowBb, PDBGFFLOWBB paFlowBbRef, uint32_t cRef) { RT_NOREF3(hFlowBb, paFlowBbRef, cRef); return VERR_NOT_IMPLEMENTED; } /** * Retains a reference for the given control flow graph branch table. * * @returns new reference count. * @param hFlowBranchTbl The branch table handle. */ VMMR3DECL(uint32_t) DBGFR3FlowBranchTblRetain(DBGFFLOWBRANCHTBL hFlowBranchTbl) { PDBGFFLOWBRANCHTBLINT pFlowBranchTbl = hFlowBranchTbl; AssertPtrReturn(pFlowBranchTbl, UINT32_MAX); uint32_t cRefs = ASMAtomicIncU32(&pFlowBranchTbl->cRefs); AssertMsg(cRefs > 1 && cRefs < _1M, ("%#x %p\n", cRefs, pFlowBranchTbl)); return cRefs; } /** * Releases a given branch table handle. * * @returns the new reference count of the given branch table, on 0 it is destroyed. * @param hFlowBranchTbl The branch table handle. */ VMMR3DECL(uint32_t) DBGFR3FlowBranchTblRelease(DBGFFLOWBRANCHTBL hFlowBranchTbl) { PDBGFFLOWBRANCHTBLINT pFlowBranchTbl = hFlowBranchTbl; if (!pFlowBranchTbl) return 0; AssertPtrReturn(pFlowBranchTbl, UINT32_MAX); uint32_t cRefs = ASMAtomicDecU32(&pFlowBranchTbl->cRefs); AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pFlowBranchTbl)); if (cRefs == 0) dbgfR3FlowBranchTblDestroy(pFlowBranchTbl); return cRefs; } /** * Return the number of slots the branch table has. * * @returns Number of slots in the branch table. * @param hFlowBranchTbl The branch table handle. */ VMMR3DECL(uint32_t) DBGFR3FlowBranchTblGetSlots(DBGFFLOWBRANCHTBL hFlowBranchTbl) { PDBGFFLOWBRANCHTBLINT pFlowBranchTbl = hFlowBranchTbl; AssertPtrReturn(pFlowBranchTbl, 0); return pFlowBranchTbl->cSlots; } /** * Returns the start address of the branch table in the guest. * * @returns Pointer to start address of the branch table (pAddrStart). * @param hFlowBranchTbl The branch table handle. * @param pAddrStart Where to store the branch table address. */ VMMR3DECL(PDBGFADDRESS) DBGFR3FlowBranchTblGetStartAddress(DBGFFLOWBRANCHTBL hFlowBranchTbl, PDBGFADDRESS pAddrStart) { PDBGFFLOWBRANCHTBLINT pFlowBranchTbl = hFlowBranchTbl; AssertPtrReturn(pFlowBranchTbl, NULL); AssertPtrReturn(pAddrStart, NULL); *pAddrStart = pFlowBranchTbl->AddrStart; return pAddrStart; } /** * Returns one address in the branch table at the given slot index. * * @return Pointer to the address at the given slot in the given branch table. * @param hFlowBranchTbl The branch table handle. * @param idxSlot The slot the address should be returned from. * @param pAddrSlot Where to store the address. */ VMMR3DECL(PDBGFADDRESS) DBGFR3FlowBranchTblGetAddrAtSlot(DBGFFLOWBRANCHTBL hFlowBranchTbl, uint32_t idxSlot, PDBGFADDRESS pAddrSlot) { PDBGFFLOWBRANCHTBLINT pFlowBranchTbl = hFlowBranchTbl; AssertPtrReturn(pFlowBranchTbl, NULL); AssertPtrReturn(pAddrSlot, NULL); AssertReturn(idxSlot < pFlowBranchTbl->cSlots, NULL); *pAddrSlot = pFlowBranchTbl->aAddresses[idxSlot]; return pAddrSlot; } /** * Query all addresses contained in the given branch table. * * @returns VBox status code. * @retval VERR_BUFFER_OVERFLOW if there is not enough space in the array to hold all addresses. * @param hFlowBranchTbl The branch table handle. * @param paAddrs Where to store the addresses on success. * @param cAddrs Number of entries the array can hold. */ VMMR3DECL(int) DBGFR3FlowBranchTblQueryAddresses(DBGFFLOWBRANCHTBL hFlowBranchTbl, PDBGFADDRESS paAddrs, uint32_t cAddrs) { PDBGFFLOWBRANCHTBLINT pFlowBranchTbl = hFlowBranchTbl; AssertPtrReturn(pFlowBranchTbl, VERR_INVALID_HANDLE); AssertPtrReturn(paAddrs, VERR_INVALID_POINTER); AssertReturn(cAddrs > 0, VERR_INVALID_PARAMETER); if (cAddrs < pFlowBranchTbl->cSlots) return VERR_BUFFER_OVERFLOW; memcpy(paAddrs, &pFlowBranchTbl->aAddresses[0], pFlowBranchTbl->cSlots * sizeof(DBGFADDRESS)); return VINF_SUCCESS; } /** * @callback_method_impl{FNRTSORTCMP} */ static DECLCALLBACK(int) dbgfR3FlowItSortCmp(void const *pvElement1, void const *pvElement2, void *pvUser) { PDBGFFLOWITORDER penmOrder = (PDBGFFLOWITORDER)pvUser; PDBGFFLOWBBINT pFlowBb1 = *(PDBGFFLOWBBINT *)pvElement1; PDBGFFLOWBBINT pFlowBb2 = *(PDBGFFLOWBBINT *)pvElement2; if (dbgfR3FlowAddrEqual(&pFlowBb1->AddrStart, &pFlowBb2->AddrStart)) return 0; if (*penmOrder == DBGFFLOWITORDER_BY_ADDR_LOWEST_FIRST) { if (dbgfR3FlowAddrLower(&pFlowBb1->AddrStart, &pFlowBb2->AddrStart)) return -1; else return 1; } else { if (dbgfR3FlowAddrLower(&pFlowBb1->AddrStart, &pFlowBb2->AddrStart)) return 1; else return -1; } } /** * Creates a new iterator for the given control flow graph. * * @returns VBox status code. * @param hFlow The control flow graph handle. * @param enmOrder The order in which the basic blocks are enumerated. * @param phFlowIt Where to store the handle to the iterator on success. */ VMMR3DECL(int) DBGFR3FlowItCreate(DBGFFLOW hFlow, DBGFFLOWITORDER enmOrder, PDBGFFLOWIT phFlowIt) { int rc = VINF_SUCCESS; PDBGFFLOWINT pFlow = hFlow; AssertPtrReturn(pFlow, VERR_INVALID_POINTER); AssertPtrReturn(phFlowIt, VERR_INVALID_POINTER); AssertReturn(enmOrder > DBGFFLOWITORDER_INVALID && enmOrder < DBGFFLOWITORDER_BREADTH_FIRST, VERR_INVALID_PARAMETER); AssertReturn(enmOrder < DBGFFLOWITORDER_DEPTH_FRIST, VERR_NOT_IMPLEMENTED); /** @todo */ PDBGFFLOWITINT pIt = (PDBGFFLOWITINT)RTMemAllocZ(RT_UOFFSETOF_DYN(DBGFFLOWITINT, apBb[pFlow->cBbs])); if (RT_LIKELY(pIt)) { DBGFR3FlowRetain(hFlow); pIt->pFlow = pFlow; pIt->idxBbNext = 0; /* Fill the list and then sort. */ uint32_t idxBb = 0; PDBGFFLOWBBINT pFlowBb; RTListForEach(&pFlow->LstFlowBb, pFlowBb, DBGFFLOWBBINT, NdFlowBb) { DBGFR3FlowBbRetain(pFlowBb); pIt->apBb[idxBb++] = pFlowBb; } /* Sort the blocks by address. */ RTSortShell(&pIt->apBb[0], pFlow->cBbs, sizeof(PDBGFFLOWBBINT), dbgfR3FlowItSortCmp, &enmOrder); *phFlowIt = pIt; } else rc = VERR_NO_MEMORY; return rc; } /** * Destroys a given control flow graph iterator. * * @returns nothing. * @param hFlowIt The control flow graph iterator handle. */ VMMR3DECL(void) DBGFR3FlowItDestroy(DBGFFLOWIT hFlowIt) { PDBGFFLOWITINT pIt = hFlowIt; AssertPtrReturnVoid(pIt); for (unsigned i = 0; i < pIt->pFlow->cBbs; i++) DBGFR3FlowBbRelease(pIt->apBb[i]); DBGFR3FlowRelease(pIt->pFlow); RTMemFree(pIt); } /** * Returns the next basic block in the iterator or NULL if there is no * basic block left. * * @returns Handle to the next basic block in the iterator or NULL if the end * was reached. * @param hFlowIt The iterator handle. * * @note If a valid handle is returned it must be release with DBGFR3FlowBbRelease() * when not required anymore. */ VMMR3DECL(DBGFFLOWBB) DBGFR3FlowItNext(DBGFFLOWIT hFlowIt) { PDBGFFLOWITINT pIt = hFlowIt; AssertPtrReturn(pIt, NULL); PDBGFFLOWBBINT pFlowBb = NULL; if (pIt->idxBbNext < pIt->pFlow->cBbs) { pFlowBb = pIt->apBb[pIt->idxBbNext++]; DBGFR3FlowBbRetain(pFlowBb); } return pFlowBb; } /** * Resets the given iterator to the beginning. * * @returns VBox status code. * @param hFlowIt The iterator handle. */ VMMR3DECL(int) DBGFR3FlowItReset(DBGFFLOWIT hFlowIt) { PDBGFFLOWITINT pIt = hFlowIt; AssertPtrReturn(pIt, VERR_INVALID_HANDLE); pIt->idxBbNext = 0; return VINF_SUCCESS; } /** * @callback_method_impl{FNRTSORTCMP} */ static DECLCALLBACK(int) dbgfR3FlowBranchTblItSortCmp(void const *pvElement1, void const *pvElement2, void *pvUser) { PDBGFFLOWITORDER penmOrder = (PDBGFFLOWITORDER)pvUser; PDBGFFLOWBRANCHTBLINT pTbl1 = *(PDBGFFLOWBRANCHTBLINT *)pvElement1; PDBGFFLOWBRANCHTBLINT pTbl2 = *(PDBGFFLOWBRANCHTBLINT *)pvElement2; if (dbgfR3FlowAddrEqual(&pTbl1->AddrStart, &pTbl2->AddrStart)) return 0; if (*penmOrder == DBGFFLOWITORDER_BY_ADDR_LOWEST_FIRST) { if (dbgfR3FlowAddrLower(&pTbl1->AddrStart, &pTbl2->AddrStart)) return -1; else return 1; } else { if (dbgfR3FlowAddrLower(&pTbl1->AddrStart, &pTbl2->AddrStart)) return 1; else return -1; } } /** * Creates a new branch table iterator for the given control flow graph. * * @returns VBox status code. * @param hFlow The control flow graph handle. * @param enmOrder The order in which the basic blocks are enumerated. * @param phFlowBranchTblIt Where to store the handle to the iterator on success. */ VMMR3DECL(int) DBGFR3FlowBranchTblItCreate(DBGFFLOW hFlow, DBGFFLOWITORDER enmOrder, PDBGFFLOWBRANCHTBLIT phFlowBranchTblIt) { int rc = VINF_SUCCESS; PDBGFFLOWINT pFlow = hFlow; AssertPtrReturn(pFlow, VERR_INVALID_POINTER); AssertPtrReturn(phFlowBranchTblIt, VERR_INVALID_POINTER); AssertReturn(enmOrder > DBGFFLOWITORDER_INVALID && enmOrder < DBGFFLOWITORDER_BREADTH_FIRST, VERR_INVALID_PARAMETER); AssertReturn(enmOrder < DBGFFLOWITORDER_DEPTH_FRIST, VERR_NOT_SUPPORTED); PDBGFFLOWBRANCHTBLITINT pIt = (PDBGFFLOWBRANCHTBLITINT)RTMemAllocZ(RT_UOFFSETOF_DYN(DBGFFLOWBRANCHTBLITINT, apBranchTbl[pFlow->cBranchTbls])); if (RT_LIKELY(pIt)) { DBGFR3FlowRetain(hFlow); pIt->pFlow = pFlow; pIt->idxTblNext = 0; /* Fill the list and then sort. */ uint32_t idxTbl = 0; PDBGFFLOWBRANCHTBLINT pFlowBranchTbl; RTListForEach(&pFlow->LstBranchTbl, pFlowBranchTbl, DBGFFLOWBRANCHTBLINT, NdBranchTbl) { DBGFR3FlowBranchTblRetain(pFlowBranchTbl); pIt->apBranchTbl[idxTbl++] = pFlowBranchTbl; } /* Sort the blocks by address. */ RTSortShell(&pIt->apBranchTbl[0], pFlow->cBranchTbls, sizeof(PDBGFFLOWBRANCHTBLINT), dbgfR3FlowBranchTblItSortCmp, &enmOrder); *phFlowBranchTblIt = pIt; } else rc = VERR_NO_MEMORY; return rc; } /** * Destroys a given control flow graph branch table iterator. * * @returns nothing. * @param hFlowBranchTblIt The control flow graph branch table iterator handle. */ VMMR3DECL(void) DBGFR3FlowBranchTblItDestroy(DBGFFLOWBRANCHTBLIT hFlowBranchTblIt) { PDBGFFLOWBRANCHTBLITINT pIt = hFlowBranchTblIt; AssertPtrReturnVoid(pIt); for (unsigned i = 0; i < pIt->pFlow->cBranchTbls; i++) DBGFR3FlowBranchTblRelease(pIt->apBranchTbl[i]); DBGFR3FlowRelease(pIt->pFlow); RTMemFree(pIt); } /** * Returns the next branch table in the iterator or NULL if there is no * branch table left. * * @returns Handle to the next basic block in the iterator or NULL if the end * was reached. * @param hFlowBranchTblIt The iterator handle. * * @note If a valid handle is returned it must be release with DBGFR3FlowBranchTblRelease() * when not required anymore. */ VMMR3DECL(DBGFFLOWBRANCHTBL) DBGFR3FlowBranchTblItNext(DBGFFLOWBRANCHTBLIT hFlowBranchTblIt) { PDBGFFLOWBRANCHTBLITINT pIt = hFlowBranchTblIt; AssertPtrReturn(pIt, NULL); PDBGFFLOWBRANCHTBLINT pTbl = NULL; if (pIt->idxTblNext < pIt->pFlow->cBranchTbls) { pTbl = pIt->apBranchTbl[pIt->idxTblNext++]; DBGFR3FlowBranchTblRetain(pTbl); } return pTbl; } /** * Resets the given iterator to the beginning. * * @returns VBox status code. * @param hFlowBranchTblIt The iterator handle. */ VMMR3DECL(int) DBGFR3FlowBranchTblItReset(DBGFFLOWBRANCHTBLIT hFlowBranchTblIt) { PDBGFFLOWBRANCHTBLITINT pIt = hFlowBranchTblIt; AssertPtrReturn(pIt, VERR_INVALID_HANDLE); pIt->idxTblNext = 0; return VINF_SUCCESS; }