static ARMAMode2* genGuestArrayOffset ( ISelEnv* env, IRRegArray* descr, IRExpr* off, Int bias ) { HReg tmp, tmp2, roff; Int elemSz = sizeofIRType(descr->elemTy); Int nElems = descr->nElems; ARMImm12A imm12a; /* throw out any cases not generated by an x86 front end. In theory there might be a day where we need to handle them -- if we ever run non-x86-guest on x86 host. */ if (nElems != 8 || (elemSz != 1 && elemSz != 8)) vpanic("genGuestArrayOffset(arm host)"); /* Compute off into a reg, %off. Then return: movl %off, %tmp addl $bias, %tmp (if bias != 0) andl %tmp, 7 ... base(%ebp, %tmp, shift) ... */ tmp = newVRegI(env); roff = iselIntExpr_R(env, off); addInstr(env, mk_iMOVsd_RR(roff, tmp)); if (bias != 0) { if ( mk_ARMImm12A( (UInt)bias, &imm12a ) ) { addInstr(env, ARMInstr_DPInstr2(ARMalu_ADD, tmp, tmp, ARMAMode1_I12A( imm12a ))); } else { HReg tmp3 = newVRegI(env); addInstr(env, ARMInstr_Literal( tmp, (UInt)bias )); addInstr(env, ARMInstr_DPInstr2(ARMalu_ADD, tmp, tmp, ARMAMode1_ShlI( tmp3, 0 ))); } } mk_ARMImm12A( (UInt)7, &imm12a ); addInstr(env, ARMInstr_DPInstr2(ARMalu_AND, tmp, tmp, ARMAMode1_I12A( imm12a ))); vassert(elemSz == 1 || elemSz == 8); // CAB: This anywhere near correct? // X86AMode_IRRS: Immediate + Reg1 + (Reg2 << Shift) // return X86AMode_IRRS( descr->base, hregX86_EBP(), tmp, elemSz==8 ? 3 : 0); tmp2 = newVRegI(env); // tmp2 = GET_BP_REG + (tmp << 3|0) addInstr(env, ARMInstr_DPInstr2(ARMalu_ADD, tmp2, GET_BP_REG(), ARMAMode1_ShlI(tmp, elemSz==8 ? 3 : 0))); return ARMAMode2_RI( tmp2, descr->base ); }
/* Store a value to memory. If a value requires more than 8 bytes a series of 8-byte stores will be generated. */ static __inline__ void 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(8)); } else { vpanic("invalid #bytes for address"); } IRType type = typeOfIRExpr(irsb->tyenv, data); vassert(type == Ity_I1 || sizeofIRType(type) <= 16); switch (type) { 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; } }
/* 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; vassert(type == Ity_I1 || sizeofIRType(type) <= 16); 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(8)); } 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); } }
unsigned sizeof_irtype(IRType ty) { return sizeofIRType(ty); }
static void collectStatementInfo(IRTypeEnv* tyenv, IRBB* bbOut, IRStmt* st, Addr* instrAddr, UInt* instrLen, IRExpr** loadAddrExpr, IRExpr** storeAddrExpr, UInt* dataSize, IRType hWordTy) { CLG_ASSERT(isFlatIRStmt(st)); switch (st->tag) { case Ist_NoOp: break; case Ist_AbiHint: /* ABI hints aren't interesting. Ignore. */ break; case Ist_IMark: /* st->Ist.IMark.addr is a 64-bit int. ULong_to_Ptr casts this to the host's native pointer type; if that is 32 bits then it discards the upper 32 bits. If we are cachegrinding on a 32-bit host then we are also ensured that the guest word size is 32 bits, due to the assertion in cg_instrument that the host and guest word sizes must be the same. Hence st->Ist.IMark.addr will have been derived from a 32-bit guest code address and truncation of it is safe. I believe this assignment should be correct for both 32- and 64-bit machines. */ *instrAddr = (Addr)ULong_to_Ptr(st->Ist.IMark.addr); *instrLen = st->Ist.IMark.len; break; case Ist_Tmp: { IRExpr* data = st->Ist.Tmp.data; if (data->tag == Iex_Load) { IRExpr* aexpr = data->Iex.Load.addr; CLG_ASSERT( isIRAtom(aexpr) ); // Note also, endianness info is ignored. I guess that's not // interesting. // XXX: repe cmpsb does two loads... the first one is ignored here! //tl_assert( NULL == *loadAddrExpr ); // XXX: ??? *loadAddrExpr = aexpr; *dataSize = sizeofIRType(data->Iex.Load.ty); } break; } case Ist_Store: { IRExpr* data = st->Ist.Store.data; IRExpr* aexpr = st->Ist.Store.addr; CLG_ASSERT( isIRAtom(aexpr) ); if ( NULL == *storeAddrExpr ) { /* this is a kludge: ignore all except the first store from an instruction. */ *storeAddrExpr = aexpr; *dataSize = sizeofIRType(typeOfIRExpr(tyenv, data)); } break; } case Ist_Dirty: { IRDirty* d = st->Ist.Dirty.details; if (d->mFx != Ifx_None) { /* This dirty helper accesses memory. Collect the details. */ CLG_ASSERT(d->mAddr != NULL); CLG_ASSERT(d->mSize != 0); *dataSize = d->mSize; if (d->mFx == Ifx_Read || d->mFx == Ifx_Modify) *loadAddrExpr = d->mAddr; if (d->mFx == Ifx_Write || d->mFx == Ifx_Modify) *storeAddrExpr = d->mAddr; } else { CLG_ASSERT(d->mAddr == NULL); CLG_ASSERT(d->mSize == 0); } break; } case Ist_Put: case Ist_PutI: case Ist_MFence: case Ist_Exit: break; default: VG_(printf)("\n"); ppIRStmt(st); VG_(printf)("\n"); VG_(tool_panic)("Callgrind: unhandled IRStmt"); } }
static IRSB* lk_instrument ( VgCallbackClosure* closure, IRSB* sbIn, VexGuestLayout* layout, VexGuestExtents* vge, VexArchInfo* archinfo_host, IRType gWordTy, IRType hWordTy ) { IRDirty* di; Int i; IRSB* sbOut; HChar fnname[100]; IRTypeEnv* tyenv = sbIn->tyenv; Addr iaddr = 0, dst; UInt ilen = 0; Bool condition_inverted = False; if (gWordTy != hWordTy) { /* We don't currently support this case. */ VG_(tool_panic)("host/guest word size mismatch"); } /* Set up SB */ sbOut = deepCopyIRSBExceptStmts(sbIn); // Copy verbatim any IR preamble preceding the first IMark i = 0; while (i < sbIn->stmts_used && sbIn->stmts[i]->tag != Ist_IMark) { addStmtToIRSB( sbOut, sbIn->stmts[i] ); i++; } if (clo_basic_counts) { /* Count this superblock. */ di = unsafeIRDirty_0_N( 0, "add_one_SB_entered", VG_(fnptr_to_fnentry)( &add_one_SB_entered ), mkIRExprVec_0() ); addStmtToIRSB( sbOut, IRStmt_Dirty(di) ); } if (clo_trace_sbs) { /* Print this superblock's address. */ di = unsafeIRDirty_0_N( 0, "trace_superblock", VG_(fnptr_to_fnentry)( &trace_superblock ), mkIRExprVec_1( mkIRExpr_HWord( vge->base[0] ) ) ); addStmtToIRSB( sbOut, IRStmt_Dirty(di) ); } if (clo_trace_mem) { events_used = 0; } for (/*use current i*/; i < sbIn->stmts_used; i++) { IRStmt* st = sbIn->stmts[i]; if (!st || st->tag == Ist_NoOp) continue; if (clo_basic_counts) { /* Count one VEX statement. */ di = unsafeIRDirty_0_N( 0, "add_one_IRStmt", VG_(fnptr_to_fnentry)( &add_one_IRStmt ), mkIRExprVec_0() ); addStmtToIRSB( sbOut, IRStmt_Dirty(di) ); } switch (st->tag) { case Ist_NoOp: case Ist_AbiHint: case Ist_Put: case Ist_PutI: case Ist_MBE: addStmtToIRSB( sbOut, st ); break; case Ist_IMark: if (clo_basic_counts) { /* Needed to be able to check for inverted condition in Ist_Exit */ iaddr = st->Ist.IMark.addr; ilen = st->Ist.IMark.len; /* Count guest instruction. */ di = unsafeIRDirty_0_N( 0, "add_one_guest_instr", VG_(fnptr_to_fnentry)( &add_one_guest_instr ), mkIRExprVec_0() ); addStmtToIRSB( sbOut, IRStmt_Dirty(di) ); /* An unconditional branch to a known destination in the * guest's instructions can be represented, in the IRSB to * instrument, by the VEX statements that are the * translation of that known destination. This feature is * called 'SB chasing' and can be influenced by command * line option --vex-guest-chase-thresh. * * To get an accurate count of the calls to a specific * function, taking SB chasing into account, we need to * check for each guest instruction (Ist_IMark) if it is * the entry point of a function. */ tl_assert(clo_fnname); tl_assert(clo_fnname[0]); if (VG_(get_fnname_if_entry)(st->Ist.IMark.addr, fnname, sizeof(fnname)) && 0 == VG_(strcmp)(fnname, clo_fnname)) { di = unsafeIRDirty_0_N( 0, "add_one_func_call", VG_(fnptr_to_fnentry)( &add_one_func_call ), mkIRExprVec_0() ); addStmtToIRSB( sbOut, IRStmt_Dirty(di) ); } } if (clo_trace_mem) { // WARNING: do not remove this function call, even if you // aren't interested in instruction reads. See the comment // above the function itself for more detail. addEvent_Ir( sbOut, mkIRExpr_HWord( (HWord)st->Ist.IMark.addr ), st->Ist.IMark.len ); } addStmtToIRSB( sbOut, st ); break; case Ist_WrTmp: // Add a call to trace_load() if --trace-mem=yes. if (clo_trace_mem) { IRExpr* data = st->Ist.WrTmp.data; if (data->tag == Iex_Load) { addEvent_Dr( sbOut, data->Iex.Load.addr, sizeofIRType(data->Iex.Load.ty) ); } } if (clo_detailed_counts) { IRExpr* expr = st->Ist.WrTmp.data; IRType type = typeOfIRExpr(sbOut->tyenv, expr); tl_assert(type != Ity_INVALID); switch (expr->tag) { case Iex_Load: instrument_detail( sbOut, OpLoad, type, NULL/*guard*/ ); break; case Iex_Unop: case Iex_Binop: case Iex_Triop: case Iex_Qop: case Iex_ITE: instrument_detail( sbOut, OpAlu, type, NULL/*guard*/ ); break; default: break; } } addStmtToIRSB( sbOut, st ); break; case Ist_Store: { IRExpr* data = st->Ist.Store.data; IRType type = typeOfIRExpr(tyenv, data); tl_assert(type != Ity_INVALID); if (clo_trace_mem) { addEvent_Dw( sbOut, st->Ist.Store.addr, sizeofIRType(type) ); } if (clo_detailed_counts) { instrument_detail( sbOut, OpStore, type, NULL/*guard*/ ); } addStmtToIRSB( sbOut, st ); break; } case Ist_StoreG: { IRStoreG* sg = st->Ist.StoreG.details; IRExpr* data = sg->data; IRType type = typeOfIRExpr(tyenv, data); tl_assert(type != Ity_INVALID); if (clo_trace_mem) { addEvent_Dw_guarded( sbOut, sg->addr, sizeofIRType(type), sg->guard ); } if (clo_detailed_counts) { instrument_detail( sbOut, OpStore, type, sg->guard ); } addStmtToIRSB( sbOut, st ); break; } case Ist_LoadG: { IRLoadG* lg = st->Ist.LoadG.details; IRType type = Ity_INVALID; /* loaded type */ IRType typeWide = Ity_INVALID; /* after implicit widening */ typeOfIRLoadGOp(lg->cvt, &typeWide, &type); tl_assert(type != Ity_INVALID); if (clo_trace_mem) { addEvent_Dr_guarded( sbOut, lg->addr, sizeofIRType(type), lg->guard ); } if (clo_detailed_counts) { instrument_detail( sbOut, OpLoad, type, lg->guard ); } addStmtToIRSB( sbOut, st ); break; } case Ist_Dirty: { if (clo_trace_mem) { Int dsize; IRDirty* d = st->Ist.Dirty.details; if (d->mFx != Ifx_None) { // This dirty helper accesses memory. Collect the details. tl_assert(d->mAddr != NULL); tl_assert(d->mSize != 0); dsize = d->mSize; if (d->mFx == Ifx_Read || d->mFx == Ifx_Modify) addEvent_Dr( sbOut, d->mAddr, dsize ); if (d->mFx == Ifx_Write || d->mFx == Ifx_Modify) addEvent_Dw( sbOut, d->mAddr, dsize ); } else { tl_assert(d->mAddr == NULL); tl_assert(d->mSize == 0); } } addStmtToIRSB( sbOut, st ); break; } case Ist_CAS: { /* We treat it as a read and a write of the location. I think that is the same behaviour as it was before IRCAS was introduced, since prior to that point, the Vex front ends would translate a lock-prefixed instruction into a (normal) read followed by a (normal) write. */ Int dataSize; IRType dataTy; IRCAS* cas = st->Ist.CAS.details; tl_assert(cas->addr != NULL); tl_assert(cas->dataLo != NULL); dataTy = typeOfIRExpr(tyenv, cas->dataLo); dataSize = sizeofIRType(dataTy); if (cas->dataHi != NULL) dataSize *= 2; /* since it's a doubleword-CAS */ if (clo_trace_mem) { addEvent_Dr( sbOut, cas->addr, dataSize ); addEvent_Dw( sbOut, cas->addr, dataSize ); } if (clo_detailed_counts) { instrument_detail( sbOut, OpLoad, dataTy, NULL/*guard*/ ); if (cas->dataHi != NULL) /* dcas */ instrument_detail( sbOut, OpLoad, dataTy, NULL/*guard*/ ); instrument_detail( sbOut, OpStore, dataTy, NULL/*guard*/ ); if (cas->dataHi != NULL) /* dcas */ instrument_detail( sbOut, OpStore, dataTy, NULL/*guard*/ ); } addStmtToIRSB( sbOut, st ); break; } case Ist_LLSC: { IRType dataTy; if (st->Ist.LLSC.storedata == NULL) { /* LL */ dataTy = typeOfIRTemp(tyenv, st->Ist.LLSC.result); if (clo_trace_mem) { addEvent_Dr( sbOut, st->Ist.LLSC.addr, sizeofIRType(dataTy) ); /* flush events before LL, helps SC to succeed */ flushEvents(sbOut); } if (clo_detailed_counts) instrument_detail( sbOut, OpLoad, dataTy, NULL/*guard*/ ); } else { /* SC */ dataTy = typeOfIRExpr(tyenv, st->Ist.LLSC.storedata); if (clo_trace_mem) addEvent_Dw( sbOut, st->Ist.LLSC.addr, sizeofIRType(dataTy) ); if (clo_detailed_counts) instrument_detail( sbOut, OpStore, dataTy, NULL/*guard*/ ); } addStmtToIRSB( sbOut, st ); break; } case Ist_Exit: if (clo_basic_counts) { // The condition of a branch was inverted by VEX if a taken // branch is in fact a fall trough according to client address tl_assert(iaddr != 0); dst = (sizeof(Addr) == 4) ? st->Ist.Exit.dst->Ico.U32 : st->Ist.Exit.dst->Ico.U64; condition_inverted = (dst == iaddr + ilen); /* Count Jcc */ if (!condition_inverted) di = unsafeIRDirty_0_N( 0, "add_one_Jcc", VG_(fnptr_to_fnentry)( &add_one_Jcc ), mkIRExprVec_0() ); else di = unsafeIRDirty_0_N( 0, "add_one_inverted_Jcc", VG_(fnptr_to_fnentry)( &add_one_inverted_Jcc ), mkIRExprVec_0() ); addStmtToIRSB( sbOut, IRStmt_Dirty(di) ); } if (clo_trace_mem) { flushEvents(sbOut); } addStmtToIRSB( sbOut, st ); // Original statement if (clo_basic_counts) { /* Count non-taken Jcc */ if (!condition_inverted) di = unsafeIRDirty_0_N( 0, "add_one_Jcc_untaken", VG_(fnptr_to_fnentry)( &add_one_Jcc_untaken ), mkIRExprVec_0() ); else di = unsafeIRDirty_0_N( 0, "add_one_inverted_Jcc_untaken", VG_(fnptr_to_fnentry)( &add_one_inverted_Jcc_untaken ), mkIRExprVec_0() ); addStmtToIRSB( sbOut, IRStmt_Dirty(di) ); } break; default: ppIRStmt(st); tl_assert(0); } } if (clo_basic_counts) { /* Count this basic block. */ di = unsafeIRDirty_0_N( 0, "add_one_SB_completed", VG_(fnptr_to_fnentry)( &add_one_SB_completed ), mkIRExprVec_0() ); addStmtToIRSB( sbOut, IRStmt_Dirty(di) ); } if (clo_trace_mem) { /* At the end of the sbIn. Flush outstandings. */ flushEvents(sbOut); } return sbOut; }
static IRSB* el_instrument ( VgCallbackClosure* closure, IRSB* sbIn, VexGuestLayout* layout, VexGuestExtents* vge, IRType gWordTy, IRType hWordTy ) { IRDirty* di; Int i; IRSB* sbOut; Char fnname[100]; IRType type; IRTypeEnv* tyenv = sbIn->tyenv; Addr iaddr = 0, dst; UInt ilen = 0; Bool condition_inverted = False; if (gWordTy != hWordTy) { /* We don't currently support this case. */ VG_(tool_panic)("host/guest word size mismatch"); } /* Set up SB */ sbOut = deepCopyIRSBExceptStmts(sbIn); // Copy verbatim any IR preamble preceding the first IMark i = 0; while (i < sbIn->stmts_used && sbIn->stmts[i]->tag != Ist_IMark) { addStmtToIRSB( sbOut, sbIn->stmts[i] ); i++; } events_used = 0; for (/*use current i*/; i < sbIn->stmts_used; i++) { IRStmt* st = sbIn->stmts[i]; if (!st || st->tag == Ist_NoOp) continue; switch (st->tag) { case Ist_NoOp: case Ist_AbiHint: case Ist_Put: case Ist_PutI: case Ist_MBE: addStmtToIRSB( sbOut, st ); break; case Ist_IMark: // Store the last seen address lastAddress = st->Ist.IMark.addr; addEvent_Ir( sbOut, mkIRExpr_HWord( (HWord)st->Ist.IMark.addr ), st->Ist.IMark.len ); VG_(get_filename)(lastAddress, (char*) g_buff1, kBuffSize); if(VG_(strcmp)(g_buff1, clo_filename) == 0) { shouldInterpret = 1; ppIRStmt(st); VG_(printf)("\n"); } else { shouldInterpret = 0; } addStmtToIRSB( sbOut, st ); break; case Ist_WrTmp: // Add a call to trace_load() if --trace-mem=yes. { if(shouldInterpret) { IRExpr* data = st->Ist.WrTmp.data; if (data->tag == Iex_Load) { addEvent_Dr( sbOut, data->Iex.Load.addr, sizeofIRType(data->Iex.Load.ty) ); } } addStmtToIRSB( sbOut, st ); break; } case Ist_Store: { if(shouldInterpret) { IRExpr* data = st->Ist.Store.data; addEvent_Dw( sbOut, st->Ist.Store.addr, sizeofIRType(typeOfIRExpr(tyenv, data)) ); } addStmtToIRSB( sbOut, st ); break; } case Ist_Dirty: { if(shouldInterpret) { Int dsize; IRDirty* d = st->Ist.Dirty.details; if (d->mFx != Ifx_None) { // This dirty helper accesses memory. Collect the details. tl_assert(d->mAddr != NULL); tl_assert(d->mSize != 0); dsize = d->mSize; if (d->mFx == Ifx_Read || d->mFx == Ifx_Modify) addEvent_Dr( sbOut, d->mAddr, dsize ); if (d->mFx == Ifx_Write || d->mFx == Ifx_Modify) addEvent_Dw( sbOut, d->mAddr, dsize ); } else { tl_assert(d->mAddr == NULL); tl_assert(d->mSize == 0); } } addStmtToIRSB( sbOut, st ); break; } case Ist_CAS: { if(shouldInterpret) { Int dataSize; IRType dataTy; IRCAS* cas = st->Ist.CAS.details; tl_assert(cas->addr != NULL); tl_assert(cas->dataLo != NULL); dataTy = typeOfIRExpr(tyenv, cas->dataLo); dataSize = sizeofIRType(dataTy); if (cas->dataHi != NULL) dataSize *= 2; /* since it's a doubleword-CAS */ addEvent_Dr( sbOut, cas->addr, dataSize ); addEvent_Dw( sbOut, cas->addr, dataSize ); } addStmtToIRSB( sbOut, st ); break; } case Ist_LLSC: { if(shouldInterpret) { IRType dataTy; if (st->Ist.LLSC.storedata == NULL) { /* LL */ dataTy = typeOfIRTemp(tyenv, st->Ist.LLSC.result); addEvent_Dr( sbOut, st->Ist.LLSC.addr, sizeofIRType(dataTy) ); } else { /* SC */ dataTy = typeOfIRExpr(tyenv, st->Ist.LLSC.storedata); addEvent_Dw( sbOut, st->Ist.LLSC.addr, sizeofIRType(dataTy) ); } } addStmtToIRSB( sbOut, st ); break; } case Ist_Exit: if(shouldInterpret) { } flushEvents(sbOut); addStmtToIRSB( sbOut, st ); // Original statement break; default: tl_assert(0); } } /* At the end of the sbIn. Flush outstandings. */ flushEvents(sbOut); return sbOut; }
static Bool handleOneStatement(IRTypeEnv* tyenv, IRBB* bbOut, IRStmt* st, IRStmt* st2, Addr* instrAddr, UInt* instrLen, IRExpr** loadAddrExpr, IRExpr** storeAddrExpr, UInt* dataSize) { tl_assert(isFlatIRStmt(st)); switch (st->tag) { case Ist_NoOp: case Ist_AbiHint: case Ist_Put: case Ist_PutI: case Ist_MFence: break; case Ist_Exit: { // This is a conditional jump. Most of the time, we want to add the // instrumentation before it, to ensure it gets executed. Eg, (1) if // this conditional jump is just before an IMark: // // t108 = Not1(t107) // [add instrumentation here] // if (t108) goto {Boring} 0x3A96637D:I32 // ------ IMark(0x3A966370, 7) ------ // // or (2) if this conditional jump is the last thing before the // block-ending unconditional jump: // // t111 = Not1(t110) // [add instrumentation here] // if (t111) goto {Boring} 0x3A96637D:I32 // goto {Boring} 0x3A966370:I32 // // One case (3) where we want the instrumentation after the conditional // jump is when the conditional jump is for an x86 REP instruction: // // ------ IMark(0x3A967F13, 2) ------ // t1 = GET:I32(4) // t6 = CmpEQ32(t1,0x0:I32) // if (t6) goto {Boring} 0x3A967F15:I32 # ignore this cond jmp // t7 = Sub32(t1,0x1:I32) // PUT(4) = t7 // ... // t56 = Not1(t55) // [add instrumentation here] // if (t56) goto {Boring} 0x3A967F15:I32 // // Therefore, we return true if the next statement is an IMark, or if // there is no next statement (which matches case (2), as the final // unconditional jump is not represented in the IRStmt list). // // Note that this approach won't do in the long run for supporting // PPC, but it's good enough for x86/AMD64 for the 3.0.X series. if (NULL == st2 || Ist_IMark == st2->tag) return True; else return False; } case Ist_IMark: /* st->Ist.IMark.addr is a 64-bit int. ULong_to_Ptr casts this to the host's native pointer type; if that is 32 bits then it discards the upper 32 bits. If we are cachegrinding on a 32-bit host then we are also ensured that the guest word size is 32 bits, due to the assertion in cg_instrument that the host and guest word sizes must be the same. Hence st->Ist.IMark.addr will have been derived from a 32-bit guest code address and truncation of it is safe. I believe this assignment should be correct for both 32- and 64-bit machines. */ *instrAddr = (Addr)ULong_to_Ptr(st->Ist.IMark.addr); *instrLen = st->Ist.IMark.len; break; case Ist_Tmp: { IRExpr* data = st->Ist.Tmp.data; if (data->tag == Iex_Load) { IRExpr* aexpr = data->Iex.Load.addr; tl_assert( isIRAtom(aexpr) ); // Note also, endianness info is ignored. I guess that's not // interesting. // XXX: repe cmpsb does two loads... the first one is ignored here! //tl_assert( NULL == *loadAddrExpr ); // XXX: ??? *loadAddrExpr = aexpr; *dataSize = sizeofIRType(data->Iex.Load.ty); } break; } case Ist_Store: { IRExpr* data = st->Ist.Store.data; IRExpr* aexpr = st->Ist.Store.addr; tl_assert( isIRAtom(aexpr) ); tl_assert( NULL == *storeAddrExpr ); // XXX: ??? *storeAddrExpr = aexpr; *dataSize = sizeofIRType(typeOfIRExpr(tyenv, data)); break; } case Ist_Dirty: { IRDirty* d = st->Ist.Dirty.details; if (d->mFx != Ifx_None) { /* This dirty helper accesses memory. Collect the details. */ tl_assert(d->mAddr != NULL); tl_assert(d->mSize != 0); *dataSize = d->mSize; if (d->mFx == Ifx_Read || d->mFx == Ifx_Modify) *loadAddrExpr = d->mAddr; if (d->mFx == Ifx_Write || d->mFx == Ifx_Modify) *storeAddrExpr = d->mAddr; } else { tl_assert(d->mAddr == NULL); tl_assert(d->mSize == 0); } break; } default: VG_(printf)("\n"); ppIRStmt(st); VG_(printf)("\n"); VG_(tool_panic)("Cachegrind: unhandled IRStmt"); } return False; }
static IRSB* sh_instrument ( VgCallbackClosure* closure, IRSB* sbIn, VexGuestLayout* layout, VexGuestExtents* vge, IRType gWordTy, IRType hWordTy ) { IRDirty* di; Int i; IRSB* sbOut; IRType type; IRTypeEnv* tyenv = sbIn->tyenv; IRStmt* imarkst; char *fnname = global_fnname; if (gWordTy != hWordTy) { /* We don't currently support this case. */ VG_(tool_panic)("host/guest word size mismatch"); } /* Set up SB */ sbOut = deepCopyIRSBExceptStmts(sbIn); // Copy verbatim any IR preamble preceding the first IMark i = 0; while (i < sbIn->stmts_used && sbIn->stmts[i]->tag != Ist_IMark) { addStmtToIRSB( sbOut, sbIn->stmts[i] ); i++; } if (clo_trace_mem) { events_used = 0; } for (/*use current i*/; i < sbIn->stmts_used; i++) { IRStmt* st = sbIn->stmts[i]; if (!st || st->tag == Ist_NoOp) continue; /* Prettyprint All IR statements Starting from main that valgrind has generated */ /*if(clo_trace_mem){ ppIRStmt(st); VG_(printf)("\n"); } */ switch (st->tag) { case Ist_NoOp: case Ist_AbiHint: case Ist_PutI: case Ist_MBE: break; case Ist_Put: if(clo_trace_mem){ Int reg_no = st->Ist.Put.offset; if(reg_no == layout->offset_SP || reg_no == 20){ IRExpr* data = st->Ist.Put.data; if(data->tag == Iex_RdTmp){ /* Add registerwrite instrumentation to IRSBout */ addEvent_RegW( sbOut, fnname, reg_no, data); } } } addStmtToIRSB( sbOut, st ); break; case Ist_IMark: imarkst = st; if (VG_(get_fnname_if_entry)( st->Ist.IMark.addr, fnname, 100)){ //VG_(printf)("-- %s --\n",fnname); if(0 == VG_(strcmp)(fnname, "main")) { di = unsafeIRDirty_0_N( 0, "trace_debug", VG_(fnptr_to_fnentry)(trace_debug), mkIRExprVec_0() ); addStmtToIRSB( sbOut, IRStmt_Dirty(di) ); //VG_(printf)("SP:%d\n",layout->offset_SP); clo_trace_mem = True; } if(clo_trace_mem){ addEvent_FnEntry(sbOut, fnname); } } if (clo_trace_mem) { // WARNING: do not remove this function call, even if you // aren't interested in instruction reads. See the comment // above the function itself for more detail. addEvent_Ir( sbOut, mkIRExpr_HWord( (HWord)st->Ist.IMark.addr ), st->Ist.IMark.len ); } addStmtToIRSB( sbOut, st ); break; case Ist_WrTmp: // Add a call to trace_load() if --trace-mem=yes. if (clo_trace_mem) { IRExpr* data = st->Ist.WrTmp.data; if (data->tag == Iex_Load) { addEvent_Dr( sbOut, fnname, data->Iex.Load.addr, sizeofIRType(data->Iex.Load.ty) ); } } addStmtToIRSB( sbOut, st ); break; case Ist_Store: if (clo_trace_mem) { IRExpr* data = st->Ist.Store.data; addEvent_Dw( sbOut, fnname, st->Ist.Store.addr, sizeofIRType(typeOfIRExpr(tyenv, data)) ); } addStmtToIRSB( sbOut, st ); break; case Ist_Dirty: { if (clo_trace_mem) { Int dsize; IRDirty* d = st->Ist.Dirty.details; if (d->mFx != Ifx_None) { // This dirty helper accesses memory. Collect the details. tl_assert(d->mAddr != NULL); tl_assert(d->mSize != 0); dsize = d->mSize; if (d->mFx == Ifx_Read || d->mFx == Ifx_Modify) addEvent_Dr( sbOut, fnname, d->mAddr, dsize ); if (d->mFx == Ifx_Write || d->mFx == Ifx_Modify) addEvent_Dw( sbOut, fnname, d->mAddr, dsize ); } else { tl_assert(d->mAddr == NULL); tl_assert(d->mSize == 0); } } addStmtToIRSB( sbOut, st ); break; } case Ist_CAS: { /* We treat it as a read and a write of the location. I think that is the same behaviour as it was before IRCAS was introduced, since prior to that point, the Vex front ends would translate a lock-prefixed instruction into a (normal) read followed by a (normal) write. */ Int dataSize; IRType dataTy; IRCAS* cas = st->Ist.CAS.details; tl_assert(cas->addr != NULL); tl_assert(cas->dataLo != NULL); dataTy = typeOfIRExpr(tyenv, cas->dataLo); dataSize = sizeofIRType(dataTy); if (clo_trace_mem) { addEvent_Dr( sbOut, fnname, cas->addr, dataSize ); addEvent_Dw( sbOut, fnname, cas->addr, dataSize ); } addStmtToIRSB( sbOut, st ); break; } case Ist_LLSC: { IRType dataTy; if (st->Ist.LLSC.storedata == NULL) { /* LL */ dataTy = typeOfIRTemp(tyenv, st->Ist.LLSC.result); if (clo_trace_mem) addEvent_Dr( sbOut, fnname, st->Ist.LLSC.addr, sizeofIRType(dataTy) ); } else { /* SC */ dataTy = typeOfIRExpr(tyenv, st->Ist.LLSC.storedata); if (clo_trace_mem) addEvent_Dw( sbOut, fnname, st->Ist.LLSC.addr, sizeofIRType(dataTy) ); } addStmtToIRSB( sbOut, st ); break; } case Ist_Exit: if (clo_trace_mem) { flushEvents(sbOut); } addStmtToIRSB( sbOut, st ); // Original statement break; default: tl_assert(0); } } if(clo_trace_mem){ if(sbIn->jumpkind == Ijk_Ret){ VG_(get_fnname)(imarkst->Ist.IMark.addr, fnname, 100); addEvent_FnExit(sbOut,fnname); } } if (clo_trace_mem) { /* At the end of the sbIn. Flush outstandings. */ flushEvents(sbOut); } return sbOut; }
static IRSB* fr_instrument(VgCallbackClosure* closure, IRSB* sbIn, VexGuestLayout* layout, VexGuestExtents* vge, IRType gWordTy, IRType hWordTy) { Int i; IRSB* sbOut; IRTypeEnv* tyenv = sbIn->tyenv; IRDirty* di; IRType dataTy; IRExpr** argv; IRCAS* cas; // We don't care about mmaps if (!clo_mmap) return sbIn; // From lackey tool tl_assert(gWordTy == hWordTy); sbOut = deepCopyIRSBExceptStmts(sbIn); // Copy verbatim any IR preamble preceding the first IMark i = 0; while (i < sbIn->stmts_used && sbIn->stmts[i]->tag != Ist_IMark) { addStmtToIRSB( sbOut, sbIn->stmts[i] ); i++; } for (/*use current i*/; i < sbIn->stmts_used; i++) { IRStmt* st = sbIn->stmts[i]; if (!st || st->tag == Ist_NoOp) continue; switch (st->tag) { case Ist_NoOp: // Make compiler happy case Ist_AbiHint: case Ist_Put: case Ist_PutI: case Ist_MBE: case Ist_IMark: case Ist_WrTmp: case Ist_Exit: addStmtToIRSB( sbOut, st ); break; case Ist_Store: dataTy = typeOfIRExpr( tyenv, st->Ist.Store.data ); argv = mkIRExprVec_2( st->Ist.Store.addr, mkIRExpr_HWord( sizeofIRType( dataTy ) ) ); di = unsafeIRDirty_0_N(/*regparms*/2, "trace_store", VG_(fnptr_to_fnentry)( trace_store ), argv); addStmtToIRSB( sbOut, IRStmt_Dirty(di) ); addStmtToIRSB( sbOut, st ); break; case Ist_LLSC: if (st->Ist.LLSC.storedata != NULL) { dataTy = typeOfIRExpr( tyenv, st->Ist.LLSC.storedata ); argv = mkIRExprVec_2( st->Ist.LLSC.addr, mkIRExpr_HWord( sizeofIRType( dataTy ) ) ); di = unsafeIRDirty_0_N(/*regparms*/2, "trace_store", VG_(fnptr_to_fnentry)( trace_store ), argv); addStmtToIRSB( sbOut, IRStmt_Dirty(di) ); addStmtToIRSB( sbOut, st ); } break; case Ist_Dirty: di = st->Ist.Dirty.details; if (di->mFx != Ifx_None) { // This dirty helper accesses memory. Collect the details. tl_assert(di->mAddr != NULL); tl_assert(di->mSize != 0); if (di->mFx == Ifx_Write || di->mFx == Ifx_Modify) { argv = mkIRExprVec_2( di->mAddr, mkIRExpr_HWord( di->mSize ) ); di = unsafeIRDirty_0_N( /*regparms*/2, "trace_store", VG_(fnptr_to_fnentry)( trace_store ), argv ); addStmtToIRSB( sbOut, IRStmt_Dirty(di) ); } } else { tl_assert(di->mAddr == NULL); tl_assert(di->mSize == 0); } addStmtToIRSB( sbOut, st ); break; case Ist_CAS: cas = st->Ist.CAS.details; tl_assert(cas->addr != NULL); tl_assert(cas->dataLo != NULL); argv = mkIRExprVec_2( cas->addr, mkIRExpr_HWord( sizeofIRType(typeOfIRExpr(tyenv, cas->dataLo)) * (cas->dataHi != NULL ? 2 : 1) ) ); di = unsafeIRDirty_0_N( /*regparms*/2, "trace_store", VG_(fnptr_to_fnentry)( trace_store ), argv ); addStmtToIRSB( sbOut, IRStmt_Dirty(di) ); addStmtToIRSB( sbOut, st ); break; } } return sbOut; }
IRSB* DRD_(instrument)(VgCallbackClosure* const closure, IRSB* const bb_in, VexGuestLayout* const layout, VexGuestExtents* const vge, IRType const gWordTy, IRType const hWordTy) { IRDirty* di; Int i; IRSB* bb; IRExpr** argv; Bool instrument = True; /* Set up BB */ bb = emptyIRSB(); bb->tyenv = deepCopyIRTypeEnv(bb_in->tyenv); bb->next = deepCopyIRExpr(bb_in->next); bb->jumpkind = bb_in->jumpkind; for (i = 0; i < bb_in->stmts_used; i++) { IRStmt* const st = bb_in->stmts[i]; tl_assert(st); if (st->tag == Ist_NoOp) continue; switch (st->tag) { /* Note: the code for not instrumenting the code in .plt */ /* sections is only necessary on CentOS 3.0 x86 (kernel 2.4.21 */ /* + glibc 2.3.2 + NPTL 0.60 + binutils 2.14.90.0.4). */ /* This is because on this platform dynamic library symbols are */ /* relocated in another way than by later binutils versions. The */ /* linker e.g. does not generate .got.plt sections on CentOS 3.0. */ case Ist_IMark: instrument = VG_(seginfo_sect_kind)(NULL, 0, st->Ist.IMark.addr) != Vg_SectPLT; addStmtToIRSB(bb, st); break; case Ist_MBE: switch (st->Ist.MBE.event) { case Imbe_Fence: break; /* not interesting */ default: tl_assert(0); } addStmtToIRSB(bb, st); break; case Ist_Store: if (instrument && /* ignore stores resulting from st{d,w}cx. */ st->Ist.Store.resSC == IRTemp_INVALID) { instrument_store(bb, st->Ist.Store.addr, sizeofIRType(typeOfIRExpr(bb->tyenv, st->Ist.Store.data))); } addStmtToIRSB(bb, st); break; case Ist_WrTmp: if (instrument) { const IRExpr* const data = st->Ist.WrTmp.data; if (data->tag == Iex_Load) { instrument_load(bb, data->Iex.Load.addr, sizeofIRType(data->Iex.Load.ty)); } } addStmtToIRSB(bb, st); break; case Ist_Dirty: if (instrument) { IRDirty* d = st->Ist.Dirty.details; IREffect const mFx = d->mFx; switch (mFx) { case Ifx_None: break; case Ifx_Read: case Ifx_Write: case Ifx_Modify: tl_assert(d->mAddr); tl_assert(d->mSize > 0); argv = mkIRExprVec_2(d->mAddr, mkIRExpr_HWord(d->mSize)); if (mFx == Ifx_Read || mFx == Ifx_Modify) { di = unsafeIRDirty_0_N( /*regparms*/2, "drd_trace_load", VG_(fnptr_to_fnentry)(DRD_(trace_load)), argv); addStmtToIRSB(bb, IRStmt_Dirty(di)); } if (mFx == Ifx_Write || mFx == Ifx_Modify) { di = unsafeIRDirty_0_N( /*regparms*/2, "drd_trace_store", VG_(fnptr_to_fnentry)(DRD_(trace_store)), argv); addStmtToIRSB(bb, IRStmt_Dirty(di)); } break; default: tl_assert(0); } } addStmtToIRSB(bb, st); break; case Ist_CAS: if (instrument) { /* * Treat compare-and-swap as a read. By handling atomic * instructions as read instructions no data races are reported * between conflicting atomic operations nor between atomic * operations and non-atomic reads. Conflicts between atomic * operations and non-atomic write operations are still reported * however. */ Int dataSize; IRCAS* cas = st->Ist.CAS.details; tl_assert(cas->addr != NULL); tl_assert(cas->dataLo != NULL); dataSize = sizeofIRType(typeOfIRExpr(bb->tyenv, cas->dataLo)); if (cas->dataHi != NULL) dataSize *= 2; /* since it's a doubleword-CAS */ instrument_load(bb, cas->addr, dataSize); } addStmtToIRSB(bb, st); break; default: addStmtToIRSB(bb, st); break; } } return bb; }
/* 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); /* HACK, compiler warning ‘opnd2’ may be used uninitialized */ opnd2 = opnd1; /* immediate_index = 0 immediate value is not used. * immediate_index = 2 opnd2 is an immediate value. */ vassert(iricb.immediate_index == 0 || iricb.immediate_index == 2); if (iricb.immediate_index == 2) { vassert((iricb.t_opnd2 == Ity_I8) || (iricb.t_opnd2 == Ity_I16) || (iricb.t_opnd2 == Ity_I32)); /* Interpret the memory as an ULong. */ if (iricb.immediate_type == Ity_I8) { opnd2 = mkU8(*((ULong *)iricb.opnd2)); } else if (iricb.immediate_type == Ity_I16) { opnd2 = mkU16(*((ULong *)iricb.opnd2)); } else if (iricb.immediate_type == Ity_I32) { opnd2 = mkU32(*((ULong *)iricb.opnd2)); } } else { 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); /* HACK, compiler warning ‘opnd3’ may be used uninitialized */ opnd3 = opnd2; /* immediate_index = 0 immediate value is not used. * immediate_index = 3 opnd3 is an immediate value. */ vassert(iricb.immediate_index == 0 || iricb.immediate_index == 3); if (iricb.immediate_index == 3) { vassert((iricb.t_opnd3 == Ity_I8) || (iricb.t_opnd3 == Ity_I16) || (iricb.t_opnd2 == Ity_I32)); if (iricb.immediate_type == Ity_I8) { opnd3 = mkU8(*((ULong *)iricb.opnd3)); } else if (iricb.immediate_type == Ity_I16) { opnd3 = mkU16(*((ULong *)iricb.opnd3)); } else if (iricb.immediate_type == Ity_I32) { opnd3 = mkU32(*((ULong *)iricb.opnd3)); } } else { 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); /* HACK, compiler warning ‘opnd4’ may be used uninitialized */ opnd4 = opnd3; /* immediate_index = 0 immediate value is not used. * immediate_index = 4 opnd4 is an immediate value. */ vassert(iricb.immediate_index == 0 || iricb.immediate_index == 4); if (iricb.immediate_index == 4) { vassert((iricb.t_opnd3 == Ity_I8) || (iricb.t_opnd3 == Ity_I16) || (iricb.t_opnd2 == Ity_I32)); if (iricb.immediate_type == Ity_I8) { opnd4 = mkU8(*((ULong *)iricb.opnd4)); } else if (iricb.immediate_type == Ity_I16) { opnd4 = mkU16(*((ULong *)iricb.opnd4)); } else if (iricb.immediate_type == Ity_I32) { opnd4 = mkU32(*((ULong *)iricb.opnd4)); } } else { 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 (iricb.t_result == Ity_I1 || 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("\nEND inject\n"); } }
/* This is copied mostly verbatim from lackey */ static IRSB* mv_instrument ( VgCallbackClosure* closure, IRSB* sbIn, VexGuestLayout* layout, VexGuestExtents* vge, VexArchInfo* archinfo_host, IRType gWordTy, IRType hWordTy ) { Int i; IRSB* sbOut; IRTypeEnv* tyenv = sbIn->tyenv; if (gWordTy != hWordTy) { /* We don't currently support this case. */ VG_(tool_panic)("host/guest word size mismatch"); } //ppIRSB(sbIn); /* Set up SB */ sbOut = deepCopyIRSBExceptStmts(sbIn); // Copy verbatim any IR preamble preceding the first IMark i = 0; while (i < sbIn->stmts_used && sbIn->stmts[i]->tag != Ist_IMark) { addStmtToIRSB( sbOut, sbIn->stmts[i] ); i++; } events_used = 0; for (/*use current i*/; i < sbIn->stmts_used; i++) { IRStmt* st = sbIn->stmts[i]; if (!st || st->tag == Ist_NoOp) continue; switch (st->tag) { case Ist_NoOp: case Ist_AbiHint: case Ist_Put: case Ist_PutI: case Ist_MBE: addStmtToIRSB( sbOut, st ); break; case Ist_IMark: canCreateModify = False; if (clo_trace_instrs) { addEvent_Ir( sbOut, mkIRExpr_HWord( (HWord)st->Ist.IMark.addr ), st->Ist.IMark.len ); } addStmtToIRSB( sbOut, st ); break; case Ist_WrTmp: { IRExpr* data = st->Ist.WrTmp.data; if (data->tag == Iex_Load) { addEvent_Dr( sbOut, data->Iex.Load.addr, sizeofIRType(data->Iex.Load.ty), IRTypeToMVType(data->Iex.Load.ty) ); } } addStmtToIRSB( sbOut, st ); break; case Ist_Store: { IRExpr* data = st->Ist.Store.data; addEvent_Dw( sbOut, st->Ist.Store.addr, sizeofIRType(typeOfIRExpr(tyenv, data)), IRTypeToMVType(typeOfIRExpr(tyenv, data)) ); } addStmtToIRSB( sbOut, st ); break; case Ist_Dirty: { Int dsize; IRDirty* d = st->Ist.Dirty.details; if (d->mFx != Ifx_None) { // This dirty helper accesses memory. Collect the details. tl_assert(d->mAddr != NULL); tl_assert(d->mSize != 0); dsize = d->mSize; if (d->mFx == Ifx_Read || d->mFx == Ifx_Modify) addEvent_Dr( sbOut, d->mAddr, dsize, MV_DataInt32 ); if (d->mFx == Ifx_Write || d->mFx == Ifx_Modify) addEvent_Dw( sbOut, d->mAddr, dsize, MV_DataInt32 ); } else { tl_assert(d->mAddr == NULL); tl_assert(d->mSize == 0); } addStmtToIRSB( sbOut, st ); break; } case Ist_CAS: { /* We treat it as a read and a write of the location. I think that is the same behaviour as it was before IRCAS was introduced, since prior to that point, the Vex front ends would translate a lock-prefixed instruction into a (normal) read followed by a (normal) write. */ Int dataSize; IRType dataTy; IRCAS* cas = st->Ist.CAS.details; tl_assert(cas->addr != NULL); tl_assert(cas->dataLo != NULL); dataTy = typeOfIRExpr(tyenv, cas->dataLo); dataSize = sizeofIRType(dataTy); if (cas->dataHi != NULL) dataSize *= 2; /* since it's a doubleword-CAS */ addEvent_Dr( sbOut, cas->addr, dataSize, IRTypeToMVType(dataTy) ); addEvent_Dw( sbOut, cas->addr, dataSize, IRTypeToMVType(dataTy) ); addStmtToIRSB( sbOut, st ); break; } case Ist_LLSC: { IRType dataTy; if (st->Ist.LLSC.storedata == NULL) { /* LL */ dataTy = typeOfIRTemp(tyenv, st->Ist.LLSC.result); addEvent_Dr( sbOut, st->Ist.LLSC.addr, sizeofIRType(dataTy), IRTypeToMVType(dataTy) ); } else { /* SC */ dataTy = typeOfIRExpr(tyenv, st->Ist.LLSC.storedata); addEvent_Dw( sbOut, st->Ist.LLSC.addr, sizeofIRType(dataTy), IRTypeToMVType(dataTy) ); } addStmtToIRSB( sbOut, st ); break; } case Ist_Exit: flushEvents(sbOut); addStmtToIRSB( sbOut, st ); // Original statement break; default: tl_assert(0); } } /* At the end of the sbIn. Flush outstandings. */ flushEvents(sbOut); return sbOut; }
static IRSB* drd_instrument(VgCallbackClosure* const closure, IRSB* const bb_in, VexGuestLayout* const layout, VexGuestExtents* const vge, IRType const gWordTy, IRType const hWordTy) { IRDirty* di; Int i; IRSB* bb; IRExpr** argv; Bool instrument = True; Bool bus_locked = False; /* Set up BB */ bb = emptyIRSB(); bb->tyenv = deepCopyIRTypeEnv(bb_in->tyenv); bb->next = deepCopyIRExpr(bb_in->next); bb->jumpkind = bb_in->jumpkind; for (i = 0; i < bb_in->stmts_used; i++) { IRStmt* const st = bb_in->stmts[i]; tl_assert(st); if (st->tag == Ist_NoOp) continue; switch (st->tag) { case Ist_MBE: switch (st->Ist.MBE.event) { case Imbe_Fence: break; /* not interesting */ case Imbe_BusLock: tl_assert(! bus_locked); bus_locked = True; break; case Imbe_BusUnlock: tl_assert(bus_locked); bus_locked = False; break; default: tl_assert(0); } addStmtToIRSB(bb, st); break; case Ist_Store: if (instrument && ! bus_locked) { instrument_store(bb, st->Ist.Store.addr, sizeofIRType(typeOfIRExpr(bb->tyenv, st->Ist.Store.data))); } addStmtToIRSB(bb, st); break; case Ist_WrTmp: if (instrument) { const IRExpr* const data = st->Ist.WrTmp.data; if (data->tag == Iex_Load) { instrument_load(bb, data->Iex.Load.addr, sizeofIRType(data->Iex.Load.ty)); } } addStmtToIRSB(bb, st); break; case Ist_Dirty: if (instrument) { IRDirty* d = st->Ist.Dirty.details; IREffect const mFx = d->mFx; switch (mFx) { case Ifx_None: break; case Ifx_Read: case Ifx_Write: case Ifx_Modify: tl_assert(d->mAddr); tl_assert(d->mSize > 0); argv = mkIRExprVec_2(d->mAddr, mkIRExpr_HWord(d->mSize)); if (mFx == Ifx_Read || mFx == Ifx_Modify) { di = unsafeIRDirty_0_N( /*regparms*/2, "drd_trace_load", VG_(fnptr_to_fnentry)(drd_trace_load), argv); addStmtToIRSB(bb, IRStmt_Dirty(di)); } if ((mFx == Ifx_Write || mFx == Ifx_Modify) && ! bus_locked) { di = unsafeIRDirty_0_N( /*regparms*/2, "drd_trace_store", VG_(fnptr_to_fnentry)(drd_trace_store), argv); addStmtToIRSB(bb, IRStmt_Dirty(di)); } break; default: tl_assert(0); } } addStmtToIRSB(bb, st); break; default: addStmtToIRSB(bb, st); break; } } tl_assert(! bus_locked); return bb; }
static IRBB* cg_instrument ( IRBB* bbIn, VexGuestLayout* layout, Addr64 orig_addr_noredir, VexGuestExtents* vge, IRType gWordTy, IRType hWordTy ) { Int i, isize; IRStmt* st; Addr64 cia; /* address of current insn */ CgState cgs; IRTypeEnv* tyenv = bbIn->tyenv; InstrInfo* curr_inode = NULL; if (gWordTy != hWordTy) { /* We don't currently support this case. */ VG_(tool_panic)("host/guest word size mismatch"); } /* Set up BB, including copying of the where-next stuff. */ cgs.bbOut = emptyIRBB(); cgs.bbOut->tyenv = dopyIRTypeEnv(tyenv); tl_assert( isIRAtom(bbIn->next) ); cgs.bbOut->next = dopyIRExpr(bbIn->next); cgs.bbOut->jumpkind = bbIn->jumpkind; // Copy verbatim any IR preamble preceding the first IMark i = 0; while (i < bbIn->stmts_used && bbIn->stmts[i]->tag != Ist_IMark) { addStmtToIRBB( cgs.bbOut, bbIn->stmts[i] ); i++; } // Get the first statement, and initial cia from it tl_assert(bbIn->stmts_used > 0); tl_assert(i < bbIn->stmts_used); st = bbIn->stmts[i]; tl_assert(Ist_IMark == st->tag); cia = st->Ist.IMark.addr; // Set up running state and get block info cgs.events_used = 0; cgs.bbInfo = get_BB_info(bbIn, (Addr)orig_addr_noredir); cgs.bbInfo_i = 0; if (DEBUG_CG) VG_(printf)("\n\n---------- cg_instrument ----------\n"); // Traverse the block, initialising inodes, adding events and flushing as // necessary. for (/*use current i*/; i < bbIn->stmts_used; i++) { st = bbIn->stmts[i]; tl_assert(isFlatIRStmt(st)); switch (st->tag) { case Ist_NoOp: case Ist_AbiHint: case Ist_Put: case Ist_PutI: case Ist_MFence: break; case Ist_IMark: cia = st->Ist.IMark.addr; isize = st->Ist.IMark.len; // If Vex fails to decode an instruction, the size will be zero. // Pretend otherwise. if (isize == 0) isize = VG_MIN_INSTR_SZB; // Sanity-check size. tl_assert( (VG_MIN_INSTR_SZB <= isize && isize <= VG_MAX_INSTR_SZB) || VG_CLREQ_SZB == isize ); // Get space for and init the inode, record it as the current one. // Subsequent Dr/Dw/Dm events from the same instruction will // also use it. curr_inode = setup_InstrInfo(&cgs, cia, isize); addEvent_Ir( &cgs, curr_inode ); break; case Ist_Tmp: { IRExpr* data = st->Ist.Tmp.data; if (data->tag == Iex_Load) { IRExpr* aexpr = data->Iex.Load.addr; // Note also, endianness info is ignored. I guess // that's not interesting. addEvent_Dr( &cgs, curr_inode, sizeofIRType(data->Iex.Load.ty), aexpr ); } break; } case Ist_Store: { IRExpr* data = st->Ist.Store.data; IRExpr* aexpr = st->Ist.Store.addr; addEvent_Dw( &cgs, curr_inode, sizeofIRType(typeOfIRExpr(tyenv, data)), aexpr ); break; } case Ist_Dirty: { Int dataSize; IRDirty* d = st->Ist.Dirty.details; if (d->mFx != Ifx_None) { /* This dirty helper accesses memory. Collect the details. */ tl_assert(d->mAddr != NULL); tl_assert(d->mSize != 0); dataSize = d->mSize; // Large (eg. 28B, 108B, 512B on x86) data-sized // instructions will be done inaccurately, but they're // very rare and this avoids errors from hitting more // than two cache lines in the simulation. if (dataSize > MIN_LINE_SIZE) dataSize = MIN_LINE_SIZE; if (d->mFx == Ifx_Read || d->mFx == Ifx_Modify) addEvent_Dr( &cgs, curr_inode, dataSize, d->mAddr ); if (d->mFx == Ifx_Write || d->mFx == Ifx_Modify) addEvent_Dw( &cgs, curr_inode, dataSize, d->mAddr ); } else { tl_assert(d->mAddr == NULL); tl_assert(d->mSize == 0); } break; } case Ist_Exit: /* We may never reach the next statement, so need to flush all outstanding transactions now. */ flushEvents( &cgs ); break; default: tl_assert(0); break; } /* Copy the original statement */ addStmtToIRBB( cgs.bbOut, st ); if (DEBUG_CG) { ppIRStmt(st); VG_(printf)("\n"); } } /* At the end of the bb. Flush outstandings. */ flushEvents( &cgs ); /* done. stay sane ... */ tl_assert(cgs.bbInfo_i == cgs.bbInfo->n_instrs); if (DEBUG_CG) { VG_(printf)( "goto {"); ppIRJumpKind(bbIn->jumpkind); VG_(printf)( "} "); ppIRExpr( bbIn->next ); VG_(printf)( "}\n"); } return cgs.bbOut; }
static IRSB* drd_instrument(VgCallbackClosure* const closure, IRSB* const bb_in, VexGuestLayout* const layout, VexGuestExtents* const vge, IRType const gWordTy, IRType const hWordTy) { IRDirty* di; Int i; IRSB* bb; IRExpr** argv; Bool instrument = True; Bool bus_locked = False; /* Set up BB */ bb = emptyIRSB(); bb->tyenv = deepCopyIRTypeEnv(bb_in->tyenv); bb->next = deepCopyIRExpr(bb_in->next); bb->jumpkind = bb_in->jumpkind; for (i = 0; i < bb_in->stmts_used; i++) { IRStmt* const st = bb_in->stmts[i]; tl_assert(st); if (st->tag == Ist_NoOp) continue; switch (st->tag) { /* Note: the code for not instrumenting the code in .plt */ /* sections is only necessary on CentOS 3.0 x86 (kernel 2.4.21 */ /* + glibc 2.3.2 + NPTL 0.60 + binutils 2.14.90.0.4). */ /* This is because on this platform dynamic library symbols are */ /* relocated in another way than by later binutils versions. The */ /* linker e.g. does not generate .got.plt sections on CentOS 3.0. */ case Ist_IMark: instrument = VG_(seginfo_sect_kind)(NULL, 0, st->Ist.IMark.addr) != Vg_SectPLT; addStmtToIRSB(bb, st); break; case Ist_MBE: switch (st->Ist.MBE.event) { case Imbe_Fence: break; /* not interesting */ case Imbe_BusLock: case Imbe_SnoopedStoreBegin: tl_assert(! bus_locked); bus_locked = True; break; case Imbe_BusUnlock: case Imbe_SnoopedStoreEnd: tl_assert(bus_locked); bus_locked = False; break; default: tl_assert(0); } addStmtToIRSB(bb, st); break; case Ist_Store: if (instrument && ! bus_locked) { instrument_store(bb, st->Ist.Store.addr, sizeofIRType(typeOfIRExpr(bb->tyenv, st->Ist.Store.data))); } addStmtToIRSB(bb, st); break; case Ist_WrTmp: if (instrument) { const IRExpr* const data = st->Ist.WrTmp.data; if (data->tag == Iex_Load) { instrument_load(bb, data->Iex.Load.addr, sizeofIRType(data->Iex.Load.ty)); } } addStmtToIRSB(bb, st); break; case Ist_Dirty: if (instrument) { IRDirty* d = st->Ist.Dirty.details; IREffect const mFx = d->mFx; switch (mFx) { case Ifx_None: break; case Ifx_Read: case Ifx_Write: case Ifx_Modify: tl_assert(d->mAddr); tl_assert(d->mSize > 0); argv = mkIRExprVec_2(d->mAddr, mkIRExpr_HWord(d->mSize)); if (mFx == Ifx_Read || mFx == Ifx_Modify) { di = unsafeIRDirty_0_N( /*regparms*/2, "drd_trace_load", VG_(fnptr_to_fnentry)(drd_trace_load), argv); addStmtToIRSB(bb, IRStmt_Dirty(di)); } if ((mFx == Ifx_Write || mFx == Ifx_Modify) && ! bus_locked) { di = unsafeIRDirty_0_N( /*regparms*/2, "drd_trace_store", VG_(fnptr_to_fnentry)(drd_trace_store), argv); addStmtToIRSB(bb, IRStmt_Dirty(di)); } break; default: tl_assert(0); } } addStmtToIRSB(bb, st); break; default: addStmtToIRSB(bb, st); break; } } tl_assert(! bus_locked); return bb; }
/* 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); if (iricb.shift_amount_is_immediate) { // This implies that the IROp is a shift op vassert(iricb.t_opnd2 == Ity_I8); opnd2 = mkU8(*((Char *)iricb.opnd2)); } else { 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 (iricb.t_result == Ity_I1 || 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("\nEND inject\n"); } }