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_scheduler.c b/coregrind/vg_scheduler.c
index b65426b..e6b8eea 100644
--- a/coregrind/vg_scheduler.c
+++ b/coregrind/vg_scheduler.c
@@ -398,6 +398,14 @@
Int i;
vg_assert(vg_tid_currently_in_baseBlock == VG_INVALID_THREADID);
+ VG_(baseBlock)[VGOFF_(ldt)] = (UInt)VG_(threads)[tid].ldt;
+ VG_(baseBlock)[VGOFF_(m_cs)] = VG_(threads)[tid].m_cs;
+ VG_(baseBlock)[VGOFF_(m_ss)] = VG_(threads)[tid].m_ss;
+ VG_(baseBlock)[VGOFF_(m_ds)] = VG_(threads)[tid].m_ds;
+ VG_(baseBlock)[VGOFF_(m_es)] = VG_(threads)[tid].m_es;
+ VG_(baseBlock)[VGOFF_(m_fs)] = VG_(threads)[tid].m_fs;
+ VG_(baseBlock)[VGOFF_(m_gs)] = VG_(threads)[tid].m_gs;
+
VG_(baseBlock)[VGOFF_(m_eax)] = VG_(threads)[tid].m_eax;
VG_(baseBlock)[VGOFF_(m_ebx)] = VG_(threads)[tid].m_ebx;
VG_(baseBlock)[VGOFF_(m_ecx)] = VG_(threads)[tid].m_ecx;
@@ -456,6 +464,21 @@
vg_assert(vg_tid_currently_in_baseBlock != VG_INVALID_THREADID);
+
+ /* We don't copy out the LDT entry, because it can never be changed
+ by the normal actions of the thread, only by the modify_ldt
+ syscall, in which case we will correctly be updating
+ VG_(threads)[tid].ldt. */
+ vg_assert((void*)VG_(threads)[tid].ldt
+ == (void*)VG_(baseBlock)[VGOFF_(ldt)]);
+
+ VG_(threads)[tid].m_cs = VG_(baseBlock)[VGOFF_(m_cs)];
+ VG_(threads)[tid].m_ss = VG_(baseBlock)[VGOFF_(m_ss)];
+ VG_(threads)[tid].m_ds = VG_(baseBlock)[VGOFF_(m_ds)];
+ VG_(threads)[tid].m_es = VG_(baseBlock)[VGOFF_(m_es)];
+ VG_(threads)[tid].m_fs = VG_(baseBlock)[VGOFF_(m_fs)];
+ VG_(threads)[tid].m_gs = VG_(baseBlock)[VGOFF_(m_gs)];
+
VG_(threads)[tid].m_eax = VG_(baseBlock)[VGOFF_(m_eax)];
VG_(threads)[tid].m_ebx = VG_(baseBlock)[VGOFF_(m_ebx)];
VG_(threads)[tid].m_ecx = VG_(baseBlock)[VGOFF_(m_ecx)];
@@ -494,6 +517,14 @@
}
/* Fill it up with junk. */
+ VG_(baseBlock)[VGOFF_(ldt)] = junk;
+ VG_(baseBlock)[VGOFF_(m_cs)] = junk;
+ VG_(baseBlock)[VGOFF_(m_ss)] = junk;
+ VG_(baseBlock)[VGOFF_(m_ds)] = junk;
+ VG_(baseBlock)[VGOFF_(m_es)] = junk;
+ VG_(baseBlock)[VGOFF_(m_fs)] = junk;
+ VG_(baseBlock)[VGOFF_(m_gs)] = junk;
+
VG_(baseBlock)[VGOFF_(m_eax)] = junk;
VG_(baseBlock)[VGOFF_(m_ebx)] = junk;
VG_(baseBlock)[VGOFF_(m_ecx)] = junk;
@@ -581,6 +612,7 @@
{
Int j;
vg_assert(tid >= 0 && tid < VG_N_THREADS);
+ VG_(threads)[tid].ldt = NULL;
VG_(threads)[tid].tid = tid;
VG_(threads)[tid].status = VgTs_Empty;
VG_(threads)[tid].associated_mx = NULL;
@@ -1659,6 +1691,10 @@
vg_waiting_fds[i].fd = -1; /* not in use */
}
}
+
+ /* Deallocate its LDT, if it ever had one. */
+ VG_(deallocate_LDT_for_thread)( VG_(threads)[tid].ldt );
+ VG_(threads)[tid].ldt = NULL;
}
@@ -2186,6 +2222,16 @@
VG_(threads)[tid].sig_mask = VG_(threads)[parent_tid].sig_mask;
VG_(ksigemptyset)(&VG_(threads)[tid].sigs_waited_for);
+ /* We inherit our parent's LDT. */
+ if (VG_(threads)[parent_tid].ldt == NULL) {
+ /* We hope this is the common case. */
+ VG_(threads)[tid].ldt = NULL;
+ } else {
+ /* No luck .. we have to take a copy of the parent's. */
+ VG_(threads)[tid].ldt
+ = VG_(allocate_LDT_for_thread)( VG_(threads)[parent_tid].ldt );
+ }
+
/* return child's tid to parent */
SET_EDX(parent_tid, tid); /* success */
}