]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
x86 guest: simulate LDT/GDT enough that code using segment override
authorJulian Seward <jseward@acm.org>
Mon, 13 Dec 2004 10:48:19 +0000 (10:48 +0000)
committerJulian Seward <jseward@acm.org>
Mon, 13 Dec 2004 10:48:19 +0000 (10:48 +0000)
prefixes can work.

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

VEX/priv/guest-x86/gdefs.h
VEX/priv/guest-x86/ghelpers.c
VEX/priv/guest-x86/toIR.c
VEX/pub/libvex.h
VEX/pub/libvex_guest_x86.h

index 298e6539b169de5823beb0d8174aeefa20225152..a220398e10a1564ce4a060f30a635c2aae232728 100644 (file)
@@ -95,13 +95,23 @@ extern ULong x86g_calculate_RCR  (
                 UInt arg, UInt rot_amt, UInt eflags_in, UInt sz 
              );
 
-extern ULong x86h_check_fldcw ( UInt fpucw );
+extern ULong x86g_check_fldcw ( UInt fpucw );
 
-extern UInt x86h_create_fpucw ( UInt fpround );
+extern UInt x86g_create_fpucw ( UInt fpround );
 
-extern ULong x86h_check_ldmxcsr ( UInt mxcsr );
+extern ULong x86g_check_ldmxcsr ( UInt mxcsr );
 
-extern UInt x86h_create_mxcsr ( UInt sseround );
+extern UInt x86g_create_mxcsr ( UInt sseround );
+
+/* Translate a guest virtual_addr into a guest linear address by
+   consulting the supplied LDT/GDT structures.  Their representation
+   must be as specified in pub/libvex_guest_x86.h.  To indicate a
+   translation failure, 1<<32 is returned.  On success, the lower 32
+   bits of the returned result indicate the linear address.  
+*/
+extern 
+ULong x86g_use_seg_selector ( HWord ldt, HWord gdt, 
+                              UInt seg_selector, UInt virtual_addr );
 
 /* --- Clean helpers for MMX --- */
 
index 5b2140025cc70970be702493ba51540da28267eb..9041b07036b2f607f8672bfc2ca2bc31569404a9 100644 (file)
@@ -1392,7 +1392,7 @@ typedef
    Extract from it the required FPROUND value and any resulting
    emulation warning, and return (warn << 32) | fpround value. 
 */
