x86 guest: simulate LDT/GDT enough that code using segment override

prefixes can work.



git-svn-id: svn://svn.valgrind.org/vex/trunk@650
This commit is contained in:
Julian Seward
2004-12-13 10:48:19 +00:00
parent b69b1851ee
commit 106fd4b1f7
5 changed files with 237 additions and 39 deletions

View 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 --- */

View 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)
}
};

View 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() )
)
);

View 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 {

View File

@@ -120,8 +120,25 @@
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;
/*---------------------------------------------------------------*/