svn commit: branches/uClibc-nptl/ldso: include ldso
sjhill at uclibc.org
sjhill at uclibc.org
Sat Oct 1 15:34:01 UTC 2005
Author: sjhill
Date: 2005-10-01 08:33:59 -0700 (Sat, 01 Oct 2005)
New Revision: 11719
Log:
Latest 'ldso' changes for TLS support dated 20050930. I just did not get them checked in last night.
Modified:
branches/uClibc-nptl/ldso/include/dl-hash.h
branches/uClibc-nptl/ldso/include/ldsodefs.h
branches/uClibc-nptl/ldso/ldso/dl-elf.c
branches/uClibc-nptl/ldso/ldso/dl-tls.c
branches/uClibc-nptl/ldso/ldso/ldso.c
Changeset:
Modified: branches/uClibc-nptl/ldso/include/dl-hash.h
===================================================================
--- branches/uClibc-nptl/ldso/include/dl-hash.h 2005-10-01 10:16:02 UTC (rev 11718)
+++ branches/uClibc-nptl/ldso/include/dl-hash.h 2005-10-01 15:33:59 UTC (rev 11719)
@@ -121,9 +121,10 @@
#define LD_ERROR_NOTDYN 5
#define LD_ERROR_MMAP_FAILED 6
#define LD_ERROR_NODYNAMIC 7
-#define LD_WRONG_RELOCS 8
-#define LD_BAD_HANDLE 9
-#define LD_NO_SYMBOL 10
+#define LD_ERROR_TLS_FAILED 8
+#define LD_WRONG_RELOCS 9
+#define LD_BAD_HANDLE 10
+#define LD_NO_SYMBOL 11
Modified: branches/uClibc-nptl/ldso/include/ldsodefs.h
===================================================================
--- branches/uClibc-nptl/ldso/include/ldsodefs.h 2005-10-01 10:16:02 UTC (rev 11718)
+++ branches/uClibc-nptl/ldso/include/ldsodefs.h 2005-10-01 15:33:59 UTC (rev 11719)
@@ -88,6 +88,8 @@
EXTERN size_t _dl_tls_static_used;
/* Alignment requirement of the static TLS block. */
EXTERN size_t _dl_tls_static_align;
+/* Function pointer for catching TLS errors. */
+EXTERN void **(*_dl_error_catch_tsd) (void) __attribute__ ((const));
/* Number of additional entries in the slotinfo array of each slotinfo
list element. A large number makes it almost certain take we never
Modified: branches/uClibc-nptl/ldso/ldso/dl-elf.c
===================================================================
--- branches/uClibc-nptl/ldso/ldso/dl-elf.c 2005-10-01 10:16:02 UTC (rev 11718)
+++ branches/uClibc-nptl/ldso/ldso/dl-elf.c 2005-10-01 15:33:59 UTC (rev 11719)
@@ -432,6 +432,87 @@
maxvma = ppnt->p_vaddr + ppnt->p_memsz;
}
}
+
+ if (ppnt->p_type == PT_TLS)
+ {
+#if USE_TLS
+ if (ppnt->p_memsz == 0)
+ /* Nothing to do for an empty segment. */
+ continue;
+
+ tpnt->l_tls_blocksize = ppnt->p_memsz;
+ tpnt->l_tls_align = ppnt->p_align;
+ if (ppnt->p_align == 0)
+ tpnt->l_tls_firstbyte_offset = 0;
+ else
+ tpnt->l_tls_firstbyte_offset = ppnt->p_vaddr &
+ (ppnt->p_align - 1);
+ tpnt->l_tls_initimage_size = ppnt->p_filesz;
+ /* Since we don't know the load address yet only store the
+ offset. We will adjust it later. */
+ tpnt->l_tls_initimage = (void *) ppnt->p_vaddr;
+
+ /* If _dl_tls_dtv_slotinfo_list == NULL, then ldso.c did
+ not set up TLS data structures, so don't use them now. */
+ if (__builtin_expect (_dl_tls_dtv_slotinfo_list != NULL, 1))
+ {
+ /* Assign the next available module ID. */
+ tpnt->l_tls_modid = _dl_next_tls_modid ();
+ continue;
+ }
+
+# ifdef SHARED
+ if (tpnt->prev == NULL)
+ /* We are loading the executable itself when the dynamic linker
+ was executed directly. The setup will happen later. */
+ continue;
+
+ /* In a static binary there is no way to tell if we dynamically
+ loaded libpthread. */
+ if (_dl_error_catch_tsd == &_dl_initial_error_catch_tsd)
+# endif
+ {
+ /* We have not yet loaded libpthread.
+ We can do the TLS setup right now! */
+
+ void *tcb;
+
+ /* The first call allocates TLS bookkeeping data structures.
+ Then we allocate the TCB for the initial thread. */
+ if (__builtin_expect (_dl_tls_setup (), 0)
+ || __builtin_expect ((tcb = _dl_allocate_tls(NULL)) == NULL, 0))
+ {
+ _dl_dprintf(2, "%s: '%s' cannot allocate TLS data structures for initial thread\n", _dl_progname, libname);
+ goto tls_failed; /* I'm using a goto, so shoot me. */
+ }
+
+ /* Now we install the TCB in the thread register. */
+ if (__builtin_expect (TLS_INIT_TP (tcb, 0) == NULL, 1))
+ {
+ /* Now we are all good. */
+ tpnt->l_tls_modid = ++_dl_tls_max_dtv_idx;
+ continue;
+ }
+
+ /* The kernel is too old or somesuch. */
+ _dl_dprintf(2, "%s: '%s' unknown TLS error\n", _dl_progname, libname);
+ _dl_deallocate_tls (tcb, 1);
+ }
+tls_failed:
+#else
+ /*
+ * Yup, the user was an idiot and tried to sneak in a library with
+ * TLS in it and we don't support it. Let's fall on our own sword
+ * and scream at the luser while we die.
+ */
+ _dl_dprintf(2, "%s: '%s' library contains unsupported TLS\n",
+ _dl_progname, libname);
+#endif
+ _dl_internal_error_number = LD_ERROR_TLS_FAILED;
+ _dl_close(infile);
+ _dl_munmap(header, _dl_pagesize);
+ return NULL;
+ }
ppnt++;
};
@@ -585,6 +666,12 @@
tpnt->ppnt = (ElfW(Phdr) *)(intptr_t) (tpnt->loadaddr + epnt->e_phoff);
tpnt->n_phent = epnt->e_phnum;
+#if USE_TLS
+ /* Adjust the address of the TLS initialization image. */
+ if (tpnt->l_tls_initimage != NULL)
+ tpnt->l_tls_initimage = (char *) tpnt->l_tls_initimage + tpnt->loadaddr;
+#endif
+
/*
* Add this object into the symbol chain
*/
@@ -681,6 +768,13 @@
tpnt->dynamic_info[DT_PLTRELSZ]);
}
}
+
+#ifdef USE_TLS
+ /* Add object to slot information data if necessasy. */
+ if (tpnt->l_tls_blocksize != 0 && tls_init_tp_called)
+ _dl_add_to_slotinfo ((struct link_map *) tpnt);
+#endif
+
return goof;
}
Modified: branches/uClibc-nptl/ldso/ldso/dl-tls.c
===================================================================
--- branches/uClibc-nptl/ldso/ldso/dl-tls.c 2005-10-01 10:16:02 UTC (rev 11718)
+++ branches/uClibc-nptl/ldso/ldso/dl-tls.c 2005-10-01 15:33:59 UTC (rev 11719)
@@ -26,16 +26,79 @@
* SUCH DAMAGE.
*/
+/*
+ * The big TODO list:
+ *
+ * - Environment variables LD_TRACE_LOADED_OBJECTS and LD_PRELOAD
+ * need to be supported.
+ * - Config option LDSO_PRELOAD_FILE_SUPPORT needs to be supported.
+ * - Support TLS information for 'ldd' command.
+ *
+ */
+
#include <tls.h>
#include <dl-tls.h>
#include <ldsodefs.h>
-#define calloc(a, b) NULL
-#define malloc(a) NULL
-#define realloc(a, b) NULL
-#define free(a)
-#define _dl_memalign(a, b) NULL
+void *(*_dl_calloc_function) (size_t __nmemb, size_t __size) = NULL;
+void *(*_dl_realloc_function) (void *__ptr, size_t __size) = NULL;
+void *(*_dl_memalign_function) (size_t __boundary, size_t __size) = NULL;
+void (*_dl_free_function) (void *__ptr) = NULL;
+void *_dl_memalign (size_t __boundary, size_t __size);
+
+
+void *
+_dl_calloc (size_t __nmemb, size_t __size)
+{
+ void *result;
+ size_t size = (__size * __nmemb);
+
+ if (_dl_calloc_function) {
+#if 1
+ _dl_debug_early("Calling libc version\n");
+#endif
+ return (*_dl_calloc_function) (__nmemb, __size);
+ }
+
+ _dl_debug_early("allocating memory and zeroing\n");
+ if (__nmemb && __size != (size / __nmemb)) {
+ _dl_dprintf(2, "%si:%i: unaligned structures\n",
+ __FUNCTION__, __LINE__);
+ _dl_exit(1);
+ }
+ if ((result = _dl_malloc(__size)) != NULL) {
+ _dl_memset(result, 0, size);
+ }
+ return result;
+}
+
+void *
+_dl_realloc (void * __ptr, size_t __size)
+{
+ _dl_debug_early("NOT IMPLEMENTED PROPERLY!!!\n");
+ if (_dl_realloc_function) {
+#if 1
+ _dl_debug_early("Calling libc version\n");
+#endif
+ return (*_dl_realloc_function) (__ptr, __size);
+ }
+ return NULL;
+}
+
+void
+_dl_free (void *__ptr)
+{
+ _dl_debug_early("NOT IMPLEMENTED PROPERLY!!!\n");
+ if (_dl_free_function) {
+#if 1
+ _dl_debug_early("Calling libc version\n");
+#endif
+ (*_dl_free_function) (__ptr);
+ }
+}
+
+
/* 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
@@ -62,7 +125,16 @@
/* Value used for dtv entries for which the allocation is delayed. */
#define TLS_DTV_UNALLOCATED ((void *) -1l)
+/* Thread control block pointer. */
+void *tcbp = NULL;
+#define _dl_fatal_printf(fmt, args...) \
+{ \
+ do { \
+ _dl_dprintf(2, "%s:%i: " fmt, __FUNCTION__, __LINE__, ## args); \
+ } while (1); \
+}
+
/* Taken from glibc/elf/dl-reloc.c */
#define CHECK_STATIC_TLS(sym_map) \
do { \
@@ -114,11 +186,7 @@
__attribute__ ((__noreturn__))
oom (void)
{
- do {
- _dl_dprintf (_dl_debug_file,
- "cannot allocate thread-local memory: ABORT\n");
- _dl_exit (127);
- } while (1);
+ _dl_fatal_printf("cannot allocate thread-local memory: ABORT\n");
}
size_t
@@ -338,7 +406,7 @@
const size_t nelem = 2 + TLS_SLOTINFO_SURPLUS;
_dl_tls_dtv_slotinfo_list
- = calloc (1, (sizeof (struct dtv_slotinfo_list)
+ = _dl_calloc (1, (sizeof (struct dtv_slotinfo_list)
+ nelem * sizeof (struct dtv_slotinfo)));
if (_dl_tls_dtv_slotinfo_list == NULL)
return -1;
@@ -367,7 +435,11 @@
initial set of modules. This should avoid in most cases expansions
of the dtv. */
dtv_length = _dl_tls_max_dtv_idx + DTV_SURPLUS;
+#ifndef __UCLIBC__
dtv = calloc (dtv_length + 2, sizeof (dtv_t));
+#else
+ dtv = _dl_calloc (dtv_length + 2, sizeof (dtv_t));
+#endif
if (dtv != NULL)
{
/* This is the initial length of the dtv. */
@@ -435,7 +507,7 @@
result = allocate_dtv (result);
if (result == NULL)
- free (allocated);
+ _dl_free (allocated);
}
return result;
@@ -545,11 +617,11 @@
for (cnt = 0; cnt < dtv[-1].counter; ++cnt)
if (! dtv[1 + cnt].pointer.is_static
&& dtv[1 + cnt].pointer.val != TLS_DTV_UNALLOCATED)
- free (dtv[1 + cnt].pointer.val);
+ _dl_free (dtv[1 + cnt].pointer.val);
/* The array starts with dtv[-1]. */
if (dtv != _dl_initial_dtv)
- free (dtv - 1);
+ _dl_free (dtv - 1);
if (dealloc_tcb)
{
@@ -561,7 +633,7 @@
tcb -= (TLS_PRE_TCB_SIZE + _dl_tls_static_align - 1)
& ~(_dl_tls_static_align - 1);
# endif
- free (tcb);
+ _dl_free (tcb);
}
}
rtld_hidden_def (_dl_deallocate_tls)
@@ -655,7 +727,7 @@
if (! dtv[total + cnt].pointer.is_static
&& dtv[total + cnt].pointer.val != TLS_DTV_UNALLOCATED)
{
- free (dtv[total + cnt].pointer.val);
+ _dl_free (dtv[total + cnt].pointer.val);
dtv[total + cnt].pointer.val = TLS_DTV_UNALLOCATED;
}
@@ -681,14 +753,14 @@
malloc instead of the real malloc. We can't
free it, we have to abandon the old storage. */
- newp = malloc ((2 + newsize) * sizeof (dtv_t));
+ newp = _dl_malloc ((2 + newsize) * sizeof (dtv_t));
if (newp == NULL)
oom ();
_dl_memcpy (newp, &dtv[-1], oldsize * sizeof (dtv_t));
}
else
{
- newp = realloc (&dtv[-1],
+ newp = _dl_realloc (&dtv[-1],
(2 + newsize) * sizeof (dtv_t));
if (newp == NULL)
oom ();
@@ -718,7 +790,7 @@
deallocate even if it is this dtv entry we are
supposed to load. The reason is that we call
memalign and not malloc. */
- free (dtv[modid].pointer.val);
+ _dl_free (dtv[modid].pointer.val);
/* This module is loaded dynamically- We defer memory
allocation. */
@@ -812,7 +884,7 @@
assert (idx == 0);
listp = prevp->next = (struct dtv_slotinfo_list *)
- malloc (sizeof (struct dtv_slotinfo_list)
+ _dl_malloc (sizeof (struct dtv_slotinfo_list)
+ TLS_SLOTINFO_SURPLUS * sizeof (struct dtv_slotinfo));
if (listp == NULL)
{
@@ -841,3 +913,98 @@
listp->slotinfo[idx].map = l;
listp->slotinfo[idx].gen = _dl_tls_generation + 1;
}
+
+/* Taken from glibc/elf/rtld.c */
+static bool tls_init_tp_called;
+
+/* _dl_error_catch_tsd points to this for the single-threaded case.
+ It's reset by the thread library for multithreaded programs. */
+void ** __attribute__ ((const))
+_dl_initial_error_catch_tsd (void)
+{
+ static void *data;
+ return &data;
+}
+
+static void *
+init_tls (void)
+{
+ /* Number of elements in the static TLS block. */
+ _dl_tls_static_nelem = _dl_tls_max_dtv_idx;
+
+ /* Do not do this twice. The audit interface might have required
+ the DTV interfaces to be set up early. */
+ if (_dl_initial_dtv != NULL)
+ return NULL;
+
+ /* Allocate the array which contains the information about the
+ dtv slots. We allocate a few entries more than needed to
+ avoid the need for reallocation. */
+ size_t nelem = _dl_tls_max_dtv_idx + 1 + TLS_SLOTINFO_SURPLUS;
+
+ /* Allocate. */
+ _dl_tls_dtv_slotinfo_list = (struct dtv_slotinfo_list *)
+ _dl_calloc (sizeof (struct dtv_slotinfo_list)
+ + nelem * sizeof (struct dtv_slotinfo), 1);
+ /* No need to check the return value. If memory allocation failed
+ the program would have been terminated. */
+
+ struct dtv_slotinfo *slotinfo = _dl_tls_dtv_slotinfo_list->slotinfo;
+ _dl_tls_dtv_slotinfo_list->len = nelem;
+ _dl_tls_dtv_slotinfo_list->next = NULL;
+
+ /* Fill in the information from the loaded modules. No namespace
+ but the base one can be filled at this time. */
+#ifndef __UCLIBC__
+ assert (_dl_ns[LM_ID_BASE + 1]._ns_loaded == NULL);
+ int i = 0;
+ struct link_map *l;
+ for (l = _dl_ns[LM_ID_BASE]._ns_loaded; l != NULL; l = l->l_next)
+ if (l->l_tls_blocksize != 0)
+ {
+ /* This is a module with TLS data. Store the map reference.
+ The generation counter is zero. */
+ slotinfo[i].map = l;
+ /* slotinfo[i].gen = 0; */
+ ++i;
+ }
+#else
+ int i = 0;
+ struct link_map *l;
+ for (l = (struct link_map *) _dl_loaded_modules; l != NULL; l = l->l_next)
+ if (l->l_tls_blocksize != 0)
+ {
+ /* This is a module with TLS data. Store the map reference.
+ The generation counter is zero. */
+ slotinfo[i].map = l;
+ /* slotinfo[i].gen = 0; */
+ ++i;
+ }
+#endif
+ assert (i == _dl_tls_max_dtv_idx);
+
+ /* Compute the TLS offsets for the various blocks. */
+ _dl_determine_tlsoffset ();
+
+ /* Construct the static TLS block and the dtv for the initial
+ thread. For some platforms this will include allocating memory
+ for the thread descriptor. The memory for the TLS block will
+ never be freed. It should be allocated accordingly. The dtv
+ array can be changed if dynamic loading requires it. */
+ void *tcbp = _dl_allocate_tls_storage ();
+ if (tcbp == NULL)
+ _dl_fatal_printf ("\ncannot allocate TLS data structures for initial thread");
+
+ /* Store for detection of the special case by __tls_get_addr
+ so it knows not to pass this dtv to the normal realloc. */
+ _dl_initial_dtv = GET_DTV (tcbp);
+
+ /* And finally install it for the main thread. If ld.so itself uses
+ TLS we know the thread pointer was initialized earlier. */
+ const char *lossage = TLS_INIT_TP (tcbp, USE___THREAD);
+ if (__builtin_expect (lossage != NULL, 0))
+ _dl_fatal_printf ("cannot set up thread-local storage: %s\n", lossage);
+ tls_init_tp_called = true;
+
+ return tcbp;
+}
Modified: branches/uClibc-nptl/ldso/ldso/ldso.c
===================================================================
--- branches/uClibc-nptl/ldso/ldso/ldso.c 2005-10-01 10:16:02 UTC (rev 11718)
+++ branches/uClibc-nptl/ldso/ldso/ldso.c 2005-10-01 15:33:59 UTC (rev 11719)
@@ -228,12 +228,22 @@
"app_tpnt->loadaddr=%x\n", app_tpnt->loadaddr);
}
+#if USE_TLS
/*
- * This adds another loop in the non-NPTL case, but we have to
- * catch the stupid user who tries to run a binary with TLS data
- * in it. For NPTL, we fill in the TLS data for the application
- * like we are supposed to.
+ * Adjust the address of the TLS initialization image in case
+ * the executable is actually an ET_DYN object.
*/
+ if (app_tpnt->l_tls_initimage != NULL)
+ app_tpnt->l_tls_initimage =
+ (char *) app_tpnt->l_tls_initimage + app_tpnt->loadaddr;
+#endif
+
+ /*
+ * This adds another loop, but we have to catch the stupid user who
+ * tries to run a binary with TLS data and the linker does not support
+ * it. Otherwise, we fill in the TLS data for the application like we
+ * are supposed to.
+ */
{
int i;
ElfW(Phdr) *ppnt = (ElfW(Phdr) *) auxvt[AT_PHDR].a_un.a_val;
@@ -263,12 +273,24 @@
}
break;
#else
- _dl_dprintf(_dl_debug_file, "Program uses TLS, but ld-uClibc.so does not support it!\n");
- _dl_exit(1);
+ _dl_fatal_printf("Program uses TLS, but ld-uClibc.so does not support it!\n");
#endif
}
}
+#if USE_TLS
+ /* We do not initialize any of the TLS functionality unless any of the
+ * initial modules uses TLS. This makes dynamic loading of modules with
+ * TLS impossible, but to support it requires either eagerly doing setup
+ * now or lazily doing it later. Doing it now makes us incompatible with
+ * an old kernel that can't perform TLS_INIT_TP, even if no TLS is ever
+ * used. Trying to do it lazily is too hairy to try when there could be
+ * multiple threads (from a non-TLS-using libpthread). */
+ bool was_tls_init_tp_called = tls_init_tp_called;
+ if (tcbp == NULL)
+ tcbp = init_tls ();
+#endif
+
/*
* This is used by gdb to locate the chain of shared libraries that are
* currently loaded.
@@ -817,6 +839,36 @@
_dl_malloc_function = (void* (*)(size_t)) (intptr_t) _dl_find_hash("malloc",
_dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT);
+#if USE_TLS
+ /* Find the real functions and make ldso functions use them from now on */
+ _dl_calloc_function = (void* (*)(size_t, size_t)) (intptr_t)
+ _dl_find_hash("calloc", _dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT);
+ _dl_realloc_function = (void* (*)(void *, size_t)) (intptr_t)
+ _dl_find_hash("recalloc", _dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT);
+ _dl_free_function = (void (*)(void *)) (intptr_t)
+ _dl_find_hash("free", _dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT);
+ _dl_memalign_function = (void* (*)(size_t, size_t)) (intptr_t)
+ _dl_find_hash("memalign", _dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT);
+
+ if (!was_tls_init_tp_called && _dl_tls_max_dtv_idx > 0)
+ ++_dl_tls_generation;
+
+ /* Now that we have completed relocation, the initializer data
+ for the TLS blocks has its final values and we can copy them
+ into the main thread's TLS area, which we allocated above. */
+ _dl_allocate_tls_init (tcbp);
+
+ /* And finally install it for the main thread. If ld.so itself uses
+ TLS we know the thread pointer was initialized earlier. */
+ if (! tls_init_tp_called)
+ {
+ const char *lossage = TLS_INIT_TP (tcbp, USE___THREAD);
+ if (__builtin_expect (lossage != NULL, 0))
+ _dl_fatal_printf ("cannot set up thread-local storage: %s\n",
+ lossage);
+ }
+#endif
+
/* Notify the debugger that all objects are now mapped in. */
_dl_debug_addr->r_state = RT_CONSISTENT;
_dl_debug_state();
@@ -902,5 +954,32 @@
return retval;
}
+#if USE_TLS
+void * _dl_memalign (size_t __boundary, size_t __size)
+{
+ void *result;
+ int i = 0;
+ size_t delta;
+ size_t rounded = 0;
+
+ if (_dl_memalign_function) {
+#if 1
+ _dl_debug_early("Calling libc version\n");
+#endif
+ return (*_dl_memalign_function) (__boundary, __size);
+ }
+
+ _dl_debug_early("allocating aligned memory\n");
+ while (rounded < __boundary) {
+ rounded = (1 << i++);
+ }
+ delta = (((size_t) _dl_malloc_addr + __size) % rounded);
+ if ((result = _dl_malloc(rounded - delta)) == NULL)
+ return result;
+ result = _dl_malloc(__size);
+ return result;
+}
+#endif
+
#include "dl-hash.c"
#include "dl-elf.c"
More information about the uClibc-cvs
mailing list