diff mbox series

[v3,16/32] elf: Move l_tls_offset into read-write part of link map

Message ID 4ec709cdae5e8bbdc2f4c14a2b030b8b21b0e11f.1701944612.git.fweimer@redhat.com
State New
Headers show
Series RELRO linkmaps | expand

Commit Message

Florian Weimer Dec. 7, 2023, 10:32 a.m. UTC
This field is written concurrently from the dynamic TLS lazy
allocation code, so it runs outside the main loader lock.  The
protected memory allocator relies on that lock for synchronization.

The early initialization of l_tls_offset in _dl_start is not needed.
The dynamic loader does not have ELF TLS.
---
 csu/libc-tls.c                         |  4 +--
 elf/dl-close.c                         | 40 ++++++++++++++------------
 elf/dl-object.c                        |  2 +-
 elf/dl-reloc.c                         | 15 +++++-----
 elf/dl-static-tls.h                    |  8 +++---
 elf/dl-support.c                       |  6 ++--
 elf/dl-tls.c                           | 35 +++++++++++-----------
 elf/rtld.c                             | 13 ++-------
 htl/pt-alloc.c                         |  5 ++--
 include/link.h                         | 36 ++++++++++++++---------
 nptl/Versions                          |  3 +-
 nptl_db/db_info.c                      |  1 +
 nptl_db/structs.def                    |  3 +-
 nptl_db/td_thr_tlsbase.c               | 12 ++++++--
 sysdeps/aarch64/dl-machine.h           |  5 ++--
 sysdeps/alpha/dl-machine.h             |  4 +--
 sysdeps/arc/dl-machine.h               |  3 +-
 sysdeps/arm/dl-machine.h               |  4 +--
 sysdeps/csky/dl-machine.h              |  2 +-
 sysdeps/hppa/dl-machine.h              |  3 +-
 sysdeps/i386/dl-machine.h              | 11 +++----
 sysdeps/ia64/dl-machine.h              |  2 +-
 sysdeps/loongarch/dl-tls.h             |  2 +-
 sysdeps/m68k/dl-tls.h                  |  2 +-
 sysdeps/microblaze/dl-machine.h        |  3 +-
 sysdeps/mips/dl-tls.h                  |  2 +-
 sysdeps/nios2/dl-tls.h                 |  2 +-
 sysdeps/or1k/dl-machine.h              |  4 +--
 sysdeps/powerpc/dl-tls.h               |  2 +-
 sysdeps/powerpc/powerpc32/dl-machine.h |  4 +--
 sysdeps/powerpc/powerpc64/dl-machine.h |  4 +--
 sysdeps/riscv/dl-tls.h                 |  2 +-
 sysdeps/s390/s390-32/dl-machine.h      |  5 ++--
 sysdeps/s390/s390-64/dl-machine.h      |  5 ++--
 sysdeps/sh/dl-machine.h                |  7 +++--
 sysdeps/sparc/sparc32/dl-machine.h     |  4 +--
 sysdeps/sparc/sparc64/dl-machine.h     |  4 +--
 sysdeps/x86_64/dl-machine.h            |  5 ++--
 38 files changed, 151 insertions(+), 123 deletions(-)

Comments

Joseph Myers Feb. 26, 2024, 9:57 p.m. UTC | #1
On Thu, 7 Dec 2023, Florian Weimer wrote:

> This field is written concurrently from the dynamic TLS lazy
> allocation code, so it runs outside the main loader lock.  The
> protected memory allocator relies on that lock for synchronization.
> 
> The early initialization of l_tls_offset in _dl_start is not needed.
> The dynamic loader does not have ELF TLS.

OK.
diff mbox series

Patch

diff --git a/csu/libc-tls.c b/csu/libc-tls.c
index 7a3238789d..2a502874a7 100644
--- a/csu/libc-tls.c
+++ b/csu/libc-tls.c
@@ -174,10 +174,10 @@  __libc_setup_tls (void)
 #if TLS_TCB_AT_TP
   _dl_static_dtv[2].pointer.val = ((char *) tlsblock + tcb_offset
 			       - roundup (memsz, align ?: 1));
-  main_map->l_tls_offset = roundup (memsz, align ?: 1);
+  main_map->l_rw->l_tls_offset = roundup (memsz, align ?: 1);
 #elif TLS_DTV_AT_TP
   _dl_static_dtv[2].pointer.val = (char *) tlsblock + tcb_offset;
-  main_map->l_tls_offset = tcb_offset;
+  main_map->l_rw->l_tls_offset = tcb_offset;
 #else
 # error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
 #endif
diff --git a/elf/dl-close.c b/elf/dl-close.c
index 1af60845f5..f242dcee9e 100644
--- a/elf/dl-close.c
+++ b/elf/dl-close.c
@@ -513,8 +513,8 @@  _dl_close_worker (struct link_map_private *map, bool force)
 		atomic_store_relaxed (&GL(dl_tls_max_dtv_idx),
 				      GL(dl_tls_static_nelem));
 
-	      if (imap->l_tls_offset != NO_TLS_OFFSET
-		  && imap->l_tls_offset != FORCED_DYNAMIC_TLS_OFFSET)
+	      if (imap->l_rw->l_tls_offset != NO_TLS_OFFSET
+		  && imap->l_rw->l_tls_offset != FORCED_DYNAMIC_TLS_OFFSET)
 		{
 		  /* Collect a contiguous chunk built from the objects in
 		     this search list, going in either direction.  When the
@@ -522,19 +522,19 @@  _dl_close_worker (struct link_map_private *map, bool force)
 		     reclaim it.  */
 #if TLS_TCB_AT_TP
 		  if (tls_free_start == NO_TLS_OFFSET
-		      || (size_t) imap->l_tls_offset == tls_free_start)
+		      || (size_t) imap->l_rw->l_tls_offset == tls_free_start)
 		    {
 		      /* Extend the contiguous chunk being reclaimed.  */
 		      tls_free_start
-			= imap->l_tls_offset - imap->l_tls_blocksize;
+			= imap->l_rw->l_tls_offset - imap->l_tls_blocksize;
 
 		      if (tls_free_end == NO_TLS_OFFSET)
-			tls_free_end = imap->l_tls_offset;
+			tls_free_end = imap->l_rw->l_tls_offset;
 		    }
