]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Adds 16 and 32 bit fnsave/frstor, and 0x66 prefix on fldl, to guest
authorJulian Seward <jseward@acm.org>
Thu, 16 Feb 2012 14:18:56 +0000 (14:18 +0000)
committerJulian Seward <jseward@acm.org>
Thu, 16 Feb 2012 14:18:56 +0000 (14:18 +0000)
amd64.

The Oracle/Sun HotSpot Java virtual machine uses fnsave and frstor,
which valgrind supports for x86 but not amd64. Even more interesting,
HotSpot uses the 0x66 size prefix on these instructions, and on
fldl. This patch adds the 16- and 32-bit versions of fnsave/frstor to
the amd64 guest, and tolerates the 0x66 size prefix on fldl (but only
on these three fpu instructions, even though the AMD docs say all
other fpu instructions (except fnstenv and fldenv) *ignore* 0x66).

Fixes #294191.  (Eliot Moss, moss@cs.umass.edu)

git-svn-id: svn://svn.valgrind.org/vex/trunk@2253

VEX/priv/guest_amd64_defs.h
VEX/priv/guest_amd64_helpers.c
VEX/priv/guest_amd64_toIR.c
VEX/priv/guest_generic_x87.h

index 55ecfe95bbda89254115dd56414ed25dacf5faf9..94d55de7c0cfb11883e469b3c40168f290a6cd51 100644 (file)
@@ -118,9 +118,13 @@ extern ULong amd64g_check_ldmxcsr ( ULong mxcsr );
 
 extern ULong amd64g_create_mxcsr ( ULong sseround );
 
-extern VexEmWarn amd64g_dirtyhelper_FLDENV ( VexGuestAMD64State*, HWord );
+extern VexEmWarn amd64g_dirtyhelper_FLDENV  ( VexGuestAMD64State*, HWord );
+extern VexEmWarn amd64g_dirtyhelper_FRSTOR  ( VexGuestAMD64State*, HWord );
+extern VexEmWarn amd64g_dirtyhelper_FRSTORS ( VexGuestAMD64State*, HWord );
 
-extern void amd64g_dirtyhelper_FSTENV ( VexGuestAMD64State*, HWord );
+extern void amd64g_dirtyhelper_FSTENV  ( VexGuestAMD64State*, HWord );
+extern void amd64g_dirtyhelper_FNSAVE  ( VexGuestAMD64State*, HWord );
+extern void amd64g_dirtyhelper_FNSAVES ( VexGuestAMD64State*, HWord );
 
 /* Translate a guest virtual_addr into a guest linear address by
    consulting the supplied LDT/GDT structures.  Their representation
index dd72b9cd7b4f22499cff5fadf008df6df3aeb534..53db1f0e37c46d80f67c3652794013dd914dec87 100644 (file)
@@ -1553,7 +1553,7 @@ VexEmWarn do_put_x87 ( Bool moveRegs,
    /* handle the control word, setting FPROUND and detecting any
       emulation warnings. */
    pair    = amd64g_check_fldcw ( (ULong)fpucw );
-   fpround = (UInt)pair;
+   fpround = (UInt)pair & 0xFFFFFFFFULL;
    ew      = (VexEmWarn)(pair >> 32);
    
    vex_state->guest_FPROUND = fpround & 3;
