blob: 7dbd7a9176ae3729435276bcfa3317068b34368a [file] [log] [blame]
/*--------------------------------------------------------------------*/
/*--- begin guest_amd64_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 AMD64 code to IR. */
/* TODO:
All Puts to CC_OP/CC_DEP1/CC_DEP2/CC_NDEP should really be checked
to ensure a 64-bit value is being written.
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.
This module uses global variables and so is not MT-safe (if that
should ever become relevant).
*/
/* Notes re address size overrides (0x67).
According to the AMD documentation (24594 Rev 3.09, Sept 2003,
"AMD64 Architecture Programmer's Manual Volume 3: General-Purpose
and System Instructions"), Section 1.2.3 ("Address-Size Override
Prefix"):
0x67 applies to all explicit memory references, causing the top
32 bits of the effective address to become zero.
0x67 has no effect on stack references (push/pop); these always
use a 64-bit address.
0x67 changes the interpretation of instructions which implicitly
reference RCX/RSI/RDI, so that in fact ECX/ESI/EDI are used
instead. These are:
cmp{s,sb,sw,sd,sq}
in{s,sb,sw,sd}
jcxz, jecxz, jrcxz
lod{s,sb,sw,sd,sq}
loop{,e,bz,be,z}
mov{s,sb,sw,sd,sq}
out{s,sb,sw,sd}
rep{,e,ne,nz}
sca{s,sb,sw,sd,sq}
sto{s,sb,sw,sd,sq}
xlat{,b} */
/* "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 16-byte preamble 48C1C703 48C1C70D
48C1C73D 48C1C733 (in the standard interpretation, that means: rolq
$3, %rdi; rolq $13, %rdi; rolq $61, %rdi; rolq $51, %rdi).
Following that, one of the following 3 are allowed (standard
interpretation in parentheses):
4887DB (xchgq %rbx,%rbx) %RDX = client_request ( %RAX )
4887C9 (xchgq %rcx,%rcx) %RAX = guest_NRADDR
4887D2 (xchgq %rdx,%rdx) call-noredir *%RAX
4887F6 (xchgq %rdi,%rdi) IR injection
Any other bytes following the 16-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.
*/
/* 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.
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.
*/
/* 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_amd64.h"
#include "main_util.h"
#include "main_globals.h"
#include "guest_generic_bb_to_IR.h"
#include "guest_generic_x87.h"
#include "guest_amd64_defs.h"
/*------------------------------------------------------------*/
/*--- Globals ---*/
/*------------------------------------------------------------*/
/* These are set at the start of the translation of an insn, right
down in disInstr_AMD64, so that we don't have to pass them around
endlessly. They are all constant during the translation of any
given insn. */
/* These are set at the start of the translation of a BB, so
that we don't have to pass them around endlessly. */
/* 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 const UChar* guest_code;
/* The guest address corresponding to guest_code[0]. */
static Addr64 guest_RIP_bbstart;
/* The guest address for the instruction currently being
translated. */
static Addr64 guest_RIP_curr_instr;
/* The IRSB* into which we're generating code. */
static IRSB* irsb;
/* For ensuring that %rip-relative addressing is done right. A read
of %rip generates the address of the next instruction. It may be
that we don't conveniently know that inside disAMode(). For sanity
checking, if the next insn %rip is needed, we make a guess at what
it is, record that guess here, and set the accompanying Bool to
indicate that -- after this insn's decode is finished -- that guess
needs to be checked. */
/* At the start of each insn decode, is set to (0, False).
After the decode, if _mustcheck is now True, _assumed is
checked. */
static Addr64 guest_RIP_next_assumed;
static Bool guest_RIP_next_mustcheck;
/*------------------------------------------------------------*/
/*--- Helpers for constructing IR. ---*/
/*------------------------------------------------------------*/
/* Generate a new temporary of the given type. */
static IRTemp newTemp ( IRType ty )
{
vassert(isPlausibleIRType(ty));
return newIRTemp( irsb->tyenv, ty );
}
/* Add a statement to the list held by "irsb". */
static void stmt ( IRStmt* st )
{
addStmtToIRSB( irsb, st );
}
/* Generate a statement "dst := e". */
static void assign ( IRTemp dst, IRExpr* e )
{
stmt( IRStmt_WrTmp(dst, e) );
}
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 ( ULong i )
{
vassert(i < 256);
return IRExpr_Const(IRConst_U8( (UChar)i ));
}
static IRExpr* mkU16 ( ULong i )
{
vassert(i < 0x10000ULL);
return IRExpr_Const(IRConst_U16( (UShort)i ));
}
static IRExpr* mkU32 ( ULong i )
{
vassert(i < 0x100000000ULL);
return IRExpr_Const(IRConst_U32( (UInt)i ));
}
static IRExpr* mkU64 ( ULong i )
{
return IRExpr_Const(IRConst_U64(i));
}
static IRExpr* mkU ( IRType ty, ULong i )
{
switch (ty) {
case Ity_I8: return mkU8(i);
case Ity_I16: return mkU16(i);
case Ity_I32: return mkU32(i);
case Ity_I64: return mkU64(i);
default: vpanic("mkU(amd64)");
}
}
static void storeLE ( IRExpr* addr, IRExpr* data )
{
stmt( IRStmt_Store(Iend_LE, addr, data) );
}
static IRExpr* loadLE ( IRType ty, IRExpr* addr )
{
return IRExpr_Load(Iend_LE, ty, addr);
}
static IROp mkSizedOp ( IRType ty, IROp op8 )
{
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_Not8 );
switch (ty) {
case Ity_I8: return 0 +op8;
case Ity_I16: return 1 +op8;
case Ity_I32: return 2 +op8;
case Ity_I64: return 3 +op8;
default: vpanic("mkSizedOp(amd64)");
}
}
static
IRExpr* doScalarWidening ( Int szSmall, Int szBig, Bool signd, IRExpr* src )
{
if (szSmall == 1 && szBig == 4) {
return unop(signd ? Iop_8Sto32 : Iop_8Uto32, src);
}
if (szSmall == 1 && szBig == 2) {
return unop(signd ? Iop_8Sto16 : Iop_8Uto16, src);
}
if (szSmall == 2 && szBig == 4) {
return unop(signd ? Iop_16Sto32 : Iop_16Uto32, src);
}
if (szSmall == 1 && szBig == 8 && !signd) {
return unop(Iop_8Uto64, src);
}
if (szSmall == 1 && szBig == 8 && signd) {
return unop(Iop_8Sto64, src);
}
if (szSmall == 2 && szBig == 8 && !signd) {
return unop(Iop_16Uto64, src);
}
if (szSmall == 2 && szBig == 8 && signd) {
return unop(Iop_16Sto64, src);
}
vpanic("doScalarWidening(amd64)");
}
/*------------------------------------------------------------*/
/*--- Debugging output ---*/
/*------------------------------------------------------------*/
/* Bomb out if we can't handle something. */
__attribute__ ((noreturn))
static void unimplemented ( const HChar* str )
{
vex_printf("amd64toIR: unimplemented feature\n");
vpanic(str);
}
#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 amd64 guest state. ---*/
/*------------------------------------------------------------*/
#define OFFB_RAX offsetof(VexGuestAMD64State,guest_RAX)
#define OFFB_RBX offsetof(VexGuestAMD64State,guest_RBX)
#define OFFB_RCX offsetof(VexGuestAMD64State,guest_RCX)
#define OFFB_RDX offsetof(VexGuestAMD64State,guest_RDX)
#define OFFB_RSP offsetof(VexGuestAMD64State,guest_RSP)
#define OFFB_RBP offsetof(VexGuestAMD64State,guest_RBP)
#define OFFB_RSI offsetof(VexGuestAMD64State,guest_RSI)
#define OFFB_RDI offsetof(VexGuestAMD64State,guest_RDI)
#define OFFB_R8 offsetof(VexGuestAMD64State,guest_R8)
#define OFFB_R9 offsetof(VexGuestAMD64State,guest_R9)
#define OFFB_R10 offsetof(VexGuestAMD64State,guest_R10)
#define OFFB_R11 offsetof(VexGuestAMD64State,guest_R11)
#define OFFB_R12 offsetof(VexGuestAMD64State,guest_R12)
#define OFFB_R13 offsetof(VexGuestAMD64State,guest_R13)
#define OFFB_R14 offsetof(VexGuestAMD64State,guest_R14)
#define OFFB_R15 offsetof(VexGuestAMD64State,guest_R15)
#define OFFB_RIP offsetof(VexGuestAMD64State,guest_RIP)
#define OFFB_FS_ZERO offsetof(VexGuestAMD64State,guest_FS_ZERO)
#define OFFB_GS_0x60 offsetof(VexGuestAMD64State,guest_GS_0x60)
#define OFFB_CC_OP offsetof(VexGuestAMD64State,guest_CC_OP)
#define OFFB_CC_DEP1 offsetof(VexGuestAMD64State,guest_CC_DEP1)
#define OFFB_CC_DEP2 offsetof(VexGuestAMD64State,guest_CC_DEP2)
#define OFFB_CC_NDEP offsetof(VexGuestAMD64State,guest_CC_NDEP)
#define OFFB_FPREGS offsetof(VexGuestAMD64State,guest_FPREG[0])
#define OFFB_FPTAGS offsetof(VexGuestAMD64State,guest_FPTAG[0])
#define OFFB_DFLAG offsetof(VexGuestAMD64State,guest_DFLAG)
#define OFFB_ACFLAG offsetof(VexGuestAMD64State,guest_ACFLAG)
#define OFFB_IDFLAG offsetof(VexGuestAMD64State,guest_IDFLAG)
#define OFFB_FTOP offsetof(VexGuestAMD64State,guest_FTOP)
#define OFFB_FC3210 offsetof(VexGuestAMD64State,guest_FC3210)
#define OFFB_FPROUND offsetof(VexGuestAMD64State,guest_FPROUND)
#define OFFB_SSEROUND offsetof(VexGuestAMD64State,guest_SSEROUND)
#define OFFB_YMM0 offsetof(VexGuestAMD64State,guest_YMM0)
#define OFFB_YMM1 offsetof(VexGuestAMD64State,guest_YMM1)
#define OFFB_YMM2 offsetof(VexGuestAMD64State,guest_YMM2)
#define OFFB_YMM3 offsetof(VexGuestAMD64State,guest_YMM3)
#define OFFB_YMM4 offsetof(VexGuestAMD64State,guest_YMM4)
#define OFFB_YMM5 offsetof(VexGuestAMD64State,guest_YMM5)
#define OFFB_YMM6 offsetof(VexGuestAMD64State,guest_YMM6)
#define OFFB_YMM7 offsetof(VexGuestAMD64State,guest_YMM7)
#define OFFB_YMM8 offsetof(VexGuestAMD64State,guest_YMM8)
#define OFFB_YMM9 offsetof(VexGuestAMD64State,guest_YMM9)
#define OFFB_YMM10 offsetof(VexGuestAMD64State,guest_YMM10)
#define OFFB_YMM11 offsetof(VexGuestAMD64State,guest_YMM11)
#define OFFB_YMM12 offsetof(VexGuestAMD64State,guest_YMM12)
#define OFFB_YMM13 offsetof(VexGuestAMD64State,guest_YMM13)
#define OFFB_YMM14 offsetof(VexGuestAMD64State,guest_YMM14)
#define OFFB_YMM15 offsetof(VexGuestAMD64State,guest_YMM15)
#define OFFB_YMM16 offsetof(VexGuestAMD64State,guest_YMM16)
#define OFFB_EMNOTE offsetof(VexGuestAMD64State,guest_EMNOTE)
#define OFFB_CMSTART offsetof(VexGuestAMD64State,guest_CMSTART)
#define OFFB_CMLEN offsetof(VexGuestAMD64State,guest_CMLEN)
#define OFFB_NRADDR offsetof(VexGuestAMD64State,guest_NRADDR)
/*------------------------------------------------------------*/
/*--- Helper bits and pieces for deconstructing the ---*/
/*--- amd64 insn stream. ---*/
/*------------------------------------------------------------*/
/* This is the AMD64 register encoding -- integer regs. */
#define R_RAX 0
#define R_RCX 1
#define R_RDX 2
#define R_RBX 3
#define R_RSP 4
#define R_RBP 5
#define R_RSI 6
#define R_RDI 7
#define R_R8 8
#define R_R9 9
#define R_R10 10
#define R_R11 11
#define R_R12 12
#define R_R13 13
#define R_R14 14
#define R_R15 15
/* 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
/* Various simple conversions */
static ULong extend_s_8to64 ( UChar x )
{
return (ULong)((((Long)x) << 56) >> 56);
}
static ULong extend_s_16to64 ( UShort x )
{
return (ULong)((((Long)x) << 48) >> 48);
}
static ULong extend_s_32to64 ( UInt x )
{
return (ULong)((((Long)x) << 32) >> 32);
}
/* 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. */
inline
static Bool epartIsReg ( UChar mod_reg_rm )
{
return toBool(0xC0 == (mod_reg_rm & 0xC0));
}
/* Extract the 'g' field from a modRM byte. This only produces 3
bits, which is not a complete register number. You should avoid
this function if at all possible. */
inline
static Int gregLO3ofRM ( UChar mod_reg_rm )
{
return (Int)( (mod_reg_rm >> 3) & 7 );
}
/* Ditto the 'e' field of a modRM byte. */
inline
static Int eregLO3ofRM ( UChar mod_reg_rm )
{
return (Int)(mod_reg_rm & 0x7);
}
/* Get a 8/16/32-bit unsigned value out of the insn stream. */
static inline UChar getUChar ( Long delta )
{
UChar v = guest_code[delta+0];
return v;
}
static UInt getUDisp16 ( Long delta )
{
UInt v = guest_code[delta+1]; v <<= 8;
v |= guest_code[delta+0];
return v & 0xFFFF;
}
//.. static UInt getUDisp ( Int size, Long delta )
//.. {
//.. switch (size) {
//.. case 4: return getUDisp32(delta);
//.. case 2: return getUDisp16(delta);
//.. case 1: return getUChar(delta);
//.. default: vpanic("getUDisp(x86)");
//.. }
//.. return 0; /*notreached*/
//.. }
/* Get a byte value out of the insn stream and sign-extend to 64
bits. */
static Long getSDisp8 ( Long delta )
{
return extend_s_8to64( guest_code[delta] );
}
/* Get a 16-bit value out of the insn stream and sign-extend to 64
bits. */
static Long getSDisp16 ( Long delta )
{
UInt v = guest_code[delta+1]; v <<= 8;
v |= guest_code[delta+0];
return extend_s_16to64( (UShort)v );
}
/* Get a 32-bit value out of the insn stream and sign-extend to 64
bits. */
static Long getSDisp32 ( Long 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 extend_s_32to64( v );
}
/* Get a 64-bit value out of the insn stream. */
static Long getDisp64 ( Long delta )
{
ULong v = 0;
v |= guest_code[delta+7]; v <<= 8;
v |= guest_code[delta+6]; v <<= 8;
v |= guest_code[delta+5]; v <<= 8;
v |= guest_code[delta+4]; v <<= 8;
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;
}
/* Note: because AMD64 doesn't allow 64-bit literals, it is an error
if this is called with size==8. Should not happen. */
static Long getSDisp ( Int size, Long delta )
{
switch (size) {
case 4: return getSDisp32(delta);
case 2: return getSDisp16(delta);
case 1: return getSDisp8(delta);
default: vpanic("getSDisp(amd64)");
}
}
static ULong mkSizeMask ( Int sz )
{
switch (sz) {
case 1: return 0x00000000000000FFULL;
case 2: return 0x000000000000FFFFULL;
case 4: return 0x00000000FFFFFFFFULL;
case 8: return 0xFFFFFFFFFFFFFFFFULL;
default: vpanic("mkSzMask(amd64)");
}
}
static Int imin ( Int a, Int b )
{
return (a < b) ? a : b;
}
static IRType szToITy ( Int n )
{
switch (n) {
case 1: return Ity_I8;
case 2: return Ity_I16;
case 4: return Ity_I32;
case 8: return Ity_I64;
default: vex_printf("\nszToITy(%d)\n", n);
vpanic("szToITy(amd64)");
}
}
/*------------------------------------------------------------*/
/*--- For dealing with prefixes. ---*/
/*------------------------------------------------------------*/
/* The idea is to pass around an int holding a bitmask summarising
info from the prefixes seen on the current instruction, including
info from the REX byte. This info is used in various places, but
most especially when making sense of register fields in
instructions.
The top 8 bits of the prefix are 0x55, just as a hacky way to
ensure it really is a valid prefix.
Things you can safely assume about a well-formed prefix:
* at most one segment-override bit (CS,DS,ES,FS,GS,SS) is set.
* if REX is not present then REXW,REXR,REXX,REXB will read
as zero.
* F2 and F3 will not both be 1.
*/
typedef UInt Prefix;
#define PFX_ASO (1<<0) /* address-size override present (0x67) */
#define PFX_66 (1<<1) /* operand-size override-to-16 present (0x66) */
#define PFX_REX (1<<2) /* REX byte present (0x40 to 0x4F) */
#define PFX_REXW (1<<3) /* REX W bit, if REX present, else 0 */
#define PFX_REXR (1<<4) /* REX R bit, if REX present, else 0 */
#define PFX_REXX (1<<5) /* REX X bit, if REX present, else 0 */
#define PFX_REXB (1<<6) /* REX B bit, if REX present, else 0 */
#define PFX_LOCK (1<<7) /* bus LOCK prefix present (0xF0) */
#define PFX_F2 (1<<8) /* REP/REPE/REPZ prefix present (0xF2) */
#define PFX_F3 (1<<9) /* REPNE/REPNZ prefix present (0xF3) */
#define PFX_CS (1<<10) /* CS segment prefix present (0x2E) */
#define PFX_DS (1<<11) /* DS segment prefix present (0x3E) */
#define PFX_ES (1<<12) /* ES segment prefix present (0x26) */
#define PFX_FS (1<<13) /* FS segment prefix present (0x64) */
#define PFX_GS (1<<14) /* GS segment prefix present (0x65) */
#define PFX_SS (1<<15) /* SS segment prefix present (0x36) */
#define PFX_VEX (1<<16) /* VEX prefix present (0xC4 or 0xC5) */
#define PFX_VEXL (1<<17) /* VEX L bit, if VEX present, else 0 */
/* The extra register field VEX.vvvv is encoded (after not-ing it) as
PFX_VEXnV3 .. PFX_VEXnV0, so these must occupy adjacent bit
positions. */
#define PFX_VEXnV0 (1<<18) /* ~VEX vvvv[0], if VEX present, else 0 */
#define PFX_VEXnV1 (1<<19) /* ~VEX vvvv[1], if VEX present, else 0 */
#define PFX_VEXnV2 (1<<20) /* ~VEX vvvv[2], if VEX present, else 0 */
#define PFX_VEXnV3 (1<<21) /* ~VEX vvvv[3], if VEX present, else 0 */
#define PFX_EMPTY 0x55000000
static Bool IS_VALID_PFX ( Prefix pfx ) {
return toBool((pfx & 0xFF000000) == PFX_EMPTY);
}
static Bool haveREX ( Prefix pfx ) {
return toBool(pfx & PFX_REX);
}
static Int getRexW ( Prefix pfx ) {
return (pfx & PFX_REXW) ? 1 : 0;
}
static Int getRexR ( Prefix pfx ) {
return (pfx & PFX_REXR) ? 1 : 0;
}
static Int getRexX ( Prefix pfx ) {
return (pfx & PFX_REXX) ? 1 : 0;
}
static Int getRexB ( Prefix pfx ) {
return (pfx & PFX_REXB) ? 1 : 0;
}
/* Check a prefix doesn't have F2 or F3 set in it, since usually that
completely changes what instruction it really is. */
static Bool haveF2orF3 ( Prefix pfx ) {
return toBool((pfx & (PFX_F2|PFX_F3)) > 0);
}
static Bool haveF2andF3 ( Prefix pfx ) {
return toBool((pfx & (PFX_F2|PFX_F3)) == (PFX_F2|PFX_F3));
}
static Bool haveF2 ( Prefix pfx ) {
return toBool((pfx & PFX_F2) > 0);
}
static Bool haveF3 ( Prefix pfx ) {
return toBool((pfx & PFX_F3) > 0);
}
static Bool have66 ( Prefix pfx ) {
return toBool((pfx & PFX_66) > 0);
}
static Bool haveASO ( Prefix pfx ) {
return toBool((pfx & PFX_ASO) > 0);
}
static Bool haveLOCK ( Prefix pfx ) {
return toBool((pfx & PFX_LOCK) > 0);
}
/* Return True iff pfx has 66 set and F2 and F3 clear */
static Bool have66noF2noF3 ( Prefix pfx )
{
return
toBool((pfx & (PFX_66|PFX_F2|PFX_F3)) == PFX_66);
}
/* Return True iff pfx has F2 set and 66 and F3 clear */
static Bool haveF2no66noF3 ( Prefix pfx )
{
return
toBool((pfx & (PFX_66|PFX_F2|PFX_F3)) == PFX_F2);
}
/* Return True iff pfx has F3 set and 66 and F2 clear */
static Bool haveF3no66noF2 ( Prefix pfx )
{
return
toBool((pfx & (PFX_66|PFX_F2|PFX_F3)) == PFX_F3);
}
/* Return True iff pfx has F3 set and F2 clear */
static Bool haveF3noF2 ( Prefix pfx )
{
return
toBool((pfx & (PFX_F2|PFX_F3)) == PFX_F3);
}
/* Return True iff pfx has F2 set and F3 clear */
static Bool haveF2noF3 ( Prefix pfx )
{
return
toBool((pfx & (PFX_F2|PFX_F3)) == PFX_F2);
}
/* Return True iff pfx has 66, F2 and F3 clear */
static Bool haveNo66noF2noF3 ( Prefix pfx )
{
return
toBool((pfx & (PFX_66|PFX_F2|PFX_F3)) == 0);
}
/* Return True iff pfx has any of 66, F2 and F3 set */
static Bool have66orF2orF3 ( Prefix pfx )
{
return toBool( ! haveNo66noF2noF3(pfx) );
}
/* Return True iff pfx has 66 or F3 set */
static Bool have66orF3 ( Prefix pfx )
{
return toBool((pfx & (PFX_66|PFX_F3)) > 0);
}
/* Clear all the segment-override bits in a prefix. */
static Prefix clearSegBits ( Prefix p )
{
return
p & ~(PFX_CS | PFX_DS | PFX_ES | PFX_FS | PFX_GS | PFX_SS);
}
/* Get the (inverted, hence back to "normal") VEX.vvvv field. */
static UInt getVexNvvvv ( Prefix pfx ) {
UInt r = (UInt)pfx;
r /= (UInt)PFX_VEXnV0; /* pray this turns into a shift */
return r & 0xF;
}
static Bool haveVEX ( Prefix pfx ) {
return toBool(pfx & PFX_VEX);
}
static Int getVexL ( Prefix pfx ) {
return (pfx & PFX_VEXL) ? 1 : 0;
}
/*------------------------------------------------------------*/
/*--- For dealing with escapes ---*/
/*------------------------------------------------------------*/
/* Escapes come after the prefixes, but before the primary opcode
byte. They escape the primary opcode byte into a bigger space.
The 0xF0000000 isn't significant, except so as to make it not
overlap valid Prefix values, for sanity checking.
*/
typedef
enum {
ESC_NONE=0xF0000000, // none
ESC_0F, // 0F
ESC_0F38, // 0F 38
ESC_0F3A // 0F 3A
}
Escape;
/*------------------------------------------------------------*/
/*--- For dealing with integer registers ---*/
/*------------------------------------------------------------*/
/* This is somewhat complex. The rules are:
For 64, 32 and 16 bit register references, the e or g fields in the
modrm bytes supply the low 3 bits of the register number. The
fourth (most-significant) bit of the register number is supplied by
the REX byte, if it is present; else that bit is taken to be zero.
The REX.R bit supplies the high bit corresponding to the g register
field, and the REX.B bit supplies the high bit corresponding to the
e register field (when the mod part of modrm indicates that modrm's
e component refers to a register and not to memory).
The REX.X bit supplies a high register bit for certain registers
in SIB address modes, and is generally rarely used.
For 8 bit register references, the presence of the REX byte itself
has significance. If there is no REX present, then the 3-bit
number extracted from the modrm e or g field is treated as an index
into the sequence %al %cl %dl %bl %ah %ch %dh %bh -- that is, the
old x86 encoding scheme.
But if there is a REX present, the register reference is
interpreted in the same way as for 64/32/16-bit references: a high
bit is extracted from REX, giving a 4-bit number, and the denoted
register is the lowest 8 bits of the 16 integer registers denoted
by the number. In particular, values 3 through 7 of this sequence
do not refer to %ah %ch %dh %bh but instead to the lowest 8 bits of
%rsp %rbp %rsi %rdi.
The REX.W bit has no bearing at all on register numbers. Instead
its presence indicates that the operand size is to be overridden
from its default value (32 bits) to 64 bits instead. This is in
the same fashion that an 0x66 prefix indicates the operand size is
to be overridden from 32 bits down to 16 bits. When both REX.W and
0x66 are present there is a conflict, and REX.W takes precedence.
Rather than try to handle this complexity using a single huge
function, several smaller ones are provided. The aim is to make it
as difficult as possible to screw up register decoding in a subtle
and hard-to-track-down way.
Because these routines fish around in the host's memory (that is,
in the guest state area) for sub-parts of guest registers, their
correctness depends on the host's endianness. So far these
routines only work for little-endian hosts. Those for which
endianness is important have assertions to ensure sanity.
*/
/* About the simplest question you can ask: where do the 64-bit
integer registers live (in the guest state) ? */
static Int integerGuestReg64Offset ( UInt reg )
{
switch (reg) {
case R_RAX: return OFFB_RAX;
case R_RCX: return OFFB_RCX;
case R_RDX: return OFFB_RDX;
case R_RBX: return OFFB_RBX;
case R_RSP: return OFFB_RSP;
case R_RBP: return OFFB_RBP;
case R_RSI: return OFFB_RSI;
case R_RDI: return OFFB_RDI;
case R_R8: return OFFB_R8;
case R_R9: return OFFB_R9;
case R_R10: return OFFB_R10;
case R_R11: return OFFB_R11;
case R_R12: return OFFB_R12;
case R_R13: return OFFB_R13;
case R_R14: return OFFB_R14;
case R_R15: return OFFB_R15;
default: vpanic("integerGuestReg64Offset(amd64)");
}
}
/* Produce the name of an integer register, for printing purposes.
reg is a number in the range 0 .. 15 that has been generated from a
3-bit reg-field number and a REX extension bit. irregular denotes
the case where sz==1 and no REX byte is present. */
static
const HChar* nameIReg ( Int sz, UInt reg, Bool irregular )
{
static const HChar* ireg64_names[16]
= { "%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi",
"%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15" };
static const HChar* ireg32_names[16]
= { "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi",
"%r8d", "%r9d", "%r10d","%r11d","%r12d","%r13d","%r14d","%r15d" };
static const HChar* ireg16_names[16]
= { "%ax", "%cx", "%dx", "%bx", "%sp", "%bp", "%si", "%di",
"%r8w", "%r9w", "%r10w","%r11w","%r12w","%r13w","%r14w","%r15w" };
static const HChar* ireg8_names[16]
= { "%al", "%cl", "%dl", "%bl", "%spl", "%bpl", "%sil", "%dil",
"%r8b", "%r9b", "%r10b","%r11b","%r12b","%r13b","%r14b","%r15b" };
static const HChar* ireg8_irregular[8]
= { "%al", "%cl", "%dl", "%bl", "%ah", "%ch", "%dh", "%bh" };
vassert(reg < 16);
if (sz == 1) {
if (irregular)
vassert(reg < 8);
} else {
vassert(irregular == False);
}
switch (sz) {
case 8: return ireg64_names[reg];
case 4: return ireg32_names[reg];
case 2: return ireg16_names[reg];
case 1: if (irregular) {
return ireg8_irregular[reg];
} else {
return ireg8_names[reg];
}
default: vpanic("nameIReg(amd64)");
}
}
/* Using the same argument conventions as nameIReg, produce the
guest state offset of an integer register. */
static
Int offsetIReg ( Int sz, UInt reg, Bool irregular )
{
vassert(reg < 16);
if (sz == 1) {
if (irregular)
vassert(reg < 8);
} else {
vassert(irregular == False);
}
/* Deal with irregular case -- sz==1 and no REX present */
if (sz == 1 && irregular) {
switch (reg) {
case R_RSP: return 1+ OFFB_RAX;
case R_RBP: return 1+ OFFB_RCX;
case R_RSI: return 1+ OFFB_RDX;
case R_RDI: return 1+ OFFB_RBX;
default: break; /* use the normal case */
}
}
/* Normal case */
return integerGuestReg64Offset(reg);
}
/* Read the %CL register :: Ity_I8, for shift/rotate operations. */
static IRExpr* getIRegCL ( void )
{
vassert(host_endness == VexEndnessLE);
return IRExpr_Get( OFFB_RCX, Ity_I8 );
}
/* Write to the %AH register. */
static void putIRegAH ( IRExpr* e )
{
vassert(host_endness == VexEndnessLE);
vassert(typeOfIRExpr(irsb->tyenv, e) == Ity_I8);
stmt( IRStmt_Put( OFFB_RAX+1, e ) );
}
/* Read/write various widths of %RAX, as it has various
special-purpose uses. */
static const HChar* nameIRegRAX ( Int sz )
{
switch (sz) {
case 1: return "%al";
case 2: return "%ax";
case 4: return "%eax";
case 8: return "%rax";
default: vpanic("nameIRegRAX(amd64)");
}
}
static IRExpr* getIRegRAX ( Int sz )
{
vassert(host_endness == VexEndnessLE);
switch (sz) {
case 1: return IRExpr_Get( OFFB_RAX, Ity_I8 );
case 2: return IRExpr_Get( OFFB_RAX, Ity_I16 );
case 4: return unop(Iop_64to32, IRExpr_Get( OFFB_RAX, Ity_I64 ));
case 8: return IRExpr_Get( OFFB_RAX, Ity_I64 );
default: vpanic("getIRegRAX(amd64)");
}
}
static void putIRegRAX ( Int sz, IRExpr* e )
{
IRType ty = typeOfIRExpr(irsb->tyenv, e);
vassert(host_endness == VexEndnessLE);
switch (sz) {
case 8: vassert(ty == Ity_I64);
stmt( IRStmt_Put( OFFB_RAX, e ));
break;
case 4: vassert(ty == Ity_I32);
stmt( IRStmt_Put( OFFB_RAX, unop(Iop_32Uto64,e) ));
break;
case 2: vassert(ty == Ity_I16);
stmt( IRStmt_Put( OFFB_RAX, e ));
break;
case 1: vassert(ty == Ity_I8);
stmt( IRStmt_Put( OFFB_RAX, e ));
break;
default: vpanic("putIRegRAX(amd64)");
}
}
/* Read/write various widths of %RDX, as it has various
special-purpose uses. */
static const HChar* nameIRegRDX ( Int sz )
{
switch (sz) {
case 1: return "%dl";
case 2: return "%dx";
case 4: return "%edx";
case 8: return "%rdx";
default: vpanic("nameIRegRDX(amd64)");
}
}
static IRExpr* getIRegRDX ( Int sz )
{
vassert(host_endness == VexEndnessLE);
switch (sz) {
case 1: return IRExpr_Get( OFFB_RDX, Ity_I8 );
case 2: return IRExpr_Get( OFFB_RDX, Ity_I16 );
case 4: return unop(Iop_64to32, IRExpr_Get( OFFB_RDX, Ity_I64 ));
case 8: return IRExpr_Get( OFFB_RDX, Ity_I64 );
default: vpanic("getIRegRDX(amd64)");
}
}
static void putIRegRDX ( Int sz, IRExpr* e )
{
vassert(host_endness == VexEndnessLE);
vassert(typeOfIRExpr(irsb->tyenv, e) == szToITy(sz));
switch (sz) {
case 8: stmt( IRStmt_Put( OFFB_RDX, e ));
break;
case 4: stmt( IRStmt_Put( OFFB_RDX, unop(Iop_32Uto64,e) ));
break;
case 2: stmt( IRStmt_Put( OFFB_RDX, e ));
break;
case 1: stmt( IRStmt_Put( OFFB_RDX, e ));
break;
default: vpanic("putIRegRDX(amd64)");
}
}
/* Simplistic functions to deal with the integer registers as a
straightforward bank of 16 64-bit regs. */
static IRExpr* getIReg64 ( UInt regno )
{
return IRExpr_Get( integerGuestReg64Offset(regno),
Ity_I64 );
}
static void putIReg64 ( UInt regno, IRExpr* e )
{
vassert(typeOfIRExpr(irsb->tyenv,e) == Ity_I64);
stmt( IRStmt_Put( integerGuestReg64Offset(regno), e ) );
}
static const HChar* nameIReg64 ( UInt regno )
{
return nameIReg( 8, regno, False );
}
/* Simplistic functions to deal with the lower halves of integer
registers as a straightforward bank of 16 32-bit regs. */
static IRExpr* getIReg32 ( UInt regno )
{
vassert(host_endness == VexEndnessLE);
return unop(Iop_64to32,
IRExpr_Get( integerGuestReg64Offset(regno),
Ity_I64 ));
}
static void putIReg32 ( UInt regno, IRExpr* e )
{
vassert(typeOfIRExpr(irsb->tyenv,e) == Ity_I32);
stmt( IRStmt_Put( integerGuestReg64Offset(regno),
unop(Iop_32Uto64,e) ) );
}
static const HChar* nameIReg32 ( UInt regno )
{
return nameIReg( 4, regno, False );
}
/* Simplistic functions to deal with the lower quarters of integer
registers as a straightforward bank of 16 16-bit regs. */
static IRExpr* getIReg16 ( UInt regno )
{
vassert(host_endness == VexEndnessLE);
return IRExpr_Get( integerGuestReg64Offset(regno),
Ity_I16 );
}
static void putIReg16 ( UInt regno, IRExpr* e )
{
vassert(typeOfIRExpr(irsb->tyenv,e) == Ity_I16);
stmt( IRStmt_Put( integerGuestReg64Offset(regno),
unop(Iop_16Uto64,e) ) );
}
static const HChar* nameIReg16 ( UInt regno )
{
return nameIReg( 2, regno, False );
}
/* Sometimes what we know is a 3-bit register number, a REX byte, and
which field of the REX byte is to be used to extend to a 4-bit
number. These functions cater for that situation.
*/
static IRExpr* getIReg64rexX ( Prefix pfx, UInt lo3bits )
{
vassert(lo3bits < 8);
vassert(IS_VALID_PFX(pfx));
return getIReg64( lo3bits | (getRexX(pfx) << 3) );
}
static const HChar* nameIReg64rexX ( Prefix pfx, UInt lo3bits )
{
vassert(lo3bits < 8);
vassert(IS_VALID_PFX(pfx));
return nameIReg( 8, lo3bits | (getRexX(pfx) << 3), False );
}
static const HChar* nameIRegRexB ( Int sz, Prefix pfx, UInt lo3bits )
{
vassert(lo3bits < 8);
vassert(IS_VALID_PFX(pfx));
vassert(sz == 8 || sz == 4 || sz == 2 || sz == 1);
return nameIReg( sz, lo3bits | (getRexB(pfx) << 3),
toBool(sz==1 && !haveREX(pfx)) );
}
static IRExpr* getIRegRexB ( Int sz, Prefix pfx, UInt lo3bits )
{
vassert(lo3bits < 8);
vassert(IS_VALID_PFX(pfx));
vassert(sz == 8 || sz == 4 || sz == 2 || sz == 1);
if (sz == 4) {
sz = 8;
return unop(Iop_64to32,
IRExpr_Get(
offsetIReg( sz, lo3bits | (getRexB(pfx) << 3),
False/*!irregular*/ ),
szToITy(sz)
)
);
} else {
return IRExpr_Get(
offsetIReg( sz, lo3bits | (getRexB(pfx) << 3),
toBool(sz==1 && !haveREX(pfx)) ),
szToITy(sz)
);
}
}
static void putIRegRexB ( Int sz, Prefix pfx, UInt lo3bits, IRExpr* e )
{
vassert(lo3bits < 8);
vassert(IS_VALID_PFX(pfx));
vassert(sz == 8 || sz == 4 || sz == 2 || sz == 1);
vassert(typeOfIRExpr(irsb->tyenv, e) == szToITy(sz));
stmt( IRStmt_Put(
offsetIReg( sz, lo3bits | (getRexB(pfx) << 3),
toBool(sz==1 && !haveREX(pfx)) ),
sz==4 ? unop(Iop_32Uto64,e) : e
));
}
/* Functions for getting register numbers from modrm bytes and REX
when we don't have to consider the complexities of integer subreg
accesses.
*/
/* Extract the g reg field from a modRM byte, and augment it using the
REX.R bit from the supplied REX byte. The R bit usually is
associated with the g register field.
*/
static UInt gregOfRexRM ( Prefix pfx, UChar mod_reg_rm )
{
Int reg = (Int)( (mod_reg_rm >> 3) & 7 );
reg += (pfx & PFX_REXR) ? 8 : 0;
return reg;
}
/* Extract the e reg field from a modRM byte, and augment it using the
REX.B bit from the supplied REX byte. The B bit usually is
associated with the e register field (when modrm indicates e is a
register, that is).
*/
static UInt eregOfRexRM ( Prefix pfx, UChar mod_reg_rm )
{
Int rm;
vassert(epartIsReg(mod_reg_rm));
rm = (Int)(mod_reg_rm & 0x7);
rm += (pfx & PFX_REXB) ? 8 : 0;
return rm;
}
/* General functions for dealing with integer register access. */
/* Produce the guest state offset for a reference to the 'g' register
field in a modrm byte, taking into account REX (or its absence),
and the size of the access.
*/
static UInt offsetIRegG ( Int sz, Prefix pfx, UChar mod_reg_rm )
{
UInt reg;
vassert(host_endness == VexEndnessLE);
vassert(IS_VALID_PFX(pfx));
vassert(sz == 8 || sz == 4 || sz == 2 || sz == 1);
reg = gregOfRexRM( pfx, mod_reg_rm );
return offsetIReg( sz, reg, toBool(sz == 1 && !haveREX(pfx)) );
}
static
IRExpr* getIRegG ( Int sz, Prefix pfx, UChar mod_reg_rm )
{
if (sz == 4) {
sz = 8;
return unop(Iop_64to32,
IRExpr_Get( offsetIRegG( sz, pfx, mod_reg_rm ),
szToITy(sz) ));
} else {
return IRExpr_Get( offsetIRegG( sz, pfx, mod_reg_rm ),
szToITy(sz) );
}
}
static
void putIRegG ( Int sz, Prefix pfx, UChar mod_reg_rm, IRExpr* e )
{
vassert(typeOfIRExpr(irsb->tyenv,e) == szToITy(sz));
if (sz == 4) {
e = unop(Iop_32Uto64,e);
}
stmt( IRStmt_Put( offsetIRegG( sz, pfx, mod_reg_rm ), e ) );
}
static
const HChar* nameIRegG ( Int sz, Prefix pfx, UChar mod_reg_rm )
{
return nameIReg( sz, gregOfRexRM(pfx,mod_reg_rm),
toBool(sz==1 && !haveREX(pfx)) );
}
static
IRExpr* getIRegV ( Int sz, Prefix pfx )
{
if (sz == 4) {
sz = 8;
return unop(Iop_64to32,
IRExpr_Get( offsetIReg( sz, getVexNvvvv(pfx), False ),
szToITy(sz) ));
} else {
return IRExpr_Get( offsetIReg( sz, getVexNvvvv(pfx), False ),
szToITy(sz) );
}
}
static
void putIRegV ( Int sz, Prefix pfx, IRExpr* e )
{
vassert(typeOfIRExpr(irsb->tyenv,e) == szToITy(sz));
if (sz == 4) {
e = unop(Iop_32Uto64,e);
}
stmt( IRStmt_Put( offsetIReg( sz, getVexNvvvv(pfx), False ), e ) );
}
static
const HChar* nameIRegV ( Int sz, Prefix pfx )
{
return nameIReg( sz, getVexNvvvv(pfx), False );
}
/* Produce the guest state offset for a reference to the 'e' register
field in a modrm byte, taking into account REX (or its absence),
and the size of the access. eregOfRexRM will assert if mod_reg_rm
denotes a memory access rather than a register access.
*/
static UInt offsetIRegE ( Int sz, Prefix pfx, UChar mod_reg_rm )
{
UInt reg;
vassert(host_endness == VexEndnessLE);
vassert(IS_VALID_PFX(pfx));
vassert(sz == 8 || sz == 4 || sz == 2 || sz == 1);
reg = eregOfRexRM( pfx, mod_reg_rm );
return offsetIReg( sz, reg, toBool(sz == 1 && !haveREX(pfx)) );
}
static
IRExpr* getIRegE ( Int sz, Prefix pfx, UChar mod_reg_rm )
{
if (sz == 4) {
sz = 8;
return unop(Iop_64to32,
IRExpr_Get( offsetIRegE( sz, pfx, mod_reg_rm ),
szToITy(sz) ));
} else {
return IRExpr_Get( offsetIRegE( sz, pfx, mod_reg_rm ),
szToITy(sz) );
}
}
static
void putIRegE ( Int sz, Prefix pfx, UChar mod_reg_rm, IRExpr* e )
{
vassert(typeOfIRExpr(irsb->tyenv,e) == szToITy(sz));
if (sz == 4) {
e = unop(Iop_32Uto64,e);
}
stmt( IRStmt_Put( offsetIRegE( sz, pfx, mod_reg_rm ), e ) );
}
static
const HChar* nameIRegE ( Int sz, Prefix pfx, UChar mod_reg_rm )
{
return nameIReg( sz, eregOfRexRM(pfx,mod_reg_rm),
toBool(sz==1 && !haveREX(pfx)) );
}
/*------------------------------------------------------------*/
/*--- For dealing with XMM registers ---*/
/*------------------------------------------------------------*/
static Int ymmGuestRegOffset ( UInt ymmreg )
{
switch (ymmreg) {
case 0: return OFFB_YMM0;
case 1: return OFFB_YMM1;
case 2: return OFFB_YMM2;
case 3: return OFFB_YMM3;
case 4: return OFFB_YMM4;
case 5: return OFFB_YMM5;
case 6: return OFFB_YMM6;
case 7: return OFFB_YMM7;
case 8: return OFFB_YMM8;
case 9: return OFFB_YMM9;
case 10: return OFFB_YMM10;
case 11: return OFFB_YMM11;
case 12: return OFFB_YMM12;
case 13: return OFFB_YMM13;
case 14: return OFFB_YMM14;
case 15: return OFFB_YMM15;
default: vpanic("ymmGuestRegOffset(amd64)");
}
}
static Int xmmGuestRegOffset ( UInt xmmreg )
{
/* Correct for little-endian host only. */
vassert(host_endness == VexEndnessLE);
return ymmGuestRegOffset( xmmreg );
}
/* 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 Int ymmGuestRegLane128offset ( UInt ymmreg, Int laneno )
{
/* Correct for little-endian host only. */
vassert(host_endness == VexEndnessLE);
vassert(laneno >= 0 && laneno < 2);
return ymmGuestRegOffset( ymmreg ) + 16 * laneno;
}
static Int ymmGuestRegLane64offset ( UInt ymmreg, Int laneno )
{
/* Correct for little-endian host only. */
vassert(host_endness == VexEndnessLE);
vassert(laneno >= 0 && laneno < 4);
return ymmGuestRegOffset( ymmreg ) + 8 * laneno;
}
static Int ymmGuestRegLane32offset ( UInt ymmreg, Int laneno )
{
/* Correct for little-endian host only. */
vassert(host_endness == VexEndnessLE);
vassert(laneno >= 0 && laneno < 8);
return ymmGuestRegOffset( ymmreg ) + 4 * laneno;
}
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 IRExpr* getXMMRegLane16 ( UInt xmmreg, Int laneno )
{
return IRExpr_Get( xmmGuestRegLane16offset(xmmreg,laneno), Ity_I16 );
}
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 IRExpr* getYMMReg ( UInt xmmreg )
{
return IRExpr_Get( ymmGuestRegOffset(xmmreg), Ity_V256 );
}
static IRExpr* getYMMRegLane128 ( UInt ymmreg, Int laneno )
{
return IRExpr_Get( ymmGuestRegLane128offset(ymmreg,laneno), Ity_V128 );
}
static IRExpr* getYMMRegLane64 ( UInt ymmreg, Int laneno )
{
return IRExpr_Get( ymmGuestRegLane64offset(ymmreg,laneno), Ity_I64 );
}
static IRExpr* getYMMRegLane32 ( UInt ymmreg, Int laneno )
{
return IRExpr_Get( ymmGuestRegLane32offset(ymmreg,laneno), Ity_I32 );
}
static void putYMMReg ( UInt ymmreg, IRExpr* e )
{
vassert(typeOfIRExpr(irsb->tyenv,e) == Ity_V256);
stmt( IRStmt_Put( ymmGuestRegOffset(ymmreg), e ) );
}
static void putYMMRegLane128 ( UInt ymmreg, Int laneno, IRExpr* e )
{
vassert(typeOfIRExpr(irsb->tyenv,e) == Ity_V128);
stmt( IRStmt_Put( ymmGuestRegLane128offset(ymmreg,laneno), e ) );
}
static void putYMMRegLane64F ( UInt ymmreg, Int laneno, IRExpr* e )
{
vassert(typeOfIRExpr(irsb->tyenv,e) == Ity_F64);
stmt( IRStmt_Put( ymmGuestRegLane64offset(ymmreg,laneno), e ) );
}
static void putYMMRegLane64 ( UInt ymmreg, Int laneno, IRExpr* e )
{
vassert(typeOfIRExpr(irsb->tyenv,e) == Ity_I64);
stmt( IRStmt_Put( ymmGuestRegLane64offset(ymmreg,laneno), e ) );
}
static void putYMMRegLane32F ( UInt ymmreg, Int laneno, IRExpr* e )
{
vassert(typeOfIRExpr(irsb->tyenv,e) == Ity_F32);
stmt( IRStmt_Put( ymmGuestRegLane32offset(ymmreg,laneno), e ) );
}
static void putYMMRegLane32 ( UInt ymmreg, Int laneno, IRExpr* e )
{
vassert(typeOfIRExpr(irsb->tyenv,e) == Ity_I32);
stmt( IRStmt_Put( ymmGuestRegLane32offset(ymmreg,laneno), e ) );
}
static IRExpr* mkV128 ( UShort mask )
{
return IRExpr_Const(IRConst_V128(mask));
}
/* Write the low half of a YMM reg and zero out the upper half. */
static void putYMMRegLoAndZU ( UInt ymmreg, IRExpr* e )
{
putYMMRegLane128( ymmreg, 0, e );
putYMMRegLane128( ymmreg, 1, mkV128(0) );
}
static IRExpr* mkAnd1 ( IRExpr* x, IRExpr* y )
{
vassert(typeOfIRExpr(irsb->tyenv,x) == Ity_I1);
vassert(typeOfIRExpr(irsb->tyenv,y) == Ity_I1);
return unop(Iop_64to1,
binop(Iop_And64,
unop(Iop_1Uto64,x),
unop(Iop_1Uto64,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,
Addr64 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_I64 || 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_U64( restart_point ),
OFFB_RIP
));
}
/*------------------------------------------------------------*/
/*--- Helpers for %rflags. ---*/
/*------------------------------------------------------------*/
/* -------------- 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_I64. */
static IRExpr* mk_amd64g_calculate_rflags_all ( void )
{
IRExpr** args
= mkIRExprVec_4( IRExpr_Get(OFFB_CC_OP, Ity_I64),
IRExpr_Get(OFFB_CC_DEP1, Ity_I64),
IRExpr_Get(OFFB_CC_DEP2, Ity_I64),
IRExpr_Get(OFFB_CC_NDEP, Ity_I64) );
IRExpr* call
= mkIRExprCCall(
Ity_I64,
0/*regparm*/,
"amd64g_calculate_rflags_all", &amd64g_calculate_rflags_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_amd64g_calculate_condition ( AMD64Condcode cond )
{
IRExpr** args
= mkIRExprVec_5( mkU64(cond),
IRExpr_Get(OFFB_CC_OP, Ity_I64),
IRExpr_Get(OFFB_CC_DEP1, Ity_I64),
IRExpr_Get(OFFB_CC_DEP2, Ity_I64),
IRExpr_Get(OFFB_CC_NDEP, Ity_I64) );
IRExpr* call
= mkIRExprCCall(
Ity_I64,
0/*regparm*/,
"amd64g_calculate_condition", &amd64g_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_64to1, call);
}
/* Build IR to calculate just the carry flag from stored
CC_OP/CC_DEP1/CC_DEP2/CC_NDEP. Returns an expression :: Ity_I64. */
static IRExpr* mk_amd64g_calculate_rflags_c ( void )
{
IRExpr** args
= mkIRExprVec_4( IRExpr_Get(OFFB_CC_OP, Ity_I64),
IRExpr_Get(OFFB_CC_DEP1, Ity_I64),
IRExpr_Get(OFFB_CC_DEP2, Ity_I64),
IRExpr_Get(OFFB_CC_NDEP, Ity_I64) );
IRExpr* call
= mkIRExprCCall(
Ity_I64,
0/*regparm*/,
"amd64g_calculate_rflags_c", &amd64g_calculate_rflags_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 1/8/16/32/64 bit int expr to 64. */
static IRExpr* widenUto64 ( IRExpr* e )
{
switch (typeOfIRExpr(irsb->tyenv,e)) {
case Ity_I64: return e;
case Ity_I32: return unop(Iop_32Uto64, e);
case Ity_I16: return unop(Iop_16Uto64, e);
case Ity_I8: return unop(Iop_8Uto64, e);
case Ity_I1: return unop(Iop_1Uto64, e);
default: vpanic("widenUto64");
}
}
/* S-widen 8/16/32/64 bit int expr to 32. */
static IRExpr* widenSto64 ( IRExpr* e )
{
switch (typeOfIRExpr(irsb->tyenv,e)) {
case Ity_I64: return e;
case Ity_I32: return unop(Iop_32Sto64, e);
case Ity_I16: return unop(Iop_16Sto64, e);
case Ity_I8: return unop(Iop_8Sto64, e);
default: vpanic("widenSto64");
}
}
/* Narrow 8/16/32/64 bit int expr to 8/16/32/64. 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);
if (src_ty == Ity_I64 && dst_ty == Ity_I32)
return unop(Iop_64to32, e);
if (src_ty == Ity_I64 && dst_ty == Ity_I16)
return unop(Iop_64to16, e);
if (src_ty == Ity_I64 && dst_ty == Ity_I8)
return unop(Iop_64to8, e);
vex_printf("\nsrc, dst tys are: ");
ppIRType(src_ty);
vex_printf(", ");
ppIRType(dst_ty);
vex_printf("\n");
vpanic("narrowTo(amd64)");
}
/* 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 = 0;
switch (ty) {
case Ity_I8: ccOp = 0; break;
case Ity_I16: ccOp = 1; break;
case Ity_I32: ccOp = 2; break;
case Ity_I64: ccOp = 3; break;
default: vassert(0);
}
switch (op8) {
case Iop_Add8: ccOp += AMD64G_CC_OP_ADDB; break;
case Iop_Sub8: ccOp += AMD64G_CC_OP_SUBB; break;
default: ppIROp(op8);
vpanic("setFlags_DEP1_DEP2(amd64)");
}
stmt( IRStmt_Put( OFFB_CC_OP, mkU64(ccOp)) );
stmt( IRStmt_Put( OFFB_CC_DEP1, widenUto64(mkexpr(dep1))) );
stmt( IRStmt_Put( OFFB_CC_DEP2, widenUto64(mkexpr(dep2))) );
}
/* Set the OP and DEP1 fields only, and write zero to DEP2. */
static
void setFlags_DEP1 ( IROp op8, IRTemp dep1, IRType ty )
{
Int ccOp = 0;
switch (ty) {
case Ity_I8: ccOp = 0; break;
case Ity_I16: ccOp = 1; break;
case Ity_I32: ccOp = 2; break;
case Ity_I64: ccOp = 3; break;
default: vassert(0);
}
switch (op8) {
case Iop_Or8:
case Iop_And8:
case Iop_Xor8: ccOp += AMD64G_CC_OP_LOGICB; break;
default: ppIROp(op8);
vpanic("setFlags_DEP1(amd64)");
}
stmt( IRStmt_Put( OFFB_CC_OP, mkU64(ccOp)) );
stmt( IRStmt_Put( OFFB_CC_DEP1, widenUto64(mkexpr(dep1))) );
stmt( IRStmt_Put( OFFB_CC_DEP2, mkU64(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 op64,
IRTemp res,
IRTemp resUS,
IRType ty,
IRTemp guard )
{
Int ccOp = 0;
switch (ty) {
case Ity_I8: ccOp = 0; break;
case Ity_I16: ccOp = 1; break;
case Ity_I32: ccOp = 2; break;
case Ity_I64: ccOp = 3; break;
default: vassert(0);
}
vassert(guard);
/* Both kinds of right shifts are handled by the same thunk
operation. */
switch (op64) {
case Iop_Shr64:
case Iop_Sar64: ccOp += AMD64G_CC_OP_SHRB; break;
case Iop_Shl64: ccOp += AMD64G_CC_OP_SHLB; break;
default: ppIROp(op64);
vpanic("setFlags_DEP1_DEP2_shift(amd64)");
}
/* 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),
mkU64(ccOp),
IRExpr_Get(OFFB_CC_OP,Ity_I64) ) ));
stmt( IRStmt_Put( OFFB_CC_DEP1,
IRExpr_ITE( mkexpr(guardB),
widenUto64(mkexpr(res)),
IRExpr_Get(OFFB_CC_DEP1,Ity_I64) ) ));
stmt( IRStmt_Put( OFFB_CC_DEP2,
IRExpr_ITE( mkexpr(guardB),
widenUto64(mkexpr(resUS)),
IRExpr_Get(OFFB_CC_DEP2,Ity_I64) ) ));
}
/* 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 ? AMD64G_CC_OP_INCB : AMD64G_CC_OP_DECB;
switch (ty) {
case Ity_I8: ccOp += 0; break;
case Ity_I16: ccOp += 1; break;
case Ity_I32: ccOp += 2; break;
case Ity_I64: ccOp += 3; break;
default: vassert(0);
}
/* This has to come first, because calculating the C flag
may require reading all four thunk fields. */
stmt( IRStmt_Put( OFFB_CC_NDEP, mk_amd64g_calculate_rflags_c()) );
stmt( IRStmt_Put( OFFB_CC_OP, mkU64(ccOp)) );
stmt( IRStmt_Put( OFFB_CC_DEP1, widenUto64(mkexpr(res))) );
stmt( IRStmt_Put( OFFB_CC_DEP2, mkU64(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, ULong base_op )
{
switch (ty) {
case Ity_I8:
stmt( IRStmt_Put( OFFB_CC_OP, mkU64(base_op+0) ) );
break;
case Ity_I16:
stmt( IRStmt_Put( OFFB_CC_OP, mkU64(base_op+1) ) );
break;
case Ity_I32:
stmt( IRStmt_Put( OFFB_CC_OP, mkU64(base_op+2) ) );
break;
case Ity_I64:
stmt( IRStmt_Put( OFFB_CC_OP, mkU64(base_op+3) ) );
break;
default:
vpanic("setFlags_MUL(amd64)");
}
stmt( IRStmt_Put( OFFB_CC_DEP1, widenUto64(mkexpr(arg1)) ));
stmt( IRStmt_Put( OFFB_CC_DEP2, widenUto64(mkexpr(arg2)) ));
}
/* -------------- Condition codes. -------------- */
/* Condition codes, using the AMD encoding. */
static const HChar* name_AMD64Condcode ( AMD64Condcode cond )
{
switch (cond) {
case AMD64CondO: return "o";
case AMD64CondNO: return "no";
case AMD64CondB: return "b";
case AMD64CondNB: return "ae"; /*"nb";*/
case AMD64CondZ: return "e"; /*"z";*/
case AMD64CondNZ: return "ne"; /*"nz";*/
case AMD64CondBE: return "be";
case AMD64CondNBE: return "a"; /*"nbe";*/
case AMD64CondS: return "s";
case AMD64CondNS: return "ns";
case AMD64CondP: return "p";
case AMD64CondNP: return "np";
case AMD64CondL: return "l";
case AMD64CondNL: return "ge"; /*"nl";*/
case AMD64CondLE: return "le";
case AMD64CondNLE: return "g"; /*"nle";*/
case AMD64CondAlways: return "ALWAYS";
default: vpanic("name_AMD64Condcode");
}
}
static
AMD64Condcode positiveIse_AMD64Condcode ( AMD64Condcode cond,
/*OUT*/Bool* needInvert )
{
vassert(cond >= AMD64CondO && cond <= AMD64CondNLE);
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_I64);
IRTemp oldcn = newTemp(ty);
IROp plus = mkSizedOp(ty, Iop_Add8);
IROp xor = mkSizedOp(ty, Iop_Xor8);
vassert(typeOfIRTemp(irsb->tyenv, tres) == ty);
switch (sz) {
case 8: thunkOp = AMD64G_CC_OP_ADCQ; break;
case 4: thunkOp = AMD64G_CC_OP_ADCL; break;
case 2: thunkOp = AMD64G_CC_OP_ADCW; break;
case 1: thunkOp = AMD64G_CC_OP_ADCB; break;
default: vassert(0);
}
/* oldc = old carry flag, 0 or 1 */
assign( oldc, binop(Iop_And64,
mk_amd64g_calculate_rflags_c(),
mkU64(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, mkU64(thunkOp) ) );
stmt( IRStmt_Put( OFFB_CC_DEP1, widenUto64(mkexpr(ta1)) ));
stmt( IRStmt_Put( OFFB_CC_DEP2, widenUto64(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_I64);
IRTemp oldcn = newTemp(ty);
IROp minus = mkSizedOp(ty, Iop_Sub8);
IROp xor = mkSizedOp(ty, Iop_Xor8);
vassert(typeOfIRTemp(irsb->tyenv, tres) == ty);
switch (sz) {
case 8: thunkOp = AMD64G_CC_OP_SBBQ; break;
case 4: thunkOp = AMD64G_CC_OP_SBBL; break;
case 2: thunkOp = AMD64G_CC_OP_SBBW; break;
case 1: thunkOp = AMD64G_CC_OP_SBBB; break;
default: vassert(0);
}
/* oldc = old carry flag, 0 or 1 */
assign( oldc, binop(Iop_And64,
mk_amd64g_calculate_rflags_c(),
mkU64(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, mkU64(thunkOp) ) );
stmt( IRStmt_Put( OFFB_CC_DEP1, widenUto64(mkexpr(ta1) )) );
stmt( IRStmt_Put( OFFB_CC_DEP2, widenUto64(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(amd64)");
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(amd64)");
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(amd64)");
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(amd64)");
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(amd64)");
return grp8_names[opc_aux];
}
//.. 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(amd64,guest)");
return mmx_names[mmxreg];
}
static const HChar* nameXMMReg ( Int xmmreg )
{
static const HChar* xmm_names[16]
= { "%xmm0", "%xmm1", "%xmm2", "%xmm3",
"%xmm4", "%xmm5", "%xmm6", "%xmm7",
"%xmm8", "%xmm9", "%xmm10", "%xmm11",
"%xmm12", "%xmm13", "%xmm14", "%xmm15" };
if (xmmreg < 0 || xmmreg > 15) vpanic("nameXMMReg(amd64)");
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(amd64,guest)");
}
}
static HChar nameISize ( Int size )
{
switch (size) {
case 8: return 'q';
case 4: return 'l';
case 2: return 'w';
case 1: return 'b';
default: vpanic("nameISize(amd64)");
}
}
static const HChar* nameYMMReg ( Int ymmreg )
{
static const HChar* ymm_names[16]
= { "%ymm0", "%ymm1", "%ymm2", "%ymm3",
"%ymm4", "%ymm5", "%ymm6", "%ymm7",
"%ymm8", "%ymm9", "%ymm10", "%ymm11",
"%ymm12", "%ymm13", "%ymm14", "%ymm15" };
if (ymmreg < 0 || ymmreg > 15) vpanic("nameYMMReg(amd64)");
return ymm_names[ymmreg];
}
/*------------------------------------------------------------*/
/*--- JMP helpers ---*/
/*------------------------------------------------------------*/
static void jmp_lit( /*MOD*/DisResult* dres,
IRJumpKind kind, Addr64 d64 )
{
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_RIP, mkU64(d64) ) );
}
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_RIP, mkexpr(t) ) );
}
static
void jcc_01 ( /*MOD*/DisResult* dres,
AMD64Condcode cond, Addr64 d64_false, Addr64 d64_true )
{
Bool invert;
AMD64Condcode 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_AMD64Condcode ( cond, &invert );
if (invert) {
stmt( IRStmt_Exit( mk_amd64g_calculate_condition(condPos),
Ijk_Boring,
IRConst_U64(d64_false),
OFFB_RIP ) );
stmt( IRStmt_Put( OFFB_RIP, mkU64(d64_true) ) );
} else {
stmt( IRStmt_Exit( mk_amd64g_calculate_condition(condPos),
Ijk_Boring,
IRConst_U64(d64_true),
OFFB_RIP ) );
stmt( IRStmt_Put( OFFB_RIP, mkU64(d64_false) ) );
}
}
/* Let new_rsp be the %rsp value after a call/return. Let nia be the
guest address of the next instruction to be executed.
This function generates an AbiHint to say that -128(%rsp)
.. -1(%rsp) should now be regarded as uninitialised.
*/
static
void make_redzone_AbiHint ( VexAbiInfo* vbi,
IRTemp new_rsp, IRTemp nia, const HChar* who )
{
Int szB = vbi->guest_stack_redzone_size;
vassert(szB >= 0);
/* A bit of a kludge. Currently the only AbI we've guested AMD64
for is ELF. So just check it's the expected 128 value
(paranoia). */
vassert(szB == 128);
if (0) vex_printf("AbiHint: %s\n", who);
vassert(typeOfIRTemp(irsb->tyenv, new_rsp) == Ity_I64);
vassert(typeOfIRTemp(irsb->tyenv, nia) == Ity_I64);
if (szB > 0)
stmt( IRStmt_AbiHint(
binop(Iop_Sub64, mkexpr(new_rsp), mkU64(szB)),
szB,
mkexpr(nia)
));
}
/*------------------------------------------------------------*/
/*--- Disassembling addressing modes ---*/
/*------------------------------------------------------------*/
static
const HChar* segRegTxt ( Prefix pfx )
{
if (pfx & PFX_CS) return "%cs:";
if (pfx & PFX_DS) return "%ds:";
if (pfx & PFX_ES) return "%es:";
if (pfx & PFX_FS) return "%fs:";
if (pfx & PFX_GS) return "%gs:";
if (pfx & PFX_SS) return "%ss:";
return ""; /* no override */
}
/* 'virtual' is an IRExpr* holding a virtual address. Convert it to a
linear address by adding any required segment override as indicated
by sorb, and also dealing with any address size override
present. */
static
IRExpr* handleAddrOverrides ( VexAbiInfo* vbi,
Prefix pfx, IRExpr* virtual )
{
/* --- segment overrides --- */
if (pfx & PFX_FS) {
if (vbi->guest_amd64_assume_fs_is_zero) {
/* Note that this is a linux-kernel specific hack that relies
on the assumption that %fs is always zero. */
/* return virtual + guest_FS_ZERO. */
virtual = binop(Iop_Add64, virtual,
IRExpr_Get(OFFB_FS_ZERO, Ity_I64));
} else {
unimplemented("amd64 %fs segment override");
}
}
if (pfx & PFX_GS) {
if (vbi->guest_amd64_assume_gs_is_0x60) {
/* Note that this is a darwin-kernel specific hack that relies
on the assumption that %gs is always 0x60. */
/* return virtual + guest_GS_0x60. */
virtual = binop(Iop_Add64, virtual,
IRExpr_Get(OFFB_GS_0x60, Ity_I64));
} else {
unimplemented("amd64 %gs segment override");
}
}
/* cs, ds, es and ss are simply ignored in 64-bit mode. */
/* --- address size override --- */
if (haveASO(pfx))
virtual = unop(Iop_32Uto64, unop(Iop_64to32, virtual));
return 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("handleAddrOverrides(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 )
//.. )
//.. );
//..
//.. /* 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 (the latter in *len). 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.
extra_bytes holds the number of bytes after the amode, as supplied
by the caller. This is needed to make sense of %rip-relative
addresses. Note that the value that *len is set to is only the
length of the amode itself and does not include the value supplied
in extra_bytes.
*/
static IRTemp disAMode_copy2tmp ( IRExpr* addr64 )
{
IRTemp tmp = newTemp(Ity_I64);
assign( tmp, addr64 );
return tmp;
}
static
IRTemp disAMode ( /*OUT*/Int* len,
VexAbiInfo* vbi, Prefix pfx, Long delta,
/*OUT*/HChar* buf, Int extra_bytes )
{
UChar mod_reg_rm = getUChar(delta);
delta++;
buf[0] = (UChar)0;
vassert(extra_bytes >= 0 && extra_bytes < 10);
/* 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) {
/* REX.B==0: (%rax) .. (%rdi), not including (%rsp) or (%rbp).
REX.B==1: (%r8) .. (%r15), not including (%r12) or (%r13).
*/
case 0x00: case 0x01: case 0x02: case 0x03:
/* ! 04 */ /* ! 05 */ case 0x06: case 0x07:
{ UChar rm = toUChar(mod_reg_rm & 7);
DIS(buf, "%s(%s)", segRegTxt(pfx), nameIRegRexB(8,pfx,rm));
*len = 1;
return disAMode_copy2tmp(
handleAddrOverrides(vbi, pfx, getIRegRexB(8,pfx,rm)));
}
/* REX.B==0: d8(%rax) ... d8(%rdi), not including d8(%rsp)
REX.B==1: d8(%r8) ... d8(%r15), not including d8(%r12)
*/
case 0x08: case 0x09: case 0x0A: case 0x0B:
/* ! 0C */ case 0x0D: case 0x0E: case 0x0F:
{ UChar rm = toUChar(mod_reg_rm & 7);
Long d = getSDisp8(delta);
if (d == 0) {
DIS(buf, "%s(%s)", segRegTxt(pfx), nameIRegRexB(8,pfx,rm));
} else {
DIS(buf, "%s%lld(%s)", segRegTxt(pfx), d, nameIRegRexB(8,pfx,rm));
}
*len = 2;
return disAMode_copy2tmp(
handleAddrOverrides(vbi, pfx,
binop(Iop_Add64,getIRegRexB(8,pfx,rm),mkU64(d))));
}
/* REX.B==0: d32(%rax) ... d32(%rdi), not including d32(%rsp)
REX.B==1: d32(%r8) ... d32(%r15), not including d32(%r12)
*/
case 0x10: case 0x11: case 0x12: case 0x13:
/* ! 14 */ case 0x15: case 0x16: case 0x17:
{ UChar rm = toUChar(mod_reg_rm & 7);
Long d = getSDisp32(delta);
DIS(buf, "%s%lld(%s)", segRegTxt(pfx), d, nameIRegRexB(8,pfx,rm));
*len = 5;
return disAMode_copy2tmp(
handleAddrOverrides(vbi, pfx,
binop(Iop_Add64,getIRegRexB(8,pfx,rm),mkU64(d))));
}
/* REX.B==0: a register, %rax .. %rdi. This shouldn't happen. */
/* REX.B==1: a register, %r8 .. %r16. This shouldn't happen. */
case 0x18: case 0x19: case 0x1A: case 0x1B:
case 0x1C: case 0x1D: case 0x1E: case 0x1F:
vpanic("disAMode(amd64): not an addr!");
/* RIP + disp32. This assumes that guest_RIP_curr_instr is set
correctly at the start of handling each instruction. */
case 0x05:
{ Long d = getSDisp32(delta);
*len = 5;
DIS(buf, "%s%lld(%%rip)", segRegTxt(pfx), d);
/* We need to know the next instruction's start address.
Try and figure out what it is, record the guess, and ask
the top-level driver logic (bbToIR_AMD64) to check we
guessed right, after the instruction is completely
decoded. */
guest_RIP_next_mustcheck = True;
guest_RIP_next_assumed = guest_RIP_bbstart
+ delta+4 + extra_bytes;
return disAMode_copy2tmp(
handleAddrOverrides(vbi, pfx,
binop(Iop_Add64, mkU64(guest_RIP_next_assumed),
mkU64(d))));
}
case 0x04: {
/* SIB, with no displacement. Special cases:
-- %rsp cannot act as an index value.
If index_r indicates %rsp, zero is used for the index.
-- when mod is zero and base indicates RBP or R13, base is
instead a 32-bit sign-extended literal.
It's all madness, I tell you. Extract %index, %base and
scale from the SIB byte. The value denoted is then:
| %index == %RSP && (%base == %RBP || %base == %R13)
= d32 following SIB byte
| %index == %RSP && !(%base == %RBP || %base == %R13)
= %base
| %index != %RSP && (%base == %RBP || %base == %R13)
= d32 following SIB byte + (%index << scale)
| %index != %RSP && !(%base == %RBP || %base == %R13)
= %base + (%index << scale)
*/
UChar sib = getUChar(delta);
UChar scale = toUChar((sib >> 6) & 3);
UChar index_r = toUChar((sib >> 3) & 7);
UChar base_r = toUChar(sib & 7);
/* correct since #(R13) == 8 + #(RBP) */
Bool base_is_BPor13 = toBool(base_r == R_RBP);
Bool index_is_SP = toBool(index_r == R_RSP && 0==getRexX(pfx));
delta++;
if ((!index_is_SP) && (!base_is_BPor13)) {
if (scale == 0) {
DIS(buf, "%s(%s,%s)", segRegTxt(pfx),
nameIRegRexB(8,pfx,base_r),
nameIReg64rexX(pfx,index_r));
} else {
DIS(buf, "%s(%s,%s,%d)", segRegTxt(pfx),
nameIRegRexB(8,pfx,base_r),
nameIReg64rexX(pfx,index_r), 1<<scale);
}
*len = 2;
return
disAMode_copy2tmp(
handleAddrOverrides(vbi, pfx,
binop(Iop_Add64,
getIRegRexB(8,pfx,base_r),
binop(Iop_Shl64, getIReg64rexX(pfx,index_r),
mkU8(scale)))));
}
if ((!index_is_SP) && base_is_BPor13) {
Long d = getSDisp32(delta);
DIS(buf, "%s%lld(,%s,%d)", segRegTxt(pfx), d,
nameIReg64rexX(pfx,index_r), 1<<scale);
*len = 6;
return
disAMode_copy2tmp(
handleAddrOverrides(vbi, pfx,
binop(Iop_Add64,
binop(Iop_Shl64, getIReg64rexX(pfx,index_r),
mkU8(scale)),
mkU64(d))));
}
if (index_is_SP && (!base_is_BPor13)) {
DIS(buf, "%s(%s)", segRegTxt(pfx), nameIRegRexB(8,pfx,base_r));
*len = 2;
return disAMode_copy2tmp(
handleAddrOverrides(vbi, pfx, getIRegRexB(8,pfx,base_r)));
}
if (index_is_SP && base_is_BPor13) {
Long d = getSDisp32(delta);
DIS(buf, "%s%lld", segRegTxt(pfx), d);
*len = 6;
return disAMode_copy2tmp(
handleAddrOverrides(vbi, pfx, mkU64(d)));
}
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 = getUChar(delta);
UChar scale = toUChar((sib >> 6) & 3);
UChar index_r = toUChar((sib >> 3) & 7);
UChar base_r = toUChar(sib & 7);
Long d = getSDisp8(delta+1);
if (index_r == R_RSP && 0==getRexX(pfx)) {
DIS(buf, "%s%lld(%s)", segRegTxt(pfx),
d, nameIRegRexB(8,pfx,base_r));
*len = 3;
return disAMode_copy2tmp(
handleAddrOverrides(vbi, pfx,
binop(Iop_Add64, getIRegRexB(8,pfx,base_r), mkU64(d)) ));
} else {
if (scale == 0) {
DIS(buf, "%s%lld(%s,%s)", segRegTxt(pfx), d,
nameIRegRexB(8,pfx,base_r),
nameIReg64rexX(pfx,index_r));
} else {
DIS(buf, "%s%lld(%s,%s,%d)", segRegTxt(pfx), d,
nameIRegRexB(8,pfx,base_r),
nameIReg64rexX(pfx,index_r), 1<<scale);
}
*len = 3;
return
disAMode_copy2tmp(
handleAddrOverrides(vbi, pfx,
binop(Iop_Add64,
binop(Iop_Add64,
getIRegRexB(8,pfx,base_r),
binop(Iop_Shl64,
getIReg64rexX(pfx,index_r), mkU8(scale))),
mkU64(d))));
}
vassert(0); /*NOTREACHED*/
}