Explorar o código

libsanitizer: ldso: implement _dl_tls_get_addr_soft for dl_phdr_info::dlpi_tls_data

glibc populates dl_phdr_info::dlpi_tls_data with the per-thread
address of the calling thread's instance of the module's PT_TLS
segment, or NULL if the thread has not yet faulted in that block.
The 'soft' variant is essential: callers (libsanitizer, valgrind,
profilers) want to observe TLS state without forcing lazy
allocation as a side effect.  Calling __tls_get_addr instead would
allocate the TLS block, which silently changes program behaviour.

uClibc-ng's __tls_get_addr already does the lookup-and-allocate
path; factor out the read-only lookup as _dl_tls_get_addr_soft and
wire it into dl_iterate_phdr's per-module callback (under SHARED;
static libc has no DTV and stays at NULL).

Together with patches 0001 and 0005 this makes uClibc-ng's
dl_phdr_info contract fully glibc-conformant: dlpi_tls_modid live,
dlpi_tls_data live, dlpi_adds / dlpi_subs live counters.

Signed-off-by: Ramin Moussavi <ramin.moussavi@yacoub.de>
Ramin Moussavi hai 3 semanas
pai
achega
fec69a435b
Modificáronse 3 ficheiros con 34 adicións e 2 borrados
  1. 2 0
      ldso/include/dl-hash.h
  2. 25 0
      ldso/ldso/dl-tls.c
  3. 7 2
      libc/misc/elf/dl-iterate-phdr.c

+ 2 - 0
ldso/include/dl-hash.h

@@ -160,6 +160,8 @@ extern struct elf_resolve * _dl_loaded_modules;
 extern struct dyn_elf     * _dl_handles;
 extern unsigned long long   _dl_load_adds;
 extern unsigned long long   _dl_load_subs;
+struct link_map;
+extern void               * _dl_tls_get_addr_soft(struct link_map *map);
 
 extern struct elf_resolve * _dl_add_elf_hash_table(const char * libname,
 	DL_LOADADDR_TYPE loadaddr, unsigned long * dynamic_info,

+ 25 - 0
ldso/ldso/dl-tls.c

@@ -886,6 +886,31 @@ __tls_get_addr (GET_ADDR_ARGS)
   return (char *) p + GET_ADDR_OFFSET;
 }
 
+/* Like __tls_get_addr but never allocates and returns NULL when the
+   calling thread has not yet touched MAP's TLS block.  Mirrors glibc's
+   dl_tls_get_addr_soft and feeds dl_phdr_info::dlpi_tls_data without
+   side effects, so callers (libsanitizer, valgrind, profilers) can
+   observe per-thread TLS state without forcing lazy allocation.  */
+void *
+_dl_tls_get_addr_soft (struct link_map *map)
+{
+  dtv_t *dtv;
+  void *p;
+
+  if (map->l_tls_modid == 0)
+    return NULL;
+
+  dtv = THREAD_DTV ();
+  if (map->l_tls_modid > (size_t) dtv[-1].counter)
+    return NULL;
+
+  p = dtv[map->l_tls_modid].pointer.val;
+  if (p == TLS_DTV_UNALLOCATED)
+    return NULL;
+
+  return p;
+}
+
 void
 _dl_add_to_slotinfo (struct link_map  *l)
 {

+ 7 - 2
libc/misc/elf/dl-iterate-phdr.c

@@ -35,11 +35,16 @@ __dl_iterate_phdr (int (*callback) (struct dl_phdr_info *info, size_t size, void
 		info.dlpi_subs = _dl_load_subs;
 #if defined(USE_TLS) && USE_TLS
 		info.dlpi_tls_modid = l->l_tls_modid;
+# ifdef SHARED
+		/* Resolves into ld.so at runtime; static libc has no DTV.  */
+		info.dlpi_tls_data = _dl_tls_get_addr_soft((struct link_map *) l);
+# else
+		info.dlpi_tls_data = NULL;
+# endif
 #else
 		info.dlpi_tls_modid = 0;
-#endif
-		/* libsanitizer falls back to __tls_get_addr() on non-glibc.  */
 		info.dlpi_tls_data = NULL;
+#endif
 		ret = callback (&info, sizeof (struct dl_phdr_info), data);
 		if (ret)
 			break;