@@ -1929,47 +1929,7 @@ ULong amd64g_create_fpucw ( ULong fpround )
 VexEmWarn amd64g_dirtyhelper_FLDENV ( /*OUT*/VexGuestAMD64State* vex_state,
                                       /*IN*/HWord x87_state)
 {
-   Int        stno, preg;
-   UInt       tag;
-   UChar*     vexTags = (UChar*)(&vex_state->guest_FPTAG[0]);
-   Fpu_State* x87     = (Fpu_State*)x87_state;
-   UInt       ftop    = (x87->env[FP_ENV_STAT] >> 11) & 7;
-   UInt       tagw    = x87->env[FP_ENV_TAG];
-   UInt       fpucw   = x87->env[FP_ENV_CTRL];
-   ULong      c3210   = x87->env[FP_ENV_STAT] & 0x4700;
-   VexEmWarn  ew;
-   ULong      fpround;
-   ULong      pair;
-
-   /* Copy tags */
-   for (stno = 0; stno < 8; stno++) {
-      preg = (stno + ftop) & 7;
-      tag = (tagw >> (2*preg)) & 3;
-      if (tag == 3) {
-         /* register is empty */
-         vexTags[preg] = 0;
-      } else {
-         /* register is non-empty */
-         vexTags[preg] = 1;
-      }
-   }
-
-   /* stack pointer */
-   vex_state->guest_FTOP = ftop;
-
-   /* status word */
-   vex_state->guest_FC3210 = c3210;
-
-   /* handle the control word, setting FPROUND and detecting any
-      emulation warnings. */
-   pair    = amd64g_check_fldcw ( (ULong)fpucw );
-   fpround = pair & 0xFFFFFFFFULL;
-   ew      = (VexEmWarn)(pair >> 32);
-   
-   vex_state->guest_FPROUND = fpround & 3;
-
-   /* emulation warnings --> caller */
-   return ew;
+   return do_put_x87( False, (UChar*)x87_state, vex_state );
 }
 
 
@@ -2014,6 +1974,130 @@ void amd64g_dirtyhelper_FSTENV ( /*IN*/VexGuestAMD64State* vex_state,
 }
 
 
+/* This is used to implement 'fnsave'.  
+   Writes 108 bytes at x87_state[0 .. 107]. */
+/* CALLED FROM GENERATED CODE */
+/* DIRTY HELPER */
+void amd64g_dirtyhelper_FNSAVE ( /*IN*/VexGuestAMD64State* vex_state,
+                                 /*OUT*/HWord x87_state)
+{
+   do_get_x87( vex_state, (UChar*)x87_state );
+}
+
+
+/* This is used to implement 'fnsaves'.  
+   Writes 94 bytes at x87_state[0 .. 93]. */
+/* CALLED FROM GENERATED CODE */
+/* DIRTY HELPER */
+void amd64g_dirtyhelper_FNSAVES ( /*IN*/VexGuestAMD64State* vex_state,
+                                  /*OUT*/HWord x87_state)
+{
+   Int           i, stno, preg;
+   UInt          tagw;
+   ULong*        vexRegs = (ULong*)(&vex_state->guest_FPREG[0]);
+   UChar*        vexTags = (UChar*)(&vex_state->guest_FPTAG[0]);
+   Fpu_State_16* x87     = (Fpu_State_16*)x87_state;
+   UInt          ftop    = vex_state->guest_FTOP;
+   UInt          c3210   = vex_state->guest_FC3210;
+
+   for (i = 0; i < 7; i++)
+      x87->env[i] = 0;
+
+   x87->env[FPS_ENV_STAT] 
+      = toUShort(((ftop & 7) << 11) | (c3210 & 0x4700));
+   x87->env[FPS_ENV_CTRL] 
+      = toUShort(amd64g_create_fpucw( vex_state->guest_FPROUND ));
+
+   /* Dump the register stack in ST order. */
+   tagw = 0;
+   for (stno = 0; stno < 8; stno++) {
+      preg = (stno + ftop) & 7;
+      if (vexTags[preg] == 0) {
+         /* register is empty */
+         tagw |= (3 << (2*preg));
+         convert_f64le_to_f80le( (UChar*)&vexRegs[preg], 
+                                 &x87->reg[10*stno] );
+      } else {
+         /* register is full. */
+         tagw |= (0 << (2*preg));
+         convert_f64le_to_f80le( (UChar*)&vexRegs[preg], 
+                                 &x87->reg[10*stno] );
+      }
+   }
+   x87->env[FPS_ENV_TAG] = toUShort(tagw);
+}
+
+
+/* This is used to implement 'frstor'.  
+   Reads 108 bytes at x87_state[0 .. 107]. */
+/* CALLED FROM GENERATED CODE */
+/* DIRTY HELPER */
+VexEmWarn amd64g_dirtyhelper_FRSTOR ( /*OUT*/VexGuestAMD64State* vex_state,
+                                      /*IN*/HWord x87_state)
+{
+   return do_put_x87( True, (UChar*)x87_state, vex_state );
+}
+
+
+/* This is used to implement 'frstors'.
+   Reads 94 bytes at x87_state[0 .. 93]. */
+/* CALLED FROM GENERATED CODE */
+/* DIRTY HELPER */
+VexEmWarn amd64g_dirtyhelper_FRSTORS ( /*OUT*/VexGuestAMD64State* vex_state,
+                                       /*IN*/HWord x87_state)
+{
+   Int           stno, preg;
+   UInt          tag;
+   ULong*        vexRegs = (ULong*)(&vex_state->guest_FPREG[0]);
+   UChar*        vexTags = (UChar*)(&vex_state->guest_FPTAG[0]);
+   Fpu_State_16* x87     = (Fpu_State_16*)x87_state;
+   UInt          ftop    = (x87->env[FPS_ENV_STAT] >> 11) & 7;
+   UInt          tagw    = x87->env[FPS_ENV_TAG];
+   UInt          fpucw   = x87->env[FPS_ENV_CTRL];
+   UInt          c3210   = x87->env[FPS_ENV_STAT] & 0x4700;
+   VexEmWarn     ew;
+   UInt          fpround;
+   ULong         pair;
+
+   /* Copy registers and tags */
+   for (stno = 0; stno < 8; stno++) {
+      preg = (stno + ftop) & 7;
+      tag = (tagw >> (2*preg)) & 3;
+      if (tag == 3) {
+         /* register is empty */
+         /* hmm, if it's empty, does it still get written?  Probably
+            safer to say it does.  If we don't, memcheck could get out
+            of sync, in that it thinks all FP registers are defined by
+            this helper, but in reality some have not been updated. */
+         vexRegs[preg] = 0; /* IEEE754 64-bit zero */
+         vexTags[preg] = 0;
+      } else {
+         /* register is non-empty */
+         convert_f80le_to_f64le( &x87->reg[10*stno], 
+                                 (UChar*)&vexRegs[preg] );
+         vexTags[preg] = 1;
+      }
+   }
+
+   /* stack pointer */
+   vex_state->guest_FTOP = ftop;
+
+   /* status word */
+   vex_state->guest_FC3210 = c3210;
+
+   /* handle the control word, setting FPROUND and detecting any
+      emulation warnings. */
+   pair    = amd64g_check_fldcw ( (ULong)fpucw );
+   fpround = (UInt)pair & 0xFFFFFFFFULL;
+   ew      = (VexEmWarn)(pair >> 32);
+   
+   vex_state->guest_FPROUND = fpround & 3;
+
+   /* emulation warnings --> caller */
+   return ew;
+}
+
+
 /*---------------------------------------------------------------*/
 /*--- Misc integer helpers, including rotates and CPUID.      ---*/
 /*---------------------------------------------------------------*/
