x86-linux/tls.c. Also some other cleanups.
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@3194
am not sure what is and isn't allowed in user-mode.
*/
-/* 13 Dec 04: all this stuff has been moved into
- coregrind/x86/state.c. */
+#include "core.h"
+#include "x86_private.h"
+#include "libvex_guest_x86.h"
+
+
+/* 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 <mingo@redhat.com>
+ */
+
+/*
+ * 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 = VG_(alloc_zeroed_x86_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 = VG_(alloc_zeroed_x86_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 = VG_(alloc_zeroed_x86_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;
+}
+
/*--------------------------------------------------------------------*/
/*--- end ---*/
asm("movl %%ebp, %0" : "=r" (ebp)); \
} while (0)
-/* ---------------------------------------------------------------------
- LDT type
- ------------------------------------------------------------------ */
-
-// XXX: eventually this will be x86-private, not seen by the core(?)
-
-/* 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 {
- UShort LimitLow;
- UShort BaseLow;
- unsigned BaseMid : 8;
- unsigned Type : 5;
- unsigned Dpl : 2;
- unsigned Pres : 1;
- unsigned LimitHi : 4;
- unsigned Sys : 1;
- unsigned Reserved_0 : 1;
- unsigned Default_Big : 1;
- unsigned Granularity : 1;
- unsigned BaseHi : 8;
- } Bits;
- struct {
- UInt word1;
- UInt word2;
- } Words;
- }
- LdtEnt;
-} VgLdtEntry;
-#endif
-
/* ---------------------------------------------------------------------
Architecture-specific part of a ThreadState
------------------------------------------------------------------ */
// Architecture-specific part of a ThreadState
// XXX: eventually this should be made abstract, ie. the fields not visible
-// to the core... then VgLdtEntry can be made non-visible to the core
-// also.
+// to the core...
typedef
struct {
/* --- BEGIN vex-mandated guest state --- */
/* Create a zeroed-out GDT. */
-static VexGuestX86SegDescr* alloc_zeroed_GDT ( void )
+VexGuestX86SegDescr* VG_(alloc_zeroed_x86_GDT) ( void )
{
Int nbytes
= VEX_GUEST_X86_GDT_NENT * sizeof(VexGuestX86SegDescr);
/* Create a zeroed-out LDT. */
-static VexGuestX86SegDescr* alloc_zeroed_LDT ( void )
+VexGuestX86SegDescr* VG_(alloc_zeroed_x86_LDT) ( void )
{
Int nbytes
= VEX_GUEST_X86_LDT_NENT * sizeof(VexGuestX86SegDescr);
}
-/* 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 <mingo@redhat.com>
- */
-
-/*
- * 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 ---*/
/*------------------------------------------------------------*/
child->vex.guest_LDT = (HWord)NULL;
} else {
/* No luck .. we have to take a copy of the parent's. */
- child->vex.guest_LDT = (HWord)alloc_zeroed_LDT();
+ child->vex.guest_LDT = (HWord)VG_(alloc_zeroed_x86_LDT)();
copy_LDT_from_to( (VexGuestX86SegDescr*)parent->vex.guest_LDT,
(VexGuestX86SegDescr*)child->vex.guest_LDT );
}
/* Is this a SSE/SSE2-capable CPU? If so, we had better save/restore
the SSE state all over the place. This is set up very early, since we
- can't even correctly snapshot the startup machine state without it. */
+ can't even correctly snapshot the startup machine state without it.
+*/
extern Bool VG_(have_ssestate);
-/* ---------------------------------------------------------------------
- Exports of vg_ldt.c
- ------------------------------------------------------------------ */
-
-/* Alloc & copy, and dealloc. */
-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 );
+/* Create LDT/GDT arrays, as specified in libvex_guest_x86.h. */
+extern VexGuestX86SegDescr* VG_(alloc_zeroed_x86_GDT) ( void );
+extern VexGuestX86SegDescr* VG_(alloc_zeroed_x86_LDT) ( void );
#endif // __X86_PRIVATE_H