]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Basic support for self-checking translations. It fits quite neatly
authorJulian Seward <jseward@acm.org>
Thu, 7 Jul 2005 01:32:16 +0000 (01:32 +0000)
committerJulian Seward <jseward@acm.org>
Thu, 7 Jul 2005 01:32:16 +0000 (01:32 +0000)
into the IR: if a translation self-check fails, the translation exits
passing VEX_TRC_JMP_TINVAL to the despatcher and with the
guest_TISTART/guest_TILEN pseudo-registers indicating what area of the
guest code needs to be invalidated.  The actual checksumming is done
by a helper function which does (a variant of) the Adler32 checksum.

Space/time overhead, whilst substantial, looks tolerable.  There's a
little room for optimisation of the basic scheme.  It would certainly
be viable to run with self-checking for all translations to support
Valgrinding JITs (including V itself) without any assistance from the
JIT.

git-svn-id: svn://svn.valgrind.org/vex/trunk@1262

VEX/priv/guest-generic/bb_to_IR.c
VEX/priv/guest-generic/bb_to_IR.h
VEX/priv/host-x86/hdefs.c
VEX/priv/main/vex_main.c
VEX/pub/libvex.h
VEX/pub/libvex_ir.h

index d8dfae08fb51d482141571592fcf192c885bee5a..226358044197e1a141d237e0dc1680a21e4ca30f 100644 (file)
 #include "guest-generic/bb_to_IR.h"
 
 
+/* Forwards .. */
+static UInt genericg_compute_adler32 ( HWord addr, UInt len );
+
+
 /* Disassemble a complete basic block, starting at guest_IP_start, 
    returning a new IRBB.  The disassembler may chase across basic
    block boundaries if it wishes and if chase_into_ok allows it.
 
    dis_instr_fn is the arch-specific fn to disassemble on function; it
    is this that does the real work.
+
+   do_self_check indicates that the caller needs a self-checking
+   translation.
+
+   offB_TIADDR and offB_TILEN are the offsets of guest_TIADDR and
+   guest_TILEN.  Since this routine has to work for any guest state,
+   without knowing what it is, those offsets have to passed in.
 */
 
 static Bool const_False ( Addr64 a ) { return False; }
