From: Julian Seward Date: Mon, 30 Sep 2002 22:56:26 +0000 (+0000) Subject: Add big comment explaining how the LDT simulation works. X-Git-Tag: svn/VALGRIND_1_9_4~283 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=04a8657df52e1e54811bf5cf3d478e3bd4f9262b;p=thirdparty%2Fvalgrind.git Add big comment explaining how the LDT simulation works. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@1144 --- diff --git a/coregrind/vg_ldt.c b/coregrind/vg_ldt.c index 973bdde3b0..3f3b760965 100644 --- a/coregrind/vg_ldt.c +++ b/coregrind/vg_ldt.c @@ -28,6 +28,61 @@ 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,