static void instrument_store(IRSB* const bb, IRExpr* const addr_expr, const HWord size) { IRExpr* size_expr; IRExpr** argv; IRDirty* di; 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)); }
static void VG_(add_stmt_call_invalidate_if_not_gdbserved) ( IRSB* sb_in, VexGuestLayout* layout, VexGuestExtents* vge, IRTemp jmp, IRSB* irsb) { void* fn; const HChar* nm; IRExpr** args; Int nargs; IRDirty* di; fn = &VG_(helperc_invalidate_if_not_gdbserved); nm = "VG_(helperc_invalidate_if_not_gdbserved)"; args = mkIRExprVec_1(IRExpr_RdTmp (jmp)); nargs = 1; di = unsafeIRDirty_0_N( nargs/*regparms*/, nm, VG_(fnptr_to_fnentry)( fn ), args ); di->nFxState = 0; addStmtToIRSB(irsb, IRStmt_Dirty(di)); }
void addDynamicDisownNonNullDetached(IRSB* sbOut, IRExpr* st){ runPureCCall(sbOut, mkIRCallee(1, "disownShadowTemp", VG_(fnptr_to_fnentry)(disownShadowTemp)), Ity_I64, mkIRExprVec_1(st)); }
void addDynamicDisownNonNull(IRSB* sbOut, IRTemp idx){ IRDirty* disownDirty = unsafeIRDirty_0_N(1, "disownShadowTempNonNullDynamic", VG_(fnptr_to_fnentry)(disownShadowTempNonNullDynamic), mkIRExprVec_1(mkU64(idx))); disownDirty->mFx = Ifx_Modify; disownDirty->mAddr = mkU64((uintptr_t)&(shadowTemps[idx])); disownDirty->mSize = sizeof(ShadowTemp*); addStmtToIRSB(sbOut, IRStmt_Dirty(disownDirty)); }
void instrument_WrTmp_Const(IRStmt* st, IRSB* sb_out) { IRTemp tmp = st->Ist.WrTmp.tmp; IRDirty* di; di = unsafeIRDirty_0_N(0, "helper_instrument_WrTmp_Const", VG_(fnptr_to_fnentry)(helper_instrument_WrTmp_Const), mkIRExprVec_1(mkIRExpr_HWord(tmp)) ); addStmtToIRSB(sb_out, IRStmt_Dirty(di)); }
/* A helper that adds the instrumentation for a detail. */ static void instrument_detail(IRSB* sb, Op op, IRType type) { IRDirty* di; IRExpr** argv; const UInt typeIx = type2index(type); tl_assert(op < N_OPS); tl_assert(typeIx < N_TYPES); argv = mkIRExprVec_1( mkIRExpr_HWord( (HWord)&detailCounts[op][typeIx] ) ); di = unsafeIRDirty_0_N( 1, "increment_detail", VG_(fnptr_to_fnentry)( &increment_detail ), argv); addStmtToIRSB( sb, IRStmt_Dirty(di) ); }
static void addEvent_FnEntry ( IRSB* sb, char *fnname) { IRExpr** argv; IRDirty* di; char *buf = (char *)VG_(malloc)("addEvent_FnEntry",100*sizeof(char)); tl_assert(buf!=NULL); VG_(strcpy)(buf,fnname); argv = mkIRExprVec_1( mkIRExpr_HWord( (HWord) buf )); di = unsafeIRDirty_0_N( /*regparms*/1, "trace_fnentry", VG_(fnptr_to_fnentry)( trace_fnentry ), argv ); if(events_used > 0) flushEvents(sb); addStmtToIRSB( sb, IRStmt_Dirty(di) ); }
void AddStoreHelper(IRSB* sb, IRExpr* addr, IRExpr* data) { IRDirty* d; HWord tmpname; switch (addr->tag) { case (Iex_RdTmp): switch (data->tag) { case (Iex_RdTmp): tmpname = (HWord) data->Iex.RdTmp.tmp; d = unsafeIRDirty_0_N(0, "EmitStoreAddr2TmpHelper", &EmitStoreAddr2TmpHelper, mkIRExprVec_3(addr, mkIRExpr_HWord(tmpname), mkIRExpr_HWord(counter) ) ); setHelperAnns(d); addStmtToIRSB(sb, IRStmt_Dirty(d)); break; case (Iex_Const): /* add code to emit new tyvar for memory address */ d = unsafeIRDirty_0_N(0, "EmitStoreAddr2ConstHelper", &EmitStoreAddr2ConstHelper, mkIRExprVec_1(addr ) ); setHelperAnns(d); addStmtToIRSB(sb,IRStmt_Dirty(d)); break; default: /* Should not reach here. */ ppIRExpr(data); vpanic("Bad store address!\n"); break; } break; default: break; } return; }
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_drd_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)); }
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; }
static IRBB* CLG_(instrument)( VgCallbackClosure* closure, IRBB* bbIn, VexGuestLayout* layout, VexGuestExtents* vge, IRType gWordTy, IRType hWordTy ) { Int i; IRBB* bbOut; IRStmt* st, *stnext; Addr instrAddr, origAddr; UInt instrLen = 0, dataSize; UInt instrCount, costOffset; IRExpr *loadAddrExpr, *storeAddrExpr; BB* bb; IRDirty* di; IRExpr *arg1, **argv; Bool bb_seen_before = False; UInt cJumps = 0, cJumpsCorrected; Bool beforeIBoundary, instrIssued; if (gWordTy != hWordTy) { /* We don't currently support this case. */ VG_(tool_panic)("host/guest word size mismatch"); } // No instrumentation if it is switched off if (! CLG_(instrument_state)) { CLG_DEBUG(5, "instrument(BB %p) [Instrumentation OFF]\n", (Addr)closure->readdr); return bbIn; } CLG_DEBUG(3, "+ instrument(BB %p)\n", (Addr)closure->readdr); /* Set up BB for instrumented IR */ bbOut = emptyIRBB(); bbOut->tyenv = dopyIRTypeEnv(bbIn->tyenv); bbOut->next = dopyIRExpr(bbIn->next); 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( bbOut, bbIn->stmts[i] ); i++; } // Get the first statement, and origAddr from it CLG_ASSERT(bbIn->stmts_used > 0); st = bbIn->stmts[i]; CLG_ASSERT(Ist_IMark == st->tag); instrAddr = origAddr = (Addr)st->Ist.IMark.addr; CLG_ASSERT(origAddr == st->Ist.IMark.addr); // XXX: check no overflow /* Get BB (creating if necessary). * JS: The hash table is keyed with orig_addr_noredir -- important! * JW: Why? If it is because of different chasing of the redirection, * this is not needed, as chasing is switched off in callgrind */ bb = CLG_(get_bb)(origAddr, bbIn, &bb_seen_before); //bb = CLG_(get_bb)(orig_addr_noredir, bbIn, &bb_seen_before); /* * Precondition: * - jmps_passed has number of cond.jumps passed in last executed BB * - current_bbcc has a pointer to the BBCC of the last executed BB * Thus, if bbcc_jmpkind is != -1 (JmpNone), * current_bbcc->bb->jmp_addr * gives the address of the jump source. * * The BBCC setup does 2 things: * - trace call: * * Unwind own call stack, i.e sync our ESP with real ESP * This is for ESP manipulation (longjmps, C++ exec handling) and RET * * For CALLs or JMPs crossing objects, record call arg + * push are on own call stack * * - prepare for cache log functions: * Set current_bbcc to BBCC that gets the costs for this BB execution * attached */ // helper call to setup_bbcc, with pointer to basic block info struct as argument arg1 = mkIRExpr_HWord( (HWord)bb ); argv = mkIRExprVec_1(arg1); di = unsafeIRDirty_0_N( 1, "setup_bbcc", VG_(fnptr_to_fnentry)( & CLG_(setup_bbcc) ), argv); addStmtToIRBB( bbOut, IRStmt_Dirty(di) ); instrCount = 0; costOffset = 0; // loop for each host instruction (starting from 'i') do { // We should be at an IMark statement CLG_ASSERT(Ist_IMark == st->tag); // Reset stuff for this original instruction loadAddrExpr = storeAddrExpr = NULL; instrIssued = False; dataSize = 0; // Process all the statements for this original instruction (ie. until // the next IMark statement, or the end of the block) do { i++; stnext = ( i < bbIn->stmts_used ? bbIn->stmts[i] : NULL ); beforeIBoundary = !stnext || (Ist_IMark == stnext->tag); collectStatementInfo(bbIn->tyenv, bbOut, st, &instrAddr, &instrLen, &loadAddrExpr, &storeAddrExpr, &dataSize, hWordTy); // instrument a simulator call before conditional jumps if (st->tag == Ist_Exit) { // Nb: instrLen will be zero if Vex failed to decode it. // Also Client requests can appear to be very large (eg. 18 // bytes on x86) because they are really multiple instructions. CLG_ASSERT( 0 == instrLen || bbIn->jumpkind == Ijk_ClientReq || (instrLen >= VG_MIN_INSTR_SZB && instrLen <= VG_MAX_INSTR_SZB) ); // Add instrumentation before this statement endOfInstr(bbOut, &(bb->instr[instrCount]), bb_seen_before, instrAddr - origAddr, instrLen, dataSize, &costOffset, instrIssued, loadAddrExpr, storeAddrExpr); // prepare for a possible further simcall in same host instr loadAddrExpr = storeAddrExpr = NULL; instrIssued = True; if (!bb_seen_before) { bb->jmp[cJumps].instr = instrCount; bb->jmp[cJumps].skip = False; } /* Update global variable jmps_passed (this is before the jump!) * A correction is needed if VEX inverted the last jump condition */ cJumpsCorrected = cJumps; if ((cJumps+1 == bb->cjmp_count) && bb->cjmp_inverted) cJumpsCorrected++; addConstMemStoreStmt( bbOut, (UWord) &CLG_(current_state).jmps_passed, cJumpsCorrected, hWordTy); cJumps++; } addStmtToIRBB( bbOut, st ); st = stnext; } while (!beforeIBoundary); // Add instrumentation for this original instruction. if (!instrIssued || (loadAddrExpr != 0) || (storeAddrExpr !=0)) endOfInstr(bbOut, &(bb->instr[instrCount]), bb_seen_before, instrAddr - origAddr, instrLen, dataSize, &costOffset, instrIssued, loadAddrExpr, storeAddrExpr); instrCount++; } while (st); /* Always update global variable jmps_passed (at end of BB) * A correction is needed if VEX inverted the last jump condition */ cJumpsCorrected = cJumps; if (bb->cjmp_inverted) cJumpsCorrected--; addConstMemStoreStmt( bbOut, (UWord) &CLG_(current_state).jmps_passed, cJumpsCorrected, hWordTy); /* This stores the instr of the call/ret at BB end */ bb->jmp[cJumps].instr = instrCount-1; CLG_ASSERT(bb->cjmp_count == cJumps); CLG_ASSERT(bb->instr_count == instrCount); instrAddr += instrLen; if (bb_seen_before) { CLG_ASSERT(bb->instr_len == instrAddr - origAddr); CLG_ASSERT(bb->cost_count == costOffset); CLG_ASSERT(bb->jmpkind == bbIn->jumpkind); } else { bb->instr_len = instrAddr - origAddr; bb->cost_count = costOffset; bb->jmpkind = bbIn->jumpkind; } CLG_DEBUG(3, "- instrument(BB %p): byteLen %u, CJumps %u, CostLen %u\n", origAddr, bb->instr_len, bb->cjmp_count, bb->cost_count); if (cJumps>0) { CLG_DEBUG(3, " [ "); for (i=0; i<cJumps; i++) CLG_DEBUG(3, "%d ", bb->jmp[i].instr); CLG_DEBUG(3, "], last inverted: %s \n", bb->cjmp_inverted ? "yes":"no"); } return bbOut; }
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 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; }
/* If a tool wants to allow gdbserver to do something at Addr, then VG_(add_stmt_call_gdbserver) will add in IRSB a call to a helper function. This helper function will check if the process must be stopped at the instruction Addr: either there is a break at Addr or the process is being single-stepped. Typical usage of the below is to instrument an Ist_IMark to allow the debugger to interact at any instruction being executed. As soon as there is one break in a block, then to allow single stepping in this block (and possible insertions of other breaks in the same sb_in while the process is stopped), a debugger statement will be inserted for all instructions of a block. */ static void VG_(add_stmt_call_gdbserver) (IRSB* sb_in, /* block being translated */ VexGuestLayout* layout, VexGuestExtents* vge, IRType gWordTy, IRType hWordTy, Addr iaddr, /* Addr of instruction being instrumented */ UChar delta, /* delta to add to iaddr to obtain IP */ IRSB* irsb) /* irsb block to which call is added */ { void* fn; const HChar* nm; IRExpr** args; Int nargs; IRDirty* di; /* first store the address in the program counter so that the check done by VG_(helperc_CallDebugger) will be based on the correct program counter. We might make this more efficient by rather searching for assignement to program counter and instrumenting that but the below is easier and I guess that the optimiser will remove the redundant store. And in any case, when debugging a piece of code, the efficiency requirement is not critical: very few blocks will be instrumented for debugging. */ /* For platforms on which the IP can differ from the addr of the instruction being executed, we need to add the delta to obtain the IP. This IP will be given to gdb (e.g. if a breakpoint is put at iaddr). For ARM, this delta will ensure that the thumb bit is set in the IP when executing thumb code. gdb uses this thumb bit a.o. to properly guess the next IP for the 'step' and 'stepi' commands. */ vg_assert(delta <= 1); addStmtToIRSB(irsb, IRStmt_Put(layout->offset_IP , mkIRExpr_HWord(iaddr + (Addr)delta))); fn = &VG_(helperc_CallDebugger); nm = "VG_(helperc_CallDebugger)"; args = mkIRExprVec_1(mkIRExpr_HWord (iaddr)); nargs = 1; di = unsafeIRDirty_0_N( nargs/*regparms*/, nm, VG_(fnptr_to_fnentry)( fn ), args ); /* Note: in fact, a debugger call can read whatever register or memory. It can also write whatever register or memory. So, in theory, we have to indicate the whole universe can be read and modified. It is however not critical to indicate precisely what is being read/written as such indications are needed for tool error detection and we do not want to have errors being detected for gdb interactions. */ di->nFxState = 2; di->fxState[0].fx = Ifx_Read; di->fxState[0].offset = layout->offset_SP; di->fxState[0].size = layout->sizeof_SP; di->fxState[0].nRepeats = 0; di->fxState[0].repeatLen = 0; di->fxState[1].fx = Ifx_Modify; di->fxState[1].offset = layout->offset_IP; di->fxState[1].size = layout->sizeof_IP; di->fxState[1].nRepeats = 0; di->fxState[1].repeatLen = 0; addStmtToIRSB(irsb, IRStmt_Dirty(di)); }
// This inserts an IR Statement responsible for calling func // code before the instruction at addr is executed. This is primarily // used for inserting the call to enter_function on function entry. // It is also used for handling of 'function priming' for GCC 3 (see // comment above prime_function). The result of looking up addr in // table will be passed to func as it's only argument. This function // does nothing if it is unable to successfully look up addr in the // provided table. static void handle_possible_entry_func(MCEnv *mce, Addr64 addr, struct genhashtable *table, const char *func_name, entry_func func) { IRDirty *di; FunctionEntry *entry = gengettable(table, (void *)(Addr)addr); if(!entry) { return; } // If fjalar_trace_prog_pts_filename is on (we are using a ppt list // file), then DO NOT generate IR code to call helper functions for // functions whose name is NOT located in prog_pts_tree. It's faster // to filter them out at translation-time instead of run-time if (entry && (!fjalar_trace_prog_pts_filename || prog_pts_tree_entry_found(entry))) { UWord entry_w = (UWord)entry; di = unsafeIRDirty_0_N(1/*regparms*/, func_name, func, mkIRExprVec_1(IRExpr_Const(IRConst_UWord(entry_w)))); // For function entry, we are interested in observing the stack // and frame pointers so make sure that they're updated by setting // the proper annotations: entry->entryPC = addr; FJALAR_DPRINTF("Found a valid entry point at %x for\n", (UInt)addr); // We need all general purpose registers. di->nFxState = 9; vex_bzero(&di->fxState, sizeof(di->fxState)); di->fxState[0].fx = Ifx_Read; di->fxState[0].offset = mce->layout->offset_SP; di->fxState[0].size = mce->layout->sizeof_SP; di->fxState[1].fx = Ifx_Read; di->fxState[1].offset = mce->layout->offset_FP; di->fxState[1].size = mce->layout->sizeof_FP; di->fxState[2].fx = Ifx_Read; di->fxState[2].offset = mce->layout->offset_IP; di->fxState[2].size = mce->layout->sizeof_IP; di->fxState[3].fx = Ifx_Read; di->fxState[3].offset = mce->layout->offset_xAX; di->fxState[3].size = mce->layout->sizeof_xAX; di->fxState[4].fx = Ifx_Read; di->fxState[4].offset = mce->layout->offset_xBX; di->fxState[4].size = mce->layout->sizeof_xBX; di->fxState[5].fx = Ifx_Read; di->fxState[5].offset = mce->layout->offset_xCX; di->fxState[5].size = mce->layout->sizeof_xCX; di->fxState[6].fx = Ifx_Read; di->fxState[6].offset = mce->layout->offset_xDX; di->fxState[6].size = mce->layout->sizeof_xDX; di->fxState[7].fx = Ifx_Read; di->fxState[7].offset = mce->layout->offset_xSI; di->fxState[7].size = mce->layout->sizeof_xSI; di->fxState[8].fx = Ifx_Read; di->fxState[8].offset = mce->layout->offset_xDI; di->fxState[8].size = mce->layout->sizeof_xDI; stmt('V', mce, IRStmt_Dirty(di) ); } }
// 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) ); }
// Handle a function exit statement, which contains a jump kind of // 'Ret'. It seems pretty accurate to cue off of currentAddr, a value // that is updated every time an Ist_IMark statement is translated, // which is quite often void handle_possible_exit(MCEnv* mce, IRJumpKind jk) { if (Ijk_Ret == jk) { IRDirty *di; FunctionEntry* curFuncPtr = getFunctionEntryFromAddr(currentAddr); if (curFuncPtr && // Also, if fjalar_trace_prog_pts_filename is on (we are // reading in a ppt list file), then DO NOT generate IR code // to call helper functions for functions whose names are NOT // located in prog_pts_tree. This will greatly speed up // processing because these functions are filtered out at // translation-time, not at run-time (!fjalar_trace_prog_pts_filename || prog_pts_tree_entry_found(curFuncPtr))) { FJALAR_DPRINTF("[handle_possible_exit] %s - %x\n", curFuncPtr->fjalar_name, (UInt)currentAddr); // The only argument to exit_function() is a pointer to the // FunctionEntry for the function that we are exiting di = unsafeIRDirty_0_N(1/*regparms*/, "exit_function", &exit_function, mkIRExprVec_1(IRExpr_Const(IRConst_UWord((Addr)curFuncPtr)))); // For function exit, we are interested in observing all general purpose // integer registers, FTOP, and FPREG[], so make sure that they are // updated by setting the proper annotations. di->nFxState = 11; vex_bzero(&di->fxState, sizeof(di->fxState)); di->fxState[0].fx = Ifx_Read; di->fxState[0].offset = mce->layout->offset_SP; di->fxState[0].size = mce->layout->sizeof_SP; di->fxState[1].fx = Ifx_Read; di->fxState[1].offset = mce->layout->offset_FP; di->fxState[1].size = mce->layout->sizeof_FP; di->fxState[2].fx = Ifx_Read; di->fxState[2].offset = mce->layout->offset_IP; di->fxState[2].size = mce->layout->sizeof_IP; di->fxState[3].fx = Ifx_Read; di->fxState[3].offset = mce->layout->offset_xAX; di->fxState[3].size = mce->layout->sizeof_xAX; di->fxState[4].fx = Ifx_Read; di->fxState[4].offset = mce->layout->offset_xBX; di->fxState[4].size = mce->layout->sizeof_xBX; di->fxState[5].fx = Ifx_Read; di->fxState[5].offset = mce->layout->offset_xCX; di->fxState[5].size = mce->layout->sizeof_xCX; di->fxState[6].fx = Ifx_Read; di->fxState[6].offset = mce->layout->offset_xDX; di->fxState[6].size = mce->layout->sizeof_xDX; di->fxState[7].fx = Ifx_Read; di->fxState[7].offset = mce->layout->offset_xSI; di->fxState[7].size = mce->layout->sizeof_xSI; di->fxState[8].fx = Ifx_Read; di->fxState[8].offset = mce->layout->offset_xDI; di->fxState[8].size = mce->layout->sizeof_xDI; di->fxState[9].fx = Ifx_Read; di->fxState[9].offset = offsetof(VexGuestArchState, guest_FTOP); di->fxState[9].size = sizeof(UInt); /* FTOP is 4 bytes even on x64 */ di->fxState[10].fx = Ifx_Read; di->fxState[10].offset = offsetof(VexGuestArchState, guest_FPREG); di->fxState[10].size = 8 * sizeof(ULong); stmt('V', mce, IRStmt_Dirty(di) ); } } }