vg_syscall.S
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 \
-Wl,--soname=libpthread.so.0
environment, except:
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.
static const int ld_library_path_len = sizeof(ld_library_path)-1;
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;
int vgliblen = strlen(valgrind_lib);
*cpp = cp;
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 = "";
}
ret[envc++] = cp;
}
-
- if (!ld_assume_kernel_done)
- ret[envc++] = "LD_ASSUME_KERNEL=2.4.1";
ret[envc] = NULL;
*esp = tst->m_esp;
*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);
+ }
}
ExeContext* VG_(get_ExeContext) ( ThreadId tid )
VG_(allocate_LDT_for_thread) ( VgLdtEntry* parent_ldt );
extern void
VG_(deallocate_LDT_for_thread) ( VgLdtEntry* ldt );
+extern void
+ VG_(clear_TLS_for_thread) ( VgLdtEntry* tls );
/* Simulate the modify_ldt syscall. */
extern Int VG_(sys_modify_ldt) ( ThreadId tid,
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. */
extern Addr VG_(do_useseg) ( UInt seg_selector, Addr virtual_addr );
deallocate this at thread exit. */
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.
extern void VG_(proxy_handlesig)( const vki_ksiginfo_t *siginfo,
const struct vki_sigcontext *sigcontext );
+/* Get the PID/TID of the ProxyLWP. */
+extern Int VG_(proxy_id)(ThreadId tid);
+
/* ---------------------------------------------------------------------
Exports of vg_syscalls.c
/* This thread's LDT pointer. */
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 */
extern Int VGOFF_(helper_undefined_instruction);
+/* 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. */
*/
Addr VG_(do_useseg) ( UInt seg_selector, Addr virtual_addr )
{
- Addr base;
- UInt limit;
- VgLdtEntry* the_ldt;
+ UInt table;
+ Addr base;
+ UInt limit;
if (0)
VG_(printf)("do_useseg: seg_selector = %p, vaddr = %p\n",
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;
- vg_assert(seg_selector >= 0 && seg_selector < 8192);
-
- /* Come up with a suitable LDT entry. We look at the thread's LDT,
- which is pointed to by a VG_(baseBlock) entry. If null, we will
- use an implied zero-entry -- although this usually implies the
- program is in deep trouble, since it is using LDT entries which
- it probably hasn't set up. */
- the_ldt = (VgLdtEntry*)VG_(baseBlock)[VGOFF_(ldt)];
- if (the_ldt == NULL) {
- base = 0;
- limit = 0;
- VG_(message)(
- Vg_UserMsg,
- "Warning: segment-override prefix encountered, but thread has no LDT"
- );
+
+ 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 {
- base = (Addr)wine_ldt_get_base ( &the_ldt[seg_selector] );
- limit = (UInt)wine_ldt_get_limit ( &the_ldt[seg_selector] );
+ VgLdtEntry* the_ldt;
+
+ vg_assert(seg_selector >= 0 && seg_selector < 8192);
+
+ /* Come up with a suitable LDT entry. We look at the thread's LDT,
+ which is pointed to by a VG_(baseBlock) entry. If null, we will
+ use an implied zero-entry -- although this usually implies the
+ program is in deep trouble, since it is using LDT entries which
+ it probably hasn't set up. */
+ the_ldt = (VgLdtEntry*)VG_(baseBlock)[VGOFF_(ldt)];
+ if (the_ldt == NULL) {
+ base = 0;
+ limit = 0;
+ VG_(message)(
+ Vg_UserMsg,
+ "Warning: segment-override prefix encountered, but thread has no LDT"
+ );
+ } else {
+ base = (Addr)wine_ldt_get_base ( &the_ldt[seg_selector] );
+ limit = (UInt)wine_ldt_get_limit ( &the_ldt[seg_selector] );
+ }
}
/* Note, this check is just slightly too slack. Really it should
);
}
+ if (0)
+ VG_(printf)("do_useseg: base = %p, addr = %p\n",
+ base, base + virtual_addr);
+
return base + virtual_addr;
}
}
+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 ---*/
/*--------------------------------------------------------------------*/
#include <pthread.h>
#undef __USE_UNIX98
+#define __USE_GNU
+#include <dlfcn.h>
+#undef __USE_GNU
+
#include <unistd.h>
#include <string.h>
#include <sys/time.h>
extern __locale_t __uselocale ( __locale_t );
#endif
+static
+void init_thread_specific_state ( void );
+
static
void init_libc_tsd_keys ( void );
and for clearing up afterwards.
------------------------------------------------ */
+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. */
static
my_assert(specifics_ptr != (void**)1); /* 1 means invalid thread */
if (specifics_ptr != NULL)
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 */,
VG_USERREQ__SET_OR_GET_DETACH,
objects which might be on the parent's stack. */
typedef
struct {
- int attr__detachstate;
- void* (*root_fn) ( void* );
- void* arg;
+ int attr__detachstate;
+ void* tls_data;
+ int tls_segment;
+ unsigned long sysinfo;
+ void* (*root_fn) ( void* );
+ void* arg;
}
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
not return. Note that this runs in the new thread, not the
__attribute__((noreturn))
void thread_wrapper ( NewThreadInfo* info )
{
- int attr__detachstate;
- void* (*root_fn) ( void* );
- void* arg;
- void* ret_val;
+ int attr__detachstate;
+ void* tls_data;
+ int tls_segment;
+ unsigned long sysinfo;
+ void* (*root_fn) ( void* );
+ void* arg;
+ void* ret_val;
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);
if (attr__detachstate == PTHREAD_CREATE_DETACHED)
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
in support of glibc-2.3. This does the biz for the all new
{
int tid_child;
NewThreadInfo* info;
+ int gs;
ensure_valgrind("pthread_create");
else
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;
VALGRIND_MAGIC_SEQUENCE(tid_child, VG_INVALID_THREADID /* default */,
------------------------------------------------ */
#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");
VALGRIND_MAGIC_SEQUENCE(tid, 1 /* default */,
/* 'cos I'm paranoid ... */
if (tid < 1 || tid >= VG_N_THREADS)
barf("__errno_location: invalid ThreadId");
- if (tid == 1)
- ret = &errno;
- else
- ret = &thread_specific_errno[tid];
-
- return ret;
+ 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
+ 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 )
{
int tid;
/* 'cos I'm paranoid ... */
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 )
{
int tid;
/* 'cos I'm paranoid ... */
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;
}
return 0;
}
-
int sem_wait ( sem_t* sem )
{
int res;
weak_alias(pthread_rwlock_trywrlock, __pthread_rwlock_trywrlock)
-/* 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 )
{
pthread_mutex_lock(file->_lock);
}
+strong_alias(_IO_flockfile, __flockfile);
weak_alias(_IO_flockfile, flockfile);
#undef _IO_funlockfile
{
pthread_mutex_unlock(file->_lock);
}
+strong_alias(_IO_funlockfile, __funlockfile);
weak_alias(_IO_funlockfile, funlockfile);
#endif
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;
Int VGOFF_(m_ds) = INVALID_OFFSET;
== 0
);
- /* 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);
VGOFF_(m_ds) = alloc_BaB(1);
/* Pretend the root thread has a completely empty LDT to start with. */
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) {
VG_(baseBlock)[VGOFF_(sh_esp)] =
vg_assert(sane);
}
+/* 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 ---*/
/*--------------------------------------------------------------------*/
vg_assert(vg_tid_currently_in_baseBlock == VG_INVALID_THREADID);
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;
VG_(baseBlock)[VGOFF_(m_ds)] = VG_(threads)[tid].m_ds;
vg_assert((void*)VG_(threads)[tid].ldt
== (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)];
VG_(threads)[tid].m_ds = VG_(baseBlock)[VGOFF_(m_ds)];
/* 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;
VG_(baseBlock)[VGOFF_(m_ds)] = junk;
{
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;
VG_(threads)[tid].associated_mx = NULL;
/* Copy VG_(baseBlock) state to tid_main's slot. */
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 );
VG_(threads)[tid_main].stack_highest_word
VG_(deallocate_LDT_for_thread)( VG_(threads)[tid].ldt );
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;
VG_(baseBlock)[VGOFF_(ldt)] = (UInt)VG_(threads)[tid].ldt;
}
+ /* 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;
}
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. */
if ((ThreadId)mutex->__m_owner == tid) {
VG_(core_panic)("syscall exit() not caught by the scheduler?!");
}
-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)
{
/* long ptrace (enum __ptrace_request request, pid_t pid,
}
}
+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)
{
/* int setresgid(gid_t rgid, gid_t egid, gid_t sgid); */
}
}
+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)
{
/* int fsync(int fd); */
sizeof(struct timeval) );
}
+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
PRE(pause)
SYSB_(clone, False),
SYSB_(modify_ldt, False),
+ SYSB_(set_thread_area, False),
+ SYSB_(get_thread_area, False),
SYSB_(execve, False),
SYSB_(brk, False),
SYSBA(adjtimex, False),
SYSBA(mmap2, False),
SYSBA(clock_gettime, False),
+ SYSBA(futex, True),
/* new signal handling makes these normal blocking syscalls */
SYSB_(pause, True),
fun:fixup
fun:_dl_runtime_resolve
}
+{
+ _dl_fini
+ Helgrind:Eraser
+ fun:_dl_fini
+}
#-------- Threading bugs?
# glibc 'knows' that destroying a locked mutex will unlock it
Memcheck:Cond
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
+}
unsigned int limit_in_pages:1;
unsigned int seg_not_present:1;
unsigned int useable:1;
+ unsigned int reserved:25;
} vki_modify_ldt_t;
#define VKI_MODIFY_LDT_CONTENTS_DATA 0
#define VKI_MODIFY_LDT_CONTENTS_STACK 1
#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() */
/* linux/sched.h */
unsigned int f_spare[5];
};
+/*
+ * 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 */
sha1_test.stderr.exp sha1_test.vgtest \
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 \
syscall-restart2.vgtest syscall-restart2.stdout.exp syscall-restart2.stderr.exp \
fucomip insn_mmx insn_sse insn_sse2 \
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
shorts_SOURCES = shorts.c
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
pth_blockedsig_LDADD = -lpthread
{
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)),
"r" (offset), "r" (val)
--- /dev/null
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <time.h>
+
+#define COUNT 10
+
+static int race;
+static __thread int local;
+__thread int global;
+extern __thread int static_extern;
+extern __thread int so_extern;
+
+/* deliberate failure */
+static int *test_race(void)
+{
+ return ∽̱
+}
+
+static int *test_local(void)
+{
+ return &local;
+}
+
+static int *test_global(void)
+{
+ return &global;
+}
+
+static int *test_static_extern(void)
+{
+ return &static_extern;
+}
+
+static int *test_so_extern(void)
+{
+ return &so_extern;
+}
+
+static const struct timespec awhile = { 0, 100000000 };
+
+typedef int *(*func_t)(void);
+struct testcase {
+ const char *name;
+ func_t func;
+};
+
+static void *tls_ptr(void *p)
+{
+ struct testcase *test = (struct testcase *)p;
+ int *ip = (*test->func)();
+ int here = 0;
+ int i;
+
+ for(i = 0; i < COUNT; i++) {
+ int a = (*ip)++;
+ int b = here++;
+ if (a != b)
+ printf("tls_ptr: case \"%s\" has mismatch: *ip=%d here=%d\n",
+ test->name, a, b);
+ nanosleep(&awhile, 0);
+ }
+
+ return 0;
+}
+
+int *test_so_extern(void);
+int *test_so_local(void);
+int *test_so_global(void);
+
+static const struct testcase tests[] = {
+#define T(t) { #t, test_##t }
+ T(race),
+ T(local),
+ T(global),
+ T(static_extern),
+ T(so_extern),
+ T(so_local),
+ T(so_global),
+#undef T
+};
+
+#define NTESTS (sizeof(tests)/sizeof(*tests))
+
+int main()
+{
+ pthread_t threads[NTESTS*2];
+ int curthread = 0;
+ static
+ int i;
+
+ for(i = 0; i < NTESTS; i++) {
+ pthread_create(&threads[curthread++], NULL, tls_ptr, (void *)&tests[i]);
+ pthread_create(&threads[curthread++], NULL, tls_ptr, (void *)&tests[i]);
+ }
+
+ for(i = 0; i < curthread; i++)
+ pthread_join(threads[i], NULL);
+
+ return 0;
+}
--- /dev/null
+tls_ptr: case "race" has mismatch: *ip=1 here=0
+tls_ptr: case "race" has mismatch: *ip=2 here=1
+tls_ptr: case "race" has mismatch: *ip=3 here=1
+tls_ptr: case "race" has mismatch: *ip=4 here=2
+tls_ptr: case "race" has mismatch: *ip=5 here=2
+tls_ptr: case "race" has mismatch: *ip=6 here=3
+tls_ptr: case "race" has mismatch: *ip=7 here=3
+tls_ptr: case "race" has mismatch: *ip=8 here=4
+tls_ptr: case "race" has mismatch: *ip=9 here=4
+tls_ptr: case "race" has mismatch: *ip=10 here=5
+tls_ptr: case "race" has mismatch: *ip=11 here=5
+tls_ptr: case "race" has mismatch: *ip=12 here=6
+tls_ptr: case "race" has mismatch: *ip=13 here=6
+tls_ptr: case "race" has mismatch: *ip=14 here=7
+tls_ptr: case "race" has mismatch: *ip=15 here=7
+tls_ptr: case "race" has mismatch: *ip=16 here=8
+tls_ptr: case "race" has mismatch: *ip=17 here=8
+tls_ptr: case "race" has mismatch: *ip=18 here=9
+tls_ptr: case "race" has mismatch: *ip=19 here=9
--- /dev/null
+__thread int static_extern;
--- /dev/null
+__thread int so_extern;
--- /dev/null
+#include <pthread.h>
+
+extern __thread int so_extern;
+static __thread int so_local;
+extern __thread int global;
+
+int *test_so_extern(void)
+{
+ return &so_extern;
+}
+
+int *test_so_local(void)
+{
+ return &so_local;
+}
+
+int *test_so_global(void)
+{
+ return &global;
+}