blob: 7f492e899c26327b2360855364a7e9b6fe95ee2d [file] [log] [blame]
/*--------------------------------------------------------------------*/
/*--- begin guest_x86_toIR.c ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Valgrind, a dynamic binary instrumentation
framework.
Copyright (C) 2004-2013 OpenWorks LLP
info@open-works.net
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; either version 2 of the
License, or (at your option) any later version.
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. See the GNU
General Public License for more details.
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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
The GNU General Public License is contained in the file COPYING.
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.
*/
/* Translates x86 code to IR. */
/* TODO:
All Puts to CC_OP/CC_DEP1/CC_DEP2/CC_NDEP should really be checked
to ensure a 32-bit value is being written.
FUCOMI(P): what happens to A and S flags? Currently are forced
to zero.
x87 FP Limitations:
* all arithmetic done at 64 bits
* no FP exceptions, except for handling stack over/underflow
* FP rounding mode observed only for float->int conversions
and int->float conversions which could lose accuracy, and
for float-to-float rounding. For all other operations,
round-to-nearest is used, regardless.
* some of the FCOM cases could do with testing -- not convinced
that the args are the right way round.
* FSAVE does not re-initialise the FPU; it should do
* FINIT not only initialises the FPU environment, it also
zeroes all the FP registers. It should leave the registers
unchanged.
SAHF should cause eflags[1] == 1, and in fact it produces 0. As
per Intel docs this bit has no meaning anyway. Since PUSHF is the
only way to observe eflags[1], a proper fix would be to make that
bit be set by PUSHF.
The state of %eflags.AC (alignment check, bit 18) is recorded by
the simulation (viz, if you set it with popf then a pushf produces
the value you set it to), but it is otherwise ignored. In
particular, setting it to 1 does NOT cause alignment checking to
happen. Programs that set it to 1 and then rely on the resulting
SIGBUSs to inform them of misaligned accesses will not work.
Implementation of sysenter is necessarily partial. sysenter is a
kind of system call entry. When doing a sysenter, the return
address is not known -- that is something that is beyond Vex's
knowledge. So the generated IR forces a return to the scheduler,
which can do what it likes to simulate the systenter, but it MUST
set this thread's guest_EIP field with the continuation address
before resuming execution. If that doesn't happen, the thread will
jump to address zero, which is probably fatal.
This module uses global variables and so is not MT-safe (if that
should ever become relevant).
The delta values are 32-bit ints, not 64-bit ints. That means
this module may not work right if run on a 64-bit host. That should
be fixed properly, really -- if anyone ever wants to use Vex to
translate x86 code for execution on a 64-bit host.
casLE (implementation of lock-prefixed insns) and rep-prefixed
insns: the side-exit back to the start of the insn is done with
Ijk_Boring. This is quite wrong, it should be done with
Ijk_NoRedir, since otherwise the side exit, which is intended to
restart the instruction for whatever reason, could go somewhere
entirely else. Doing it right (with Ijk_NoRedir jumps) would make
no-redir jumps performance critical, at least for rep-prefixed
instructions, since all iterations thereof would involve such a
jump. It's not such a big deal with casLE since the side exit is
only taken if the CAS fails, that is, the location is contended,
which is relatively unlikely.
XXXX: Nov 2009: handling of SWP on ARM suffers from the same
problem.
Note also, the test for CAS success vs failure is done using
Iop_CasCmp{EQ,NE}{8,16,32,64} rather than the ordinary
Iop_Cmp{EQ,NE} equivalents. This is so as to tell Memcheck that it
shouldn't definedness-check these comparisons. See
COMMENT_ON_CasCmpEQ in memcheck/mc_translate.c for
background/rationale.
*/
/* Performance holes:
- fcom ; fstsw %ax ; sahf
sahf does not update the O flag (sigh) and so O needs to
be computed. This is done expensively; it would be better
to have a calculate_eflags_o helper.
- emwarns; some FP codes can generate huge numbers of these
if the fpucw is changed in an inner loop. It would be
better for the guest state to have an emwarn-enable reg
which can be set zero or nonzero. If it is zero, emwarns
are not flagged, and instead control just flows all the
way through bbs as usual.
*/
/* "Special" instructions.
This instruction decoder can decode three special instructions
which mean nothing natively (are no-ops as far as regs/mem are
concerned) but have meaning for supporting Valgrind. A special
instruction is flagged by the 12-byte preamble C1C703 C1C70D C1C71D
C1C713 (in the standard interpretation, that means: roll $3, %edi;
roll $13, %edi; roll $29, %edi; roll $19, %edi). Following that,
one of the following 3 are allowed (standard interpretation in
parentheses):
87DB (xchgl %ebx,%ebx) %EDX = client_request ( %EAX )
87C9 (xchgl %ecx,%ecx) %EAX = guest_NRADDR
87D2 (xchgl %edx,%edx) call-noredir *%EAX
87FF (xchgl %edi,%edi) IR injection
Any other bytes following the 12-byte preamble are illegal and
constitute a failure in instruction decoding. This all assumes
that the preamble will never occur except in specific code
fragments designed for Valgrind to catch.
No prefixes may precede a "Special" instruction.
*/
/* LOCK prefixed instructions. These are translated using IR-level
CAS statements (IRCAS) and are believed to preserve atomicity, even
from the point of view of some other process racing against a
simulated one (presumably they communicate via a shared memory
segment).
Handlers which are aware of LOCK prefixes are:
dis_op2_G_E (add, or, adc, sbb, and, sub, xor)
dis_cmpxchg_G_E (cmpxchg)
dis_Grp1 (add, or, adc, sbb, and, sub, xor)
dis_Grp3 (not, neg)
dis_Grp4 (inc, dec)
dis_Grp5 (inc, dec)
dis_Grp8_Imm (bts, btc, btr)
dis_bt_G_E (bts, btc, btr)
dis_xadd_G_E (xadd)
*/
#include "libvex_basictypes.h"
#include "libvex_ir.h"
#include "libvex.h"
#include "libvex_guest_x86.h"
#include "main_util.h"
#include "main_globals.h"
#include "guest_generic_bb_to_IR.h"
#include "guest_generic_x87.h"
#include "guest_x86_defs.h"
/*------------------------------------------------------------*/
/*--- Globals ---*/
/*------------------------------------------------------------*/
/* These are set at the start of the translation of an insn, right
down in disInstr_X86, so that we don't have to pass them around
endlessly. They are all constant during the translation of any
given insn. */
/* We need to know this to do sub-register accesses correctly. */
static VexEndness host_endness;
/* Pointer to the guest code area (points to start of BB, not to the
insn being processed). */
static UChar* guest_code;
/* The guest address corresponding to guest_code[0]. */
static Addr32 guest_EIP_bbstart;
/* The guest address for the instruction currently being
translated. */
static Addr32 guest_EIP_curr_instr;
/* The IRSB* into which we're generating code. */
static IRSB* irsb;
/*------------------------------------------------------------*/
/*--- Debugging output ---*/
/*------------------------------------------------------------*/
#define DIP(format, args...) \
if (vex_traceflags & VEX_TRACE_FE) \
vex_printf(format, ## args)
#define DIS(buf, format, args...) \
if (vex_traceflags & VEX_TRACE_FE) \
vex_sprintf(buf, format, ## args)
/*------------------------------------------------------------*/
/*--- Offsets of various parts of the x86 guest state. ---*/
/*------------------------------------------------------------*/
#define OFFB_EAX offsetof(VexGuestX86State,guest_EAX)
#define OFFB_EBX offsetof(VexGuestX86State,guest_EBX)
#define OFFB_ECX offsetof(VexGuestX86State,guest_ECX)
#define OFFB_EDX offsetof(VexGuestX86State,guest_EDX)
#define OFFB_ESP offsetof(VexGuestX86State,guest_ESP)
#define OFFB_EBP offsetof(VexGuestX86State,guest_EBP)
#define OFFB_ESI offsetof(VexGuestX86State,guest_ESI)
#define OFFB_EDI offsetof(VexGuestX86State,guest_EDI)
#define OFFB_EIP offsetof(VexGuestX86State,guest_EIP)
#define OFFB_CC_OP offsetof(VexGuestX86State,guest_CC_OP)
#define OFFB_CC_DEP1 offsetof(VexGuestX86State,guest_CC_DEP1)
#define OFFB_CC_DEP2 offsetof(VexGuestX86State,guest_CC_DEP2)
#define OFFB_CC_NDEP offsetof(VexGuestX86State,guest_CC_NDEP)
#define OFFB_FPREGS offsetof(VexGuestX86State,guest_FPREG[0])
#define OFFB_FPTAGS offsetof(VexGuestX86State,guest_FPTAG[0])
#define OFFB_DFLAG offsetof(VexGuestX86State,guest_DFLAG)
#define OFFB_IDFLAG offsetof(VexGuestX86State,guest_IDFLAG)
#define OFFB_ACFLAG offsetof(VexGuestX86State,guest_ACFLAG)
#define OFFB_FTOP offsetof(VexGuestX86State,guest_FTOP)
#define OFFB_FC3210 offsetof(VexGuestX86State,guest_FC3210)
#define OFFB_FPROUND offsetof(VexGuestX86State,guest_FPROUND)
#define OFFB_CS offsetof(VexGuestX86State,guest_CS)
#define OFFB_DS offsetof(VexGuestX86State,guest_DS)
#define OFFB_ES offsetof(VexGuestX86State,guest_ES)
#define OFFB_FS offsetof(VexGuestX86State,guest_FS)
#define OFFB_GS offsetof(VexGuestX86State,guest_GS)
#define OFFB_SS offsetof(VexGuestX86State,guest_SS)
#define OFFB_LDT offsetof(VexGuestX86State,guest_LDT)
#define OFFB_GDT offsetof(VexGuestX86State,guest_GDT)
#define OFFB_SSEROUND offsetof(VexGuestX86State,guest_SSEROUND)
#define OFFB_XMM0 offsetof(VexGuestX86State,guest_XMM0)
#define OFFB_XMM1 offsetof(VexGuestX86State,guest_XMM1)
#define OFFB_XMM2 offsetof(VexGuestX86State,guest_XMM2)
#define OFFB_XMM3 offsetof(VexGuestX86State,guest_XMM3)
#define OFFB_XMM4 offsetof(VexGuestX86State,guest_XMM4)
#define OFFB_XMM5 offsetof(VexGuestX86State,guest_XMM5)
#define OFFB_XMM6 offsetof(VexGuestX86State,guest_XMM6)
#define OFFB_XMM7 offsetof(VexGuestX86State,guest_XMM7)
#define OFFB_EMNOTE offsetof(VexGuestX86State,guest_EMNOTE)
#define OFFB_CMSTART offsetof(VexGuestX86State,guest_CMSTART)
#define OFFB_CMLEN offsetof(VexGuestX86State,guest_CMLEN)
#define OFFB_NRADDR offsetof(VexGuestX86State,guest_NRADDR)
#define OFFB_IP_AT_SYSCALL offsetof(VexGuestX86State,guest_IP_AT_SYSCALL)
/*------------------------------------------------------------*/
/*--- Helper bits and pieces for deconstructing the ---*/
/*--- x86 insn stream. ---*/
/*------------------------------------------------------------*/
/* This is the Intel register encoding -- integer regs. */
#define R_EAX 0
#define R_ECX 1
#define R_EDX 2
#define R_EBX 3
#define R_ESP 4
#define R_EBP 5
#define R_ESI 6
#define R_EDI 7
#define R_AL (0+R_EAX)
#define R_AH (4+R_EAX)
/* This is the Intel register encoding -- segment regs. */
#define R_ES 0
#define R_CS 1
#define R_SS 2
#define R_DS 3
#define R_FS 4
#define R_GS 5
/* Add a statement to the list held by "irbb". */
static void stmt ( IRStmt* st )
{
addStmtToIRSB( irsb, st );
}
/* Generate a new temporary of the given type. */
static IRTemp newTemp ( IRType ty )
{
vassert(isPlausibleIRType(ty));
return newIRTemp( irsb->tyenv, ty );
}
/* Various simple conversions */
static UInt extend_s_8to32( UInt x )
{
return (UInt)((((Int)x) << 24) >> 24);
}
static UInt extend_s_16to32 ( UInt x )
{
return (UInt)((((Int)x) << 16) >> 16);
}
/* Fetch a byte from the guest insn stream. */
static UChar getIByte ( Int delta )
{
return guest_code[delta];
}
/* Extract the reg field from a modRM byte. */
static Int gregOfRM ( UChar mod_reg_rm )
{
return (Int)( (mod_reg_rm >> 3) & 7 );
}
/* Figure out whether the mod and rm parts of a modRM byte refer to a
register or memory. If so, the byte will have the form 11XXXYYY,
where YYY is the register number. */
static Bool epartIsReg ( UChar mod_reg_rm )
{
return toBool(0xC0 == (mod_reg_rm & 0xC0));
}
/* ... and extract the register number ... */
static Int eregOfRM ( UChar mod_reg_rm )
{
return (Int)(mod_reg_rm & 0x7);
}
/* Get a 8/16/32-bit unsigned value out of the insn stream. */
static UChar getUChar ( Int delta )
{
UChar v = guest_code[delta+0];
return toUChar(v);
}
static UInt getUDisp16 ( Int delta )
{
UInt v = guest_code[delta+1]; v <<= 8;
v |= guest_code[delta+0];
return v & 0xFFFF;
}
static UInt getUDisp32 ( Int delta )
{
UInt v = guest_code[delta+3]; v <<= 8;
v |= guest_code[delta+2]; v <<= 8;
v |= guest_code[delta+1]; v <<= 8;
v |= guest_code[delta+0];
return v;
}
static UInt getUDisp ( Int size, Int delta )
{
switch (size) {
case 4: return getUDisp32(delta);
case 2: return getUDisp16(delta);
case 1: return (UInt)getUChar(delta);
default: vpanic("getUDisp(x86)");
}
return 0; /*notreached*/
}
/* Get a byte value out of the insn stream and sign-extend to 32
bits. */
static UInt getSDisp8 ( Int delta )
{
return extend_s_8to32( (UInt) (guest_code[delta]) );
}
static UInt getSDisp16 ( Int delta0 )
{
UChar* eip = (UChar*)(&guest_code[delta0]);
UInt d = *eip++;
d |= ((*eip++) << 8);
return extend_s_16to32(d);
}
static UInt getSDisp ( Int size, Int delta )
{
switch (size) {
case 4: return getUDisp32(delta);
case 2: return getSDisp16(delta);
case 1: return getSDisp8(delta);
default: vpanic("getSDisp(x86)");
}
return 0; /*notreached*/
}
/*------------------------------------------------------------*/
/*--- Helpers for constructing IR. ---*/
/*------------------------------------------------------------*/
/* Create a 1/2/4 byte read of an x86 integer registers. For 16/8 bit
register references, we need to take the host endianness into
account. Supplied value is 0 .. 7 and in the Intel instruction
encoding. */
static IRType szToITy ( Int n )
{
switch (n) {
case 1: return Ity_I8;
case 2: return Ity_I16;
case 4: return Ity_I32;
default: vpanic("szToITy(x86)");
}
}
/* On a little-endian host, less significant bits of the guest
registers are at lower addresses. Therefore, if a reference to a
register low half has the safe guest state offset as a reference to
the full register.
*/
static Int integerGuestRegOffset ( Int sz, UInt archreg )
{
vassert(archreg < 8);
/* Correct for little-endian host only. */
vassert(host_endness == VexEndnessLE);
if (sz == 4 || sz == 2 || (sz == 1 && archreg < 4)) {
switch (archreg) {
case R_EAX: return OFFB_EAX;
case R_EBX: return OFFB_EBX;
case R_ECX: return OFFB_ECX;
case R_EDX: return OFFB_EDX;
case R_ESI: return OFFB_ESI;
case R_EDI: return OFFB_EDI;
case R_ESP: return OFFB_ESP;
case R_EBP: return OFFB_EBP;
default: vpanic("integerGuestRegOffset(x86,le)(4,2)");
}
}
vassert(archreg >= 4 && archreg < 8 && sz == 1);
switch (archreg-4) {
case R_EAX: return 1+ OFFB_EAX;
case R_EBX: return 1+ OFFB_EBX;
case R_ECX: return 1+ OFFB_ECX;
case R_EDX: return 1+ OFFB_EDX;
default: vpanic("integerGuestRegOffset(x86,le)(1h)");
}
/* NOTREACHED */
vpanic("integerGuestRegOffset(x86,le)");
}
static Int segmentGuestRegOffset ( UInt sreg )
{
switch (sreg) {
case R_ES: return OFFB_ES;
case R_CS: return OFFB_CS;
case R_SS: return OFFB_SS;
case R_DS: return OFFB_DS;
case R_FS: return OFFB_FS;
case R_GS: return OFFB_GS;
default: vpanic("segmentGuestRegOffset(x86)");
}
}
static Int xmmGuestRegOffset ( UInt xmmreg )
{
switch (xmmreg) {
case 0: return OFFB_XMM0;
case 1: return OFFB_XMM1;
case 2: return OFFB_XMM2;
case 3: return OFFB_XMM3;
case 4: return OFFB_XMM4;
case 5: return OFFB_XMM5;
case 6: return OFFB_XMM6;
case 7: return OFFB_XMM7;
default: vpanic("xmmGuestRegOffset");
}
}
/* Lanes of vector registers are always numbered from zero being the
least significant lane (rightmost in the register). */
static Int xmmGuestRegLane16offset ( UInt xmmreg, Int laneno )
{
/* Correct for little-endian host only. */
vassert(host_endness == VexEndnessLE);
vassert(laneno >= 0 && laneno < 8);
return xmmGuestRegOffset( xmmreg ) + 2 * laneno;
}
static Int xmmGuestRegLane32offset ( UInt xmmreg, Int laneno )
{
/* Correct for little-endian host only. */
vassert(host_endness == VexEndnessLE);
vassert(laneno >= 0 && laneno < 4);
return xmmGuestRegOffset( xmmreg ) + 4 * laneno;
}
static Int xmmGuestRegLane64offset ( UInt xmmreg, Int laneno )
{
/* Correct for little-endian host only. */
vassert(host_endness == VexEndnessLE);
vassert(laneno >= 0 && laneno < 2);
return xmmGuestRegOffset( xmmreg ) + 8 * laneno;
}
static IRExpr* getIReg ( Int sz, UInt archreg )
{
vassert(sz == 1 || sz == 2 || sz == 4);
vassert(archreg < 8);
return IRExpr_Get( integerGuestRegOffset(sz,archreg),
szToITy(sz) );
}
/* Ditto, but write to a reg instead. */
static void putIReg ( Int sz, UInt archreg, IRExpr* e )
{
IRType ty = typeOfIRExpr(irsb->tyenv, e);
switch (sz) {
case 1: vassert(ty == Ity_I8); break;
case 2: vassert(ty == Ity_I16); break;
case 4: vassert(ty == Ity_I32); break;
default: vpanic("putIReg(x86)");
}
vassert(archreg < 8);
stmt( IRStmt_Put(integerGuestRegOffset(sz,archreg), e) );
}
static IRExpr* getSReg ( UInt sreg )
{
return IRExpr_Get( segmentGuestRegOffset(sreg), Ity_I16 );
}
static void putSReg ( UInt sreg, IRExpr* e )
{
vassert(typeOfIRExpr(irsb->tyenv,e) == Ity_I16);
stmt( IRStmt_Put( segmentGuestRegOffset(sreg), e ) );
}
static IRExpr* getXMMReg ( UInt xmmreg )
{
return IRExpr_Get( xmmGuestRegOffset(xmmreg), Ity_V128 );
}
static IRExpr* getXMMRegLane64 ( UInt xmmreg, Int laneno )
{
return IRExpr_Get( xmmGuestRegLane64offset(xmmreg,laneno), Ity_I64 );
}
static IRExpr* getXMMRegLane64F ( UInt xmmreg, Int laneno )
{
return IRExpr_Get( xmmGuestRegLane64offset(xmmreg,laneno), Ity_F64 );
}
static IRExpr* getXMMRegLane32 ( UInt xmmreg, Int laneno )
{
return IRExpr_Get( xmmGuestRegLane32offset(xmmreg,laneno), Ity_I32 );
}
static IRExpr* getXMMRegLane32F ( UInt xmmreg, Int laneno )
{
return IRExpr_Get( xmmGuestRegLane32offset(xmmreg,laneno), Ity_F32 );
}
static void putXMMReg ( UInt xmmreg, IRExpr* e )
{
vassert(typeOfIRExpr(irsb->tyenv,e) == Ity_V128);
stmt( IRStmt_Put( xmmGuestRegOffset(xmmreg), e ) );
}
static void putXMMRegLane64 ( UInt xmmreg, Int laneno, IRExpr* e )
{
vassert(typeOfIRExpr(irsb->tyenv,e) == Ity_I64);
stmt( IRStmt_Put( xmmGuestRegLane64offset(xmmreg,laneno), e ) );
}
static void putXMMRegLane64F ( UInt xmmreg, Int laneno, IRExpr* e )
{
vassert(typeOfIRExpr(irsb->tyenv,e) == Ity_F64);
stmt( IRStmt_Put( xmmGuestRegLane64offset(xmmreg,laneno), e ) );
}
static void putXMMRegLane32F ( UInt xmmreg, Int laneno, IRExpr* e )
{
vassert(typeOfIRExpr(irsb->tyenv,e) == Ity_F32);
stmt( IRStmt_Put( xmmGuestRegLane32offset(xmmreg,laneno), e ) );
}
static void putXMMRegLane32 ( UInt xmmreg, Int laneno, IRExpr* e )
{
vassert(typeOfIRExpr(irsb->tyenv,e) == Ity_I32);
stmt( IRStmt_Put( xmmGuestRegLane32offset(xmmreg,laneno), e ) );
}
static void putXMMRegLane16 ( UInt xmmreg, Int laneno, IRExpr* e )
{
vassert(typeOfIRExpr(irsb->tyenv,e) == Ity_I16);
stmt( IRStmt_Put( xmmGuestRegLane16offset(xmmreg,laneno), e ) );
}
static void assign ( IRTemp dst, IRExpr* e )
{
stmt( IRStmt_WrTmp(dst, e) );
}
static void storeLE ( IRExpr* addr, IRExpr* data )
{
stmt( IRStmt_Store(Iend_LE, addr, data) );
}
static IRExpr* unop ( IROp op, IRExpr* a )
{
return IRExpr_Unop(op, a);
}
static IRExpr* binop ( IROp op, IRExpr* a1, IRExpr* a2 )
{
return IRExpr_Binop(op, a1, a2);
}
static IRExpr* triop ( IROp op, IRExpr* a1, IRExpr* a2, IRExpr* a3 )
{
return IRExpr_Triop(op, a1, a2, a3);
}
static IRExpr* mkexpr ( IRTemp tmp )
{
return IRExpr_RdTmp(tmp);
}
static IRExpr* mkU8 ( UInt i )
{
vassert(i < 256);
return IRExpr_Const(IRConst_U8( (UChar)i ));
}
static IRExpr* mkU16 ( UInt i )
{
vassert(i < 65536);
return IRExpr_Const(IRConst_U16( (UShort)i ));
}
static IRExpr* mkU32 ( UInt i )
{
return IRExpr_Const(IRConst_U32(i));
}
static IRExpr* mkU64 ( ULong i )
{
return IRExpr_Const(IRConst_U64(i));
}
static IRExpr* mkU ( IRType ty, UInt i )
{
if (ty == Ity_I8) return mkU8(i);
if (ty == Ity_I16) return mkU16(i);
if (ty == Ity_I32) return mkU32(i);
/* If this panics, it usually means you passed a size (1,2,4)
value as the IRType, rather than a real IRType. */
vpanic("mkU(x86)");
}
static IRExpr* mkV128 ( UShort mask )
{
return IRExpr_Const(IRConst_V128(mask));
}
static IRExpr* loadLE ( IRType ty, IRExpr* addr )
{
return IRExpr_Load(Iend_LE, ty, addr);
}
static IROp mkSizedOp ( IRType ty, IROp op8 )
{
Int adj;
vassert(ty == Ity_I8 || ty == Ity_I16 || ty == Ity_I32);
vassert(op8 == Iop_Add8 || op8 == Iop_Sub8
|| op8 == Iop_Mul8
|| op8 == Iop_Or8 || op8 == Iop_And8 || op8 == Iop_Xor8
|| op8 == Iop_Shl8 || op8 == Iop_Shr8 || op8 == Iop_Sar8
|| op8 == Iop_CmpEQ8 || op8 == Iop_CmpNE8
|| op8 == Iop_CasCmpNE8
|| op8 == Iop_ExpCmpNE8
|| op8 == Iop_Not8);
adj = ty==Ity_I8 ? 0 : (ty==Ity_I16 ? 1 : 2);
return adj + op8;
}
static IROp mkWidenOp ( Int szSmall, Int szBig, Bool signd )
{
if (szSmall == 1 && szBig == 4) {
return signd ? Iop_8Sto32 : Iop_8Uto32;
}
if (szSmall == 1 && szBig == 2) {
return signd ? Iop_8Sto16 : Iop_8Uto16;
}
if (szSmall == 2 && szBig == 4) {
return signd ? Iop_16Sto32 : Iop_16Uto32;
}
vpanic("mkWidenOp(x86,guest)");
}
static IRExpr* mkAnd1 ( IRExpr* x, IRExpr* y )
{
vassert(typeOfIRExpr(irsb->tyenv,x) == Ity_I1);
vassert(typeOfIRExpr(irsb->tyenv,y) == Ity_I1);
return unop(Iop_32to1,
binop(Iop_And32,
unop(Iop_1Uto32,x),
unop(Iop_1Uto32,y)));
}
/* Generate a compare-and-swap operation, operating on memory at
'addr'. The expected value is 'expVal' and the new value is
'newVal'. If the operation fails, then transfer control (with a
no-redir jump (XXX no -- see comment at top of this file)) to
'restart_point', which is presumably the address of the guest
instruction again -- retrying, essentially. */
static void casLE ( IRExpr* addr, IRExpr* expVal, IRExpr* newVal,
Addr32 restart_point )
{
IRCAS* cas;
IRType tyE = typeOfIRExpr(irsb->tyenv, expVal);
IRType tyN = typeOfIRExpr(irsb->tyenv, newVal);
IRTemp oldTmp = newTemp(tyE);
IRTemp expTmp = newTemp(tyE);
vassert(tyE == tyN);
vassert(tyE == Ity_I32 || tyE == Ity_I16 || tyE == Ity_I8);
assign(expTmp, expVal);
cas = mkIRCAS( IRTemp_INVALID, oldTmp, Iend_LE, addr,
NULL, mkexpr(expTmp), NULL, newVal );
stmt( IRStmt_CAS(cas) );
stmt( IRStmt_Exit(
binop( mkSizedOp(tyE,Iop_CasCmpNE8),
mkexpr(oldTmp), mkexpr(expTmp) ),
Ijk_Boring, /*Ijk_NoRedir*/
IRConst_U32( restart_point ),
OFFB_EIP
));
}
/*------------------------------------------------------------*/
/*--- Helpers for %eflags. ---*/
/*------------------------------------------------------------*/
/* -------------- Evaluating the flags-thunk. -------------- */
/* Build IR to calculate all the eflags from stored
CC_OP/CC_DEP1/CC_DEP2/CC_NDEP. Returns an expression ::
Ity_I32. */
static IRExpr* mk_x86g_calculate_eflags_all ( void )
{
IRExpr** args
= mkIRExprVec_4( IRExpr_Get(OFFB_CC_OP, Ity_I32),
IRExpr_Get(OFFB_CC_DEP1, Ity_I32),
IRExpr_Get(OFFB_CC_DEP2, Ity_I32),
IRExpr_Get(OFFB_CC_NDEP, Ity_I32) );
IRExpr* call
= mkIRExprCCall(
Ity_I32,
0/*regparm*/,
"x86g_calculate_eflags_all", &x86g_calculate_eflags_all,
args
);
/* Exclude OP and NDEP from definedness checking. We're only
interested in DEP1 and DEP2. */
call->Iex.CCall.cee->mcx_mask = (1<<0) | (1<<3);
return call;
}
/* Build IR to calculate some particular condition from stored
CC_OP/CC_DEP1/CC_DEP2/CC_NDEP. Returns an expression ::
Ity_Bit. */
static IRExpr* mk_x86g_calculate_condition ( X86Condcode cond )
{
IRExpr** args
= mkIRExprVec_5( mkU32(cond),
IRExpr_Get(OFFB_CC_OP, Ity_I32),
IRExpr_Get(OFFB_CC_DEP1, Ity_I32),
IRExpr_Get(OFFB_CC_DEP2, Ity_I32),
IRExpr_Get(OFFB_CC_NDEP, Ity_I32) );
IRExpr* call
= mkIRExprCCall(
Ity_I32,
0/*regparm*/,
"x86g_calculate_condition", &x86g_calculate_condition,
args
);
/* Exclude the requested condition, OP and NDEP from definedness
checking. We're only interested in DEP1 and DEP2. */
call->Iex.CCall.cee->mcx_mask = (1<<0) | (1<<1) | (1<<4);
return unop(Iop_32to1, call);
}
/* Build IR to calculate just the carry flag from stored
CC_OP/CC_DEP1/CC_DEP2/CC_NDEP. Returns an expression :: Ity_I32. */
static IRExpr* mk_x86g_calculate_eflags_c ( void )
{
IRExpr** args
= mkIRExprVec_4( IRExpr_Get(OFFB_CC_OP, Ity_I32),
IRExpr_Get(OFFB_CC_DEP1, Ity_I32),
IRExpr_Get(OFFB_CC_DEP2, Ity_I32),
IRExpr_Get(OFFB_CC_NDEP, Ity_I32) );
IRExpr* call
= mkIRExprCCall(
Ity_I32,
3/*regparm*/,
"x86g_calculate_eflags_c", &x86g_calculate_eflags_c,
args
);
/* Exclude OP and NDEP from definedness checking. We're only
interested in DEP1 and DEP2. */
call->Iex.CCall.cee->mcx_mask = (1<<0) | (1<<3);
return call;
}
/* -------------- Building the flags-thunk. -------------- */
/* The machinery in this section builds the flag-thunk following a
flag-setting operation. Hence the various setFlags_* functions.
*/
static Bool isAddSub ( IROp op8 )
{
return toBool(op8 == Iop_Add8 || op8 == Iop_Sub8);
}
static Bool isLogic ( IROp op8 )
{
return toBool(op8 == Iop_And8 || op8 == Iop_Or8 || op8 == Iop_Xor8);
}
/* U-widen 8/16/32 bit int expr to 32. */
static IRExpr* widenUto32 ( IRExpr* e )
{
switch (typeOfIRExpr(irsb->tyenv,e)) {
case Ity_I32: return e;
case Ity_I16: return unop(Iop_16Uto32,e);
case Ity_I8: return unop(Iop_8Uto32,e);
default: vpanic("widenUto32");
}
}
/* S-widen 8/16/32 bit int expr to 32. */
static IRExpr* widenSto32 ( IRExpr* e )
{
switch (typeOfIRExpr(irsb->tyenv,e)) {
case Ity_I32: return e;
case Ity_I16: return unop(Iop_16Sto32,e);
case Ity_I8: return unop(Iop_8Sto32,e);
default: vpanic("widenSto32");
}
}
/* Narrow 8/16/32 bit int expr to 8/16/32. Clearly only some
of these combinations make sense. */
static IRExpr* narrowTo ( IRType dst_ty, IRExpr* e )
{
IRType src_ty = typeOfIRExpr(irsb->tyenv,e);
if (src_ty == dst_ty)
return e;
if (src_ty == Ity_I32 && dst_ty == Ity_I16)
return unop(Iop_32to16, e);
if (src_ty == Ity_I32 && dst_ty == Ity_I8)
return unop(Iop_32to8, e);
vex_printf("\nsrc, dst tys are: ");
ppIRType(src_ty);
vex_printf(", ");
ppIRType(dst_ty);
vex_printf("\n");
vpanic("narrowTo(x86)");
}
/* Set the flags thunk OP, DEP1 and DEP2 fields. The supplied op is
auto-sized up to the real op. */
static
void setFlags_DEP1_DEP2 ( IROp op8, IRTemp dep1, IRTemp dep2, IRType ty )
{
Int ccOp = ty==Ity_I8 ? 0 : (ty==Ity_I16 ? 1 : 2);
vassert(ty == Ity_I8 || ty == Ity_I16 || ty == Ity_I32);
switch (op8) {
case Iop_Add8: ccOp += X86G_CC_OP_ADDB; break;
case Iop_Sub8: ccOp += X86G_CC_OP_SUBB; break;
default: ppIROp(op8);
vpanic("setFlags_DEP1_DEP2(x86)");
}
stmt( IRStmt_Put( OFFB_CC_OP, mkU32(ccOp)) );
stmt( IRStmt_Put( OFFB_CC_DEP1, widenUto32(mkexpr(dep1))) );
stmt( IRStmt_Put( OFFB_CC_DEP2, widenUto32(mkexpr(dep2))) );
/* Set NDEP even though it isn't used. This makes redundant-PUT
elimination of previous stores to this field work better. */
stmt( IRStmt_Put( OFFB_CC_NDEP, mkU32(0) ));
}
/* Set the OP and DEP1 fields only, and write zero to DEP2. */
static
void setFlags_DEP1 ( IROp op8, IRTemp dep1, IRType ty )
{
Int ccOp = ty==Ity_I8 ? 0 : (ty==Ity_I16 ? 1 : 2);
vassert(ty == Ity_I8 || ty == Ity_I16 || ty == Ity_I32);
switch (op8) {
case Iop_Or8:
case Iop_And8:
case Iop_Xor8: ccOp += X86G_CC_OP_LOGICB; break;
default: ppIROp(op8);
vpanic("setFlags_DEP1(x86)");
}
stmt( IRStmt_Put( OFFB_CC_OP, mkU32(ccOp)) );
stmt( IRStmt_Put( OFFB_CC_DEP1, widenUto32(mkexpr(dep1))) );
stmt( IRStmt_Put( OFFB_CC_DEP2, mkU32(0)) );
/* Set NDEP even though it isn't used. This makes redundant-PUT
elimination of previous stores to this field work better. */
stmt( IRStmt_Put( OFFB_CC_NDEP, mkU32(0) ));
}
/* For shift operations, we put in the result and the undershifted
result. Except if the shift amount is zero, the thunk is left
unchanged. */
static void setFlags_DEP1_DEP2_shift ( IROp op32,
IRTemp res,
IRTemp resUS,
IRType ty,
IRTemp guard )
{
Int ccOp = ty==Ity_I8 ? 2 : (ty==Ity_I16 ? 1 : 0);
vassert(ty == Ity_I8 || ty == Ity_I16 || ty == Ity_I32);
vassert(guard);
/* Both kinds of right shifts are handled by the same thunk
operation. */
switch (op32) {
case Iop_Shr32:
case Iop_Sar32: ccOp = X86G_CC_OP_SHRL - ccOp; break;
case Iop_Shl32: ccOp = X86G_CC_OP_SHLL - ccOp; break;
default: ppIROp(op32);
vpanic("setFlags_DEP1_DEP2_shift(x86)");
}
/* guard :: Ity_I8. We need to convert it to I1. */
IRTemp guardB = newTemp(Ity_I1);
assign( guardB, binop(Iop_CmpNE8, mkexpr(guard), mkU8(0)) );
/* DEP1 contains the result, DEP2 contains the undershifted value. */
stmt( IRStmt_Put( OFFB_CC_OP,
IRExpr_ITE( mkexpr(guardB),
mkU32(ccOp),
IRExpr_Get(OFFB_CC_OP,Ity_I32) ) ));
stmt( IRStmt_Put( OFFB_CC_DEP1,
IRExpr_ITE( mkexpr(guardB),
widenUto32(mkexpr(res)),
IRExpr_Get(OFFB_CC_DEP1,Ity_I32) ) ));
stmt( IRStmt_Put( OFFB_CC_DEP2,
IRExpr_ITE( mkexpr(guardB),
widenUto32(mkexpr(resUS)),
IRExpr_Get(OFFB_CC_DEP2,Ity_I32) ) ));
/* Set NDEP even though it isn't used. This makes redundant-PUT
elimination of previous stores to this field work better. */
stmt( IRStmt_Put( OFFB_CC_NDEP,
IRExpr_ITE( mkexpr(guardB),
mkU32(0),
IRExpr_Get(OFFB_CC_NDEP,Ity_I32) ) ));
}
/* For the inc/dec case, we store in DEP1 the result value and in NDEP
the former value of the carry flag, which unfortunately we have to
compute. */
static void setFlags_INC_DEC ( Bool inc, IRTemp res, IRType ty )
{
Int ccOp = inc ? X86G_CC_OP_INCB : X86G_CC_OP_DECB;
ccOp += ty==Ity_I8 ? 0 : (ty==Ity_I16 ? 1 : 2);
vassert(ty == Ity_I8 || ty == Ity_I16 || ty == Ity_I32);
/* This has to come first, because calculating the C flag
may require reading all four thunk fields. */
stmt( IRStmt_Put( OFFB_CC_NDEP, mk_x86g_calculate_eflags_c()) );
stmt( IRStmt_Put( OFFB_CC_OP, mkU32(ccOp)) );
stmt( IRStmt_Put( OFFB_CC_DEP1, widenUto32(mkexpr(res))) );
stmt( IRStmt_Put( OFFB_CC_DEP2, mkU32(0)) );
}
/* Multiplies are pretty much like add and sub: DEP1 and DEP2 hold the
two arguments. */
static
void setFlags_MUL ( IRType ty, IRTemp arg1, IRTemp arg2, UInt base_op )
{
switch (ty) {
case Ity_I8:
stmt( IRStmt_Put( OFFB_CC_OP, mkU32(base_op+0) ) );
break;
case Ity_I16:
stmt( IRStmt_Put( OFFB_CC_OP, mkU32(base_op+1) ) );
break;
case Ity_I32:
stmt( IRStmt_Put( OFFB_CC_OP, mkU32(base_op+2) ) );
break;
default:
vpanic("setFlags_MUL(x86)");
}
stmt( IRStmt_Put( OFFB_CC_DEP1, widenUto32(mkexpr(arg1)) ));
stmt( IRStmt_Put( OFFB_CC_DEP2, widenUto32(mkexpr(arg2)) ));
/* Set NDEP even though it isn't used. This makes redundant-PUT
elimination of previous stores to this field work better. */
stmt( IRStmt_Put( OFFB_CC_NDEP, mkU32(0) ));
}
/* -------------- Condition codes. -------------- */
/* Condition codes, using the Intel encoding. */
static const HChar* name_X86Condcode ( X86Condcode cond )
{
switch (cond) {
case X86CondO: return "o";
case X86CondNO: return "no";
case X86CondB: return "b";
case X86CondNB: return "nb";
case X86CondZ: return "z";
case X86CondNZ: return "nz";
case X86CondBE: return "be";
case X86CondNBE: return "nbe";
case X86CondS: return "s";
case X86CondNS: return "ns";
case X86CondP: return "p";
case X86CondNP: return "np";
case X86CondL: return "l";
case X86CondNL: return "nl";
case X86CondLE: return "le";
case X86CondNLE: return "nle";
case X86CondAlways: return "ALWAYS";
default: vpanic("name_X86Condcode");
}
}
static
X86Condcode positiveIse_X86Condcode ( X86Condcode cond,
Bool* needInvert )
{
vassert(cond >= X86CondO && cond <= X86CondNLE);
if (cond & 1) {
*needInvert = True;
return cond-1;
} else {
*needInvert = False;
return cond;
}
}
/* -------------- Helpers for ADD/SUB with carry. -------------- */
/* Given ta1, ta2 and tres, compute tres = ADC(ta1,ta2) and set flags
appropriately.
Optionally, generate a store for the 'tres' value. This can either
be a normal store, or it can be a cas-with-possible-failure style
store:
if taddr is IRTemp_INVALID, then no store is generated.
if taddr is not IRTemp_INVALID, then a store (using taddr as
the address) is generated:
if texpVal is IRTemp_INVALID then a normal store is
generated, and restart_point must be zero (it is irrelevant).
if texpVal is not IRTemp_INVALID then a cas-style store is
generated. texpVal is the expected value, restart_point
is the restart point if the store fails, and texpVal must
have the same type as tres.
*/
static void helper_ADC ( Int sz,
IRTemp tres, IRTemp ta1, IRTemp ta2,
/* info about optional store: */
IRTemp taddr, IRTemp texpVal, Addr32 restart_point )
{
UInt thunkOp;
IRType ty = szToITy(sz);
IRTemp oldc = newTemp(Ity_I32);
IRTemp oldcn = newTemp(ty);
IROp plus = mkSizedOp(ty, Iop_Add8);
IROp xor = mkSizedOp(ty, Iop_Xor8);
vassert(typeOfIRTemp(irsb->tyenv, tres) == ty);
vassert(sz == 1 || sz == 2 || sz == 4);
thunkOp = sz==4 ? X86G_CC_OP_ADCL
: (sz==2 ? X86G_CC_OP_ADCW : X86G_CC_OP_ADCB);
/* oldc = old carry flag, 0 or 1 */
assign( oldc, binop(Iop_And32,
mk_x86g_calculate_eflags_c(),
mkU32(1)) );
assign( oldcn, narrowTo(ty, mkexpr(oldc)) );
assign( tres, binop(plus,
binop(plus,mkexpr(ta1),mkexpr(ta2)),
mkexpr(oldcn)) );
/* Possibly generate a store of 'tres' to 'taddr'. See comment at
start of this function. */
if (taddr != IRTemp_INVALID) {
if (texpVal == IRTemp_INVALID) {
vassert(restart_point == 0);
storeLE( mkexpr(taddr), mkexpr(tres) );
} else {
vassert(typeOfIRTemp(irsb->tyenv, texpVal) == ty);
/* .. and hence 'texpVal' has the same type as 'tres'. */
casLE( mkexpr(taddr),
mkexpr(texpVal), mkexpr(tres), restart_point );
}
}
stmt( IRStmt_Put( OFFB_CC_OP, mkU32(thunkOp) ) );
stmt( IRStmt_Put( OFFB_CC_DEP1, widenUto32(mkexpr(ta1)) ));
stmt( IRStmt_Put( OFFB_CC_DEP2, widenUto32(binop(xor, mkexpr(ta2),
mkexpr(oldcn)) )) );
stmt( IRStmt_Put( OFFB_CC_NDEP, mkexpr(oldc) ) );
}
/* Given ta1, ta2 and tres, compute tres = SBB(ta1,ta2) and set flags
appropriately. As with helper_ADC, possibly generate a store of
the result -- see comments on helper_ADC for details.
*/
static void helper_SBB ( Int sz,
IRTemp tres, IRTemp ta1, IRTemp ta2,
/* info about optional store: */
IRTemp taddr, IRTemp texpVal, Addr32 restart_point )
{
UInt thunkOp;
IRType ty = szToITy(sz);
IRTemp oldc = newTemp(Ity_I32);
IRTemp oldcn = newTemp(ty);
IROp minus = mkSizedOp(ty, Iop_Sub8);
IROp xor = mkSizedOp(ty, Iop_Xor8);
vassert(typeOfIRTemp(irsb->tyenv, tres) == ty);
vassert(sz == 1 || sz == 2 || sz == 4);
thunkOp = sz==4 ? X86G_CC_OP_SBBL
: (sz==2 ? X86G_CC_OP_SBBW : X86G_CC_OP_SBBB);
/* oldc = old carry flag, 0 or 1 */
assign( oldc, binop(Iop_And32,
mk_x86g_calculate_eflags_c(),
mkU32(1)) );
assign( oldcn, narrowTo(ty, mkexpr(oldc)) );
assign( tres, binop(minus,
binop(minus,mkexpr(ta1),mkexpr(ta2)),
mkexpr(oldcn)) );
/* Possibly generate a store of 'tres' to 'taddr'. See comment at
start of this function. */
if (taddr != IRTemp_INVALID) {
if (texpVal == IRTemp_INVALID) {
vassert(restart_point == 0);
storeLE( mkexpr(taddr), mkexpr(tres) );
} else {
vassert(typeOfIRTemp(irsb->tyenv, texpVal) == ty);
/* .. and hence 'texpVal' has the same type as 'tres'. */
casLE( mkexpr(taddr),
mkexpr(texpVal), mkexpr(tres), restart_point );
}
}
stmt( IRStmt_Put( OFFB_CC_OP, mkU32(thunkOp) ) );
stmt( IRStmt_Put( OFFB_CC_DEP1, widenUto32(mkexpr(ta1) )) );
stmt( IRStmt_Put( OFFB_CC_DEP2, widenUto32(binop(xor, mkexpr(ta2),
mkexpr(oldcn)) )) );
stmt( IRStmt_Put( OFFB_CC_NDEP, mkexpr(oldc) ) );
}
/* -------------- Helpers for disassembly printing. -------------- */
static const HChar* nameGrp1 ( Int opc_aux )
{
static const HChar* grp1_names[8]
= { "add", "or", "adc", "sbb", "and", "sub", "xor", "cmp" };
if (opc_aux < 0 || opc_aux > 7) vpanic("nameGrp1(x86)");
return grp1_names[opc_aux];
}
static const HChar* nameGrp2 ( Int opc_aux )
{
static const HChar* grp2_names[8]
= { "rol", "ror", "rcl", "rcr", "shl", "shr", "shl", "sar" };
if (opc_aux < 0 || opc_aux > 7) vpanic("nameGrp2(x86)");
return grp2_names[opc_aux];
}
static const HChar* nameGrp4 ( Int opc_aux )
{
static const HChar* grp4_names[8]
= { "inc", "dec", "???", "???", "???", "???", "???", "???" };
if (opc_aux < 0 || opc_aux > 1) vpanic("nameGrp4(x86)");
return grp4_names[opc_aux];
}
static const HChar* nameGrp5 ( Int opc_aux )
{
static const HChar* grp5_names[8]
= { "inc", "dec", "call*", "call*", "jmp*", "jmp*", "push", "???" };
if (opc_aux < 0 || opc_aux > 6) vpanic("nameGrp5(x86)");
return grp5_names[opc_aux];
}
static const HChar* nameGrp8 ( Int opc_aux )
{
static const HChar* grp8_names[8]
= { "???", "???", "???", "???", "bt", "bts", "btr", "btc" };
if (opc_aux < 4 || opc_aux > 7) vpanic("nameGrp8(x86)");
return grp8_names[opc_aux];
}
static const HChar* nameIReg ( Int size, Int reg )
{
static const HChar* ireg32_names[8]
= { "%eax", "%ecx", "%edx", "%ebx",
"%esp", "%ebp", "%esi", "%edi" };
static const HChar* ireg16_names[8]
= { "%ax", "%cx", "%dx", "%bx", "%sp", "%bp", "%si", "%di" };
static const HChar* ireg8_names[8]
= { "%al", "%cl", "%dl", "%bl",
"%ah{sp}", "%ch{bp}", "%dh{si}", "%bh{di}" };
if (reg < 0 || reg > 7) goto bad;
switch (size) {
case 4: return ireg32_names[reg];
case 2: return ireg16_names[reg];
case 1: return ireg8_names[reg];
}
bad:
vpanic("nameIReg(X86)");
return NULL; /*notreached*/
}
static const HChar* nameSReg ( UInt sreg )
{
switch (sreg) {
case R_ES: return "%es";
case R_CS: return "%cs";
case R_SS: return "%ss";
case R_DS: return "%ds";
case R_FS: return "%fs";
case R_GS: return "%gs";
default: vpanic("nameSReg(x86)");
}
}
static const HChar* nameMMXReg ( Int mmxreg )
{
static const HChar* mmx_names[8]
= { "%mm0", "%mm1", "%mm2", "%mm3", "%mm4", "%mm5", "%mm6", "%mm7" };
if (mmxreg < 0 || mmxreg > 7) vpanic("nameMMXReg(x86,guest)");
return mmx_names[mmxreg];
}
static const HChar* nameXMMReg ( Int xmmreg )
{
static const HChar* xmm_names[8]
= { "%xmm0", "%xmm1", "%xmm2", "%xmm3",
"%xmm4", "%xmm5", "%xmm6", "%xmm7" };
if (xmmreg < 0 || xmmreg > 7) vpanic("name_of_xmm_reg");
return xmm_names[xmmreg];
}
static const HChar* nameMMXGran ( Int gran )
{
switch (gran) {
case 0: return "b";
case 1: return "w";
case 2: return "d";
case 3: return "q";
default: vpanic("nameMMXGran(x86,guest)");
}
}
static HChar nameISize ( Int size )
{
switch (size) {
case 4: return 'l';
case 2: return 'w';
case 1: return 'b';
default: vpanic("nameISize(x86)");
}
}
/*------------------------------------------------------------*/
/*--- JMP helpers ---*/
/*------------------------------------------------------------*/
static void jmp_lit( /*MOD*/DisResult* dres,
IRJumpKind kind, Addr32 d32 )
{
vassert(dres->whatNext == Dis_Continue);
vassert(dres->len == 0);
vassert(dres->continueAt == 0);
vassert(dres->jk_StopHere == Ijk_INVALID);
dres->whatNext = Dis_StopHere;
dres->jk_StopHere = kind;
stmt( IRStmt_Put( OFFB_EIP, mkU32(d32) ) );
}
static void jmp_treg( /*MOD*/DisResult* dres,
IRJumpKind kind, IRTemp t )
{
vassert(dres->whatNext == Dis_Continue);
vassert(dres->len == 0);
vassert(dres->continueAt == 0);
vassert(dres->jk_StopHere == Ijk_INVALID);
dres->whatNext = Dis_StopHere;
dres->jk_StopHere = kind;
stmt( IRStmt_Put( OFFB_EIP, mkexpr(t) ) );
}
static
void jcc_01( /*MOD*/DisResult* dres,
X86Condcode cond, Addr32 d32_false, Addr32 d32_true )
{
Bool invert;
X86Condcode condPos;
vassert(dres->whatNext == Dis_Continue);
vassert(dres->len == 0);
vassert(dres->continueAt == 0);
vassert(dres->jk_StopHere == Ijk_INVALID);
dres->whatNext = Dis_StopHere;
dres->jk_StopHere = Ijk_Boring;
condPos = positiveIse_X86Condcode ( cond, &invert );
if (invert) {
stmt( IRStmt_Exit( mk_x86g_calculate_condition(condPos),
Ijk_Boring,
IRConst_U32(d32_false),
OFFB_EIP ) );
stmt( IRStmt_Put( OFFB_EIP, mkU32(d32_true) ) );
} else {
stmt( IRStmt_Exit( mk_x86g_calculate_condition(condPos),
Ijk_Boring,
IRConst_U32(d32_true),
OFFB_EIP ) );
stmt( IRStmt_Put( OFFB_EIP, mkU32(d32_false) ) );
}
}
/*------------------------------------------------------------*/
/*--- Disassembling addressing modes ---*/
/*------------------------------------------------------------*/
static
const HChar* sorbTxt ( UChar sorb )
{
switch (sorb) {
case 0: return ""; /* no override */
case 0x3E: return "%ds";
case 0x26: return "%es:";
case 0x64: return "%fs:";
case 0x65: return "%gs:";
default: vpanic("sorbTxt(x86,guest)");
}
}
/* 'virtual' is an IRExpr* holding a virtual address. Convert it to a
linear address by adding any required segment override as indicated
by sorb. */
static
IRExpr* handleSegOverride ( UChar sorb, IRExpr* virtual )
{
Int sreg;
IRType hWordTy;
IRTemp ldt_ptr, gdt_ptr, seg_selector, r64;
if (sorb == 0)
/* the common case - no override */
return virtual;
switch (sorb) {
case 0x3E: sreg = R_DS; break;
case 0x26: sreg = R_ES; break;
case 0x64: sreg = R_FS; break;
case 0x65: sreg = R_GS; break;
default: vpanic("handleSegOverride(x86,guest)");
}
hWordTy = sizeof(HWord)==4 ? Ity_I32 : Ity_I64;
seg_selector = newTemp(Ity_I32);
ldt_ptr = newTemp(hWordTy);
gdt_ptr = newTemp(hWordTy);
r64 = newTemp(Ity_I64);
assign( seg_selector, unop(Iop_16Uto32, getSReg(sreg)) );
assign( ldt_ptr, IRExpr_Get( OFFB_LDT, hWordTy ));
assign( gdt_ptr, IRExpr_Get( OFFB_GDT, hWordTy ));
/*
Call this to do the translation and limit checks:
ULong x86g_use_seg_selector ( HWord ldt, HWord gdt,
UInt seg_selector, UInt virtual_addr )
*/
assign(
r64,
mkIRExprCCall(
Ity_I64,
0/*regparms*/,
"x86g_use_seg_selector",
&x86g_use_seg_selector,
mkIRExprVec_4( mkexpr(ldt_ptr), mkexpr(gdt_ptr),
mkexpr(seg_selector), virtual)
)
);
/* If the high 32 of the result are non-zero, there was a
failure in address translation. In which case, make a
quick exit.
*/
stmt(
IRStmt_Exit(
binop(Iop_CmpNE32, unop(Iop_64HIto32, mkexpr(r64)), mkU32(0)),
Ijk_MapFail,
IRConst_U32( guest_EIP_curr_instr ),
OFFB_EIP
)
);
/* otherwise, here's the translated result. */
return unop(Iop_64to32, mkexpr(r64));
}
/* Generate IR to calculate an address indicated by a ModRM and
following SIB bytes. The expression, and the number of bytes in
the address mode, are returned. Note that this fn should not be
called if the R/M part of the address denotes a register instead of
memory. If print_codegen is true, text of the addressing mode is
placed in buf.
The computed address is stored in a new tempreg, and the
identity of the tempreg is returned. */
static IRTemp disAMode_copy2tmp ( IRExpr* addr32 )
{
IRTemp tmp = newTemp(Ity_I32);
assign( tmp, addr32 );
return tmp;
}
static
IRTemp disAMode ( Int* len, UChar sorb, Int delta, HChar* buf )
{
UChar mod_reg_rm = getIByte(delta);
delta++;
buf[0] = (UChar)0;
/* squeeze out the reg field from mod_reg_rm, since a 256-entry
jump table seems a bit excessive.
*/
mod_reg_rm &= 0xC7; /* is now XX000YYY */
mod_reg_rm = toUChar(mod_reg_rm | (mod_reg_rm >> 3));
/* is now XX0XXYYY */
mod_reg_rm &= 0x1F; /* is now 000XXYYY */
switch (mod_reg_rm) {
/* (%eax) .. (%edi), not including (%esp) or (%ebp).
--> GET %reg, t
*/
case 0x00: case 0x01: case 0x02: case 0x03:
/* ! 04 */ /* ! 05 */ case 0x06: case 0x07:
{ UChar rm = mod_reg_rm;
DIS(buf, "%s(%s)", sorbTxt(sorb), nameIReg(4,rm));
*len = 1;
return disAMode_copy2tmp(
handleSegOverride(sorb, getIReg(4,rm)));
}
/* d8(%eax) ... d8(%edi), not including d8(%esp)
--> GET %reg, t ; ADDL d8, t
*/
case 0x08: case 0x09: case 0x0A: case 0x0B:
/* ! 0C */ case 0x0D: case 0x0E: case 0x0F:
{ UChar rm = toUChar(mod_reg_rm & 7);
UInt d = getSDisp8(delta);
DIS(buf, "%s%d(%s)", sorbTxt(sorb), (Int)d, nameIReg(4,rm));
*len = 2;
return disAMode_copy2tmp(
handleSegOverride(sorb,
binop(Iop_Add32,getIReg(4,rm),mkU32(d))));
}
/* d32(%eax) ... d32(%edi), not including d32(%esp)
--> GET %reg, t ; ADDL d8, t
*/
case 0x10: case 0x11: case 0x12: case 0x13:
/* ! 14 */ case 0x15: case 0x16: case 0x17:
{ UChar rm = toUChar(mod_reg_rm & 7);
UInt d = getUDisp32(delta);
DIS(buf, "%s0x%x(%s)", sorbTxt(sorb), (Int)d, nameIReg(4,rm));
*len = 5;
return disAMode_copy2tmp(
handleSegOverride(sorb,
binop(Iop_Add32,getIReg(4,rm),mkU32(d))));
}
/* a register, %eax .. %edi. This shouldn't happen. */
case 0x18: case 0x19: case 0x1A: case 0x1B:
case 0x1C: case 0x1D: case 0x1E: case 0x1F:
vpanic("disAMode(x86): not an addr!");
/* a 32-bit literal address
--> MOV d32, tmp
*/
case 0x05:
{ UInt d = getUDisp32(delta);
*len = 5;
DIS(buf, "%s(0x%x)", sorbTxt(sorb), d);
return disAMode_copy2tmp(
handleSegOverride(sorb, mkU32(d)));
}
case 0x04: {
/* SIB, with no displacement. Special cases:
-- %esp cannot act as an index value.
If index_r indicates %esp, zero is used for the index.
-- when mod is zero and base indicates EBP, base is instead
a 32-bit literal.
It's all madness, I tell you. Extract %index, %base and
scale from the SIB byte. The value denoted is then:
| %index == %ESP && %base == %EBP
= d32 following SIB byte
| %index == %ESP && %base != %EBP
= %base
| %index != %ESP && %base == %EBP
= d32 following SIB byte + (%index << scale)
| %index != %ESP && %base != %ESP
= %base + (%index << scale)
What happens to the souls of CPU architects who dream up such
horrendous schemes, do you suppose?
*/
UChar sib = getIByte(delta);
UChar scale = toUChar((sib >> 6) & 3);
UChar index_r = toUChar((sib >> 3) & 7);
UChar base_r = toUChar(sib & 7);
delta++;
if (index_r != R_ESP && base_r != R_EBP) {
DIS(buf, "%s(%s,%s,%d)", sorbTxt(sorb),
nameIReg(4,base_r), nameIReg(4,index_r), 1<<scale);
*len = 2;
return
disAMode_copy2tmp(
handleSegOverride(sorb,
binop(Iop_Add32,
getIReg(4,base_r),
binop(Iop_Shl32, getIReg(4,index_r),
mkU8(scale)))));
}
if (index_r != R_ESP && base_r == R_EBP) {
UInt d = getUDisp32(delta);
DIS(buf, "%s0x%x(,%s,%d)", sorbTxt(sorb), d,
nameIReg(4,index_r), 1<<scale);
*len = 6;
return
disAMode_copy2tmp(
handleSegOverride(sorb,
binop(Iop_Add32,
binop(Iop_Shl32, getIReg(4,index_r), mkU8(scale)),
mkU32(d))));
}
if (index_r == R_ESP && base_r != R_EBP) {
DIS(buf, "%s(%s,,)", sorbTxt(sorb), nameIReg(4,base_r));
*len = 2;
return disAMode_copy2tmp(
handleSegOverride(sorb, getIReg(4,base_r)));
}
if (index_r == R_ESP && base_r == R_EBP) {
UInt d = getUDisp32(delta);
DIS(buf, "%s0x%x(,,)", sorbTxt(sorb), d);
*len = 6;
return disAMode_copy2tmp(
handleSegOverride(sorb, mkU32(d)));
}
/*NOTREACHED*/
vassert(0);
}
/* SIB, with 8-bit displacement. Special cases:
-- %esp cannot act as an index value.
If index_r indicates %esp, zero is used for the index.
Denoted value is:
| %index == %ESP
= d8 + %base
| %index != %ESP
= d8 + %base + (%index << scale)
*/
case 0x0C: {
UChar sib = getIByte(delta);
UChar scale = toUChar((sib >> 6) & 3);
UChar index_r = toUChar((sib >> 3) & 7);
UChar base_r = toUChar(sib & 7);
UInt d = getSDisp8(delta+1);
if (index_r == R_ESP) {
DIS(buf, "%s%d(%s,,)", sorbTxt(sorb),
(Int)d, nameIReg(4,base_r));
*len = 3;
return disAMode_copy2tmp(
handleSegOverride(sorb,
binop(Iop_Add32, getIReg(4,base_r), mkU32(d)) ));
} else {
DIS(buf, "%s%d(%s,%s,%d)", sorbTxt(sorb), (Int)d,
nameIReg(4,base_r), nameIReg(4,index_r), 1<<scale);
*len = 3;
return
disAMode_copy2tmp(
handleSegOverride(sorb,
binop(Iop_Add32,
binop(Iop_Add32,
getIReg(4,base_r),
binop(Iop_Shl32,
getIReg(4,index_r), mkU8(scale))),
mkU32(d))));
}
/*NOTREACHED*/
vassert(0);
}
/* SIB, with 32-bit displacement. Special cases:
-- %esp cannot act as an index value.
If index_r indicates %esp, zero is used for the index.
Denoted value is:
| %index == %ESP
= d32 + %base
| %index != %ESP
= d32 + %base + (%index << scale)
*/
case 0x14: {
UChar sib = getIByte(delta);
UChar scale = toUChar((sib >> 6) & 3);
UChar index_r = toUChar((sib >> 3) & 7);
UChar base_r = toUChar(sib & 7);
UInt d = getUDisp32(delta+1);
if (index_r == R_ESP) {
DIS(buf, "%s%d(%s,,)", sorbTxt(sorb),
(Int)d, nameIReg(4,base_r));
*len = 6;
return disAMode_copy2tmp(
handleSegOverride(sorb,
binop(Iop_Add32, getIReg(4,base_r), mkU32(d)) ));
} else {
DIS(buf, "%s%d(%s,%s,%d)", sorbTxt(sorb), (Int)d,
nameIReg(4,base_r), nameIReg(4,index_r), 1<<scale);
*len = 6;
return
disAMode_copy2tmp(
handleSegOverride(sorb,
binop(Iop_Add32,
binop(Iop_Add32,
getIReg(4,base_r),
binop(Iop_Shl32,
getIReg(4,index_r), mkU8(scale))),
mkU32(d))));
}
/*NOTREACHED*/
vassert(0);
}
default:
vpanic("disAMode(x86)");
return 0; /*notreached*/
}
}
/* Figure out the number of (insn-stream) bytes constituting the amode
beginning at delta. Is useful for getting hold of literals beyond
the end of the amode before it has been disassembled. */
static UInt lengthAMode ( Int delta )
{
UChar mod_reg_rm = getIByte(delta); delta++;
/* squeeze out the reg field from mod_reg_rm, since a 256-entry
jump table seems a bit excessive.
*/
mod_reg_rm &= 0xC7; /* is now XX000YYY */
mod_reg_rm = toUChar(mod_reg_rm | (mod_reg_rm >> 3));
/* is now XX0XXYYY */
mod_reg_rm &= 0x1F; /* is now 000XXYYY */
switch (mod_reg_rm) {
/* (%eax) .. (%edi), not including (%esp) or (%ebp). */
case 0x00: case 0x01: case 0x02: case 0x03:
/* ! 04 */ /* ! 05 */ case 0x06: case 0x07:
return 1;
/* d8(%eax) ... d8(%edi), not including d8(%esp). */
case 0x08: case 0x09: case 0x0A: case 0x0B:
/* ! 0C */ case 0x0D: case 0x0E: case 0x0F:
return 2;
/* d32(%eax) ... d32(%edi), not including d32(%esp). */
case 0x10: case 0x11: case 0x12: case 0x13:
/* ! 14 */ case 0x15: case 0x16: case 0x17:
return 5;
/* a register, %eax .. %edi. (Not an addr, but still handled.) */
case 0x18: case 0x19: case 0x1A: case 0x1B:
case 0x1C: case 0x1D: case 0x1E: case 0x1F:
return 1;
/* a 32-bit literal address. */
case 0x05: return 5;
/* SIB, no displacement. */
case 0x04: {
UChar sib = getIByte(delta);
UChar base_r = toUChar(sib & 7);
if (base_r == R_EBP) return 6; else return 2;
}
/* SIB, with 8-bit displacement. */
case 0x0C: return 3;
/* SIB, with 32-bit displacement. */
case 0x14: return 6;
default:
vpanic("lengthAMode");
return 0; /*notreached*/
}
}
/*------------------------------------------------------------*/
/*--- Disassembling common idioms ---*/
/*------------------------------------------------------------*/
/* Handle binary integer instructions of the form
op E, G meaning
op reg-or-mem, reg
Is passed the a ptr to the modRM byte, the actual operation, and the
data size. Returns the address advanced completely over this
instruction.
E(src) is reg-or-mem
G(dst) is reg.
If E is reg, --> GET %G, tmp
OP %E, tmp
PUT tmp, %G
If E is mem and OP is not reversible,
--> (getAddr E) -> tmpa
LD (tmpa), tmpa
GET %G, tmp2
OP tmpa, tmp2
PUT tmp2, %G
If E is mem and OP is reversible
--> (getAddr E) -> tmpa
LD (tmpa), tmpa
OP %G, tmpa
PUT tmpa, %G
*/
static
UInt dis_op2_E_G ( UChar sorb,
Bool addSubCarry,
IROp op8,
Bool keep,
Int size,
Int delta0,
const HChar* t_x86opc )
{
HChar dis_buf[50];
Int len;
IRType ty = szToITy(size);
IRTemp dst1 = newTemp(ty);
IRTemp src = newTemp(ty);
IRTemp dst0 = newTemp(ty);
UChar rm = getUChar(delta0);
IRTemp addr = IRTemp_INVALID;
/* addSubCarry == True indicates the intended operation is
add-with-carry or subtract-with-borrow. */
if (addSubCarry) {
vassert(op8 == Iop_Add8 || op8 == Iop_Sub8);
vassert(keep);
}
if (epartIsReg(rm)) {
/* Specially handle XOR reg,reg, because that doesn't really
depend on reg, and doing the obvious thing potentially
generates a spurious value check failure due to the bogus
dependency. Ditto SBB reg,reg. */
if ((op8 == Iop_Xor8 || (op8 == Iop_Sub8 && addSubCarry))
&& gregOfRM(rm) == eregOfRM(rm)) {
putIReg(size, gregOfRM(rm), mkU(ty,0));
}
assign( dst0, getIReg(size,gregOfRM(rm)) );
assign( src, getIReg(size,eregOfRM(rm)) );
if (addSubCarry && op8 == Iop_Add8) {
helper_ADC( size, dst1, dst0, src,
/*no store*/IRTemp_INVALID, IRTemp_INVALID, 0 );
putIReg(size, gregOfRM(rm), mkexpr(dst1));
} else
if (addSubCarry && op8 == Iop_Sub8) {
helper_SBB( size, dst1, dst0, src,
/*no store*/IRTemp_INVALID, IRTemp_INVALID, 0 );
putIReg(size, gregOfRM(rm), mkexpr(dst1));
} else {
assign( dst1, binop(mkSizedOp(ty,op8), mkexpr(dst0), mkexpr(src)) );
if (isAddSub(op8))
setFlags_DEP1_DEP2(op8, dst0, src, ty);
else
setFlags_DEP1(op8, dst1, ty);
if (keep)
putIReg(size, gregOfRM(rm), mkexpr(dst1));
}
DIP("%s%c %s,%s\n", t_x86opc, nameISize(size),
nameIReg(size,eregOfRM(rm)),
nameIReg(size,gregOfRM(rm)));
return 1+delta0;
} else {
/* E refers to memory */
addr = disAMode ( &len, sorb, delta0, dis_buf);
assign( dst0, getIReg(size,gregOfRM(rm)) );
assign( src, loadLE(szToITy(size), mkexpr(addr)) );
if (addSubCarry && op8 == Iop_Add8) {
helper_ADC( size, dst1, dst0, src,
/*no store*/IRTemp_INVALID, IRTemp_INVALID, 0 );
putIReg(size, gregOfRM(rm), mkexpr(dst1));
} else
if (addSubCarry && op8 == Iop_Sub8) {
helper_SBB( size, dst1, dst0, src,
/*no store*/IRTemp_INVALID, IRTemp_INVALID, 0 );
putIReg(size, gregOfRM(rm), mkexpr(dst1));
} else {
assign( dst1, binop(mkSizedOp(ty,op8), mkexpr(dst0), mkexpr(src)) );
if (isAddSub(op8))
setFlags_DEP1_DEP2(op8, dst0, src, ty);
else
setFlags_DEP1(op8, dst1, ty);
if (keep)
putIReg(size, gregOfRM(rm), mkexpr(dst1));
}
DIP("%s%c %s,%s\n", t_x86opc, nameISize(size),
dis_buf,nameIReg(size,gregOfRM(rm)));
return len+delta0;
}
}
/* Handle binary integer instructions of the form
op G, E meaning
op reg, reg-or-mem
Is passed the a ptr to the modRM byte, the actual operation, and the
data size. Returns the address advanced completely over this
instruction.
G(src) is reg.
E(dst) is reg-or-mem
If E is reg, --> GET %E, tmp
OP %G, tmp
PUT tmp, %E
If E is mem, --> (getAddr E) -> tmpa
LD (tmpa), tmpv
OP %G, tmpv
ST tmpv, (tmpa)
*/
static
UInt dis_op2_G_E ( UChar sorb,
Bool locked,
Bool addSubCarry,
IROp op8,
Bool keep,
Int size,
Int delta0,
const HChar* t_x86opc )
{
HChar dis_buf[50];
Int len;
IRType ty = szToITy(size);
IRTemp dst1 = newTemp(ty);
IRTemp src = newTemp(ty);
IRTemp dst0 = newTemp(ty);
UChar rm = getIByte(delta0);
IRTemp addr = IRTemp_INVALID;
/* addSubCarry == True indicates the intended operation is
add-with-carry or subtract-with-borrow. */
if (addSubCarry) {
vassert(op8 == Iop_Add8 || op8 == Iop_Sub8);
vassert(keep);
}
if (epartIsReg(rm)) {
/* Specially handle XOR reg,reg, because that doesn't really
depend on reg, and doing the obvious thing potentially
generates a spurious value check failure due to the bogus
dependency. Ditto SBB reg,reg.*/
if ((op8 == Iop_Xor8 || (op8 == Iop_Sub8 && addSubCarry))
&& gregOfRM(rm) == eregOfRM(rm)) {
putIReg(size, eregOfRM(rm), mkU(ty,0));
}
assign(dst0, getIReg(size,eregOfRM(rm)));
assign(src, getIReg(size,gregOfRM(rm)));
if (addSubCarry && op8 == Iop_Add8) {
helper_ADC( size, dst1, dst0, src,
/*no store*/IRTemp_INVALID, IRTemp_INVALID, 0 );
putIReg(size, eregOfRM(rm), mkexpr(dst1));
} else
if (addSubCarry && op8 == Iop_Sub8) {
helper_SBB( size, dst1, dst0, src,
/*no store*/IRTemp_INVALID, IRTemp_INVALID, 0 );
putIReg(size, eregOfRM(rm), mkexpr(dst1));
} else {
assign(dst1, binop(mkSizedOp(ty,op8), mkexpr(dst0), mkexpr(src)));
if (isAddSub(op8))
setFlags_DEP1_DEP2(op8, dst0, src, ty);
else
setFlags_DEP1(op8, dst1, ty);
if (keep)
putIReg(size, eregOfRM(rm), mkexpr(dst1));
}
DIP("%s%c %s,%s\n", t_x86opc, nameISize(size),
nameIReg(size,gregOfRM(rm)),
nameIReg(size,eregOfRM(rm)));
return 1+delta0;
}
/* E refers to memory */
{
addr = disAMode ( &len, sorb, delta0, dis_buf);
assign(dst0, loadLE(ty,mkexpr(addr)));
assign(src, getIReg(size,gregOfRM(rm)));
if (addSubCarry && op8 == Iop_Add8) {
if (locked) {
/* cas-style store */
helper_ADC( size, dst1, dst0, src,
/*store*/addr, dst0/*expVal*/, guest_EIP_curr_instr );
} else {
/* normal store */
helper_ADC( size, dst1, dst0, src,
/*store*/addr, IRTemp_INVALID, 0 );
}
} else
if (addSubCarry && op8 == Iop_Sub8) {
if (locked) {
/* cas-style store */
helper_SBB( size, dst1, dst0, src,
/*store*/addr, dst0/*expVal*/, guest_EIP_curr_instr );
} else {
/* normal store */
helper_SBB( size, dst1, dst0, src,
/*store*/addr, IRTemp_INVALID, 0 );
}
} else {
assign(dst1, binop(mkSizedOp(ty,op8), mkexpr(dst0), mkexpr(src)));
if (keep) {
if (locked) {
if (0) vex_printf("locked case\n" );
casLE( mkexpr(addr),
mkexpr(dst0)/*expval*/,
mkexpr(dst1)/*newval*/, guest_EIP_curr_instr );
} else {
if (0) vex_printf("nonlocked case\n");
storeLE(mkexpr(addr), mkexpr(dst1));
}
}
if (isAddSub(op8))
setFlags_DEP1_DEP2(op8, dst0, src, ty);
else
setFlags_DEP1(op8, dst1, ty);
}
DIP("%s%c %s,%s\n", t_x86opc, nameISize(size),
nameIReg(size,gregOfRM(rm)), dis_buf);
return len+delta0;
}
}
/* Handle move instructions of the form
mov E, G meaning
mov reg-or-mem, reg
Is passed the a ptr to the modRM byte, and the data size. Returns
the address advanced completely over this instruction.
E(src) is reg-or-mem
G(dst) is reg.
If E is reg, --> GET %E, tmpv
PUT tmpv, %G
If E is mem --> (getAddr E) -> tmpa
LD (tmpa), tmpb
PUT tmpb, %G
*/
static
UInt dis_mov_E_G ( UChar sorb,
Int size,
Int delta0 )
{
Int len;
UChar rm = getIByte(delta0);
HChar dis_buf[50];
if (epartIsReg(rm)) {
putIReg(size, gregOfRM(rm), getIReg(size, eregOfRM(rm)));
DIP("mov%c %s,%s\n", nameISize(size),
nameIReg(size,eregOfRM(rm)),
nameIReg(size,gregOfRM(rm)));
return 1+delta0;
}
/* E refers to memory */
{
IRTemp addr = disAMode ( &len, sorb, delta0, dis_buf );
putIReg(size, gregOfRM(rm), loadLE(szToITy(size), mkexpr(addr)));
DIP("mov%c %s,%s\n", nameISize(size),
dis_buf,nameIReg(size,gregOfRM(rm)));
return delta0+len;
}
}
/* Handle move instructions of the form
mov G, E meaning
mov reg, reg-or-mem
Is passed the a ptr to the modRM byte, and the data size. Returns
the address advanced completely over this instruction.
G(src) is reg.
E(dst) is reg-or-mem
If E is reg, --> GET %G, tmp
PUT tmp, %E
If E is mem, --> (getAddr E) -> tmpa
GET %G, tmpv
ST tmpv, (tmpa)
*/
static
UInt dis_mov_G_E ( UChar sorb,
Int size,
Int delta0 )
{
Int len;
UChar rm = getIByte(delta0);
HChar dis_buf[50];
if (epartIsReg(rm)) {
putIReg(size, eregOfRM(rm), getIReg(size, gregOfRM(rm)));
DIP("mov%c %s,%s\n", nameISize(size),
nameIReg(size,gregOfRM(rm)),
nameIReg(size,eregOfRM(rm)));
return 1+delta0;
}
/* E refers to memory */
{
IRTemp addr = disAMode ( &len, sorb, delta0, dis_buf);
storeLE( mkexpr(addr), getIReg(size, gregOfRM(rm)) );
DIP("mov%c %s,%s\n", nameISize(size),
nameIReg(size,gregOfRM(rm)), dis_buf);
return len+delta0;
}
}
/* op $immediate, AL/AX/EAX. */
static
UInt dis_op_imm_A ( Int size,
Bool carrying,
IROp op8,
Bool keep,
Int delta,
const HChar* t_x86opc )
{
IRType ty = szToITy(size);
IRTemp dst0 = newTemp(ty);
IRTemp src = newTemp(ty);
IRTemp dst1 = newTemp(ty);
UInt lit = getUDisp(size,delta);
assign(dst0, getIReg(size,R_EAX));
assign(src, mkU(ty,lit));
if (isAddSub(op8) && !carrying) {
assign(dst1, binop(mkSizedOp(ty,op8), mkexpr(dst0), mkexpr(src)) );
setFlags_DEP1_DEP2(op8, dst0, src, ty);
}
else
if (isLogic(op8)) {
vassert(!carrying);
assign(dst1, binop(mkSizedOp(ty,op8), mkexpr(dst0), mkexpr(src)) );
setFlags_DEP1(op8, dst1, ty);
}
else
if (op8 == Iop_Add8 && carrying) {
helper_ADC( size, dst1, dst0, src,
/*no store*/IRTemp_INVALID, IRTemp_INVALID, 0 );
}
else
if (op8 == Iop_Sub8 && carrying) {
helper_SBB( size, dst1, dst0, src,
/*no store*/IRTemp_INVALID, IRTemp_INVALID, 0 );
}
else
vpanic("dis_op_imm_A(x86,guest)");
if (keep)
putIReg(size, R_EAX, mkexpr(dst1));
DIP("%s%c $0x%x, %s\n", t_x86opc, nameISize(size),
lit, nameIReg(size,R_EAX));
return delta+size;
}
/* Sign- and Zero-extending moves. */
static
UInt dis_movx_E_G ( UChar sorb,
Int delta, Int szs, Int szd, Bool sign_extend )
{
UChar rm = getIByte(delta);
if (epartIsReg(rm)) {
if (szd == szs) {
// mutant case. See #250799
putIReg(szd, gregOfRM(rm),
getIReg(szs,eregOfRM(rm)));
} else {
// normal case
putIReg(szd, gregOfRM(rm),
unop(mkWidenOp(szs,szd,sign_extend),
getIReg(szs,eregOfRM(rm))));
}
DIP("mov%c%c%c %s,%s\n", sign_extend ? 's' : 'z',
nameISize(szs), nameISize(szd),
nameIReg(szs,eregOfRM(rm)),
nameIReg(szd,gregOfRM(rm)));
return 1+delta;
}
/* E refers to memory */
{
Int len;
HChar dis_buf[50];
IRTemp addr = disAMode ( &len, sorb, delta, dis_buf );
if (szd == szs) {
// mutant case. See #250799
putIReg(szd, gregOfRM(rm),
loadLE(szToITy(szs),mkexpr(addr)));
} else {
// normal case
putIReg(szd, gregOfRM(rm),
unop(mkWidenOp(szs,szd,sign_extend),
loadLE(szToITy(szs),mkexpr(addr))));
}
DIP("mov%c%c%c %s,%s\n", sign_extend ? 's' : 'z',
nameISize(szs), nameISize(szd),
dis_buf, nameIReg(szd,gregOfRM(rm)));
return len+delta;
}
}
/* Generate code to divide ArchRegs EDX:EAX / DX:AX / AX by the 32 /
16 / 8 bit quantity in the given IRTemp. */
static
void codegen_div ( Int sz, IRTemp t, Bool signed_divide )
{
IROp op = signed_divide ? Iop_DivModS64to32 : Iop_DivModU64to32;
IRTemp src64 = newTemp(Ity_I64);
IRTemp dst64 = newTemp(Ity_I64);
switch (sz) {
case 4:
assign( src64, binop(Iop_32HLto64,
getIReg(4,R_EDX), getIReg(4,R_EAX)) );
assign( dst64, binop(op, mkexpr(src64), mkexpr(t)) );
putIReg( 4, R_EAX, unop(Iop_64to32,mkexpr(dst64)) );
putIReg( 4, R_EDX, unop(Iop_64HIto32,mkexpr(dst64)) );
break;
case 2: {
IROp widen3264 = signed_divide ? Iop_32Sto64 : Iop_32Uto64;
IROp widen1632 = signed_divide ? Iop_16Sto32 : Iop_16Uto32;
assign( src64, unop(widen3264,
binop(Iop_16HLto32,
getIReg(2,R_EDX), getIReg(2,R_EAX))) );
assign( dst64, binop(op, mkexpr(src64), unop(widen1632,mkexpr(t))) );
putIReg( 2, R_EAX, unop(Iop_32to16,unop(Iop_64to32,mkexpr(dst64))) );
putIReg( 2, R_EDX, unop(Iop_32to16,unop(Iop_64HIto32,mkexpr(dst64))) );
break;
}
case 1: {
IROp widen3264 = signed_divide ? Iop_32Sto64 : Iop_32Uto64;
IROp widen1632 = signed_divide ? Iop_16Sto32 : Iop_16Uto32;
IROp widen816 = signed_divide ? Iop_8Sto16 : Iop_8Uto16;
assign( src64, unop(widen3264, unop(widen1632, getIReg(2,R_EAX))) );
assign( dst64,
binop(op, mkexpr(src64),
unop(widen1632, unop(widen816, mkexpr(t)))) );
putIReg( 1, R_AL, unop(Iop_16to8, unop(Iop_32to16,
unop(Iop_64to32,mkexpr(dst64)))) );
putIReg( 1, R_AH, unop(Iop_16to8, unop(Iop_32to16,
unop(Iop_64HIto32,mkexpr(dst64)))) );
break;
}
default: vpanic("codegen_div(x86)");
}
}
static
UInt dis_Grp1 ( UChar sorb, Bool locked,
Int delta, UChar modrm,
Int am_sz, Int d_sz, Int sz, UInt d32 )
{
Int len;
HChar dis_buf[50];
IRType ty = szToITy(sz);
IRTemp dst1 = newTemp(ty);
IRTemp src = newTemp(ty);
IRTemp dst0 = newTemp(ty);
IRTemp addr = IRTemp_INVALID;
IROp op8 = Iop_INVALID;
UInt mask = sz==1 ? 0xFF : (sz==2 ? 0xFFFF : 0xFFFFFFFF);
switch (gregOfRM(modrm)) {
case 0: op8 = Iop_Add8; break; case 1: op8 = Iop_Or8; break;
case 2: break; // ADC
case 3: break; // SBB
case 4: op8 = Iop_And8; break; case 5: op8 = Iop_Sub8; break;
case 6: op8 = Iop_Xor8; break; case 7: op8 = Iop_Sub8; break;
/*NOTREACHED*/
default: vpanic("dis_Grp1: unhandled case");
}
if (epartIsReg(modrm)) {
vassert(am_sz == 1);
assign(dst0, getIReg(sz,eregOfRM(modrm)));
assign(src, mkU(ty,d32 & mask));
if (gregOfRM(modrm) == 2 /* ADC */) {
helper_ADC( sz, dst1, dst0, src,
/*no store*/IRTemp_INVALID, IRTemp_INVALID, 0 );
} else
if (gregOfRM(modrm) == 3 /* SBB */) {
helper_SBB( sz, dst1, dst0, src,
/*no store*/IRTemp_INVALID, IRTemp_INVALID, 0 );
} else {
assign(dst1, binop(mkSizedOp(ty,op8), mkexpr(dst0), mkexpr(src)));
if (isAddSub(op8))
setFlags_DEP1_DEP2(op8, dst0, src, ty);
else
setFlags_DEP1(op8, dst1, ty);
}
if (gregOfRM(modrm) < 7)
putIReg(sz, eregOfRM(modrm), mkexpr(dst1));
delta += (am_sz + d_sz);
DIP("%s%c $0x%x, %s\n", nameGrp1(gregOfRM(modrm)), nameISize(sz), d32,
nameIReg(sz,eregOfRM(modrm)));
} else {
addr = disAMode ( &len, sorb, delta, dis_buf);
assign(dst0, loadLE(ty,mkexpr(addr)));
assign(src, mkU(ty,d32 & mask));
if (gregOfRM(modrm) == 2 /* ADC */) {
if (locked) {
/* cas-style store */
helper_ADC( sz, dst1, dst0, src,
/*store*/addr, dst0/*expVal*/, guest_EIP_curr_instr );
} else {
/* normal store */
helper_ADC( sz, dst1, dst0, src,
/*store*/addr, IRTemp_INVALID, 0 );
}
} else
if (gregOfRM(modrm) == 3 /* SBB */) {
if (locked) {
/* cas-style store */
helper_SBB( sz, dst1, dst0, src,
/*store*/addr, dst0/*expVal*/, guest_EIP_curr_instr );
} else {
/* normal store */
helper_SBB( sz, dst1, dst0, src,
/*store*/addr, IRTemp_INVALID, 0 );
}
} else {
assign(dst1, binop(mkSizedOp(ty,op8), mkexpr(dst0), mkexpr(src)));
if (gregOfRM(modrm) < 7) {
if (locked) {
casLE( mkexpr(addr), mkexpr(dst0)/*expVal*/,
mkexpr(dst1)/*newVal*/,
guest_EIP_curr_instr );
} else {
storeLE(mkexpr(addr), mkexpr(dst1));
}
}
if (isAddSub(op8))
setFlags_DEP1_DEP2(op8, dst0, src, ty);
else
setFlags_DEP1(op8, dst1, ty);
}
delta += (len+d_sz);
DIP("%s%c $0x%x, %s\n", nameGrp1(gregOfRM(modrm)), nameISize(sz),
d32, dis_buf);
}
return delta;
}
/* Group 2 extended opcodes. shift_expr must be an 8-bit typed
expression. */
static
UInt dis_Grp2 ( UChar sorb,
Int delta, UChar modrm,
Int am_sz, Int d_sz, Int sz, IRExpr* shift_expr,
const HChar* shift_expr_txt, Bool* decode_OK )
{
/* delta on entry points at the modrm byte. */
HChar dis_buf[50];
Int len;
Bool isShift, isRotate, isRotateC;
IRType ty = szToITy(sz);
IRTemp dst0 = newTemp(ty);
IRTemp dst1 = newTemp(ty);
IRTemp addr = IRTemp_INVALID;
*decode_OK = True;
vassert(sz == 1 || sz == 2 || sz == 4);
/* Put value to shift/rotate in dst0. */
if (epartIsReg(modrm)) {
assign(dst0, getIReg(sz, eregOfRM(modrm)));
delta += (am_sz + d_sz);
} else {
addr = disAMode ( &len, sorb, delta, dis_buf);
assign(dst0, loadLE(ty,mkexpr(addr)));
delta += len + d_sz;
}
isShift = False;
switch (gregOfRM(modrm)) { case 4: case 5: case 6: case 7: isShift = True; }
isRotate = False;
switch (gregOfRM(modrm)) { case 0: case 1: isRotate = True; }
isRotateC = False;
switch (gregOfRM(modrm)) { case 2: case 3: isRotateC = True; }
if (!isShift && !isRotate && !isRotateC) {
/*NOTREACHED*/
vpanic("dis_Grp2(Reg): unhandled case(x86)");
}
if (isRotateC) {
/* call a helper; these insns are so ridiculous they do not
deserve better */
Bool left = toBool(gregOfRM(modrm) == 2);
IRTemp r64 = newTemp(Ity_I64);
IRExpr** args
= mkIRExprVec_4( widenUto32(mkexpr(dst0)), /* thing to rotate */
widenUto32(shift_expr), /* rotate amount */
widenUto32(mk_x86g_calculate_eflags_all()),
mkU32(sz) );
assign( r64, mkIRExprCCall(
Ity_I64,
0/*regparm*/,
left ? "x86g_calculate_RCL" : "x86g_calculate_RCR",
left ? &x86g_calculate_RCL : &x86g_calculate_RCR,
args
)
);
/* new eflags in hi half r64; new value in lo half r64 */
assign( dst1, narrowTo(ty, unop(Iop_64to32, mkexpr(r64))) );
stmt( IRStmt_Put( OFFB_CC_OP, mkU32(X86G_CC_OP_COPY) ));
stmt( IRStmt_Put( OFFB_CC_DEP1, unop(Iop_64HIto32, mkexpr(r64)) ));
stmt( IRStmt_Put( OFFB_CC_DEP2, mkU32(0) ));
/* Set NDEP even though it isn't used. This makes redundant-PUT
elimination of previous stores to this field work better. */
stmt( IRStmt_Put( OFFB_CC_NDEP, mkU32(0) ));
}
if (isShift) {
IRTemp pre32 = newTemp(Ity_I32);
IRTemp res32 = newTemp(Ity_I32);
IRTemp res32ss = newTemp(Ity_I32);
IRTemp shift_amt = newTemp(Ity_I8);
IROp op32;
switch (gregOfRM(modrm)) {
case 4: op32 = Iop_Shl32; break;
case 5: op32 = Iop_Shr32; break;
case 6: op32 = Iop_Shl32; break;
case 7: op32 = Iop_Sar32; break;
/*NOTREACHED*/
default: vpanic("dis_Grp2:shift"); break;
}
/* Widen the value to be shifted to 32 bits, do the shift, and
narrow back down. This seems surprisingly long-winded, but
unfortunately the Intel semantics requires that 8/16-bit
shifts give defined results for shift values all the way up
to 31, and this seems the simplest way to do it. It has the
advantage that the only IR level shifts generated are of 32
bit values, and the shift amount is guaranteed to be in the
range 0 .. 31, thereby observing the IR semantics requiring
all shift values to be in the range 0 .. 2^word_size-1. */
/* shift_amt = shift_expr & 31, regardless of operation size */
assign( shift_amt, binop(Iop_And8, shift_expr, mkU8(31)) );
/* suitably widen the value to be shifted to 32 bits. */
assign( pre32, op32==Iop_Sar32 ? widenSto32(mkexpr(dst0))
: widenUto32(mkexpr(dst0)) );
/* res32 = pre32 `shift` shift_amt */
assign( res32, binop(op32, mkexpr(pre32), mkexpr(shift_amt)) );
/* res32ss = pre32 `shift` ((shift_amt - 1) & 31) */
assign( res32ss,
binop(op32,
mkexpr(pre32),
binop(Iop_And8,
binop(Iop_Sub8,
mkexpr(shift_amt), mkU8(1)),
mkU8(31))) );
/* Build the flags thunk. */
setFlags_DEP1_DEP2_shift(op32, res32, res32ss, ty, shift_amt);
/* Narrow the result back down. */
assign( dst1, narrowTo(ty, mkexpr(res32)) );
} /* if (isShift) */
else
if (isRotate) {
Int ccOp = ty==Ity_I8 ? 0 : (ty==Ity_I16 ? 1 : 2);
Bool left = toBool(gregOfRM(modrm) == 0);
IRTemp rot_amt = newTemp(Ity_I8);
IRTemp rot_amt32 = newTemp(Ity_I8);
IRTemp oldFlags = newTemp(Ity_I32);
/* rot_amt = shift_expr & mask */
/* By masking the rotate amount thusly, the IR-level Shl/Shr
expressions never shift beyond the word size and thus remain
well defined. */
assign(rot_amt32, binop(Iop_And8, shift_expr, mkU8(31)));
if (ty == Ity_I32)
assign(rot_amt, mkexpr(rot_amt32));
else
assign(rot_amt, binop(Iop_And8, mkexpr(rot_amt32), mkU8(8*sz-1)));
if (left) {
/* dst1 = (dst0 << rot_amt) | (dst0 >>u (wordsize-rot_amt)) */
assign(dst1,
binop( mkSizedOp(ty,Iop_Or8),
binop( mkSizedOp(ty,Iop_Shl8),
mkexpr(dst0),
mkexpr(rot_amt)
),
binop( mkSizedOp(ty,Iop_Shr8),
mkexpr(dst0),
binop(Iop_Sub8,mkU8(8*sz), mkexpr(rot_amt))
)
)
);
ccOp += X86G_CC_OP_ROLB;
} else { /* right */
/* dst1 = (dst0 >>u rot_amt) | (dst0 << (wordsize-rot_amt)) */
assign(dst1,
binop( mkSizedOp(ty,Iop_Or8),
binop( mkSizedOp(ty,Iop_Shr8),
mkexpr(dst0),
mkexpr(rot_amt)
),
binop( mkSizedOp(ty,Iop_Shl8),
mkexpr(dst0),
binop(Iop_Sub8,mkU8(8*sz), mkexpr(rot_amt))
)
)
);
ccOp += X86G_CC_OP_RORB;
}
/* dst1 now holds the rotated value. Build flag thunk. We
need the resulting value for this, and the previous flags.
Except don't set it if the rotate count is zero. */
assign(oldFlags, mk_x86g_calculate_eflags_all());
/* rot_amt32 :: Ity_I8. We need to convert it to I1. */
IRTemp rot_amt32b = newTemp(Ity_I1);
assign(rot_amt32b, binop(Iop_CmpNE8, mkexpr(rot_amt32), mkU8(0)) );
/* CC_DEP1 is the rotated value. CC_NDEP is flags before. */
stmt( IRStmt_Put( OFFB_CC_OP,
IRExpr_ITE( mkexpr(rot_amt32b),
mkU32(ccOp),
IRExpr_Get(OFFB_CC_OP,Ity_I32) ) ));
stmt( IRStmt_Put( OFFB_CC_DEP1,
IRExpr_ITE( mkexpr(rot_amt32b),
widenUto32(mkexpr(dst1)),
IRExpr_Get(OFFB_CC_DEP1,Ity_I32) ) ));
stmt( IRStmt_Put( OFFB_CC_DEP2,
IRExpr_ITE( mkexpr(rot_amt32b),
mkU32(0),
IRExpr_Get(OFFB_CC_DEP2,Ity_I32) ) ));
stmt( IRStmt_Put( OFFB_CC_NDEP,
IRExpr_ITE( mkexpr(rot_amt32b),
mkexpr(oldFlags),
IRExpr_Get(OFFB_CC_NDEP,Ity_I32) ) ));
} /* if (isRotate) */
/* Save result, and finish up. */
if (epartIsReg(modrm)) {
putIReg(sz, eregOfRM(modrm), mkexpr(dst1));
if (vex_traceflags & VEX_TRACE_FE) {
vex_printf("%s%c ",
nameGrp2(gregOfRM(modrm)), nameISize(sz) );
if (shift_expr_txt)
vex_printf("%s", shift_expr_txt);
else
ppIRExpr(shift_expr);
vex_printf(", %s\n", nameIReg(sz,eregOfRM(modrm)));
}
} else {
storeLE(mkexpr(addr), mkexpr(dst1));
if (vex_traceflags & VEX_TRACE_FE) {
vex_printf("%s%c ",
nameGrp2(gregOfRM(modrm)), nameISize(sz) );
if (shift_expr_txt)