/* Do some minimal initialization which has to be done during the startup of the C library. */ void __pthread_initialize_minimal(void) { #ifdef USE_TLS pthread_descr self; /* First of all init __pthread_handles[0] and [1] if needed. */ # if __LT_SPINLOCK_INIT != 0 __pthread_handles[0].h_lock = __LOCK_INITIALIZER; __pthread_handles[1].h_lock = __LOCK_INITIALIZER; # endif # ifndef SHARED /* Unlike in the dynamically linked case the dynamic linker has not taken care of initializing the TLS data structures. */ __libc_setup_tls (TLS_TCB_SIZE, TLS_TCB_ALIGN); # elif !USE___THREAD if (__builtin_expect (GL(dl_tls_dtv_slotinfo_list) == NULL, 0)) { tcbhead_t *tcbp; /* There is no actual TLS being used, so the thread register was not initialized in the dynamic linker. */ /* We need to install special hooks so that the malloc and memalign calls in _dl_tls_setup and _dl_allocate_tls won't cause full malloc initialization that will try to set up its thread state. */ extern void __libc_malloc_pthread_startup (bool first_time); __libc_malloc_pthread_startup (true); if (__builtin_expect (_dl_tls_setup (), 0) || __builtin_expect ((tcbp = _dl_allocate_tls (NULL)) == NULL, 0)) { static const char msg[] = "\ cannot allocate TLS data structures for initial thread\n"; TEMP_FAILURE_RETRY (write_not_cancel (STDERR_FILENO, msg, sizeof msg - 1)); abort (); } const char *lossage = TLS_INIT_TP (tcbp, 0); if (__builtin_expect (lossage != NULL, 0)) { static const char msg[] = "cannot set up thread-local storage: "; const char nl = '\n'; TEMP_FAILURE_RETRY (write_not_cancel (STDERR_FILENO, msg, sizeof msg - 1)); TEMP_FAILURE_RETRY (write_not_cancel (STDERR_FILENO, lossage, strlen (lossage))); TEMP_FAILURE_RETRY (write_not_cancel (STDERR_FILENO, &nl, 1)); } /* Though it was allocated with libc's malloc, that was done without the user's __malloc_hook installed. A later realloc that uses the hooks might not work with that block from the plain malloc. So we record this block as unfreeable just as the dynamic linker does when it allocates the DTV before the libc malloc exists. */ GL(dl_initial_dtv) = GET_DTV (tcbp); __libc_malloc_pthread_startup (false); }
void internal_function _dl_deallocate_tls (void *tcb, bool dealloc_tcb) { dtv_t *dtv = GET_DTV (tcb); size_t cnt; /* We need to free the memory allocated for non-static TLS. */ for (cnt = 0; cnt < dtv[-1].counter; ++cnt) if (! dtv[1 + cnt].pointer.is_static && dtv[1 + cnt].pointer.val != TLS_DTV_UNALLOCATED) _dl_free (dtv[1 + cnt].pointer.val); /* The array starts with dtv[-1]. */ if (dtv != _dl_initial_dtv) _dl_free (dtv - 1); if (dealloc_tcb) { # ifdef TLS_TCB_AT_TP /* The TCB follows the TLS blocks. Back up to free the whole block. */ tcb -= _dl_tls_static_size - TLS_TCB_SIZE; # elif defined(TLS_DTV_AT_TP) /* Back up the TLS_PRE_TCB_SIZE bytes. */ tcb -= (TLS_PRE_TCB_SIZE + _dl_tls_static_align - 1) & ~(_dl_tls_static_align - 1); # endif _dl_free (tcb); } }
internal_function 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_assert (_dl_tls_dtv_slotinfo_list == NULL); _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. */ 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. */ /* Skeep slot[0]: it will be never used */ slotinfo[++i].map = l; } _dl_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_debug_early("\ncannot allocate TLS data structures for initial thread"); _dl_exit(30); } /* 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_debug_early("cannot set up thread-local storage: %s\n", lossage); _dl_exit(30); } tls_init_tp_called = true; return tcbp; }
internal_function _dl_allocate_tls_init (void *result) { if (result == NULL) /* The memory allocation failed. */ return NULL; dtv_t *dtv = GET_DTV (result); struct dtv_slotinfo_list *listp; size_t total = 0; size_t maxgen = 0; /* We have to prepare the dtv for all currently loaded modules using TLS. For those which are dynamically loaded we add the values indicating deferred allocation. */ listp = _dl_tls_dtv_slotinfo_list; while (1) { size_t cnt; for (cnt = total == 0 ? 1 : 0; cnt < listp->len; ++cnt) { struct link_map *map; void *dest; /* Check for the total number of used slots. */ if (total + cnt > _dl_tls_max_dtv_idx) break; map = listp->slotinfo[cnt].map; if (map == NULL) /* Unused entry. */ continue; /* Keep track of the maximum generation number. This might not be the generation counter. */ maxgen = MAX (maxgen, listp->slotinfo[cnt].gen); if (map->l_tls_offset == NO_TLS_OFFSET) { /* For dynamically loaded modules we simply store the value indicating deferred allocation. */ dtv[map->l_tls_modid].pointer.val = TLS_DTV_UNALLOCATED; dtv[map->l_tls_modid].pointer.is_static = false; continue; } _dl_assert (map->l_tls_modid == cnt); _dl_assert (map->l_tls_blocksize >= map->l_tls_initimage_size); # ifdef TLS_TCB_AT_TP _dl_assert ((size_t) map->l_tls_offset >= map->l_tls_blocksize); dest = (char *) result - map->l_tls_offset; # elif defined(TLS_DTV_AT_TP) dest = (char *) result + map->l_tls_offset; # else # error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined" # endif /* Copy the initialization image and clear the BSS part. */ dtv[map->l_tls_modid].pointer.val = dest; dtv[map->l_tls_modid].pointer.is_static = true; _dl_memcpy(dest, map->l_tls_initimage, map->l_tls_initimage_size); _dl_memset((dest + map->l_tls_initimage_size), '\0', map->l_tls_blocksize - map->l_tls_initimage_size); } total += cnt; if (total >= _dl_tls_max_dtv_idx) break; listp = listp->next; _dl_assert (listp != NULL); } /* The DTV version is up-to-date now. */ dtv[0].counter = maxgen; return result; }
/* * Get the TD address - this function needs to be called * when the DTV value has not been restored yet. */ void* get_td_addr(void) { dtv_t* dtv = GET_DTV(); return read_dtv_val(dtv); }