@@ -62,7 +73,10 @@ IRBB* bb_to_IR ( /*OUT*/VexGuestExtents* vge,
                  /*IN*/ Bool             (*chase_into_ok)(Addr64),
                  /*IN*/ Bool             host_bigendian,
                  /*IN*/ VexArchInfo*     archinfo_guest,
-                 /*IN*/ IRType           guest_word_type )
+                 /*IN*/ IRType           guest_word_type,
+                 /*IN*/ Bool             do_self_check,
+                 /*IN*/ Int              offB_TISTART,
+                 /*IN*/ Int              offB_TILEN )
 {
    Long       delta;
    Int        i, n_instrs, first_stmt_idx;
@@ -71,6 +85,7 @@ IRBB* bb_to_IR ( /*OUT*/VexGuestExtents* vge,
    IRStmt*    imark;
    static Int n_resteers = 0;
    Int        d_resteers = 0;
+   Int        selfcheck_idx = 0;
    IRBB*      irbb;
    Addr64     guest_IP_curr_instr;
 
@@ -78,9 +93,15 @@ IRBB* bb_to_IR ( /*OUT*/VexGuestExtents* vge,
 
    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 < 500);
+   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);
@@ -98,6 +119,17 @@ IRBB* bb_to_IR ( /*OUT*/VexGuestExtents* vge,
    delta    = 0;
    n_instrs = 0;
 
+   /* If asked to make a self-checking translation, leave a 3 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() );
+   }
+
+   /* Process instructions. */
    while (True) {
       vassert(n_instrs < vex_control.guest_max_insns);
 
@@ -106,6 +138,11 @@ IRBB* bb_to_IR ( /*OUT*/VexGuestExtents* vge,
       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
@@ -182,11 +219,11 @@ IRBB* bb_to_IR ( /*OUT*/VexGuestExtents* vge,
       }
 
       /* Update the VexGuestExtents we are constructing. */
-      /* If vex_control.guest_max_insns is required to be < 500 and
-        each insn is at max 15 bytes long, this limit of 10000 then
+      /* 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
-        500 * 15 == 7500. */
-      vassert(vge->len[vge->n_used-1] < 10000);
+        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++;
@@ -209,12 +246,12 @@ IRBB* bb_to_IR ( /*OUT*/VexGuestExtents* vge,
                           ? IRConst_U32(toUInt(guest_IP_bbstart+delta))
                           : IRConst_U64(guest_IP_bbstart+delta)
                     );
-               return irbb;
+               goto done;
             }
             break;
          case Dis_StopHere:
             vassert(irbb->next != NULL);
-            return irbb;
+            goto done;
          case Dis_Resteer:
             /* Check that we actually allowed a resteer .. */
             vassert(resteerOK);
@@ -238,9 +275,114 @@ IRBB* bb_to_IR ( /*OUT*/VexGuestExtents* vge,
             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;
+      IRConst* guest_IP_bbstart_IRConst;
+
+      vassert(vge->n_used == 1);
+      len2check = vge->len[0];
+      if (len2check == 0) 
+         len2check = 1;
+
+     adler32 = genericg_compute_adler32( (HWord)guest_code, len2check );
+
+     guest_IP_bbstart_IRConst
+        = guest_word_type==Ity_I32 
+             ? IRConst_U32(guest_IP_bbstart)
+             : IRConst_U64(guest_IP_bbstart);
+
+     /* 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. */
+
+     irbb->stmts[selfcheck_idx+0]
+        = IRStmt_Put( offB_TILEN, 
+                      guest_word_type==Ity_I32 
+                         ? IRExpr_Const(IRConst_U32(len2check)) 
+                         : IRExpr_Const(IRConst_U64(len2check)) );
+
+     irbb->stmts[selfcheck_idx+1]
+        = IRStmt_Put( offB_TISTART, IRExpr_Const(guest_IP_bbstart_IRConst) );
+
+     irbb->stmts[selfcheck_idx+2]
+        = IRStmt_Exit( 
+             IRExpr_Binop( 
+                Iop_CmpNE32, 
+                mkIRExprCCall( 
+                   Ity_I32, 
+                   0/*regparms*/, 
+                   "genericg_compute_adler32",
+                   &genericg_compute_adler32,
+                   mkIRExprVec_2( 
+                      mkIRExpr_HWord( (HWord)guest_code ), 
+                      IRExpr_Const(IRConst_U32(len2check))
+                   )
+                ),
+                IRExpr_Const(IRConst_U32(adler32))
+             ),
+             Ijk_TInval,
+             guest_IP_bbstart_IRConst
+          );
+   }
+
+   return irbb;
 }
 
 
+/*-------------------------------------------------------------
+  A support routine for doing self-checking translations. 
+  -------------------------------------------------------------*/
+
+/* CLEAN HELPER */
+/* CALLED FROM GENERATED CODE */
+
+/* Compute the Adler32 checksum of host memory at [addr
+   .. addr+len-1].  This presumably holds guest code.  Note this is
+   not a proper implementation of Adler32 in that it fails to mod the
+   counts with 65521 every 5552 bytes, but we really never expect to
+   get anywhere near that many bytes to deal with.  This fn is called
+   once for every use of a self-checking translation, so it needs to
+   be as fast as possible. */
+static UInt genericg_compute_adler32 ( HWord addr, UInt len )
+{
+   UInt   i;
+   UInt   s1 = 1;
+   UInt   s2 = 0;
+   UChar* buf = (UChar*)addr;
+   for (i = 0; i < len; i++) {
+      s1 += (UInt)buf[i];
+      s2 += s1;
+   }
+#if 0
+   while (len >= 4) {
+      s1 += buf[0];
+      s2 += s1;
+      s1 += buf[1];
+      s2 += s1;
+      s1 += buf[2];
+      s2 += s1;
+      s1 += buf[3];
+      s2 += s1;
+      buf += 4;
+      len -= 4;
+   }
+   while (len > 0) {
+      s1 += buf[0];
+      s2 += s1;
+      len--;
+      buf++;
+   }
+#endif
+   return (s2 << 16) + s1;
+}
+
 
 /*--------------------------------------------------------------------*/
 /*--- end                                 guest-generic/bb_to_IR.c ---*/
index 3d26938dfd000eb0e5424eb11f07c9c895d5b93b..91a8aae0b562ceb3f878339a3704038efc82ea91 100644 (file)
@@ -146,7 +146,10 @@ IRBB* bb_to_IR ( /*OUT*/VexGuestExtents* vge,
                  /*IN*/ Bool             (*chase_into_ok)(Addr64),
                  /*IN*/ Bool             host_bigendian,
                  /*IN*/ VexArchInfo*     archinfo_guest,
-                 /*IN*/ IRType           guest_word_type );
+                 /*IN*/ IRType           guest_word_type,
+                 /*IN*/ Bool             do_self_check,
+                 /*IN*/ Int              offB_TISTART,
+                 /*IN*/ Int              offB_TILEN );
 
 
 #endif /* ndef GENERIC_BB_TO_IR_H */
index eb74feb5c447e0ccbceccc5b23a2490d074cfcf2..d2720826ef3243dba73c13adc5aec50cf497bf49 100644 (file)
@@ -2151,6 +2151,9 @@ Int emit_X86Instr ( UChar* buf, Int nbuf, X86Instr* i )
          case Ijk_NoDecode:
             *p++ = 0xBD;
             p = emit32(p, VEX_TRC_JMP_NODECODE); break;
+         case Ijk_TInval:
+            *p++ = 0xBD;
+            p = emit32(p, VEX_TRC_JMP_TINVAL); break;
          case Ijk_Ret:
         case Ijk_Call:
          case Ijk_Boring:
index 52e66b059c937db9dc4c4cdc5a5d1cc4346123ce..3145608f7dbccd17c8397a792c3a7c115bc327a8 100644 (file)
@@ -221,6 +221,7 @@ VexTranslateResult LibVEX_Translate (
    HInstrArray*    vcode;
    HInstrArray*    rcode;
    Int             i, j, k, out_used, guest_sizeB;
+   Int             offB_TISTART, offB_TILEN;
    UChar           insn_bytes[32];
    IRType          guest_word_type;
    IRType          host_word_type;
@@ -242,6 +243,8 @@ VexTranslateResult LibVEX_Translate (
    disInstrFn             = NULL;
    guest_word_type        = Ity_INVALID;
    host_word_type         = Ity_INVALID;
+   offB_TISTART           = 0;
+   offB_TILEN             = 0;
 
    vex_traceflags = traceflags;
 
@@ -322,9 +325,13 @@ VexTranslateResult LibVEX_Translate (
          guest_sizeB      = sizeof(VexGuestX86State);
          guest_word_type  = Ity_I32;
          guest_layout     = &x86guest_layout;
+         offB_TISTART     = offsetof(VexGuestX86State,guest_TISTART);
+         offB_TILEN       = offsetof(VexGuestX86State,guest_TILEN);
          vassert(archinfo_guest->subarch == VexSubArchX86_sse0
                  || archinfo_guest->subarch == VexSubArchX86_sse1
                  || archinfo_guest->subarch == VexSubArchX86_sse2);
+         vassert(sizeof( ((VexGuestX86State*)0)->guest_TISTART ) == 4);
+         vassert(sizeof( ((VexGuestX86State*)0)->guest_TILEN ) == 4);
          break;
 
       case VexArchAMD64:
@@ -334,7 +341,11 @@ VexTranslateResult LibVEX_Translate (
          guest_sizeB      = sizeof(VexGuestAMD64State);
          guest_word_type  = Ity_I64;
          guest_layout     = &amd64guest_layout;
+         offB_TISTART     = offsetof(VexGuestAMD64State,guest_TISTART);
+         offB_TILEN       = offsetof(VexGuestAMD64State,guest_TILEN);
          vassert(archinfo_guest->subarch == VexSubArch_NONE);
+         vassert(sizeof( ((VexGuestAMD64State*)0)->guest_TISTART ) == 8);
+         vassert(sizeof( ((VexGuestAMD64State*)0)->guest_TILEN ) == 8);
          break;
 
       case VexArchARM:
@@ -344,6 +355,8 @@ VexTranslateResult LibVEX_Translate (
          guest_sizeB      = sizeof(VexGuestARMState);
          guest_word_type  = Ity_I32;
          guest_layout     = &armGuest_layout;
+         offB_TISTART     = 0; /* hack ... arm has bitrot */
+         offB_TILEN       = 0; /* hack ... arm has bitrot */
          vassert(archinfo_guest->subarch == VexSubArchARM_v4);
          break;
 
@@ -354,8 +367,12 @@ VexTranslateResult LibVEX_Translate (
          guest_sizeB      = sizeof(VexGuestPPC32State);
          guest_word_type  = Ity_I32;
          guest_layout     = &ppc32Guest_layout;
+         offB_TISTART     = offsetof(VexGuestPPC32State,guest_TISTART);
+         offB_TILEN       = offsetof(VexGuestPPC32State,guest_TILEN);
          vassert(archinfo_guest->subarch == VexSubArchPPC32_noAV
                  || archinfo_guest->subarch == VexSubArchPPC32_AV);
+         vassert(sizeof( ((VexGuestPPC32State*)0)->guest_TISTART ) == 4);
+         vassert(sizeof( ((VexGuestPPC32State*)0)->guest_TILEN ) == 4);
          break;
 
       default:
@@ -382,7 +399,10 @@ VexTranslateResult LibVEX_Translate (
                      chase_into_ok,
                      host_is_bigendian,
                      archinfo_guest,
-                     guest_word_type );
+                     guest_word_type,
+                     False/*selfcheck*/,
+                     offB_TISTART,
+                     offB_TILEN );
 
    if (irbb == NULL) {
       /* Access failure. */
index 871da1aad3694314c02320819c9b351d0c9172d4..eec566a3087d83acd334bce2b43d3bb787a7733b 100644 (file)
@@ -331,9 +331,15 @@ extern void LibVEX_ShowStats ( void );
    stack tags will not be as expected, and (2) after returning to
    generated code, the generated code is likely to go wrong.  This
    really should be fixed.
-*/
-
 
+   ALL GUEST ARCHITECTURES
+   ~~~~~~~~~~~~~~~~~~~~~~~
+   The architecture must contain two pseudo-registers, guest_TISTART
+   and guest_TILEN.  These are used to pass the address of areas of
+   guest code, translations of which are to be invalidated, back to
+   the despatcher.  Both pseudo-regs must have size equal to the guest
+   word size.
+*/
 #endif /* ndef __LIBVEX_H */
 
 /*---------------------------------------------------------------*/
index eeaac93cf6ecc19ae5d4292ba6cafc1641c17405..5e5fbf40225c63d765f68f39462eff1ef643e521 100644 (file)
@@ -778,12 +778,12 @@ extern Bool eqIRAtom ( IRExpr*, IRExpr* );
 /* This describes hints which can be passed to the dispatcher at guest
    control-flow transfer points.
 
-   Re Ijk_Invalidate: typically the guest state will have two
-   pseudo-registers, guest_TISTART and guest_TILEN, which
-   specify the start and length of the region to be invalidated.
-   It is the responsibility of the relevant toIR.c to ensure that
-   these are filled in with suitable values before issuing a jump
-   of kind Ijk_TInval.
+   Re Ijk_Invalidate: the guest state _must_ have two
+   pseudo-registers, guest_TISTART and guest_TILEN, which specify the
+   start and length of the region to be invalidated.  These are both
+   the size of a guest word. It is the responsibility of the relevant
+   toIR.c to ensure that these are filled in with suitable values
+   before issuing a jump of kind Ijk_TInval.  
 */
 typedef
    enum {