From 91a05eed82fda46526cff26ad4ad1201164b34c6 Mon Sep 17 00:00:00 2001 From: Florian Krohm Date: Tue, 28 Aug 2012 16:49:30 +0000 Subject: [PATCH] VEX-side support for the V-bit tester. - recognise the new "special instruction" for all architectures (ARM needs implementation work; x86 and ARM are untested) - inject IR into the superblock - type definition for the IR injection control block git-svn-id: svn://svn.valgrind.org/vex/trunk@2490 --- VEX/priv/guest_amd64_toIR.c | 22 ++++ VEX/priv/guest_arm_toIR.c | 25 ++++ VEX/priv/guest_mips_toIR.c | 19 +++ VEX/priv/guest_ppc_toIR.c | 22 ++++ VEX/priv/guest_s390_toIR.c | 17 +++ VEX/priv/guest_x86_toIR.c | 21 +++ VEX/priv/ir_inject.c | 253 ++++++++++++++++++++++++++++++++++++ VEX/pub/libvex.h | 27 ++++ VEX/pub/libvex_ir.h | 10 +- 9 files changed, 415 insertions(+), 1 deletion(-) create mode 100644 VEX/priv/ir_inject.c diff --git a/VEX/priv/guest_amd64_toIR.c b/VEX/priv/guest_amd64_toIR.c index fc24a3563c..4cf7e6da14 100644 --- a/VEX/priv/guest_amd64_toIR.c +++ b/VEX/priv/guest_amd64_toIR.c @@ -117,6 +117,7 @@ 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 @@ -26548,6 +26549,27 @@ DisResult disInstr_AMD64_WRK ( vassert(dres.whatNext == Dis_StopHere); goto decode_success; } + else + if (code[16] == 0x48 && code[17] == 0x87 + && code[18] == 0xff /* xchgq %rdi,%rdi */) { + /* IR injection */ + DIP("IR injection\n"); + vex_inject_ir(irsb, Iend_LE); + + // Invalidate the current insn. The reason is that the IRop we're + // injecting here can change. In which case the translation has to + // be redone. For ease of handling, we simply invalidate all the + // time. + stmt(IRStmt_Put(OFFB_TISTART, mkU64(guest_RIP_curr_instr))); + stmt(IRStmt_Put(OFFB_TILEN, mkU64(19))); + + delta += 19; + + stmt( IRStmt_Put( OFFB_RIP, mkU64(guest_RIP_bbstart + delta) ) ); + dres.whatNext = Dis_StopHere; + dres.jk_StopHere = Ijk_TInval; + goto decode_success; + } /* We don't know what it is. */ goto decode_failure; /*NOTREACHED*/ diff --git a/VEX/priv/guest_arm_toIR.c b/VEX/priv/guest_arm_toIR.c index 553dee1c8a..29e36510da 100644 --- a/VEX/priv/guest_arm_toIR.c +++ b/VEX/priv/guest_arm_toIR.c @@ -91,6 +91,7 @@ E18AA00A (orr r10,r10,r10) R3 = client_request ( R4 ) E18BB00B (orr r11,r11,r11) R3 = guest_NRADDR E18CC00C (orr r12,r12,r12) branch-and-link-to-noredir R4 + E18DD00D (orr r13,r13,r13) IR injection Any other bytes following the 16-byte preamble are illegal and constitute a failure in instruction decoding. This all assumes @@ -12476,6 +12477,30 @@ DisResult disInstr_ARM_WRK ( dres.whatNext = Dis_StopHere; goto decode_success; } + else + if (getUIntLittleEndianly(code+16) == 0xE18DD00D + /* orr r13,r13,r13 */) { + /* IR injection */ + DIP("IR injection\n"); + + vex_inject_ir(irsb, Iend_LE); + + // Invalidate the current insn. The reason is that the IRop we're + // injecting here can change. In which case the translation has to + // be redone. For ease of handling, we simply invalidate all the + // time. +#if 0 + // FIXME: needs to be fixed + stmt(IRStmt_Put(OFFB_TISTART, mkU32(guest_R15_curr_instr_notENC))); + stmt(IRStmt_Put(OFFB_TILEN, mkU32(20))); + + llPutIReg(15, mkU32( guest_R15_curr_instr_notENC + 20 )); + + dres.whatNext = Dis_StopHere; + dres.jk_StopHere = Ijk_TInval; + goto decode_success; +#endif + } /* We don't know what it is. Set opc1/opc2 so decode_failure can print the insn following the Special-insn preamble. */ insn = getUIntLittleEndianly(code+16); diff --git a/VEX/priv/guest_mips_toIR.c b/VEX/priv/guest_mips_toIR.c index 0fcb07ce7e..3c72619cdf 100644 --- a/VEX/priv/guest_mips_toIR.c +++ b/VEX/priv/guest_mips_toIR.c @@ -1292,6 +1292,25 @@ static DisResult disInstr_MIPS_WRK ( Bool(*resteerOkFn) (/*opaque */void *, dres.jk_StopHere = Ijk_NoRedir; dres.whatNext = Dis_StopHere; goto decode_success; + } else if (getUInt(code + 16) == 0x016b5825/* or t3,t3,t3 */ ) { + /* IR injection */ + DIP("IR injection\n"); +#if defined (_MIPSEL) + vex_inject_ir(irsb, Iend_LE); +#elif defined (_MIPSEB) + vex_inject_ir(irsb, Iend_BE); +#endif + stmt(IRStmt_Put(offsetof(VexGuestMIPS32State, guest_TISTART), + mkU32(guest_PC_curr_instr))); + stmt(IRStmt_Put(offsetof(VexGuestMIPS32State, guest_TILEN), + mkU32(20))); + + putPC(mkU32(guest_PC_curr_instr + 20)); + dres.whatNext = Dis_StopHere; + dres.jk_StopHere = Ijk_TInval; + dres.len = 20; + delta += 20; + goto decode_success; } /* We don't know what it is. Set opc1/opc2 so decode_failure diff --git a/VEX/priv/guest_ppc_toIR.c b/VEX/priv/guest_ppc_toIR.c index cffdad1f46..6439e79d37 100644 --- a/VEX/priv/guest_ppc_toIR.c +++ b/VEX/priv/guest_ppc_toIR.c @@ -93,6 +93,7 @@ 7C421378 (or 2,2,2) %R3 = guest_NRADDR 7C631B78 (or 3,3,3) branch-and-link-to-noredir %R11 7C842378 (or 4,4,4) %R3 = guest_NRADDR_GPR2 + 7CA52B78 (or 5,5,5) IR injection Any other bytes following the 16-byte preamble are illegal and constitute a failure in instruction decoding. This all assumes @@ -16589,6 +16590,27 @@ DisResult disInstr_PPC_WRK ( putIReg(3, IRExpr_Get( OFFB_NRADDR_GPR2, ty )); goto decode_success; } + else + if (getUIntBigendianly(code+16) == 0x7CA52B78 /* or 5,5,5 */) { + DIP("IR injection\n"); + + vex_inject_ir(irsb, Iend_BE); + + delta += 20; + dres.len = 20; + + // Invalidate the current insn. The reason is that the IRop we're + // injecting here can change. In which case the translation has to + // be redone. For ease of handling, we simply invalidate all the + // time. + stmt(IRStmt_Put(OFFB_TISTART, mkU64(guest_CIA_curr_instr))); + stmt(IRStmt_Put(OFFB_TILEN, mkU64(20))); + + putGST( PPC_GST_CIA, mkSzImm( ty, guest_CIA_bbstart + delta )); + dres.whatNext = Dis_StopHere; + dres.jk_StopHere = Ijk_TInval; + goto decode_success; + } /* We don't know what it is. Set opc1/opc2 so decode_failure can print the insn following the Special-insn preamble. */ theInstr = getUIntBigendianly(code+16); diff --git a/VEX/priv/guest_s390_toIR.c b/VEX/priv/guest_s390_toIR.c index da1c92f448..d7e18dd2ae 100644 --- a/VEX/priv/guest_s390_toIR.c +++ b/VEX/priv/guest_s390_toIR.c @@ -14129,6 +14129,23 @@ s390_decode_special_and_irgen(UChar *bytes) s390_irgen_guest_NRADDR(); } else if (bytes[0] == 0x18 && bytes[1] == 0x44 /* lr %r4, %r4 */) { s390_irgen_call_noredir(); + } else if (bytes[0] == 0x18 && bytes[1] == 0x55 /* lr %r5, %r5 */) { + vex_inject_ir(irsb, Iend_BE); + + /* Invalidate the current insn. The reason is that the IRop we're + injecting here can change. In which case the translation has to + be redone. For ease of handling, we simply invalidate all the + time. */ + stmt(IRStmt_Put(S390X_GUEST_OFFSET(guest_TISTART), + mkU64(guest_IA_curr_instr))); + stmt(IRStmt_Put(S390X_GUEST_OFFSET(guest_TILEN), + mkU64(guest_IA_next_instr - guest_IA_curr_instr))); + vassert(guest_IA_next_instr - guest_IA_curr_instr == + S390_SPECIAL_OP_PREAMBLE_SIZE + S390_SPECIAL_OP_SIZE); + + put_IA(mkaddr_expr(guest_IA_next_instr)); + dis_res->whatNext = Dis_StopHere; + dis_res->jk_StopHere = Ijk_TInval; } else { /* We don't know what it is. */ return S390_DECODE_UNKNOWN_SPECIAL_INSN; diff --git a/VEX/priv/guest_x86_toIR.c b/VEX/priv/guest_x86_toIR.c index 5dc58b5e9f..e98762d5d8 100644 --- a/VEX/priv/guest_x86_toIR.c +++ b/VEX/priv/guest_x86_toIR.c @@ -148,6 +148,7 @@ 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 @@ -8017,6 +8018,26 @@ DisResult disInstr_X86_WRK ( vassert(dres.whatNext == Dis_StopHere); goto decode_success; } + else + if (code[12] == 0x87 && code[13] == 0xFF /* xchgl %edi,%edi */) { + /* IR injection */ + DIP("IR injection\n"); + vex_inject_ir(irsb, Iend_LE); + + // Invalidate the current insn. The reason is that the IRop we're + // injecting here can change. In which case the translation has to + // be redone. For ease of handling, we simply invalidate all the + // time. + stmt(IRStmt_Put(OFFB_TISTART, mkU32(guest_EIP_curr_instr))); + stmt(IRStmt_Put(OFFB_TILEN, mkU32(14))); + + delta += 14; + + stmt( IRStmt_Put( OFFB_EIP, mkU32(guest_EIP_bbstart + delta) ) ); + dres.whatNext = Dis_StopHere; + dres.jk_StopHere = Ijk_TInval; + goto decode_success; + } /* We don't know what it is. */ goto decode_failure; /*NOTREACHED*/ diff --git a/VEX/priv/ir_inject.c b/VEX/priv/ir_inject.c new file mode 100644 index 0000000000..53addbb339 --- /dev/null +++ b/VEX/priv/ir_inject.c @@ -0,0 +1,253 @@ +/* -*- mode: C; c-basic-offset: 3; -*- */ + +/*---------------------------------------------------------------*/ +/*--- begin ir_inject.c ---*/ +/*---------------------------------------------------------------*/ + + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2012-2012 Florian Krohm (britzel@acm.org) + + 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. +*/ + +#include "libvex_basictypes.h" +#include "libvex_ir.h" +#include "libvex.h" +#include "main_util.h" + +/* Convenience macros for readibility */ +#define mkU32(v) IRExpr_Const(IRConst_U32(v)) +#define mkU64(v) IRExpr_Const(IRConst_U64(v)) +#define unop(kind, a) IRExpr_Unop(kind, a) +#define binop(kind, a1, a2) IRExpr_Binop(kind, a1, a2) +#define triop(kind, a1, a2, a3) IRExpr_Triop(kind, a1, a2, a3) +#define qop(kind, a1, a2, a3, a4) IRExpr_Qop(kind, a1, a2, a3, a4) +#define stmt(irsb, st) addStmtToIRSB(irsb, st) + + +/* The IR Injection Control Block. vex_inject_ir will query its contents + to construct IR statements for testing purposes. */ +static IRICB iricb; + + +void +LibVEX_InitIRI(const IRICB *iricb_in) +{ + iricb = *iricb_in; // copy in +} + + +static IRExpr * +load_aux(IREndness endian, IRType type, IRExpr *addr) +{ + if (type == Ity_D64) { + /* The insn selectors do not support loading a DFP value from memory. + So we need to fix it here by loading an integer value and + reinterpreting it as DFP. */ + return unop(Iop_ReinterpI64asD64, + IRExpr_Load(endian, Ity_I64, addr)); + } + if (type == Ity_I1) { + /* A Boolean value is stored as a 32-bit entity (see store_aux). */ + return unop(Iop_32to1, IRExpr_Load(endian, Ity_I32, addr)); + } + + return IRExpr_Load(endian, type, addr); +} + + +/* Load a value from memory. Loads of more than 8 byte are split into + a series of 8-byte loads and combined using appropriate IROps. */ +static IRExpr * +load(IREndness endian, IRType type, HWord haddr) +{ + IROp concat; + IRExpr *addr, *next_addr; + + if (VEX_HOST_WORDSIZE == 8) { + addr = mkU64(haddr); + next_addr = binop(Iop_Add64, addr, mkU64(8)); + } else if (VEX_HOST_WORDSIZE == 4) { + addr = mkU32(haddr); + next_addr = binop(Iop_Add32, addr, mkU32(4)); + } else { + vpanic("invalid #bytes for address"); + } + + switch (type) { + case Ity_I128: concat = Iop_64HLto128; type = Ity_I64; goto load128; + case Ity_F128: concat = Iop_F64HLtoF128; type = Ity_F64; goto load128; + case Ity_D128: concat = Iop_D64HLtoD128; type = Ity_D64; goto load128; + + load128: + /* Two loads of 64 bit each. */ + if (endian == Iend_BE) { + /* The more significant bits are at the lower address. */ + return binop(concat, + load_aux(endian, type, addr), + load_aux(endian, type, next_addr)); + } else { + /* The more significant bits are at the higher address. */ + return binop(concat, + load_aux(endian, type, next_addr), + load_aux(endian, type, addr)); + } + + default: + return load_aux(endian, type, addr); + } +} + + +static void +store_aux(IRSB *irsb, IREndness endian, IRExpr *addr, IRExpr *data) +{ + if (typeOfIRExpr(irsb->tyenv, data) == Ity_D64) { + /* The insn selectors do not support writing a DFP value to memory. + So we need to fix it here by reinterpreting the DFP value as an + integer and storing that. */ + data = unop(Iop_ReinterpD64asI64, data); + } + if (typeOfIRExpr(irsb->tyenv, data) == Ity_I1) { + /* We cannot store a single bit. So we store it in a 32-bit container. + See also load_aux. */ + data = unop(Iop_1Uto32, data); + } + stmt(irsb, IRStmt_Store(endian, addr, data)); +} + + +/* Store a value to memory. If a value requires more than 8 bytes a series + of 8-byte loads will be generated. */ +static void __inline__ +store(IRSB *irsb, IREndness endian, HWord haddr, IRExpr *data) +{ + IROp high, low; + IRExpr *addr, *next_addr; + + if (VEX_HOST_WORDSIZE == 8) { + addr = mkU64(haddr); + next_addr = binop(Iop_Add64, addr, mkU64(8)); + } else if (VEX_HOST_WORDSIZE == 4) { + addr = mkU32(haddr); + next_addr = binop(Iop_Add32, addr, mkU32(4)); + } else { + vpanic("invalid #bytes for address"); + } + + switch (typeOfIRExpr(irsb->tyenv, data)) { + case Ity_I128: high = Iop_128HIto64; low = Iop_128to64; goto store128; + case Ity_F128: high = Iop_F128HItoF64; low = Iop_F128LOtoF64; goto store128; + case Ity_D128: high = Iop_D128HItoD64; low = Iop_D128LOtoD64; goto store128; + + store128: + /* Two stores of 64 bit each. */ + if (endian == Iend_BE) { + /* The more significant bits are at the lower address. */ + store_aux(irsb, endian, addr, unop(high, data)); + store_aux(irsb, endian, next_addr, unop(low, data)); + } else { + /* The more significant bits are at the higher address. */ + store_aux(irsb, endian, addr, unop(low, data)); + store_aux(irsb, endian, next_addr, unop(high, data)); + } + return; + + default: + store_aux(irsb, endian, addr, data); + return; + } +} + + +/* Inject IR stmts depending on the data provided in the control + block iricb. */ +void +vex_inject_ir(IRSB *irsb, IREndness endian) +{ + IRExpr *data, *rounding_mode, *opnd1, *opnd2, *opnd3, *opnd4; + + rounding_mode = NULL; + if (iricb.rounding_mode != NO_ROUNDING_MODE) { + rounding_mode = mkU32(iricb.rounding_mode); + } + + switch (iricb.num_operands) { + case 1: + opnd1 = load(endian, iricb.t_opnd1, iricb.opnd1); + if (rounding_mode) + data = binop(iricb.op, rounding_mode, opnd1); + else + data = unop(iricb.op, opnd1); + break; + + case 2: + opnd1 = load(endian, iricb.t_opnd1, iricb.opnd1); + opnd2 = load(endian, iricb.t_opnd2, iricb.opnd2); + if (rounding_mode) + data = triop(iricb.op, rounding_mode, opnd1, opnd2); + else + data = binop(iricb.op, opnd1, opnd2); + break; + + case 3: + opnd1 = load(endian, iricb.t_opnd1, iricb.opnd1); + opnd2 = load(endian, iricb.t_opnd2, iricb.opnd2); + opnd3 = load(endian, iricb.t_opnd3, iricb.opnd3); + if (rounding_mode) + data = qop(iricb.op, rounding_mode, opnd1, opnd2, opnd3); + else + data = triop(iricb.op, opnd1, opnd2, opnd3); + break; + + case 4: + vassert(rounding_mode == NULL); + opnd1 = load(endian, iricb.t_opnd1, iricb.opnd1); + opnd2 = load(endian, iricb.t_opnd2, iricb.opnd2); + opnd3 = load(endian, iricb.t_opnd3, iricb.opnd3); + opnd4 = load(endian, iricb.t_opnd4, iricb.opnd4); + data = qop(iricb.op, opnd1, opnd2, opnd3, opnd4); + break; + + default: + vpanic("unsupported operator"); + } + + store(irsb, endian, iricb.result, data); + + if (0) { + vex_printf("BEGIN inject\n"); + if (sizeofIRType(iricb.t_result) <= 8) { + ppIRStmt(irsb->stmts[irsb->stmts_used - 1]); + } else if (sizeofIRType(iricb.t_result) == 16) { + ppIRStmt(irsb->stmts[irsb->stmts_used - 2]); + vex_printf("\n"); + ppIRStmt(irsb->stmts[irsb->stmts_used - 1]); + vex_printf("\n"); + } + vex_printf("END inject\n"); + } +} + +/*---------------------------------------------------------------*/ +/*--- end ir_inject.c ---*/ +/*---------------------------------------------------------------*/ diff --git a/VEX/pub/libvex.h b/VEX/pub/libvex.h index 95551a26a6..d175f731a3 100644 --- a/VEX/pub/libvex.h +++ b/VEX/pub/libvex.h @@ -750,6 +750,33 @@ VexInvalRange LibVEX_PatchProfInc ( VexArch arch_host, extern void LibVEX_ShowStats ( void ); +/*-------------------------------------------------------*/ +/*-- IR injection --*/ +/*-------------------------------------------------------*/ + +/* IR Injection Control Block */ + +#define NO_ROUNDING_MODE (~0u) + +typedef + struct { + IROp op; // the operation to perform + HWord result; // address of the result + HWord opnd1; // address of 1st operand + HWord opnd2; // address of 2nd operand + HWord opnd3; // address of 3rd operand + HWord opnd4; // address of 4th operand + IRType t_result; // type of result + IRType t_opnd1; // type of 1st operand + IRType t_opnd2; // type of 2nd operand + IRType t_opnd3; // type of 3rd operand + IRType t_opnd4; // type of 4th operand + UInt rounding_mode; + UInt num_operands; // excluding rounding mode, if any + } + IRICB; + +extern void LibVEX_InitIRI ( const IRICB * ); /*-------------------------------------------------------*/ /*--- Notes ---*/ diff --git a/VEX/pub/libvex_ir.h b/VEX/pub/libvex_ir.h index 24d428d9e4..6886087899 100644 --- a/VEX/pub/libvex_ir.h +++ b/VEX/pub/libvex_ir.h @@ -1461,7 +1461,8 @@ typedef Iop_Recip32Fx8, Iop_Max32Fx8, Iop_Min32Fx8, - Iop_Max64Fx4, Iop_Min64Fx4 + Iop_Max64Fx4, Iop_Min64Fx4, + Iop_LAST /* must be the last enumerator */ } IROp; @@ -2496,6 +2497,13 @@ extern Bool isFlatIRStmt ( IRStmt* ); /* Is this any value actually in the enumeration 'IRType' ? */ extern Bool isPlausibleIRType ( IRType ty ); + +/*---------------------------------------------------------------*/ +/*--- IR injection ---*/ +/*---------------------------------------------------------------*/ +void vex_inject_ir(IRSB *, IREndness); + + #endif /* ndef __LIBVEX_IR_H */ -- 2.47.2