From: Nicholas Nethercote Date: Sun, 15 May 2005 20:52:04 +0000 (+0000) Subject: Improved structure of LDT-related code: X-Git-Tag: svn/VALGRIND_3_0_0~592 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=7a6ee2fc930351cd76525bfb088a18ef900df491;p=thirdparty%2Fvalgrind.git Improved structure of LDT-related code: - one declarations from core.h removed, one moved to within m_syscalls. - all the x86 LDT stuff made local to m_syscalls. x86-linux/ldt.c removed as a result. x86/state.c slimmed down, too. x86/x86_private.h removed too. - all the AMD64 LDT stuff was deleted, since it was all commented out. It can be added back in later in the appropriate places if necessary. Thus amd64-linux/ldt.c and amd64/amd64_private.h were removed. - other minor naming changes I hope I didn't break AMD64 compilation. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@3726 --- diff --git a/coregrind/amd64-linux/Makefile.am b/coregrind/amd64-linux/Makefile.am index d7d46e0320..a4daba551e 100644 --- a/coregrind/amd64-linux/Makefile.am +++ b/coregrind/amd64-linux/Makefile.am @@ -11,8 +11,7 @@ noinst_LIBRARIES = libplatform.a libplatform_a_SOURCES = \ - core_platform.c \ - ldt.c + core_platform.c if USE_PIE libplatform_a_CFLAGS = $(AM_CFLAGS) -fpie diff --git a/coregrind/amd64-linux/core_platform.h b/coregrind/amd64-linux/core_platform.h index ddf203c3b3..39dba1b331 100644 --- a/coregrind/amd64-linux/core_platform.h +++ b/coregrind/amd64-linux/core_platform.h @@ -35,29 +35,6 @@ //#include "core_platform_asm.h" // platform-specific asm stuff //#include "platform_arch.h" // platform-specific tool stuff -/* --------------------------------------------------------------------- - Exports of vg_ldt.c - ------------------------------------------------------------------ */ - -// XXX: eventually all these should be x86-private, and not visible to the -// core (except maybe do_useseg()?) - -#if 0 -/* Simulate the modify_ldt syscall. */ -extern Int VG_(sys_modify_ldt) ( ThreadId tid, - Int func, void* ptr, UInt bytecount ); - -/* Simulate the {get,set}_thread_area syscalls. */ -extern Int VG_(sys_set_thread_area) ( ThreadId tid, - vki_modify_ldt_t* info ); -extern Int VG_(sys_get_thread_area) ( ThreadId tid, - vki_modify_ldt_t* info ); - -/* Called from generated code. Given a segment selector and a virtual - address, return a linear address, and do limit checks too. */ -extern Addr VG_(do_useseg) ( UInt seg_selector, Addr virtual_addr ); -#endif - /* --------------------------------------------------------------------- ucontext stuff ------------------------------------------------------------------ */ diff --git a/coregrind/amd64-linux/ldt.c b/coregrind/amd64-linux/ldt.c deleted file mode 100644 index 60977d6574..0000000000 --- a/coregrind/amd64-linux/ldt.c +++ /dev/null @@ -1,495 +0,0 @@ - -/*--------------------------------------------------------------------*/ -/*--- Simulation of Local Descriptor Tables amd64-linux/ldt.c ---*/ -/*--------------------------------------------------------------------*/ - -/* - This file is part of Valgrind, a dynamic binary instrumentation - framework. - - Copyright (C) 2000-2005 Julian Seward - jseward@acm.org - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. - - The GNU General Public License is contained in the file COPYING. -*/ - -// XXX: this is copied straight from the x86 code... perhaps they should be -// shared. (Are AMD64 LDTs the same as x86 LDTs? Don't know. --njn) - -/* Details of the LDT simulation - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - When a program runs natively, the linux kernel allows each *thread* - in it to have its own LDT. Almost all programs never do this -- - it's wildly unportable, after all -- and so the kernel never - allocates the structure, which is just as well as an LDT occupies - 64k of memory (8192 entries of size 8 bytes). - - A thread may choose to modify its LDT entries, by doing the - __NR_modify_ldt syscall. In such a situation the kernel will then - allocate an LDT structure for it. Each LDT entry is basically a - (base, limit) pair. A virtual address in a specific segment is - translated to a linear address by adding the segment's base value. - In addition, the virtual address must not exceed the limit value. - - To use an LDT entry, a thread loads one of the segment registers - (%cs, %ss, %ds, %es, %fs, %gs) with the index of the LDT entry (0 - .. 8191) it wants to use. In fact, the required value is (index << - 3) + 7, but that's not important right now. Any normal instruction - which includes an addressing mode can then be made relative to that - LDT entry by prefixing the insn with a so-called segment-override - prefix, a byte which indicates which of the 6 segment registers - holds the LDT index. - - Now, a key constraint is that valgrind's address checks operate in - terms of linear addresses. So we have to explicitly translate - virtual addrs into linear addrs, and that means doing a complete - LDT simulation. - - Calls to modify_ldt are intercepted. For each thread, we maintain - an LDT (with the same normally-never-allocated optimisation that - the kernel does). This is updated as expected via calls to - modify_ldt. - - When a thread does an amode calculation involving a segment - override prefix, the relevant LDT entry for the thread is - consulted. It all works. - - There is a conceptual problem, which appears when switching back to - native execution, either temporarily to pass syscalls to the - kernel, or permanently, when debugging V. Problem at such points - is that it's pretty pointless to copy the simulated machine's - segment registers to the real machine, because we'd also need to - copy the simulated LDT into the real one, and that's prohibitively - expensive. - - Fortunately it looks like no syscalls rely on the segment regs or - LDT being correct, so we can get away with it. Apart from that the - simulation is pretty straightforward. All 6 segment registers are - tracked, although only %ds, %es, %fs and %gs are allowed as - prefixes. Perhaps it could be restricted even more than that -- I - am not sure what is and isn't allowed in user-mode. -*/ - -#include "core.h" - -#if 0 -/* 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, 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; -} -#endif - -/*--------------------------------------------------------------------*/ -/*--- end ---*/ -/*--------------------------------------------------------------------*/ diff --git a/coregrind/amd64/Makefile.am b/coregrind/amd64/Makefile.am index 5baea3a643..7160e2cbfa 100644 --- a/coregrind/amd64/Makefile.am +++ b/coregrind/amd64/Makefile.am @@ -5,8 +5,7 @@ AM_CFLAGS = $(WERROR) -Winline -Wall -Wshadow -O -fomit-frame-pointer -g noinst_HEADERS = \ core_arch.h \ - core_arch_asm.h \ - amd64_private.h + core_arch_asm.h noinst_LIBRARIES = libarch.a diff --git a/coregrind/amd64/amd64_private.h b/coregrind/amd64/amd64_private.h deleted file mode 100644 index 83419db399..0000000000 --- a/coregrind/amd64/amd64_private.h +++ /dev/null @@ -1,46 +0,0 @@ - -/*--------------------------------------------------------------------*/ -/*--- Private arch-specific header. amd64/amd64_private.h ---*/ -/*--------------------------------------------------------------------*/ - -/* - This file is part of Valgrind, a dynamic binary instrumentation - framework. - - Copyright (C) 2000-2005 Nicholas Nethercote - njn@valgrind.org - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. - - The GNU General Public License is contained in the file COPYING. -*/ - -#ifndef __AMD64_PRIVATE_H -#define __AMD64_PRIVATE_H - -#include "core_arch_asm.h" // arch-specific asm stuff -#include "tool_arch.h" // arch-specific tool stuff - -/* --------------------------------------------------------------------- - Exports of state.c that are not core-visible - ------------------------------------------------------------------ */ - - -#endif // __AMD64_PRIVATE_H - -/*--------------------------------------------------------------------*/ -/*--- end ---*/ -/*--------------------------------------------------------------------*/ diff --git a/coregrind/amd64/state.c b/coregrind/amd64/state.c index ebee8f2651..fd874cd907 100644 --- a/coregrind/amd64/state.c +++ b/coregrind/amd64/state.c @@ -30,7 +30,6 @@ #include "core.h" #include "pub_core_tooliface.h" -#include "amd64_private.h" #include #include "libvex_guest_amd64.h" @@ -87,36 +86,6 @@ void VGA_(init_thread1state) ( Addr client_rip, /*--- Thread stuff ---*/ /*------------------------------------------------------------*/ -void VGA_(cleanup_thread) ( ThreadArchState *arch ) -{ - /* TODO: deallocate the thread's LDT / GDT ? */ -} - - -void VGA_(setup_child) ( /*OUT*/ ThreadArchState *child, - /*IN*/ ThreadArchState *parent ) -{ - /* We inherit our parent's guest state. */ - child->vex = parent->vex; - child->vex_shadow = parent->vex_shadow; -#if 0 - /* TODO: inherit the thread's LDT / GDT ? */ - /* We inherit our parent's LDT. */ - if (parent->vex.guest_LDT == (HWord)NULL) { - /* We hope this is the common case. */ - child->vex.guest_LDT = (HWord)NULL; - } else { - /* No luck .. we have to take a copy of the parent's. */ - child->vex.guest_LDT = (HWord)VG_(alloc_zeroed_x86_LDT)(); - copy_LDT_from_to( (VexGuestX86SegDescr*)parent->vex.guest_LDT, - (VexGuestX86SegDescr*)child->vex.guest_LDT ); - } - - /* We need an empty GDT. */ - child->vex.guest_GDT = (HWord)NULL; -#endif -} - void VGA_(mark_from_registers)(ThreadId tid, void (*marker)(Addr)) { ThreadState *tst = VG_(get_ThreadState)(tid); diff --git a/coregrind/core.h b/coregrind/core.h index f10abe39e9..07948b77e5 100644 --- a/coregrind/core.h +++ b/coregrind/core.h @@ -805,10 +805,6 @@ extern void Addr esp_at_startup, /*MOD*/ ThreadArchState* arch ); -// Thread stuff -extern void VGA_(cleanup_thread) ( ThreadArchState* ); -extern void VGA_(setup_child) ( ThreadArchState*, ThreadArchState* ); - // OS/Platform-specific thread clear (after thread exit) extern void VGA_(os_state_clear)(ThreadState *); diff --git a/coregrind/m_syscalls/syscalls-amd64-linux.c b/coregrind/m_syscalls/syscalls-amd64-linux.c index 8e4343dbd6..cf92678fc4 100644 --- a/coregrind/m_syscalls/syscalls-amd64-linux.c +++ b/coregrind/m_syscalls/syscalls-amd64-linux.c @@ -45,7 +45,7 @@ /* --------------------------------------------------------------------- - Stacks, thread wrappers, clone + Stacks, thread wrappers Note. Why is this stuff here? ------------------------------------------------------------------ */ @@ -107,7 +107,7 @@ static void restart_syscall(ThreadArchState *arch) get called multiple times. */ /* NB: this is identical to the x86 version */ -void VGA_(interrupted_syscall)(ThreadId tid, +void VGP_(interrupted_syscall)(ThreadId tid, struct vki_ucontext *uc, Bool restart) { @@ -301,9 +301,14 @@ static Int start_thread(void *arg) VG_(core_panic)("Thread exit failed?\n"); } -/* +/* --------------------------------------------------------------------- clone() handling + ------------------------------------------------------------------ */ +// forward declaration +static void setup_child ( ThreadArchState*, ThreadArchState* ); + +/* When a client clones, we need to keep track of the new thread. This means: 1. allocate a ThreadId+ThreadState+stack for the the thread @@ -348,7 +353,7 @@ static Int do_clone(ThreadId ptid, If the clone call specifies a NULL rsp for the new thread, then it actually gets a copy of the parent's rsp. */ - VGA_(setup_child)( &ctst->arch, &ptst->arch ); + setup_child( &ctst->arch, &ptst->arch ); VGP_SET_SYSCALL_RESULT(ctst->arch, 0); if (rsp != 0) @@ -401,7 +406,7 @@ static Int do_clone(ThreadId ptid, if (ret < 0) { /* clone failed */ - VGA_(cleanup_thread)(&ctst->arch); + VGP_(cleanup_thread)(&ctst->arch); ctst->status = VgTs_Empty; } @@ -450,6 +455,22 @@ static Int do_fork_clone(ThreadId tid, UInt flags, Addr rsp, Int *parent_tidptr, return ret; } +/* --------------------------------------------------------------------- + More thread stuff + ------------------------------------------------------------------ */ + +void VGP_(cleanup_thread) ( ThreadArchState *arch ) +{ +} + +void setup_child ( /*OUT*/ ThreadArchState *child, + /*IN*/ ThreadArchState *parent ) +{ + /* We inherit our parent's guest state. */ + child->vex = parent->vex; + child->vex_shadow = parent->vex_shadow; +} + /* --------------------------------------------------------------------- PRE/POST wrappers for AMD64/Linux-specific syscalls ------------------------------------------------------------------ */ diff --git a/coregrind/m_syscalls/syscalls-x86-linux.c b/coregrind/m_syscalls/syscalls-x86-linux.c index 8cfdafdb36..9f2dbedebc 100644 --- a/coregrind/m_syscalls/syscalls-x86-linux.c +++ b/coregrind/m_syscalls/syscalls-x86-linux.c @@ -43,7 +43,7 @@ /* --------------------------------------------------------------------- - Stacks, thread wrappers, clone + Stacks, thread wrappers Note. Why is this stuff here? ------------------------------------------------------------------ */ @@ -105,7 +105,7 @@ static void restart_syscall(ThreadArchState *arch) get called multiple times. */ /* NB: this is identical to the amd64 version */ -void VGA_(interrupted_syscall)(ThreadId tid, +void VGP_(interrupted_syscall)(ThreadId tid, struct vki_ucontext *uc, Bool restart) { @@ -299,9 +299,15 @@ static Int start_thread(void *arg) VG_(core_panic)("Thread exit failed?\n"); } -/* +/* --------------------------------------------------------------------- clone() handling + ------------------------------------------------------------------ */ + +// forward declarations +static void setup_child ( ThreadArchState*, ThreadArchState* ); +static Int sys_set_thread_area ( ThreadId, vki_modify_ldt_t* ); +/* When a client clones, we need to keep track of the new thread. This means: 1. allocate a ThreadId+ThreadState+stack for the the thread @@ -346,7 +352,7 @@ static Int do_clone(ThreadId ptid, If the clone call specifies a NULL esp for the new thread, then it actually gets a copy of the parent's esp. */ - VGA_(setup_child)( &ctst->arch, &ptst->arch ); + setup_child( &ctst->arch, &ptst->arch ); VGP_SET_SYSCALL_RESULT(ctst->arch, 0); if (esp != 0) @@ -386,7 +392,7 @@ static Int do_clone(ThreadId ptid, tlsinfo, tlsinfo->entry_number, tlsinfo->base_addr, tlsinfo->limit, ptst->arch.vex.guest_ESP, ctst->arch.vex.guest_FS, ctst->arch.vex.guest_GS); - ret = VG_(sys_set_thread_area)(ctid, tlsinfo); + ret = sys_set_thread_area(ctid, tlsinfo); if (ret != 0) goto out; @@ -406,7 +412,7 @@ static Int do_clone(ThreadId ptid, out: if (ret < 0) { /* clone failed */ - VGA_(cleanup_thread)(&ctst->arch); + VGP_(cleanup_thread)(&ctst->arch); ctst->status = VgTs_Empty; } @@ -455,6 +461,411 @@ static Int do_fork_clone(ThreadId tid, UInt flags, Addr esp, Int *parent_tidptr, return ret; } +/* --------------------------------------------------------------------- + LDT/GDT simulation + ------------------------------------------------------------------ */ + +/* Details of the LDT simulation + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + When a program runs natively, the linux kernel allows each *thread* + in it to have its own LDT. Almost all programs never do this -- + it's wildly unportable, after all -- and so the kernel never + allocates the structure, which is just as well as an LDT occupies + 64k of memory (8192 entries of size 8 bytes). + + A thread may choose to modify its LDT entries, by doing the + __NR_modify_ldt syscall. In such a situation the kernel will then + allocate an LDT structure for it. Each LDT entry is basically a + (base, limit) pair. A virtual address in a specific segment is + translated to a linear address by adding the segment's base value. + In addition, the virtual address must not exceed the limit value. + + To use an LDT entry, a thread loads one of the segment registers + (%cs, %ss, %ds, %es, %fs, %gs) with the index of the LDT entry (0 + .. 8191) it wants to use. In fact, the required value is (index << + 3) + 7, but that's not important right now. Any normal instruction + which includes an addressing mode can then be made relative to that + LDT entry by prefixing the insn with a so-called segment-override + prefix, a byte which indicates which of the 6 segment registers + holds the LDT index. + + Now, a key constraint is that valgrind's address checks operate in + terms of linear addresses. So we have to explicitly translate + virtual addrs into linear addrs, and that means doing a complete + LDT simulation. + + Calls to modify_ldt are intercepted. For each thread, we maintain + an LDT (with the same normally-never-allocated optimisation that + the kernel does). This is updated as expected via calls to + modify_ldt. + + When a thread does an amode calculation involving a segment + override prefix, the relevant LDT entry for the thread is + consulted. It all works. + + There is a conceptual problem, which appears when switching back to + native execution, either temporarily to pass syscalls to the + kernel, or permanently, when debugging V. Problem at such points + is that it's pretty pointless to copy the simulated machine's + segment registers to the real machine, because we'd also need to + copy the simulated LDT into the real one, and that's prohibitively + expensive. + + Fortunately it looks like no syscalls rely on the segment regs or + LDT being correct, so we can get away with it. Apart from that the + simulation is pretty straightforward. All 6 segment registers are + tracked, although only %ds, %es, %fs and %gs are allowed as + prefixes. Perhaps it could be restricted even more than that -- I + am not sure what is and isn't allowed in user-mode. +*/ + +/* 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; +} + +/* Create a zeroed-out GDT. */ +static VexGuestX86SegDescr* alloc_zeroed_x86_GDT ( void ) +{ + Int nbytes = VEX_GUEST_X86_GDT_NENT * sizeof(VexGuestX86SegDescr); + return VG_(arena_calloc)(VG_AR_CORE, nbytes, 1); +} + +/* Create a zeroed-out LDT. */ +static VexGuestX86SegDescr* alloc_zeroed_x86_LDT ( void ) +{ + Int nbytes = VEX_GUEST_X86_LDT_NENT * sizeof(VexGuestX86SegDescr); + return VG_(arena_calloc)(VG_AR_CORE, 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; + } +} + + +/* + * 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_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; +} + + +static Int 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; +} + + +static Int 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_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; +} + + +static Int 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_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; +} + +/* --------------------------------------------------------------------- + More thread stuff + ------------------------------------------------------------------ */ + +void VGP_(cleanup_thread) ( ThreadArchState* arch ) +{ + /* Release arch-specific resources held by this thread. */ + /* On x86, we have to dump the LDT and GDT. */ + deallocate_LGDTs_for_thread( &arch->vex ); +} + + +static void setup_child ( /*OUT*/ ThreadArchState *child, + /*IN*/ ThreadArchState *parent ) +{ + /* We inherit our parent's guest state. */ + child->vex = parent->vex; + child->vex_shadow = parent->vex_shadow; + /* We inherit our parent's LDT. */ + if (parent->vex.guest_LDT == (HWord)NULL) { + /* We hope this is the common case. */ + 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_x86_LDT(); + copy_LDT_from_to( (VexGuestX86SegDescr*)parent->vex.guest_LDT, + (VexGuestX86SegDescr*)child->vex.guest_LDT ); + } + + /* We need an empty GDT. */ + child->vex.guest_GDT = (HWord)NULL; +} + /* --------------------------------------------------------------------- PRE/POST wrappers for x86/Linux-specific syscalls ------------------------------------------------------------------ */ @@ -640,7 +1051,7 @@ PRE(sys_modify_ldt, Special) PRE_MEM_READ( "modify_ldt(ptr)", ARG2, sizeof(vki_modify_ldt_t) ); } /* "do" the syscall ourselves; the kernel never sees it */ - SET_RESULT( VG_(sys_modify_ldt)( tid, ARG1, (void*)ARG2, ARG3 ) ); + SET_RESULT( sys_modify_ldt( tid, ARG1, (void*)ARG2, ARG3 ) ); if (ARG1 == 0 && !VG_(is_kerror)(RES) && RES > 0) { POST_MEM_WRITE( ARG2, RES ); @@ -654,7 +1065,7 @@ PRE(sys_set_thread_area, Special) PRE_MEM_READ( "set_thread_area(u_info)", ARG1, sizeof(vki_modify_ldt_t) ); /* "do" the syscall ourselves; the kernel never sees it */ - SET_RESULT( VG_(sys_set_thread_area)( tid, (void *)ARG1 ) ); + SET_RESULT( sys_set_thread_area( tid, (void *)ARG1 ) ); } PRE(sys_get_thread_area, Special) @@ -664,7 +1075,7 @@ PRE(sys_get_thread_area, Special) PRE_MEM_WRITE( "get_thread_area(u_info)", ARG1, sizeof(vki_modify_ldt_t) ); /* "do" the syscall ourselves; the kernel never sees it */ - SET_RESULT( VG_(sys_get_thread_area)( tid, (void *)ARG1 ) ); + SET_RESULT( sys_get_thread_area( tid, (void *)ARG1 ) ); if (!VG_(is_kerror)(RES)) { POST_MEM_WRITE( ARG1, sizeof(vki_modify_ldt_t) ); diff --git a/coregrind/pub_core_syscalls.h b/coregrind/pub_core_syscalls.h index 1830d8b6da..1df0faaf92 100644 --- a/coregrind/pub_core_syscalls.h +++ b/coregrind/pub_core_syscalls.h @@ -47,10 +47,13 @@ extern void VG_(post_syscall) ( ThreadId tid ); // interrupted with a signal. Returns True if the syscall completed // (either interrupted or finished normally), or False if it was // restarted (or the signal didn't actually interrupt a syscall). -extern void VGA_(interrupted_syscall)(ThreadId tid, +extern void VGP_(interrupted_syscall)(ThreadId tid, struct vki_ucontext *uc, Bool restart); +// Release resources held by this thread +extern void VGP_(cleanup_thread) ( ThreadArchState* ); + extern Bool VG_(is_kerror) ( Word res ); /* Internal atfork handlers */ diff --git a/coregrind/vg_scheduler.c b/coregrind/vg_scheduler.c index 2dba272435..c8f9edcd71 100644 --- a/coregrind/vg_scheduler.c +++ b/coregrind/vg_scheduler.c @@ -551,7 +551,7 @@ void mostly_clear_thread_record ( ThreadId tid ) vki_sigset_t savedmask; vg_assert(tid >= 0 && tid < VG_N_THREADS); - VGA_(cleanup_thread)(&VG_(threads)[tid].arch); + VGP_(cleanup_thread)(&VG_(threads)[tid].arch); VG_(threads)[tid].tid = tid; /* Leave the thread in Zombie, so that it doesn't get reallocated diff --git a/coregrind/vg_signals.c b/coregrind/vg_signals.c index 199eaca32c..24f7c09997 100644 --- a/coregrind/vg_signals.c +++ b/coregrind/vg_signals.c @@ -1651,7 +1651,7 @@ void async_signalhandler ( Int sigNo, vki_siginfo_t *info, struct vki_ucontext * sigNo, tid, info->si_code); /* Update thread state properly */ - VGA_(interrupted_syscall)(tid, uc, + VGP_(interrupted_syscall)(tid, uc, !!(scss.scss_per_sig[sigNo].scss_flags & VKI_SA_RESTART)); /* Set up the thread's state to deliver a signal */ diff --git a/coregrind/x86-linux/Makefile.am b/coregrind/x86-linux/Makefile.am index d4cf08496c..ca6ac2e4cd 100644 --- a/coregrind/x86-linux/Makefile.am +++ b/coregrind/x86-linux/Makefile.am @@ -11,8 +11,7 @@ noinst_LIBRARIES = libplatform.a libplatform_a_SOURCES = \ - core_platform.c \ - ldt.c + core_platform.c if USE_PIE libplatform_a_CFLAGS = $(AM_CFLAGS) -fpie diff --git a/coregrind/x86-linux/core_platform.h b/coregrind/x86-linux/core_platform.h index 91ea35a030..52de2c380b 100644 --- a/coregrind/x86-linux/core_platform.h +++ b/coregrind/x86-linux/core_platform.h @@ -35,27 +35,6 @@ //#include "core_platform_asm.h" // platform-specific asm stuff //#include "platform_arch.h" // platform-specific tool stuff -/* --------------------------------------------------------------------- - Exports of vg_ldt.c - ------------------------------------------------------------------ */ - -// XXX: eventually all these should be x86-private, and not visible to the -// core (except maybe do_useseg()?) - -/* Simulate the modify_ldt syscall. */ -extern Int VG_(sys_modify_ldt) ( ThreadId tid, - Int func, void* ptr, UInt bytecount ); - -/* Simulate the {get,set}_thread_area syscalls. */ -extern Int VG_(sys_set_thread_area) ( ThreadId tid, - vki_modify_ldt_t* info ); -extern Int VG_(sys_get_thread_area) ( ThreadId tid, - vki_modify_ldt_t* info ); - -/* Called from generated code. Given a segment selector and a virtual - address, return a linear address, and do limit checks too. */ -extern Addr VG_(do_useseg) ( UInt seg_selector, Addr virtual_addr ); - /* --------------------------------------------------------------------- ucontext stuff ------------------------------------------------------------------ */ diff --git a/coregrind/x86-linux/ldt.c b/coregrind/x86-linux/ldt.c deleted file mode 100644 index 968de80766..0000000000 --- a/coregrind/x86-linux/ldt.c +++ /dev/null @@ -1,357 +0,0 @@ - -/*--------------------------------------------------------------------*/ -/*--- Simulation of Local Descriptor Tables x86-linux/ldt.c ---*/ -/*--------------------------------------------------------------------*/ - -/* - This file is part of Valgrind, a dynamic binary instrumentation - framework. - - Copyright (C) 2000-2005 Julian Seward - jseward@acm.org - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. - - The GNU General Public License is contained in the file COPYING. -*/ - -/* Details of the LDT simulation - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - When a program runs natively, the linux kernel allows each *thread* - in it to have its own LDT. Almost all programs never do this -- - it's wildly unportable, after all -- and so the kernel never - allocates the structure, which is just as well as an LDT occupies - 64k of memory (8192 entries of size 8 bytes). - - A thread may choose to modify its LDT entries, by doing the - __NR_modify_ldt syscall. In such a situation the kernel will then - allocate an LDT structure for it. Each LDT entry is basically a - (base, limit) pair. A virtual address in a specific segment is - translated to a linear address by adding the segment's base value. - In addition, the virtual address must not exceed the limit value. - - To use an LDT entry, a thread loads one of the segment registers - (%cs, %ss, %ds, %es, %fs, %gs) with the index of the LDT entry (0 - .. 8191) it wants to use. In fact, the required value is (index << - 3) + 7, but that's not important right now. Any normal instruction - which includes an addressing mode can then be made relative to that - LDT entry by prefixing the insn with a so-called segment-override - prefix, a byte which indicates which of the 6 segment registers - holds the LDT index. - - Now, a key constraint is that valgrind's address checks operate in - terms of linear addresses. So we have to explicitly translate - virtual addrs into linear addrs, and that means doing a complete - LDT simulation. - - Calls to modify_ldt are intercepted. For each thread, we maintain - an LDT (with the same normally-never-allocated optimisation that - the kernel does). This is updated as expected via calls to - modify_ldt. - - When a thread does an amode calculation involving a segment - override prefix, the relevant LDT entry for the thread is - consulted. It all works. - - There is a conceptual problem, which appears when switching back to - native execution, either temporarily to pass syscalls to the - kernel, or permanently, when debugging V. Problem at such points - is that it's pretty pointless to copy the simulated machine's - segment registers to the real machine, because we'd also need to - copy the simulated LDT into the real one, and that's prohibitively - expensive. - - Fortunately it looks like no syscalls rely on the segment regs or - LDT being correct, so we can get away with it. Apart from that the - simulation is pretty straightforward. All 6 segment registers are - tracked, although only %ds, %es, %fs and %gs are allowed as - prefixes. Perhaps it could be restricted even more than that -- I - am not sure what is and isn't allowed in user-mode. -*/ - -#include "core.h" -#include "pub_core_tooliface.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/Makefile.am b/coregrind/x86/Makefile.am index 65d955233f..d5ccd3605e 100644 --- a/coregrind/x86/Makefile.am +++ b/coregrind/x86/Makefile.am @@ -5,8 +5,7 @@ AM_CFLAGS = $(WERROR) -Wmissing-prototypes -Winline -Wall -Wshadow -O -g noinst_HEADERS = \ core_arch.h \ - core_arch_asm.h \ - x86_private.h + core_arch_asm.h noinst_LIBRARIES = libarch.a diff --git a/coregrind/x86/state.c b/coregrind/x86/state.c index 19fbee73aa..ac5eaf0e3c 100644 --- a/coregrind/x86/state.c +++ b/coregrind/x86/state.c @@ -30,7 +30,6 @@ #include "core.h" #include "pub_core_tooliface.h" -#include "x86_private.h" #include "vki_unistd.h" #include @@ -141,104 +140,10 @@ void VGA_(init_thread1state) ( Addr client_eip, sizeof(VexGuestArchState)); } - -/*------------------------------------------------------------*/ -/*--- LDT/GDT stuff ---*/ -/*------------------------------------------------------------*/ - -/* Create a zeroed-out GDT. */ - -VexGuestX86SegDescr* VG_(alloc_zeroed_x86_GDT) ( void ) -{ - Int nbytes = VEX_GUEST_X86_GDT_NENT * sizeof(VexGuestX86SegDescr); - return VG_(arena_calloc)(VG_AR_CORE, nbytes, 1); -} - -/* Create a zeroed-out LDT. */ - -VexGuestX86SegDescr* VG_(alloc_zeroed_x86_LDT) ( void ) -{ - Int nbytes = VEX_GUEST_X86_LDT_NENT * sizeof(VexGuestX86SegDescr); - return VG_(arena_calloc)(VG_AR_CORE, 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; - } -} - - /*------------------------------------------------------------*/ /*--- Thread stuff ---*/ /*------------------------------------------------------------*/ -void VGA_(cleanup_thread) ( ThreadArchState* arch ) -{ - /* 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_(setup_child) ( /*OUT*/ ThreadArchState *child, - /*IN*/ ThreadArchState *parent ) -{ - /* We inherit our parent's guest state. */ - child->vex = parent->vex; - child->vex_shadow = parent->vex_shadow; - /* We inherit our parent's LDT. */ - if (parent->vex.guest_LDT == (HWord)NULL) { - /* We hope this is the common case. */ - child->vex.guest_LDT = (HWord)NULL; - } else { - /* No luck .. we have to take a copy of the parent's. */ - child->vex.guest_LDT = (HWord)VG_(alloc_zeroed_x86_LDT)(); - copy_LDT_from_to( (VexGuestX86SegDescr*)parent->vex.guest_LDT, - (VexGuestX86SegDescr*)child->vex.guest_LDT ); - } - - /* We need an empty GDT. */ - child->vex.guest_GDT = (HWord)NULL; -} - - void VGA_(mark_from_registers)(ThreadId tid, void (*marker)(Addr)) { ThreadState *tst = VG_(get_ThreadState)(tid); diff --git a/coregrind/x86/x86_private.h b/coregrind/x86/x86_private.h deleted file mode 100644 index 37297fe328..0000000000 --- a/coregrind/x86/x86_private.h +++ /dev/null @@ -1,52 +0,0 @@ - -/*--------------------------------------------------------------------*/ -/*--- Private arch-specific header. x86/x86_private.h ---*/ -/*--------------------------------------------------------------------*/ - -/* - This file is part of Valgrind, a dynamic binary instrumentation - framework. - - Copyright (C) 2000-2005 Nicholas Nethercote - njn@valgrind.org - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307, USA. - - The GNU General Public License is contained in the file COPYING. -*/ - -#ifndef __X86_PRIVATE_H -#define __X86_PRIVATE_H - -#include "core_arch_asm.h" // 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 - ------------------------------------------------------------------ */ - -/* 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 - -/*--------------------------------------------------------------------*/ -/*--- end ---*/ -/*--------------------------------------------------------------------*/