make sure tls static block is initialized
authorMathieu Lacage <mathieu.lacage@sophia.inria.fr>
Thu, 16 Sep 2010 10:00:26 +0200
changeset 608 0fe0080331a8
parent 607 c50c2680a03e
child 609 5f1f625e78f9
make sure tls static block is initialized
stage1.c
vdl-dl.c
vdl-tls.c
vdl-tls.h
vdl.h
--- a/stage1.c	Thu Sep 16 10:00:07 2010 +0200
+++ b/stage1.c	Thu Sep 16 10:00:26 2010 +0200
@@ -87,7 +87,7 @@
   vdl->tls_static_current_size = 0;
   vdl->tls_static_align = 0;
   vdl->tls_n_dtv = 0;
-  vdl->tls_tcb_created = false;
+  vdl->tls_next_index = 1;
   vdl->futex = futex_new ();
   vdl->errors = vdl_list_new ();
   vdl->n_added = 0;
--- a/vdl-dl.c	Thu Sep 16 10:00:07 2010 +0200
+++ b/vdl-dl.c	Thu Sep 16 10:00:26 2010 +0200
@@ -22,6 +22,9 @@
 #include "vdl-init.h"
 #include "vdl-fini.h"
 
+// reuse glibc flag.
+#define __RTLD_OPENEXEC 0x20000000
+
 static struct VdlError *find_error (void)
 {
   unsigned long thread_pointer = machine_thread_pointer_get ();
@@ -176,6 +179,11 @@
       goto error;
     }
 
+  if (flags & __RTLD_OPENEXEC)
+    {
+      map.requested->is_executable = 1;
+    }
+
   bool ok = vdl_tls_file_initialize (map.newly_mapped);
 
   if (!ok)
@@ -235,6 +243,17 @@
   vdl_linkmap_append_range (vdl_list_begin (map.newly_mapped),
 			    vdl_list_end (map.newly_mapped));
 
+  // now, we want to update the dtv of _this_ thread.
+  // i.e., we can't touch the dtv of the other threads 
+  // because of locking issues so, if the code we loaded
+  // uses the tls direct model to access the static block
+  // and if any of the other threads try to call in this code
+  // and if it tries to access the static tls block directly,
+  // BOOOOM. nasty. anyway, we protect the caller if it tries to
+  // access these tls static blocks by updating the dtv forcibly here
+  // this indirectly initializes the content of the tls static area.
+  vdl_tls_dtv_update ();
+
   gdb_notify ();
 
   glibc_patch (map.newly_mapped);
--- a/vdl-tls.c	Thu Sep 16 10:00:07 2010 +0200
+++ b/vdl-tls.c	Thu Sep 16 10:00:26 2010 +0200
@@ -13,41 +13,6 @@
 
 #define TLS_EXTRA_STATIC_ALLOC 1000
 
-static unsigned long
-allocate_tls_index (void)
-{
-  // This is the slowest but simplest implementation possible
-  // For each possible module index, we try to find a module
-  // which has been already assigned that module index.
-  // If it has been already assigned, we try another one, otherwise,
-  // we return it.
-  unsigned long i;
-  unsigned long ul_max = 0;
-  ul_max = ~ul_max;
-  struct VdlList *linkmap = vdl_linkmap_copy ();
-  for (i = 1; i < ul_max; i++)
-    {
-      void **cur;
-      for (cur = vdl_list_begin (linkmap); cur != vdl_list_end (linkmap); cur = vdl_list_next (cur))
-	{
-	  struct VdlFile *item = *cur;
-	  if (item->tls_initialized &&
-	      item->has_tls && 
-	      item->tls_index == i)
-	    {
-	      break;
-	    }
-	}
-      if (cur == vdl_list_end (linkmap))
-	{
-	  vdl_list_delete (linkmap);
-	  return i;
-	}
-    }
-  VDL_LOG_ASSERT (0, "All tls module indexes are used up ? impossible !");
-  return 0; // quiet compiler
-}
-
 static void
 file_initialize (struct VdlFile *file)
 {
@@ -65,18 +30,21 @@
       file->tls_initialized = 1;
       return;
     }
-  // note that the call below is _before tls_initialized = 1 because
-  // it does matter. allocate_tls_index looks into the global g_vdl.link_map
-  unsigned long tls_index = allocate_tls_index ();
   file->has_tls = 1;
   file->tls_initialized = 1;
   file->tls_tmpl_start = file->load_base + pt_tls->p_vaddr;
   file->tls_tmpl_size = pt_tls->p_filesz;
   file->tls_init_zero_size = pt_tls->p_memsz - pt_tls->p_filesz;
   file->tls_align = pt_tls->p_align;
-  file->tls_index = tls_index;
+  file->tls_index = g_vdl.tls_next_index;
   file->tls_is_static = (dt_flags & DF_STATIC_TLS)?1:0;
   file->tls_tmpl_gen = g_vdl.tls_gen;
