]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Add big comment explaining how the LDT simulation works.
authorJulian Seward <jseward@acm.org>
Mon, 30 Sep 2002 22:56:26 +0000 (22:56 +0000)
committerJulian Seward <jseward@acm.org>
Mon, 30 Sep 2002 22:56:26 +0000 (22:56 +0000)
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@1144

coregrind/vg_ldt.c

index 973bdde3b0017c65ba41f55a913ec826fce2c50c..3f3b760965306db9d3faf786cfd34e0b91349d92 100644 (file)
    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 "vg_include.h"
 /* Allocate and deallocate LDTs for threads. */
 
@@ -133,6 +188,9 @@ Addr VG_(do_useseg) ( UInt seg_selector, Addr virtual_addr )
       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,