+ struct dso *p;
+ size_t dyn[DYN_CNT];
+ for (p=fini_head; p; p=p->fini_next) {
+ if (!p->constructed) continue;
+ decode_vec(p->dynv, dyn, DYN_CNT);
+ if (dyn[0] & (1<<DT_FINI_ARRAY)) {
+ size_t n = dyn[DT_FINI_ARRAYSZ]/sizeof(size_t);
+ size_t *fn = (size_t *)laddr(p, dyn[DT_FINI_ARRAY])+n;
+ while (n--) ((void (*)(void))*--fn)();
+ }
+#ifndef NO_LEGACY_INITFINI
+ if ((dyn[0] & (1<<DT_FINI)) && dyn[DT_FINI])
+ fpaddr(p, dyn[DT_FINI])();
+#endif
+ }
+}
+
+static void do_init_fini(struct dso *p)
+{
+ size_t dyn[DYN_CNT];
+ int need_locking = libc.threads_minus_1;
+ /* Allow recursive calls that arise when a library calls
+ * dlopen from one of its constructors, but block any
+ * other threads until all ctors have finished. */
+ if (need_locking) pthread_mutex_lock(&init_fini_lock);
+ for (; p; p=p->prev) {
+ if (p->constructed) continue;
+ p->constructed = 1;
+ decode_vec(p->dynv, dyn, DYN_CNT);
+ if (dyn[0] & ((1<<DT_FINI) | (1<<DT_FINI_ARRAY))) {
+ p->fini_next = fini_head;
+ fini_head = p;
+ }
+#ifndef NO_LEGACY_INITFINI
+ if ((dyn[0] & (1<<DT_INIT)) && dyn[DT_INIT])
+ fpaddr(p, dyn[DT_INIT])();
+#endif
+ if (dyn[0] & (1<<DT_INIT_ARRAY)) {
+ size_t n = dyn[DT_INIT_ARRAYSZ]/sizeof(size_t);
+ size_t *fn = laddr(p, dyn[DT_INIT_ARRAY]);
+ while (n--) ((void (*)(void))*fn++)();
+ }
+ if (!need_locking && libc.threads_minus_1) {
+ need_locking = 1;
+ pthread_mutex_lock(&init_fini_lock);
+ }
+ }
+ if (need_locking) pthread_mutex_unlock(&init_fini_lock);
+}
+
+void __libc_start_init(void)
+{
+ do_init_fini(tail);
+}
+
+static void dl_debug_state(void)
+{
+}
+
+weak_alias(dl_debug_state, _dl_debug_state);
+
+void __reset_tls()
+{
+ pthread_t self = __pthread_self();
+ struct dso *p;
+ for (p=head; p; p=p->next) {
+ if (!p->tls_id || !self->dtv[p->tls_id]) continue;
+ memcpy(self->dtv[p->tls_id], p->tls_image, p->tls_len);
+ memset((char *)self->dtv[p->tls_id]+p->tls_len, 0,
+ p->tls_size - p->tls_len);
+ if (p->tls_id == (size_t)self->dtv[0]) break;
+ }
+}
+
+void *__copy_tls(unsigned char *mem)
+{
+ pthread_t td;
+ struct dso *p;
+ void **dtv;
+
+#ifdef TLS_ABOVE_TP
+ dtv = (void **)(mem + libc.tls_size) - (tls_cnt + 1);
+
+ mem += -((uintptr_t)mem + sizeof(struct pthread)) & (tls_align-1);
+ td = (pthread_t)mem;
+ mem += sizeof(struct pthread);
+
+ for (p=head; p; p=p->next) {
+ if (!p->tls_id) continue;
+ dtv[p->tls_id] = mem + p->tls_offset;
+ memcpy(dtv[p->tls_id], p->tls_image, p->tls_len);
+ }
+#else
+ dtv = (void **)mem;
+
+ mem += libc.tls_size - sizeof(struct pthread);
+ mem -= (uintptr_t)mem & (tls_align-1);
+ td = (pthread_t)mem;
+
+ for (p=head; p; p=p->next) {
+ if (!p->tls_id) continue;
+ dtv[p->tls_id] = mem - p->tls_offset;
+ memcpy(dtv[p->tls_id], p->tls_image, p->tls_len);
+ }
+#endif
+ dtv[0] = (void *)tls_cnt;
+ td->dtv = td->dtv_copy = dtv;
+ return td;
+}
+
+__attribute__((__visibility__("hidden")))
+void *__tls_get_new(size_t *v)
+{
+ pthread_t self = __pthread_self();
+
+ /* Block signals to make accessing new TLS async-signal-safe */
+ sigset_t set;
+ __block_all_sigs(&set);
+ if (v[0]<=(size_t)self->dtv[0]) {
+ __restore_sigs(&set);
+ return (char *)self->dtv[v[0]]+v[1]+DTP_OFFSET;
+ }
+
+ /* This is safe without any locks held because, if the caller
+ * is able to request the Nth entry of the DTV, the DSO list
+ * must be valid at least that far out and it was synchronized
+ * at program startup or by an already-completed call to dlopen. */
+ struct dso *p;
+ for (p=head; p->tls_id != v[0]; p=p->next);
+
+ /* Get new DTV space from new DSO if needed */
+ if (v[0] > (size_t)self->dtv[0]) {
+ void **newdtv = p->new_dtv +
+ (v[0]+1)*sizeof(void *)*a_fetch_add(&p->new_dtv_idx,1);
+ memcpy(newdtv, self->dtv,
+ ((size_t)self->dtv[0]+1) * sizeof(void *));
+ newdtv[0] = (void *)v[0];
+ self->dtv = self->dtv_copy = newdtv;
+ }
+
+ /* Get new TLS memory from all new DSOs up to the requested one */
+ unsigned char *mem;
+ for (p=head; ; p=p->next) {
+ if (!p->tls_id || self->dtv[p->tls_id]) continue;
+ mem = p->new_tls + (p->tls_size + p->tls_align)
+ * a_fetch_add(&p->new_tls_idx,1);
+ mem += ((uintptr_t)p->tls_image - (uintptr_t)mem)
+ & (p->tls_align-1);
+ self->dtv[p->tls_id] = mem;
+ memcpy(mem, p->tls_image, p->tls_len);
+ if (p->tls_id == v[0]) break;
+ }
+ __restore_sigs(&set);
+ return mem + v[1] + DTP_OFFSET;
+}
+
+static void update_tls_size()
+{
+ libc.tls_size = ALIGN(
+ (1+tls_cnt) * sizeof(void *) +
+ tls_offset +
+ sizeof(struct pthread) +
+ tls_align * 2,
+ tls_align);
+}