index 883b2fdff2bfe9cff2b58cbad8c87893e974b5ef..93326e75a85da7e4101932b13069e423c476d11f 100644 (file)
@@ -6038,108 +6038,149 @@ ULong dis_FPU ( /*OUT*/Bool* decode_ok,
                fp_pop();
                break;
 
-//..             case 4: { /* FRSTOR m108 */
-//..                /* Uses dirty helper: 
-//..                      VexEmWarn x86g_do_FRSTOR ( VexGuestX86State*, Addr32 ) */
-//..                IRTemp   ew = newTemp(Ity_I32);
-//..                IRDirty* d  = unsafeIRDirty_0_N ( 
-//..                                 0/*regparms*/, 
-//..                                 "x86g_dirtyhelper_FRSTOR", 
-//..                                 &x86g_dirtyhelper_FRSTOR,
-//..                                 mkIRExprVec_1( mkexpr(addr) )
-//..                              );
-//..                d->needsBBP = True;
-//..                d->tmp      = ew;
-//..                /* declare we're reading memory */
-//..                d->mFx   = Ifx_Read;
-//..                d->mAddr = mkexpr(addr);
-//..                d->mSize = 108;
-//.. 
-//..                /* declare we're writing guest state */
-//..                d->nFxState = 5;
-//.. 
-//..                d->fxState[0].fx     = Ifx_Write;
-//..                d->fxState[0].offset = OFFB_FTOP;
-//..                d->fxState[0].size   = sizeof(UInt);
-//.. 
-//..                d->fxState[1].fx     = Ifx_Write;
-//..                d->fxState[1].offset = OFFB_FPREGS;
-//..                d->fxState[1].size   = 8 * sizeof(ULong);
-//.. 
-//..                d->fxState[2].fx     = Ifx_Write;
-//..                d->fxState[2].offset = OFFB_FPTAGS;
-//..                d->fxState[2].size   = 8 * sizeof(UChar);
-//.. 
-//..                d->fxState[3].fx     = Ifx_Write;
-//..                d->fxState[3].offset = OFFB_FPROUND;
-//..                d->fxState[3].size   = sizeof(UInt);
-//.. 
-//..                d->fxState[4].fx     = Ifx_Write;
-//..                d->fxState[4].offset = OFFB_FC3210;
-//..                d->fxState[4].size   = sizeof(UInt);
-//.. 
-//..                stmt( IRStmt_Dirty(d) );
-//.. 
-//..                /* ew contains any emulation warning we may need to
-//..                   issue.  If needed, side-exit to the next insn,
-//..                   reporting the warning, so that Valgrind's dispatcher
-//..                   sees the warning. */
-//..                put_emwarn( mkexpr(ew) );
-//..                stmt( 
-//..                   IRStmt_Exit(
-//..                      binop(Iop_CmpNE32, mkexpr(ew), mkU32(0)),
-//..                      Ijk_EmWarn,
-//..                      IRConst_U32( ((Addr32)guest_eip_bbstart)+delta)
-//..                   )
-//..                );
-//.. 
-//..                DIP("frstor %s\n", dis_buf);
-//..                break;
-//..             }
-//.. 
-//..             case 6: { /* FNSAVE m108 */
-//..                /* Uses dirty helper: 
-//..                      void x86g_do_FSAVE ( VexGuestX86State*, UInt ) */
-//..                IRDirty* d = unsafeIRDirty_0_N ( 
-//..                                0/*regparms*/, 
-//..                                "x86g_dirtyhelper_FSAVE", 
-//..                                &x86g_dirtyhelper_FSAVE,
-//..                                mkIRExprVec_1( mkexpr(addr) )
-//..                             );
-//..                d->needsBBP = True;
-//..                /* declare we're writing memory */
-//..                d->mFx   = Ifx_Write;
-//..                d->mAddr = mkexpr(addr);
-//..                d->mSize = 108;
-//.. 
-//..                /* declare we're reading guest state */
-//..                d->nFxState = 5;
-//.. 
-//..                d->fxState[0].fx     = Ifx_Read;
-//..                d->fxState[0].offset = OFFB_FTOP;
-//..                d->fxState[0].size   = sizeof(UInt);
-//.. 
-//..                d->fxState[1].fx     = Ifx_Read;
-//..                d->fxState[1].offset = OFFB_FPREGS;
-//..                d->fxState[1].size   = 8 * sizeof(ULong);
-//.. 
-//..                d->fxState[2].fx     = Ifx_Read;
-//..                d->fxState[2].offset = OFFB_FPTAGS;
-//..                d->fxState[2].size   = 8 * sizeof(UChar);
-//.. 
-//..                d->fxState[3].fx     = Ifx_Read;
-//..                d->fxState[3].offset = OFFB_FPROUND;
-//..                d->fxState[3].size   = sizeof(UInt);
-//.. 
-//..                d->fxState[4].fx     = Ifx_Read;
-//..                d->fxState[4].offset = OFFB_FC3210;
-//..                d->fxState[4].size   = sizeof(UInt);
-//.. 
-//..                stmt( IRStmt_Dirty(d) );
-//.. 
-//..                DIP("fnsave %s\n", dis_buf);
-//..                break;
-//..             }
+            case 4: { /* FRSTOR m94/m108 */
+               IRTemp   ew = newTemp(Ity_I32);
+               IRTemp  w64 = newTemp(Ity_I64);
+               IRDirty*  d;
+               if ( have66(pfx) ) {
+                  /* Uses dirty helper: 
+                     VexEmWarn amd64g_dirtyhelper_FRSTORS
+                                  ( VexGuestAMD64State*, HWord ) */
+                  d = unsafeIRDirty_0_N ( 
+                         0/*regparms*/, 
+                         "amd64g_dirtyhelper_FRSTORS",
+                         &amd64g_dirtyhelper_FRSTORS,
+                         mkIRExprVec_1( mkexpr(addr) )
+                      );
+                  d->mSize = 94;
+               } else {
+                  /* Uses dirty helper: 
+                     VexEmWarn amd64g_dirtyhelper_FRSTOR 
+                                  ( VexGuestAMD64State*, HWord ) */
+                  d = unsafeIRDirty_0_N ( 
+                         0/*regparms*/, 
+                         "amd64g_dirtyhelper_FRSTOR",
+                         &amd64g_dirtyhelper_FRSTOR,
+                         mkIRExprVec_1( mkexpr(addr) )
+                      );
+                  d->mSize = 108;
+               }
+
+               d->needsBBP = True;
+               d->tmp      = w64;
+               /* declare we're reading memory */
+               d->mFx   = Ifx_Read;
+               d->mAddr = mkexpr(addr);
+               /* d->mSize set above */
+
+               /* declare we're writing guest state */
+               d->nFxState = 5;
+
+               d->fxState[0].fx     = Ifx_Write;
+               d->fxState[0].offset = OFFB_FTOP;
+               d->fxState[0].size   = sizeof(UInt);
+
+               d->fxState[1].fx     = Ifx_Write;
+               d->fxState[1].offset = OFFB_FPREGS;
+               d->fxState[1].size   = 8 * sizeof(ULong);
+
+               d->fxState[2].fx     = Ifx_Write;
+               d->fxState[2].offset = OFFB_FPTAGS;
+               d->fxState[2].size   = 8 * sizeof(UChar);
+
+               d->fxState[3].fx     = Ifx_Write;
+               d->fxState[3].offset = OFFB_FPROUND;
+               d->fxState[3].size   = sizeof(ULong);
+
+               d->fxState[4].fx     = Ifx_Write;
+               d->fxState[4].offset = OFFB_FC3210;
+               d->fxState[4].size   = sizeof(ULong);
+
+               stmt( IRStmt_Dirty(d) );
+
+               /* ew contains any emulation warning we may need to
+                  issue.  If needed, side-exit to the next insn,
+                  reporting the warning, so that Valgrind's dispatcher
+                  sees the warning. */
+               assign(ew, unop(Iop_64to32,mkexpr(w64)) );
+               put_emwarn( mkexpr(ew) );
+               stmt( 
+                  IRStmt_Exit(
+                     binop(Iop_CmpNE32, mkexpr(ew), mkU32(0)),
+                     Ijk_EmWarn,
+                     IRConst_U64( guest_RIP_bbstart+delta )
+                  )
+               );
+
+               if ( have66(pfx) ) {
+                  DIP("frstors %s\n", dis_buf);
+               } else {
+                  DIP("frstor %s\n", dis_buf);
+               }
+               break;
+            }
+
+            case 6: { /* FNSAVE m94/m108 */
+               IRDirty *d;
+               if ( have66(pfx) ) {
+                 /* Uses dirty helper: 
+                    void amd64g_dirtyhelper_FNSAVES ( VexGuestX86State*, HWord ) */
+                  d = unsafeIRDirty_0_N ( 
+                         0/*regparms*/, 
+                         "amd64g_dirtyhelper_FNSAVES", 
+                         &amd64g_dirtyhelper_FNSAVES,
+                         mkIRExprVec_1( mkexpr(addr) )
+                         );
+                  d->mSize = 94;
+               } else {
+                 /* Uses dirty helper: 
+                    void amd64g_dirtyhelper_FNSAVE ( VexGuestX86State*, HWord ) */
+                  d = unsafeIRDirty_0_N ( 
+                         0/*regparms*/, 
+                         "amd64g_dirtyhelper_FNSAVE",
+                         &amd64g_dirtyhelper_FNSAVE,
+                         mkIRExprVec_1( mkexpr(addr) )
+                         );
+                  d->mSize = 108;
+               }
+               d->needsBBP = True;
+               /* declare we're writing memory */
+               d->mFx   = Ifx_Write;
+               d->mAddr = mkexpr(addr);
+               /* d->mSize set above */
+
+               /* declare we're reading guest state */
+               d->nFxState = 5;
+
+               d->fxState[0].fx     = Ifx_Read;
+               d->fxState[0].offset = OFFB_FTOP;
+               d->fxState[0].size   = sizeof(UInt);
+
+               d->fxState[1].fx     = Ifx_Read;
+               d->fxState[1].offset = OFFB_FPREGS;
+               d->fxState[1].size   = 8 * sizeof(ULong);
+
+               d->fxState[2].fx     = Ifx_Read;
+               d->fxState[2].offset = OFFB_FPTAGS;
+               d->fxState[2].size   = 8 * sizeof(UChar);
+
+               d->fxState[3].fx     = Ifx_Read;
+               d->fxState[3].offset = OFFB_FPROUND;
+               d->fxState[3].size   = sizeof(ULong);
+
+               d->fxState[4].fx     = Ifx_Read;
+               d->fxState[4].offset = OFFB_FC3210;
+               d->fxState[4].size   = sizeof(ULong);
+
+               stmt( IRStmt_Dirty(d) );
+
+               if ( have66(pfx) ) {
+                 DIP("fnsaves %s\n", dis_buf);
+               } else {
+                 DIP("fnsave %s\n", dis_buf);
+               }
+               break;
+            }
 
             case 7: { /* FNSTSW m16 */
                IRExpr* sw = get_FPU_sw();
@@ -17550,15 +17591,30 @@ Long dis_ESC_NONE (
       if ( (opc == 0xD9 && getUChar(delta+0) == 0xFA)/*fsqrt*/ )
          redundantREXWok = True;
 
-      if ( (sz == 4 || (sz == 8 && redundantREXWok))
-           && haveNo66noF2noF3(pfx)) {
-         Bool decode_OK = False;
-         delta = dis_FPU ( &decode_OK, vbi, pfx, delta );
-         if (!decode_OK)
-            goto decode_failure;
-      } else {
+      Bool size_OK = False;
+      if ( sz == 4 )
+         size_OK = True;
+      else if ( sz == 8 )
+         size_OK = redundantREXWok;
+      else if ( sz == 2 ) {
+         int mod_rm = getUChar(delta+0);
+         int reg = gregLO3ofRM(mod_rm);
+         /* The HotSpot JVM uses these */
+         if ( (opc == 0xDD) && (reg == 0 /* FLDL   */ ||
+                                reg == 4 /* FNSAVE */ ||
+                                reg == 6 /* FRSTOR */ ) )
+            size_OK = True;
+      }
+      /* AMD manual says 0x66 size override is ignored, except where
+         it is meaningful */
+      if (!size_OK)
          goto decode_failure;
-      }
+
+      Bool decode_OK = False;
+      delta = dis_FPU ( &decode_OK, vbi, pfx, delta );
+      if (!decode_OK)
+         goto decode_failure;
+
       return delta;
    }
 
index 997c2c2acedfd2778446db518e77062797878a17..65ab4466c2d23def4ac40f388c8eaa0747ed5ba8 100644 (file)
@@ -89,11 +89,30 @@ typedef
 #define FP_ENV_TAG    4
 #define FP_ENV_IP     6 /* and 7 */
 #define FP_ENV_CS     8
+#define FP_ENV_LSTOP  9
 #define FP_ENV_OPOFF  10 /* and 11 */
 #define FP_ENV_OPSEL  12
 #define FP_REG(ii)    (10*(7-(ii)))
 
 
+/* Layout of the 16-bit FNSAVE x87 state. */
+typedef
+   struct {
+      UShort env[7];
+      UChar  reg[80];
+   }
+   Fpu_State_16;
+
+/* Offsets, in 16-bit ints, into the FPU environment (env) area. */
+#define FPS_ENV_CTRL   0
+#define FPS_ENV_STAT   1
+#define FPS_ENV_TAG    2
+#define FPS_ENV_IP     3
+#define FPS_ENV_CS     4
+#define FPS_ENV_OPOFF  5
+#define FPS_ENV_OPSEL  6
+
+
 /* Do the computations for x86/amd64 FXTRACT.  Called directly from
    generated code.  CLEAN HELPER. */
 extern ULong x86amd64g_calculate_FXTRACT ( ULong arg, HWord getExp );