Index: /trunk/src/VBox/VMM/VMMR3/PATM.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR3/PATM.cpp	(revision 36668)
+++ /trunk/src/VBox/VMM/VMMR3/PATM.cpp	(revision 36669)
@@ -55,4 +55,18 @@
 //#define PATM_DISABLE_ALL
 
+/**
+ * Refresh trampoline patch state.
+ */
+typedef struct PATMREFRESHPATCH
+{
+    /** Pointer to the VM structure. */
+    PVM        pVM;
+    /** The trampoline patch record. */
+    PPATCHINFO pPatchTrampoline;
+    /** The new patch we want to jump to. */
+    PPATCHINFO pPatchRec;
+} PATMREFRESHPATCH, *PPATMREFRESHPATCH;
+
+
 /*******************************************************************************
 *   Internal Functions                                                         *
@@ -1152,4 +1166,7 @@
     uint32_t PatchOffset = pPatchInstrHC - pVM->patm.s.pPatchMemHC;  /* Offset in memory reserved for PATM. */
 
+    LogFlowFunc(("pVM=%#p pPatch=%#p pPatchInstrHC=%#p pInstrGC=%#x enmType=%d fDirty=%RTbool\n",
+                 pVM, pPatch, pPatchInstrHC, pInstrGC, enmType, fDirty));
+
     if (enmType == PATM_LOOKUP_PATCH2GUEST)
     {
@@ -3258,4 +3275,7 @@
     uint32_t    orgOffsetPatchMem = ~0;
     int         rc = VERR_PATCHING_REFUSED;
+    PPATCHINFO  pPatchToJmp = NULL; /**< Patch the trampoline jumps to. */
+    PTRAMPREC   pTrampRec = NULL; /**< Trampoline record used to find the patch. */
+    bool        fInserted = false;
 
     Log(("patmCreateTrampoline %RRv\n", pInstrGC));
@@ -3276,20 +3296,19 @@
             if (pPatchPage->aPatch[i])
             {
-                PPATCHINFO pPatch2 = pPatchPage->aPatch[i];
-
-                if (    (pPatch2->flags & PATMFL_DUPLICATE_FUNCTION)
-                    &&  pPatch2->uState == PATCH_ENABLED)
+                pPatchToJmp = pPatchPage->aPatch[i];
+
+                if (    (pPatchToJmp->flags & PATMFL_DUPLICATE_FUNCTION)
+                    &&  pPatchToJmp->uState == PATCH_ENABLED)
                 {
-                    pPatchTargetGC = patmGuestGCPtrToPatchGCPtr(pVM, pPatch2, pInstrGC);
+                    pPatchTargetGC = patmGuestGCPtrToPatchGCPtr(pVM, pPatchToJmp, pInstrGC);
                     if (pPatchTargetGC)
                     {
                         uint32_t         offsetPatch      = pPatchTargetGC - pVM->patm.s.pPatchMemGC;
-                        PRECPATCHTOGUEST pPatchToGuestRec = (PRECPATCHTOGUEST)RTAvlU32GetBestFit(&pPatch2->Patch2GuestAddrTree, offsetPatch, false);
+                        PRECPATCHTOGUEST pPatchToGuestRec = (PRECPATCHTOGUEST)RTAvlU32GetBestFit(&pPatchToJmp->Patch2GuestAddrTree, offsetPatch, false);
                         Assert(pPatchToGuestRec);
 
                         pPatchToGuestRec->fJumpTarget = true;
-                        Assert(pPatchTargetGC != pPatch2->pPrivInstrGC);
-                        Log(("patmCreateTrampoline: generating jump to code inside patch at %RRv\n", pPatch2->pPrivInstrGC));
-                        pPatch2->flags |= PATMFL_EXTERNAL_JUMP_INSIDE;
+                        Assert(pPatchTargetGC != pPatchToJmp->pPrivInstrGC);
+                        Log(("patmCreateTrampoline: generating jump to code inside patch at %RRv (patch target %RRv)\n", pPatchToJmp->pPrivInstrGC, pPatchTargetGC));
                         break;
                     }
@@ -3298,5 +3317,24 @@
         }
     }
-    AssertReturn(pPatchPage && pPatchTargetGC, VERR_PATCHING_REFUSED);
+    AssertReturn(pPatchPage && pPatchTargetGC && pPatchToJmp, VERR_PATCHING_REFUSED);
+
+    /*
+     * Only record the trampoline patch if this is the first patch to the target
+     * or we recorded other patches already.
+     * The goal is to refuse refreshing function duplicates if the guest
+     * modifies code after a saved state was loaded because it is not possible
+     * to save the relation between trampoline and target without changing the
+     * saved satte version.
+     */
+    if (   !(pPatchToJmp->flags & PATMFL_EXTERNAL_JUMP_INSIDE)
+        || pPatchToJmp->pTrampolinePatchesHead)
+    {
+        pPatchToJmp->flags |= PATMFL_EXTERNAL_JUMP_INSIDE;
+        pTrampRec = (PTRAMPREC)MMR3HeapAllocZ(pVM, MM_TAG_PATM_PATCH, sizeof(*pTrampRec));
+        if (!pTrampRec)
+            return VERR_NO_MEMORY; /* or better return VERR_PATCHING_REFUSED to let the VM continue? */
+
+        pTrampRec->pPatchTrampoline = pPatchRec;
+    }
 
     pPatch->nrPatch2GuestRecs = 0;
@@ -3324,7 +3362,7 @@
     LogFlow(("Insert %RRv patch offset %RRv\n", pPatchRec->patch.pPrivInstrGC, pPatch->pPatchBlockOffset));
     pPatchRec->CoreOffset.Key = pPatch->pPatchBlockOffset;
-    rc = RTAvloU32Insert(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, &pPatchRec->CoreOffset);
-    AssertMsg(rc, ("RTAvloU32Insert failed for %x\n", pPatchRec->CoreOffset.Key));
-    if (!rc)
+    fInserted = RTAvloU32Insert(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, &pPatchRec->CoreOffset);
+    AssertMsg(fInserted, ("RTAvloU32Insert failed for %x\n", pPatchRec->CoreOffset.Key));
+    if (!fInserted)
     {
         rc = VERR_PATCHING_REFUSED;
@@ -3347,4 +3385,10 @@
     /* We allow this patch to be called as a function. */
     pPatch->flags |= PATMFL_CALLABLE_AS_FUNCTION;
+
+    if (pTrampRec)
+    {
+        pTrampRec->pNext = pPatchToJmp->pTrampolinePatchesHead;
+        pPatchToJmp->pTrampolinePatchesHead = pTrampRec;
+    }
     STAM_COUNTER_INC(&pVM->patm.s.StatInstalledTrampoline);
     return VINF_SUCCESS;
@@ -3370,4 +3414,7 @@
     Assert(orgOffsetPatchMem != (uint32_t)~0);
     pVM->patm.s.offPatchMem = orgOffsetPatchMem;
+
+    if (pTrampRec)
+        MMR3HeapFree(pTrampRec);
 
     return rc;
@@ -4162,4 +4209,5 @@
     pPatchRec->patch.flags   = flags;
     pPatchRec->patch.uOpMode = (flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
+    pPatchRec->patch.pTrampolinePatchesHead = NULL;
 
     pPatchRec->patch.pInstrGCLowest  = pInstrGC;
@@ -5425,4 +5473,44 @@
 
 /**
+ * RTAvlU32DoWithAll() worker.
+ * Checks whether the current trampoline instruction is the jump to the target patch
+ * and updates the displacement to jump to the new target.
+ *
+ * @returns VBox status code.
+ * @retval  VERR_ALREADY_EXISTS if the jump was found.
+ * @param   pNode    The current patch to guest record to check.
+ * @param   pvUser   The refresh state.
+ */
+static int patmR3PatchRefreshFindTrampolinePatch(PAVLU32NODECORE pNode, void *pvUser)
+{
+    PRECPATCHTOGUEST  pPatch2GuestRec = (PRECPATCHTOGUEST)pNode;
+    PPATMREFRESHPATCH pRefreshPatchState = (PPATMREFRESHPATCH)pvUser;
+    PVM               pVM = pRefreshPatchState->pVM;
+
+    uint8_t *pPatchInstr = (uint8_t *)(pVM->patm.s.pPatchMemHC + pPatch2GuestRec->Core.Key);
+
+    /*
+     * Check if the patch instruction starts with a jump.
+     * ASSUMES that there is no other patch to guest record that starts
+     * with a jump.
+     */
+    if (*pPatchInstr == 0xE9)
+    {
+        /* Jump found, update the displacement. */
+        RTRCPTR pPatchTargetGC = patmGuestGCPtrToPatchGCPtr(pVM, pRefreshPatchState->pPatchRec,
+                                                            pRefreshPatchState->pPatchTrampoline->pPrivInstrGC);
+        int32_t displ =  pPatchTargetGC - (pVM->patm.s.pPatchMemGC + pPatch2GuestRec->Core.Key + SIZEOF_NEARJUMP32);
+
+        LogFlow(("Updating trampoline patch new patch target %RRv, new displacment %d (old was %d)\n",
+                 pPatchTargetGC, displ, *(uint32_t *)&pPatchInstr[1]));
+
+        *(uint32_t *)&pPatchInstr[1] = displ;
+        return VERR_ALREADY_EXISTS; /** @todo better return code */
+    }
+
+    return VINF_SUCCESS;
+}
+
+/**
  * Attempt to refresh the patch by recompiling its entire code block
  *
@@ -5436,4 +5524,5 @@
     int         rc;
     RTRCPTR     pInstrGC = pPatchRec->patch.pPrivInstrGC;
+    PTRAMPREC   pTrampolinePatchesHead = NULL;
 
     Log(("patmR3RefreshPatch: attempt to refresh patch at %RRv\n", pInstrGC));
@@ -5443,6 +5532,20 @@
     if (pPatch->flags & PATMFL_EXTERNAL_JUMP_INSIDE)
     {
-        Log(("patmR3RefreshPatch: refused because external jumps to this patch exist\n"));
-        return VERR_PATCHING_REFUSED;
+        if (!pPatch->pTrampolinePatchesHead)
+        {
+            /*
+             * It is sometimes possible that there are trampoline patches to this patch
+             * but they are not recorded (after a saved state load for example).
+             * Refuse to refresh those patches.
+             * Can hurt performance in theory if the patched code is modified by the guest
+             * and is executed often. However most of the time states are saved after the guest
+             * code was modified and is not updated anymore afterwards so this shouldn't be a
+             * big problem.
+             */
+            Log(("patmR3RefreshPatch: refused because external jumps to this patch exist but the jumps are not recorded\n"));
+            return VERR_PATCHING_REFUSED;
+        }
+        Log(("patmR3RefreshPatch: external jumps to this patch exist, updating\n"));
+        pTrampolinePatchesHead = pPatch->pTrampolinePatchesHead;
     }
 
@@ -5518,4 +5621,36 @@
         /* Used by another patch, so don't remove it! */
         pNewPatchRec->patch.flags |= PATMFL_CODE_REFERENCED;
+
+        if (pTrampolinePatchesHead)
+        {
+            /* Update all trampoline patches to jump to the new patch. */
+            PTRAMPREC pTrampRec = NULL;
+            PATMREFRESHPATCH RefreshPatch;
+
+            RefreshPatch.pVM = pVM;
+            RefreshPatch.pPatchRec = &pNewPatchRec->patch;
+
+            pTrampRec = pTrampolinePatchesHead;
+
+            while (pTrampRec)
+            {
+                PPATCHINFO pPatchTrampoline = &pTrampRec->pPatchTrampoline->patch;
+
+                RefreshPatch.pPatchTrampoline = pPatchTrampoline;
+                /*
+                 * We have to find the right patch2guest record because there might be others
+                 * for statistics.
+                 */
+                rc = RTAvlU32DoWithAll(&pPatchTrampoline->Patch2GuestAddrTree, true,
+                                       patmR3PatchRefreshFindTrampolinePatch, &RefreshPatch);
+                Assert(rc == VERR_ALREADY_EXISTS);
+                rc = VINF_SUCCESS;
+                pTrampRec = pTrampRec->pNext;
+            }
+            pNewPatchRec->patch.pTrampolinePatchesHead = pTrampolinePatchesHead;
+            pNewPatchRec->patch.flags                 |= PATMFL_EXTERNAL_JUMP_INSIDE;
+            /* Clear the list of trampoline patches for the old patch (safety precaution). */
+            pPatchRec->patch.pTrampolinePatchesHead = NULL;
+        }
     }
 