-		  else if (imap->l_tls_offset - imap->l_tls_blocksize
+		  else if (imap->l_rw->l_tls_offset - imap->l_tls_blocksize
 			   == tls_free_end)
 		    /* Extend the chunk backwards.  */
-		    tls_free_end = imap->l_tls_offset;
+		    tls_free_end = imap->l_rw->l_tls_offset;
 		  else
 		    {
 		      /* This isn't contiguous with the last chunk freed.
@@ -543,19 +543,20 @@  _dl_close_worker (struct link_map_private *map, bool force)
 		      if (tls_free_end == GL(dl_tls_static_used))
 			{
 			  GL(dl_tls_static_used) = tls_free_start;
-			  tls_free_end = imap->l_tls_offset;
+			  tls_free_end = imap->l_rw->l_tls_offset;
 			  tls_free_start
 			    = tls_free_end - imap->l_tls_blocksize;
 			}
-		      else if ((size_t) imap->l_tls_offset
+		      else if ((size_t) imap->l_rw->l_tls_offset
 			       == GL(dl_tls_static_used))
 			GL(dl_tls_static_used)
-			  = imap->l_tls_offset - imap->l_tls_blocksize;
-		      else if (tls_free_end < (size_t) imap->l_tls_offset)
+			  = imap->l_rw->l_tls_offset - imap->l_tls_blocksize;
+		      else if (tls_free_end
+			       < (size_t) imap->l_rw->l_tls_offset)
 			{
 			  /* We pick the later block.  It has a chance to
 			     be freed.  */
-			  tls_free_end = imap->l_tls_offset;
+			  tls_free_end = imap->l_rw->l_tls_offset;
 			  tls_free_start
 			    = tls_free_end - imap->l_tls_blocksize;
 			}
@@ -564,34 +565,37 @@  _dl_close_worker (struct link_map_private *map, bool force)
 		  if (tls_free_start == NO_TLS_OFFSET)
 		    {
 		      tls_free_start = imap->l_tls_firstbyte_offset;
-		      tls_free_end = (imap->l_tls_offset
+		      tls_free_end = (imap->l_rw->l_tls_offset
 				      + imap->l_tls_blocksize);
 		    }
 		  else if (imap->l_tls_firstbyte_offset == tls_free_end)
 		    /* Extend the contiguous chunk being reclaimed.  */
-		    tls_free_end = imap->l_tls_offset + imap->l_tls_blocksize;
-		  else if (imap->l_tls_offset + imap->l_tls_blocksize
+		    tls_free_end = (imap->l_rw->l_tls_offset
+				    + imap->l_tls_blocksize);
+		  else if (imap->l_rw->l_tls_offset + imap->l_tls_blocksize
 			   == tls_free_start)
 		    /* Extend the chunk backwards.  */
 		    tls_free_start = imap->l_tls_firstbyte_offset;
 		  /* This isn't contiguous with the last chunk freed.
 		     One of them will be leaked unless we can free
 		     one block right away.  */
-		  else if (imap->l_tls_offset + imap->l_tls_blocksize
+		  else if (imap->l_rw->l_tls_offset + imap->l_tls_blocksize
 			   == GL(dl_tls_static_used))
 		    GL(dl_tls_static_used) = imap->l_tls_firstbyte_offset;
 		  else if (tls_free_end == GL(dl_tls_static_used))
 		    {
 		      GL(dl_tls_static_used) = tls_free_start;
 		      tls_free_start = imap->l_tls_firstbyte_offset;
-		      tls_free_end = imap->l_tls_offset + imap->l_tls_blocksize;
+		      tls_free_end = (imap->l_rw->l_tls_offset
+				      + imap->l_tls_blocksize);
 		    }
 		  else if (tls_free_end < imap->l_tls_firstbyte_offset)
 		    {
 		      /* We pick the later block.  It has a chance to
 			 be freed.  */
 		      tls_free_start = imap->l_tls_firstbyte_offset;
-		      tls_free_end = imap->l_tls_offset + imap->l_tls_blocksize;
+		      tls_free_end = (imap->l_rw->l_tls_offset
+				      + imap->l_tls_blocksize);
 		    }
 #else
 # error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
diff --git a/elf/dl-object.c b/elf/dl-object.c
index c6c0f7824b..1a9b04dd3c 100644
--- a/elf/dl-object.c
+++ b/elf/dl-object.c
@@ -137,7 +137,7 @@  _dl_new_object (char *realname, const char *libname, int type,
     new->l_used = 1;
   new->l_loader = loader;
 #if NO_TLS_OFFSET != 0
-  new->l_tls_offset = NO_TLS_OFFSET;
+  new->l_rw->l_tls_offset = NO_TLS_OFFSET;
 #endif
   new->l_ns = nsid;
 
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index cbe4fcee4c..183efadfa2 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -41,7 +41,7 @@ 
    dynamically loaded.  This can only work if there is enough surplus in
    the static TLS area already allocated for each running thread.  If this
    object's TLS segment is too big to fit, we fail with -1.  If it fits,
-   we set MAP->l_tls_offset and return 0.
+   we set MAP->l_rw->l_tls_offset and return 0.
    A portion of the surplus static TLS can be optionally used to optimize
    dynamic TLS access (with TLSDESC or powerpc TLS optimizations).
    If OPTIONAL is true then TLS is allocated for such optimization and
@@ -53,7 +53,7 @@  _dl_try_allocate_static_tls (struct link_map_private *map, bool optional)
 {
   /* If we've already used the variable with dynamic access, or if the
      alignment requirements are too high, fail.  */
-  if (map->l_tls_offset == FORCED_DYNAMIC_TLS_OFFSET
+  if (map->l_rw->l_tls_offset == FORCED_DYNAMIC_TLS_OFFSET
       || map->l_tls_align > GLRO (dl_tls_static_align))
     {
     fail:
@@ -81,7 +81,7 @@  _dl_try_allocate_static_tls (struct link_map_private *map, bool optional)
 
   size_t offset = GL(dl_tls_static_used) + use;
 
-  map->l_tls_offset = GL(dl_tls_static_used) = offset;
+  map->l_rw->l_tls_offset = GL(dl_tls_static_used) = offset;
 #elif TLS_DTV_AT_TP
   /* dl_tls_static_used includes the TCB at the beginning.  */
   size_t offset = (ALIGN_UP(GL(dl_tls_static_used)
@@ -100,7 +100,7 @@  _dl_try_allocate_static_tls (struct link_map_private *map, bool optional)
   else if (optional)
     GL(dl_tls_static_optional) -= use;
 
-  map->l_tls_offset = offset;
+  map->l_rw->l_tls_offset = offset;
   map->l_tls_firstbyte_offset = GL(dl_tls_static_used);
   GL(dl_tls_static_used) = used;
 #else
@@ -134,7 +134,7 @@  void
 __attribute_noinline__
 _dl_allocate_static_tls (struct link_map_private *map)
 {
-  if (map->l_tls_offset == FORCED_DYNAMIC_TLS_OFFSET
+  if (map->l_rw->l_tls_offset == FORCED_DYNAMIC_TLS_OFFSET
       || _dl_try_allocate_static_tls (map, false))
     {
       _dl_signal_error (0, map->l_public.l_name, NULL, N_("\
@@ -150,9 +150,10 @@  void
 _dl_nothread_init_static_tls (struct link_map_private *map)
 {
 #if TLS_TCB_AT_TP
-  void *dest = (char *) THREAD_SELF - map->l_tls_offset;
+  void *dest = (char *) THREAD_SELF - map->l_rw->l_tls_offset;
 #elif TLS_DTV_AT_TP
-  void *dest = (char *) THREAD_SELF + map->l_tls_offset + TLS_PRE_TCB_SIZE;
+  void *dest = ((char *) THREAD_SELF + map->l_rw->l_tls_offset
+		+ TLS_PRE_TCB_SIZE);
 #else
 # error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
 #endif
diff --git a/elf/dl-static-tls.h b/elf/dl-static-tls.h
index d40dd882f6..b330100beb 100644
--- a/elf/dl-static-tls.h
+++ b/elf/dl-static-tls.h
@@ -29,8 +29,8 @@ 
    can't be done, we fall back to the error that DF_STATIC_TLS is
    intended to produce.  */
 #define HAVE_STATIC_TLS(map, sym_map)					\
-    (__builtin_expect ((sym_map)->l_tls_offset != NO_TLS_OFFSET		\
-		       && ((sym_map)->l_tls_offset			\
+    (__builtin_expect ((sym_map)->l_rw->l_tls_offset != NO_TLS_OFFSET	\
+		       && ((sym_map)->l_rw->l_tls_offset		\
 			   != FORCED_DYNAMIC_TLS_OFFSET), 1))
 
 #define CHECK_STATIC_TLS(map, sym_map)					\
@@ -40,9 +40,9 @@ 
     } while (0)
 
 #define TRY_STATIC_TLS(map, sym_map)					\
-    (__builtin_expect ((sym_map)->l_tls_offset				\
+    (__builtin_expect ((sym_map)->l_rw->l_tls_offset			\
 		       != FORCED_DYNAMIC_TLS_OFFSET, 1)			\
-     && (__builtin_expect ((sym_map)->l_tls_offset != NO_TLS_OFFSET, 1)	\
+     && (__builtin_expect ((sym_map)->l_rw->l_tls_offset != NO_TLS_OFFSET, 1)\
 	 || _dl_try_allocate_static_tls (sym_map, true) == 0))
 
 int _dl_try_allocate_static_tls (struct link_map_private *map, bool optional)
diff --git a/elf/dl-support.c b/elf/dl-support.c
index 9c422baa9b..3e3eae7edc 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -81,7 +81,10 @@  int _dl_bind_not;
 static struct link_map_private _dl_main_map =
   {
     .l_public = { .l_name = (char *) "", },
-    .l_rw = &(struct link_map_rw) { },
+    .l_rw = &(struct link_map_rw)
+    {
+      .l_tls_offset = NO_TLS_OFFSET,
+    },
     .l_real = &_dl_main_map,
     .l_ns = LM_ID_BASE,
     .l_libname = &(struct libname_list) { .name = "", .dont_free = 1 },
@@ -101,7 +104,6 @@  static struct link_map_private _dl_main_map =
     .l_scope = _dl_main_map.l_scope_mem,
     .l_local_scope = { &_dl_main_map.l_searchlist },
     .l_used = 1,
-    .l_tls_offset = NO_TLS_OFFSET,
     .l_serial = 1,
   };
 
diff --git a/elf/dl-tls.c b/elf/dl-tls.c
index 68ed806c8e..da8ba37df8 100644
--- a/elf/dl-tls.c
+++ b/elf/dl-tls.c
@@ -278,7 +278,7 @@  _dl_determine_tlsoffset (void)
 
 	      /* XXX For some architectures we perhaps should store the
 		 negative offset.  */
-	      slotinfo[cnt].map->l_tls_offset = off;
+	      slotinfo[cnt].map->l_rw->l_tls_offset = off;
 	      continue;
 	    }
 	}
@@ -295,7 +295,7 @@  _dl_determine_tlsoffset (void)
 
       /* XXX For some architectures we perhaps should store the
 	 negative offset.  */
-      slotinfo[cnt].map->l_tls_offset = off;
+      slotinfo[cnt].map->l_rw->l_tls_offset = off;
     }
 
   GL(dl_tls_static_used) = offset;
@@ -322,7 +322,7 @@  _dl_determine_tlsoffset (void)
 	    off += slotinfo[cnt].map->l_tls_align;
 	  if (off + slotinfo[cnt].map->l_tls_blocksize - firstbyte <= freetop)
 	    {
-	      slotinfo[cnt].map->l_tls_offset = off - firstbyte;
+	      slotinfo[cnt].map->l_rw->l_tls_offset = off - firstbyte;
 	      freebottom = (off + slotinfo[cnt].map->l_tls_blocksize
 			    - firstbyte);
 	      continue;
@@ -333,7 +333,7 @@  _dl_determine_tlsoffset (void)
       if (off - offset < firstbyte)
 	off += slotinfo[cnt].map->l_tls_align;
 
-      slotinfo[cnt].map->l_tls_offset = off - firstbyte;
+      slotinfo[cnt].map->l_rw->l_tls_offset = off - firstbyte;
       if (off - firstbyte - offset > freetop - freebottom)
 	{
 	  freebottom = offset;
@@ -573,17 +573,17 @@  _dl_allocate_tls_init (void *result, bool init_tls)
 	  dtv[map->l_tls_modid].pointer.val = TLS_DTV_UNALLOCATED;
 	  dtv[map->l_tls_modid].pointer.to_free = NULL;
 
-	  if (map->l_tls_offset == NO_TLS_OFFSET
-	      || map->l_tls_offset == FORCED_DYNAMIC_TLS_OFFSET)
+	  if (map->l_rw->l_tls_offset == NO_TLS_OFFSET
+	      || map->l_rw->l_tls_offset == FORCED_DYNAMIC_TLS_OFFSET)
 	    continue;
 
 	  assert (map->l_tls_modid == total + cnt);
 	  assert (map->l_tls_blocksize >= map->l_tls_initimage_size);
 #if TLS_TCB_AT_TP
-	  assert ((size_t) map->l_tls_offset >= map->l_tls_blocksize);
-	  dest = (char *) result - map->l_tls_offset;
+	  assert ((size_t) map->l_rw->l_tls_offset >= map->l_tls_blocksize);
+	  dest = (char *) result - map->l_rw->l_tls_offset;
 #elif TLS_DTV_AT_TP
-	  dest = (char *) result + map->l_tls_offset;
+	  dest = (char *) result + map->l_rw->l_tls_offset;
 #else
 # error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
 #endif
@@ -872,22 +872,23 @@  tls_get_addr_tail (GET_ADDR_ARGS, dtv_t *dtv, struct link_map_private *the_map)
      variable into static storage, we'll wait until the address in the
      static TLS block is set up, and use that.  If we're undecided
      yet, make sure we make the decision holding the lock as well.  */
-  if (__glibc_unlikely (the_map->l_tls_offset
+  if (__glibc_unlikely (the_map->l_rw->l_tls_offset
 			!= FORCED_DYNAMIC_TLS_OFFSET))
     {
       __rtld_lock_lock_recursive (GL(dl_load_tls_lock));
-      if (__glibc_likely (the_map->l_tls_offset == NO_TLS_OFFSET))
+      if (__glibc_likely (the_map->l_rw->l_tls_offset == NO_TLS_OFFSET))
 	{
-	  the_map->l_tls_offset = FORCED_DYNAMIC_TLS_OFFSET;
+	  the_map->l_rw->l_tls_offset = FORCED_DYNAMIC_TLS_OFFSET;
 	  __rtld_lock_unlock_recursive (GL(dl_load_tls_lock));
 	}
-      else if (__glibc_likely (the_map->l_tls_offset
+      else if (__glibc_likely (the_map->l_rw->l_tls_offset
 			       != FORCED_DYNAMIC_TLS_OFFSET))
 	{
 #if TLS_TCB_AT_TP
-	  void *p = (char *) THREAD_SELF - the_map->l_tls_offset;
+	  void *p = (char *) THREAD_SELF - the_map->l_rw->l_tls_offset;
 #elif TLS_DTV_AT_TP
-	  void *p = (char *) THREAD_SELF + the_map->l_tls_offset + TLS_PRE_TCB_SIZE;
+	  void *p = ((char *) THREAD_SELF + the_map->l_rw->l_tls_offset
+		     + TLS_PRE_TCB_SIZE);
 #else
 # error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
 #endif
@@ -1076,9 +1077,9 @@  static inline void __attribute__((always_inline))
 init_one_static_tls (struct pthread *curp, struct link_map_private *map)
 {
 # if TLS_TCB_AT_TP
-  void *dest = (char *) curp - map->l_tls_offset;
+  void *dest = (char *) curp - map->l_rw->l_tls_offset;
 # elif TLS_DTV_AT_TP
-  void *dest = (char *) curp + map->l_tls_offset + TLS_PRE_TCB_SIZE;
+  void *dest = (char *) curp + map->l_rw->l_tls_offset + TLS_PRE_TCB_SIZE;
 # else
 #  error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
 # endif
diff --git a/elf/rtld.c b/elf/rtld.c
index 8e1cc38800..25a9c8aa58 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -460,6 +460,9 @@  _dl_start_final (void *arg, struct dl_start_final_info *info)
 
   static struct link_map_rw rtld_map_rw;
   GL (dl_rtld_map).l_rw = &rtld_map_rw;
+#if NO_TLS_OFFSET != 0
+  GL (dl_rtld_map).l_rw->l_tls_offset = NO_TLS_OFFSET;
+#endif
 
   /* If it hasn't happen yet record the startup time.  */
   rtld_timer_start (&start_time);
@@ -481,12 +484,6 @@  _dl_start_final (void *arg, struct dl_start_final_info *info)
   GL(dl_rtld_map).l_real = &GL(dl_rtld_map);
   GL(dl_rtld_map).l_map_start = (ElfW(Addr)) &__ehdr_start;
   GL(dl_rtld_map).l_map_end = (ElfW(Addr)) _end;
-  /* Copy the TLS related data if necessary.  */
-#ifndef DONT_USE_BOOTSTRAP_MAP
-# if NO_TLS_OFFSET != 0
-  GL(dl_rtld_map).l_tls_offset = NO_TLS_OFFSET;
-# endif
-#endif
 
   /* Initialize the stack end variable.  */
   __libc_stack_end = __builtin_frame_address (0);
@@ -552,10 +549,6 @@  _dl_start (void *arg)
   bootstrap_map.l_ld_readonly = DL_RO_DYN_SECTION;
   elf_get_dynamic_info (&bootstrap_map, true, false);
 
-#if NO_TLS_OFFSET != 0
-  bootstrap_map.l_tls_offset = NO_TLS_OFFSET;
-#endif
-
 #ifdef ELF_MACHINE_BEFORE_RTLD_RELOC
   ELF_MACHINE_BEFORE_RTLD_RELOC (&bootstrap_map, bootstrap_map.l_info);
 #endif
diff --git a/htl/pt-alloc.c b/htl/pt-alloc.c
index c94a4dcad9..ec1b66c53d 100644
--- a/htl/pt-alloc.c
+++ b/htl/pt-alloc.c
@@ -217,9 +217,10 @@  __pthread_init_static_tls (struct link_map_private *map)
 	continue;
 
 # if TLS_TCB_AT_TP
-      void *dest = (char *) t->tcb - map->l_tls_offset;
+      void *dest = (char *) t->tcb - map->l_rw->l_tls_offset;
 # elif TLS_DTV_AT_TP
-      void *dest = (char *) t->tcb + map->l_tls_offset + TLS_PRE_TCB_SIZE;
+      void *dest = ((char *) t->tcb + map->l_rw->l_tls_offset
+		    + TLS_PRE_TCB_SIZE);
 # else
 #  error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
 # endif
diff --git a/include/link.h b/include/link.h
index 03194c0db2..220926248c 100644
--- a/include/link.h
+++ b/include/link.h
@@ -91,6 +91,28 @@  struct link_map_rw
      accessor function below.  */
   unsigned int l_direct_opencount;
 
+  /* For objects present at startup time: offset in the static TLS
+     block.  For loaded objects, it can be NO_TLS_OFFSET (not yet
+     initialized), FORCED_DYNAMIC_TLS_OFFSET (if fully dynamic TLS is
+     used), or an actual TLS offset (if the static TLS allocation has
+     been re-used to satisfy dynamic TLS needs).
+
+     This field is written outside the general loader lock, so it has
+     to reside in the read-write porition of the link map.  */
+#ifndef NO_TLS_OFFSET
+# define NO_TLS_OFFSET	0
+#endif
+#ifndef FORCED_DYNAMIC_TLS_OFFSET
+# if NO_TLS_OFFSET == 0
+#  define FORCED_DYNAMIC_TLS_OFFSET -1
+# elif NO_TLS_OFFSET == -1
+#  define FORCED_DYNAMIC_TLS_OFFSET -2
+# else
+#  error "FORCED_DYNAMIC_TLS_OFFSET is not defined"
+# endif
+#endif
+  ptrdiff_t l_tls_offset;
+
   /* Number of thread_local objects constructed by this DSO.  This is
      atomically accessed and modified and is not always protected by the load
      lock.  See also: CONCURRENCY NOTES in cxa_thread_atexit_impl.c.  */
@@ -322,20 +344,6 @@  struct link_map_private
     size_t l_tls_align;
     /* Offset of first byte module alignment.  */
     size_t l_tls_firstbyte_offset;
-#ifndef NO_TLS_OFFSET
-# define NO_TLS_OFFSET	0
-#endif
-#ifndef FORCED_DYNAMIC_TLS_OFFSET
-# if NO_TLS_OFFSET == 0
-#  define FORCED_DYNAMIC_TLS_OFFSET -1
-# elif NO_TLS_OFFSET == -1
-#  define FORCED_DYNAMIC_TLS_OFFSET -2
-# else
-#  error "FORCED_DYNAMIC_TLS_OFFSET is not defined"
-# endif
-#endif
-    /* For objects present at startup time: offset in the static TLS block.  */
-    ptrdiff_t l_tls_offset;
     /* Index of the module in the dtv array.  */
     size_t l_tls_modid;
 
diff --git a/nptl/Versions b/nptl/Versions
index 3221de89d1..ea1ab9e5a8 100644
--- a/nptl/Versions
+++ b/nptl/Versions
@@ -404,8 +404,9 @@  libc {
     _thread_db_dtv_slotinfo_map;
     _thread_db_dtv_t_counter;
     _thread_db_dtv_t_pointer_val;
+    _thread_db_link_map_l_rw;
     _thread_db_link_map_l_tls_modid;
-    _thread_db_link_map_l_tls_offset;
+    _thread_db_link_map_rw_l_tls_offset;
     _thread_db_list_t_next;
     _thread_db_list_t_prev;
     _thread_db_pthread_cancelhandling;
diff --git a/nptl_db/db_info.c b/nptl_db/db_info.c
index 64e5d220ef..20ccb1dda0 100644
--- a/nptl_db/db_info.c
+++ b/nptl_db/db_info.c
@@ -38,6 +38,7 @@  typedef struct
 } dtv;
 
 typedef struct link_map_private link_map;
+typedef struct link_map_rw link_map_rw;
 typedef struct rtld_global rtld_global;
 typedef struct dtv_slotinfo_list dtv_slotinfo_list;
 typedef struct dtv_slotinfo dtv_slotinfo;
diff --git a/nptl_db/structs.def b/nptl_db/structs.def
index c105f0925c..25013c7c09 100644
--- a/nptl_db/structs.def
+++ b/nptl_db/structs.def
@@ -93,7 +93,8 @@  DB_STRUCT (pthread_key_data_level2)
 DB_STRUCT_ARRAY_FIELD (pthread_key_data_level2, data)
 
 DB_STRUCT_FIELD (link_map, l_tls_modid)
-DB_STRUCT_FIELD (link_map, l_tls_offset)
+DB_STRUCT_FIELD (link_map, l_rw)
+DB_STRUCT_FIELD (link_map_rw, l_tls_offset)
 
 DB_STRUCT_ARRAY_FIELD (dtv, dtv)
 #define pointer_val pointer.val /* Field of anonymous struct in dtv_t.  */
diff --git a/nptl_db/td_thr_tlsbase.c b/nptl_db/td_thr_tlsbase.c
index d05abf6213..1755129cf4 100644
--- a/nptl_db/td_thr_tlsbase.c
+++ b/nptl_db/td_thr_tlsbase.c
@@ -191,9 +191,15 @@  td_thr_tlsbase (const td_thrhandle_t *th,
   /* Is the DTV current enough?  */
   if (dtvgen < modgen)
     {
-    try_static_tls:
-      /* If the module uses Static TLS, we're still good.  */
-      err = DB_GET_FIELD (temp, th->th_ta_p, map, link_map, l_tls_offset, 0);
+    try_static_tls:;
+      /* If the module uses Static TLS, we're still good.  Follow the
+	 l_rw pointer to l_tls_offset.  */
+      psaddr_t l_rw;
+      err = DB_GET_FIELD (l_rw, th->th_ta_p, map, link_map, l_rw, 0);
+      if (err != TD_OK)
+	return err;
+      err = DB_GET_FIELD (temp, th->th_ta_p, l_rw, link_map_rw,
+			  l_tls_offset, 0);
       if (err != TD_OK)
 	return err;
       ptrdiff_t tlsoff = (uintptr_t)temp;
diff --git a/sysdeps/aarch64/dl-machine.h b/sysdeps/aarch64/dl-machine.h
index 370bbfceba..63a48b1452 100644
--- a/sysdeps/aarch64/dl-machine.h
+++ b/sysdeps/aarch64/dl-machine.h
@@ -250,7 +250,8 @@  elf_machine_rela (struct link_map_private *map, struct r_scope_elem *scope[],
 		else
 # endif
 		  {
-		    td->arg = (void*)(sym->st_value + sym_map->l_tls_offset
+		    td->arg = (void*)(sym->st_value
+				      + sym_map->l_rw->l_tls_offset
 				      + reloc->r_addend);
 		    td->entry = _dl_tlsdesc_return;
 		  }
@@ -275,7 +276,7 @@  elf_machine_rela (struct link_map_private *map, struct r_scope_elem *scope[],
 	    {
 	      CHECK_STATIC_TLS (map, sym_map);
 	      *reloc_addr =
-		sym->st_value + reloc->r_addend + sym_map->l_tls_offset;
+		sym->st_value + reloc->r_addend + sym_map->l_rw->l_tls_offset;
 	    }
 	  break;
 
diff --git a/sysdeps/alpha/dl-machine.h b/sysdeps/alpha/dl-machine.h
index 746c7d8189..4553f4fbca 100644
--- a/sysdeps/alpha/dl-machine.h
+++ b/sysdeps/alpha/dl-machine.h
@@ -402,12 +402,12 @@  elf_machine_rela (struct link_map_private *map, struct r_scope_elem *scope[],
       else if (r_type == R_ALPHA_TPREL64)
 	{
 # ifdef RTLD_BOOTSTRAP
-	  *reloc_addr = sym_raw_value + map->l_tls_offset;
+	  *reloc_addr = sym_raw_value + map->l_rw->l_tls_offset;
 # else
 	  if (sym_map)
 	    {
 	      CHECK_STATIC_TLS (map, sym_map);
-	      *reloc_addr = sym_raw_value + sym_map->l_tls_offset;
+	      *reloc_addr = sym_raw_value + sym_map->l_rw->l_tls_offset;
 	    }
 # endif
 	}
diff --git a/sysdeps/arc/dl-machine.h b/sysdeps/arc/dl-machine.h
index c07ebe0838..ade3646d47 100644
--- a/sysdeps/arc/dl-machine.h
+++ b/sysdeps/arc/dl-machine.h
@@ -285,7 +285,8 @@  elf_machine_rela (struct link_map_private *map, struct r_scope_elem *scope[],
           if (sym != NULL)
             {
               CHECK_STATIC_TLS (map, sym_map);
-              *reloc_addr = sym_map->l_tls_offset + sym->st_value + reloc->r_addend;
+              *reloc_addr = (sym_map->l_rw->l_tls_offset + sym->st_value
+			     + reloc->r_addend);
             }
           break;
 
diff --git a/sysdeps/arm/dl-machine.h b/sysdeps/arm/dl-machine.h
index e5fd0cc4d2..b138596252 100644
--- a/sysdeps/arm/dl-machine.h
+++ b/sysdeps/arm/dl-machine.h
@@ -406,7 +406,7 @@  elf_machine_rel (struct link_map_private *map, struct r_scope_elem *scope[],
 #  endif
 # endif
 		{
-		  td->argument.value = value + sym_map->l_tls_offset;
+		  td->argument.value = value + sym_map->l_rw->l_tls_offset;
 		  td->entry = _dl_tlsdesc_return;
 		}
 	      }
@@ -436,7 +436,7 @@  elf_machine_rel (struct link_map_private *map, struct r_scope_elem *scope[],
 	  if (sym != NULL)
 	    {
 	      CHECK_STATIC_TLS (map, sym_map);
-	      *reloc_addr += sym->st_value + sym_map->l_tls_offset;
+	      *reloc_addr += sym->st_value + sym_map->l_rw->l_tls_offset;
 	    }
 	  break;
 	case R_ARM_IRELATIVE:
diff --git a/sysdeps/csky/dl-machine.h b/sysdeps/csky/dl-machine.h
index 8dcb9b11c8..e01b993391 100644
--- a/sysdeps/csky/dl-machine.h
+++ b/sysdeps/csky/dl-machine.h
@@ -303,7 +303,7 @@  elf_machine_rela (struct link_map_private *map, struct r_scope_elem *scope[],
 	  if (sym != NULL)
 	    {
 	      CHECK_STATIC_TLS (map, sym_map);
-	      *reloc_addr = (sym->st_value + sym_map->l_tls_offset
+	      *reloc_addr = (sym->st_value + sym_map->l_rw->l_tls_offset
 			     + reloc->r_addend);
 	    }
 	  break;
diff --git a/sysdeps/hppa/dl-machine.h b/sysdeps/hppa/dl-machine.h
index 60ef82c3e7..a37e834df8 100644
--- a/sysdeps/hppa/dl-machine.h
+++ b/sysdeps/hppa/dl-machine.h
@@ -716,7 +716,8 @@  elf_machine_rela (struct link_map_private *map, struct r_scope_elem *scope[],
       if (sym != NULL)
 	{
 	  CHECK_STATIC_TLS (map, sym_map);
-	  value = sym_map->l_tls_offset + sym->st_value + reloc->r_addend;
+	  value = (sym_map->l_rw->l_tls_offset + sym->st_value
+		   + reloc->r_addend);
 	}
       break;
 #endif	/* use TLS */
diff --git a/sysdeps/i386/dl-machine.h b/sysdeps/i386/dl-machine.h
index b0bba3621f..b0bcb3bb53 100644
--- a/sysdeps/i386/dl-machine.h
+++ b/sysdeps/i386/dl-machine.h
@@ -361,7 +361,8 @@  and creates an unsatisfiable circular dependency.\n",
 #  endif
 # endif
 		  {
-		    td->arg = (void*)(sym->st_value - sym_map->l_tls_offset
+		    td->arg = (void*)(sym->st_value
+				      - sym_map->l_rw->l_tls_offset
 				      + (ElfW(Word))td->arg);
 		    td->entry = _dl_tlsdesc_return;
 		  }
@@ -371,7 +372,7 @@  and creates an unsatisfiable circular dependency.\n",
 	case R_386_TLS_TPOFF32:
 	  /* The offset is positive, backward from the thread pointer.  */
 #  ifdef RTLD_BOOTSTRAP
-	  *reloc_addr += map->l_tls_offset - sym->st_value;
+	  *reloc_addr += map->l_rw->l_tls_offset - sym->st_value;
 #  else
 	  /* We know the offset of object the symbol is contained in.
 	     It is a positive value which will be subtracted from the
@@ -380,14 +381,14 @@  and creates an unsatisfiable circular dependency.\n",
 	  if (sym != NULL)
 	    {
 	      CHECK_STATIC_TLS (map, sym_map);
-	      *reloc_addr += sym_map->l_tls_offset - sym->st_value;
+	      *reloc_addr += sym_map->l_rw->l_tls_offset - sym->st_value;
 	    }
 # endif
 	  break;
 	case R_386_TLS_TPOFF:
 	  /* The offset is negative, forward from the thread pointer.  */
 # ifdef RTLD_BOOTSTRAP
-	  *reloc_addr += sym->st_value - map->l_tls_offset;
+	  *reloc_addr += sym->st_value - map->l_rw->l_tls_offset;
 # else
 	  /* We know the offset of object the symbol is contained in.
 	     It is a negative value which will be added to the
@@ -395,7 +396,7 @@  and creates an unsatisfiable circular dependency.\n",
 	  if (sym != NULL)
 	    {
 	      CHECK_STATIC_TLS (map, sym_map);
-	      *reloc_addr += sym->st_value - sym_map->l_tls_offset;
+	      *reloc_addr += sym->st_value - sym_map->l_rw->l_tls_offset;
 	    }
 # endif
 	  break;
diff --git a/sysdeps/ia64/dl-machine.h b/sysdeps/ia64/dl-machine.h
index e9c59dc65a..0379322b99 100644
--- a/sysdeps/ia64/dl-machine.h
+++ b/sysdeps/ia64/dl-machine.h
@@ -399,7 +399,7 @@  elf_machine_rela (struct link_map_private *map, struct r_scope_elem *scope[],
 #ifndef RTLD_BOOTSTRAP
 	      CHECK_STATIC_TLS (map, sym_map);
 #endif
-	      value += sym_map->l_tls_offset - sym_map->l_public.l_addr;
+	      value += sym_map->l_rw->l_tls_offset - sym_map->l_public.l_addr;
 	    }
 	  else
 	    _dl_reloc_bad_type (map, r_type, 0);
diff --git a/sysdeps/loongarch/dl-tls.h b/sysdeps/loongarch/dl-tls.h
index a551594b64..aef696606d 100644
--- a/sysdeps/loongarch/dl-tls.h
+++ b/sysdeps/loongarch/dl-tls.h
@@ -32,7 +32,7 @@  typedef struct
 
 /* Compute the value for a GOTTPREL reloc.  */
 #define TLS_TPREL_VALUE(sym_map, sym) \
-  ((sym_map)->l_tls_offset + (sym)->st_value - TLS_TP_OFFSET)
+  ((sym_map)->l_rw->l_tls_offset + (sym)->st_value - TLS_TP_OFFSET)
 
 /* Compute the value for a DTPREL reloc.  */
 #define TLS_DTPREL_VALUE(sym) ((sym)->st_value - TLS_DTV_OFFSET)
diff --git a/sysdeps/m68k/dl-tls.h b/sysdeps/m68k/dl-tls.h
index a98948dbd5..c67d441808 100644
--- a/sysdeps/m68k/dl-tls.h
+++ b/sysdeps/m68k/dl-tls.h
@@ -33,7 +33,7 @@  typedef struct
 
 /* Compute the value for a TPREL reloc.  */
 #define TLS_TPREL_VALUE(sym_map, sym, reloc)				\
-  ((sym_map)->l_tls_offset + (sym)->st_value + (reloc)->r_addend	\
+  ((sym_map)->l_rw->l_tls_offset + (sym)->st_value + (reloc)->r_addend	\
    - TLS_TP_OFFSET)
 
 /* Compute the value for a DTPREL reloc.  */
diff --git a/sysdeps/microblaze/dl-machine.h b/sysdeps/microblaze/dl-machine.h
index e8f9cebe2f..213048d593 100644
--- a/sysdeps/microblaze/dl-machine.h
+++ b/sysdeps/microblaze/dl-machine.h
@@ -263,7 +263,8 @@  elf_machine_rela (struct link_map_private *map, struct r_scope_elem *scope[],
 	  if (sym != NULL)
 	    {
 	      CHECK_STATIC_TLS (map, sym_map);
-	      *reloc_addr = sym->st_value + sym_map->l_tls_offset + reloc->r_addend;
+	      *reloc_addr = (sym->st_value + sym_map->l_rw->l_tls_offset
+			     + reloc->r_addend);
 	    }
 	}
 #endif
diff --git a/sysdeps/mips/dl-tls.h b/sysdeps/mips/dl-tls.h
index b4dca8291e..6f516c820b 100644
--- a/sysdeps/mips/dl-tls.h
+++ b/sysdeps/mips/dl-tls.h
@@ -33,7 +33,7 @@  typedef struct
 
 /* Compute the value for a GOTTPREL reloc.  */
 #define TLS_TPREL_VALUE(sym_map, sym) \
-  ((sym_map)->l_tls_offset + (sym)->st_value - TLS_TP_OFFSET)
+  ((sym_map)->l_rw->l_tls_offset + (sym)->st_value - TLS_TP_OFFSET)
 
 /* Compute the value for a DTPREL reloc.  */
 #define TLS_DTPREL_VALUE(sym) \
diff --git a/sysdeps/nios2/dl-tls.h b/sysdeps/nios2/dl-tls.h
index 4059b7e10d..df33af6492 100644
--- a/sysdeps/nios2/dl-tls.h
+++ b/sysdeps/nios2/dl-tls.h
@@ -33,7 +33,7 @@  typedef struct
 
 /* Compute the value for a GOTTPREL reloc.  */
 #define TLS_TPREL_VALUE(sym_map, sym) \
-  ((sym_map)->l_tls_offset + (sym)->st_value - TLS_TP_OFFSET)
+  ((sym_map)->l_rw->l_tls_offset + (sym)->st_value - TLS_TP_OFFSET)
 
 /* Compute the value for a DTPREL reloc.  */
 #define TLS_DTPREL_VALUE(sym) \
diff --git a/sysdeps/or1k/dl-machine.h b/sysdeps/or1k/dl-machine.h
index d016493be9..41abaab4da 100644
--- a/sysdeps/or1k/dl-machine.h
+++ b/sysdeps/or1k/dl-machine.h
@@ -251,13 +251,13 @@  elf_machine_rela (struct link_map_private *map, struct r_scope_elem *scope[],
 	  case R_OR1K_TLS_TPOFF:
 # ifdef RTLD_BOOTSTRAP
 	    *reloc_addr = sym->st_value + reloc->r_addend +
-	      map->l_tls_offset - TLS_TCB_SIZE;
+	      map->l_rw->l_tls_offset - TLS_TCB_SIZE;
 # else
 	    if (sym_map != NULL)
 	      {
 		CHECK_STATIC_TLS (map, sym_map);
 		*reloc_addr = sym->st_value + reloc->r_addend +
-		  sym_map->l_tls_offset - TLS_TCB_SIZE;
+		  sym_map->l_rw->l_tls_offset - TLS_TCB_SIZE;
 	      }
 # endif
 	    break;
diff --git a/sysdeps/powerpc/dl-tls.h b/sysdeps/powerpc/dl-tls.h
index 3324713e40..a444d63d9b 100644
--- a/sysdeps/powerpc/dl-tls.h
+++ b/sysdeps/powerpc/dl-tls.h
@@ -35,7 +35,7 @@  typedef struct
 
 /* Compute the value for a @tprel reloc.  */
 #define TLS_TPREL_VALUE(sym_map, sym, reloc) \
-  ((sym_map)->l_tls_offset + (sym)->st_value + (reloc)->r_addend \
+  ((sym_map)->l_rw->l_tls_offset + (sym)->st_value + (reloc)->r_addend \
    - TLS_TP_OFFSET)
 
 /* Compute the value for a @dtprel reloc.  */
diff --git a/sysdeps/powerpc/powerpc32/dl-machine.h b/sysdeps/powerpc/powerpc32/dl-machine.h
index 789255e427..8fa1cc0d71 100644
--- a/sysdeps/powerpc/powerpc32/dl-machine.h
+++ b/sysdeps/powerpc/powerpc32/dl-machine.h
@@ -355,7 +355,7 @@  elf_machine_rela (struct link_map_private *map, struct r_scope_elem *scope[],
 	  if (!NOT_BOOTSTRAP)
 	    {
 	      reloc_addr[0] = 0;
-	      reloc_addr[1] = (sym_map->l_tls_offset - TLS_TP_OFFSET
+	      reloc_addr[1] = (sym_map->l_rw->l_tls_offset - TLS_TP_OFFSET
 			       + TLS_DTV_OFFSET);
 	      break;
 	    }
@@ -369,7 +369,7 @@  elf_machine_rela (struct link_map_private *map, struct r_scope_elem *scope[],
 		{
 		  reloc_addr[0] = 0;
 		  /* Set up for local dynamic.  */
-		  reloc_addr[1] = (sym_map->l_tls_offset - TLS_TP_OFFSET
+		  reloc_addr[1] = (sym_map->l_rw->l_tls_offset - TLS_TP_OFFSET
 				   + TLS_DTV_OFFSET);
 		  break;
 		}
diff --git a/sysdeps/powerpc/powerpc64/dl-machine.h b/sysdeps/powerpc/powerpc64/dl-machine.h
index 9db1838f44..6104d6ae9c 100644
--- a/sysdeps/powerpc/powerpc64/dl-machine.h
+++ b/sysdeps/powerpc/powerpc64/dl-machine.h
@@ -732,7 +732,7 @@  elf_machine_rela (struct link_map_private *map, struct r_scope_elem *scope[],
 	{
 #ifdef RTLD_BOOTSTRAP
 	  reloc_addr[0] = 0;
-	  reloc_addr[1] = (sym_map->l_tls_offset - TLS_TP_OFFSET
+	  reloc_addr[1] = (sym_map->l_rw->l_tls_offset - TLS_TP_OFFSET
 			   + TLS_DTV_OFFSET);
 	  return;
 #else
@@ -746,7 +746,7 @@  elf_machine_rela (struct link_map_private *map, struct r_scope_elem *scope[],
 		{
 		  reloc_addr[0] = 0;
 		  /* Set up for local dynamic.  */
-		  reloc_addr[1] = (sym_map->l_tls_offset - TLS_TP_OFFSET
+		  reloc_addr[1] = (sym_map->l_rw->l_tls_offset - TLS_TP_OFFSET
 				   + TLS_DTV_OFFSET);
 		  return;
 		}
diff --git a/sysdeps/riscv/dl-tls.h b/sysdeps/riscv/dl-tls.h
index 67c8ae639c..8ede95f014 100644
--- a/sysdeps/riscv/dl-tls.h
+++ b/sysdeps/riscv/dl-tls.h
@@ -33,7 +33,7 @@  typedef struct
 
 /* Compute the value for a GOTTPREL reloc.  */
 #define TLS_TPREL_VALUE(sym_map, sym) \
-  ((sym_map)->l_tls_offset + (sym)->st_value - TLS_TP_OFFSET)
+  ((sym_map)->l_rw->l_tls_offset + (sym)->st_value - TLS_TP_OFFSET)
 
 /* Compute the value for a DTPREL reloc.  */
 #define TLS_DTPREL_VALUE(sym) \
diff --git a/sysdeps/s390/s390-32/dl-machine.h b/sysdeps/s390/s390-32/dl-machine.h
index a92248d2ed..ea83216155 100644
--- a/sysdeps/s390/s390-32/dl-machine.h
+++ b/sysdeps/s390/s390-32/dl-machine.h
@@ -357,7 +357,8 @@  elf_machine_rela (struct link_map_private *map, struct r_scope_elem *scope[],
 	case R_390_TLS_TPOFF:
 	  /* The offset is negative, forward from the thread pointer.  */
 #ifdef RTLD_BOOTSTRAP
-	  *reloc_addr = sym->st_value + reloc->r_addend - map->l_tls_offset;
+	  *reloc_addr = (sym->st_value + reloc->r_addend
+			 - map->l_rw->l_tls_offset);
 #else
 	  /* We know the offset of the object the symbol is contained in.
 	     It is a negative value which will be added to the
@@ -366,7 +367,7 @@  elf_machine_rela (struct link_map_private *map, struct r_scope_elem *scope[],
 	    {
 	      CHECK_STATIC_TLS (map, sym_map);
 	      *reloc_addr = (sym->st_value + reloc->r_addend
-			     - sym_map->l_tls_offset);
+			     - sym_map->l_rw->l_tls_offset);
 	    }
 #endif
 	  break;
diff --git a/sysdeps/s390/s390-64/dl-machine.h b/sysdeps/s390/s390-64/dl-machine.h
index f21df5232c..a4be61d61c 100644
--- a/sysdeps/s390/s390-64/dl-machine.h
+++ b/sysdeps/s390/s390-64/dl-machine.h
@@ -333,7 +333,8 @@  elf_machine_rela (struct link_map_private *map, struct r_scope_elem *scope[],
 	case R_390_TLS_TPOFF:
 	  /* The offset is negative, forward from the thread pointer.  */
 #ifdef RTLD_BOOTSTRAP
-	  *reloc_addr = sym->st_value + reloc->r_addend - map->l_tls_offset;
+	  *reloc_addr = (sym->st_value + reloc->r_addend
+			 - map->l_rw->l_tls_offset);
 #else
 	  /* We know the offset of the object the symbol is contained in.
 	     It is a negative value which will be added to the
@@ -342,7 +343,7 @@  elf_machine_rela (struct link_map_private *map, struct r_scope_elem *scope[],
 	    {
 	      CHECK_STATIC_TLS (map, sym_map);
 	      *reloc_addr = (sym->st_value + reloc->r_addend
-			     - sym_map->l_tls_offset);
+			     - sym_map->l_rw->l_tls_offset);
 	    }
 #endif
 	  break;
diff --git a/sysdeps/sh/dl-machine.h b/sysdeps/sh/dl-machine.h
index 6841671ae6..786cd00cab 100644
--- a/sysdeps/sh/dl-machine.h
+++ b/sysdeps/sh/dl-machine.h
@@ -364,7 +364,8 @@  elf_machine_rela (struct link_map_private *map, struct r_scope_elem *scope[],
 	case R_SH_TLS_TPOFF32:
 	  /* The offset is positive, afterward from the thread pointer.  */
 #ifdef RTLD_BOOTSTRAP
-	  *reloc_addr = map->l_tls_offset + sym->st_value + reloc->r_addend;
+	  *reloc_addr = (map->l_rw->l_tls_offset + sym->st_value
+			 + reloc->r_addend);
 #else
 	  /* We know the offset of object the symbol is contained in.
 	     It is a positive value which will be added to the thread
@@ -373,8 +374,8 @@  elf_machine_rela (struct link_map_private *map, struct r_scope_elem *scope[],
 	  if (sym != NULL)
 	    {
 	      CHECK_STATIC_TLS (map, sym_map);
-	      *reloc_addr = sym_map->l_tls_offset + sym->st_value
-			    + reloc->r_addend;
+	      *reloc_addr = (sym_map->l_rw->l_tls_offset + sym->st_value
+			     + reloc->r_addend);
 	    }
 #endif
 	  break;
diff --git a/sysdeps/sparc/sparc32/dl-machine.h b/sysdeps/sparc/sparc32/dl-machine.h
index 2e9c84e771..450e17295a 100644
--- a/sysdeps/sparc/sparc32/dl-machine.h
+++ b/sysdeps/sparc/sparc32/dl-machine.h
@@ -378,7 +378,7 @@  elf_machine_rela (struct link_map_private *map, struct r_scope_elem *scope[],
       if (sym != NULL)
 	{
 	  CHECK_STATIC_TLS (map, sym_map);
-	  *reloc_addr = sym->st_value - sym_map->l_tls_offset
+	  *reloc_addr = sym->st_value - sym_map->l_rw->l_tls_offset
 	    + reloc->r_addend;
 	}
       break;
@@ -388,7 +388,7 @@  elf_machine_rela (struct link_map_private *map, struct r_scope_elem *scope[],
       if (sym != NULL)
 	{
 	  CHECK_STATIC_TLS (map, sym_map);
-	  value = sym->st_value - sym_map->l_tls_offset
+	  value = sym->st_value - sym_map->l_rw->l_tls_offset
 	    + reloc->r_addend;
 	  if (r_type == R_SPARC_TLS_LE_HIX22)
 	    *reloc_addr = (*reloc_addr & 0xffc00000) | ((~value) >> 10);
diff --git a/sysdeps/sparc/sparc64/dl-machine.h b/sysdeps/sparc/sparc64/dl-machine.h
index c06f568a45..21ab5572a4 100644
--- a/sysdeps/sparc/sparc64/dl-machine.h
+++ b/sysdeps/sparc/sparc64/dl-machine.h
@@ -387,7 +387,7 @@  elf_machine_rela (struct link_map_private *map, struct r_scope_elem *scope[],
       if (sym != NULL)
 	{
 	  CHECK_STATIC_TLS (map, sym_map);
-	  *reloc_addr = sym->st_value - sym_map->l_tls_offset
+	  *reloc_addr = sym->st_value - sym_map->l_rw->l_tls_offset
 	    + reloc->r_addend;
 	}
       break;
@@ -397,7 +397,7 @@  elf_machine_rela (struct link_map_private *map, struct r_scope_elem *scope[],
       if (sym != NULL)
 	{
 	  CHECK_STATIC_TLS (map, sym_map);
-	  value = sym->st_value - sym_map->l_tls_offset
+	  value = sym->st_value - sym_map->l_rw->l_tls_offset
 	    + reloc->r_addend;
 	  if (r_type == R_SPARC_TLS_LE_HIX22)
 	    *(unsigned int *)reloc_addr =
diff --git a/sysdeps/x86_64/dl-machine.h b/sysdeps/x86_64/dl-machine.h
index 915ac77dd8..3136a0335b 100644
--- a/sysdeps/x86_64/dl-machine.h
+++ b/sysdeps/x86_64/dl-machine.h
@@ -375,7 +375,8 @@  and creates an unsatisfiable circular dependency.\n",
 		else
 #  endif
 		  {
-		    td->arg = (void*)(sym->st_value - sym_map->l_tls_offset
+		    td->arg = (void*)(sym->st_value
+				      - sym_map->l_rw->l_tls_offset
 				      + reloc->r_addend);
 		    td->entry = _dl_tlsdesc_return;
 		  }
@@ -391,7 +392,7 @@  and creates an unsatisfiable circular dependency.\n",
 		 It is a negative value which will be added to the
 		 thread pointer.  */
 	      value = (sym->st_value + reloc->r_addend
-		       - sym_map->l_tls_offset);
+		       - sym_map->l_rw->l_tls_offset);
 # ifdef __ILP32__
 	      /* The symbol and addend values are 32 bits but the GOT
 		 entry is 64 bits wide and the whole 64-bit entry is used