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; }
static EventSet* insert_simcall(IRBB* bbOut, InstrInfo* ii, UInt dataSize, Bool instrIssued, IRExpr* loadAddrExpr, IRExpr* storeAddrExpr) { HChar* helperName; void* helperAddr; Int argc; EventSet* es; IRExpr *arg1, *arg2 = 0, *arg3 = 0, **argv; IRDirty* di; /* Check type of original instruction regarding memory access, * and collect info to be able to generate fitting helper call */ if (!loadAddrExpr && !storeAddrExpr) { // no load/store CLG_ASSERT(0 == dataSize); if (instrIssued) { helperName = 0; helperAddr = 0; } else { helperName = CLG_(cachesim).log_1I0D_name; helperAddr = CLG_(cachesim).log_1I0D; } argc = 1; es = CLG_(sets).D0; } else if (loadAddrExpr && !storeAddrExpr) { // load CLG_ASSERT( isIRAtom(loadAddrExpr) ); if (instrIssued) { helperName = CLG_(cachesim).log_0I1Dr_name; helperAddr = CLG_(cachesim).log_0I1Dr; } else { helperName = CLG_(cachesim).log_1I1Dr_name; helperAddr = CLG_(cachesim).log_1I1Dr; } argc = 2; arg2 = loadAddrExpr; es = CLG_(sets).D1r; } else if (!loadAddrExpr && storeAddrExpr) { // store CLG_ASSERT( isIRAtom(storeAddrExpr) ); if (instrIssued) { helperName = CLG_(cachesim).log_0I1Dw_name; helperAddr = CLG_(cachesim).log_0I1Dw; } else { helperName = CLG_(cachesim).log_1I1Dw_name; helperAddr = CLG_(cachesim).log_1I1Dw; } argc = 2; arg2 = storeAddrExpr; es = CLG_(sets).D1w; } else { CLG_ASSERT( loadAddrExpr && storeAddrExpr ); CLG_ASSERT( isIRAtom(loadAddrExpr) ); CLG_ASSERT( isIRAtom(storeAddrExpr) ); if ( loadStoreAddrsMatch(loadAddrExpr, storeAddrExpr) ) { /* modify: suppose write access, as this is * more resource consuming (as in callgrind for VG2) * Cachegrind does a read here (!) * DISCUSS: Best way depends on simulation model? */ if (instrIssued) { helperName = CLG_(cachesim).log_0I1Dw_name; helperAddr = CLG_(cachesim).log_0I1Dw; } else { helperName = CLG_(cachesim).log_1I1Dw_name; helperAddr = CLG_(cachesim).log_1I1Dw; } argc = 2; arg2 = storeAddrExpr; es = CLG_(sets).D1w; } else { // load/store if (instrIssued) { helperName = CLG_(cachesim).log_0I2D_name; helperAddr = CLG_(cachesim).log_0I2D; } else { helperName = CLG_(cachesim).log_1I2D_name; helperAddr = CLG_(cachesim).log_1I2D; } argc = 3; arg2 = loadAddrExpr; arg3 = storeAddrExpr; es = CLG_(sets).D2; } } /* helper could be unset depending on the simulator used */ if (helperAddr == 0) return 0; /* Setup 1st arg: InstrInfo */ arg1 = mkIRExpr_HWord( (HWord)ii ); // Add call to the instrumentation function if (argc == 1) argv = mkIRExprVec_1(arg1); else if (argc == 2) argv = mkIRExprVec_2(arg1, arg2); else if (argc == 3) argv = mkIRExprVec_3(arg1, arg2, arg3); else VG_(tool_panic)("argc... not 1 or 2 or 3?"); di = unsafeIRDirty_0_N( argc, helperName, VG_(fnptr_to_fnentry)( helperAddr ), argv); addStmtToIRBB( bbOut, IRStmt_Dirty(di) ); return es; }
static void flushEvents ( CgState* cgs ) { Int i, regparms; Char* helperName; void* helperAddr; IRExpr** argv; IRExpr* i_node_expr; IRDirty* di; Event* ev; Event* ev2; Event* ev3; i = 0; while (i < cgs->events_used) { helperName = NULL; helperAddr = NULL; argv = NULL; regparms = 0; /* generate IR to notify event i and possibly the ones immediately following it. */ tl_assert(i >= 0 && i < cgs->events_used); ev = &cgs->events[i]; ev2 = ( i < cgs->events_used-1 ? &cgs->events[i+1] : NULL ); ev3 = ( i < cgs->events_used-2 ? &cgs->events[i+2] : NULL ); if (DEBUG_CG) { VG_(printf)(" flush "); showEvent( ev ); } i_node_expr = mkIRExpr_HWord( (HWord)ev->inode ); /* Decide on helper fn to call and args to pass it, and advance i appropriately. */ switch (ev->ekind) { case Event_Ir: /* Merge with a following Dr/Dm if it is from this insn. */ if (ev2 && (ev2->ekind == Event_Dr || ev2->ekind == Event_Dm)) { tl_assert(ev2->inode == ev->inode); helperName = "log_1I_1Dr_cache_access"; helperAddr = &log_1I_1Dr_cache_access; argv = mkIRExprVec_3( i_node_expr, ev2->dataEA, mkIRExpr_HWord( ev2->datasize ) ); regparms = 3; i += 2; } /* Merge with a following Dw if it is from this insn. */ else if (ev2 && ev2->ekind == Event_Dw) { tl_assert(ev2->inode == ev->inode); helperName = "log_1I_1Dw_cache_access"; helperAddr = &log_1I_1Dw_cache_access; argv = mkIRExprVec_3( i_node_expr, ev2->dataEA, mkIRExpr_HWord( ev2->datasize ) ); regparms = 3; i += 2; } /* Merge with two following Irs if possible. */ else if (ev2 && ev3 && ev2->ekind == Event_Ir && ev3->ekind == Event_Ir) { helperName = "log_3I_0D_cache_access"; helperAddr = &log_3I_0D_cache_access; argv = mkIRExprVec_3( i_node_expr, mkIRExpr_HWord( (HWord)ev2->inode ), mkIRExpr_HWord( (HWord)ev3->inode ) ); regparms = 3; i += 3; } /* Merge with a following Ir if possible. */ else if (ev2 && ev2->ekind == Event_Ir) { helperName = "log_2I_0D_cache_access"; helperAddr = &log_2I_0D_cache_access; argv = mkIRExprVec_2( i_node_expr, mkIRExpr_HWord( (HWord)ev2->inode ) ); regparms = 2; i += 2; } /* No merging possible; emit as-is. */ else { // Assertion: this Event_Ir must be the last one in the // events buffer, otherwise it would have been merged with a // following event. tl_assert(!ev2 && !ev3); helperName = "log_1I_0D_cache_access"; helperAddr = &log_1I_0D_cache_access; argv = mkIRExprVec_1( i_node_expr ); regparms = 1; i++; } break; case Event_Dr: case Event_Dm: helperName = "log_0I_1Dr_cache_access"; helperAddr = &log_0I_1Dr_cache_access; argv = mkIRExprVec_3( i_node_expr, ev->dataEA, mkIRExpr_HWord( ev->datasize ) ); regparms = 3; i++; break; case Event_Dw: helperName = "log_0I_1Dw_cache_access"; helperAddr = &log_0I_1Dw_cache_access; argv = mkIRExprVec_3( i_node_expr, ev->dataEA, mkIRExpr_HWord( ev->datasize ) ); regparms = 3; i++; break; default: tl_assert(0); } /* Add the helper. */ tl_assert(helperName); tl_assert(helperAddr); tl_assert(argv); di = unsafeIRDirty_0_N( regparms, helperName, helperAddr, argv); addStmtToIRBB( cgs->bbOut, IRStmt_Dirty(di) ); } cgs->events_used = 0; }
void AddBinopHelper(IRSB* sb,IRStmt* st) { IROp op; IRDirty* d1; IRDirty* d2; IRExpr* arg1; IRExpr* arg2; HWord lhs,tmpname; // HWord cur_ctr = (HWord)counter; vassert(st->tag = Ist_WrTmp); op = (HWord)st->Ist.WrTmp.data->Iex.Binop.op; arg1 = st->Ist.WrTmp.data->Iex.Binop.arg1; arg2 = st->Ist.WrTmp.data->Iex.Binop.arg2; lhs = (HWord)st->Ist.WrTmp.tmp; d1 = unsafeIRDirty_0_N(0, "EmitNewTmpTyvarHelper", &EmitNewTmpTyvarHelper, mkIRExprVec_2( mkIRExpr_HWord(lhs), mkIRExpr_HWord(counter) ) ); setHelperAnns(d1); addStmtToIRSB(sb,IRStmt_Dirty(d1)); if (arg1->tag == Iex_RdTmp && arg2->tag == Iex_RdTmp) { d2 = unsafeIRDirty_0_N(0, "EmitBinopTmpTmpTypeHelper", &EmitBinopTmpTmpTypeHelper, mkIRExprVec_5( mkIRExpr_HWord(lhs), mkIRExpr_HWord(op), mkIRExpr_HWord((HWord)arg1->Iex.RdTmp.tmp), mkIRExpr_HWord((HWord)arg2->Iex.RdTmp.tmp), mkIRExpr_HWord(counter) ) ); setHelperAnns(d2); addStmtToIRSB(sb,IRStmt_Dirty(d2)); } if ((arg1->tag == Iex_RdTmp && arg2->tag == Iex_Const) || (arg1->tag == Iex_Const && arg2->tag == Iex_RdTmp)) { if (arg1->tag == Iex_RdTmp) tmpname = (HWord)arg1->Iex.RdTmp.tmp; else if (arg2->tag == Iex_RdTmp) tmpname = (HWord)arg2->Iex.RdTmp.tmp; else vpanic("Neither arg1 nor arg2 is a tmp! \n"); d2 = unsafeIRDirty_0_N(0, "EmitBinopTmpConstTypeHelper", &EmitBinopTmpConstTypeHelper, mkIRExprVec_4( mkIRExpr_HWord(lhs), mkIRExpr_HWord(op), mkIRExpr_HWord(tmpname), mkIRExpr_HWord(counter) ) ); setHelperAnns(d2); addStmtToIRSB(sb,IRStmt_Dirty(d2)); } return; }
// Instrumentation for the end of each original instruction. static void instrumentInstr(IRBB* bbOut, instr_info* i_node, Bool bbSeenBefore, UInt instrAddr, UInt instrLen, UInt dataSize, IRExpr* loadAddrExpr, IRExpr* storeAddrExpr) { IRDirty* di; IRExpr *arg1, *arg2, *arg3, **argv; Int argc; Char* helperName; void* helperAddr; IRType wordTy; // Stay sane ... tl_assert(sizeof(HWord) == sizeof(void*)); if (sizeof(HWord) == 4) { wordTy = Ity_I32; } else if (sizeof(HWord) == 8) { wordTy = Ity_I64; } else { VG_(tool_panic)("instrumentInstr: strange word size"); } if (loadAddrExpr) tl_assert(wordTy == typeOfIRExpr(bbOut->tyenv, loadAddrExpr)); if (storeAddrExpr) tl_assert(wordTy == typeOfIRExpr(bbOut->tyenv, storeAddrExpr)); // Nb: instrLen will be zero if Vex failed to decode it. tl_assert( 0 == instrLen || (instrLen >= VG_MIN_INSTR_SZB && instrLen <= VG_MAX_INSTR_SZB) ); // 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; // Setup 1st arg: instr_info node's address // Believed to be 64-bit clean do_details(i_node, bbSeenBefore, instrAddr, instrLen, dataSize ); arg1 = mkIRExpr_HWord( (HWord)i_node ); if (!loadAddrExpr && !storeAddrExpr) { // no load/store tl_assert(0 == dataSize); helperName = "log_1I_0D_cache_access"; helperAddr = &log_1I_0D_cache_access; argc = 1; argv = mkIRExprVec_1(arg1); } else if (loadAddrExpr && !storeAddrExpr) { // load tl_assert( isIRAtom(loadAddrExpr) ); helperName = "log_1I_1Dr_cache_access"; helperAddr = &log_1I_1Dr_cache_access; argc = 2; arg2 = loadAddrExpr; argv = mkIRExprVec_2(arg1, arg2); } else if (!loadAddrExpr && storeAddrExpr) { // store tl_assert( isIRAtom(storeAddrExpr) ); helperName = "log_1I_1Dw_cache_access"; helperAddr = &log_1I_1Dw_cache_access; argc = 2; arg2 = storeAddrExpr; argv = mkIRExprVec_2(arg1, arg2); } else { tl_assert( loadAddrExpr && storeAddrExpr ); tl_assert( isIRAtom(loadAddrExpr) ); tl_assert( isIRAtom(storeAddrExpr) ); if ( loadStoreAddrsMatch(loadAddrExpr, storeAddrExpr) ) { // modify helperName = "log_1I_1Dr_cache_access"; helperAddr = &log_1I_1Dr_cache_access; argc = 2; arg2 = loadAddrExpr; argv = mkIRExprVec_2(arg1, arg2); } else { // load/store helperName = "log_1I_2D_cache_access"; helperAddr = &log_1I_2D_cache_access; argc = 3; arg2 = loadAddrExpr; arg3 = storeAddrExpr; argv = mkIRExprVec_3(arg1, arg2, arg3); } } // Add call to the instrumentation function di = unsafeIRDirty_0_N( argc, helperName, helperAddr, argv); addStmtToIRBB( bbOut, IRStmt_Dirty(di) ); }
static void instrument_store(IRSB* const bb, IRExpr* const addr_expr, const HWord size) { IRExpr* size_expr; IRExpr** argv; IRDirty* di; if (UNLIKELY(DRD_(any_address_is_traced)())) { addStmtToIRSB(bb, IRStmt_Dirty( unsafeIRDirty_0_N(/*regparms*/2, "drd_trace_store", VG_(fnptr_to_fnentry) (drd_trace_mem_store), mkIRExprVec_2(addr_expr, mkIRExpr_HWord(size))))); } if (! s_check_stack_accesses && is_stack_access(bb, addr_expr)) return; switch (size) { case 1: argv = mkIRExprVec_1(addr_expr); di = unsafeIRDirty_0_N(/*regparms*/1, "drd_trace_store_1", VG_(fnptr_to_fnentry)(drd_trace_store_1), argv); break; case 2: argv = mkIRExprVec_1(addr_expr); di = unsafeIRDirty_0_N(/*regparms*/1, "drd_trace_store_2", VG_(fnptr_to_fnentry)(drd_trace_store_2), argv); break; case 4: argv = mkIRExprVec_1(addr_expr); di = unsafeIRDirty_0_N(/*regparms*/1, "drd_trace_store_4", VG_(fnptr_to_fnentry)(drd_trace_store_4), argv); break; case 8: argv = mkIRExprVec_1(addr_expr); di = unsafeIRDirty_0_N(/*regparms*/1, "drd_trace_store_8", VG_(fnptr_to_fnentry)(drd_trace_store_8), argv); break; default: size_expr = mkIRExpr_HWord(size); argv = mkIRExprVec_2(addr_expr, size_expr); di = unsafeIRDirty_0_N(/*regparms*/2, "drd_trace_store", VG_(fnptr_to_fnentry)(DRD_(trace_store)), argv); break; } addStmtToIRSB(bb, IRStmt_Dirty(di)); }
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; }
/* This version of flushEvents (currently unused) is similar to the one in lackey with the primary difference that it groups together pairs of events for a single callback. This helps to reduce the total amount of function call overhead. */ static void flushEventsCB(IRSB* sb) { IRDirty* di; Int i; for (i = 0; i < events_used; i++) { Event* ev = &events[i]; const HChar* helperName; void* helperAddr; IRExpr** argv; Event* ev2; Int regparms; ev2 = i < events_used-1 ? &events[i+1] : NULL; if (ev2 && ev->ekind == ev2->ekind && ev->size == ev2->size) { // Decide on helper fn to call and args to pass it. switch (ev->ekind) { case Event_Ir: helperName = "trace_2instr"; helperAddr = trace_2instr; break; case Event_Dr: helperName = "trace_2load"; helperAddr = trace_2load; break; case Event_Dw: helperName = "trace_2store"; helperAddr = trace_2store; break; case Event_Dm: helperName = "trace_2modify"; helperAddr = trace_2modify; break; default: tl_assert(0); } argv = mkIRExprVec_3( ev->addr, ev2->addr, mkIRExpr_HWord( ev->size )); regparms = 3; // Skip the next event, since we paired it i++; } else if (ev2 && ev->ekind == Event_Dr && ev2->ekind == Event_Dw && ev->size == ev2->size) { // Load then store helperName = "trace_loadstore"; helperAddr = trace_loadstore; argv = mkIRExprVec_3( ev->addr, ev2->addr, mkIRExpr_HWord( ev->size )); regparms = 3; i++; } else if (ev2 && ev->ekind == Event_Dw && ev2->ekind == Event_Dr && ev->size == ev2->size) { // Store then load helperName = "trace_storeload"; helperAddr = trace_storeload; argv = mkIRExprVec_3( ev->addr, ev2->addr, mkIRExpr_HWord( ev->size )); regparms = 3; i++; } else { // Decide on helper fn to call and args to pass it. switch (ev->ekind) { case Event_Ir: helperName = "trace_instr"; helperAddr = trace_instr; break; case Event_Dr: helperName = "trace_load"; helperAddr = trace_load; break; case Event_Dw: helperName = "trace_store"; helperAddr = trace_store; break; case Event_Dm: helperName = "trace_modify"; helperAddr = trace_modify; break; default: tl_assert(0); } argv = mkIRExprVec_2( ev->addr, mkIRExpr_HWord( ev->size ) ); regparms = 2; } // Add the helper. di = unsafeIRDirty_0_N(regparms, helperName, VG_(fnptr_to_fnentry)( helperAddr ), argv ); addStmtToIRSB( sb, IRStmt_Dirty(di) ); } events_used = 0; }
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; }
IRBB* bb_to_IR ( /*OUT*/VexGuestExtents* vge, /*IN*/ void* callback_opaque, /*IN*/ DisOneInstrFn dis_instr_fn, /*IN*/ UChar* guest_code, /*IN*/ Addr64 guest_IP_bbstart, /*IN*/ Bool (*chase_into_ok)(void*,Addr64), /*IN*/ Bool host_bigendian, /*IN*/ VexArch arch_guest, /*IN*/ VexArchInfo* archinfo_guest, /*IN*/ IRType guest_word_type, /*IN*/ Bool do_self_check, /*IN*/ Bool (*preamble_function)(void*,IRBB*), /*IN*/ Int offB_TISTART, /*IN*/ Int offB_TILEN ) { Long delta; Int i, n_instrs, first_stmt_idx; Bool resteerOK, need_to_put_IP, debug_print; DisResult dres; IRStmt* imark; static Int n_resteers = 0; Int d_resteers = 0; Int selfcheck_idx = 0; IRBB* irbb; Addr64 guest_IP_curr_instr; IRConst* guest_IP_bbstart_IRConst = NULL; Bool (*resteerOKfn)(void*,Addr64) = NULL; debug_print = toBool(vex_traceflags & VEX_TRACE_FE); /* Note: for adler32 to work without % operation for the self check, need to limit length of stuff it scans to 5552 bytes. Therefore limiting the max bb len to 100 insns seems generously conservative. */ /* check sanity .. */ vassert(sizeof(HWord) == sizeof(void*)); vassert(vex_control.guest_max_insns >= 1); vassert(vex_control.guest_max_insns < 100); vassert(vex_control.guest_chase_thresh >= 0); vassert(vex_control.guest_chase_thresh < vex_control.guest_max_insns); vassert(guest_word_type == Ity_I32 || guest_word_type == Ity_I64); /* Start a new, empty extent. */ vge->n_used = 1; vge->base[0] = guest_IP_bbstart; vge->len[0] = 0; /* And a new IR BB to dump the result into. */ irbb = emptyIRBB(); /* Delta keeps track of how far along the guest_code array we have so far gone. */ delta = 0; n_instrs = 0; /* Guest addresses as IRConsts. Used in the two self-checks generated. */ if (do_self_check) { guest_IP_bbstart_IRConst = guest_word_type==Ity_I32 ? IRConst_U32(toUInt(guest_IP_bbstart)) : IRConst_U64(guest_IP_bbstart); } /* If asked to make a self-checking translation, leave 5 spaces in which to put the check statements. We'll fill them in later when we know the length and adler32 of the area to check. */ if (do_self_check) { selfcheck_idx = irbb->stmts_used; addStmtToIRBB( irbb, IRStmt_NoOp() ); addStmtToIRBB( irbb, IRStmt_NoOp() ); addStmtToIRBB( irbb, IRStmt_NoOp() ); addStmtToIRBB( irbb, IRStmt_NoOp() ); addStmtToIRBB( irbb, IRStmt_NoOp() ); } /* If the caller supplied a function to add its own preamble, use it now. */ if (preamble_function) { Bool stopNow = preamble_function( callback_opaque, irbb ); if (stopNow) { /* The callback has completed the IR block without any guest insns being disassembled into it, so just return it at this point, even if a self-check was requested - as there is nothing to self-check. The five self-check no-ops will still be in place, but they are harmless. */ return irbb; } } /* Process instructions. */ while (True) { vassert(n_instrs < vex_control.guest_max_insns); /* Regardless of what chase_into_ok says, is chasing permissible at all right now? Set resteerOKfn accordingly. */ resteerOK = toBool( n_instrs < vex_control.guest_chase_thresh /* If making self-checking translations, don't chase .. it makes the checks too complicated. We only want to scan just one sequence of bytes in the check, not a whole bunch. */ && !do_self_check /* we can't afford to have a resteer once we're on the last extent slot. */ && vge->n_used < 3 ); resteerOKfn = resteerOK ? chase_into_ok : const_False; /* This is the IP of the instruction we're just about to deal with. */ guest_IP_curr_instr = guest_IP_bbstart + delta; /* This is the irbb statement array index of the first stmt in this insn. That will always be the instruction-mark descriptor. */ first_stmt_idx = irbb->stmts_used; /* Add an instruction-mark statement. We won't know until after disassembling the instruction how long it instruction is, so just put in a zero length and we'll fix it up later. */ addStmtToIRBB( irbb, IRStmt_IMark( guest_IP_curr_instr, 0 )); /* for the first insn, the dispatch loop will have set %IP, but for all the others we have to do it ourselves. */ need_to_put_IP = toBool(n_instrs > 0); /* Finally, actually disassemble an instruction. */ dres = dis_instr_fn ( irbb, need_to_put_IP, resteerOKfn, callback_opaque, guest_code, delta, guest_IP_curr_instr, arch_guest, archinfo_guest, host_bigendian ); /* stay sane ... */ vassert(dres.whatNext == Dis_StopHere || dres.whatNext == Dis_Continue || dres.whatNext == Dis_Resteer); vassert(dres.len >= 0 && dres.len <= 20); if (dres.whatNext != Dis_Resteer) vassert(dres.continueAt == 0); /* Fill in the insn-mark length field. */ vassert(first_stmt_idx >= 0 && first_stmt_idx < irbb->stmts_used); imark = irbb->stmts[first_stmt_idx]; vassert(imark); vassert(imark->tag == Ist_IMark); vassert(imark->Ist.IMark.len == 0); imark->Ist.IMark.len = toUInt(dres.len); /* Print the resulting IR, if needed. */ if (vex_traceflags & VEX_TRACE_FE) { for (i = first_stmt_idx; i < irbb->stmts_used; i++) { vex_printf(" "); ppIRStmt(irbb->stmts[i]); vex_printf("\n"); } } /* If dis_instr_fn terminated the BB at this point, check it also filled in the irbb->next field. */ if (dres.whatNext == Dis_StopHere) { vassert(irbb->next != NULL); if (debug_print) { vex_printf(" "); vex_printf( "goto {"); ppIRJumpKind(irbb->jumpkind); vex_printf( "} "); ppIRExpr( irbb->next ); vex_printf( "\n"); } } /* Update the VexGuestExtents we are constructing. */ /* If vex_control.guest_max_insns is required to be < 100 and each insn is at max 20 bytes long, this limit of 5000 then seems reasonable since the max possible extent length will be 100 * 20 == 2000. */ vassert(vge->len[vge->n_used-1] < 5000); vge->len[vge->n_used-1] = toUShort(toUInt( vge->len[vge->n_used-1] + dres.len )); n_instrs++; if (debug_print) vex_printf("\n"); /* Advance delta (inconspicuous but very important :-) */ delta += (Long)dres.len; switch (dres.whatNext) { case Dis_Continue: vassert(irbb->next == NULL); if (n_instrs < vex_control.guest_max_insns) { /* keep going */ } else { /* We have to stop. */ irbb->next = IRExpr_Const( guest_word_type == Ity_I32 ? IRConst_U32(toUInt(guest_IP_bbstart+delta)) : IRConst_U64(guest_IP_bbstart+delta) ); goto done; } break; case Dis_StopHere: vassert(irbb->next != NULL); goto done; case Dis_Resteer: /* Check that we actually allowed a resteer .. */ vassert(resteerOK); vassert(irbb->next == NULL); /* figure out a new delta to continue at. */ vassert(resteerOKfn(callback_opaque,dres.continueAt)); delta = dres.continueAt - guest_IP_bbstart; /* we now have to start a new extent slot. */ vge->n_used++; vassert(vge->n_used <= 3); vge->base[vge->n_used-1] = dres.continueAt; vge->len[vge->n_used-1] = 0; n_resteers++; d_resteers++; if (0 && (n_resteers & 0xFF) == 0) vex_printf("resteer[%d,%d] to 0x%llx (delta = %lld)\n", n_resteers, d_resteers, dres.continueAt, delta); break; default: vpanic("bb_to_IR"); } } /*NOTREACHED*/ vassert(0); done: /* We're done. The only thing that might need attending to is that a self-checking preamble may need to be created. */ if (do_self_check) { UInt len2check, adler32; IRTemp tistart_tmp, tilen_tmp; vassert(vge->n_used == 1); len2check = vge->len[0]; if (len2check == 0) len2check = 1; adler32 = genericg_compute_adler32( (HWord)guest_code, len2check ); /* Set TISTART and TILEN. These will describe to the despatcher the area of guest code to invalidate should we exit with a self-check failure. */ tistart_tmp = newIRTemp(irbb->tyenv, guest_word_type); tilen_tmp = newIRTemp(irbb->tyenv, guest_word_type); irbb->stmts[selfcheck_idx+0] = IRStmt_Tmp(tistart_tmp, IRExpr_Const(guest_IP_bbstart_IRConst) ); irbb->stmts[selfcheck_idx+1] = IRStmt_Tmp(tilen_tmp, guest_word_type==Ity_I32 ? IRExpr_Const(IRConst_U32(len2check)) : IRExpr_Const(IRConst_U64(len2check)) ); irbb->stmts[selfcheck_idx+2] = IRStmt_Put( offB_TISTART, IRExpr_Tmp(tistart_tmp) ); irbb->stmts[selfcheck_idx+3] = IRStmt_Put( offB_TILEN, IRExpr_Tmp(tilen_tmp) ); irbb->stmts[selfcheck_idx+4] = IRStmt_Exit( IRExpr_Binop( Iop_CmpNE32, mkIRExprCCall( Ity_I32, 2/*regparms*/, "genericg_compute_adler32", #if defined(__powerpc__) && defined(__powerpc64__) (void*)((ULong*)(&genericg_compute_adler32))[0], #else &genericg_compute_adler32, #endif mkIRExprVec_2( mkIRExpr_HWord( (HWord)guest_code ), mkIRExpr_HWord( (HWord)len2check ) ) ), IRExpr_Const(IRConst_U32(adler32)) ), Ijk_TInval, guest_IP_bbstart_IRConst ); } return irbb; }
void findRepMovIRExpr(IRBB * bb, IRStmt * s, findRepMovContext * context, UInt tagVal) { IROp theOp; IRExpr * expr; IRExpr * arg1; IRExpr * arg2; UInt tmpName; IRDirty * d; expr = s->Ist.Tmp.data; switch (expr->tag) { case Iex_Binop: theOp = expr->Iex.Binop.op; arg1 = expr->Iex.Binop.arg1; arg2 = expr->Iex.Binop.arg2; switch(theOp) { case Iop_Sub32: case Iop_Sub16: case Iop_Sub8: if (arg2->tag == Iex_Const && arg1->tag == Iex_Tmp) { tmpName = (UInt) arg1->Iex.Tmp.tmp; context->subTmpVars[tmpName] = 1; if (checkTmpRepMov(tmpName, context) == 1) { // VG_(printf)("XXX tmpName: %u BB %u flagged by checkTmpRepMov! \n", tmpName, tagVal); d = unsafeIRDirty_0_N(0, "emitFlaggedTmpRepMov", &emitFlaggedTmpRepMov, mkIRExprVec_2( mkIRExpr_HWord(tmpName), mkIRExpr_HWord(tagVal) ) ); setHelperAnns(d); addStmtToIRBB(bb, IRStmt_Dirty(d)); } } break; case Iop_CmpEQ32: case Iop_CmpEQ16: case Iop_CmpEQ8: if (arg2->tag == Iex_Const && arg1->tag == Iex_Tmp) { tmpName = (UInt) arg1->Iex.Tmp.tmp; if (arg2->Iex.Const.con->Ico.U32 == 0) { context->cmpZeroTmpVars[tmpName] = 1; if (checkTmpRepMov(tmpName, context) == 1) { // VG_(printf)("XXX tmpName: %u BB %u flagged by checkTmpRepMov! \n", tmpName, tagVal); d = unsafeIRDirty_0_N(0, "emitFlaggedTmpRepMov", &emitFlaggedTmpRepMov, mkIRExprVec_2( mkIRExpr_HWord(tmpName), mkIRExpr_HWord(tagVal) ) ); setHelperAnns(d); addStmtToIRBB(bb, IRStmt_Dirty(d)); } } } break; default: break; } default: break; } return; }
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; }