]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
This change implements the TLS extension to the x86 ABI. This allows
authorJeremy Fitzhardinge <jeremy@valgrind.org>
Wed, 21 Jan 2004 01:27:27 +0000 (01:27 +0000)
committerJeremy Fitzhardinge <jeremy@valgrind.org>
Wed, 21 Jan 2004 01:27:27 +0000 (01:27 +0000)
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

21 files changed:
coregrind/Makefile.am
coregrind/stage2.c
coregrind/vg_execontext.c
coregrind/vg_include.h
coregrind/vg_ldt.c
coregrind/vg_libpthread.c
coregrind/vg_main.c
coregrind/vg_proxylwp.c
coregrind/vg_scheduler.c
coregrind/vg_syscalls.c
glibc-2.3.supp
include/vg_kerneliface.h
none/tests/Makefile.am
none/tests/seg_override.c
none/tests/tls.c [new file with mode: 0644]
none/tests/tls.stderr.exp [new file with mode: 0644]
none/tests/tls.stdout.exp [new file with mode: 0644]
none/tests/tls.vgtest [new file with mode: 0644]
none/tests/tls2.c [new file with mode: 0644]
none/tests/tls2_so.c [new file with mode: 0644]
none/tests/tls_so.c [new file with mode: 0644]

index 473297fed2bb1984d4bf892765705bdcd430d509..94587ff91deb6ada1a80f431d487d1348d493366 100644 (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
index 3f26fbbc8b03c7b96da20fda03edd1586a1da53f..8315824551ec234344519cfc4f1e3277a571fab6 100644 (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;
 
index 5c5c7945d699d0031e3710ce8c0caf1c93ac7492..f2e9752e29a09e8f516b09a27c2381eb656b3f7e 100644 (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 )
index 8681485ba79c7600aad22f8ae97fd3fd4d72328b..8cc1c8b6dce4c5fd27bc3b25ccf0a01e61a5f828 100644 (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);
index ca5390da5f14eb15efd44710907a971a3d8674d3..5edb82170b02b9f0103991fd7e11be74aa16fef0 100644 (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 ---*/
 /*--------------------------------------------------------------------*/
index 11e7ecb18f9651a4cd85d77ccfaf236300b5ec83..02d1a61ca9caa58dc9c9de9c017d4efa0ef557c0 100644 (file)
 #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
 
index b1a74a4b09f154f97122eb677ff50ee6745e0e20..5e87e6296f64b277910a12d36882eb33a337c1b7 100644 (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)]    = 
index cd76a1235cc6d730daa36caee9f5302933e1d05e..55cd12a415131ca7bfa79f72030ff2866ac99067 100644 (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 ---*/
 /*--------------------------------------------------------------------*/
index 3a8d7ce1e08191db74ede4da073427acdfc6bff3..850f6685456458232141d3c7e585e3b0813661cf 100644 (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) {
index ee591732933b127098157c63783572c559ed06b8..b36245a9201ea19ebee5f49acc10520ec0d38a65 100644 (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),
index c0ba8bf937ffacb582d74a5b402865db8bf71e6b..22827b43ca5171febbeb1d67837033b76cb2f42b 100644 (file)
    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
+}
index 1767e32c360c61961ed5df74aba54a8eabea27aa..2df0a93d2e25ad32f3d4bc8808b1578219b8a1f5 100644 (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 */
 
index c6e23d08ce28194ef6a35af0be6c03072a911701..1e503c131cb07ba01d9fdb8c6dea2b8bc855f6e9 100644 (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
index 805bfda370c600db7994ab93504f65c2cebcb499..0f8cb2d8cfde935ed61ff51523023edd0735a08c 100644 (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)
diff --git a/none/tests/tls.c b/none/tests/tls.c
new file mode 100644 (file)
index 0000000..78993c2
--- /dev/null
@@ -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;
+}
diff --git a/none/tests/tls.stderr.exp b/none/tests/tls.stderr.exp
new file mode 100644 (file)
index 0000000..139597f
--- /dev/null
@@ -0,0 +1,2 @@
+
+
diff --git a/none/tests/tls.stdout.exp b/none/tests/tls.stdout.exp
new file mode 100644 (file)
index 0000000..dbb4134
--- /dev/null
@@ -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
diff --git a/none/tests/tls.vgtest b/none/tests/tls.vgtest
new file mode 100644 (file)
index 0000000..c27bf7f
--- /dev/null
@@ -0,0 +1 @@
+prog: tls
diff --git a/none/tests/tls2.c b/none/tests/tls2.c
new file mode 100644 (file)
index 0000000..6883882
--- /dev/null
@@ -0,0 +1 @@
+__thread int static_extern;
diff --git a/none/tests/tls2_so.c b/none/tests/tls2_so.c
new file mode 100644 (file)
index 0000000..d0a9a26
--- /dev/null
@@ -0,0 +1 @@
+__thread int so_extern;
diff --git a/none/tests/tls_so.c b/none/tests/tls_so.c
new file mode 100644 (file)
index 0000000..492507e
--- /dev/null
@@ -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;
+}