|
From: <sv...@va...> - 2012-02-16 14:23:34
|
Author: sewardj
Date: 2012-02-16 14:18:56 +0000 (Thu, 16 Feb 2012)
New Revision: 2253
Log:
Adds 16 and 32 bit fnsave/frstor, and 0x66 prefix on fldl, to guest
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, mo...@cs...)
Modified:
trunk/priv/guest_amd64_defs.h
trunk/priv/guest_amd64_helpers.c
trunk/priv/guest_amd64_toIR.c
trunk/priv/guest_generic_x87.h
Modified: trunk/priv/guest_amd64_defs.h
===================================================================
--- trunk/priv/guest_amd64_defs.h 2012-02-16 13:45:13 UTC (rev 2252)
+++ trunk/priv/guest_amd64_defs.h 2012-02-16 14:18:56 UTC (rev 2253)
@@ -118,9 +118,13 @@
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
Modified: trunk/priv/guest_amd64_helpers.c
===================================================================
--- trunk/priv/guest_amd64_helpers.c 2012-02-16 13:45:13 UTC (rev 2252)
+++ trunk/priv/guest_amd64_helpers.c 2012-02-16 14:18:56 UTC (rev 2253)
@@ -1553,7 +1553,7 @@
/* 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 @@
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 @@
}
+/* 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. ---*/
/*---------------------------------------------------------------*/
Modified: trunk/priv/guest_amd64_toIR.c
===================================================================
--- trunk/priv/guest_amd64_toIR.c 2012-02-16 13:45:13 UTC (rev 2252)
+++ trunk/priv/guest_amd64_toIR.c 2012-02-16 14:18:56 UTC (rev 2253)
@@ -6038,109 +6038,150 @@
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();
vassert(typeOfIRExpr(irsb->tyenv, sw) == Ity_I16);
@@ -17550,15 +17591,30 @@
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;
}
Modified: trunk/priv/guest_generic_x87.h
===================================================================
--- trunk/priv/guest_generic_x87.h 2012-02-16 13:45:13 UTC (rev 2252)
+++ trunk/priv/guest_generic_x87.h 2012-02-16 14:18:56 UTC (rev 2253)
@@ -89,11 +89,30 @@
#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 );
|