Index: /trunk/src/VBox/VMM/VMMR3/PATMSSM.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR3/PATMSSM.cpp	(revision 36668)
+++ /trunk/src/VBox/VMM/VMMR3/PATMSSM.cpp	(revision 36669)
@@ -42,4 +42,97 @@
 #include <VBox/dis.h>
 #include <VBox/disopcode.h>
+
+/**
+ * Patch information - SSM version.
+ *
+ * the difference is the missing pTrampolinePatchesHead member
+ * to avoid changing the saved state version for now (will come later).
+ */
+typedef struct _PATCHINFOSSM
+{
+    uint32_t              uState;
+    uint32_t              uOldState;
+    DISCPUMODE            uOpMode;
+
+    /* GC pointer of privileged instruction */
+    RCPTRTYPE(uint8_t *)  pPrivInstrGC;
+    R3PTRTYPE(uint8_t *)  unusedHC;                             /* todo Can't remove due to structure size dependencies in saved states. */
+    uint8_t               aPrivInstr[MAX_INSTR_SIZE];
+    uint32_t              cbPrivInstr;
+    uint32_t              opcode;      //opcode for priv instr (OP_*)
+    uint32_t              cbPatchJump; //patch jump size
+
+    /* Only valid for PATMFL_JUMP_CONFLICT patches */
+    RTRCPTR               pPatchJumpDestGC;
+
+    RTGCUINTPTR32         pPatchBlockOffset;
+    uint32_t              cbPatchBlockSize;
+    uint32_t              uCurPatchOffset;
+#if HC_ARCH_BITS == 64
+    uint32_t              Alignment0;         /**< Align flags correctly. */
+#endif
+
+    uint64_t              flags;
+
+    /**
+     * Lowest and highest patched GC instruction address. To optimize searches.
+     */
+    RTRCPTR               pInstrGCLowest;
+    RTRCPTR               pInstrGCHighest;
+
+    /* Tree of fixup records for the patch. */
+    R3PTRTYPE(PAVLPVNODECORE) FixupTree;
+    uint32_t                  nrFixups;
+
+    /* Tree of jumps inside the generated patch code. */
+    uint32_t                  nrJumpRecs;
+    R3PTRTYPE(PAVLPVNODECORE) JumpTree;
+
+    /**
+     * Lookup trees for determining the corresponding guest address of an
+     * instruction in the patch block.
+     */
+    R3PTRTYPE(PAVLU32NODECORE) Patch2GuestAddrTree;
+    R3PTRTYPE(PAVLU32NODECORE) Guest2PatchAddrTree;
+    uint32_t                  nrPatch2GuestRecs;
+#if HC_ARCH_BITS == 64
+    uint32_t        Alignment1;
+#endif
+
+    /* Unused, but can't remove due to structure size dependencies in the saved state. */
+    PATMP2GLOOKUPREC_OBSOLETE    unused;
+
+    /* Temporary information during patch creation. Don't waste hypervisor memory for this. */
+    R3PTRTYPE(PPATCHINFOTEMP) pTempInfo;
+
+    /* Count the number of writes to the corresponding guest code. */
+    uint32_t        cCodeWrites;
+
+    /* Count the number of invalid writes to pages monitored for the patch. */
+    //some statistics to determine if we should keep this patch activated
+    uint32_t        cTraps;
+
+    uint32_t        cInvalidWrites;
+
+    // Index into the uPatchRun and uPatchTrap arrays (0..MAX_PATCHES-1)
+    uint32_t        uPatchIdx;
+
+    /* First opcode byte, that's overwritten when a patch is marked dirty. */
+    uint8_t         bDirtyOpcode;
+    uint8_t         Alignment2[7];      /**< Align the structure size on a 8-byte boundary. */
+} PATCHINFOSSM, *PPATCHINFOSSM;
+
+/**
+ * Lookup record for patches - SSM version.
+ */
+typedef struct PATMPATCHRECSSM
+{
+    /** The key is a GC virtual address. */
+    AVLOU32NODECORE  Core;
+    /** The key is a patch offset. */
+    AVLOU32NODECORE  CoreOffset;
+
+    PATCHINFOSSM     patch;
+} PATMPATCHRECSSM, *PPATMPATCHRECSSM;
 
 /*******************************************************************************
@@ -184,50 +277,50 @@
 static SSMFIELD const g_aPatmPatchRecFields[] =
 {
-    SSMFIELD_ENTRY(                 PATMPATCHREC, Core.Key),
-    SSMFIELD_ENTRY_IGNORE(          PATMPATCHREC, Core.pLeft),
-    SSMFIELD_ENTRY_IGNORE(          PATMPATCHREC, Core.pRight),
-    SSMFIELD_ENTRY_IGNORE(          PATMPATCHREC, Core.uchHeight),
+    SSMFIELD_ENTRY(                 PATMPATCHRECSSM, Core.Key),
+    SSMFIELD_ENTRY_IGNORE(          PATMPATCHRECSSM, Core.pLeft),
+    SSMFIELD_ENTRY_IGNORE(          PATMPATCHRECSSM, Core.pRight),
+    SSMFIELD_ENTRY_IGNORE(          PATMPATCHRECSSM, Core.uchHeight),
     SSMFIELD_ENTRY_PAD_HC_AUTO(     3, 3),
-    SSMFIELD_ENTRY(                 PATMPATCHREC, CoreOffset.Key),
-    SSMFIELD_ENTRY_IGNORE(          PATMPATCHREC, CoreOffset.pLeft),
-    SSMFIELD_ENTRY_IGNORE(          PATMPATCHREC, CoreOffset.pRight),
-    SSMFIELD_ENTRY_IGNORE(          PATMPATCHREC, CoreOffset.uchHeight),
+    SSMFIELD_ENTRY(                 PATMPATCHRECSSM, CoreOffset.Key),
+    SSMFIELD_ENTRY_IGNORE(          PATMPATCHRECSSM, CoreOffset.pLeft),
+    SSMFIELD_ENTRY_IGNORE(          PATMPATCHRECSSM, CoreOffset.pRight),
+    SSMFIELD_ENTRY_IGNORE(          PATMPATCHRECSSM, CoreOffset.uchHeight),
     SSMFIELD_ENTRY_PAD_HC_AUTO(     3, 3),
-    SSMFIELD_ENTRY(                 PATMPATCHREC, patch.uState),
-    SSMFIELD_ENTRY(                 PATMPATCHREC, patch.uOldState),
-    SSMFIELD_ENTRY(                 PATMPATCHREC, patch.uOpMode),
-    SSMFIELD_ENTRY_RCPTR(           PATMPATCHREC, patch.pPrivInstrGC),
-    SSMFIELD_ENTRY_IGN_HCPTR(       PATMPATCHREC, patch.unusedHC),
-    SSMFIELD_ENTRY(                 PATMPATCHREC, patch.aPrivInstr),
-    SSMFIELD_ENTRY(                 PATMPATCHREC, patch.cbPrivInstr),
-    SSMFIELD_ENTRY(                 PATMPATCHREC, patch.opcode),
-    SSMFIELD_ENTRY(                 PATMPATCHREC, patch.cbPatchJump),
-    SSMFIELD_ENTRY_RCPTR(           PATMPATCHREC, patch.pPatchJumpDestGC),
-    SSMFIELD_ENTRY(                 PATMPATCHREC, patch.pPatchBlockOffset),
-    SSMFIELD_ENTRY(                 PATMPATCHREC, patch.cbPatchBlockSize),
-    SSMFIELD_ENTRY(                 PATMPATCHREC, patch.uCurPatchOffset),
-    SSMFIELD_ENTRY_PAD_HC64(        PATMPATCHREC, patch.Alignment0, sizeof(uint32_t)),
-    SSMFIELD_ENTRY(                 PATMPATCHREC, patch.flags),
-    SSMFIELD_ENTRY_RCPTR(           PATMPATCHREC, patch.pInstrGCLowest),
-    SSMFIELD_ENTRY_RCPTR(           PATMPATCHREC, patch.pInstrGCHighest),
-    SSMFIELD_ENTRY_IGN_HCPTR(       PATMPATCHREC, patch.FixupTree),
-    SSMFIELD_ENTRY(                 PATMPATCHREC, patch.nrFixups),
-    SSMFIELD_ENTRY(                 PATMPATCHREC, patch.nrJumpRecs), // should be zero?
-    SSMFIELD_ENTRY_IGN_HCPTR(       PATMPATCHREC, patch.JumpTree),
-    SSMFIELD_ENTRY_IGN_HCPTR(       PATMPATCHREC, patch.Patch2GuestAddrTree),
-    SSMFIELD_ENTRY_IGN_HCPTR(       PATMPATCHREC, patch.Guest2PatchAddrTree),
-    SSMFIELD_ENTRY(                 PATMPATCHREC, patch.nrPatch2GuestRecs),
-    SSMFIELD_ENTRY_PAD_HC64(        PATMPATCHREC, patch.Alignment1, sizeof(uint32_t)),
-    SSMFIELD_ENTRY_IGN_HCPTR(       PATMPATCHREC, patch.unused.pPatchLocStartHC), // saved as zero
-    SSMFIELD_ENTRY_IGN_HCPTR(       PATMPATCHREC, patch.unused.pPatchLocEndHC),   // ditto
-    SSMFIELD_ENTRY_IGN_RCPTR(       PATMPATCHREC, patch.unused.pGuestLoc),        // ditto
-    SSMFIELD_ENTRY_IGNORE(          PATMPATCHREC, patch.unused.opsize),           // ditto
-    SSMFIELD_ENTRY_IGN_HCPTR(       PATMPATCHREC, patch.pTempInfo),
-    SSMFIELD_ENTRY(                 PATMPATCHREC, patch.cCodeWrites),
-    SSMFIELD_ENTRY(                 PATMPATCHREC, patch.cTraps),
-    SSMFIELD_ENTRY(                 PATMPATCHREC, patch.cInvalidWrites),
-    SSMFIELD_ENTRY(                 PATMPATCHREC, patch.uPatchIdx),
-    SSMFIELD_ENTRY(                 PATMPATCHREC, patch.bDirtyOpcode),
-    SSMFIELD_ENTRY_IGNORE(          PATMPATCHREC, patch.Alignment2),
+    SSMFIELD_ENTRY(                 PATMPATCHRECSSM, patch.uState),
+    SSMFIELD_ENTRY(                 PATMPATCHRECSSM, patch.uOldState),
+    SSMFIELD_ENTRY(                 PATMPATCHRECSSM, patch.uOpMode),
+    SSMFIELD_ENTRY_RCPTR(           PATMPATCHRECSSM, patch.pPrivInstrGC),
+    SSMFIELD_ENTRY_IGN_HCPTR(       PATMPATCHRECSSM, patch.unusedHC),
+    SSMFIELD_ENTRY(                 PATMPATCHRECSSM, patch.aPrivInstr),
+    SSMFIELD_ENTRY(                 PATMPATCHRECSSM, patch.cbPrivInstr),
+    SSMFIELD_ENTRY(                 PATMPATCHRECSSM, patch.opcode),
+    SSMFIELD_ENTRY(                 PATMPATCHRECSSM, patch.cbPatchJump),
+    SSMFIELD_ENTRY_RCPTR(           PATMPATCHRECSSM, patch.pPatchJumpDestGC),
+    SSMFIELD_ENTRY(                 PATMPATCHRECSSM, patch.pPatchBlockOffset),
+    SSMFIELD_ENTRY(                 PATMPATCHRECSSM, patch.cbPatchBlockSize),
+    SSMFIELD_ENTRY(                 PATMPATCHRECSSM, patch.uCurPatchOffset),
+    SSMFIELD_ENTRY_PAD_HC64(        PATMPATCHRECSSM, patch.Alignment0, sizeof(uint32_t)),
+    SSMFIELD_ENTRY(                 PATMPATCHRECSSM, patch.flags),
+    SSMFIELD_ENTRY_RCPTR(           PATMPATCHRECSSM, patch.pInstrGCLowest),
+    SSMFIELD_ENTRY_RCPTR(           PATMPATCHRECSSM, patch.pInstrGCHighest),
+    SSMFIELD_ENTRY_IGN_HCPTR(       PATMPATCHRECSSM, patch.FixupTree),
+    SSMFIELD_ENTRY(                 PATMPATCHRECSSM, patch.nrFixups),
+    SSMFIELD_ENTRY(                 PATMPATCHRECSSM, patch.nrJumpRecs), // should be zero?
+    SSMFIELD_ENTRY_IGN_HCPTR(       PATMPATCHRECSSM, patch.JumpTree),
+    SSMFIELD_ENTRY_IGN_HCPTR(       PATMPATCHRECSSM, patch.Patch2GuestAddrTree),
+    SSMFIELD_ENTRY_IGN_HCPTR(       PATMPATCHRECSSM, patch.Guest2PatchAddrTree),
+    SSMFIELD_ENTRY(                 PATMPATCHRECSSM, patch.nrPatch2GuestRecs),
+    SSMFIELD_ENTRY_PAD_HC64(        PATMPATCHRECSSM, patch.Alignment1, sizeof(uint32_t)),
+    SSMFIELD_ENTRY_IGN_HCPTR(       PATMPATCHRECSSM, patch.unused.pPatchLocStartHC), // saved as zero
+    SSMFIELD_ENTRY_IGN_HCPTR(       PATMPATCHRECSSM, patch.unused.pPatchLocEndHC),   // ditto
+    SSMFIELD_ENTRY_IGN_RCPTR(       PATMPATCHRECSSM, patch.unused.pGuestLoc),        // ditto
+    SSMFIELD_ENTRY_IGNORE(          PATMPATCHRECSSM, patch.unused.opsize),           // ditto
+    SSMFIELD_ENTRY_IGN_HCPTR(       PATMPATCHRECSSM, patch.pTempInfo),
+    SSMFIELD_ENTRY(                 PATMPATCHRECSSM, patch.cCodeWrites),
+    SSMFIELD_ENTRY(                 PATMPATCHRECSSM, patch.cTraps),
+    SSMFIELD_ENTRY(                 PATMPATCHRECSSM, patch.cInvalidWrites),
+    SSMFIELD_ENTRY(                 PATMPATCHRECSSM, patch.uPatchIdx),
+    SSMFIELD_ENTRY(                 PATMPATCHRECSSM, patch.bDirtyOpcode),
+    SSMFIELD_ENTRY_IGNORE(          PATMPATCHRECSSM, patch.Alignment2),
     SSMFIELD_ENTRY_TERM()
 };
@@ -387,4 +480,93 @@
 }
 
+/**
+ * Converts a saved state patch record to the memory record.
+ *
+ * @returns nothing.
+ * @param   pPatch       The memory record.
+ * @param   pPatchSSM    The SSM version of the patch record.
+ */
+static void patmR3PatchConvertSSM2Mem(PPATMPATCHREC pPatch, PPATMPATCHRECSSM pPatchSSM)
+{
+    /*
+     * Only restore the patch part of the tree record; not the internal data (except the key of course)
+     */
+    pPatch->Core.Key                  = pPatchSSM->Core.Key;
+    pPatch->CoreOffset.Key            = pPatchSSM->CoreOffset.Key;
+    pPatch->patch.uState              = pPatchSSM->patch.uState;
+    pPatch->patch.uOldState           = pPatchSSM->patch.uOldState;
+    pPatch->patch.uOpMode             = pPatchSSM->patch.uOpMode;
+    pPatch->patch.pPrivInstrGC        = pPatchSSM->patch.pPrivInstrGC;
+    pPatch->patch.unusedHC            = pPatchSSM->patch.unusedHC;
+    memcpy(&pPatch->patch.aPrivInstr[0], &pPatchSSM->patch.aPrivInstr[0], MAX_INSTR_SIZE);
+    pPatch->patch.cbPrivInstr         = pPatchSSM->patch.cbPrivInstr;
+    pPatch->patch.opcode              = pPatchSSM->patch.opcode;
+    pPatch->patch.cbPatchJump         = pPatchSSM->patch.cbPatchJump;
+    pPatch->patch.pPatchJumpDestGC    = pPatchSSM->patch.pPatchJumpDestGC;
+    pPatch->patch.pPatchBlockOffset   = pPatchSSM->patch.pPatchBlockOffset;
+    pPatch->patch.cbPatchBlockSize    = pPatchSSM->patch.cbPatchBlockSize;
+    pPatch->patch.uCurPatchOffset     = pPatchSSM->patch.uCurPatchOffset;
+    pPatch->patch.flags               = pPatchSSM->patch.flags;
+    pPatch->patch.pInstrGCLowest      = pPatchSSM->patch.pInstrGCLowest;
+    pPatch->patch.pInstrGCHighest     = pPatchSSM->patch.pInstrGCHighest;
+    pPatch->patch.FixupTree           = pPatchSSM->patch.FixupTree;
+    pPatch->patch.nrFixups            = pPatchSSM->patch.nrFixups;
+    pPatch->patch.nrJumpRecs          = pPatchSSM->patch.nrJumpRecs;
+    pPatch->patch.JumpTree            = pPatchSSM->patch.JumpTree;
+    pPatch->patch.Patch2GuestAddrTree = pPatchSSM->patch.Patch2GuestAddrTree;
+    pPatch->patch.Guest2PatchAddrTree = pPatchSSM->patch.Guest2PatchAddrTree;
+    pPatch->patch.nrPatch2GuestRecs   = pPatchSSM->patch.nrPatch2GuestRecs;
+    pPatch->patch.unused              = pPatchSSM->patch.unused;
+    pPatch->patch.pTempInfo           = pPatchSSM->patch.pTempInfo;
+    pPatch->patch.cCodeWrites         = pPatchSSM->patch.cCodeWrites;
+    pPatch->patch.cTraps              = pPatchSSM->patch.cTraps;
+    pPatch->patch.cInvalidWrites      = pPatchSSM->patch.cInvalidWrites;
+    pPatch->patch.uPatchIdx           = pPatchSSM->patch.uPatchIdx;
+    pPatch->patch.bDirtyOpcode        = pPatchSSM->patch.bDirtyOpcode;
+    pPatch->patch.pTrampolinePatchesHead = NULL;
+}
+
+/**
+ * Converts a memory patch record to the saved state version.
+ *
+ * @returns nothing.
+ * @param   pPatchSSM    The saved state record.
+ * @param   pPatch       The memory version to save.
+ */
+static void patmR3PatchConvertMem2SSM(PPATMPATCHRECSSM pPatchSSM, PPATMPATCHREC pPatch)
+{
+    pPatchSSM->Core                      = pPatch->Core;
+    pPatchSSM->CoreOffset                = pPatch->CoreOffset;
+    pPatchSSM->patch.uState              = pPatch->patch.uState;
+    pPatchSSM->patch.uOldState           = pPatch->patch.uOldState;
+    pPatchSSM->patch.uOpMode             = pPatch->patch.uOpMode;
+    pPatchSSM->patch.pPrivInstrGC        = pPatch->patch.pPrivInstrGC;
+    pPatchSSM->patch.unusedHC            = pPatch->patch.unusedHC;
+    memcpy(&pPatchSSM->patch.aPrivInstr[0], &pPatch->patch.aPrivInstr[0], MAX_INSTR_SIZE);
+    pPatchSSM->patch.cbPrivInstr         = pPatch->patch.cbPrivInstr;
+    pPatchSSM->patch.opcode              = pPatch->patch.opcode;
+    pPatchSSM->patch.cbPatchJump         = pPatch->patch.cbPatchJump;
+    pPatchSSM->patch.pPatchJumpDestGC    = pPatch->patch.pPatchJumpDestGC;
+    pPatchSSM->patch.pPatchBlockOffset   = pPatch->patch.pPatchBlockOffset;
+    pPatchSSM->patch.cbPatchBlockSize    = pPatch->patch.cbPatchBlockSize;
+    pPatchSSM->patch.uCurPatchOffset     = pPatch->patch.uCurPatchOffset;
+    pPatchSSM->patch.flags               = pPatch->patch.flags;
+    pPatchSSM->patch.pInstrGCLowest      = pPatch->patch.pInstrGCLowest;
+    pPatchSSM->patch.pInstrGCHighest     = pPatch->patch.pInstrGCHighest;
+    pPatchSSM->patch.FixupTree           = pPatch->patch.FixupTree;
+    pPatchSSM->patch.nrFixups            = pPatch->patch.nrFixups;
+    pPatchSSM->patch.nrJumpRecs          = pPatch->patch.nrJumpRecs;
+    pPatchSSM->patch.JumpTree            = pPatch->patch.JumpTree;
+    pPatchSSM->patch.Patch2GuestAddrTree = pPatch->patch.Patch2GuestAddrTree;
+    pPatchSSM->patch.Guest2PatchAddrTree = pPatch->patch.Guest2PatchAddrTree;
+    pPatchSSM->patch.nrPatch2GuestRecs   = pPatch->patch.nrPatch2GuestRecs;
+    pPatchSSM->patch.unused              = pPatch->patch.unused;
+    pPatchSSM->patch.pTempInfo           = pPatch->patch.pTempInfo;
+    pPatchSSM->patch.cCodeWrites         = pPatch->patch.cCodeWrites;
+    pPatchSSM->patch.cTraps              = pPatch->patch.cTraps;
+    pPatchSSM->patch.cInvalidWrites      = pPatch->patch.cInvalidWrites;
+    pPatchSSM->patch.uPatchIdx           = pPatch->patch.uPatchIdx;
+    pPatchSSM->patch.bDirtyOpcode        = pPatch->patch.bDirtyOpcode;
+}
 
 /**
@@ -399,11 +581,13 @@
 static DECLCALLBACK(int) patmSavePatchState(PAVLOU32NODECORE pNode, void *pVM1)
 {
-    PVM           pVM    = (PVM)pVM1;
-    PPATMPATCHREC pPatch = (PPATMPATCHREC)pNode;
-    PATMPATCHREC  patch  = *pPatch;
-    PSSMHANDLE    pSSM   = pVM->patm.s.savedstate.pSSM;
-    int           rc;
+    PVM             pVM    = (PVM)pVM1;
+    PPATMPATCHREC   pPatch = (PPATMPATCHREC)pNode;
+    PATMPATCHRECSSM patch;
+    PSSMHANDLE      pSSM   = pVM->patm.s.savedstate.pSSM;
+    int             rc;
 
     Assert(!(pPatch->patch.flags & PATMFL_GLOBAL_FUNCTIONS));
+
+    patmR3PatchConvertMem2SSM(&patch, pPatch);
 
     /*
@@ -602,5 +786,6 @@
     for (unsigned i = 0; i < patmInfo.savedstate.cPatches; i++)
     {
-        PATMPATCHREC patch, *pPatchRec;
+        PATMPATCHRECSSM patch;
+        PATMPATCHREC *pPatchRec;
 
         RT_ZERO(patch);
@@ -616,10 +801,7 @@
             return VERR_NO_MEMORY;
         }
-        /*
-         * Only restore the patch part of the tree record; not the internal data (except the key of course)
-         */
-        pPatchRec->patch             = patch.patch;
-        pPatchRec->Core.Key          = patch.Core.Key;
-        pPatchRec->CoreOffset.Key    = patch.CoreOffset.Key;
+
+        /* Convert SSM version to memory. */
+        patmR3PatchConvertSSM2Mem(pPatchRec, &patch);
 
         Log(("Restoring patch %RRv -> %RRv\n", pPatchRec->patch.pPrivInstrGC, patmInfo.pPatchMemGC + pPatchRec->patch.pPatchBlockOffset));
