diff options
-rw-r--r-- | ChangeLog | 18 | ||||
-rw-r--r-- | elf/Makefile | 9 | ||||
-rw-r--r-- | elf/dl-load.c | 4 | ||||
-rw-r--r-- | elf/dl-reloc.c | 37 | ||||
-rw-r--r-- | elf/rtld.c | 11 | ||||
-rw-r--r-- | elf/tst-tls14.c | 56 | ||||
-rw-r--r-- | elf/tst-tlsmod14a.c | 36 | ||||
-rw-r--r-- | elf/tst-tlsmod14b.c | 2 | ||||
-rw-r--r-- | include/link.h | 4 | ||||
-rw-r--r-- | sysdeps/generic/dl-tls.c | 74 | ||||
-rw-r--r-- | sysdeps/generic/ldsodefs.h | 2 |
11 files changed, 231 insertions, 22 deletions
@@ -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, |