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. */
/* 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);
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. */
/* 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);
/* 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);
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++) {
# 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;
}
}
+/*----------------------------------------------*/
+/*--- 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. ---*/
/* 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),
/* 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)
}
};
#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)
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;
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));
}
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)))
unop( Iop_32to16,
mkIRExprCCall(
Ity_I32, 0/*regp*/,
- "x86h_create_fpucw", &x86h_create_fpucw,
+ "x86g_create_fpucw", &x86g_create_fpucw,
mkIRExprVec_1( get_fpround() )
)
)
/* 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)) )
)
);
storeLE( mkexpr(addr),
mkIRExprCCall(
Ity_I32, 0/*regp*/,
- "x86h_create_mxcsr", &x86h_create_mxcsr,
+ "x86g_create_mxcsr", &x86g_create_mxcsr,
mkIRExprVec_1( get_fpround() )
)
);
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 */
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 */
}
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;
/*---------------------------------------------------------------*/