From 106fd4b1f7d7cf58ea377cf0d6028b4d07cfd95e Mon Sep 17 00:00:00 2001 From: Julian Seward Date: Mon, 13 Dec 2004 10:48:19 +0000 Subject: [PATCH] x86 guest: simulate LDT/GDT enough that code using segment override prefixes can work. git-svn-id: svn://svn.valgrind.org/vex/trunk@650 --- VEX/priv/guest-x86/gdefs.h | 18 ++++- VEX/priv/guest-x86/ghelpers.c | 145 ++++++++++++++++++++++++++++++---- VEX/priv/guest-x86/toIR.c | 53 +++++++++---- VEX/pub/libvex.h | 2 +- VEX/pub/libvex_guest_x86.h | 58 +++++++++++++- 5 files changed, 237 insertions(+), 39 deletions(-) diff --git a/VEX/priv/guest-x86/gdefs.h b/VEX/priv/guest-x86/gdefs.h index 298e6539b..a220398e1 100644 --- a/VEX/priv/guest-x86/gdefs.h +++ b/VEX/priv/guest-x86/gdefs.h @@ -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 --- */ diff --git a/VEX/priv/guest-x86/ghelpers.c b/VEX/priv/guest-x86/ghelpers.c index 5b2140025..9041b0703 100644 --- a/VEX/priv/guest-x86/ghelpers.c +++ b/VEX/priv/guest-x86/ghelpers.c @@ -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) } }; diff --git a/VEX/priv/guest-x86/toIR.c b/VEX/priv/guest-x86/toIR.c index a67c8d335..b1217c61e 100644 --- a/VEX/priv/guest-x86/toIR.c +++ b/VEX/priv/guest-x86/toIR.c @@ -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() ) ) ); diff --git a/VEX/pub/libvex.h b/VEX/pub/libvex.h index e94f5214b..ea5a85cf0 100644 --- a/VEX/pub/libvex.h +++ b/VEX/pub/libvex.h @@ -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 { diff --git a/VEX/pub/libvex_guest_x86.h b/VEX/pub/libvex_guest_x86.h index ac95b045a..6ae6e6e45 100644 --- a/VEX/pub/libvex_guest_x86.h +++ b/VEX/pub/libvex_guest_x86.h @@ -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; /*---------------------------------------------------------------*/