summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog18
-rw-r--r--elf/Makefile9
-rw-r--r--elf/dl-load.c4
-rw-r--r--elf/dl-reloc.c37
-rw-r--r--elf/rtld.c11
-rw-r--r--elf/tst-tls14.c56
-rw-r--r--elf/tst-tlsmod14a.c36
-rw-r--r--elf/tst-tlsmod14b.c2
-rw-r--r--include/link.h4
-rw-r--r--sysdeps/generic/dl-tls.c74
-rw-r--r--sysdeps/generic/ldsodefs.h2
11 files changed, 231 insertions, 22 deletions
diff --git a/ChangeLog b/ChangeLog
index 66ebe002e2..c96f699e67 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2003-07-24 Ulrich Drepper <drepper@redhat.com>
+
+ * include/link.h (struct link_map): Add l_tls_firstbyte_offset field.
+ * sysdeps/generic/dl-tls.c [TLS_TCB_AT_TP] (_dl_determine_tlsoffset):
+ Fix calculation of offsets to take misalignment of first byte in
+ file into account.
+ * elf/dl-load.c (_dl_map_object_from_fd): Initialize
+ l_tls_firstbyte_offset field.
+ * elf/rtld.c (_dl_start_final, _dl_start, dl_main): Likewise.
+ * elf/dl-reloc.c (_dl_allocate_static_tls): Change return type to int.
+ Take l_tls_firstbyte_offset information into account.
+ (CHECK_STATIS_TLS): _dl_allocate_static_tls can fail now.
+ * sysdeps/generic/ldsodefs.h: Adjust _dl_allocate_static_tls prototype.
+ * elf/Makefile: Add rules to build and run tst-tls14.
+ * elf/tst-tls14.c: New file.
+ * elf/tst-tlsmod14a.c: New file.
+ * elf/tst-tlsmod14b.c: New file.
+
2003-07-23 Jakub Jelinek <jakub@redhat.com>
* sysdeps/pthread/lio_listio.c (LIO_OPCODE_BASE): Define.
diff --git a/elf/Makefile b/elf/Makefile
index 7c654f86ae..bfecc360e2 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -148,7 +148,7 @@ tests += loadtest restest1 preloadtest loadfail multiload origtest resolvfail \
neededtest3 neededtest4 unload2 lateglobal initfirst global \
restest2 next dblload dblunload reldep5 reldep6 reldep7 reldep8 \
circleload1 tst-tls3 tst-tls4 tst-tls5 tst-tls6 tst-tls7 tst-tls8 \
- tst-tls10 tst-tls11 tst-tls12 tst-tls13
+ tst-tls10 tst-tls11 tst-tls12 tst-tls13 tst-tls14
# reldep9
test-srcs = tst-pathopt
tests-vis-yes = vismain
@@ -171,7 +171,7 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
tst-tlsmod1 tst-tlsmod2 tst-tlsmod3 tst-tlsmod4 \
tst-tlsmod5 tst-tlsmod6 tst-tlsmod7 tst-tlsmod8 \
tst-tlsmod9 tst-tlsmod10 tst-tlsmod11 tst-tlsmod12 \
- tst-tlsmod13 tst-tlsmod13a \
+ tst-tlsmod13 tst-tlsmod13a tst-tlsmod14a tst-tlsmod14b \
circlemod1 circlemod1a circlemod2 circlemod2a \
circlemod3 circlemod3a \
reldep8mod1 reldep8mod2 reldep8mod3 \
@@ -428,6 +428,8 @@ tst-tlsmod8.so-no-z-defs = yes
tst-tlsmod9.so-no-z-defs = yes
tst-tlsmod10.so-no-z-defs = yes
tst-tlsmod12.so-no-z-defs = yes
+tst-tlsmod14a.so-no-z-defs = yes
+tst-tlsmod14b.so-no-z-defs = yes
circlemod2.so-no-z-defs = yes
circlemod3.so-no-z-defs = yes
circlemod3a.so-no-z-defs = yes
@@ -633,6 +635,9 @@ $(objpfx)tst-tls12: $(objpfx)tst-tlsmod12.so
$(objpfx)tst-tls13: $(libdl)
$(objpfx)tst-tls13.out: $(objpfx)tst-tlsmod13a.so
+$(objpfx)tst-tls14: $(objpfx)tst-tlsmod14a.so $(libdl)
+$(objpfx)tst-tls14.out:$(objpfx)tst-tlsmod14b.so
+
ifdef libdl
$(objpfx)tst-tls9-static: $(common-objpfx)dlfcn/libdl.a
$(objpfx)tst-tls9-static.out: $(objpfx)tst-tlsmod5.so $(objpfx)tst-tlsmod6.so
diff --git a/elf/dl-load.c b/elf/dl-load.c
index f3c9e82423..249ef84639 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -987,6 +987,10 @@ _dl_map_object_from_fd (const char *name, int fd, struct filebuf *fbp,
l->l_tls_blocksize = ph->p_memsz;
l->l_tls_align = ph->p_align;
+ if (ph->p_align == 0)
+ l->l_tls_firstbyte_offset = 0;
+ else
+ l->l_tls_firstbyte_offset = ph->p_vaddr & (ph->p_align - 1);
l->l_tls_initimage_size = ph->p_filesz;
/* Since we don't know the load address yet only store the
offset. We will adjust it later. */
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index 6165fe4aca..d82ea108d0 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -41,27 +41,39 @@
the static TLS area already allocated for each running thread. If this
object's TLS segment is too big to fit, we fail. If it fits,
we set MAP->l_tls_offset and return. */
-void
+int
internal_function __attribute_noinline__
_dl_allocate_static_tls (struct link_map *map)
{
size_t offset;
size_t used;
size_t check;
+ size_t freebytes;
+ size_t n;
+ size_t blsize;
+
+ /* If the alignment requirements are too high fail. */
+ if (map->l_tls_align > GL(dl_tls_static_align))
+ return 1;
# if TLS_TCB_AT_TP
- offset = roundup (GL(dl_tls_static_used) + map->l_tls_blocksize,
- map->l_tls_align);
- used = offset;
- check = offset + TLS_TCB_SIZE;
+ freebytes = GL(dl_tls_static_size) - GL(dl_tls_static_used) - TLS_TCB_SIZE;
+
+ blsize = map->l_tls_blocksize + map->l_tls_firstbyte_offset;
+ if (freebytes < blsize)
+ return 1;
+
+ n = (freebytes - blsize) / map->l_tls_align;
+
+ offset = GL(dl_tls_static_used) + (freebytes - n * map->l_tls_align
+ - map->l_tls_firstbyte_offset);
+
+ map->l_tls_offset = GL(dl_tls_static_used) = offset;
# elif TLS_DTV_AT_TP
offset = roundup (GL(dl_tls_static_used), map->l_tls_align);
used = offset + map->l_tls_blocksize;
check = used;
/* dl_tls_static_used includes the TCB at the beginning. */
-# else
-# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
-# endif
if (check > GL(dl_tls_static_size))
{
@@ -72,6 +84,11 @@ shared object cannot be dlopen()ed: static TLS memory too small");
map->l_tls_offset = offset;
GL(dl_tls_static_used) = used;
+# else
+# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
+# endif
+
+ return 0;
}
#endif
@@ -212,7 +229,9 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
#define CHECK_STATIC_TLS(map, sym_map) \
do { \
if (__builtin_expect ((sym_map)->l_tls_offset == NO_TLS_OFFSET, 0)) \
- _dl_allocate_static_tls (sym_map); \
+ if (_dl_allocate_static_tls (sym_map) != 0) \
+ INTUSE(_dl_signal_error) (0, sym_map->l_name, NULL, N_("\
+cannot allocate memory in static TLS block")); \
} while (0)
#include "dynamic-link.h"
diff --git a/elf/rtld.c b/elf/rtld.c
index 4a086c2d32..c63405ac99 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -227,6 +227,7 @@ _dl_start_final (void *arg, struct dl_start_final_info *info)
assert (info->l.l_tls_modid != 0);
GL(dl_rtld_map).l_tls_blocksize = info->l.l_tls_blocksize;
GL(dl_rtld_map).l_tls_align = info->l.l_tls_align;
+ GL(dl_rtld_map).l_tls_firstbyte_offset = info->l.l_tls_firstbyte_offset;
GL(dl_rtld_map).l_tls_initimage_size = info->l.l_tls_initimage_size;
GL(dl_rtld_map).l_tls_initimage = info->l.l_tls_initimage;
GL(dl_rtld_map).l_tls_offset = info->l.l_tls_offset;
@@ -347,6 +348,11 @@ _dl_start (void *arg)
bootstrap_map.l_tls_blocksize = phdr[cnt].p_memsz;
bootstrap_map.l_tls_align = phdr[cnt].p_align;
+ if (phdr[cnt].p_align == 0)
+ bootstrap_map.l_tls_firstbyte_offset = 0;
+ else
+ bootstrap_map.l_tls_firstbyte_offset = (phdr[cnt].p_vaddr
+ & (phdr[cnt].p_align - 1));
assert (bootstrap_map.l_tls_blocksize != 0);
bootstrap_map.l_tls_initimage_size = phdr[cnt].p_filesz;
bootstrap_map.l_tls_initimage = (void *) (bootstrap_map.l_addr
@@ -860,6 +866,11 @@ of this helper program; chances are you did not intend to run this program.\n\
check for this special but unimportant case. */
GL(dl_loaded)->l_tls_blocksize = ph->p_memsz;
GL(dl_loaded)->l_tls_align = ph->p_align;
+ if (ph->p_align == 0)
+ GL(dl_loaded)->l_tls_firstbyte_offset = 0;
+ else
+ GL(dl_loaded)->l_tls_firstbyte_offset = (ph->p_vaddr
+ & (ph->p_align - 1));
GL(dl_loaded)->l_tls_initimage_size = ph->p_filesz;
GL(dl_loaded)->l_tls_initimage = (void *) ph->p_vaddr;
diff --git a/elf/tst-tls14.c b/elf/tst-tls14.c
new file mode 100644
index 0000000000..4ae367a38f
--- /dev/null
+++ b/elf/tst-tls14.c
@@ -0,0 +1,56 @@
+/* Check alignment of TLS variable. */
+#include <dlfcn.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+
+#define AL 4096
+struct foo
+{
+ int i;
+} __attribute ((aligned (AL)));
+
+static __thread struct foo f;
+static struct foo g;
+
+
+extern int in_dso1 (void);
+
+
+static int
+do_test (void)
+{
+ int result = 0;
+
+ int fail = (((uintptr_t) &f) & (AL - 1)) != 0;
+ printf ("&f = %p %s\n", &f, fail ? "FAIL" : "OK");
+ result |= fail;
+
+ fail = (((uintptr_t) &g) & (AL - 1)) != 0;
+ printf ("&g = %p %s\n", &g, fail ? "FAIL" : "OK");
+ result |= fail;
+
+ result |= in_dso1 ();
+
+ void *h = dlopen ("tst-tlsmod14b.so", RTLD_LAZY);
+ if (h == NULL)
+ {
+ printf ("cannot open tst-tlsmod14b.so: %m\n");
+ exit (1);
+ }
+
+ int (*fp) (void) = (int (*) (void)) dlsym (h, "in_dso2");
+ if (fp == NULL)
+ {
+ puts ("cannot find in_dso2");
+ exit (1);
+ }
+
+ result |= fp ();
+
+ return result;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/elf/tst-tlsmod14a.c b/elf/tst-tlsmod14a.c
new file mode 100644
index 0000000000..4843e5937e
--- /dev/null
+++ b/elf/tst-tlsmod14a.c
@@ -0,0 +1,36 @@
+#include <stdint.h>
+#include <stdio.h>
+
+
+#define AL 4096
+struct foo
+{
+ int i;
+} __attribute ((aligned (AL)));
+
+static __thread struct foo f;
+static struct foo g;
+
+
+#ifndef FCT
+# define FCT in_dso1
+#endif
+
+
+int
+FCT (void)
+{
+ puts (__func__);
+
+ int result = 0;
+
+ int fail = (((uintptr_t) &f) & (AL - 1)) != 0;
+ printf ("&f = %p %s\n", &f, fail ? "FAIL" : "OK");
+ result |= fail;
+
+ fail = (((uintptr_t) &g) & (AL - 1)) != 0;
+ printf ("&g = %p %s\n", &g, fail ? "FAIL" : "OK");
+ result |= fail;
+
+ return result;
+}
diff --git a/elf/tst-tlsmod14b.c b/elf/tst-tlsmod14b.c
new file mode 100644
index 0000000000..24d9ceaf7e
--- /dev/null
+++ b/elf/tst-tlsmod14b.c
@@ -0,0 +1,2 @@
+#define FCT in_dso2
+#include "tst-tlsmod14a.c"
diff --git a/include/link.h b/include/link.h
index cc2387b7a0..cd66222795 100644
--- a/include/link.h
+++ b/include/link.h
@@ -1,6 +1,6 @@
/* Data structure for communication from the run-time dynamic linker for
loaded ELF shared objects.
- Copyright (C) 1995-1999,2000,01,02 Free Software Foundation, Inc.
+ Copyright (C) 1995-2002, 2003 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
@@ -268,6 +268,8 @@ struct link_map
size_t l_tls_blocksize;
/* Alignment requirement of the TLS block. */
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
diff --git a/sysdeps/generic/dl-tls.c b/sysdeps/generic/dl-tls.c
index a51e3f7bc7..ccad53c59f 100644
--- a/sysdeps/generic/dl-tls.c
+++ b/sysdeps/generic/dl-tls.c
@@ -117,9 +117,10 @@ internal_function
_dl_determine_tlsoffset (void)
{
struct dtv_slotinfo *slotinfo;
- size_t max_align = __alignof__ (void *);
+ size_t max_align;
size_t offset;
size_t cnt;
+ size_t freebytes;
/* The first element of the dtv slot info list is allocated. */
assert (GL(dl_tls_dtv_slotinfo_list) != NULL);
@@ -127,24 +128,78 @@ _dl_determine_tlsoffset (void)
dl_tls_dtv_slotinfo_list list. */
assert (GL(dl_tls_dtv_slotinfo_list)->next == NULL);
+ /* Determining the offset of the various parts of the static TLS
+ block has several dependencies. In addition we have to work
+ around bugs in some toolchains.
+
+ Each TLS block from the objects available at link time has a size
+ and an alignment requirement. The GNU ld computes the alignment
+ requirements for the data at the positions *in the file*, though.
+ I.e, it is not simply possible to allocate a block with the size
+ of the TLS program header entry. The data is layed out assuming
+ that the first byte of the TLS block fulfills
+
+ p_vaddr mod p_align == &TLS_BLOCK mod p_align
+
+ This means we have to add artificial padding at the beginning of
+ the TLS block. These bytes are never used for the TLS data in
+ this module but the first byte allocated must be aligned
+ according to mod p_align == 0 so that the first byte of the TLS
+ block is aligned according to p_vaddr mod p_align. This is ugly
+ and the linker can help by computing the offsets in the TLS block
+ assuming the first byte of the TLS block is aligned according to
+ p_align.
+
+ We can handle this wrong behavior because of another bug in GNU
+ ld. The p_vaddr field of the TLS segment must be zero (according
+ to the spec) since the linker does not know the address or offset
+ where it will end up at. Once a linker is available which
+ handles the alignment correctly it should set p_addr to zero and
+ all will automatically fall into place.
+
+ The extra space which might be allocated before the first byte of
+ the TLS block need not go unused. The code below tries to use
+ that memory for the next TLS block. This can work if the total
+ memory requirement for the next TLS block is smaller than the
+ gap. */
+
# if TLS_TCB_AT_TP
/* We simply start with zero. */
+ max_align = __alignof (void *);
offset = 0;
+ freebytes = 0;
slotinfo = GL(dl_tls_dtv_slotinfo_list)->slotinfo;
for (cnt = 1; slotinfo[cnt].map != NULL; ++cnt)
{
assert (cnt < GL(dl_tls_dtv_slotinfo_list)->len);
- max_align = MAX (max_align, slotinfo[cnt].map->l_tls_align);
+ size_t blsize = (slotinfo[cnt].map->l_tls_blocksize
+ + slotinfo[cnt].map->l_tls_firstbyte_offset);
- /* Compute the offset of the next TLS block. */
- offset = roundup (offset + slotinfo[cnt].map->l_tls_blocksize,
- slotinfo[cnt].map->l_tls_align);
+ if (blsize <= freebytes)
+ {
+ /* When we come here the amount of memory we was "wasted"
+ for the correct alignment of the previous block is larger
+ than what we need for this module. So use it. */
+ size_t n = (freebytes - blsize) / slotinfo[cnt].map->l_tls_align;
+ freebytes = (n * slotinfo[cnt].map->l_tls_align
+ + slotinfo[cnt].map->l_tls_firstbyte_offset);
+ }
+ else
+ {
+ /* There is either no gap from the bottom of the static TLS
+ block to the first used byte or the gap is too small.
+ Extend the static TLS block. */
+ offset += roundup (blsize, max_align);
+ freebytes = slotinfo[cnt].map->l_tls_firstbyte_offset;
+ }
+
+ max_align = MAX (max_align, slotinfo[cnt].map->l_tls_align);
/* XXX For some architectures we perhaps should store the
negative offset. */
- slotinfo[cnt].map->l_tls_offset = offset;
+ slotinfo[cnt].map->l_tls_offset = offset - freebytes;
}
/* The thread descriptor (pointed to by the thread pointer) has its
@@ -156,11 +211,12 @@ _dl_determine_tlsoffset (void)
// XXX model.
GL(dl_tls_static_used) = offset;
- GL(dl_tls_static_size) = roundup (offset + TLS_STATIC_SURPLUS + TLS_TCB_SIZE,
- TLS_TCB_ALIGN);
+ GL(dl_tls_static_size) = (offset + roundup (TLS_STATIC_SURPLUS, max_align)
+ + TLS_TCB_SIZE);
# elif TLS_DTV_AT_TP
/* The TLS blocks start right after the TCB. */
offset = TLS_TCB_SIZE;
+ max_align = __alignof (void *);
/* The first block starts right after the TCB. */
slotinfo = GL(dl_tls_dtv_slotinfo_list)->slotinfo;
@@ -201,7 +257,7 @@ _dl_determine_tlsoffset (void)
# endif
/* The alignment requirement for the static TLS block. */
- GL(dl_tls_static_align) = MAX (TLS_TCB_ALIGN, max_align);
+ GL(dl_tls_static_align) = max_align;
}
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 570b52261b..565edb3fd8 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -797,7 +797,7 @@ rtld_hidden_proto (_dl_allocate_tls)
extern void _dl_get_tls_static_info (size_t *sizep, size_t *alignp)
internal_function;
-extern void _dl_allocate_static_tls (struct link_map *map)
+extern int _dl_allocate_static_tls (struct link_map *map)
internal_function attribute_hidden;
/* These are internal entry points to the two halves of _dl_allocate_tls,