From: Julian Seward Date: Mon, 13 Dec 2004 10:52:08 +0000 (+0000) Subject: Split up the old x86 LDT/GDT handling stuff. Push some of it into X-Git-Tag: svn/VALGRIND_3_0_0~1125 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bf737faf0c5ac3a368f93e143adb97799b5f55fe;p=thirdparty%2Fvalgrind.git Split up the old x86 LDT/GDT handling stuff. Push some of it into VEX, and clean up the rest of it and push it into coregrind/x86/state.c. coregrind/x86-linux/ldt.c is now empty, but I see that some of the stuff in x86/state.c should really be in x86-linux/ldt.c. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@3193 --- diff --git a/coregrind/core.h b/coregrind/core.h index de9b576220..b1bc0e3be3 100644 --- a/coregrind/core.h +++ b/coregrind/core.h @@ -1810,7 +1810,6 @@ extern void /*MOD*/ ThreadArchState* arch ); // Thread stuff -extern void VGA_(clear_thread) ( ThreadArchState* ); extern void VGA_(cleanup_thread) ( ThreadArchState* ); extern void VGA_(setup_child) ( ThreadArchState*, ThreadArchState* ); diff --git a/coregrind/vg_scheduler.c b/coregrind/vg_scheduler.c index dc26df820d..16145551e3 100644 --- a/coregrind/vg_scheduler.c +++ b/coregrind/vg_scheduler.c @@ -354,7 +354,7 @@ static void mostly_clear_thread_record ( ThreadId tid ) { vg_assert(tid >= 0 && tid < VG_N_THREADS); - VGA_(clear_thread)(&VG_(threads)[tid].arch); + VGA_(cleanup_thread)(&VG_(threads)[tid].arch); VG_(threads)[tid].tid = tid; VG_(threads)[tid].status = VgTs_Empty; VG_(threads)[tid].associated_mx = NULL; diff --git a/coregrind/x86-linux/ldt.c b/coregrind/x86-linux/ldt.c index 049db43a53..8ad9405720 100644 --- a/coregrind/x86-linux/ldt.c +++ b/coregrind/x86-linux/ldt.c @@ -83,411 +83,8 @@ am not sure what is and isn't allowed in user-mode. */ -#include "core.h" - - -/* Maximum number of LDT entries supported (by the x86). */ -#define VG_M_LDT_ENTRIES 8192 -/* The size of each LDT entry == sizeof(VgLdtEntry) */ -#define VG_LDT_ENTRY_SIZE 8 - -/* Allocate and deallocate LDTs for threads. */ - -/* Create an LDT. If the parent_ldt is NULL, zero out the - new one. If non-NULL, copy the parent. */ -VgLdtEntry* VG_(allocate_LDT_for_thread) ( VgLdtEntry* parent_ldt ) -{ - UInt nbytes, i; - VgLdtEntry* ldt; - - if (0) - VG_(printf)("allocate_LDT_for_thread: parent = %p\n", parent_ldt ); - vg_assert(VG_LDT_ENTRY_SIZE == sizeof(VgLdtEntry)); - nbytes = VG_M_LDT_ENTRIES * VG_LDT_ENTRY_SIZE; - - if (parent_ldt == NULL) { - /* Allocate a new zeroed-out one. */ - ldt = (VgLdtEntry*)VG_(arena_calloc)(VG_AR_CORE, VG_MIN_MALLOC_SZB, - nbytes, 1); - } else { - ldt = (VgLdtEntry*)VG_(arena_malloc)(VG_AR_CORE, nbytes); - for (i = 0; i < VG_M_LDT_ENTRIES; i++) - ldt[i] = parent_ldt[i]; - } - - return ldt; -} - -/* Free an LDT created by the above function. */ -void VG_(deallocate_LDT_for_thread) ( VgLdtEntry* ldt ) -{ - if (0) - VG_(printf)("deallocate_LDT_for_thread: ldt = %p\n", ldt ); - if (ldt != NULL) - VG_(arena_free)(VG_AR_CORE, ldt); -} - - - -/* Clear a TLS array. */ -void VG_(clear_TLS_for_thread) ( VgLdtEntry* tls ) -{ - VgLdtEntry* tlsp; - - if (0) - VG_(printf)("clear_TLS_for_thread\n" ); - - for (tlsp = tls; tlsp < tls + VKI_GDT_ENTRY_TLS_ENTRIES; tlsp++) { - tlsp->LdtEnt.Words.word1 = 0; - tlsp->LdtEnt.Words.word2 = 0; - } - - return; -} - - - -/* Fish the base field out of an VgLdtEntry. This is the only part we - are particularly interested in. */ - -static -void *wine_ldt_get_base( const VgLdtEntry *ent ) -{ - return (void *)(ent->LdtEnt.Bits.BaseLow | - ((unsigned long)ent->LdtEnt.Bits.BaseMid) << 16 | - ((unsigned long)ent->LdtEnt.Bits.BaseHi) << 24); -} - -static -unsigned int wine_ldt_get_limit( const VgLdtEntry *ent ) -{ - unsigned int limit = ent->LdtEnt.Bits.LimitLow - | (ent->LdtEnt.Bits.LimitHi << 16); - if (ent->LdtEnt.Bits.Granularity) limit = (limit << 12) | 0xfff; - return limit; -} - - -#if 0 -/* Actually _DO_ the segment translation. This is the whole entire - point of this accursed, overcomplicated, baroque, pointless - segment-override-and-LDT/GDT garbage foisted upon us all by Intel, - in its infinite wisdom. - - THIS IS CALLED FROM GENERATED CODE (AND SO RUNS ON REAL CPU). -*/ -Addr VG_(do_useseg) ( UInt seg_selector, Addr virtual_addr ) -{ - UInt table; - Addr base; - UInt limit; - - if (0) - VG_(printf)("do_useseg: seg_selector = %p, vaddr = %p\n", - seg_selector, virtual_addr); - - 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) { - VG_(synth_fault)(VG_(get_current_tid)()); - return 0; - } - - /* Extract the table number */ - table = (seg_selector & 4) >> 2; - - /* Convert the segment selector onto a table index */ - seg_selector >>= 3; - - if (table == 0) { - VgLdtEntry* the_tls; - - vg_assert(seg_selector >= VKI_GDT_ENTRY_TLS_MIN && seg_selector <= VKI_GDT_ENTRY_TLS_MAX); - - /* Come up with a suitable GDT entry. We look at the thread's TLS - array, which is pointed to by a VG_(baseBlock) entry. */ - the_tls = (VgLdtEntry*)VG_(baseBlock)[VGOFF_(tls_ptr)]; - base = (Addr)wine_ldt_get_base ( &the_tls[seg_selector-VKI_GDT_ENTRY_TLS_MIN] ); - limit = (UInt)wine_ldt_get_limit ( &the_tls[seg_selector-VKI_GDT_ENTRY_TLS_MIN] ); - } else { - VgLdtEntry* the_ldt; - - vg_assert(seg_selector >= 0 && seg_selector < 8192); - - /* Come up with a suitable LDT entry. We look at the thread's LDT, - which is pointed to by a VG_(baseBlock) entry. If null, we will - use an implied zero-entry -- although this usually implies the - program is in deep trouble, since it is using LDT entries which - it probably hasn't set up. */ - the_ldt = (VgLdtEntry*)VG_(baseBlock)[VGOFF_(ldt)]; - if (the_ldt == NULL) { - base = 0; - limit = 0; - VG_(message)( - Vg_UserMsg, - "Warning: segment-override prefix encountered, but thread has no LDT" - ); - } else { - base = (Addr)wine_ldt_get_base ( &the_ldt[seg_selector] ); - limit = (UInt)wine_ldt_get_limit ( &the_ldt[seg_selector] ); - } - } - - /* 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) { - VG_(message)( - Vg_UserMsg, - "Warning: segment access: virtual addr %d exceeds segment limit of %d", - virtual_addr, limit - ); - } - - if (0) - VG_(printf)("do_useseg: base = %p, addr = %p\n", - base, base + virtual_addr); - - return base + virtual_addr; -} -#endif - -/* Translate a struct modify_ldt_ldt_s to an VgLdtEntry, using the - Linux kernel's logic (cut-n-paste of code in linux/kernel/ldt.c). */ - -static -void translate_to_hw_format ( /* IN */ vki_modify_ldt_t* inn, - /* OUT */ VgLdtEntry* out, - Int oldmode ) -{ - UInt entry_1, entry_2; - - if (0) - VG_(printf)("translate_to_hw_format: base %p, limit %d\n", - inn->base_addr, inn->limit ); - - /* Allow LDTs to be cleared by the user. */ - if (inn->base_addr == 0 && inn->limit == 0) { - if (oldmode || - (inn->contents == 0 && - inn->read_exec_only == 1 && - inn->seg_32bit == 0 && - inn->limit_in_pages == 0 && - inn->seg_not_present == 1 && - inn->useable == 0 )) { - entry_1 = 0; - entry_2 = 0; - goto install; - } - } - - entry_1 = ((inn->base_addr & 0x0000ffff) << 16) | - (inn->limit & 0x0ffff); - entry_2 = (inn->base_addr & 0xff000000) | - ((inn->base_addr & 0x00ff0000) >> 16) | - (inn->limit & 0xf0000) | - ((inn->read_exec_only ^ 1) << 9) | - (inn->contents << 10) | - ((inn->seg_not_present ^ 1) << 15) | - (inn->seg_32bit << 22) | - (inn->limit_in_pages << 23) | - 0x7000; - if (!oldmode) - entry_2 |= (inn->useable << 20); - - /* Install the new entry ... */ - install: - out->LdtEnt.Words.word1 = entry_1; - out->LdtEnt.Words.word2 = entry_2; -} - - -/* - * linux/kernel/ldt.c - * - * Copyright (C) 1992 Krishna Balasubramanian and Linus Torvalds - * Copyright (C) 1999 Ingo Molnar - */ - -/* - * read_ldt() is not really atomic - this is not a problem since - * synchronization of reads and writes done to the LDT has to be - * assured by user-space anyway. Writes are atomic, to protect - * the security checks done on new descriptors. - */ -static -Int read_ldt ( ThreadId tid, UChar* ptr, UInt bytecount ) -{ - Int err; - UInt i, size; - Char* ldt; - - if (0) - VG_(printf)("read_ldt: tid = %d, ptr = %p, bytecount = %d\n", - tid, ptr, bytecount ); - - ldt = (Char*)(VG_(threads)[tid].arch.ldt); - err = 0; - if (ldt == NULL) - /* LDT not allocated, meaning all entries are null */ - goto out; - - size = VG_M_LDT_ENTRIES * VG_LDT_ENTRY_SIZE; - if (size > bytecount) - size = bytecount; - - err = size; - for (i = 0; i < size; i++) - ptr[i] = ldt[i]; - - out: - return err; -} - - -static -Int write_ldt ( ThreadId tid, void* ptr, UInt bytecount, Int oldmode ) -{ - Int error; - VgLdtEntry* ldt; - vki_modify_ldt_t* ldt_info; - - if (0) - VG_(printf)("write_ldt: tid = %d, ptr = %p, " - "bytecount = %d, oldmode = %d\n", - tid, ptr, bytecount, oldmode ); - - ldt = VG_(threads)[tid].arch.ldt; - ldt_info = (vki_modify_ldt_t*)ptr; - - error = -VKI_EINVAL; - if (bytecount != sizeof(vki_modify_ldt_t)) - goto out; - - error = -VKI_EINVAL; - if (ldt_info->entry_number >= VG_M_LDT_ENTRIES) - goto out; - if (ldt_info->contents == 3) { - if (oldmode) - goto out; - if (ldt_info->seg_not_present == 0) - goto out; - } - - /* If this thread doesn't have an LDT, we'd better allocate it - now. */ - if (ldt == NULL) { - ldt = VG_(allocate_LDT_for_thread)( NULL ); - VG_(threads)[tid].arch.ldt = ldt; - } - - /* Install the new entry ... */ - translate_to_hw_format ( ldt_info, &ldt[ldt_info->entry_number], oldmode ); - error = 0; - - out: - return error; -} - - -Int VG_(sys_modify_ldt) ( ThreadId tid, - Int func, void* ptr, UInt bytecount ) -{ - Int ret = -VKI_ENOSYS; - - switch (func) { - case 0: - ret = read_ldt(tid, ptr, bytecount); - break; - case 1: - ret = write_ldt(tid, ptr, bytecount, 1); - break; - case 2: - VG_(unimplemented)("sys_modify_ldt: func == 2"); - /* god knows what this is about */ - /* ret = read_default_ldt(ptr, bytecount); */ - /*UNREACHED*/ - break; - case 0x11: - ret = write_ldt(tid, ptr, bytecount, 0); - break; - } - return ret; -} - - -Int VG_(sys_set_thread_area) ( ThreadId tid, - vki_modify_ldt_t* info ) -{ - Int idx; - - if (info == NULL) - return -VKI_EFAULT; - - idx = info->entry_number; - - if (idx == -1) { - for (idx = 0; idx < VKI_GDT_ENTRY_TLS_ENTRIES; idx++) { - VgLdtEntry* tls = VG_(threads)[tid].arch.tls + idx; - - if (tls->LdtEnt.Words.word1 == 0 && tls->LdtEnt.Words.word2 == 0) - break; - } - - if (idx == VKI_GDT_ENTRY_TLS_ENTRIES) - return -VKI_ESRCH; - } else if (idx < VKI_GDT_ENTRY_TLS_MIN || idx > VKI_GDT_ENTRY_TLS_MAX) { - return -VKI_EINVAL; - } else { - idx = info->entry_number - VKI_GDT_ENTRY_TLS_MIN; - } - - translate_to_hw_format(info, VG_(threads)[tid].arch.tls + idx, 0); - - VG_TRACK( pre_mem_write, Vg_CoreSysCall, tid, - "set_thread_area(info->entry)", - (Addr) & info->entry_number, sizeof(unsigned int) ); - info->entry_number = idx + VKI_GDT_ENTRY_TLS_MIN; - VG_TRACK( post_mem_write, Vg_CoreSysCall, tid, - (Addr) & info->entry_number, sizeof(unsigned int) ); - - return 0; -} - - -Int VG_(sys_get_thread_area) ( ThreadId tid, - vki_modify_ldt_t* info ) -{ - Int idx; - VgLdtEntry* tls; - - if (info == NULL) - return -VKI_EFAULT; - - idx = info->entry_number; - - if (idx < VKI_GDT_ENTRY_TLS_MIN || idx > VKI_GDT_ENTRY_TLS_MAX) - return -VKI_EINVAL; - - tls = VG_(threads)[tid].arch.tls + idx - VKI_GDT_ENTRY_TLS_MIN; - - info->base_addr = ( tls->LdtEnt.Bits.BaseHi << 24 ) | - ( tls->LdtEnt.Bits.BaseMid << 16 ) | - tls->LdtEnt.Bits.BaseLow; - info->limit = ( tls->LdtEnt.Bits.LimitHi << 16 ) | - tls->LdtEnt.Bits.LimitLow; - info->seg_32bit = tls->LdtEnt.Bits.Default_Big; - info->contents = ( tls->LdtEnt.Bits.Type >> 2 ) & 0x3; - info->read_exec_only = ( tls->LdtEnt.Bits.Type & 0x1 ) ^ 0x1; - info->limit_in_pages = tls->LdtEnt.Bits.Granularity; - info->seg_not_present = tls->LdtEnt.Bits.Pres ^ 0x1; - info->useable = tls->LdtEnt.Bits.Sys; - info->reserved = 0; - - return 0; -} +/* 13 Dec 04: all this stuff has been moved into + coregrind/x86/state.c. */ /*--------------------------------------------------------------------*/ /*--- end ---*/ diff --git a/coregrind/x86/core_arch.h b/coregrind/x86/core_arch.h index 653e7920a4..b56ebbc6eb 100644 --- a/coregrind/x86/core_arch.h +++ b/coregrind/x86/core_arch.h @@ -87,7 +87,7 @@ /* This is the hardware-format for a segment descriptor, ie what the x86 actually deals with. It is 8 bytes long. It's ugly. */ - +#if 0 typedef struct _LDT_ENTRY { union { struct { @@ -111,7 +111,7 @@ typedef struct _LDT_ENTRY { } LdtEnt; } VgLdtEntry; - +#endif /* --------------------------------------------------------------------- Architecture-specific part of a ThreadState @@ -123,18 +123,6 @@ typedef struct _LDT_ENTRY { // also. typedef struct { - /* Pointer to this thread's Local (Segment) Descriptor Table. - Starts out as NULL, indicating there is no table, and we hope - to keep it that way. If the thread does __NR_modify_ldt to - create entries, we allocate a 8192-entry table at that point. - This is a straight copy of the Linux kernel's scheme. Don't - forget to deallocate this at thread exit. */ - VgLdtEntry* ldt; - - /* TLS table. This consists of a small number (currently 3) of - entries from the Global Descriptor Table. */ - VgLdtEntry tls[VKI_GDT_ENTRY_TLS_ENTRIES]; - /* --- BEGIN vex-mandated guest state --- */ /* Saved machine context. */ diff --git a/coregrind/x86/state.c b/coregrind/x86/state.c index 8bf701c2fb..15aad6b5cd 100644 --- a/coregrind/x86/state.c +++ b/coregrind/x86/state.c @@ -38,6 +38,18 @@ SSE/fxsave/fxrestor features. */ Bool VG_(have_ssestate); + +/* Global and Local descriptor tables for threads. + + See comments in libvex_guest_x86.h for LibVEX's model of x86 + segment descriptors. + + Mostly, threads never generate LDT (or GDT?) entries. Therefore, + we will initially start off with LDTs and GDTs being (HWord)NULL + and allocate them on demand. +*/ + + /*------------------------------------------------------------*/ /*--- Initialising the first thread ---*/ /*------------------------------------------------------------*/ @@ -93,41 +105,367 @@ void VGA_(init_thread1state) ( Addr client_eip, } +/*------------------------------------------------------------*/ +/*--- LDT/GDT stuff ---*/ +/*------------------------------------------------------------*/ + +/* Create a zeroed-out GDT. */ + +static VexGuestX86SegDescr* alloc_zeroed_GDT ( void ) +{ + Int nbytes + = VEX_GUEST_X86_GDT_NENT * sizeof(VexGuestX86SegDescr); + return + VG_(arena_calloc)(VG_AR_CORE, VG_MIN_MALLOC_SZB, nbytes, 1); +} + +/* Create a zeroed-out LDT. */ + +static VexGuestX86SegDescr* alloc_zeroed_LDT ( void ) +{ + Int nbytes + = VEX_GUEST_X86_LDT_NENT * sizeof(VexGuestX86SegDescr); + return + VG_(arena_calloc)(VG_AR_CORE, VG_MIN_MALLOC_SZB, nbytes, 1); +} + +/* Free up an LDT or GDT allocated by the above fns. */ + +static void free_LDT_or_GDT ( VexGuestX86SegDescr* dt ) +{ + vg_assert(dt); + VG_(arena_free)(VG_AR_CORE, (void*)dt); +} + +/* Copy contents between two existing LDTs. */ + +static void copy_LDT_from_to ( VexGuestX86SegDescr* src, + VexGuestX86SegDescr* dst ) +{ + Int i; + vg_assert(src); + vg_assert(dst); + for (i = 0; i < VEX_GUEST_X86_LDT_NENT; i++) + dst[i] = src[i]; +} + +/* Free this thread's DTs, if it has any. */ + +static void deallocate_LGDTs_for_thread ( VexGuestX86State* vex ) +{ + vg_assert(sizeof(HWord) == sizeof(void*)); + + if (0) + VG_(printf)("deallocate_LGDTs_for_thread: " + "ldt = 0x%x, gdt = 0x%x\n", + vex->guest_LDT, vex->guest_GDT ); + + if (vex->guest_LDT != (HWord)NULL) { + free_LDT_or_GDT( (VexGuestX86SegDescr*)vex->guest_LDT ); + vex->guest_LDT = (HWord)NULL; + } + + if (vex->guest_GDT != (HWord)NULL) { + free_LDT_or_GDT( (VexGuestX86SegDescr*)vex->guest_GDT ); + vex->guest_GDT = (HWord)NULL; + } +} + + +/* Translate a struct modify_ldt_ldt_s to a VexGuestX86SegDescr, using + the Linux kernel's logic (cut-n-paste of code in + linux/kernel/ldt.c). */ + +static +void translate_to_hw_format ( /* IN */ vki_modify_ldt_t* inn, + /* OUT */ VexGuestX86SegDescr* out, + Int oldmode ) +{ + UInt entry_1, entry_2; + vg_assert(8 == sizeof(VexGuestX86SegDescr)); + + if (0) + VG_(printf)("translate_to_hw_format: base %p, limit %d\n", + inn->base_addr, inn->limit ); + + /* Allow LDTs to be cleared by the user. */ + if (inn->base_addr == 0 && inn->limit == 0) { + if (oldmode || + (inn->contents == 0 && + inn->read_exec_only == 1 && + inn->seg_32bit == 0 && + inn->limit_in_pages == 0 && + inn->seg_not_present == 1 && + inn->useable == 0 )) { + entry_1 = 0; + entry_2 = 0; + goto install; + } + } + + entry_1 = ((inn->base_addr & 0x0000ffff) << 16) | + (inn->limit & 0x0ffff); + entry_2 = (inn->base_addr & 0xff000000) | + ((inn->base_addr & 0x00ff0000) >> 16) | + (inn->limit & 0xf0000) | + ((inn->read_exec_only ^ 1) << 9) | + (inn->contents << 10) | + ((inn->seg_not_present ^ 1) << 15) | + (inn->seg_32bit << 22) | + (inn->limit_in_pages << 23) | + 0x7000; + if (!oldmode) + entry_2 |= (inn->useable << 20); + + /* Install the new entry ... */ + install: + out->LdtEnt.Words.word1 = entry_1; + out->LdtEnt.Words.word2 = entry_2; +} + + +/* + * linux/kernel/ldt.c + * + * Copyright (C) 1992 Krishna Balasubramanian and Linus Torvalds + * Copyright (C) 1999 Ingo Molnar + */ + +/* + * read_ldt() is not really atomic - this is not a problem since + * synchronization of reads and writes done to the LDT has to be + * assured by user-space anyway. Writes are atomic, to protect + * the security checks done on new descriptors. + */ +static +Int read_ldt ( ThreadId tid, UChar* ptr, UInt bytecount ) +{ + Int err; + UInt i, size; + UChar* ldt; + + if (0) + VG_(printf)("read_ldt: tid = %d, ptr = %p, bytecount = %d\n", + tid, ptr, bytecount ); + + vg_assert(sizeof(HWord) == sizeof(VexGuestX86SegDescr*)); + vg_assert(8 == sizeof(VexGuestX86SegDescr)); + + ldt = (Char*)(VG_(threads)[tid].arch.vex.guest_LDT); + err = 0; + if (ldt == NULL) + /* LDT not allocated, meaning all entries are null */ + goto out; + + size = VEX_GUEST_X86_LDT_NENT * sizeof(VexGuestX86SegDescr); + if (size > bytecount) + size = bytecount; + + err = size; + for (i = 0; i < size; i++) + ptr[i] = ldt[i]; + + out: + return err; +} + + +static +Int write_ldt ( ThreadId tid, void* ptr, UInt bytecount, Int oldmode ) +{ + Int error; + VexGuestX86SegDescr* ldt; + vki_modify_ldt_t* ldt_info; + + if (0) + VG_(printf)("write_ldt: tid = %d, ptr = %p, " + "bytecount = %d, oldmode = %d\n", + tid, ptr, bytecount, oldmode ); + + vg_assert(8 == sizeof(VexGuestX86SegDescr)); + vg_assert(sizeof(HWord) == sizeof(VexGuestX86SegDescr*)); + + ldt = (VexGuestX86SegDescr*)VG_(threads)[tid].arch.vex.guest_LDT; + ldt_info = (vki_modify_ldt_t*)ptr; + + error = -VKI_EINVAL; + if (bytecount != sizeof(vki_modify_ldt_t)) + goto out; + + error = -VKI_EINVAL; + if (ldt_info->entry_number >= VEX_GUEST_X86_LDT_NENT) + goto out; + if (ldt_info->contents == 3) { + if (oldmode) + goto out; + if (ldt_info->seg_not_present == 0) + goto out; + } + + /* If this thread doesn't have an LDT, we'd better allocate it + now. */ + if (ldt == (HWord)NULL) { + ldt = alloc_zeroed_LDT(); + VG_(threads)[tid].arch.vex.guest_LDT = (HWord)ldt; + } + + /* Install the new entry ... */ + translate_to_hw_format ( ldt_info, &ldt[ldt_info->entry_number], oldmode ); + error = 0; + + out: + return error; +} + + +Int VG_(sys_modify_ldt) ( ThreadId tid, + Int func, void* ptr, UInt bytecount ) +{ + Int ret = -VKI_ENOSYS; + + switch (func) { + case 0: + ret = read_ldt(tid, ptr, bytecount); + break; + case 1: + ret = write_ldt(tid, ptr, bytecount, 1); + break; + case 2: + VG_(unimplemented)("sys_modify_ldt: func == 2"); + /* god knows what this is about */ + /* ret = read_default_ldt(ptr, bytecount); */ + /*UNREACHED*/ + break; + case 0x11: + ret = write_ldt(tid, ptr, bytecount, 0); + break; + } + return ret; +} + + +Int VG_(sys_set_thread_area) ( ThreadId tid, + vki_modify_ldt_t* info ) +{ + Int idx; + VexGuestX86SegDescr* gdt; + + vg_assert(8 == sizeof(VexGuestX86SegDescr)); + vg_assert(sizeof(HWord) == sizeof(VexGuestX86SegDescr*)); + + if (info == NULL) + return -VKI_EFAULT; + + gdt = (VexGuestX86SegDescr*)VG_(threads)[tid].arch.vex.guest_GDT; + + /* If the thread doesn't have a GDT, allocate it now. */ + if (!gdt) { + gdt = alloc_zeroed_GDT(); + VG_(threads)[tid].arch.vex.guest_GDT = (HWord)gdt; + } + + idx = info->entry_number; + + if (idx == -1) { + /* Find and use the first free entry. */ + for (idx = 0; idx < VEX_GUEST_X86_GDT_NENT; idx++) { + if (gdt[idx].LdtEnt.Words.word1 == 0 + && gdt[idx].LdtEnt.Words.word2 == 0) + break; + } + + if (idx == VEX_GUEST_X86_GDT_NENT) + return -VKI_ESRCH; + } else if (idx < 0 || idx >= VEX_GUEST_X86_GDT_NENT) { + return -VKI_EINVAL; + } + + translate_to_hw_format(info, &gdt[idx], 0); + + VG_TRACK( pre_mem_write, Vg_CoreSysCall, tid, + "set_thread_area(info->entry)", + (Addr) & info->entry_number, sizeof(unsigned int) ); + info->entry_number = idx; + VG_TRACK( post_mem_write, Vg_CoreSysCall, tid, + (Addr) & info->entry_number, sizeof(unsigned int) ); + + return 0; +} + + +Int VG_(sys_get_thread_area) ( ThreadId tid, + vki_modify_ldt_t* info ) +{ + Int idx; + VexGuestX86SegDescr* gdt; + + vg_assert(sizeof(HWord) == sizeof(VexGuestX86SegDescr*)); + vg_assert(8 == sizeof(VexGuestX86SegDescr)); + + if (info == NULL) + return -VKI_EFAULT; + + idx = info->entry_number; + + if (idx < 0 || idx >= VEX_GUEST_X86_GDT_NENT) + return -VKI_EINVAL; + + gdt = (VexGuestX86SegDescr*)VG_(threads)[tid].arch.vex.guest_GDT; + + /* If the thread doesn't have a GDT, allocate it now. */ + if (!gdt) { + gdt = alloc_zeroed_GDT(); + VG_(threads)[tid].arch.vex.guest_GDT = (HWord)gdt; + } + + info->base_addr = ( gdt[idx].LdtEnt.Bits.BaseHi << 24 ) | + ( gdt[idx].LdtEnt.Bits.BaseMid << 16 ) | + gdt[idx].LdtEnt.Bits.BaseLow; + info->limit = ( gdt[idx].LdtEnt.Bits.LimitHi << 16 ) | + gdt[idx].LdtEnt.Bits.LimitLow; + info->seg_32bit = gdt[idx].LdtEnt.Bits.Default_Big; + info->contents = ( gdt[idx].LdtEnt.Bits.Type >> 2 ) & 0x3; + info->read_exec_only = ( gdt[idx].LdtEnt.Bits.Type & 0x1 ) ^ 0x1; + info->limit_in_pages = gdt[idx].LdtEnt.Bits.Granularity; + info->seg_not_present = gdt[idx].LdtEnt.Bits.Pres ^ 0x1; + info->useable = gdt[idx].LdtEnt.Bits.Sys; + info->reserved = 0; + + return 0; +} + + /*------------------------------------------------------------*/ /*--- Thread stuff ---*/ /*------------------------------------------------------------*/ -void VGA_(clear_thread)( ThreadArchState *arch ) +void VGA_(cleanup_thread) ( ThreadArchState* arch ) { - arch->ldt = NULL; - VG_(clear_TLS_for_thread)(arch->tls); + /* Release arch-specific resources held by this thread. */ + /* On x86, we have to dump the LDT and GDT. */ + deallocate_LGDTs_for_thread( &arch->vex ); } -void VGA_(cleanup_thread) ( ThreadArchState *arch ) -{ - /* Deallocate its LDT, if it ever had one. */ - VG_(deallocate_LDT_for_thread)( arch->ldt ); - arch->ldt = NULL; - - /* Clear its TLS array. */ - VG_(clear_TLS_for_thread)( arch->tls ); -} -void VGA_(setup_child) ( ThreadArchState *arch, ThreadArchState *parent_arch ) -{ +void VGA_(setup_child) ( /*OUT*/ ThreadArchState *child, + /*IN*/ ThreadArchState *parent ) +{ /* We inherit our parent's LDT. */ - if (parent_arch->ldt == NULL) { + if (parent->vex.guest_LDT == (HWord)NULL) { /* We hope this is the common case. */ - arch->ldt = NULL; + child->vex.guest_LDT = (HWord)NULL; } else { /* No luck .. we have to take a copy of the parent's. */ - arch->ldt = VG_(allocate_LDT_for_thread)( parent_arch->ldt ); + child->vex.guest_LDT = (HWord)alloc_zeroed_LDT(); + copy_LDT_from_to( (VexGuestX86SegDescr*)parent->vex.guest_LDT, + (VexGuestX86SegDescr*)child->vex.guest_LDT ); } - /* Initialise the thread's TLS array */ - VG_(clear_TLS_for_thread)( arch->tls ); + /* We need an empty GDT. */ + child->vex.guest_GDT = (HWord)NULL; } + void VGA_(set_arg_and_bogus_ret)( ThreadId tid, UWord arg, Addr ret ) { /* Push the arg, and mark it as readable. */ @@ -143,6 +481,7 @@ void VGA_(set_arg_and_bogus_ret)( ThreadId tid, UWord arg, Addr ret ) * (UInt*)(VG_(threads)[tid].arch.vex.guest_ESP) = ret; } + void VGA_(thread_initial_stack)(ThreadId tid, UWord arg, Addr ret) { Addr esp = (Addr)STACK_PTR(VG_(threads)[tid].arch); @@ -162,7 +501,6 @@ void VGA_(thread_initial_stack)(ThreadId tid, UWord arg, Addr ret) VG_TRACK ( post_mem_write, Vg_CoreSignal, tid, esp, 2 * sizeof(UWord) ); } - /*------------------------------------------------------------*/ /*--- Symtab stuff ---*/ /*------------------------------------------------------------*/ diff --git a/coregrind/x86/x86_private.h b/coregrind/x86/x86_private.h index 2c78e8d134..c0846b7822 100644 --- a/coregrind/x86/x86_private.h +++ b/coregrind/x86/x86_private.h @@ -35,6 +35,8 @@ #include "x86_private_asm.h" // private arch-specific asm stuff #include "tool_arch.h" // arch-specific tool stuff +#include "libvex_guest_x86.h" // for VexGuestX86SegDescr + /* --------------------------------------------------------------------- Exports of state.c that are not core-visible ------------------------------------------------------------------ */ @@ -49,9 +51,12 @@ extern Bool VG_(have_ssestate); ------------------------------------------------------------------ */ /* Alloc & copy, and dealloc. */ -extern VgLdtEntry* VG_(allocate_LDT_for_thread) ( VgLdtEntry* parent_ldt ); -extern void VG_(deallocate_LDT_for_thread) ( VgLdtEntry* ldt ); -extern void VG_(clear_TLS_for_thread) ( VgLdtEntry* tls ); +extern VexGuestX86SegDescr* + VG_(allocate_LDT_for_thread) ( VexGuestX86SegDescr* parent_ldt ); +extern void + VG_(deallocate_LDT_for_thread) ( VexGuestX86SegDescr* ldt ); +extern void + VG_(clear_TLS_for_thread) ( VexGuestX86SegDescr* tls ); #endif // __X86_PRIVATE_H