+  // XXX: the next_index increment code below is bad for many reasons.
+  // Instead, we should try to reuse tls indexes that are not used anymore
+  // to ensure that the tls index we use is as small as possible to ensure
+  // that the dtv array is as small as possible. we should keep
+  // track of all allocated indexes in a global list.
+  g_vdl.tls_next_index++;
   g_vdl.tls_gen++;
   g_vdl.tls_n_dtv++;
   VDL_LOG_DEBUG ("file=%s tmpl_size=%lu zero_size=%lu\n", 
@@ -126,9 +94,9 @@
   // static and local tls model. We also initialize correctly
   // the tls_offset field to be able to perform relocations
   // next (the TLS relocations need the tls_offset field).
-  unsigned long tcb_size = 0;
+  unsigned long tcb_size = g_vdl.tls_static_current_size;
   unsigned long n_dtv = 0;
-  unsigned long max_align = 1;
+  unsigned long max_align = g_vdl.tls_static_align;
   void **cur;
   for (cur = vdl_list_begin (list); 
        cur != vdl_list_end (list); 
@@ -161,11 +129,10 @@
 {
   file_list_initialize (files);
   struct static_tls static_tls = initialize_static_tls (files);
-  long new_size = vdl_utils_align_up (g_vdl.tls_static_current_size + static_tls.size, 
-				      static_tls.align);
-  if (new_size < g_vdl.tls_static_total_size)
+  if (static_tls.size < g_vdl.tls_static_total_size)
     {
-      g_vdl.tls_static_current_size = new_size;
+      g_vdl.tls_static_current_size = static_tls.size;
+      g_vdl.tls_static_align = static_tls.align;
       return true;
     }
   return false;
@@ -208,11 +175,10 @@
   file_list_initialize (list);
   // then perform initial setup of the static tls area
   struct static_tls static_tls = initialize_static_tls (list);
-  g_vdl.tls_static_current_size = vdl_utils_align_up (static_tls.size, static_tls.align);
+  g_vdl.tls_static_current_size = static_tls.size;
   g_vdl.tls_static_total_size = vdl_utils_align_up (g_vdl.tls_static_current_size + TLS_EXTRA_STATIC_ALLOC,
 						    static_tls.align);
   g_vdl.tls_static_align = static_tls.align;
-  g_vdl.tls_tcb_created = false;
 }
 
 unsigned long
@@ -220,7 +186,6 @@
 {
   // we allocate continuous memory for the set of tls blocks + libpthread TCB
   unsigned long tcb_size = g_vdl.tls_static_total_size;
-  g_vdl.tls_tcb_created = true;
   unsigned long total_size = tcb_size + CONFIG_TCB_SIZE; // specific to variant II
   unsigned long buffer = (unsigned long) vdl_alloc_malloc (total_size);
   vdl_memset ((void*)buffer, 0, total_size);
@@ -361,15 +326,17 @@
   vdl_memcpy (&dtv, (void*)(tp+CONFIG_TCB_DTV_OFFSET), sizeof (dtv));
   return dtv;
 }
-static void
-update_dtv (void)
+void
+vdl_tls_dtv_update (void)
 {
   VDL_LOG_FUNCTION ("");
   unsigned long tp = machine_thread_pointer_get ();
   struct dtv_t *dtv = get_current_dtv ();
   unsigned long dtv_size = dtv[-1].value;
-  VDL_LOG_ASSERT (dtv[0].gen != g_vdl.tls_gen, 
-		  "dtv is not up-to-date");
+  if (dtv[0].gen == g_vdl.tls_gen)
+    {
+      return;
+    }
 
   // first, we update the currently-available entries of the dtv.
   {
@@ -442,8 +409,17 @@
 	  // nothing to do here, just skip this empty entry
 	  continue;
 	}
-      VDL_LOG_ASSERT (!file->tls_is_static, "tls modules with static tls blocks should never"
-		      "be initialized here");
+      if (file->tls_is_static)
+	{
+	  signed long tcb = machine_thread_pointer_get ();
+	  signed long dtvi = tcb + file->tls_offset;
+	  new_dtv[file->tls_index].value = dtvi;
+	  new_dtv[file->tls_index].is_static = 1;
+	  new_dtv[file->tls_index].gen = file->tls_tmpl_gen;
+	  // copy the template in the module tls block
+	  vdl_memcpy ((void*)dtvi, (void*)file->tls_tmpl_start, file->tls_tmpl_size);
+	  vdl_memset ((void*)(dtvi + file->tls_tmpl_size), 0, file->tls_init_zero_size);
+	}
     }
   // now that the dtv is updated, update the generation
   new_dtv[0].gen = g_vdl.tls_gen;
@@ -494,7 +470,7 @@
       return dtv[module].value + offset;
     }
   // we know for sure that the dtv is _not_ uptodate now
-  update_dtv ();
+  vdl_tls_dtv_update ();
 
   // now that the dtv is supposed to be uptodate, attempt to make
   // the request again
--- a/vdl-tls.h	Thu Sep 16 10:00:07 2010 +0200
+++ b/vdl-tls.h	Thu Sep 16 10:00:26 2010 +0200
@@ -29,6 +29,9 @@
 // the _slow version needs a lock held
 unsigned long vdl_tls_get_addr_slow (unsigned long module, unsigned long offset);
 
+// ensure that the caller dtv is uptodate.
+void vdl_tls_dtv_update (void);
+
 void vdl_tls_file_deinitialize (struct VdlList *files);
 
 #endif /* VDL_TLS_H */
--- a/vdl.h	Thu Sep 16 10:00:07 2010 +0200
+++ b/vdl.h	Thu Sep 16 10:00:26 2010 +0200
@@ -57,7 +57,7 @@
   unsigned long tls_static_current_size;
   unsigned long tls_static_align;
   unsigned long tls_n_dtv;
-  bool tls_tcb_created;
+  unsigned long tls_next_index;
   struct Futex *futex;
   // holds an entry for each thread which calls one a function
   // which potentially sets the dlerror state.