PATMPatch.cpp revision fb7ab6d77b3abfd80c3e4224ba5f11b41450c131
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * PATMPatch - Dynamic Guest OS Instruction patches
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * NOTE: CSAM assumes patch memory is never reused!!
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * Copyright (C) 2006-2007 Sun Microsystems, Inc.
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * available from http://www.virtualbox.org. This file is free software;
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * you can redistribute it and/or modify it under the terms of the GNU
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * General Public License (GPL) as published by the Free Software
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * Clara, CA 95054 USA or visit http://www.sun.com if you need
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * additional information or have any questions.
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync/*******************************************************************************
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync* Header Files *
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync*******************************************************************************/
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync/* internal structure for passing more information about call fixups to patmPatchGenCode */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsynctypedef struct
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsyncint patmPatchAddReloc32(PVM pVM, PPATCHINFO pPatch, uint8_t *pRelocHC, uint32_t uType, RTGCPTR pSource, RTGCPTR pDest)
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync Assert(uType == FIXUP_ABSOLUTE || ((uType == FIXUP_REL_JMPTOPATCH || uType == FIXUP_REL_JMPTOGUEST) && pSource && pDest));
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync LogFlow(("patmPatchAddReloc32 type=%d pRelocGC=%VGv source=%VGv dest=%VGv\n", uType, pRelocHC - pVM->patm.s.pPatchMemGC + pVM->patm.s.pPatchMemGC , pSource, pDest));
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync pRec = (PRELOCREC)MMR3HeapAllocZ(pVM, MM_TAG_PATM_PATCH, sizeof(*pRec));
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync bool ret = RTAvlPVInsert(&pPatch->FixupTree, &pRec->Core);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsyncint patmPatchAddJump(PVM pVM, PPATCHINFO pPatch, uint8_t *pJumpHC, uint32_t offset, RTGCPTR pTargetGC, uint32_t opcode)
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync pRec = (PJUMPREC)MMR3HeapAllocZ(pVM, MM_TAG_PATM_PATCH, sizeof(*pRec));
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync bool ret = RTAvlPVInsert(&pPatch->JumpTree, &pRec->Core);
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync pPB = PATCHCODE_PTR_HC(pPatch) + pPatch->uCurPatchOffset; \
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync if (pPB + 256 >= pVM->patm.s.pPatchMemHC + pVM->patm.s.cbPatchMem) \
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync Assert(pPB + 256 >= pVM->patm.s.pPatchMemHC + pVM->patm.s.cbPatchMem); \
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsyncstatic uint32_t patmPatchGenCode(PVM pVM, PPATCHINFO pPatch, uint8_t *pPB, PPATCHASMRECORD pAsmRecord, GCPTRTYPE(uint8_t *) pReturnAddrGC, bool fGenJump,
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync Assert(pAsmRecord && pAsmRecord->size > sizeof(pAsmRecord->uReloc[0]));
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync // Copy the code block
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync memcpy(pPB, pAsmRecord->pFunction, pAsmRecord->size);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync // Process all fixups
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync dest = pVM->patm.s.pGCStateGC + RT_OFFSETOF(PATMGCSTATE, uVMFlags);
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync dest = pVM->patm.s.pGCStateGC + RT_OFFSETOF(PATMGCSTATE, uPendingAction);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync /* Offset in uReloc[i+1] is from the base of the function. */
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync dest = (RTGCUINTPTR)pVM->patm.s.pPatchMemGC + pAsmRecord->uReloc[i+1] + (RTGCUINTPTR)(pPB - pVM->patm.s.pPatchMemHC);
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync dest = pVM->patm.s.pGCStateGC + RT_OFFSETOF(PATMGCSTATE, uPatchCalls);
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync dest = pVM->patm.s.pGCStateGC + RT_OFFSETOF(PATMGCSTATE, uIretEFlags);
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync dest = pVM->patm.s.pGCStateGC + RT_OFFSETOF(PATMGCSTATE, uIretCS);
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync dest = pVM->patm.s.pGCStateGC + RT_OFFSETOF(PATMGCSTATE, uIretEIP);
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync dest = pVM->patm.s.pGCStateGC + RT_OFFSETOF(PATMGCSTATE, Psp);
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /* The first part of our PATM stack is used to store offsets of patch return addresses; the 2nd
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * part to store the original return addresses.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync case PATM_RETURNADDR: /* absolute guest address; no fixup required */
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync Assert(pCallInfo && pAsmRecord->uReloc[i] >= PATM_NO_FIXUP);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync case PATM_PATCHNEXTBLOCK: /* relative address of instruction following this block */
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync Assert(pCallInfo && pAsmRecord->uReloc[i] >= PATM_NO_FIXUP);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync /** @note hardcoded assumption that we must return to the instruction following this block */
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync dest = (uintptr_t)pPB - (uintptr_t)pVM->patm.s.pPatchMemHC + pAsmRecord->size;
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync case PATM_CALLTARGET: /* relative to patch address; no fixup requird */
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync Assert(pCallInfo && pAsmRecord->uReloc[i] >= PATM_NO_FIXUP);
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /* Address must be filled in later. (see patmr3SetBranchTargets) */
case PATM_CPUID_STD_PTR:
case PATM_CPUID_EXT_PTR:
case PATM_CPUID_CENTAUR_PTR:
case PATM_CPUID_DEF_PTR:
case PATM_CPUID_STD_MAX:
case PATM_CPUID_EXT_MAX:
case PATM_CPUID_CENTAUR_MAX:
case PATM_INTERRUPTFLAG:
case PATM_INHIBITIRQADDR:
case PATM_NEXTINSTRADDR:
case PATM_CURINSTRADDR:
case PATM_VM_FORCEDACTIONS:
case PATM_TEMP_EAX:
case PATM_TEMP_ECX:
case PATM_TEMP_EDI:
case PATM_TEMP_EFLAGS:
case PATM_TEMP_RESTORE_FLAGS:
case PATM_CALL_RETURN_ADDR:
RTGCPTR pInstrAfterCall = pVM->patm.s.pPatchMemGC + (RTGCUINTPTR)(&pPB[j] + sizeof(RTGCPTR) - pVM->patm.s.pPatchMemHC);
case PATM_RETURN_FUNCTION:
RTGCPTR pInstrAfterCall = pVM->patm.s.pPatchMemGC + (RTGCUINTPTR)(&pPB[j] + sizeof(RTGCPTR) - pVM->patm.s.pPatchMemHC);
case PATM_IRET_FUNCTION:
RTGCPTR pInstrAfterCall = pVM->patm.s.pPatchMemGC + (RTGCUINTPTR)(&pPB[j] + sizeof(RTGCPTR) - pVM->patm.s.pPatchMemHC);
RTGCPTR pInstrAfterCall = pVM->patm.s.pPatchMemGC + (RTGCUINTPTR)(&pPB[j] + sizeof(RTGCPTR) - pVM->patm.s.pPatchMemHC);
AssertRelease(0);
if (fGenJump)
int32_t displ = pReturnAddrGC - (PATCHCODE_PTR_GC(pPatch) + pPatch->uCurPatchOffset + pAsmRecord->offJump - 1 + SIZEOF_NEARJUMP32);
patmr3AddP2GLookupRecord(pVM, pPatch, &pPB[pAsmRecord->offJump - 1], pReturnAddrGC, PATM_LOOKUP_PATCH2GUEST);
return VINF_SUCCESS;
int patmPatchGenDuplicate(PVM pVM, PPATCHINFO pPatch, DISCPUSTATE *pCpu, GCPTRTYPE(uint8_t *) pCurInstrGC)
return rc;
return VINF_SUCCESS;
return VINF_SUCCESS;
return VINF_SUCCESS;
int patmPatchGenPopf(PVM pVM, PPATCHINFO pPatch, GCPTRTYPE(uint8_t *) pReturnAddrGC, bool fSizeOverride, bool fGenJumpBack)
/* Note: keep IOPL in mind when changing any of this!! (see comments in PATMA.asm, PATMPopf32Replacement) */
if (fSizeOverride == true)
size = patmPatchGenCode(pVM, pPatch, pPB, (fGenJumpBack) ? &PATMPopf16Record : &PATMPopf16Record_NoExit , pReturnAddrGC, fGenJumpBack, &callInfo);
size = patmPatchGenCode(pVM, pPatch, pPB, (fGenJumpBack) ? &PATMPopf32Record : &PATMPopf32Record_NoExit, pReturnAddrGC, fGenJumpBack, &callInfo);
return VINF_SUCCESS;
if (fSizeOverride == true)
return VINF_SUCCESS;
return VINF_SUCCESS;
int patmPatchGenLoop(PVM pVM, PPATCHINFO pPatch, GCPTRTYPE(uint8_t *) pTargetGC, uint32_t opcode, bool fSizeOverride)
switch (opcode)
case OP_LOOP:
case OP_LOOPNE:
case OP_LOOPE:
case OP_JECXZ:
return VERR_INVALID_PARAMETER;
Log(("PatchGenLoop %d jump %d to %08x offrel=%d\n", opcode, pPatch->nrJumpRecs, pTargetGC, pPatchAsmRec->offRelJump));
if (fSizeOverride)
return VINF_SUCCESS;
int patmPatchGenRelJump(PVM pVM, PPATCHINFO pPatch, GCPTRTYPE(uint8_t *) pTargetGC, uint32_t opcode, bool fSizeOverride)
switch (opcode)
case OP_JO:
case OP_JNO:
case OP_JC:
case OP_JNC:
case OP_JE:
case OP_JNE:
case OP_JBE:
case OP_JNBE:
case OP_JS:
case OP_JNS:
case OP_JP:
case OP_JNP:
case OP_JL:
case OP_JNL:
case OP_JLE:
case OP_JNLE:
case OP_JMP:
/* If interrupted here, then jump to the target instruction. Used by PATM.cpp for jumping to known instructions. */
case OP_JECXZ:
case OP_LOOP:
case OP_LOOPNE:
case OP_LOOPE:
return VERR_PATCHING_REFUSED;
else offset++;
return VINF_SUCCESS;
int patmPatchGenCall(PVM pVM, PPATCHINFO pPatch, DISCPUSTATE *pCpu, RTGCPTR pCurInstrGC, RTGCPTR pTargetGC, bool fIndirect)
int rc;
/** @note It's dangerous to do this for 'normal' patches. the jump target might be inside the generated patch jump. (seen this!) */
return rc;
if (fIndirect)
offset = 0;
rc = patmPatchReadBytes(pVM, &pPB[offset], (RTGCPTR)((RTGCUINTPTR)pCurInstrGC + i), pCpu->opsize - i);
AssertMsg(PATMIsPatchGCAddr(pVM, pTargetGC) == false, ("Target is already a patch address (%VGv)?!?\n", pTargetGC));
Log(("PatchGenCall from %VGv (next=%VGv) to %VGv\n", pCurInstrGC, pCurInstrGC + pCpu->opsize, pTargetGC));
offset = 0;
if (size)
for (i=0;i<size;i++)
/* 3: Generate code to lookup address in our local cache; call hypervisor PATM code if it can't be located. */
size = patmPatchGenCode(pVM, pPatch, pPB, (fIndirect) ? &PATMCallIndirectRecord : &PATMCallRecord, 0, false, &callInfo);
return rc;
return VINF_SUCCESS;
int rc;
return rc;
offset = 0;
rc = patmPatchReadBytes(pVM, &pPB[offset], (RTGCPTR)((RTGCUINTPTR)pCurInstrGC + i), pCpu->opsize - i);
if (size)
for (i=0;i<size;i++)
/* 3: Generate code to lookup address in our local cache; call hypervisor PATM code if it can't be located. */
return VINF_SUCCESS;
int patmPatchGenRet(PVM pVM, PPATCHINFO pPatch, DISCPUSTATE *pCpu, GCPTRTYPE(uint8_t *) pCurInstrGC)
/** @note optimization: multiple identical ret instruction in a single patch can share a single patched ret. */
&& pPatch->pTempInfo->uPatchRetParam1 == (uint32_t)pCpu->param1.parval) /* nr of bytes popped off the stack should be identical of course! */
if (size)
for (int i=0;i<size;i++)
return rc;
int size = 0;
return VINF_SUCCESS;
return VINF_SUCCESS;
return VINF_SUCCESS;
return VINF_SUCCESS;
return VINF_SUCCESS;
int size;
Assert((pPatch->flags & (PATMFL_GENERATE_JUMPTOGUEST|PATMFL_DUPLICATE_FUNCTION)) != (PATMFL_GENERATE_JUMPTOGUEST|PATMFL_DUPLICATE_FUNCTION));
return VINF_SUCCESS;
(pPatch->flags & PATMFL_INTHANDLER_WITH_ERRORCODE) ? &PATMIntEntryRecordErrorCode : &PATMIntEntryRecord,
return rc;
(pPatch->flags & PATMFL_TRAPHANDLER_WITH_ERRORCODE) ? &PATMTrapEntryRecordErrorCode : &PATMTrapEntryRecord,
pTrapHandlerGC, true);
return VINF_SUCCESS;
#ifdef VBOX_WITH_STATISTICS
return VINF_SUCCESS;
switch (dbgreg)
case USE_REG_DR0:
case USE_REG_DR1:
case USE_REG_DR2:
case USE_REG_DR3:
case USE_REG_DR4:
case USE_REG_DR5:
case USE_REG_DR6:
case USE_REG_DR7:
AssertFailed();
offset = 0;
return rc;
switch (ctrlreg)
case USE_REG_CR0:
case USE_REG_CR2:
case USE_REG_CR3:
case USE_REG_CR4:
AssertFailed();
offset = 0;
return rc;
offset = 0;
offset = 0;
return VINF_SUCCESS;
uint32_t i;
// 8B 15 [32 bits addr] mov edx, CPUMCTX.tr/ldtr
//66 A1 48 7C 42 00 mov ax, CPUMCTX.tr/ldtr
rc = patmPatchReadBytes(pVM, &pPB[offset], (RTGCPTR)((RTGCUINTPTR)pCurInstrGC + i), pCpu->opsize - i);
return rc;
uint32_t i;
case OP_SGDT:
case OP_SIDT:
return VERR_INVALID_PARAMETER;
rc = patmPatchReadBytes(pVM, &pPB[offset], (RTGCPTR)((RTGCUINTPTR)pCurInstrGC + i), pCpu->opsize - i);
return rc;
return VINF_SUCCESS;
int patmPatchGenJumpToGuest(PVM pVM, PPATCHINFO pPatch, GCPTRTYPE(uint8_t *) pReturnAddrGC, bool fClearInhibitIRQs)
if (fClearInhibitIRQs)
return rc;
return rc;
int patmPatchGenPatchJump(PVM pVM, PPATCHINFO pPatch, RTGCPTR pCurInstrGC, GCPTRTYPE(uint8_t *) pPatchAddrGC, bool fAddLookupRecord)
if (fAddLookupRecord)
return rc;