dis.h revision 10f18618390096a9c968016b6ca94d77b91618fb
/** @file
* DIS - The VirtualBox Disassembler.
*/
/*
* Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*/
#ifndef ___VBox_dis_h
#define ___VBox_dis_h
#include <VBox/disopcode.h>
/**
* CPU mode flags (DISCPUSTATE::mode).
*/
typedef enum DISCPUMODE
{
DISCPUMODE_INVALID = 0,
/** hack forcing the size of the enum to 32-bits. */
DISCPUMODE_MAKE_32BIT_HACK = 0x7fffffff
} DISCPUMODE;
/** @name Prefix byte flags (DISCPUSTATE::fPrefix).
* @{
*/
/** non-default address size. */
/** non-default operand size. */
/** lock prefix. */
/** segment prefix. */
/** rep(e) prefix (not a prefix, but we'll treat is as one). */
/** rep(e) prefix (not a prefix, but we'll treat is as one). */
/** REX prefix (64 bits) */
/** @} */
/** @name 64 bits prefix byte flags (DISCPUSTATE::fRexPrefix).
* Requires VBox/disopcode.h.
* @{
*/
#define DISPREFIX_REX_OP_2_FLAGS(a) (a - OP_PARM_REX_START)
/** @} */
/** @name Operand type (DISOPCODE::fOpType).
* @{
*/
#define DISOPTYPE_INVALID RT_BIT_32(0)
#define DISOPTYPE_RRM_DANGEROUS RT_BIT_32(14) /**< Some additional dangerous ones when recompiling raw r0. */
#define DISOPTYPE_RRM_DANGEROUS_16 RT_BIT_32(15) /**< Some additional dangerous ones when recompiling 16-bit raw r0. */
#define DISOPTYPE_FORCED_64_OP_SIZE RT_BIT_32(22) /**< Forced 64 bits operand size; regardless of prefix bytes */
#define DISOPTYPE_REXB_EXTENDS_OPREG RT_BIT_32(23) /**< REX.B extends the register field in the opcode byte */
#define DISOPTYPE_FORCED_32_OP_SIZE_X86 RT_BIT_32(25) /**< Forced 32 bits operand size; regardless of prefix bytes (only in 16 & 32 bits mode!) */
/** @} */
/** @name Parameter usage flags.
* @{
*/
#define DISUSE_BASE RT_BIT_64(0)
/** DS:ESI */
/** ES:EDI */
/** Mask of immediate use flags. */
#define DISUSE_IMMEDIATE ( DISUSE_IMMEDIATE8 \
/** Check if the use flags indicates an effective address. */
& ( DISUSE_BASE \
| DISUSE_INDEX \
| DISUSE_RIPDISPLACEMENT32) ))
/** @} */
/** @name 64-bit general register indexes.
* This matches the AMD64 register encoding. It is found used in
* DISOPPARAM::Base.idxGenReg and DISOPPARAM::Index.idxGenReg.
* @note Safe to assume same values as the 16-bit and 32-bit general registers.
* @{
*/
#define DISGREG_RAX UINT8_C(0)
/** @} */
/** @name 32-bit general register indexes.
* This matches the AMD64 register encoding. It is found used in
* DISOPPARAM::Base.idxGenReg and DISOPPARAM::Index.idxGenReg.
* @note Safe to assume same values as the 16-bit and 64-bit general registers.
* @{
*/
#define DISGREG_EAX UINT8_C(0)
/** @} */
/** @name 16-bit general register indexes.
* This matches the AMD64 register encoding. It is found used in
* DISOPPARAM::Base.idxGenReg and DISOPPARAM::Index.idxGenReg.
* @note Safe to assume same values as the 32-bit and 64-bit general registers.
* @{
*/
#define DISGREG_AX UINT8_C(0)
/** @} */
/** @name 8-bit general register indexes.
* This mostly (?) matches the AMD64 register encoding. It is found used in
* DISOPPARAM::Base.idxGenReg and DISOPPARAM::Index.idxGenReg.
* @{
*/
#define DISGREG_AL UINT8_C(0)
/** @} */
/** @name Segment registerindexes.
* This matches the AMD64 register encoding. It is found used in
* DISOPPARAM::Base.idxSegReg.
* @{
*/
typedef enum
{
DISSELREG_ES = 0,
DISSELREG_CS = 1,
DISSELREG_SS = 2,
DISSELREG_DS = 3,
DISSELREG_FS = 4,
DISSELREG_GS = 5,
/** End of the valid register index values. */
/** The usual 32-bit paranoia. */
DIS_SEGREG_32BIT_HACK = 0x7fffffff
} DISSELREG;
/** @} */
/** @name FPU register indexes.
* This matches the AMD64 register encoding. It is found used in
* DISOPPARAM::Base.idxFpuReg.
* @{
*/
#define DISFPREG_ST0 UINT8_C(0)
/** @} */
/** @name Control register indexes.
* This matches the AMD64 register encoding. It is found used in
* DISOPPARAM::Base.idxCtrlReg.
* @{
*/
#define DISCREG_CR0 UINT8_C(0)
/** @} */
/** @name Debug register indexes.
* This matches the AMD64 register encoding. It is found used in
* DISOPPARAM::Base.idxDbgReg.
* @{
*/
#define DISDREG_DR0 UINT8_C(0)
/** @} */
/** @name MMX register indexes.
* This matches the AMD64 register encoding. It is found used in
* DISOPPARAM::Base.idxMmxReg.
* @{
*/
#define DISMREG_MMX0 UINT8_C(0)
/** @} */
/** @name SSE register indexes.
* This matches the AMD64 register encoding. It is found used in
* DISOPPARAM::Base.idxXmmReg.
* @{
*/
#define DISXREG_XMM0 UINT8_C(0)
/** @} */
/**
* Opcode parameter (operand) details.
*/
typedef struct DISOPPARAM
{
/** A combination of DISUSE_XXX. */
/** Immediate value or address, applicable if any of the flags included in
* DISUSE_IMMEDIATE are set in fUse. */
/** Disposition. */
union
{
/** 64-bit displacement, applicable if DISUSE_DISPLACEMENT64 is set in fUse. */
/** 32-bit displacement, applicable if DISUSE_DISPLACEMENT32 or
* DISUSE_RIPDISPLACEMENT32 is set in fUse. */
/** 16-bit displacement, applicable if DISUSE_DISPLACEMENT16 is set in fUse. */
/** 8-bit displacement, applicable if DISUSE_DISPLACEMENT8 is set in fUse. */
} uDisp;
/** The base register from ModR/M or SIB, applicable if DISUSE_BASE is
* set in fUse. */
union
{
/** General register index (DISGREG_XXX), applicable if DISUSE_REG_GEN8,
* DISUSE_REG_GEN16, DISUSE_REG_GEN32 or DISUSE_REG_GEN64 is set in fUse. */
/** FPU stack register index (DISFPREG_XXX), applicable if DISUSE_REG_FP is
* set in fUse. 1:1 indexes. */
/** MMX register index (DISMREG_XXX), applicable if DISUSE_REG_MMX is
* set in fUse. 1:1 indexes. */
/** SSE register index (DISXREG_XXX), applicable if DISUSE_REG_XMM is
* set in fUse. 1:1 indexes. */
/** Segment register index (DISSELREG_XXX), applicable if DISUSE_REG_SEG is
* set in fUse. */
/** Test register, TR0-TR7, present on early IA32 CPUs, applicable if
* DISUSE_REG_TEST is set in fUse. No index defines for these. */
/** Control register index (DISCREG_XXX), applicable if DISUSE_REG_CR is
* set in fUse. 1:1 indexes. */
/** Debug register index (DISDREG_XXX), applicable if DISUSE_REG_DBG is
* set in fUse. 1:1 indexes. */
} Base;
/** The SIB index register meaning, applicable if DISUSE_INDEX is
* set in fUse. */
union
{
/** General register index (DISGREG_XXX), applicable if DISUSE_REG_GEN8,
* DISUSE_REG_GEN16, DISUSE_REG_GEN32 or DISUSE_REG_GEN64 is set in fUse. */
} Index;
/** 2, 4 or 8, if DISUSE_SCALE is set in fUse. */
/** Parameter size. */
/** Copy of the corresponding DISOPCODE::fParam1 / DISOPCODE::fParam2 /
* DISOPCODE::fParam3. */
} DISOPPARAM;
/** Pointer to opcode parameter. */
typedef DISOPPARAM *PDISOPPARAM;
/** Pointer to opcode parameter. */
typedef const DISOPPARAM *PCDISOPPARAM;
/**
* Opcode descriptor.
*/
typedef struct DISOPCODE
{
#ifndef DIS_CORE_ONLY
const char *pszOpcode;
#endif
/** Parameter \#1 parser index. */
/** Parameter \#2 parser index. */
/** Parameter \#3 parser index. */
/** Unused padding. */
/** The opcode identifier. This DIS specific, @see grp_dis_opcodes and
* VBox/disopcode.h. */
/** Parameter \#1 info, @see grp_dis_opparam. */
/** Parameter \#2 info, @see grp_dis_opparam. */
/** Parameter \#3 info, @see grp_dis_opparam. */
/** Operand type flags, DISOPTYPE_XXX. */
} DISOPCODE;
/** Pointer to const opcode. */
typedef const struct DISOPCODE *PCDISOPCODE;
/**
* Callback for reading instruction bytes.
*
* @returns VBox status code, bytes in DISCPUSTATE::abInstr and byte count in
* DISCPUSTATE::cbCachedInstr.
* @param pDis Pointer to the disassembler state. The user
* argument can be found in DISCPUSTATE::pvUser if
* needed.
* @param offInstr The offset relative to the start of the instruction.
*
* To get the source address, add this to
* DISCPUSTATE::uInstrAddr.
*
* To calculate the destination buffer address, use it
* as an index into DISCPUSTATE::abInstr.
*
* @param cbMinRead The minimum number of bytes to read.
* @param cbMaxRead The maximum number of bytes that may be read.
*/
typedef DECLCALLBACK(int) FNDISREADBYTES(PDISCPUSTATE pDis, uint8_t offInstr, uint8_t cbMinRead, uint8_t cbMaxRead);
/** Pointer to a opcode byte reader. */
typedef FNDISREADBYTES *PFNDISREADBYTES;
/** Parser callback.
* @remark no DECLCALLBACK() here because it's considered to be internal (really, I'm too lazy to update all the functions). */
typedef unsigned FNDISPARSE(RTUINTPTR pu8CodeBlock, PCDISOPCODE pOp, PDISOPPARAM pParam, PDISCPUSTATE pCpu);
/** Pointer to a disassembler parser function. */
typedef FNDISPARSE *PFNDISPARSE;
/** Pointer to a const disassembler parser function pointer. */
typedef PFNDISPARSE const *PCPFNDISPARSE;
/**
* The diassembler state and result.
*
* @todo ModRM and SIB could be joined and 6 bytes would be saved, only it
* doesn't make any sense right now because of structure alignment.
*/
typedef struct DISCPUSTATE
{
/* Because of pvUser2, put the less frequently used bits at the top for
now. (Might be better off in the middle?) */
/* off: 0x060 (96) */
/** ModRM fields. */
union
{
/** Bitfield view */
struct
{
unsigned Rm : 4;
unsigned Reg : 4;
unsigned Mod : 2;
} Bits;
/** unsigned view */
unsigned u;
} ModRM;
/** SIB fields. */
union
{
/** Bitfield view */
struct
{
unsigned Base : 4;
unsigned Index : 4;
unsigned Scale : 2;
} Bits;
/** unsigned view */
unsigned u;
} SIB;
/** SIB displacment. */
/* off: 0x06c (108) */
/** The CPU mode (DISCPUMODE). */
/** The addressing mode (DISCPUMODE). */
/** The operand mode (DISCPUMODE). */
/** Per instruction prefix settings. */
/* off: 0x070 (112) */
/** REX prefix value (64 bits only). */
/** Segment prefix value (DISSELREG). */
/** Last prefix byte (for SSE2 extension tables). */
/** First opcode byte of instruction. */
/* off: 0x074 (116) */
/** The size of the prefix bytes. */
/** The instruction size. */
/** The number of valid bytes in abInstr.
* @todo Implement caching and read-ahead tomorrow. */
/** Unused byte. */
/* off: 0x078 (120) */
/** Return code set by a worker function like the opcode bytes readers. */
/** Internal: instruction filter */
/* off: 0x080 (128) */
/** Internal: pointer to disassembly function table */
#if ARCH_BITS == 32
#endif
/** Pointer to the current instruction. */
#if ARCH_BITS == 32
#endif
/* off: 0x090 (144) */
/** The address of the instruction. */
/* off: 0x098 (152) */
/** Optional read function */
#if ARCH_BITS == 32
#endif
/* off: 0x0a0 (160) */
/** The instruction bytes. */
/* off: 0x0b0 (176) */
/** User data supplied as an argument to the APIs. */
void *pvUser;
#if ARCH_BITS == 32
#endif
/** User data that can be set prior to calling the API.
* @deprecated Please don't use this any more. */
void *pvUser2;
#if ARCH_BITS == 32
#endif
} DISCPUSTATE;
DISDECL(int) DISInstrToStrWithReader(RTUINTPTR uInstrAddr, DISCPUMODE enmCpuMode, PFNDISREADBYTES pfnReadBytes, void *pvUser,
DISDECL(int) DISInstr(void const *pvInstr, DISCPUMODE enmCpuMode, PDISCPUSTATE pCpu, uint32_t *pcbInstr);
DISDECL(int) DISInstrWithReader(RTUINTPTR uInstrAddr, DISCPUMODE enmCpuMode, PFNDISREADBYTES pfnReadBytes, void *pvUser,
/** @name Flags returned by DISQueryParamVal (DISQPVPARAMVAL::flags).
* @{
*/
/** @} */
/** @name Types returned by DISQueryParamVal (DISQPVPARAMVAL::flags).
* @{ */
/** @} */
typedef struct
{
union
{
struct
{
} farptr;
} val;
/** Pointer to opcode parameter value. */
typedef DISQPVPARAMVAL *PDISQPVPARAMVAL;
/** Indicates which parameter DISQueryParamVal should operate on. */
typedef enum DISQPVWHICH
{
DISQPVWHICH_DST = 1,
DISQPVWHAT_32_BIT_HACK = 0x7fffffff
} DISQPVWHICH;
DISDECL(int) DISQueryParamVal(PCPUMCTXCORE pCtx, PDISCPUSTATE pCpu, PDISOPPARAM pParam, PDISQPVPARAMVAL pParamVal, DISQPVWHICH parmtype);
DISDECL(int) DISQueryParamRegPtr(PCPUMCTXCORE pCtx, PDISCPUSTATE pCpu, PDISOPPARAM pParam, void **ppReg, size_t *pcbSize);
DISDECL(int) DISFetchRegSegEx(PCCPUMCTXCORE pCtx, DISSELREG sel, RTSEL *pVal, PCPUMSELREGHID *ppSelHidReg);
/**
* Try resolve an address into a symbol name.
*
* For use with DISFormatYasmEx(), DISFormatMasmEx() and DISFormatGasEx().
*
* @returns VBox status code.
* @retval VINF_SUCCESS on success, pszBuf contains the full symbol name.
* @retval VINF_BUFFER_OVERFLOW if pszBuf is too small the symbol name. The
* content of pszBuf is truncated and zero terminated.
* @retval VERR_SYMBOL_NOT_FOUND if no matching symbol was found for the address.
*
* @param pCpu Pointer to the disassembler CPU state.
* @param u32Sel The selector value. Use DIS_FMT_SEL_IS_REG, DIS_FMT_SEL_GET_VALUE,
* DIS_FMT_SEL_GET_REG to access this.
* @param uAddress The segment address.
* @param pszBuf Where to store the symbol name
* @param cchBuf The size of the buffer.
* @param poff If not a perfect match, then this is where the offset from the return
* symbol to the specified address is returned.
* @param pvUser The user argument.
*/
typedef DECLCALLBACK(int) FNDISGETSYMBOL(PCDISCPUSTATE pCpu, uint32_t u32Sel, RTUINTPTR uAddress, char *pszBuf, size_t cchBuf, RTINTPTR *poff, void *pvUser);
/** Pointer to a FNDISGETSYMBOL(). */
typedef FNDISGETSYMBOL *PFNDISGETSYMBOL;
/**
* Checks if the FNDISGETSYMBOL argument u32Sel is a register or not.
*/
/**
* Extracts the selector value from the FNDISGETSYMBOL argument u32Sel.
* @returns Selector value.
*/
/**
* Extracts the register number from the FNDISGETSYMBOL argument u32Sel.
* @returns USE_REG_CS, USE_REG_SS, USE_REG_DS, USE_REG_ES, USE_REG_FS or USE_REG_FS.
*/
/** @internal */
/** @internal */
/** @name Flags for use with DISFormatYasmEx(), DISFormatMasmEx() and DISFormatGasEx().
* @{
*/
/** Put the address to the right. */
#define DIS_FMT_FLAGS_ADDR_RIGHT RT_BIT_32(0)
/** Put the address to the left. */
/** Put the address in comments.
* For some assemblers this implies placing it to the right. */
/** Put the instruction bytes to the right of the disassembly. */
/** Put the instruction bytes to the left of the disassembly. */
/** Put the instruction bytes in comments.
* For some assemblers this implies placing the bytes to the right. */
/** Put the bytes in square brackets. */
/** Put spaces between the bytes. */
/** Display the relative +/- offset of branch instructions that uses relative addresses,
* and put the target address in parenthesis. */
/** Strict assembly. The assembly should, when ever possible, make the
* assembler reproduce the exact same binary. (Refers to the yasm
* strict keyword.) */
/** Checks if the given flags are a valid combination. */
#define DIS_FMT_FLAGS_IS_VALID(fFlags) \
&& ((fFlags) & (DIS_FMT_FLAGS_ADDR_RIGHT | DIS_FMT_FLAGS_ADDR_LEFT)) != (DIS_FMT_FLAGS_ADDR_RIGHT | DIS_FMT_FLAGS_ADDR_LEFT) \
&& ( !((fFlags) & DIS_FMT_FLAGS_ADDR_COMMENT) \
&& ((fFlags) & (DIS_FMT_FLAGS_BYTES_RIGHT | DIS_FMT_FLAGS_BYTES_LEFT)) != (DIS_FMT_FLAGS_BYTES_RIGHT | DIS_FMT_FLAGS_BYTES_LEFT) \
)
/** @} */
DISDECL(size_t) DISFormatYasmEx(PCDISCPUSTATE pCpu, char *pszBuf, size_t cchBuf, uint32_t fFlags, PFNDISGETSYMBOL pfnGetSymbol, void *pvUser);
DISDECL(size_t) DISFormatMasmEx(PCDISCPUSTATE pCpu, char *pszBuf, size_t cchBuf, uint32_t fFlags, PFNDISGETSYMBOL pfnGetSymbol, void *pvUser);
DISDECL(size_t) DISFormatGasEx( PCDISCPUSTATE pCpu, char *pszBuf, size_t cchBuf, uint32_t fFlags, PFNDISGETSYMBOL pfnGetSymbol, void *pvUser);
/** @todo DISAnnotate(PCDISCPUSTATE pCpu, char *pszBuf, size_t cchBuf, register reader, memory reader); */
#endif