First half of the long-overdue support for segment override prefixes,
LDTs and __NR_modify_ldt.

- Each thread has its own LDT.  Usually NULL, but if we need to
  change an entry, it is allocated.  LDTs are inherited from parents
  as one would expect.

- We intercept __NR_modify_ldt and update the calling thread's LDT
  accordingly.  This is done in coregrind/vg_ldt.c.  The kernel
  never sees these syscalls.

- New architectural state for %cs, %ss, %ds, %es, %fs and %gs.
  Probably overkill including %cs and %ss.  These are saved and
  restored in the usual way, _except_ at syscalls -- there's no
  point, since we are hiding all LDT operations from the kernel now.
  This does assume that no syscall implicitly looks at the
  segment registers, but I think that's safe.

Still only halfway there.  JITter is still unaware of seg regs
and override prefixes.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@1133 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/vg_ldt.c b/coregrind/vg_ldt.c
new file mode 100644
index 0000000..a431ac9
--- /dev/null
+++ b/coregrind/vg_ldt.c
@@ -0,0 +1,249 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Simulation of Local Descriptor Tables               vg_ldt.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, an x86 protected-mode emulator 
+   designed for debugging and profiling binaries on x86-Unixes.
+
+   Copyright (C) 2000-2002 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.
+*/
+
+#include "vg_include.h"
+/* 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;
+
+   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 )
+{
+   VG_(printf)("deallocate_LDT_for_thread: ldt = %p\n", ldt );
+   if (ldt != NULL)
+      VG_(arena_free)(VG_AR_CORE, ldt);
+}
+
+
+
+/* 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);
+}
+
+#if 0
+inline static unsigned int wine_ldt_get_limit( const VgLdtEntry *ent )
+{
+    unsigned int limit = ent->LimitLow | (ent->HighWord.Bits.LimitHi << 16);
+    if (ent->HighWord.Bits.Granularity) limit = (limit << 12) | 0xfff;
+    return limit;
+}
+#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  */ struct vki_modify_ldt_ldt_s* inn,
+			      /* OUT */ VgLdtEntry* out,
+                                        Int oldmode )
+{
+   UInt entry_1, entry_2;
+
+   /* 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;
+   Char* ldt;
+
+   VG_(printf)("read_ldt: tid = %d, ptr = %p, bytecount = %d\n",
+               tid, ptr, bytecount );
+
+   ldt = (Char*)(VG_(threads)[tid].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;
+   struct vki_modify_ldt_ldt_s* ldt_info; 
+
+   VG_(printf)("write_ldt: tid = %d, ptr = %p, "
+               "bytecount = %d, oldmode = %d\n",
+               tid, ptr, bytecount, oldmode );
+
+   ldt      = VG_(threads)[tid].ldt;
+   ldt_info = (struct vki_modify_ldt_ldt_s*)ptr;
+
+   error = -VKI_EINVAL;
+   if (bytecount != sizeof(struct vki_modify_ldt_ldt_s))
+      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].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;
+}
+
+
+/*--------------------------------------------------------------------*/
+/*--- end                                                 vg_ldt.c ---*/
+/*--------------------------------------------------------------------*/