-ULong x86h_check_fldcw ( UInt fpucw )
+ULong x86g_check_fldcw ( UInt fpucw )
 {
    /* Decide on a rounding mode.  fpucw[11:10] holds it. */
    /* NOTE, encoded exactly as per enum IRRoundingMode. */
@@ -1417,7 +1417,7 @@ ULong x86h_check_fldcw ( UInt fpucw )
 /* CLEAN HELPER */
 /* Given fpround as an IRRoundingMode value, create a suitable x87
    native format FPU control word. */
-UInt x86h_create_fpucw ( UInt fpround )
+UInt x86g_create_fpucw ( UInt fpround )
 {
    fpround &= 3;
    return 0x037F | (fpround << 10);
@@ -1429,7 +1429,7 @@ UInt x86h_create_fpucw ( UInt fpround )
    Extract from it the required SSEROUND value and any resulting
    emulation warning, and return (warn << 32) | sseround value.
 */
-ULong x86h_check_ldmxcsr ( UInt mxcsr )
+ULong x86g_check_ldmxcsr ( UInt mxcsr )
 {
    /* Decide on a rounding mode.  mxcsr[14:13] holds it. */
    /* NOTE, encoded exactly as per enum IRRoundingMode. */
@@ -1455,7 +1455,7 @@ ULong x86h_check_ldmxcsr ( UInt mxcsr )
 /* CLEAN HELPER */
 /* Given sseround as an IRRoundingMode value, create a suitable SSE
    native format MXCSR value. */
-UInt x86h_create_mxcsr ( UInt sseround )
+UInt x86g_create_mxcsr ( UInt sseround )
 {
    sseround &= 3;
    return 0x1F80 | (sseround << 13);
@@ -1507,7 +1507,7 @@ VexEmWarn put_x87 ( Bool moveRegs,
 
    /* handle the control word, setting FPROUND and detecting any
       emulation warnings. */
-   pair    = x86h_check_fldcw ( (UInt)fpucw );
+   pair    = x86g_check_fldcw ( (UInt)fpucw );
    fpround = (UInt)pair;
    ew      = (VexEmWarn)(pair >> 32);
    
@@ -1544,7 +1544,7 @@ void LibVEX_GuestX86_get_x87 ( /*IN*/VexGuestX86State* vex_state,
    x87->env[1] = x87->env[3] = x87->env[5] = x87->env[13] = 0xFFFF;
    x87->env[FP_ENV_STAT] = ((ftop & 7) << 11) | (c3210 & 0x4700);
    x87->env[FP_ENV_CTRL] 
-      = (UShort)x86h_create_fpucw( vex_state->guest_FPROUND );
+      = (UShort)x86g_create_fpucw( vex_state->guest_FPROUND );
 
    tagw = 0;
    for (r = 0; r < 8; r++) {
@@ -1647,12 +1647,14 @@ void LibVEX_GuestX86_initialise ( /*OUT*/VexGuestX86State* vex_state )
 
 #  undef SSEZERO
 
-   vex_state->guest_CS = 0;
-   vex_state->guest_DS = 0;
-   vex_state->guest_ES = 0;
-   vex_state->guest_FS = 0;
-   vex_state->guest_GS = 0;
-   vex_state->guest_SS = 0;
+   vex_state->guest_CS  = 0;
+   vex_state->guest_DS  = 0;
+   vex_state->guest_ES  = 0;
+   vex_state->guest_FS  = 0;
+   vex_state->guest_GS  = 0;
+   vex_state->guest_SS  = 0;
+   vex_state->guest_LDT = 0;
+   vex_state->guest_GDT = 0;
 
    vex_state->guest_EMWARN = EmWarn_NONE;
 }
@@ -2702,6 +2704,115 @@ UInt x86g_calculate_sse_pmovmskb ( ULong w64hi, ULong w64lo )
 }
 
 
+/*----------------------------------------------*/
+/*--- Helpers for segment overrides          ---*/
+/*----------------------------------------------*/
+
+static inline 
+UInt get_segdescr_base ( VexGuestX86SegDescr* ent )
+{
+   UInt lo  = 0xFFFF & (UInt)ent->LdtEnt.Bits.BaseLow;
+   UInt mid =   0xFF & (UInt)ent->LdtEnt.Bits.BaseMid;
+   UInt hi  =   0xFF & (UInt)ent->LdtEnt.Bits.BaseHi;
+   return (hi << 24) | (mid << 16) | lo;
+}
+
+static inline
+UInt get_segdescr_limit ( VexGuestX86SegDescr* ent )
+{
+    UInt lo    = 0xFFFF & (UInt)ent->LdtEnt.Bits.LimitLow;
+    UInt hi    =    0xF & (UInt)ent->LdtEnt.Bits.LimitHi;
+    UInt limit = (hi << 16) | lo;
+    if (ent->LdtEnt.Bits.Granularity) 
+       limit = (limit << 12) | 0xFFF;
+    return limit;
+}
+
+ULong x86g_use_seg_selector ( HWord ldt, HWord gdt,
+                              UInt seg_selector, UInt virtual_addr )
+{
+   UInt tiBit, base, limit;
+   VexGuestX86SegDescr* the_descrs;
+
+   Bool verboze = False;
+
+   /* If this isn't true, we're in Big Trouble. */
+   vassert(8 == sizeof(VexGuestX86SegDescr));
+
+   if (verboze) 
+      vex_printf("x86h_use_seg_selector: "
+                 "seg_selector = 0x%x, vaddr = 0x%x\n", 
+                 seg_selector, virtual_addr);
+
+   /* Check for wildly invalid selector. */
+   if (seg_selector & ~0xFFFF)
+      goto bad;
+
+   seg_selector &= 0x0000FFFF;
+  
+   /* Sanity check the segment selector.  Ensure that RPL=11b (least
+      privilege).  This forms the bottom 2 bits of the selector. */
+   if ((seg_selector & 3) != 3)
+      goto bad;
+
+   /* Extract the TI bit (0 means GDT, 1 means LDT) */
+   tiBit = (seg_selector >> 2) & 1;
+
+   /* Convert the segment selector onto a table index */
+   seg_selector >>= 3;
+   vassert(seg_selector >= 0 && seg_selector < 8192);
+
+   if (tiBit == 0) {
+
+      /* GDT access. */
+      /* Do we actually have a GDT to look at? */
+      if (gdt == 0)
+         goto bad;
+
+      /* Check for access to non-existent entry. */
+      if (seg_selector >= VEX_GUEST_X86_GDT_NENT)
+         goto bad;
+
+      the_descrs = (VexGuestX86SegDescr*)gdt;
+      base  = get_segdescr_base (&the_descrs[seg_selector]);
+      limit = get_segdescr_limit(&the_descrs[seg_selector]);
+
+   } else {
+
+      /* All the same stuff, except for the LDT. */
+      if (ldt == 0)
+         goto bad;
+
+      if (seg_selector >= VEX_GUEST_X86_LDT_NENT)
+         goto bad;
+
+      the_descrs = (VexGuestX86SegDescr*)ldt;
+      base  = get_segdescr_base (&the_descrs[seg_selector]);
+      limit = get_segdescr_limit(&the_descrs[seg_selector]);
+
+   }
+
+   /* Do the limit check.  Note, this check is just slightly too
+      slack.  Really it should be "if (virtual_addr + size - 1 >=
+      limit)," but we don't have the size info to hand.  Getting it
+      could be significantly complex.  */
+   if (virtual_addr >= limit)
+      goto bad;
+
+   if (verboze) 
+      vex_printf("x86h_use_seg_selector: "
+                 "base = 0x%x, addr = 0x%x\n", 
+                 base, base + virtual_addr);
+
+   /* High 32 bits are zero, indicating success. */
+   return (ULong)( ((UInt)virtual_addr) + base );
+
+ bad:
+   return 1ULL << 32;
+}
+
+
+
 /*-----------------------------------------------------------*/
 /*--- Describing the x86 guest state, for the benefit     ---*/
 /*--- of iropt and instrumenters.                         ---*/
@@ -2757,11 +2868,11 @@ VexGuestLayout
 
           /* Describe any sections to be regarded by Memcheck as
              'always-defined'. */
-          .n_alwaysDefd = 16,
+          .n_alwaysDefd = 18,
+
           /* flags thunk: OP and NDEP are always defd, whereas DEP1
              and DEP2 have to be tracked.  See detailed comment in
              gdefs.h on meaning of thunk fields. */
-
           .alwaysDefd 
              = { /*  0 */ ALWAYSDEFD(guest_CC_OP),
                  /*  1 */ ALWAYSDEFD(guest_CC_NDEP),
@@ -2777,8 +2888,10 @@ VexGuestLayout
                  /* 11 */ ALWAYSDEFD(guest_ES),
                  /* 12 */ ALWAYSDEFD(guest_FS),
                  /* 13 */ ALWAYSDEFD(guest_GS),
-                /* 14 */ ALWAYSDEFD(guest_SS),
-                /* 15 */ ALWAYSDEFD(guest_EMWARN)
+                 /* 14 */ ALWAYSDEFD(guest_SS),
+                 /* 15 */ ALWAYSDEFD(guest_LDT),
+                 /* 16 */ ALWAYSDEFD(guest_GDT),
+                 /* 17 */ ALWAYSDEFD(guest_EMWARN)
                }
         };
 
index a67c8d3355d4c5f6afa8576e40cc9985cecdddaa..b1217c61e738d9fc5ea3ac8800fa4b2ceefd5bd5 100644 (file)
@@ -133,6 +133,8 @@ static IRBB* irbb;
 #define OFFB_FS        offsetof(VexGuestX86State,guest_FS)
 #define OFFB_GS        offsetof(VexGuestX86State,guest_GS)
 #define OFFB_SS        offsetof(VexGuestX86State,guest_SS)
+#define OFFB_LDT       offsetof(VexGuestX86State,guest_LDT)
+#define OFFB_GDT       offsetof(VexGuestX86State,guest_GDT)
 
 #define OFFB_SSEROUND  offsetof(VexGuestX86State,guest_SSEROUND)
 #define OFFB_XMM0      offsetof(VexGuestX86State,guest_XMM0)
@@ -1546,14 +1548,14 @@ UChar* sorbTxt ( UChar sorb )
 static
 IRExpr* handleSegOverride ( UChar sorb, IRExpr* virtual )
 {
-  //IRTemp tsreg; //Int sreg, tsreg;
+   Int    sreg;
+   IRType hWordTy;
+   IRTemp ldt_ptr, gdt_ptr, seg_selector, r64;
 
    if (sorb == 0)
       /* the common case - no override */
       return virtual;
 
-   unimplemented("vex x86->IR: segment override prefix");
-#if 0
    switch (sorb) {
       case 0x3E: sreg = R_DS; break;
       case 0x26: sreg = R_ES; break;
@@ -1562,18 +1564,35 @@ IRExpr* handleSegOverride ( UChar sorb, IRExpr* virtual )
       default: vpanic("handleSegOverride(x86,guest)");
    }
 
-   tsreg = newTemp(Ity_I32);
+   hWordTy = sizeof(HWord)==4 ? Ity_I32 : Ity_I64;
 
-   /* tsreg becomes the relevant LDT descriptor */
-   assign( tsreg, unop(Iop_16Uto32, getSReg(sreg)) );
+   seg_selector = newTemp(Ity_I32);
+   ldt_ptr      = newTemp(hWordTy);
+   gdt_ptr      = newTemp(hWordTy);
+   r64          = newTemp(Ity_I64);
 
-   /* virtual += segment_base(ldt[tsreg]); also do limit check */
-return 
-  binop(Iop_Add32, mkexpr(virtual),
+   assign( seg_selector, unop(Iop_16Uto32, getSReg(sreg)) );
+   assign( ldt_ptr, IRExpr_Get( OFFB_LDT, hWordTy ));
+   assign( gdt_ptr, IRExpr_Get( OFFB_GDT, hWordTy ));
 
+   /*
+   Call this to do the translation and limit checks: 
+   ULong x86g_use_seg_selector ( HWord ldt, HWord gdt,
+                                 UInt seg_selector, UInt virtual_addr )
+   */
+   assign( 
+      r64, 
+      mkIRExprCCall( 
+         Ity_I64, 
+         0/*regparms*/, 
+         "x86g_use_seg_selector", 
+         &x86g_use_seg_selector, 
+         mkIRExprVec_4( mkexpr(ldt_ptr), mkexpr(gdt_ptr), 
+                        mkexpr(seg_selector), virtual)
+      )
+   );
 
-   uInstr2(cb, USESEG, 0, TempReg, tsreg, TempReg, tmp );
-#endif
+   return unop(Iop_64to32, mkexpr(r64));
 }
 
 
@@ -3918,8 +3937,8 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta )
                DIP("fldcw %s", dis_buf);
                assign( t64, mkIRExprCCall(
                                Ity_I64, 0/*regparms*/, 
-                               "x86h_check_fldcw",
-                               &x86h_check_fldcw, 
+                               "x86g_check_fldcw",
+                               &x86g_check_fldcw, 
                                mkIRExprVec_1( 
                                   unop( Iop_16Uto32, 
                                         loadLE(Ity_I16, mkexpr(addr)))
@@ -3994,7 +4013,7 @@ UInt dis_FPU ( Bool* decode_ok, UChar sorb, UInt delta )
                   unop( Iop_32to16, 
                         mkIRExprCCall(
                            Ity_I32, 0/*regp*/,
-                           "x86h_create_fpucw", &x86h_create_fpucw, 
+                           "x86g_create_fpucw", &x86g_create_fpucw, 
                            mkIRExprVec_1( get_fpround() ) 
                         ) 
                   ) 
@@ -7287,8 +7306,8 @@ static DisResult disInstr ( /*IN*/  Bool    resteerOK,
       /* ULong x86h_check_ldmxcsr ( UInt ); */
       assign( t64, mkIRExprCCall(
                       Ity_I64, 0/*regparms*/, 
-                      "x86h_check_ldmxcsr",
-                      &x86h_check_ldmxcsr, 
+                      "x86g_check_ldmxcsr",
+                      &x86g_check_ldmxcsr, 
                       mkIRExprVec_1( loadLE(Ity_I32, mkexpr(addr)) )
                    )
             );
@@ -7916,7 +7935,7 @@ static DisResult disInstr ( /*IN*/  Bool    resteerOK,
       storeLE( mkexpr(addr), 
                mkIRExprCCall(
                   Ity_I32, 0/*regp*/,
-                  "x86h_create_mxcsr", &x86h_create_mxcsr, 
+                  "x86g_create_mxcsr", &x86g_create_mxcsr, 
                   mkIRExprVec_1( get_fpround() ) 
                ) 
              );
index e94f5214b4a45051d949110108ee2a8a6845d7d2..ea5a85cf0bb0b3269cc8ef21d2b33e77710fa7fc 100644 (file)
@@ -121,7 +121,7 @@ extern void* LibVEX_Alloc ( Int nbytes );
 
 /* The max number of guest state chunks which we can describe as
    always defined (for the benefit of Memcheck). */
-#define VEXGLO_N_ALWAYSDEFD  16
+#define VEXGLO_N_ALWAYSDEFD  18
 
 typedef
    struct {
index ac95b045aa479c50fbfddf806aba7ae9f848805f..6ae6e6e458c234346d9a1d7adc89b0a14b5a5e05 100644 (file)
    0x1F80, 0x3F80, 0x5F80 or 0x7F80.  Vex will emit an emulation
    warning if you try and load a control word which either (1) unmasks
    any exceptions, (2) sets FZ (flush-to-zero) to 1, or (3) sets DAZ
-   (denormals-are-zeroes) to 1. */
-
+   (denormals-are-zeroes) to 1. 
+
+   Segments: initial prefixes of local and global segment descriptor
+   tables are modelled.  guest_LDT is either zero (NULL) or points in
+   the host address space to an array of VEX_GUEST_X86_LDT_NENT
+   descriptors, which have the type VexGuestX86SegDescr, defined
+   below.  Similarly, guest_GDT is either zero or points in the host
+   address space to an array of VEX_GUEST_X86_GDT_NENT descriptors.
+   The only place where these are used are in the helper function
+   x86g_use_seg().  LibVEX's client is responsible for pointing
+   guest_LDT and guest_GDT at suitable tables.  The contents of these
+   tables are expected not to change during the execution of any given
+   superblock, but they may validly be changed by LibVEX's client in
+   between superblock executions.
+
+   Since x86g_use_seg() only expects these tables to have
+   VEX_GUEST_X86_{LDT,GDT}_NENT entries, LibVEX's client should not
+   attempt to write entries beyond those limits.
+*/
 typedef
    struct {
       UInt  guest_EAX;         /* 0 */
@@ -166,6 +183,9 @@ typedef
       UShort guest_FS;
       UShort guest_GS;
       UShort guest_SS;
+      /* LDT/GDT stuff. */
+      HWord  guest_LDT; /* host addr, a VexGuestX86SegDescr* */
+      HWord  guest_GDT; /* host addr, a VexGuestX86SegDescr* */
       /* Emulation warnings */
       UInt   guest_EMWARN;
       /* Padding to make it have an 8-aligned size */
@@ -173,6 +193,42 @@ typedef
    }
    VexGuestX86State;
 
+#define VEX_GUEST_X86_LDT_NENT 64
+#define VEX_GUEST_X86_GDT_NENT 16
+
+
+/*---------------------------------------------------------------*/
+/*--- Types for x86 guest stuff.                              ---*/
+/*---------------------------------------------------------------*/
+
+/* VISIBLE TO LIBRARY CLIENT */
+
+/* This is the hardware-format for a segment descriptor, ie what the
+   x86 actually deals with.  It is 8 bytes long.  It's ugly. */
+
+typedef struct {
+    union {
+       struct {
+          UShort  LimitLow;
+          UShort  BaseLow;
+          UInt    BaseMid         : 8;
+          UInt    Type            : 5;
+          UInt    Dpl             : 2;
+          UInt    Pres            : 1;
+          UInt    LimitHi         : 4;
+          UInt    Sys             : 1;
+          UInt    Reserved_0      : 1;
+          UInt    Default_Big     : 1;
+          UInt    Granularity     : 1;
+          UInt    BaseHi          : 8;
+       } Bits;
+       struct {
+          UInt word1;
+          UInt word2;
+       } Words;
+    }
+    LdtEnt;
+} VexGuestX86SegDescr;
 
 
 /*---------------------------------------------------------------*/