From: Julian Seward Date: Mon, 13 Dec 2004 13:14:05 +0000 (+0000) Subject: Move x86-TLS specific stuff out of x86/state.c, back into X-Git-Tag: svn/VALGRIND_3_0_0~1124 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9e2b15ef84d5ca74840909ead6c94e0a057cb989;p=thirdparty%2Fvalgrind.git Move x86-TLS specific stuff out of x86/state.c, back into x86-linux/tls.c. Also some other cleanups. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@3194 --- diff --git a/coregrind/x86-linux/ldt.c b/coregrind/x86-linux/ldt.c index 8ad9405720..a89aea5722 100644 --- a/coregrind/x86-linux/ldt.c +++ b/coregrind/x86-linux/ldt.c @@ -83,8 +83,273 @@ 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 + */ + +/* + * 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 ---*/ diff --git a/coregrind/x86/core_arch.h b/coregrind/x86/core_arch.h index b56ebbc6eb..62958f1a35 100644 --- a/coregrind/x86/core_arch.h +++ b/coregrind/x86/core_arch.h @@ -79,48 +79,13 @@ 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 --- */ diff --git a/coregrind/x86/state.c b/coregrind/x86/state.c index 15aad6b5cd..942acaa06f 100644 --- a/coregrind/x86/state.c +++ b/coregrind/x86/state.c @@ -111,7 +111,7 @@ void VGA_(init_thread1state) ( Addr client_eip, /* 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); @@ -121,7 +121,7 @@ static VexGuestX86SegDescr* alloc_zeroed_GDT ( void ) /* 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); @@ -172,269 +172,6 @@ static void deallocate_LGDTs_for_thread ( VexGuestX86State* vex ) } -/* 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 ---*/ /*------------------------------------------------------------*/ @@ -456,7 +193,7 @@ void VGA_(setup_child) ( /*OUT*/ ThreadArchState *child, 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 ); } diff --git a/coregrind/x86/x86_private.h b/coregrind/x86/x86_private.h index c0846b7822..7197f1f8dc 100644 --- a/coregrind/x86/x86_private.h +++ b/coregrind/x86/x86_private.h @@ -43,20 +43,13 @@ /* 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