mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-13 22:46:59 +00:00
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:
@@ -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 --- */
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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() )
|
||||
)
|
||||
);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
/*---------------------------------------------------------------*/
|
||||
|
||||
Reference in New Issue
Block a user