2002-02-06  Ulrich Drepper  <drepper@redhat.com>

	* Versions.def [ld]: Add GLIBC_2.3.
	* elf/Versions [ld]: Add __tls_get_addr to GLIBC_2.3.
	* elf/Makefile (dl-routines): Add dl-tls.
	(distribute): Add dl-tls.h.
	* sysdeps/generic/ldsodefs.h (struct rtld_global): Remove
	_dl_tls_module_cnt, add _dl_tls_max_dtv_idx and _dl_tls_dtv_gaps.
	Add prototypes for _dl_next_tls_modid and _dl_determine_tlsoffset.
	* elf/dl-load.c (_dl_map_object_from_fd): Store alignment requirement
	along with the other info in the link map.  Change queueing of init
	images for double linked list.  Use _dl_next_tls_modid to compute
	l_tls_modid.
	* elf/rtld.c (_dl_start_final): Store alignment requirement
	along with the other info in rtld map and executable map.
	(dl_main): Add ld.so to the init image list if necessary.  Compute
	final module ID with _dl_next_tls_modid.
	* include/link.h (struct link_map): Add l_tls_previmage and
	l_tls_align.
	* eld/dl-support.c: Define _dl_tls_max_dtv_idx and _dl_tls_dtv_gaps.
	* sysdeps/i386/elf/Versions: New file.
	* sysdeps/generic/dl-tls.c: New file.
	* sysdeps/generic/dl-tls.h: New file.
	* sysdeps/i386/dl-tls.h: New file.

	attribute((packed)) to counter stupid people misusing gcc options.
This commit is contained in:
Ulrich Drepper 2002-02-07 04:08:19 +00:00
parent a53a253b90
commit 3fb558781f
10 changed files with 321 additions and 22 deletions

View file

@ -1,3 +1,28 @@
2002-02-06 Ulrich Drepper <drepper@redhat.com>
* Versions.def [ld]: Add GLIBC_2.3.
* elf/Versions [ld]: Add __tls_get_addr to GLIBC_2.3.
* elf/Makefile (dl-routines): Add dl-tls.
(distribute): Add dl-tls.h.
* sysdeps/generic/ldsodefs.h (struct rtld_global): Remove
_dl_tls_module_cnt, add _dl_tls_max_dtv_idx and _dl_tls_dtv_gaps.
Add prototypes for _dl_next_tls_modid and _dl_determine_tlsoffset.
* elf/dl-load.c (_dl_map_object_from_fd): Store alignment requirement
along with the other info in the link map. Change queueing of init
images for double linked list. Use _dl_next_tls_modid to compute
l_tls_modid.
* elf/rtld.c (_dl_start_final): Store alignment requirement
along with the other info in rtld map and executable map.
(dl_main): Add ld.so to the init image list if necessary. Compute
final module ID with _dl_next_tls_modid.
* include/link.h (struct link_map): Add l_tls_previmage and
l_tls_align.
* eld/dl-support.c: Define _dl_tls_max_dtv_idx and _dl_tls_dtv_gaps.
* sysdeps/i386/elf/Versions: New file.
* sysdeps/generic/dl-tls.c: New file.
* sysdeps/generic/dl-tls.h: New file.
* sysdeps/i386/dl-tls.h: New file.
2002-02-06 Roland McGrath <roland@frob.com>
* sysdeps/unix/sysv/linux/netinet/ip.h: Moved to ...
@ -19,7 +44,7 @@
2002-02-06 Ulrich Drepper <drepper@redhat.com>
* sysdeps/unix/sysv/linux/bits/stat.h (struct stat): Add
attribute((packed)) to counter stupid people misuing gcc options.
attribute((packed)) to counter stupid people misusing gcc options.
(struct stat64): Likewise.
2002-02-05 Ulrich Drepper <drepper@redhat.com>

View file

