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.
This commit is contained in:
Ulrich Drepper 2003-07-24 20:07:00 +00:00
parent 6bc0b95489
commit 99fe3b0e7a
11 changed files with 231 additions and 22 deletions

View File

@ -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> 2003-07-23 Jakub Jelinek <jakub@redhat.com>
* sysdeps/pthread/lio_listio.c (LIO_OPCODE_BASE): Define. * sysdeps/pthread/lio_listio.c (LIO_OPCODE_BASE): Define.

View File

@ -148,7 +148,7 @@ tests += loadtest restest1 preloadtest loadfail multiload origtest resolvfail \
neededtest3 neededtest4 unload2 lateglobal initfirst global \ neededtest3 neededtest4 unload2 lateglobal initfirst global \
restest2 next dblload dblunload reldep5 reldep6 reldep7 reldep8 \ restest2 next dblload dblunload reldep5 reldep6 reldep7 reldep8 \
circleload1 tst-tls3 tst-tls4 tst-tls5 tst-tls6 tst-tls7 tst-tls8 \ 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 # reldep9
test-srcs = tst-pathopt test-srcs = tst-pathopt
tests-vis-yes = vismain 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-tlsmod1 tst-tlsmod2 tst-tlsmod3 tst-tlsmod4 \
tst-tlsmod5 tst-tlsmod6 tst-tlsmod7 tst-tlsmod8 \ tst-tlsmod5 tst-tlsmod6 tst-tlsmod7 tst-tlsmod8 \
tst-tlsmod9 tst-tlsmod10 tst-tlsmod11 tst-tlsmod12 \ tst-tlsmod9 tst-tlsmod10 tst-tlsmod11 tst-tlsmod12 \
tst-tlsmod13 tst-tlsmod13a \ tst-tlsmod13 tst-tlsmod13a tst-tlsmod14a tst-tlsmod14b \
circlemod1 circlemod1a circlemod2 circlemod2a \ circlemod1 circlemod1a circlemod2 circlemod2a \
circlemod3 circlemod3a \ circlemod3 circlemod3a \
reldep8mod1 reldep8mod2 reldep8mod3 \ reldep8mod1 reldep8mod2 reldep8mod3 \
@ -428,6 +428,8 @@ tst-tlsmod8.so-no-z-defs = yes
tst-tlsmod9.so-no-z-defs = yes tst-tlsmod9.so-no-z-defs = yes
tst-tlsmod10.so-no-z-defs = yes tst-tlsmod10.so-no-z-defs = yes
tst-tlsmod12.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 circlemod2.so-no-z-defs = yes
circlemod3.so-no-z-defs = yes circlemod3.so-no-z-defs = yes
circlemod3a.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: $(libdl)
$(objpfx)tst-tls13.out: $(objpfx)tst-tlsmod13a.so $(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 ifdef libdl
$(objpfx)tst-tls9-static: $(common-objpfx)dlfcn/libdl.a $(objpfx)tst-tls9-static: $(common-objpfx)dlfcn/libdl.a
$(objpfx)tst-tls9-static.out: $(objpfx)tst-tlsmod5.so $(objpfx)tst-tlsmod6.so $(objpfx)tst-tls9-static.out: $(objpfx)tst-tlsmod5.so $(objpfx)tst-tlsmod6.so

View File

@ -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_blocksize = ph->p_memsz;
l->l_tls_align = ph->p_align; 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; l->l_tls_initimage_size = ph->p_filesz;
/* Since we don't know the load address yet only store the /* Since we don't know the load address yet only store the
offset. We will adjust it later. */ offset. We will adjust it later. */

View File

@ -41,27 +41,39 @@
the static TLS area already allocated for each running thread. If this 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, object's TLS segment is too big to fit, we fail. If it fits,
we set MAP->l_tls_offset and return. */ we set MAP->l_tls_offset and return. */
void int
internal_function __attribute_noinline__ internal_function __attribute_noinline__
_dl_allocate_static_tls (struct link_map *map) _dl_allocate_static_tls (struct link_map *map)
{ {
size_t offset; size_t offset;
size_t used; size_t used;
size_t check; 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 # if TLS_TCB_AT_TP
offset = roundup (GL(dl_tls_static_used) + map->l_tls_blocksize, freebytes = GL(dl_tls_static_size) - GL(dl_tls_static_used) - TLS_TCB_SIZE;
map->l_tls_align);
used = offset; blsize = map->l_tls_blocksize + map->l_tls_firstbyte_offset;
check = offset + TLS_TCB_SIZE; 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 # elif TLS_DTV_AT_TP
offset = roundup (GL(dl_tls_static_used), map->l_tls_align); offset = roundup (GL(dl_tls_static_used), map->l_tls_align);
used = offset + map->l_tls_blocksize; used = offset + map->l_tls_blocksize;
check = used; check = used;
/* dl_tls_static_used includes the TCB at the beginning. */ /* 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)) 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; map->l_tls_offset = offset;
GL(dl_tls_static_used) = used; 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 #endif
@ -212,7 +229,9 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
#define CHECK_STATIC_TLS(map, sym_map) \ #define CHECK_STATIC_TLS(map, sym_map) \
do { \ do { \
if (__builtin_expect ((sym_map)->l_tls_offset == NO_TLS_OFFSET, 0)) \ 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) } while (0)
#include "dynamic-link.h" #include "dynamic-link.h"

View File

@ -227,6 +227,7 @@ _dl_start_final (void *arg, struct dl_start_final_info *info)
assert (info->l.l_tls_modid != 0); 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_blocksize = info->l.l_tls_blocksize;
GL(dl_rtld_map).l_tls_align = info->l.l_tls_align; 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_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_initimage = info->l.l_tls_initimage;
GL(dl_rtld_map).l_tls_offset = info->l.l_tls_offset; 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_blocksize = phdr[cnt].p_memsz;
bootstrap_map.l_tls_align = phdr[cnt].p_align; 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); assert (bootstrap_map.l_tls_blocksize != 0);
bootstrap_map.l_tls_initimage_size = phdr[cnt].p_filesz; bootstrap_map.l_tls_initimage_size = phdr[cnt].p_filesz;
bootstrap_map.l_tls_initimage = (void *) (bootstrap_map.l_addr 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. */ check for this special but unimportant case. */
GL(dl_loaded)->l_tls_blocksize = ph->p_memsz; GL(dl_loaded)->l_tls_blocksize = ph->p_memsz;
GL(dl_loaded)->l_tls_align = ph->p_align; 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_size = ph->p_filesz;
GL(dl_loaded)->l_tls_initimage = (void *) ph->p_vaddr; GL(dl_loaded)->l_tls_initimage = (void *) ph->p_vaddr;

56
elf/tst-tls14.c Normal file
View File

@ -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"

36
elf/tst-tlsmod14a.c Normal file
View File

@ -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;
}

2
elf/tst-tlsmod14b.c Normal file
View File

@ -0,0 +1,2 @@
#define FCT in_dso2
#include "tst-tlsmod14a.c"

View File

@ -1,6 +1,6 @@
/* Data structure for communication from the run-time dynamic linker for /* Data structure for communication from the run-time dynamic linker for
loaded ELF shared objects. 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. This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or 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; size_t l_tls_blocksize;
/* Alignment requirement of the TLS block. */ /* Alignment requirement of the TLS block. */
size_t l_tls_align; size_t l_tls_align;
/* Offset of first byte module alignment. */
size_t l_tls_firstbyte_offset;
# ifndef NO_TLS_OFFSET # ifndef NO_TLS_OFFSET
# define NO_TLS_OFFSET 0 # define NO_TLS_OFFSET 0
# endif # endif

View File

@ -117,9 +117,10 @@ internal_function
_dl_determine_tlsoffset (void) _dl_determine_tlsoffset (void)
{ {
struct dtv_slotinfo *slotinfo; struct dtv_slotinfo *slotinfo;
size_t max_align = __alignof__ (void *); size_t max_align;
size_t offset; size_t offset;
size_t cnt; size_t cnt;
size_t freebytes;
/* The first element of the dtv slot info list is allocated. */ /* The first element of the dtv slot info list is allocated. */
assert (GL(dl_tls_dtv_slotinfo_list) != NULL); assert (GL(dl_tls_dtv_slotinfo_list) != NULL);
@ -127,24 +128,78 @@ _dl_determine_tlsoffset (void)
dl_tls_dtv_slotinfo_list list. */ dl_tls_dtv_slotinfo_list list. */
assert (GL(dl_tls_dtv_slotinfo_list)->next == NULL); 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 # if TLS_TCB_AT_TP
/* We simply start with zero. */ /* We simply start with zero. */
max_align = __alignof (void *);
offset = 0; offset = 0;
freebytes = 0;
slotinfo = GL(dl_tls_dtv_slotinfo_list)->slotinfo; slotinfo = GL(dl_tls_dtv_slotinfo_list)->slotinfo;
for (cnt = 1; slotinfo[cnt].map != NULL; ++cnt) for (cnt = 1; slotinfo[cnt].map != NULL; ++cnt)
{ {
assert (cnt < GL(dl_tls_dtv_slotinfo_list)->len); 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. */ if (blsize <= freebytes)
offset = roundup (offset + slotinfo[cnt].map->l_tls_blocksize, {
slotinfo[cnt].map->l_tls_align); /* 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 /* XXX For some architectures we perhaps should store the
negative offset. */ 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 /* The thread descriptor (pointed to by the thread pointer) has its
@ -156,11 +211,12 @@ _dl_determine_tlsoffset (void)
// XXX model. // XXX model.
GL(dl_tls_static_used) = offset; GL(dl_tls_static_used) = offset;
GL(dl_tls_static_size) = roundup (offset + TLS_STATIC_SURPLUS + TLS_TCB_SIZE, GL(dl_tls_static_size) = (offset + roundup (TLS_STATIC_SURPLUS, max_align)
TLS_TCB_ALIGN); + TLS_TCB_SIZE);
# elif TLS_DTV_AT_TP # elif TLS_DTV_AT_TP
/* The TLS blocks start right after the TCB. */ /* The TLS blocks start right after the TCB. */
offset = TLS_TCB_SIZE; offset = TLS_TCB_SIZE;
max_align = __alignof (void *);
/* The first block starts right after the TCB. */ /* The first block starts right after the TCB. */
slotinfo = GL(dl_tls_dtv_slotinfo_list)->slotinfo; slotinfo = GL(dl_tls_dtv_slotinfo_list)->slotinfo;
@ -201,7 +257,7 @@ _dl_determine_tlsoffset (void)
# endif # endif
/* The alignment requirement for the static TLS block. */ /* 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;
} }

View File

@ -797,7 +797,7 @@ rtld_hidden_proto (_dl_allocate_tls)
extern void _dl_get_tls_static_info (size_t *sizep, size_t *alignp) extern void _dl_get_tls_static_info (size_t *sizep, size_t *alignp)
internal_function; 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; internal_function attribute_hidden;
/* These are internal entry points to the two halves of _dl_allocate_tls, /* These are internal entry points to the two halves of _dl_allocate_tls,