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.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@2215
This commit is contained in:
Jeremy Fitzhardinge 2004-01-21 01:27:27 +00:00
parent e821cecc7c
commit 75d6dc8434
21 changed files with 655 additions and 86 deletions

View File

@ -108,7 +108,7 @@ libpthread_so_SOURCES = \
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

View File

@ -472,7 +472,6 @@ static void list_tools(void)
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.
@ -488,13 +487,10 @@ static char **fix_environment(char **origenv, const char *preload)
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);
@ -570,9 +566,6 @@ static char **fix_environment(char **origenv, const char *preload)
*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 = "";
}
@ -599,9 +592,6 @@ static char **fix_environment(char **origenv, const char *preload)
ret[envc++] = cp;
}
if (!ld_assume_kernel_done)
ret[envc++] = "LD_ASSUME_KERNEL=2.4.1";
ret[envc] = NULL;

View File

@ -319,6 +319,21 @@ void get_needed_regs(ThreadId tid, Addr* eip, Addr* ebp, Addr* esp,
*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 )

View File

@ -624,11 +624,19 @@ extern VgLdtEntry*
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 );
@ -803,6 +811,10 @@ typedef
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.
@ -1622,6 +1634,9 @@ __attribute__ ((__noreturn__))
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
@ -1830,6 +1845,9 @@ extern Int VGOFF_(sh_eflags);
/* 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);

View File

@ -121,6 +121,24 @@ void VG_(deallocate_LDT_for_thread) ( VgLdtEntry* ldt )
/* 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. */
@ -151,9 +169,9 @@ unsigned int wine_ldt_get_limit( const VgLdtEntry *ent )
*/
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",
@ -161,31 +179,48 @@ Addr VG_(do_useseg) ( UInt seg_selector, Addr virtual_addr )
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
@ -199,6 +234,10 @@ Addr VG_(do_useseg) ( UInt seg_selector, Addr virtual_addr )
);
}
if (0)
VG_(printf)("do_useseg: base = %p, addr = %p\n",
base, base + virtual_addr);
return base + virtual_addr;
}
@ -367,6 +406,63 @@ 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 ---*/
/*--------------------------------------------------------------------*/

View File