@ -143,8 +143,10 @@ __libc_lock_define_initialized_recursive (, _dl_load_lock)
initialize new TLS blocks. */
struct link_map *_dl_initimage_list;
/* Count the number of modules which define TLS data. */
size_t _dl_tls_module_cnt;
/* Highest dtv index currently needed. */
size_t _dl_tls_max_dtv_idx;
/* Flag signalling whether there are gaps in the module ID allocation. */
bool _dl_tls_dtv_gaps;
#endif

View file

@ -259,9 +259,11 @@ _dl_start_final (void *arg, struct link_map *bootstrap_map_p,
if (phdr[cnt].p_type == PT_TLS)
{
void *tlsblock;
size_t align = MAX (TLS_INIT_TCB_ALIGN, phdr[cnt].p_align);
size_t max_align = MAX (TLS_INIT_TCB_ALIGN, phdr[cnt].p_align);
GL(dl_rtld_map).l_tls_blocksize = phdr[cnt].p_memsz;
GL(dl_rtld_map).l_tls_align = phdr[cnt].p_align;
assert (GL(dl_rtld_map).l_tls_blocksize != 0);
GL(dl_rtld_map).l_tls_initimage_size = phdr[cnt].p_filesz;
GL(dl_rtld_map).l_tls_initimage = (void *) (GL(dl_rtld_map).l_map_start
+ phdr[cnt].p_offset);
@ -274,19 +276,20 @@ _dl_start_final (void *arg, struct link_map *bootstrap_map_p,
tlsblock = alloca (roundup (GL(dl_rtld_map).l_tls_blocksize,
TLS_INIT_TCB_ALIGN)
+ TLS_INIT_TCB_SIZE
+ align);
+ max_align);
# elif TLS_DTV_AT_TP
tlsblock = alloca (roundup (TLS_INIT_TCB_SIZE, phdr[cnt].p_align)
tlsblock = alloca (roundup (TLS_INIT_TCB_SIZE,
GL(dl_rtld_map).l_tls_align)
+ GL(dl_rtld_map).l_tls_blocksize
+ align);
+ max_align);
# else
/* In case a model with a different layout for the TCB and DTV
is defined add another #elif here and in the following #ifs. */
# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
# endif
/* Align the TLS block. */
tlsblock = (void *) (((uintptr_t) tlsblock + align - 1)
& ~(align - 1));
tlsblock = (void *) (((uintptr_t) tlsblock + max_align - 1)
& ~(max_align - 1));
/* Initialize the dtv. */
initdtv[0].counter = 1;
@ -296,7 +299,7 @@ _dl_start_final (void *arg, struct link_map *bootstrap_map_p,
initdtv[1].pointer = tlsblock;
# elif TLS_DTV_AT_TP
GL(dl_rtld_map).l_tls_offset = roundup (TLS_INIT_TCB_SIZE,
phdr[cnt].p_align);
GL(dl_rtld_map).l_tls_align);
initdtv[1].pointer = (char *) tlsblock + GL(dl_rtld_map).l_tls_offset);
# else
# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
@ -716,16 +719,18 @@ of this helper program; chances are you did not intend to run this program.\n\
_dl_start_final. But the result is repeatable so do not
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;
GL(dl_loaded)->l_tls_initimage_size = ph->p_filesz;
GL(dl_loaded)->l_tls_initimage = (void *) (GL(dl_loaded)->l_addr
+ ph->p_offset);
/* This is the first element of the initialization image list.
It is created as a circular list so that we can easily
append to it. */
GL(dl_initimage_list) = GL(dl_loaded)->l_tls_nextimage = GL(dl_loaded);
We create the list as circular since we have to append at
the end. */
GL(dl_initimage_list) = GL(dl_loaded)->l_tls_nextimage
= GL(dl_loaded)->l_tls_previmage = GL(dl_loaded);
/* This image get the ID one. */
GL(dl_tls_module_cnt) = GL(dl_loaded)->l_tls_modid = 1;
/* This image gets the ID one. */
GL(dl_tls_max_dtv_idx) = GL(dl_loaded)->l_tls_modid = 1;
break;
#endif
}
@ -736,7 +741,7 @@ of this helper program; chances are you did not intend to run this program.\n\
/* We were invoked directly, so the program might not have a
PT_INTERP. */
_dl_rtld_libname.name = GL(dl_rtld_map).l_name;
/* _dl_rtld_libname.next = NULL; Alread zero. */
/* _dl_rtld_libname.next = NULL; Already zero. */
GL(dl_rtld_map).l_libname = &_dl_rtld_libname;
}
else
@ -973,7 +978,7 @@ of this helper program; chances are you did not intend to run this program.\n\
assert (GL(dl_rtld_map).l_prev->l_next == GL(dl_rtld_map).l_next);
GL(dl_rtld_map).l_prev->l_next = &GL(dl_rtld_map);
if (GL(dl_rtld_map).l_next)
if (GL(dl_rtld_map).l_next != NULL)
{
assert (GL(dl_rtld_map).l_next->l_prev == GL(dl_rtld_map).l_prev);
GL(dl_rtld_map).l_next->l_prev = &GL(dl_rtld_map);
@ -1328,6 +1333,41 @@ of this helper program; chances are you did not intend to run this program.\n\
we need it in the memory handling later. */
GL(dl_initial_searchlist) = *GL(dl_main_searchlist);
#ifdef USE_TLS
/* Now it is time to determine the layout of the static TLS block
and allocate it for the initial thread. Note that we always
allocate the static block, we never defer it even if no
DF_STATIC_TLS bit is set. The reason is that we know glibc will
use the static model. First add the dynamic linker to the list
if it also uses TLS. */
if (GL(dl_rtld_map).l_tls_blocksize != 0)
{
/* At to the list. */
if (GL(dl_initimage_list) == NULL)
GL(dl_initimage_list) = GL(dl_rtld_map).l_tls_nextimage
= GL(dl_rtld_map).l_tls_previmage = &GL(dl_rtld_map);
else
{
GL(dl_rtld_map).l_tls_nextimage
= GL(dl_initimage_list)->l_tls_nextimage;
GL(dl_rtld_map).l_tls_nextimage->l_tls_previmage
= &GL(dl_rtld_map);
GL(dl_rtld_map).l_tls_previmage = GL(dl_initimage_list);
GL(dl_rtld_map).l_tls_previmage->l_tls_nextimage
= &GL(dl_rtld_map);
GL(dl_initimage_list) = &GL(dl_rtld_map);
}
/* Assign a module ID. */
GL(dl_rtld_map).l_tls_modid = _dl_next_tls_modid ();
}
if (GL(dl_initimage_list) != NULL)
/* This means we actually have some modules which use TLS.
Computer the TLS offsets for the various blocks. */
_dl_determine_tlsoffset (GL(dl_initimage_list)->l_tls_nextimage);
#endif
{
/* Initialize _r_debug. */
struct r_debug *r = _dl_debug_initialize (GL(dl_rtld_map).l_addr);
@ -1344,14 +1384,14 @@ of this helper program; chances are you did not intend to run this program.\n\
#else
if (l->l_info[DT_DEBUG])
if (l->l_info[DT_DEBUG] != NULL)
/* There is a DT_DEBUG entry in the dynamic section. Fill it in
with the run-time address of the r_debug structure */
l->l_info[DT_DEBUG]->d_un.d_ptr = (ElfW(Addr)) r;
/* Fill in the pointer in the dynamic linker's own dynamic section, in
case you run gdb on the dynamic linker directly. */
if (GL(dl_rtld_map).l_info[DT_DEBUG])
if (GL(dl_rtld_map).l_info[DT_DEBUG] != NULL)
GL(dl_rtld_map).l_info[DT_DEBUG]->d_un.d_ptr = (ElfW(Addr)) r;
#endif

View file

@ -256,20 +256,26 @@ struct link_map
const ElfW(Sym) *ret;
} l_lookup_cache;
#ifdef USE_TLS
/* Thread-local storage related info. */
/* Next module in list of initialization images. */
struct link_map *l_tls_nextimage;
/* Previous module in list of initialization images. */
struct link_map *l_tls_previmage;
/* Start of the initialization image. */
void *l_tls_initimage;
/* Size of the initialization image. */
size_t l_tls_initimage_size;
/* Size of the TLS block. */
size_t l_tls_blocksize;
/* Alignment rquirement of the TLS block. */
size_t l_tls_align;
/* 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;
#endif
};
struct dl_phdr_info

View file

@ -1,3 +1,7 @@
2002-02-06 Ulrich Drepper <drepper@redhat.com>
* sysdeps/i386/tls.h: Define THREAD_DTV.
2002-02-04 Ulrich Drepper <drepper@redhat.com>
* internals.h: Move thread descriptor definition...

View file

@ -90,6 +90,13 @@ typedef struct
asm ("hlt"); \
} while (0)
/* Return the address of the dtv for the current thread. */
# define THREAD_DTV() \
({ struct _pthread_descr_struct *__descr; \
THREAD_GETMEM (__descr, p_header.data.dtvp); })
#endif
#endif /* tls.h */

155
sysdeps/generic/dl-tls.c Normal file
View file

@ -0,0 +1,155 @@
/* Thread-local storage handling in the ELF dynamic linker. Generic version.
Copyright (C) 2002 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
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
#include <assert.h>
#include <dl-tls.h>
#include <ldsodefs.h>
/* We don't need any of this if TLS is not supported. */
#ifdef USE_TLS
/* Value used for dtv entries for which the allocation is delayed. */
# define TLS_DTV_UNALLOCATE ((void *) -1l)
size_t
internal_function
_dl_next_tls_modid (void)
{
size_t result;
if (__builtin_expect (GL(dl_tls_dtv_gaps), false))
{
/* XXX If this method proves too costly we can optimize
it to use a constant time method. But I don't think
it's a problem. */
struct link_map *runp = GL(dl_initimage_list);
bool used[GL(dl_tls_max_dtv_idx)];
assert (runp != NULL);
do
{
assert (runp->l_tls_modid > 0
&& runp->l_tls_modid <= GL(dl_tls_max_dtv_idx));
used[runp->l_tls_modid - 1] = true;
}
while ((runp = runp->l_tls_nextimage) != GL(dl_initimage_list));
result = 0;
do
/* The information about the gaps is pessimistic. It might be
there are actually none. */
if (result >= GL(dl_tls_max_dtv_idx))
{
/* Now we know there is actually no gap. Bump the maximum
ID number and remember that there are no gaps. */
result = ++GL(dl_tls_max_dtv_idx);
GL(dl_tls_dtv_gaps) = false;
break;
}
while (used[result++]);
}
else
/* No gaps, allocate a new entry. */
result = ++GL(dl_tls_max_dtv_idx);
return result;
}
void
internal_function
_dl_determine_tlsoffset (struct link_map *firstp)
{
struct link_map *runp = firstp;
size_t max_align = 0;
size_t offset;
# if TLS_TCB_AT_TP
/* We simply start with zero. */
offset = 0;
do
{
max_align = MAX (max_align, runp->l_tls_align);
/* Compute the offset of the next TLS block. */
offset = roundup (offset + runp->l_tls_blocksize, runp->l_tls_align);
/* XXX For some architectures we perhaps should store the
negative offset. */
runp->l_tls_offset = offset;
}
while ((runp = runp->l_tls_nextimage) != firstp);
# elif TLS_DTV_AT_TP
struct link_map *lastp;
/* The first block starts right after the TCB. */
offset = TLS_TCB_SIZE;
max_align = runp->l_tls_align;
runp->l_tls_offset = offset;
lastp = runp;
while ((runp = runp->l_tls_nextimage) != firstp)
{
max_align = MAX (max_align, runp->l_tls_align);
/* Compute the offset of the next TLS block. */
offset = roundup (offset + lastp->l_tls_blocksize, runp->l_tls_align);
runp->l_tls_offset = offset;
}
# else
# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
# endif
}
/* The __tls_get_addr function has two basic forms which differ in the
arguments. The IA-64 form takes two parameters, the module ID and
offset. The form used, among others, on IA-32 takes a reference to
a special structure which contain the same information. The second
form seems to be more often used (in the moment) so we default to
it. Users of the IA-64 form have to provide adequate definitions
of the following macros. */
# ifndef GET_ADDR_ARGS
# define GET_ADDR_ARGS struct tls_index *ti
# endif
# ifndef GET_ADDR_MODULE
# define GET_ADDR_MODULE ti->ti_module
# endif
# ifndef GET_ADDR_OFFSET
# define GET_ADDR_OFFSET ti->ti_offset
# endif
void *
__tls_get_addr (GET_ADDR_ARGS)
{
dtv_t *dtv = THREAD_DTV ();
if (dtv[GET_ADDR_MODULE].pointer == TLS_DTV_UNALLOCATE)
/* XXX */;
return (char *) dtv[GET_ADDR_MODULE].pointer + GET_ADDR_OFFSET;
}
#endif /* use TLS */

2
sysdeps/generic/dl-tls.h Normal file
View file

@ -0,0 +1,2 @@
/* There has to be an architecture specific version of this file. */
#error "architecture-specific version of <dl-tls.h> missing"

View file

@ -22,6 +22,7 @@
#include <features.h>
#include <stdbool.h>
#define __need_size_t
#define __need_NULL
#include <stddef.h>
@ -230,7 +231,7 @@ struct rtld_global
#define DL_DEBUG_RELOC (1 << 5)
#define DL_DEBUG_FILES (1 << 6)
#define DL_DEBUG_STATISTICS (1 << 7)
/* This one is used only internally. */
/* These two are used only internally. */
#define DL_DEBUG_HELP (1 << 8)
#define DL_DEBUG_PRELINK (1 << 9)
@ -293,8 +294,10 @@ struct rtld_global
initialize new TLS blocks. */
EXTERN struct link_map *_dl_initimage_list;
/* Count the number of modules which define TLS data. */
EXTERN size_t _dl_tls_module_cnt;
/* Highest dtv index currently needed. */
EXTERN size_t _dl_tls_max_dtv_idx;
/* Flag signalling whether there are gaps in the module ID allocation. */
EXTERN bool _dl_tls_dtv_gaps;
#endif
/* Name of the shared object to be profiled (if any). */
@ -651,6 +654,14 @@ extern void _dl_sysdep_start_cleanup (void)
internal_function;
/* Determine next available module ID. */
extern size_t _dl_next_tls_modid (void) internal_function;
/* Calculate offset of the TLS blocks in the static TLS block. */
extern void _dl_determine_tlsoffset (struct link_map *firstp)
internal_function;
__END_DECLS
#endif /* ldsodefs.h */

47
sysdeps/i386/dl-tls.h Normal file
View file

@ -0,0 +1,47 @@
/* Thread-local storage handling in the ELF dynamic linker. i386 version.
Copyright (C) 2002 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
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
/* Type used for the representation of TLS information in the GOT. */
struct tls_index
{
unsigned long int ti_module;
unsigned long int ti_offset;
};
/* This is the prototype for the GNU version. */
extern void *___tls_get_addr (struct tls_index *ti)
__attribute__ ((__regparm__ (1)));
/* The special thing about the x86 TLS ABI is that we have two
variants of the __tls_get_addr function with different calling
conventions. The GNU version, which we are mostly concerned here,
takes the parameter in a register. The name is changed by adding
an additional underscore at the beginning. The Sun version uses
the normal calling convention. */
void *
__tls_get_addr (struct tls_index *ti)
{
return ___tls_get_addr (ti);
}
/* Prepare using the definition of __tls_get_addr in the generic
version of this file. */
#define __tls_get_addr __attribute__ ((__regparm__ (1))) ___tls_get_addr