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 IRBB* cg_instrument ( IRBB* bbIn, VexGuestLayout* layout, IRType gWordTy, IRType hWordTy ) { Int i, dataSize = 0, bbInfo_i; IRBB* bbOut; IRStmt* st; BB_info* bbInfo; Bool bbSeenBefore = False, addedInstrumentation, addInstNow; Addr instrAddr, origAddr; UInt instrLen; IRExpr *loadAddrExpr, *storeAddrExpr; if (gWordTy != hWordTy) { /* We don't currently support this case. */ VG_(tool_panic)("host/guest word size mismatch"); } /* Set up BB */ bbOut = emptyIRBB(); bbOut->tyenv = dopyIRTypeEnv(bbIn->tyenv); bbOut->next = dopyIRExpr(bbIn->next); bbOut->jumpkind = bbIn->jumpkind; // Get the first statement, and origAddr from it i = 0; tl_assert(bbIn->stmts_used > 0); st = bbIn->stmts[0]; tl_assert(Ist_IMark == st->tag); origAddr = (Addr)st->Ist.IMark.addr; tl_assert(origAddr == st->Ist.IMark.addr); // XXX: check no overflow // Get block info bbInfo = get_BB_info(bbIn, origAddr, &bbSeenBefore); bbInfo_i = 0; do { // We should be at an IMark statement tl_assert(Ist_IMark == st->tag); // Reset stuff for this original instruction loadAddrExpr = storeAddrExpr = NULL; dataSize = 0; addedInstrumentation = False; // Process all the statements for this original instruction (ie. until // the next IMark statement, or the end of the block) do { IRStmt* st2 = ( i+1 < bbIn->stmts_used ? bbIn->stmts[i+1] : NULL ); addInstNow = handleOneStatement(bbIn->tyenv, bbOut, st, st2, &instrAddr, &instrLen, &loadAddrExpr, &storeAddrExpr, &dataSize); if (addInstNow) { tl_assert(!addedInstrumentation); addedInstrumentation = True; // Add instrumentation before this statement. instrumentInstr(bbOut, &bbInfo->instrs[ bbInfo_i ], bbSeenBefore, instrAddr, instrLen, dataSize, loadAddrExpr, storeAddrExpr); } addStmtToIRBB( bbOut, st ); i++; st = st2; } while (st && Ist_IMark != st->tag); if (!addedInstrumentation) { // Add instrumentation now, after all the instruction's statements. instrumentInstr(bbOut, &bbInfo->instrs[ bbInfo_i ], bbSeenBefore, instrAddr, instrLen, dataSize, loadAddrExpr, storeAddrExpr); } bbInfo_i++; } while (st); return bbOut; }
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; }
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; }