mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-03 18:13:01 +00:00
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:
parent
e821cecc7c
commit
75d6dc8434
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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 )
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 ---*/
|
||||
/*--------------------------------------------------------------------*/
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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)] =
|
||||
|
||||
@ -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 ---*/
|
||||
/*--------------------------------------------------------------------*/
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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 */
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
101
none/tests/tls.c
Normal 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 ∽̱
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
2
none/tests/tls.stderr.exp
Normal file
2
none/tests/tls.stderr.exp
Normal file
@ -0,0 +1,2 @@
|
||||
|
||||
|
||||
19
none/tests/tls.stdout.exp
Normal file
19
none/tests/tls.stdout.exp
Normal 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
1
none/tests/tls.vgtest
Normal file
@ -0,0 +1 @@
|
||||
prog: tls
|
||||
1
none/tests/tls2.c
Normal file
1
none/tests/tls2.c
Normal file
@ -0,0 +1 @@
|
||||
__thread int static_extern;
|
||||
1
none/tests/tls2_so.c
Normal file
1
none/tests/tls2_so.c
Normal file
@ -0,0 +1 @@
|
||||
__thread int so_extern;
|
||||
20
none/tests/tls_so.c
Normal file
20
none/tests/tls_so.c
Normal 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;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user