Index: /trunk/src/VBox/VMM/include/PATMInternal.h
===================================================================
--- /trunk/src/VBox/VMM/include/PATMInternal.h	(revision 36668)
+++ /trunk/src/VBox/VMM/include/PATMInternal.h	(revision 36669)
@@ -241,4 +241,7 @@
 } PATCHINFOTEMP, *PPATCHINFOTEMP;
 
+/** Forward declaration for a pointer to a trampoline patch record. */
+typedef struct TRAMPREC *PTRAMPREC;
+
 typedef struct _PATCHINFO
 {
@@ -298,4 +301,8 @@
     R3PTRTYPE(PPATCHINFOTEMP) pTempInfo;
 
+    /* List of trampoline patches referencing this patch.
+     * Used when refreshing the patch. (Only for function duplicates) */
+    R3PTRTYPE(PTRAMPREC)      pTrampolinePatchesHead;
+
     /* Count the number of writes to the corresponding guest code. */
     uint32_t        cCodeWrites;
@@ -312,5 +319,5 @@
     /* First opcode byte, that's overwritten when a patch is marked dirty. */
     uint8_t         bDirtyOpcode;
-    uint8_t         Alignment2[7];      /**< Align the structure size on a 8-byte boundary. */
+    uint8_t         Alignment2[HC_ARCH_BITS == 64 ? 7 : 3];      /**< Align the structure size on a 8-byte boundary. */
 } PATCHINFO, *PPATCHINFO;
 
@@ -330,4 +337,15 @@
     PATCHINFO  patch;
 } PATMPATCHREC, *PPATMPATCHREC;
+
+/**
+ * Record for a trampoline patch.
+ */
+typedef struct TRAMPREC
+{
+    /** Pointer to the next trampoline patch. */
+    struct TRAMPREC    *pNext;
+    /** Pointer to the trampoline patch record. */
+    PPATMPATCHREC       pPatchTrampoline;
+} TRAMPREC;
 
 /** Increment for allocating room for pointer array */
Index: /trunk/src/VBox/VMM/testcase/tstVMStructRC.cpp
===================================================================
--- /trunk/src/VBox/VMM/testcase/tstVMStructRC.cpp	(revision 36668)
+++ /trunk/src/VBox/VMM/testcase/tstVMStructRC.cpp	(revision 36669)
@@ -1172,4 +1172,5 @@
     GEN_CHECK_OFF(PATCHINFO, unused.opsize);
     GEN_CHECK_OFF(PATCHINFO, pTempInfo);
+    GEN_CHECK_OFF(PATCHINFO, pTrampolinePatchesHead);
     GEN_CHECK_OFF(PATCHINFO, cCodeWrites);
     GEN_CHECK_OFF(PATCHINFO, cTraps);
