diff options
-rw-r--r-- | elf/dl-misc.c | 31 | ||||
-rw-r--r-- | elf/dl-tls.c | 159 | ||||
-rw-r--r-- | elf/rtld.c | 2 | ||||
-rw-r--r-- | sysdeps/generic/ldsodefs.h | 5 |
4 files changed, 189 insertions, 8 deletions
diff --git a/elf/dl-misc.c b/elf/dl-misc.c index ec70298..ed90394 100644 --- a/elf/dl-misc.c +++ b/elf/dl-misc.c @@ -81,7 +81,10 @@ _dl_debug_vdprintf (int fd, int tag_p, const char *fmt, va_list arg) struct iovec iov[NIOVMAX]; int niov = 0; pid_t pid = 0; - char pidbuf[12]; + char pidbuf[23]; + pid_t tid = 0; + /* Start with a known bad value, should never get used. */ + char *tag_start = NULL; while (*fmt != '\0') { @@ -93,20 +96,32 @@ _dl_debug_vdprintf (int fd, int tag_p, const char *fmt, va_list arg) colon followed by a tab. */ if (pid == 0) { - char *p; + char *p = &pidbuf[21]; + pid = __getpid (); assert (pid >= 0 && sizeof (pid_t) <= 4); - p = _itoa (pid, &pidbuf[10], 10, 0); - while (p > pidbuf) + + /* If we are doing thread-related output, maybe add a thread id, + taking care that pid continues to appear at the same + positions. */ + tid = _dl_tls_tid (); + if (tid > 0) + { + p = _itoa (tid, p, 10, 0); + *--p = '/'; + } + tag_start = p - 10; + p = _itoa (pid, p, 10, 0); + while (p > tag_start) *--p = ' '; - pidbuf[10] = ':'; - pidbuf[11] = '\t'; + pidbuf[21] = ':'; + pidbuf[22] = '\t'; } /* Append to the output. */ assert (niov < NIOVMAX); - iov[niov].iov_len = 12; - iov[niov++].iov_base = pidbuf; + iov[niov].iov_len = &(pidbuf[23]) - tag_start; + iov[niov++].iov_base = tag_start; /* No more tags until we see the next newline. */ tag_p = -1; diff --git a/elf/dl-tls.c b/elf/dl-tls.c index 67198f7..592512f 100644 --- a/elf/dl-tls.c +++ b/elf/dl-tls.c @@ -30,6 +30,8 @@ #include <dl-tls.h> #include <ldsodefs.h> +static void _dl_print_dtv(const char *msg, dtv_t *dtv, int numentries); + /* Amount of excess space to allocate in the static TLS area to allow dynamic loading of modules defining IE-model TLS data. */ #define TLS_STATIC_SURPLUS 64 + DL_NNS * 100 @@ -428,6 +430,13 @@ _dl_resize_dtv (dtv_t *dtv) memset (newp + 2 + oldsize, '\0', (newsize - oldsize) * sizeof (dtv_t)); + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_TLS)) + _dl_debug_printf ("Resized dtv 0x%0*Zx, size %lu, to dtv 0x%0*Zx, size %lu\n", + (int) sizeof (void *) * 2, (unsigned long int) dtv, + oldsize, + (int) sizeof (void *) * 2, (unsigned long int) &newp[1], + newsize); + /* Return the generation counter. */ return &newp[1]; } @@ -484,6 +493,12 @@ _dl_allocate_tls_init (void *result) dtv[map->l_tls_modid].pointer.val = TLS_DTV_UNALLOCATED; dtv[map->l_tls_modid].pointer.to_free = NULL; + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_TLS)) + _dl_debug_printf ("_dl_allocate_tls_init unallocates %sdtv 0x%0*Zx module %lu\n", + (dtv == THREAD_DTV () ? "own " : ""), + (int) sizeof (void *) * 2, + (unsigned long int) dtv, + map->l_tls_modid); if (map->l_tls_offset == NO_TLS_OFFSET || map->l_tls_offset == FORCED_DYNAMIC_TLS_OFFSET) @@ -503,6 +518,14 @@ _dl_allocate_tls_init (void *result) /* Set up the DTV entry. The simplified __tls_get_addr that some platforms use in static programs requires it. */ dtv[map->l_tls_modid].pointer.val = dest; + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_TLS)) + _dl_debug_printf ("_dl_allocate_tls_init sets %sdtv 0x%0*Zx module %lu to 0x%0*Zx\n", + (dtv == THREAD_DTV () ? "own " : ""), + (int) sizeof (void *) * 2, + (unsigned long int) dtv, + map->l_tls_modid, + (int) sizeof (void *) * 2, + (unsigned long int) dest); /* Copy the initialization image and clear the BSS part. */ memset (__mempcpy (dest, map->l_tls_initimage, @@ -521,6 +544,9 @@ _dl_allocate_tls_init (void *result) /* The DTV version is up-to-date now. */ dtv[0].counter = maxgen; + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_TLS)) + _dl_print_dtv ("_dl_allocate_tls_init return ", dtv, 10); + return result; } rtld_hidden_def (_dl_allocate_tls_init) @@ -655,6 +681,12 @@ _dl_update_slotinfo (unsigned long int req_modid) struct link_map *the_map = NULL; dtv_t *dtv = THREAD_DTV (); + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_TLS)) + // (should mention module name?) + _dl_debug_printf ("Updating slot info for own dtv 0x%0*Zx module %lu\n", + (int) sizeof (void *) * 2, (unsigned long int) dtv, + req_modid); + /* The global dl_tls_dtv_slotinfo array contains for each module index the generation counter current when the entry was created. This array never shrinks so that all module indices which were @@ -698,6 +730,11 @@ _dl_update_slotinfo (unsigned long int req_modid) if (dtv[0].counter >= listp->slotinfo[idx].gen) { _dl_unmask_signals (&old); + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_TLS)) + _dl_debug_printf ("Slot info update for own dtv 0x%0*Zx module %lu done, exiting early\n", + (int) sizeof (void *) * 2, + (unsigned long int) dtv, + req_modid); return the_map; } } @@ -728,6 +765,13 @@ _dl_update_slotinfo (unsigned long int req_modid) { /* If this modid was used at some point the memory might still be allocated. */ + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_TLS)) + _dl_debug_printf ("Slot info update for own dtv 0x%0*Zx module %lu unallocating 0x%0*Zx\n", + (int) sizeof (void *) * 2, + (unsigned long int) dtv, + total + cnt, + (int) sizeof (void *) * 2, + (unsigned long int) dtv[total + cnt].pointer.val); __signal_safe_free (dtv[total + cnt].pointer.to_free); dtv[total + cnt].pointer.val = TLS_DTV_UNALLOCATED; dtv[total + cnt].pointer.to_free = NULL; @@ -755,6 +799,13 @@ _dl_update_slotinfo (unsigned long int req_modid) dtv entry free it. */ /* XXX Ideally we will at some point create a memory pool. */ + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_TLS)) + _dl_debug_printf ("Slot info update for own dtv 0x%0*Zx module %lu unallocating 0x%0*Zx\n", + (int) sizeof (void *) * 2, + (unsigned long int) dtv, + modid, + (int) sizeof (void *) * 2, + (unsigned long int) dtv[modid].pointer.val); __signal_safe_free (dtv[modid].pointer.to_free); dtv[modid].pointer.val = TLS_DTV_UNALLOCATED; dtv[modid].pointer.to_free = NULL; @@ -796,6 +847,15 @@ tls_get_addr_tail (GET_ADDR_ARGS, dtv_t *dtv, struct link_map *the_map) the_map = listp->slotinfo[idx].map; } + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_TLS)) + { + char *map_name = (the_map->l_name ? the_map->l_name : "no name"); + _dl_debug_printf ("tls_get_addr_tail entry, own dtv 0x%0*Zx module %lu (%s) pointer.val = 0x%0*Zx\n", + (int) sizeof (void *) * 2, (unsigned long int) dtv, + GET_ADDR_MODULE, map_name, + (int) sizeof (void *) * 2, + (unsigned long int) dtv[GET_ADDR_MODULE].pointer.val); + } if (!GLRO(dl_async_signal_safe)) { @@ -826,6 +886,13 @@ tls_get_addr_tail (GET_ADDR_ARGS, dtv_t *dtv, struct link_map *the_map) dtv[GET_ADDR_MODULE].pointer.to_free = NULL; dtv[GET_ADDR_MODULE].pointer.val = p; + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_TLS)) + _dl_debug_printf ("tls_get_addr_tail sets own dtv 0x%0*Zx module %lu pointer.val to 0x%0*Zx, returns\n", + (int) sizeof (void *) * 2, + (unsigned long int) dtv, + GET_ADDR_MODULE, + (int) sizeof (void *) * 2, + (unsigned long int) p); return (char *) p + GET_ADDR_OFFSET; } @@ -835,6 +902,11 @@ tls_get_addr_tail (GET_ADDR_ARGS, dtv_t *dtv, struct link_map *the_map) struct dtv_pointer result = allocate_and_init (the_map); dtv[GET_ADDR_MODULE].pointer = result; assert (result.to_free != NULL); + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_TLS)) + _dl_debug_printf ("tls_get_addr_tail sets own dtv 0x%0*Zx module %lu pointer.val to 0x%0*Zx, returns\n", + (int) sizeof (void *) * 2, (unsigned long int) dtv, + GET_ADDR_MODULE, + (int) sizeof (void *) * 2, (unsigned long int) result.val); return (char *) result.val + GET_ADDR_OFFSET; @@ -849,6 +921,9 @@ tls_get_addr_tail (GET_ADDR_ARGS, dtv_t *dtv, struct link_map *the_map) { _dl_unmask_signals (&old); + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_TLS)) + _dl_debug_printf ("tls_get_addr_tail keeps own dtv pointer.val, returns\n"); + return (char *) dtv[GET_ADDR_MODULE].pointer.val + GET_ADDR_OFFSET; } @@ -874,10 +949,22 @@ tls_get_addr_tail (GET_ADDR_ARGS, dtv_t *dtv, struct link_map *the_map) if (offset == FORCED_DYNAMIC_TLS_OFFSET) { signal_safe_allocate_and_init (&dtv[GET_ADDR_MODULE], the_map); + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_TLS)) + _dl_debug_printf ("tls_get_addr_tail allocates own dtv 0x%0*Zx module %lu pointer.val = 0x%0*Zx\n", + (int) sizeof (void *) * 2, + (unsigned long int) dtv, + GET_ADDR_MODULE, + (int) sizeof (void *) * 2, + (unsigned long int) dtv[GET_ADDR_MODULE].pointer.val); } else { void ** volatile pp = &dtv[GET_ADDR_MODULE].pointer.val; + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_TLS)) + _dl_debug_printf ("tls_get_addr_tail waiting for own dtv 0x%0*Zx module %lu to be allocated\n", + (int) sizeof (void *) * 2, + (unsigned long int) dtv, + GET_ADDR_MODULE); while (atomic_forced_read (*pp) == TLS_DTV_UNALLOCATED) { /* for lack of a better (safe) thing to do, just spin. @@ -893,6 +980,13 @@ tls_get_addr_tail (GET_ADDR_ARGS, dtv_t *dtv, struct link_map *the_map) init_one_static_tls, guaranteeing that we see their write of the tls_initimage into the static region. */ atomic_read_barrier (); + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_TLS)) + _dl_debug_printf ("tls_get_addr_tail sees own dtv 0x%0*Zx module %lu has pointer.val = 0x%0*Zx\n", + (int) sizeof (void *) * 2, + (unsigned long int) dtv, + GET_ADDR_MODULE, + (int) sizeof (void *) * 2, + (unsigned long int) dtv[GET_ADDR_MODULE].pointer.val); } assert (dtv[GET_ADDR_MODULE].pointer.val != TLS_DTV_UNALLOCATED); _dl_unmask_signals (&old); @@ -1052,3 +1146,68 @@ cannot create TLS data structures")); listp->slotinfo[idx].map = l; listp->slotinfo[idx].gen = GL(dl_tls_generation) + 1; } + +/* Return a thread id of the current thread if we are debugging tls and the + value is meaningful. */ + +pid_t +_dl_tls_tid (void) +{ + if (!__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_TLS)) + return 0; + +#ifdef SHARED + if (GL(dl_initial_dtv) == NULL) + return 0; +#endif + + struct pthread *thr = THREAD_SELF; + + return thr->tid; +} + +/* Print all or part of a dtv. Note that the output may be large; for instance + nptl/tst-stack4 has dtv's with hundreds of entries. */ + +static void +_dl_print_dtv (const char *msg, dtv_t *dtv, int numentries) +{ + size_t cnt, last_used, num_to_print, i; + + cnt = dtv[-1].counter; + last_used = 0; + for (i = 1; i <= cnt; ++i) + { + if (dtv[i].pointer.val || dtv[i].pointer.to_free) + last_used = i; + } + num_to_print = last_used; + if (numentries >= 0 && numentries < num_to_print) + num_to_print = numentries; + _dl_debug_printf ("%sdtv 0x%0*Zx has %lu used of %lu entries, generation %lu\n", + msg, + (int) sizeof (void *) * 2, (unsigned long int) dtv, + last_used, cnt, dtv[0].counter); + for (i = 1; i <= num_to_print; ++i) + { + if (dtv[i].pointer.to_free == dtv[i].pointer.val) + _dl_debug_printf ("%*lu: pointer.val = 0x%0*Zx to_free = same\n", + 4, i, + (int) sizeof (void *) * 2, + (unsigned long int) dtv[i].pointer.val); + else if (dtv[i].pointer.to_free) + _dl_debug_printf ("%*lu: pointer.val = 0x%0*Zx to_free = 0x%0*Zx\n", + 4, i, + (int) sizeof (void *) * 2, + (unsigned long int) dtv[i].pointer.val, + (int) sizeof (void *) * 2, + (unsigned long int) dtv[i].pointer.to_free); + else + _dl_debug_printf ("%*lu: pointer.val = 0x%0*Zx\n", + 4, i, + (int) sizeof (void *) * 2, + (unsigned long int) dtv[i].pointer.val); + } + if (num_to_print < last_used) + _dl_debug_printf (" [...]\n"); +} @@ -2395,6 +2395,8 @@ process_dl_debug (const char *dl_debug) DL_DEBUG_VERSIONS | DL_DEBUG_IMPCALLS }, { LEN_AND_STR ("scopes"), "display scope information", DL_DEBUG_SCOPES }, + { LEN_AND_STR ("tls"), "display thread-local storage processing", + DL_DEBUG_TLS }, { LEN_AND_STR ("fastload"), "display fastload information", DL_DEBUG_FASTLOAD }, { LEN_AND_STR ("all"), "all previous options combined", diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h index 32647f8..0495f85 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -500,6 +500,8 @@ struct rtld_global_ro /* Google-local. */ #define DL_DEBUG_FASTLOAD (1 << 12) +#define DL_DEBUG_TLS (1 << 13) + /* OS version. */ EXTERN unsigned int _dl_osversion; /* Platform name. */ @@ -1196,6 +1198,9 @@ extern struct link_map *_dl_update_slotinfo (unsigned long int req_modid) but never touch anything. Return null if it's not allocated yet. */ extern void *_dl_tls_get_addr_soft (struct link_map *l) attribute_hidden; +/* Return an id for the current thread if possible, otherwise 0. */ +extern pid_t _dl_tls_tid (void) attribute_hidden; + extern int _dl_addr_inside_object (struct link_map *l, const ElfW(Addr) addr) attribute_hidden; |