@ -60,6 +60,10 @@
#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>
@ -106,6 +110,9 @@ int is_kerror ( int res )
extern __locale_t __uselocale ( __locale_t );
#endif
static
void init_thread_specific_state ( void );
static
void init_libc_tsd_keys ( void );
@ -523,6 +530,38 @@ int pthread_getconcurrency(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
@ -569,7 +608,13 @@ void thread_exit_wrapper ( void* ret_val )
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,
@ -600,12 +645,25 @@ void thread_exit_wrapper ( void* ret_val )
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
@ -614,15 +672,50 @@ static
__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);
@ -633,6 +726,9 @@ void thread_wrapper ( NewThreadInfo* 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
@ -688,6 +784,7 @@ pthread_create (pthread_t *__restrict __thredd,
{
int tid_child;
NewThreadInfo* info;
int gs;
ensure_valgrind("pthread_create");
@ -705,6 +802,29 @@ pthread_create (pthread_t *__restrict __thredd,
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 */,
@ -1619,17 +1739,33 @@ 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");
VALGRIND_MAGIC_SEQUENCE(tid, 1 /* default */,
@ -1638,16 +1774,17 @@ int* __errno_location ( void )
/* '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;
@ -1658,14 +1795,17 @@ int* __h_errno_location ( void )
/* '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;
@ -1676,9 +1816,18 @@ struct __res_state* __res_state ( void )
/* '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;
}
@ -2331,7 +2480,6 @@ int sem_init(sem_t *sem, int pshared, unsigned int value)
return 0;
}
int sem_wait ( sem_t* sem )
{
int res;
@ -2918,15 +3066,20 @@ weak_alias(pthread_rwlock_tryrdlock, __pthread_rwlock_tryrdlock)
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
@ -2934,6 +3087,7 @@ void _IO_funlockfile ( _IO_FILE * file )
{
pthread_mutex_unlock(file->_lock);
}
strong_alias(_IO_funlockfile, __funlockfile);
weak_alias(_IO_funlockfile, funlockfile);
#endif

View File

@ -58,6 +58,7 @@ Int VGOFF_(m_eflags) = INVALID_OFFSET;
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;
@ -327,8 +328,9 @@ static void vg_init_baseBlock ( void )
== 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);
@ -448,6 +450,9 @@ static void vg_init_baseBlock ( void )
/* 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)] =

View File

@ -1373,6 +1373,15 @@ void VG_(proxy_sanity)(void)
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 ---*/
/*--------------------------------------------------------------------*/

View File

@ -339,6 +339,7 @@ void VG_(load_thread_state) ( ThreadId tid )
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;
@ -420,6 +421,19 @@ void VG_(save_thread_state) ( ThreadId tid )
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)];
@ -469,6 +483,7 @@ void VG_(save_thread_state) ( ThreadId tid )
/* 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;
@ -538,6 +553,7 @@ void mostly_clear_thread_record ( ThreadId tid )
{
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;
@ -596,6 +612,7 @@ void VG_(scheduler_init) ( void )
/* 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
@ -1372,6 +1389,9 @@ void cleanup_after_thread_exited ( ThreadId tid, Bool forcekill )
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;
@ -1858,6 +1878,10 @@ void do__apply_in_new_thread ( ThreadId parent_tid,
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;
@ -2075,8 +2099,12 @@ 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. */
if ((ThreadId)mutex->__m_owner == tid) {

View File

@ -1046,15 +1046,6 @@ PRE(exit)
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,
@ -1184,6 +1175,33 @@ 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)
{
/* int setresgid(gid_t rgid, gid_t egid, gid_t sgid); */
@ -2141,6 +2159,24 @@ 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)
{
/* int fsync(int fd); */
@ -4727,6 +4763,27 @@ PRE(utimes)
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)
@ -4927,6 +4984,8 @@ static const struct sys_info special_sys[] = {
SYSB_(clone, False),
SYSB_(modify_ldt, False),
SYSB_(set_thread_area, False),
SYSB_(get_thread_area, False),
SYSB_(execve, False),
SYSB_(brk, False),
@ -5126,6 +5185,7 @@ static const struct sys_info sys_info[] = {
SYSBA(adjtimex, False),
SYSBA(mmap2, False),
SYSBA(clock_gettime, False),
SYSBA(futex, True),
/* new signal handling makes these normal blocking syscalls */
SYSB_(pause, True),

View File

@ -129,6 +129,11 @@
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
@ -191,3 +196,21 @@
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
}

View File

@ -631,12 +631,16 @@ typedef struct vki_modify_ldt_ldt_s {
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 */
@ -726,6 +730,15 @@ struct statfs64 {
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 */

View File

@ -42,6 +42,7 @@ EXTRA_DIST = $(noinst_SCRIPTS) \
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 \
@ -53,7 +54,7 @@ check_PROGRAMS = \
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
@ -95,9 +96,21 @@ shortpush_SOURCES = shortpush.c
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

View File

@ -110,8 +110,8 @@ void ldt_seg_write ( int ldt_entno, unsigned offset, unsigned val )
{
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)

101
none/tests/tls.c Normal file
View File

@ -0,0 +1,101 @@
#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 &race;
}
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;
}

View File

@ -0,0 +1,2 @@

19
none/tests/tls.stdout.exp Normal file
View File

@ -0,0 +1,19 @@
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

1
none/tests/tls.vgtest Normal file
View File

@ -0,0 +1 @@
prog: tls

1
none/tests/tls2.c Normal file
View File

@ -0,0 +1 @@
__thread int static_extern;

1
none/tests/tls2_so.c Normal file
View File

@ -0,0 +1 @@
__thread int so_extern;

20
none/tests/tls_so.c Normal file
View File

@ -0,0 +1,20 @@
#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;
}