diff options
-rw-r--r-- | ChangeLog | 27 | ||||
-rw-r--r-- | elf/dl-support.c | 6 | ||||
-rw-r--r-- | elf/rtld.c | 72 | ||||
-rw-r--r-- | include/link.h | 6 | ||||
-rw-r--r-- | linuxthreads/ChangeLog | 4 | ||||
-rw-r--r-- | linuxthreads/sysdeps/i386/tls.h | 7 | ||||
-rw-r--r-- | sysdeps/generic/dl-tls.c | 155 | ||||
-rw-r--r-- | sysdeps/generic/dl-tls.h | 2 | ||||
-rw-r--r-- | sysdeps/generic/ldsodefs.h | 17 | ||||
-rw-r--r-- | sysdeps/i386/dl-tls.h | 47 |
10 files changed, 321 insertions, 22 deletions
@@ -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> diff --git a/elf/dl-support.c b/elf/dl-support.c index 10446dc68a..4eaa6757a2 100644 --- a/elf/dl-support.c +++ b/elf/dl-support.c @@ -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 diff --git a/elf/rtld.c b/elf/rtld.c index b9cda1d841..088e761e64 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -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 diff --git a/include/link.h b/include/link.h index 0bb244da1b..ca3784bc66 100644 --- a/include/link.h +++ b/include/link.h @@ -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 diff --git a/linuxthreads/ChangeLog b/linuxthreads/ChangeLog index f55477ea41..c243f554f2 100644 --- a/linuxthreads/ChangeLog +++ b/linuxthreads/ChangeLog @@ -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... diff --git a/linuxthreads/sysdeps/i386/tls.h b/linuxthreads/sysdeps/i386/tls.h index 17f6e0ae45..ed95e210c7 100644 --- a/linuxthreads/sysdeps/i386/tls.h +++ b/linuxthreads/sysdeps/i386/tls.h @@ -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 */ diff --git a/sysdeps/generic/dl-tls.c b/sysdeps/generic/dl-tls.c new file mode 100644 index 0000000000..557a023453 --- /dev/null +++ b/sysdeps/generic/dl-tls.c @@ -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 */ diff --git a/sysdeps/generic/dl-tls.h b/sysdeps/generic/dl-tls.h new file mode 100644 index 0000000000..7703a97525 --- /dev/null +++ b/sysdeps/generic/dl-tls.h @@ -0,0 +1,2 @@ +/* There has to be an architecture specific version of this file. */ +#error "architecture-specific version of <dl-tls.h> missing" diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h index 75f82bdd95..9913820b03 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -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 */ diff --git a/sysdeps/i386/dl-tls.h b/sysdeps/i386/dl-tls.h new file mode 100644 index 0000000000..2ff1aa1ef7 --- /dev/null +++ b/sysdeps/i386/dl-tls.h @@ -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 |