|
From: Jeremy F. <je...@go...> - 2004-01-21 01:28:04
|
CVS commit by fitzhardinge:
This change implements the TLS extension to the x86 ABI. This allows
threads to have thread-private data which is quickly accessible via a
segment in the GDT, stored in %gs. The patch implements the relevent
syscalls (setthreadarea), and also manages switching the VCPU's segment
information at thread context-switch time. Mostly Tom Hughes' work.
A none/tests/tls.c 1.1 [POSSIBLY UNSAFE: printf] [no copyright]
A none/tests/tls.stderr.exp 1.1
A none/tests/tls.stdout.exp 1.1
A none/tests/tls.vgtest 1.1
A none/tests/tls2.c 1.1 [no copyright]
A none/tests/tls2_so.c 1.1 [no copyright]
A none/tests/tls_so.c 1.1 [no copyright]
M +23 -0 glibc-2.3.supp 1.10
M +1 -1 coregrind/Makefile.am 1.63
M +0 -10 coregrind/stage2.c 1.7
M +15 -0 coregrind/vg_execontext.c 1.15
M +18 -0 coregrind/vg_include.h 1.170
M +120 -24 coregrind/vg_ldt.c 1.12
M +190 -36 coregrind/vg_libpthread.c 1.144
M +6 -1 coregrind/vg_main.c 1.138
M +9 -0 coregrind/vg_proxylwp.c 1.13
M +30 -2 coregrind/vg_scheduler.c 1.137
M +69 -9 coregrind/vg_syscalls.c 1.80
M +13 -0 include/vg_kerneliface.h 1.12
M +14 -1 none/tests/Makefile.am 1.23
M +2 -2 none/tests/seg_override.c 1.2
--- valgrind/glibc-2.3.supp #1.9:1.10
@@ -130,4 +130,9 @@
fun:_dl_runtime_resolve
}
+{
+ _dl_fini
+ Helgrind:Eraser
+ fun:_dl_fini
+}
#-------- Threading bugs?
@@ -192,2 +197,20 @@
obj:/lib/ld-2.3.2.so
}
+
+##----------------------------------------------------------------------##
+## SuSE 9 after FV changes (post 2.1.0)
+
+{
+ strlen/_dl_init_paths/dl_main/_dl_sysdep_start(Cond)
+ Memcheck:Cond
+ fun:strlen
+ fun:_dl_init_paths
+ fun:dl_main
+ fun:_dl_sysdep_start
+}
+
+{
+ Ugly strchr error in /lib/ld-2.3.2.so
+ Memcheck:Cond
+ obj:/lib/ld-2.3.2.so
+}
--- valgrind/coregrind/Makefile.am #1.62:1.63
@@ -109,5 +109,5 @@
libpthread_so_DEPENDENCIES = $(srcdir)/vg_libpthread.vs
libpthread_so_LDFLAGS = -Werror -fno-omit-frame-pointer -UVG_LIBDIR \
- -shared -fpic \
+ -shared -fpic -ldl \
-Wl,-version-script $(srcdir)/vg_libpthread.vs \
-Wl,-z,nodelete \
--- valgrind/coregrind/stage2.c #1.6:1.7
@@ -473,5 +473,4 @@ static void list_tools(void)
1. LD_LIBRARY_PATH=$VALGRINDLIB:$LD_LIBRARY_PATH
2. LD_PRELOAD=$VALGRINDLIB/vg_inject.so:($VALGRINDLIB/vgpreload_TOOL.so:)?$LD_PRELOAD
- 3. LD_ASSUME_KERNEL=2.4.1
If any of these is missing, then it is added.
@@ -489,11 +488,8 @@ static char **fix_environment(char **ori
static const char ld_preload[] = "LD_PRELOAD=";
static const int ld_preload_len = sizeof(ld_preload)-1;
- static const char ld_assume_kernel[] = "LD_ASSUME_KERNEL=";
- static const int ld_assume_kernel_len = sizeof(ld_assume_kernel)-1;
static const char valgrind_clo[] = VALGRINDCLO "=";
static const char valgrind_clo_len = sizeof(valgrind_clo)-1;
int ld_preload_done = 0;
int ld_library_path_done = 0;
- int ld_assume_kernel_done = 0;
char *inject_path;
int inject_path_len;
@@ -571,7 +567,4 @@ static char **fix_environment(char **ori
ld_preload_done = 1;
- } else if (memcmp(*cpp, ld_assume_kernel, ld_assume_kernel_len) == 0) {
- *cpp = "LD_ASSUME_KERNEL=2.4.1";
- ld_assume_kernel_done = 1;
} else if (memcmp(*cpp, valgrind_clo, valgrind_clo_len) == 0) {
*cpp = "";
@@ -601,7 +594,4 @@ static char **fix_environment(char **ori
}
- if (!ld_assume_kernel_done)
- ret[envc++] = "LD_ASSUME_KERNEL=2.4.1";
-
ret[envc] = NULL;
--- valgrind/coregrind/vg_execontext.c #1.14:1.15
@@ -320,4 +320,19 @@ void get_needed_regs(ThreadId tid, Addr*
*stack_highest_word = tst->stack_highest_word;
}
+
+ /* Nasty little hack to deal with sysinfo syscalls - if libc is
+ using the sysinfo page for syscalls (the TLS version does), then
+ eip will always appear to be in that page when doing a syscall,
+ not the actual libc function doing the syscall. This check sees
+ if EIP is within the syscall code, and pops the return address
+ off the stack so that eip is placed within the library function
+ calling the syscall. This makes stack backtraces much more
+ useful. */
+ if (*eip >= VG_(client_trampoline_code)+VG_(tramp_syscall_offset) &&
+ *eip < VG_(client_trampoline_code)+VG_(trampoline_code_length) &&
+ VG_(is_addressable)(*esp, sizeof(Addr))) {
+ *eip = *(Addr *)*esp;
+ *esp += sizeof(Addr);
+ }
}
--- valgrind/coregrind/vg_include.h #1.169:1.170
@@ -625,4 +625,6 @@ extern VgLdtEntry*
extern void
VG_(deallocate_LDT_for_thread) ( VgLdtEntry* ldt );
+extern void
+ VG_(clear_TLS_for_thread) ( VgLdtEntry* tls );
/* Simulate the modify_ldt syscall. */
@@ -630,4 +632,10 @@ extern Int VG_(sys_modify_ldt) ( ThreadI
Int func, void* ptr, UInt bytecount );
+/* Simulate the {get,set}_thread_area syscalls. */
+extern Int VG_(sys_set_thread_area) ( ThreadId tid,
+ struct vki_modify_ldt_ldt_s* info );
+extern Int VG_(sys_get_thread_area) ( ThreadId tid,
+ struct vki_modify_ldt_ldt_s* info );
+
/* Called from generated code. Given a segment selector and a virtual
address, return a linear address, and do limit checks too. */
@@ -804,4 +812,8 @@ typedef
VgLdtEntry* ldt;
+ /* TLS table. This consists of a small number (currently 3) of
+ entries from the Global Descriptor Table. */
+ VgLdtEntry tls[VKI_GDT_TLS_ENTRIES];
+
/* Saved machine context. Note the FPU state, %EIP and segment
registers are not shadowed.
@@ -1623,4 +1635,7 @@ extern void VG_(proxy_handlesig)( const
const struct vki_sigcontext *sigcontext );
+/* Get the PID/TID of the ProxyLWP. */
+extern Int VG_(proxy_id)(ThreadId tid);
+
/* ---------------------------------------------------------------------
@@ -1831,4 +1846,7 @@ extern Int VGOFF_(sh_eflags);
extern Int VGOFF_(ldt);
+/* This thread's TLS pointer. */
+extern Int VGOFF_(tls);
+
/* Nb: Most helper offsets are in include/vg_skin.h, for use by skins */
--- valgrind/coregrind/vg_ldt.c #1.11:1.12
@@ -122,4 +122,22 @@ void VG_(deallocate_LDT_for_thread) ( Vg
+/* 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_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. */
@@ -152,7 +170,7 @@ unsigned int wine_ldt_get_limit( const V
Addr VG_(do_useseg) ( UInt seg_selector, Addr virtual_addr )
{
+ UInt table;
Addr base;
UInt limit;
- VgLdtEntry* the_ldt;
if (0)
@@ -162,11 +180,27 @@ Addr VG_(do_useseg) ( UInt seg_selector,
seg_selector &= 0x0000FFFF;
- /* Sanity check the segment selector. Ensure that TI=1 (LDT) and
- that RPL=11b (least privilege). These form the bottom 3 bits
- of the selector. */
- vg_assert((seg_selector & 7) == 7);
+ /* Sanity check the segment selector. Ensure that RPL=11b (least
+ privilege). This forms the bottom 2 bits of the selector. */
+ vg_assert((seg_selector & 3) == 3);
- /* convert it onto a table index */
+ /* 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_TLS_MIN && seg_selector < VKI_GDT_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)];
+ base = (Addr)wine_ldt_get_base ( &the_tls[seg_selector-VKI_GDT_TLS_MIN] );
+ limit = (UInt)wine_ldt_get_limit ( &the_tls[seg_selector-VKI_GDT_TLS_MIN] );
+ } else {
+ VgLdtEntry* the_ldt;
+
vg_assert(seg_selector >= 0 && seg_selector < 8192);
@@ -188,4 +222,5 @@ Addr VG_(do_useseg) ( UInt seg_selector,
limit = (UInt)wine_ldt_get_limit ( &the_ldt[seg_selector] );
}
+ }
/* Note, this check is just slightly too slack. Really it should
@@ -200,4 +235,8 @@ Addr VG_(do_useseg) ( UInt seg_selector,
}
+ if (0)
+ VG_(printf)("do_useseg: base = %p, addr = %p\n",
+ base, base + virtual_addr);
+
return base + virtual_addr;
}
@@ -368,4 +407,61 @@ Int VG_(sys_modify_ldt) ( ThreadId tid,
+Int VG_(sys_set_thread_area) ( ThreadId tid,
+ struct vki_modify_ldt_ldt_s* info )
+{
+ Int idx = info->entry_number;
+
+ if (idx == -1) {
+ for (idx = 0; idx < VKI_GDT_TLS_ENTRIES; idx++) {
+ VgLdtEntry* tls = VG_(threads)[tid].tls + idx;
+
+ if (tls->LdtEnt.Words.word1 == 0 && tls->LdtEnt.Words.word2 == 0)
+ break;
+ }
+
+ if (idx == VKI_GDT_TLS_ENTRIES)
+ return -VKI_ESRCH;
+ } else if (idx < VKI_GDT_TLS_MIN || idx > VKI_GDT_TLS_MAX) {
+ return -VKI_EINVAL;
+ } else {
+ idx = info->entry_number - VKI_GDT_TLS_MIN;
+ }
+
+ translate_to_hw_format(info, VG_(threads)[tid].tls + idx, 0);
+
+ info->entry_number = idx + VKI_GDT_TLS_MIN;
+
+ return 0;
+}
+
+
+Int VG_(sys_get_thread_area) ( ThreadId tid,
+ struct vki_modify_ldt_ldt_s* info )
+{
+ Int idx = info->entry_number;
+ VgLdtEntry* tls;
+
+ if (idx < VKI_GDT_TLS_MIN || idx > VKI_GDT_TLS_MAX)
+ return -VKI_EINVAL;
+
+ tls = VG_(threads)[tid].tls + idx - VKI_GDT_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;
+}
+
+
/*--------------------------------------------------------------------*/
/*--- end vg_ldt.c ---*/
--- valgrind/coregrind/vg_libpthread.c #1.143:1.144
@@ -61,4 +61,8 @@
#undef __USE_UNIX98
+#define __USE_GNU
+#include <dlfcn.h>
+#undef __USE_GNU
+
#include <unistd.h>
#include <string.h>
@@ -108,4 +112,7 @@ int is_kerror ( int res )
static
+void init_thread_specific_state ( void );
+
+static
void init_libc_tsd_keys ( void );
@@ -524,4 +531,36 @@ int pthread_getconcurrency(void)
------------------------------------------------ */
+typedef void *(*__attribute__ ((regparm (3), stdcall)) allocate_tls_t) (void *result);
+typedef void (*__attribute__ ((regparm (3), stdcall)) deallocate_tls_t) (void *tcb, int dealloc_tcb);
+
+static allocate_tls_t allocate_tls = NULL;
+static deallocate_tls_t deallocate_tls = NULL;
+
+static
+int get_gs()
+{
+ int gs;
+
+ asm volatile ("movw %%gs, %w0" : "=q" (gs));
+
+ return gs & 0xffff;
+}
+
+static
+void set_gs( int gs )
+{
+ asm volatile ("movw %w0, %%gs" :: "q" (gs));
+}
+
+static
+void *get_tcb()
+{
+ void *tcb;
+
+ asm volatile ("movl %%gs:0, %0" : "=r" (tcb));
+
+ return tcb;
+}
+
/* All exiting threads eventually pass through here, bearing the
return value, or PTHREAD_CANCELED, in ret_val. */
@@ -571,4 +610,10 @@ void thread_exit_wrapper ( void* ret_val
my_free(specifics_ptr);
+ /* Free up any TLS data */
+ if ((get_gs() & 7) == 3 && pthread_self() > 1) {
+ my_assert(deallocate_tls != NULL);
+ deallocate_tls(get_tcb(), 1);
+ }
+
/* Decide on my final disposition. */
VALGRIND_MAGIC_SEQUENCE(detached, (-1) /* default */,
@@ -602,4 +647,7 @@ typedef
struct {
int attr__detachstate;
+ void* tls_data;
+ int tls_segment;
+ unsigned long sysinfo;
void* (*root_fn) ( void* );
void* arg;
@@ -607,4 +655,14 @@ typedef
NewThreadInfo;
+/* Struct used to describe a TDB header, copied from glibc. */
+typedef
+ struct {
+ void *tcb;
+ void *dtv;
+ void *self;
+ int multiple_threads;
+ unsigned long sysinfo;
+ }
+ tcbhead_t;
/* This is passed to the VG_USERREQ__APPLY_IN_NEW_THREAD and so must
@@ -616,4 +674,7 @@ void thread_wrapper ( NewThreadInfo* inf
{
int attr__detachstate;
+ void* tls_data;
+ int tls_segment;
+ unsigned long sysinfo;
void* (*root_fn) ( void* );
void* arg;
@@ -621,7 +682,39 @@ void thread_wrapper ( NewThreadInfo* inf
attr__detachstate = info->attr__detachstate;
+ tls_data = info->tls_data;
+ tls_segment = info->tls_segment;
+ sysinfo = info->sysinfo;
root_fn = info->root_fn;
arg = info->arg;
+ if (tls_data) {
+ tcbhead_t *tcb = tls_data;
+ struct vki_modify_ldt_ldt_s ldt_info;
+
+ /* Fill in the TCB header */
+ tcb->tcb = tcb;
+ tcb->self = tcb;
+ tcb->multiple_threads = 1;
+ tcb->sysinfo = sysinfo;
+
+ /* Fill in an LDT descriptor */
+ ldt_info.entry_number = tls_segment;
+ ldt_info.base_addr = (unsigned long)tls_data;
+ ldt_info.limit = 0xfffff;
+ ldt_info.seg_32bit = 1;
+ ldt_info.contents = 0;
+ ldt_info.read_exec_only = 0;
+ ldt_info.limit_in_pages = 1;
+ ldt_info.seg_not_present = 0;
+ ldt_info.useable = 1;
+ ldt_info.reserved = 0;
+
+ /* Install the thread area */
+ VG_(do_syscall)(__NR_set_thread_area, &ldt_info);
+
+ /* Setup the GS segment register */
+ set_gs(ldt_info.entry_number * 8 + 3);
+ }
+
/* Free up the arg block that pthread_create malloced. */
my_free(info);
@@ -634,4 +727,7 @@ void thread_wrapper ( NewThreadInfo* inf
pthread_detach(pthread_self());
+ /* Initialise thread specific state */
+ init_thread_specific_state();
+
# ifdef GLIBC_2_3
/* Set this thread's locale to the global (default) locale. A hack
@@ -689,4 +785,5 @@ pthread_create (pthread_t *__restrict __
int tid_child;
NewThreadInfo* info;
+ int gs;
ensure_valgrind("pthread_create");
@@ -706,4 +803,27 @@ pthread_create (pthread_t *__restrict __
info->attr__detachstate = PTHREAD_CREATE_JOINABLE;
+ gs = get_gs();
+
+ if ((gs & 7) == 3) {
+ tcbhead_t *tcb = get_tcb();
+
+ if (allocate_tls == NULL || deallocate_tls == NULL) {
+ allocate_tls = (allocate_tls_t)dlsym(RTLD_DEFAULT, "_dl_allocate_tls");
+ deallocate_tls = (deallocate_tls_t)dlsym(RTLD_DEFAULT, "_dl_deallocate_tls");
+ }
+
+ my_assert(allocate_tls != NULL);
+
+ info->tls_data = allocate_tls(NULL);
+ info->tls_segment = gs >> 3;
+ info->sysinfo = tcb->sysinfo;
+
+ tcb->multiple_threads = 1;
+ } else {
+ info->tls_data = NULL;
+ info->tls_segment = -1;
+ info->sysinfo = 0;
+ }
+
info->root_fn = __start_routine;
info->arg = __arg;
@@ -1620,15 +1740,31 @@ void __pthread_initialize ( void )
#include <resolv.h>
-static int thread_specific_errno[VG_N_THREADS];
-static int thread_specific_h_errno[VG_N_THREADS];
-static struct __res_state
- thread_specific_res_state[VG_N_THREADS];
-#undef errno
-extern int errno;
+typedef
+ struct {
+ int *errno_ptr;
+ int *h_errno_ptr;
+ struct __res_state *res_state_ptr;
+ int errno_data;
+ int h_errno_data;
+ struct __res_state res_state_data;
+ }
+ ThreadSpecificState;
+
+static ThreadSpecificState thread_specific_state[VG_N_THREADS];
+
+static
+void init_thread_specific_state ( void )
+{
+ int tid = pthread_self();
+
+ thread_specific_state[tid].errno_ptr = NULL;
+ thread_specific_state[tid].h_errno_ptr = NULL;
+ thread_specific_state[tid].res_state_ptr = NULL;
+}
+
int* __errno_location ( void )
{
int tid;
- int *ret;
ensure_valgrind("__errno_location");
@@ -1639,14 +1775,15 @@ int* __errno_location ( void )
if (tid < 1 || tid >= VG_N_THREADS)
barf("__errno_location: invalid ThreadId");
- if (tid == 1)
- ret = &errno;
+ if (thread_specific_state[tid].errno_ptr == NULL) {
+ if ((get_gs() & 7) == 3)
+ thread_specific_state[tid].errno_ptr = dlsym(RTLD_DEFAULT, "errno");
+ else if (tid == 1)
+ thread_specific_state[tid].errno_ptr = dlvsym(RTLD_DEFAULT, "errno", "GLIBC_2.0");
else
- ret = &thread_specific_errno[tid];
-
- return ret;
+ thread_specific_state[tid].errno_ptr = &thread_specific_state[tid].errno_data;
+ }
+ return thread_specific_state[tid].errno_ptr;
}
-#undef h_errno
-extern int h_errno;
int* __h_errno_location ( void )
{
@@ -1659,12 +1796,15 @@ int* __h_errno_location ( void )
if (tid < 1 || tid >= VG_N_THREADS)
barf("__h_errno_location: invalid ThreadId");
- if (tid == 1)
- return &h_errno;
- return & thread_specific_h_errno[tid];
+ if (thread_specific_state[tid].h_errno_ptr == NULL) {
+ if ((get_gs() & 7) == 3)
+ thread_specific_state[tid].h_errno_ptr = dlsym(RTLD_DEFAULT, "h_errno");
+ else if (tid == 1)
+ thread_specific_state[tid].h_errno_ptr = dlvsym(RTLD_DEFAULT, "h_errno", "GLIBC_2.0");
+ else
+ thread_specific_state[tid].h_errno_ptr = &thread_specific_state[tid].h_errno_data;
+ }
+ return thread_specific_state[tid].h_errno_ptr;
}
-
-#undef _res
-extern struct __res_state _res;
struct __res_state* __res_state ( void )
{
@@ -1677,7 +1817,16 @@ struct __res_state* __res_state ( void )
if (tid < 1 || tid >= VG_N_THREADS)
barf("__res_state: invalid ThreadId");
- if (tid == 1)
- return & _res;
- return & thread_specific_res_state[tid];
+ if (thread_specific_state[tid].res_state_ptr == NULL) {
+ if ((get_gs() & 7) == 3) {
+ struct __res_state **resp = dlsym(RTLD_DEFAULT, "__resp");
+
+ thread_specific_state[tid].res_state_ptr = *resp;
+ } else if (tid == 1) {
+ thread_specific_state[tid].res_state_ptr = dlvsym(RTLD_DEFAULT, "_res", "GLIBC_2.0");
+ } else {
+ thread_specific_state[tid].res_state_ptr = &thread_specific_state[tid].res_state_data;
+ }
+ }
+ return thread_specific_state[tid].res_state_ptr;
}
@@ -2919,8 +3067,12 @@ weak_alias(pthread_rwlock_trywrlock, __p
-/* I've no idea what these are, but they get called quite a lot.
- Anybody know? */
-
#ifndef __UCLIBC__
+/* These are called as part of stdio to lock the FILE structure for MT
+ programs. Unfortunately, the lock is not always a pthreads lock -
+ the NPTL version uses a lighter-weight lock which uses futex
+ directly (and uses a structure which is smaller than
+ pthread_mutex). So basically, this is completely broken on recent
+ glibcs. */
+
#undef _IO_flockfile
void _IO_flockfile ( _IO_FILE * file )
@@ -2928,4 +3080,5 @@ void _IO_flockfile ( _IO_FILE * file )
pthread_mutex_lock(file->_lock);
}
+strong_alias(_IO_flockfile, __flockfile);
weak_alias(_IO_flockfile, flockfile);
@@ -2935,4 +3088,5 @@ void _IO_funlockfile ( _IO_FILE * file )
pthread_mutex_unlock(file->_lock);
}
+strong_alias(_IO_funlockfile, __funlockfile);
weak_alias(_IO_funlockfile, funlockfile);
#endif
--- valgrind/coregrind/vg_main.c #1.137:1.138
@@ -59,4 +59,5 @@ Int VGOFF_(m_dflag) = INVALID_OFFSET;
Int VGOFF_(m_ssestate) = INVALID_OFFSET;
Int VGOFF_(ldt) = INVALID_OFFSET;
+Int VGOFF_(tls) = INVALID_OFFSET;
Int VGOFF_(m_cs) = INVALID_OFFSET;
Int VGOFF_(m_ss) = INVALID_OFFSET;
@@ -328,6 +329,7 @@ static void vg_init_baseBlock ( void )
);
- /* This thread's LDT pointer, and segment registers. */
+ /* This thread's LDT and TLS pointers, and segment registers. */
VGOFF_(ldt) = alloc_BaB(1);
+ VGOFF_(tls) = alloc_BaB(1);
VGOFF_(m_cs) = alloc_BaB(1);
VGOFF_(m_ss) = alloc_BaB(1);
@@ -449,4 +451,7 @@ static void vg_init_baseBlock ( void )
VG_(baseBlock)[VGOFF_(ldt)] = (UInt)NULL;
+ /* Pretend the root thread has no TLS array for now. */
+ VG_(baseBlock)[VGOFF_(tls)] = (UInt)NULL;
+
/* Initialise shadow regs */
if (VG_(needs).shadow_regs) {
--- valgrind/coregrind/vg_proxylwp.c #1.12:1.13
@@ -1374,4 +1374,13 @@ void VG_(proxy_sanity)(void)
}
+/* Get the PID/TID of the ProxyLWP. */
+Int VG_(proxy_id)(ThreadId tid)
+{
+ ThreadState *tst = VG_(get_ThreadState)(tid);
+ ProxyLWP *proxy = tst->proxy;
+
+ return proxy->lwp;
+}
+
/*--------------------------------------------------------------------*/
/*--- Proxy LWP machinery. vg_proxylwp.c ---*/
--- valgrind/coregrind/vg_scheduler.c #1.136:1.137
@@ -340,4 +340,5 @@ void VG_(load_thread_state) ( ThreadId t
VG_(baseBlock)[VGOFF_(ldt)] = (UInt)VG_(threads)[tid].ldt;
+ VG_(baseBlock)[VGOFF_(tls)] = (UInt)VG_(threads)[tid].tls;
VG_(baseBlock)[VGOFF_(m_cs)] = VG_(threads)[tid].m_cs;
VG_(baseBlock)[VGOFF_(m_ss)] = VG_(threads)[tid].m_ss;
@@ -421,4 +422,17 @@ void VG_(save_thread_state) ( ThreadId t
== (void*)VG_(baseBlock)[VGOFF_(ldt)]);
+ /* We don't copy out the TLS entry, because it can never be changed
+ by the normal actions of the thread, only by the set_thread_area
+ syscall, in which case we will correctly be updating
+ VG_(threads)[tid].tls. This printf happens iff the following
+ assertion fails. */
+ if ((void*)VG_(threads)[tid].tls != (void*)VG_(baseBlock)[VGOFF_(tls)])
+ VG_(printf)("VG_(threads)[%d].tls=%p VG_(baseBlock)[VGOFF_(tls)]=%p\n",
+ tid, (void*)VG_(threads)[tid].tls,
+ (void*)VG_(baseBlock)[VGOFF_(tls)]);
+
+ vg_assert((void*)VG_(threads)[tid].tls
+ == (void*)VG_(baseBlock)[VGOFF_(tls)]);
+
VG_(threads)[tid].m_cs = VG_(baseBlock)[VGOFF_(m_cs)];
VG_(threads)[tid].m_ss = VG_(baseBlock)[VGOFF_(m_ss)];
@@ -470,4 +484,5 @@ void VG_(save_thread_state) ( ThreadId t
/* Fill it up with junk. */
VG_(baseBlock)[VGOFF_(ldt)] = junk;
+ VG_(baseBlock)[VGOFF_(tls)] = junk;
VG_(baseBlock)[VGOFF_(m_cs)] = junk;
VG_(baseBlock)[VGOFF_(m_ss)] = junk;
@@ -539,4 +554,5 @@ void mostly_clear_thread_record ( Thread
vg_assert(tid >= 0 && tid < VG_N_THREADS);
VG_(threads)[tid].ldt = NULL;
+ VG_(clear_TLS_for_thread)(VG_(threads)[tid].tls);
VG_(threads)[tid].tid = tid;
VG_(threads)[tid].status = VgTs_Empty;
@@ -597,4 +613,5 @@ void VG_(scheduler_init) ( void )
vg_tid_currently_in_baseBlock = tid_main;
vg_tid_last_in_baseBlock = tid_main;
+ VG_(baseBlock)[VGOFF_(tls)] = (UInt)VG_(threads)[tid_main].tls;
VG_(save_thread_state) ( tid_main );
@@ -1373,4 +1390,7 @@ void cleanup_after_thread_exited ( Threa
VG_(threads)[tid].ldt = NULL;
+ /* Clear its TLS array. */
+ VG_(clear_TLS_for_thread)( VG_(threads)[tid].tls );
+
/* Not interested in the timeout anymore */
VG_(threads)[tid].awaken_at = 0xFFFFFFFF;
@@ -1859,4 +1879,8 @@ void do__apply_in_new_thread ( ThreadId
}
+ /* Initialise the thread's TLS array */
+ VG_(clear_TLS_for_thread)( VG_(threads)[tid].tls );
+ VG_(baseBlock)[VGOFF_(tls)] = (UInt)VG_(threads)[tid].tls;
+
VG_(save_thread_state)(tid);
vg_tid_last_in_baseBlock = tid;
@@ -2076,6 +2100,10 @@ void do_pthread_mutex_lock( ThreadId tid
if (mutex->__m_count > 0) {
-
- vg_assert(VG_(is_valid_tid)((ThreadId)mutex->__m_owner));
+ if (!VG_(is_valid_tid)((ThreadId)mutex->__m_owner)) {
+ VG_(record_pthread_error)( tid,
+ "pthread_mutex_lock/trylock: mutex has invalid owner");
+ SET_PTHREQ_RETVAL(tid, VKI_EINVAL);
+ return;
+ }
/* Someone has it already. */
--- valgrind/coregrind/vg_syscalls.c #1.79:1.80
@@ -1047,13 +1047,4 @@ PRE(exit)
}
-PRE(clone)
-{
- VG_(unimplemented)
- ("clone(): not supported by Valgrind.\n "
- "We do now support programs linked against\n "
- "libpthread.so, though. Re-run with -v and ensure that\n "
- "you are picking up Valgrind's implementation of libpthread.so.");
-}
-
PRE(ptrace)
{
@@ -1185,4 +1176,31 @@ PRE(modify_ldt)
}
+PRE(set_thread_area)
+{
+ MAYBE_PRINTF("set_thread_area ( %p )\n", arg1);
+
+ SYSCALL_TRACK( pre_mem_read, tid,
+ "set_thread_area(ptr)", arg1,
+ sizeof(struct vki_modify_ldt_ldt_s) );
+
+ /* "do" the syscall ourselves; the kernel never sees it */
+ res = VG_(sys_set_thread_area)( tid, (void *)arg1 );
+}
+
+PRE(get_thread_area)
+{
+ MAYBE_PRINTF("get_thread_area ( %p )\n", arg1);
+ SYSCALL_TRACK( pre_mem_write, tid,
+ "get_thread_area(ptr)", arg1,
+ sizeof(struct vki_modify_ldt_ldt_s) );
+
+ /* "do" the syscall ourselves; the kernel never sees it */
+ res = VG_(sys_get_thread_area)( tid, (void *)arg1 );
+
+ if (!VG_(is_kerror)(res)) {
+ VG_TRACK( post_mem_write, arg1, sizeof(struct vki_modify_ldt_ldt_s) );
+ }
+}
+
PRE(setresgid)
{
@@ -2142,4 +2160,22 @@ POST(fork)
}
+PRE(clone)
+{
+ MAYBE_PRINTF("clone ( %d, %p, %p, %p, %p )\n",arg1,arg2,arg3,arg4,arg5);
+
+ if (arg2 == 0 &&
+ arg1 == (VKI_CLONE_CHILD_CLEARTID|VKI_CLONE_CHILD_SETTID|VKI_SIGCHLD)) {
+ before_fork(tid, tst);
+ res = VG_(do_syscall)(SYSNO, arg1, arg2, arg3, arg4, arg5);
+ after_fork(tid, tst);
+ } else {
+ VG_(unimplemented)
+ ("clone(): not supported by Valgrind.\n "
+ "We do now support programs linked against\n "
+ "libpthread.so, though. Re-run with -v and ensure that\n "
+ "you are picking up Valgrind's implementation of libpthread.so.");
+ }
+}
+
PRE(fsync)
{
@@ -4728,4 +4764,25 @@ PRE(utimes)
}
+PRE(futex)
+{
+ /* int futex(void *futex, int op, int val, const struct timespec *timeout); */
+ MAYBE_PRINTF("futex ( %p, %d, %d, %p, %p )\n", arg1,arg2,arg3,arg4,arg5);
+ SYSCALL_TRACK( pre_mem_read, tid, "futex(futex)", arg1, sizeof(int) );
+ if (arg2 == VKI_FUTEX_WAIT && arg4 != (UInt)NULL)
+ SYSCALL_TRACK( pre_mem_read, tid, "futex(timeout)", arg4,
+ sizeof(struct timespec) );
+ if (arg2 == VKI_FUTEX_REQUEUE)
+ SYSCALL_TRACK( pre_mem_read, tid, "futex(futex2)", arg4, sizeof(int) );
+}
+
+POST(futex)
+{
+ if (!VG_(is_kerror)(res)) {
+ VG_TRACK( post_mem_write, arg1, sizeof(int) );
+ if (arg2 == VKI_FUTEX_FD && VG_(clo_track_fds))
+ record_fd_open(tid, res, NULL);
+ }
+}
+
#define SIGNAL_SIMULATION 1
@@ -4928,4 +4985,6 @@ static const struct sys_info special_sys
SYSB_(modify_ldt, False),
+ SYSB_(set_thread_area, False),
+ SYSB_(get_thread_area, False),
SYSB_(execve, False),
@@ -5127,4 +5186,5 @@ static const struct sys_info sys_info[]
SYSBA(mmap2, False),
SYSBA(clock_gettime, False),
+ SYSBA(futex, True),
/* new signal handling makes these normal blocking syscalls */
--- valgrind/include/vg_kerneliface.h #1.11:1.12
@@ -632,4 +632,5 @@ typedef struct vki_modify_ldt_ldt_s {
unsigned int seg_not_present:1;
unsigned int useable:1;
+ unsigned int reserved:25;
} vki_modify_ldt_t;
@@ -638,4 +639,7 @@ typedef struct vki_modify_ldt_ldt_s {
#define VKI_MODIFY_LDT_CONTENTS_CODE 2
+#define VKI_GDT_TLS_ENTRIES 3
+#define VKI_GDT_TLS_MIN 6
+#define VKI_GDT_TLS_MAX (VKI_GDT_TLS_MIN + VKI_GDT_TLS_ENTRIES)
/* Flags for clone() */
@@ -727,4 +731,13 @@ struct statfs64 {
};
+/*
+ * linux/futex.h
+ */
+
+#define VKI_FUTEX_WAIT 0
+#define VKI_FUTEX_WAKE 1
+#define VKI_FUTEX_FD 2
+#define VKI_FUTEX_REQUEUE 3
+
#endif /* __VG_KERNELIFACE_H */
--- valgrind/none/tests/Makefile.am #1.22:1.23
@@ -43,4 +43,5 @@
shortpush.stderr.exp shortpush.vgtest \
shorts.stderr.exp shorts.vgtest \
+ tls.stderr.exp tls.stdout.exp tls.vgtest \
smc1.stderr.exp smc1.stdout.exp smc1.vgtest \
syscall-restart1.vgtest syscall-restart1.stdout.exp syscall-restart1.stderr.exp \
@@ -54,5 +55,5 @@
munmap_exe map_unmap mremap rcl_assert \
rcrl readline1 resolv seg_override sha1_test shortpush shorts smc1 \
- pth_blockedsig \
+ tls.so tls2.so tls pth_blockedsig \
syscall-restart1 syscall-restart2 \
coolo_sigaction gxx304 yield
@@ -96,7 +97,19 @@
syscall_restart1_SOURCES = syscall-restart1.c
syscall_restart2_SOURCES = syscall-restart2.c
+tls_SOURCES = tls.c tls2.c
+tls_DEPENDENCIES = tls.so
+tls_LDFLAGS = -Wl,-rpath,$(srcdir)
+tls_LDADD = tls.so -lpthread
+tls_so_SOURCES = tls_so.c
+tls_so_LDADD = tls2.so
+tls_so_DEPENDENCIES = tls2.so
+tls_so_LDFLAGS = -Wl,-rpath,$(srcdir) -shared
+tls2_so_SOURCES = tls2_so.c
+tls2_so_LDFLAGS = -shared
yield_SOURCES = yield.c
yield_LDADD = -lpthread
+tls_so.o tls2_so.o: CFLAGS += -fpic
+
# pthread C ones
pth_blockedsig_SOURCES = pth_blockedsig.c
--- valgrind/none/tests/seg_override.c #1.1:1.2
@@ -111,6 +111,6 @@ void ldt_seg_write ( int ldt_entno, unsi
asm volatile("movl %2, %%eax\n\t"
"movl %1, %%edx\n\t"
- "movl %0, %%gs\n\t"
- "movl %%eax, %%gs:(%%edx)\t"
+ "movl %0, %%fs\n\t"
+ "movl %%eax, %%fs:(%%edx)\t"
:
: "r" (7 /* LDT(TI), least privilege */ + (ldt_entno << 3)),
|