From: Julian Seward Date: Mon, 29 Nov 2004 19:57:54 +0000 (+0000) Subject: Create a new mechanism: "emulation warnings", which is a way for Vex X-Git-Tag: svn/VALGRIND_3_0_1^2~739 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=26338fd250bbb95fe664da2da9ec8ab5c45de316;p=thirdparty%2Fvalgrind.git Create a new mechanism: "emulation warnings", which is a way for Vex to report to whatever is using it that it cannot emulate precisely. Net result is that a bb can exit with the guest state pointer set to VEX_TRC_EMWARN. In this case, the (mandatory) guest state psuedo-reg called "guest_EMWARN" holds a value of type VexEmWarn, indicating the kind of problem encounted. Use this to warn about approximations in the x87 FPU simulation: unmasked exceptions not supported, round to +inf/-inf not supported, precisions other than 80-bit not supported. git-svn-id: svn://svn.valgrind.org/vex/trunk@595 --- diff --git a/VEX/Makefile b/VEX/Makefile index 20fb76647c..cfeae6fe14 100644 --- a/VEX/Makefile +++ b/VEX/Makefile @@ -3,6 +3,7 @@ PUB_HEADERS = pub/libvex_basictypes.h \ pub/libvex_ir.h \ pub/libvex.h \ pub/libvex_trc_values.h \ + pub/libvex_emwarn.h \ pub/libvex_guest_x86.h \ pub/libvex_guest_amd64.h \ pub/libvex_guest_arm.h diff --git a/VEX/priv/guest-x86/gdefs.h b/VEX/priv/guest-x86/gdefs.h index 2cca5032a2..84f47eb687 100644 --- a/VEX/priv/guest-x86/gdefs.h +++ b/VEX/priv/guest-x86/gdefs.h @@ -95,6 +95,10 @@ extern ULong x86g_calculate_RCR ( UInt arg, UInt rot_amt, UInt eflags_in, UInt sz ); +extern ULong x86h_check_fldcw ( UInt fpucw ); + +extern UInt x86h_create_fpucw ( UInt fptrz ); + /* --- Clean helpers for MMX --- */ extern ULong x86g_calculate_add32x2 ( ULong, ULong ); diff --git a/VEX/priv/guest-x86/ghelpers.c b/VEX/priv/guest-x86/ghelpers.c index e09b919e4a..91ab8d88bb 100644 --- a/VEX/priv/guest-x86/ghelpers.c +++ b/VEX/priv/guest-x86/ghelpers.c @@ -34,6 +34,7 @@ */ #include "libvex_basictypes.h" +#include "libvex_emwarn.h" #include "libvex_guest_x86.h" #include "libvex_ir.h" #include "libvex.h" @@ -1386,9 +1387,51 @@ typedef #define FP_REG(ii) (10*(7-(ii))) +/* native_fpucw[15:0] contains a x87 native format FPU control word. + Extract from it the required FPRTZ value and any resulting + emulation warning, and return (warn << 32) | fprtz value. */ +/* CLEAN HELPER */ +ULong x86h_check_fldcw ( UInt fpucw ) +{ + /* Decide on a rounding mode. fpucw[11:10] must be either + 00b(round to nearest) or 11b(round to zero). No others + supported. Others are mapped to round-to-nearest. */ + UInt rmode = (fpucw >> 10) & 3; + UInt fprtz = rmode==0 ? 0 : 1; + + /* Detect any required emulation warnings. */ + VexEmWarn ew = EmWarn_NONE; + + if ((fpucw & 0x3F) != 0x3F) { + /* unmasked exceptions! */ + ew = EmWarn_X86_x87exns; + } + else + if (rmode != 0 && rmode != 3) { + /* unsupported rounding mode */ + ew = EmWarn_X86_x87rounding; + } + else + if (((fpucw >> 8) & 3) != 3) { + /* unsupported precision */ + ew = EmWarn_X86_x87precision; + } + + return (((ULong)ew) << 32) | ((ULong)fprtz); +} + +/* CLEAN HELPER */ +/* Given fprtz as 1 or 0, create a suitable x87 native format + FPU control word. */ +UInt x86h_create_fpucw ( UInt fprtz ) +{ + return (fprtz & 1) ? 0x0F7F : 0x037F; +} + + /* VISIBLE TO LIBVEX CLIENT */ -void LibVEX_GuestX86_put_x87 ( /*IN*/UChar* x87_state, - /*OUT*/VexGuestX86State* vex_state ) +VexEmWarn LibVEX_GuestX86_put_x87 ( /*IN*/UChar* x87_state, + /*OUT*/VexGuestX86State* vex_state ) { Int r; UInt tag; @@ -1399,6 +1442,9 @@ void LibVEX_GuestX86_put_x87 ( /*IN*/UChar* x87_state, UInt tagw = x87->env[FP_ENV_TAG]; UInt fpucw = x87->env[FP_ENV_CTRL]; UInt c3210 = x87->env[FP_ENV_STAT] & 0x4700; + VexEmWarn ew; + UInt fprtz; + ULong pair; /* Copy registers and tags */ for (r = 0; r < 8; r++) { @@ -1417,11 +1463,19 @@ void LibVEX_GuestX86_put_x87 ( /*IN*/UChar* x87_state, /* stack pointer */ vex_state->guest_FTOP = ftop; - /* control word */ - vex_state->guest_FPUCW = fpucw; - /* status word */ vex_state->guest_FC3210 = c3210; + + /* handle the control word, setting FPRTZ and detecting any + emulation warnings. */ + pair = x86h_check_fldcw ( (UInt)fpucw ); + fprtz = (UInt)pair; + ew = (VexEmWarn)(pair >> 32); + + vex_state->guest_FPRTZ = fprtz & 1; + + /* emulation warnings --> caller */ + return ew; } @@ -1441,8 +1495,9 @@ void LibVEX_GuestX86_get_x87 ( /*IN*/VexGuestX86State* vex_state, x87->env[i] = 0; x87->env[1] = x87->env[3] = x87->env[5] = x87->env[13] = 0xFFFF; - x87->env[FP_ENV_CTRL] = (UShort)( vex_state->guest_FPUCW ); x87->env[FP_ENV_STAT] = ((ftop & 7) << 11) | (c3210 & 0x4700); + x87->env[FP_ENV_CTRL] + = (UShort)x86h_create_fpucw( vex_state->guest_FPRTZ ); tagw = 0; for (r = 0; r < 8; r++) { @@ -1528,9 +1583,7 @@ void LibVEX_GuestX86_initialise ( /*OUT*/VexGuestX86State* vex_state ) vex_state->guest_FPTAG[i] = 0; /* empty */ vex_state->guest_FPREG[i] = 0; /* IEEE754 64-bit zero */ } - /* The default setting: all fp exceptions masked, rounding to - nearest, precision to 64 bits */ - vex_state->guest_FPUCW = 0x03F7; + vex_state->guest_FPRTZ = 0; /* round to nearest */ vex_state->guest_FC3210 = 0; vex_state->guest_CS = 0; @@ -1539,6 +1592,8 @@ void LibVEX_GuestX86_initialise ( /*OUT*/VexGuestX86State* vex_state ) vex_state->guest_FS = 0; vex_state->guest_GS = 0; vex_state->guest_SS = 0; + + vex_state->guest_EMWARN = EmWarn_NONE; } @@ -2434,7 +2489,7 @@ VexGuestLayout /* Describe any sections to be regarded by Memcheck as 'always-defined'. */ - .n_alwaysDefd = 15, + .n_alwaysDefd = 16, /* flags thunk: OP and NDEP are always defd, whereas DEP1 and DEP2 have to be tracked. See detailed comment in gdefs.h on meaning of thunk fields. */ @@ -2447,14 +2502,15 @@ VexGuestLayout /* 4 */ ALWAYSDEFD(guest_EIP), /* 5 */ ALWAYSDEFD(guest_FTOP), /* 6 */ ALWAYSDEFD(guest_FPTAG), - /* 7 */ ALWAYSDEFD(guest_FPUCW), + /* 7 */ ALWAYSDEFD(guest_FPRTZ), /* 8 */ ALWAYSDEFD(guest_FC3210), /* 9 */ ALWAYSDEFD(guest_CS), /* 10 */ ALWAYSDEFD(guest_DS), /* 11 */ ALWAYSDEFD(guest_ES), /* 12 */ ALWAYSDEFD(guest_FS), /* 13 */ ALWAYSDEFD(guest_GS), - /* 14 */ ALWAYSDEFD(guest_SS) + /* 14 */ ALWAYSDEFD(guest_SS), + /* 15 */ ALWAYSDEFD(guest_EMWARN) } }; diff --git a/VEX/priv/guest-x86/toIR.c b/VEX/priv/guest-x86/toIR.c index 3c33b84259..685dd8ab4c 100644 --- a/VEX/priv/guest-x86/toIR.c +++ b/VEX/priv/guest-x86/toIR.c @@ -120,7 +120,9 @@ static IRBB* irbb; #define OFFB_IDFLAG offsetof(VexGuestX86State,guest_IDFLAG) #define OFFB_FTOP offsetof(VexGuestX86State,guest_FTOP) #define OFFB_FC3210 offsetof(VexGuestX86State,guest_FC3210) -#define OFFB_FPUCW offsetof(VexGuestX86State,guest_FPUCW) +#define OFFB_FPRTZ offsetof(VexGuestX86State,guest_FPRTZ) + +#define OFFB_EMWARN offsetof(VexGuestX86State,guest_EMWARN) /*------------------------------------------------------------*/ @@ -1433,11 +1435,13 @@ void jcc_01( X86Condcode cond, Addr32 d32_false, Addr32 d32_true ) condPos = positiveIse_X86Condcode ( cond, &invert ); if (invert) { stmt( IRStmt_Exit( mk_x86g_calculate_condition(condPos), + Ijk_Boring, IRConst_U32(d32_false) ) ); irbb->next = mkU32(d32_true); irbb->jumpkind = Ijk_Boring; } else { stmt( IRStmt_Exit( mk_x86g_calculate_condition(condPos), + Ijk_Boring, IRConst_U32(d32_true) ) ); irbb->next = mkU32(d32_false); irbb->jumpkind = Ijk_Boring; @@ -3230,6 +3234,7 @@ void dis_REP_op ( X86Condcode cond, //uInstr2 (cb, JIFZ, 4, TempReg, tc, Literal, 0); //uLiteral(cb, eip_next); stmt( IRStmt_Exit( binop(Iop_CmpEQ32,mkexpr(tc),mkU32(0)), + Ijk_Boring, IRConst_U32(eip_next) ) ); //uInstr1 (cb, DEC, 4, TempReg, tc); @@ -3243,6 +3248,7 @@ void dis_REP_op ( X86Condcode cond, jmp_lit(Ijk_Boring,eip); } else { stmt( IRStmt_Exit( mk_x86g_calculate_condition(cond), + Ijk_Boring, IRConst_U32(eip) ) ); jmp_lit(Ijk_Boring,eip_next); } @@ -3349,6 +3355,13 @@ UInt dis_imul_I_E_G ( UChar sorb, /* --- Helper functions for dealing with the register stack. --- */ +/* --- Set the emulation-warning pseudo-register. --- */ + +static void put_emwarn ( IRExpr* e /* :: Ity_I32 */ ) +{ + stmt( IRStmt_Put( OFFB_EMWARN, e ) ); +} + /* --- Produce an IRExpr* denoting a 64-bit QNaN. --- */ static IRExpr* mkQNaN64 ( void ) @@ -3360,7 +3373,7 @@ static IRExpr* mkQNaN64 ( void ) return IRExpr_Const(IRConst_F64i(0x7FF8000000000000ULL)); } -/* --------- Get/set the top-of-stack pointer. --------- */ +/* --------- Get/put the top-of-stack pointer. --------- */ static IRExpr* get_ftop ( void ) { @@ -3372,7 +3385,7 @@ static void put_ftop ( IRExpr* e ) stmt( IRStmt_Put( OFFB_FTOP, e ) ); } -/* --------- Get/set the C3210 bits of the control word. --------- */ +/* --------- Get/put the C3210 bits. --------- */ static IRExpr* get_C3210 ( void ) { @@ -3384,30 +3397,32 @@ static void put_C3210 ( IRExpr* e ) stmt( IRStmt_Put( OFFB_FC3210, e ) ); } -/* --------- Get/set the FPU control word. --------- */ -/* Note, IA32 has this as a 16-bit value, so fstcw/fldcw need to cast - to/from 16 bits. Here we represent it in 32 bits. */ -static IRExpr* /* :: Ity_I32 */ get_fpucw ( void ) +/* --------- Get/put the FPU rounding mode. --------- */ +static IRExpr* /* :: Ity_I32 */ get_fprtz ( void ) { - return IRExpr_Get( OFFB_FPUCW, Ity_I32 ); + return IRExpr_Get( OFFB_FPRTZ, Ity_I32 ); } -static void put_fpucw ( IRExpr* /* :: Ity_I32 */ e ) +static void put_fprtz ( IRExpr* /* :: Ity_I32 */ e ) { - stmt( IRStmt_Put( OFFB_FPUCW, e ) ); + stmt( IRStmt_Put( OFFB_FPRTZ, e ) ); } -/* --------- Get the FPU rounding mode from the CW. --------- */ +/* --------- Synthesise a 2-bit FPU rounding mode. --------- */ /* Produces a value in 0 .. 3, which is encoded as per the type - IRRoundingMode. On IA32 the relevant value is precisely bits 11 - and 10 of the control word. + IRRoundingMode. Since 11b means round-to-zero and 00b means + round-to-nearest, this means the value can be synthesised from + bit 0 of guest_FPRTZ as (w<<31) >>s 31, where w = guest_FPRTZ. */ static IRExpr* /* :: Ity_I32 */ get_roundingmode ( void ) { - return binop( Iop_And32, - binop(Iop_Shr32, get_fpucw(), mkU8(10)), - mkU32(3) ); + return + binop( Iop_And32, + binop( Iop_Sar32, + binop(Iop_Shl32, get_fprtz(), mkU8(31)), + mkU8(31) ), + mkU32(3) ); } @@ -3801,14 +3816,62 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta ) fp_pop(); break; - case 5: /* FLDCW */ + case 5: {/* FLDCW */ + /* The only thing we observe in the control word is the + rounding mode, and even that only two values of. + Therefore, pass the 16-bit value (x87 native-format + control word) to a clean helper, getting back a + 64-bit value, the lower half of which is the FPRTZ + value to store, and the upper half of which is the + emulation-warning token which may be generated. + */ + /* ULong x86h_check_fldcw ( UInt ); */ + IRTemp t64 = newTemp(Ity_I64); + IRTemp ew = newTemp(Ity_I32); DIP("fldcw %s", dis_buf); - put_fpucw( unop(Iop_16Uto32, loadLE(Ity_I16, mkexpr(addr))) ); + assign( t64, mkIRExprCCall( + Ity_I64, 0/*regparms*/, + "x86h_check_fldcw", + &x86h_check_fldcw, + mkIRExprVec_1( + unop( Iop_16Uto32, + loadLE(Ity_I16, mkexpr(addr))) + ) + ) + ); + + put_fprtz( unop(Iop_64to32, mkexpr(t64)) ); + assign( ew, unop(Iop_64HIto32, mkexpr(t64) ) ); + put_emwarn( mkexpr(ew) ); + /* Finally, if an emulation warning was reported, + side-exit to the next insn, reporting the warning, + so that Valgrind's dispatcher sees the warning. */ + stmt( + IRStmt_Exit( + binop(Iop_CmpNE32, mkexpr(ew), mkU32(0)), + Ijk_EmWarn, + IRConst_U32( ((Addr32)guest_eip_bbstart)+delta) + ) + ); break; + } case 7: /* FNSTCW */ + /* Fake up a native x87 FPU control word. The only + thing it depends on is FPRTZ[0], so call a clean + helper to cook it up. */ + /* UInt x86h_create_fpucw ( UInt fptrz ) */ DIP("fnstcw %s", dis_buf); - storeLE(mkexpr(addr), unop(Iop_32to16, get_fpucw())); + storeLE( + mkexpr(addr), + unop( Iop_32to16, + mkIRExprCCall( + Ity_I32, 0/*regp*/, + "x86h_create_fpucw", &x86h_create_fpucw, + mkIRExprVec_1( get_fprtz() ) + ) + ) + ); break; default: @@ -4367,21 +4430,24 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta ) break; case 4: { /* FRSTOR m108 */ - /* Uses dirty helper: x86g_do_FRSTOR ( VexGuestX86State*, UInt ) */ - IRDirty* d = unsafeIRDirty_0_N ( - 0/*regparms*/, - "x86g_dirtyhelper_FRSTOR", - &x86g_dirtyhelper_FRSTOR, - mkIRExprVec_1( mkexpr(addr) ) - ); + /* 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->nFxState = 5; d->fxState[0].fx = Ifx_Write; d->fxState[0].offset = offsetof(VexGuestX86State,guest_FTOP); @@ -4396,7 +4462,7 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta ) d->fxState[2].size = 8 * sizeof(UChar); d->fxState[3].fx = Ifx_Write; - d->fxState[3].offset = offsetof(VexGuestX86State,guest_FPUCW); + d->fxState[3].offset = offsetof(VexGuestX86State,guest_FPRTZ); d->fxState[3].size = sizeof(UInt); d->fxState[4].fx = Ifx_Write; @@ -4405,12 +4471,26 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta ) 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", dis_buf); break; } case 6: { /* FNSAVE m108 */ - /* Uses dirty helper: x86g_do_FSAVE ( VexGuestX86State*, UInt ) */ + /* Uses dirty helper: + void x86g_do_FSAVE ( VexGuestX86State*, UInt ) */ IRDirty* d = unsafeIRDirty_0_N ( 0/*regparms*/, "x86g_dirtyhelper_FSAVE", @@ -4424,7 +4504,7 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta ) d->mSize = 108; /* declare we're reading guest state */ - d->nFxState = 5; + d->nFxState = 5; d->fxState[0].fx = Ifx_Read; d->fxState[0].offset = offsetof(VexGuestX86State,guest_FTOP); @@ -4439,7 +4519,7 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta ) d->fxState[2].size = 8 * sizeof(UChar); d->fxState[3].fx = Ifx_Read; - d->fxState[3].offset = offsetof(VexGuestX86State,guest_FPUCW); + d->fxState[3].offset = offsetof(VexGuestX86State,guest_FPRTZ); d->fxState[3].size = sizeof(UInt); d->fxState[4].fx = Ifx_Read; @@ -4955,7 +5035,7 @@ UInt dis_MMX ( Bool* decode_ok, UChar sorb, Int sz, UInt delta ) } else { IRTemp addr = disAMode( &len, sorb, delta, dis_buf ); delta += len; - storeLE( mkexpr(addr), getMMXReg(gregOfRM(modrm)) ); + storeLE( mkexpr(addr), getMMXReg(gregOfRM(modrm)) ); DIP("mov(nt)q %s, %s\n", nameMMXReg(gregOfRM(modrm)), dis_buf); } @@ -8256,6 +8336,7 @@ static DisResult disInstr ( /*IN*/ Bool resteerOK, binop(mkSizedOp(ty,Iop_CmpEQ8), getIReg(sz,R_ECX), mkU(ty,0)), + Ijk_Boring, IRConst_U32(d32)) ); diff --git a/VEX/priv/host-x86/hdefs.c b/VEX/priv/host-x86/hdefs.c index 70e9f8af61..97dd66cc63 100644 --- a/VEX/priv/host-x86/hdefs.c +++ b/VEX/priv/host-x86/hdefs.c @@ -563,8 +563,6 @@ X86Instr* X86Instr_Goto ( IRJumpKind jk, X86CondCode cond, X86RI* dst ) { i->Xin.Goto.cond = cond; i->Xin.Goto.dst = dst; i->Xin.Goto.jk = jk; - /* non-Boring conditional jumps are not allowed. */ - vassert(jk == Ijk_Boring || cond == Xcc_ALWAYS); return i; } X86Instr* X86Instr_CMov32 ( X86CondCode cond, X86RM* src, HReg dst ) { @@ -749,23 +747,21 @@ void ppX86Instr ( X86Instr* i ) { vex_printf("0x%x", i->Xin.Call.target); break; case Xin_Goto: - if (i->Xin.Goto.jk == Ijk_ClientReq - || i->Xin.Goto.jk == Ijk_Syscall - || i->Xin.Goto.jk == Ijk_Yield) { + if (i->Xin.Goto.cond != Xcc_ALWAYS) { + vex_printf("if (%%eflags.%s) { ", + showX86CondCode(i->Xin.Goto.cond)); + } + if (i->Xin.Goto.jk != Ijk_Boring) { vex_printf("movl $"); ppIRJumpKind(i->Xin.Goto.jk); - vex_printf(", %%ebp ; "); - } - if (i->Xin.Goto.cond == Xcc_ALWAYS) { - vex_printf("movl "); - ppX86RI(i->Xin.Goto.dst); - vex_printf(",%%eax ; ret"); - } else { - vex_printf("if (%%eflags.%s) { movl ", - showX86CondCode(i->Xin.Goto.cond)); - ppX86RI(i->Xin.Goto.dst); - vex_printf(",%%eax ; ret }"); + vex_printf(",%%ebp ; "); } + vex_printf("movl "); + ppX86RI(i->Xin.Goto.dst); + vex_printf(",%%eax ; ret"); + if (i->Xin.Goto.cond != Xcc_ALWAYS) { + vex_printf(" }"); + } return; case Xin_CMov32: vex_printf("cmov%s ", showX86CondCode(i->Xin.CMov32.cond)); @@ -953,9 +949,7 @@ void getRegUsage_X86Instr (HRegUsage* u, X86Instr* i) case Xin_Goto: addRegUsage_X86RI(u, i->Xin.Goto.dst); addHRegUse(u, HRmWrite, hregX86_EAX()); - if (i->Xin.Goto.jk == Ijk_ClientReq - || i->Xin.Goto.jk == Ijk_Syscall - || i->Xin.Goto.jk == Ijk_Yield) + if (i->Xin.Goto.jk != Ijk_Boring) addHRegUse(u, HRmWrite, hregX86_EBP()); return; case Xin_CMov32: @@ -1719,65 +1713,72 @@ Int emit_X86Instr ( UChar* buf, Int nbuf, X86Instr* i ) goto done; case Xin_Goto: - /* If a non-boring unconditional jump, set %ebp (the guest state - pointer) appropriately. */ - if (i->Xin.Goto.cond == Xcc_ALWAYS - && (i->Xin.Goto.jk == Ijk_ClientReq - || i->Xin.Goto.jk == Ijk_Syscall - || i->Xin.Goto.jk == Ijk_Yield)) { - /* movl $magic_number, %ebp */ - *p++ = 0xBD; - switch (i->Xin.Goto.jk) { - case Ijk_ClientReq: - p = emit32(p, VEX_TRC_JMP_CLIENTREQ); break; - case Ijk_Syscall: - p = emit32(p, VEX_TRC_JMP_SYSCALL); break; - case Ijk_Yield: - p = emit32(p, VEX_TRC_JMP_YIELD); break; - default: - ppIRJumpKind(i->Xin.Goto.jk); - vpanic("emit_X86Instr.Xin_Goto: unknown jump kind"); - } + /* Use ptmp for backpatching conditional jumps. */ + ptmp = NULL; + + /* First off, if this is conditional, create a conditional + jump over the rest of it. */ + if (i->Xin.Goto.cond != Xcc_ALWAYS) { + /* jmp fwds if !condition */ + *p++ = 0x70 + (i->Xin.Goto.cond ^ 1); + ptmp = p; /* fill in this bit later */ + *p++ = 0; /* # of bytes to jump over; don't know how many yet. */ + } + + /* If a non-boring, set %ebp (the guest state pointer) + appropriately. */ + /* movl $magic_number, %ebp */ + switch (i->Xin.Goto.jk) { + case Ijk_ClientReq: + *p++ = 0xBD; + p = emit32(p, VEX_TRC_JMP_CLIENTREQ); break; + case Ijk_Syscall: + *p++ = 0xBD; + p = emit32(p, VEX_TRC_JMP_SYSCALL); break; + case Ijk_Yield: + *p++ = 0xBD; + p = emit32(p, VEX_TRC_JMP_YIELD); break; + case Ijk_EmWarn: + *p++ = 0xBD; + p = emit32(p, VEX_TRC_JMP_EMWARN); break; + case Ijk_Ret: + case Ijk_Call: + case Ijk_Boring: + break; + default: + ppIRJumpKind(i->Xin.Goto.jk); + vpanic("emit_X86Instr.Xin_Goto: unknown jump kind"); } - /* unconditional jump to immediate */ - if (i->Xin.Goto.cond == Xcc_ALWAYS - && i->Xin.Goto.dst->tag == Xri_Imm) { + + /* Get the destination address into %eax */ + if (i->Xin.Goto.dst->tag == Xri_Imm) { /* movl $immediate, %eax ; ret */ *p++ = 0xB8; p = emit32(p, i->Xin.Goto.dst->Xri.Imm.imm32); - *p++ = 0xC3; - goto done; - } - /* unconditional jump to reg */ - if (i->Xin.Goto.cond == Xcc_ALWAYS - && i->Xin.Goto.dst->tag == Xri_Reg) { + } else { + vassert(i->Xin.Goto.dst->tag == Xri_Reg); /* movl %reg, %eax ; ret */ if (i->Xin.Goto.dst->Xri.Reg.reg != hregX86_EAX()) { *p++ = 0x89; p = doAMode_R(p, i->Xin.Goto.dst->Xri.Reg.reg, hregX86_EAX()); } - *p++ = 0xC3; - goto done; } - /* conditional jump to immediate */ - if (i->Xin.Goto.cond != Xcc_ALWAYS - && i->Xin.Goto.dst->tag == Xri_Imm) { - vassert(i->Xin.Goto.jk == Ijk_Boring); - /* jmp fwds if !condition */ - *p++ = 0x70 + (i->Xin.Goto.cond ^ 1); - *p++ = 6; /* # of bytes in the next bit */ - /* movl $immediate, %eax ; ret */ - *p++ = 0xB8; - p = emit32(p, i->Xin.Goto.dst->Xri.Imm.imm32); - *p++ = 0xC3; - goto done; + + /* ret */ + *p++ = 0xC3; + + /* Fix up the conditional jump, if there was one. */ + if (i->Xin.Goto.cond != Xcc_ALWAYS) { + Int delta = p - ptmp; + vassert(delta > 0 && delta < 20); + *ptmp = (UChar)(delta-1); } - break; + goto done; case Xin_CMov32: vassert(i->Xin.CMov32.cond != Xcc_ALWAYS); #if 0 - /* This generates cmov, which is illegal on P5. */ + /* This generates cmov, which is illegal on P54/P55. */ *p++ = 0x0F; *p++ = 0x40 + i->Xin.CMov32.cond; if (i->Xin.CMov32.src->tag == Xrm_Reg) { diff --git a/VEX/priv/host-x86/isel.c b/VEX/priv/host-x86/isel.c index 84a859868a..a912875684 100644 --- a/VEX/priv/host-x86/isel.c +++ b/VEX/priv/host-x86/isel.c @@ -2555,7 +2555,7 @@ static void iselStmt ( ISelEnv* env, IRStmt* stmt ) vpanic("isel_x86: Ist_Exit: dst is not a 32-bit value"); dst = iselIntExpr_RI(env, IRExpr_Const(stmt->Ist.Exit.dst)); cc = iselCondCode(env,stmt->Ist.Exit.guard); - addInstr(env, X86Instr_Goto(Ijk_Boring, cc, dst)); + addInstr(env, X86Instr_Goto(stmt->Ist.Exit.jk, cc, dst)); return; } diff --git a/VEX/priv/ir/irdefs.c b/VEX/priv/ir/irdefs.c index d915bb4899..8105796254 100644 --- a/VEX/priv/ir/irdefs.c +++ b/VEX/priv/ir/irdefs.c @@ -346,6 +346,20 @@ void ppIRDirty ( IRDirty* d ) vex_printf(")"); } +void ppIRJumpKind ( IRJumpKind kind ) +{ + switch (kind) { + case Ijk_Boring: vex_printf("Boring"); break; + case Ijk_Call: vex_printf("Call"); break; + case Ijk_Ret: vex_printf("Return"); break; + case Ijk_ClientReq: vex_printf("ClientReq"); break; + case Ijk_Syscall: vex_printf("Syscall"); break; + case Ijk_Yield: vex_printf("Yield"); break; + case Ijk_EmWarn: vex_printf("EmWarn"); break; + default: vpanic("ppIRJumpKind"); + } +} + void ppIRStmt ( IRStmt* s ) { switch (s->tag) { @@ -378,7 +392,9 @@ void ppIRStmt ( IRStmt* s ) case Ist_Exit: vex_printf( "if (" ); ppIRExpr(s->Ist.Exit.guard); - vex_printf( ") goto "); + vex_printf( ") goto {"); + ppIRJumpKind(s->Ist.Exit.jk); + vex_printf("} "); ppIRConst(s->Ist.Exit.dst); break; default: @@ -386,19 +402,6 @@ void ppIRStmt ( IRStmt* s ) } } -void ppIRJumpKind ( IRJumpKind kind ) -{ - switch (kind) { - case Ijk_Boring: vex_printf("Boring"); break; - case Ijk_Call: vex_printf("Call"); break; - case Ijk_Ret: vex_printf("Return"); break; - case Ijk_ClientReq: vex_printf("ClientReq"); break; - case Ijk_Syscall: vex_printf("Syscall"); break; - case Ijk_Yield: vex_printf("Yield"); break; - default: vpanic("ppIRJumpKind"); - } -} - void ppIRTypeEnv ( IRTypeEnv* env ) { UInt i; for (i = 0; i < env->types_used; i++) { @@ -712,10 +715,11 @@ IRStmt* IRStmt_Dirty ( IRDirty* d ) s->Ist.Dirty.details = d; return s; } -IRStmt* IRStmt_Exit ( IRExpr* guard, IRConst* dst ) { +IRStmt* IRStmt_Exit ( IRExpr* guard, IRJumpKind jk, IRConst* dst ) { IRStmt* s = LibVEX_Alloc(sizeof(IRStmt)); s->tag = Ist_Exit; s->Ist.Exit.guard = guard; + s->Ist.Exit.jk = jk; s->Ist.Exit.dst = dst; return s; } @@ -885,7 +889,8 @@ IRStmt* dopyIRStmt ( IRStmt* s ) case Ist_Dirty: return IRStmt_Dirty(dopyIRDirty(s->Ist.Dirty.details)); case Ist_Exit: - return IRStmt_Exit(dopyIRExpr(s->Ist.Exit.guard), + return IRStmt_Exit(dopyIRExpr(s->Ist.Exit.guard), + s->Ist.Exit.jk, dopyIRConst(s->Ist.Exit.dst)); default: vpanic("dopyIRStmt"); diff --git a/VEX/priv/ir/iropt.c b/VEX/priv/ir/iropt.c index ef7c65220f..4e76d65a05 100644 --- a/VEX/priv/ir/iropt.c +++ b/VEX/priv/ir/iropt.c @@ -328,7 +328,8 @@ static void flatten_Stmt ( IRBB* bb, IRStmt* st ) break; case Ist_Exit: e1 = flatten_Expr(bb, st->Ist.Exit.guard); - addStmtToIRBB(bb, IRStmt_Exit(e1, st->Ist.Exit.dst)); + addStmtToIRBB(bb, IRStmt_Exit(e1, st->Ist.Exit.jk, + st->Ist.Exit.dst)); break; default: vex_printf("\n"); @@ -859,7 +860,7 @@ static IRStmt* subst_and_fold_Stmt ( IRExpr** env, IRStmt* st ) vex_printf("vex iropt: IRStmt_Exit became unconditional\n"); } } - return IRStmt_Exit(fcond,st->Ist.Exit.dst); + return IRStmt_Exit(fcond, st->Ist.Exit.jk, st->Ist.Exit.dst); } default: @@ -1737,6 +1738,7 @@ static IRStmt* tbSubst_Stmt ( TmpInfo** env, IRStmt* st ) case Ist_Exit: return IRStmt_Exit( tbSubst_Expr(env, st->Ist.Exit.guard), + st->Ist.Exit.jk, st->Ist.Exit.dst ); case Ist_Dirty: diff --git a/VEX/priv/main/vex_main.c b/VEX/priv/main/vex_main.c index 062574cf34..8bf93c9991 100644 --- a/VEX/priv/main/vex_main.c +++ b/VEX/priv/main/vex_main.c @@ -34,6 +34,7 @@ */ #include "libvex.h" +#include "libvex_emwarn.h" #include "libvex_guest_x86.h" #include "libvex_guest_arm.h" @@ -441,6 +442,27 @@ TranslateResult LibVEX_Translate ( } +/* --------- Emulation warnings. --------- */ + +HChar* LibVEX_EmWarn_string ( VexEmWarn ew ) +{ + switch (ew) { + case EmWarn_NONE: + return "none"; + case EmWarn_X86_x87exns: + return "Unmasking x87 FP exceptions"; + case EmWarn_X86_x87rounding: + return "Selection of unsupported x87 FP rounding mode (+inf/-inf)"; + case EmWarn_X86_x87precision: + return "Selection of non-80-bit x87 FP precision"; + case EmWarn_X86_sseExns: + return "Unmasking SSE FP exceptionss"; + case EmWarn_X86_sseRounding: + return "Selection of unsupported SSE FP rounding mode"; + default: + vpanic("LibVEX_EmWarn_string: unknown warning"); + } +} /*---------------------------------------------------------------*/ /*--- end main/vex_main.c ---*/ diff --git a/VEX/pub/libvex_emwarn.h b/VEX/pub/libvex_emwarn.h new file mode 100644 index 0000000000..1fd7e37ce1 --- /dev/null +++ b/VEX/pub/libvex_emwarn.h @@ -0,0 +1,84 @@ + +/*---------------------------------------------------------------*/ +/*--- ---*/ +/*--- This file (libvex_emwarn.h) is ---*/ +/*--- Copyright (c) 2004 OpenWorks LLP. All rights reserved. ---*/ +/*--- ---*/ +/*---------------------------------------------------------------*/ + +/* + This file is part of LibVEX, a library for dynamic binary + instrumentation and translation. + + Copyright (C) 2004 OpenWorks, LLP. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; Version 2 dated June 1991 of the + license. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or liability + for damages. See the GNU General Public License for more details. + + Neither the names of the U.S. Department of Energy nor the + University of California nor the names of its contributors may be + used to endorse or promote products derived from this software + without prior written permission. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +*/ + +#ifndef __LIBVEX_EMWARN_H +#define __LIBVEX_EMWARN_H + + +/* VEX can sometimes generate code which returns to the dispatcher + with the guest state pointer set to VEX_TRC_JMP_EMWARN. This means + that VEX is trying to warn Valgrind that it is doing imprecise + emulation in some sense. The guest's pseudo-register + "guest_EMWARN" will hold a value of type VexEmWarn, which describes + the nature of the warning. Currently the limitations that are + warned about apply primarily to floating point support. + + All guest states should have a 32-bit (UInt) guest_EMWARN pseudo- + register, that emulation warnings can be written in to. + + Note that guest_EMWARN only carries a valid value at the jump + marked as VEX_TRC_JMP_EMWARN. You can't assume it will continue to + carry a valid value from any amount of time after the jump. +*/ + +typedef + enum { + /* no warning indicated */ + EmWarn_NONE=0, + /* unmasking x87 FP exceptions is not supported */ + EmWarn_X86_x87exns, + /* use of unsupported x87 FP rounding mode */ + EmWarn_X86_x87rounding, + /* change of x87 FP precision away from 64-bit (mantissa) */ + EmWarn_X86_x87precision, + /* unmasking SSE FP exceptions is not supported */ + EmWarn_X86_sseExns, + /* use of unsupported SSE FP rounding mode */ + EmWarn_X86_sseRounding, + + EmWarn_NUMBER + } + VexEmWarn; + + +/* Produces a short string describing the warning. */ +extern HChar* LibVEX_EmWarn_string ( VexEmWarn ); + + +#endif /* ndef __LIBVEX_EMWARN_H */ + +/*---------------------------------------------------------------*/ +/*--- libvex_emwarn.h ---*/ +/*---------------------------------------------------------------*/ diff --git a/VEX/pub/libvex_guest_amd64.h b/VEX/pub/libvex_guest_amd64.h index 68cd0c94c1..079fb43b4d 100644 --- a/VEX/pub/libvex_guest_amd64.h +++ b/VEX/pub/libvex_guest_amd64.h @@ -74,6 +74,8 @@ typedef segment registers */ + /* Emulation warnings */ + UInt guest_EMWARN; /* Padding to make it have an 8-aligned size */ /* UInt padding; */ } diff --git a/VEX/pub/libvex_guest_arm.h b/VEX/pub/libvex_guest_arm.h index 4ab746377d..9969605e3b 100644 --- a/VEX/pub/libvex_guest_arm.h +++ b/VEX/pub/libvex_guest_arm.h @@ -78,6 +78,9 @@ typedef UInt guest_CC_DEP1; UInt guest_CC_DEP2; + /* Emulation warnings */ + UInt guest_EMWARN; + /* Padding to make it have an 8-aligned size */ /* UInt padding; */ } diff --git a/VEX/pub/libvex_guest_x86.h b/VEX/pub/libvex_guest_x86.h index bbdb8995fe..ac7dfa2417 100644 --- a/VEX/pub/libvex_guest_x86.h +++ b/VEX/pub/libvex_guest_x86.h @@ -37,6 +37,8 @@ #define __LIBVEX_PUB_GUEST_X86_H #include "libvex_basictypes.h" +#include "libvex_emwarn.h" + /*---------------------------------------------------------------*/ /*--- Vex's representation of the x86 CPU state. ---*/ @@ -89,14 +91,15 @@ * fst from st(0) to st(i) does not take an overflow fault even if the destination is already full. - FPUCW[15:0] is the FPU's control word. FPUCW[31:16] is unused. + FPRTZ[0] is the FPU's notional rounding mode -- 0 "to nearest" (the + default), 1 "to zero". FPRTZ[31:1] is unused. Round to + +infinity/-infinity is not supported. FC3210 contains the C3, C2, C1 and C0 bits in the same place they are in the FPU's status word. (bits 14, 10, 9, 8 respectively). All other bits should be zero. The relevant mask to select just those bits is 0x4700. To select C3, C2 and C0 only, the mask is - 0x4500. -*/ + 0x4500. */ typedef struct { @@ -123,7 +126,7 @@ typedef UInt guest_FTOP; ULong guest_FPREG[8]; UChar guest_FPTAG[8]; - UInt guest_FPUCW; + UInt guest_FPRTZ; UInt guest_FC3210; /* Segment registers. */ UShort guest_CS; @@ -132,8 +135,10 @@ typedef UShort guest_FS; UShort guest_GS; UShort guest_SS; + /* Emulation warnings */ + UInt guest_EMWARN; /* Padding to make it have an 8-aligned size */ - UInt padding; + /* UInt padding; */ } VexGuestX86State; @@ -154,10 +159,12 @@ void LibVEX_GuestX86_initialise ( /*OUT*/VexGuestX86State* vex_state ); /* Convert a saved x87 FPU image (as created by fsave) and write it into the supplied VexGuestX86State structure. The non-FP parts of - said structure are left unchanged. + said structure are left unchanged. May return an emulation warning + value. */ extern -void LibVEX_GuestX86_put_x87 ( /*IN*/UChar* x87_state, +VexEmWarn + LibVEX_GuestX86_put_x87 ( /*IN*/UChar* x87_state, /*OUT*/VexGuestX86State* vex_state ); /* Extract from the supplied VexGuestX86State structure, an x87 FPU diff --git a/VEX/pub/libvex_ir.h b/VEX/pub/libvex_ir.h index aa847a3f00..c37addecc6 100644 --- a/VEX/pub/libvex_ir.h +++ b/VEX/pub/libvex_ir.h @@ -496,6 +496,26 @@ inline static Bool isAtom ( IRExpr* e ) { } +/* ------------------ Jump kinds ------------------ */ + +/* This describes hints which can be passed to the dispatcher at guest + control-flow transfer points. +*/ +typedef + enum { + Ijk_Boring=0x14000, /* not interesting; just goto next */ + Ijk_Call, /* guest is doing a call */ + Ijk_Ret, /* guest is doing a return */ + Ijk_ClientReq, /* do guest client req before continuing */ + Ijk_Syscall, /* do guest syscall before continuing */ + Ijk_Yield, /* client is yielding to thread scheduler */ + Ijk_EmWarn /* report emulation warning before continuing */ + } + IRJumpKind; + +extern void ppIRJumpKind ( IRJumpKind ); + + /* ------------------ Dirty helper calls ------------------ */ /* A dirty call is a flexible mechanism for calling a helper function @@ -638,8 +658,9 @@ typedef IRDirty* details; } Dirty; struct { - IRExpr* guard; - IRConst* dst; + IRExpr* guard; + IRJumpKind jk; + IRConst* dst; } Exit; } Ist; } @@ -651,7 +672,7 @@ extern IRStmt* IRStmt_PutI ( IRArray* descr, IRExpr* ix, Int bias, extern IRStmt* IRStmt_Tmp ( IRTemp tmp, IRExpr* data ); extern IRStmt* IRStmt_STle ( IRExpr* addr, IRExpr* data ); extern IRStmt* IRStmt_Dirty ( IRDirty* details ); -extern IRStmt* IRStmt_Exit ( IRExpr* guard, IRConst* dst ); +extern IRStmt* IRStmt_Exit ( IRExpr* guard, IRJumpKind jk, IRConst* dst ); extern IRStmt* dopyIRStmt ( IRStmt* ); @@ -660,25 +681,6 @@ extern void ppIRStmt ( IRStmt* ); /* ------------------ Basic Blocks ------------------ */ -/* This describes the unconditional jumps which implicitly happen at - the end of each basic block. Conditional jumps -- which can only - be done with the IRStmt_Exit statement -- are implicitly of the - Ijk_Boring kind. */ - -typedef - enum { - Ijk_Boring=0x14000, /* not interesting; just goto next */ - Ijk_Call, /* guest is doing a call */ - Ijk_Ret, /* guest is doing a return */ - Ijk_ClientReq, /* do guest client req before continuing */ - Ijk_Syscall, /* do guest syscall before continuing */ - Ijk_Yield /* client is yielding to thread scheduler */ - } - IRJumpKind; - -extern void ppIRJumpKind ( IRJumpKind ); - - /* A bunch of statements, expressions, etc, are incomplete without an environment indicating the type of each IRTemp. So this provides one. IR temporaries are really just unsigned ints and so this @@ -723,7 +725,7 @@ extern IRBB* dopyIRBB ( IRBB* ); extern void ppIRBB ( IRBB* ); -extern void addStmtToIRBB ( IRBB*, IRStmt* ); +extern void addStmtToIRBB ( IRBB*, IRStmt* ); /*---------------------------------------------------------------*/ diff --git a/VEX/pub/libvex_trc_values.h b/VEX/pub/libvex_trc_values.h index 93088f97ff..9172b8544e 100644 --- a/VEX/pub/libvex_trc_values.h +++ b/VEX/pub/libvex_trc_values.h @@ -44,6 +44,8 @@ This file may get included in assembly code, so do not put C-specific constructs in it. */ +#define VEX_TRC_JMP_EMWARN 17 /* deliver emulation warning before + continuing */ #define VEX_TRC_JMP_SYSCALL 19 /* do a system call before continuing */ #define VEX_TRC_JMP_CLIENTREQ 23 /* do a client req before continuing */ #define VEX_TRC_JMP_YIELD 27 /* yield to thread sched