X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;ds=sidebyside;f=ldso%2Fdynlink.c;h=a2f059cbc9941513435aa847200767cd2b8f9ada;hb=2f1f51ae7b2d78247568e7fdb8462f3c19e469a4;hp=34775b83417bf0ab114cb2cd1d40822007284dcb;hpb=403555690775f7c8806372644f543518e6664e3b;p=oweals%2Fmusl.git diff --git a/ldso/dynlink.c b/ldso/dynlink.c index 34775b83..a2f059cb 100644 --- a/ldso/dynlink.c +++ b/ldso/dynlink.c @@ -73,8 +73,11 @@ struct dso { char kernel_mapped; char mark; char bfs_built; + char runtime_loaded; struct dso **deps, *needed_by; size_t ndeps_direct; + size_t next_dep; + int ctor_visitor; char *rpath_orig, *rpath; struct tls_module tls; size_t tls_id; @@ -121,13 +124,16 @@ static int runtime; static int ldd_mode; static int ldso_fail; static int noload; +static int shutting_down; static jmp_buf *rtld_fail; static pthread_rwlock_t lock; static struct debug debug; static struct tls_module *tls_tail; static size_t tls_cnt, tls_offset, tls_align = MIN_TLS_ALIGN; static size_t static_tls_cnt; -static pthread_mutex_t init_fini_lock = { ._m_type = PTHREAD_MUTEX_RECURSIVE }; +static pthread_mutex_t init_fini_lock; +static pthread_cond_t ctor_cond; +static struct dso **main_ctor_queue; static struct fdpic_loadmap *app_loadmap; static struct fdpic_dummy_loadmap app_dummy_loadmap; @@ -1107,6 +1113,7 @@ static struct dso *load_library(const char *name, struct dso *needed_by) p->ino = st.st_ino; p->needed_by = needed_by; p->name = p->buf; + p->runtime_loaded = runtime; strcpy(p->name, pathname); /* Add a shortname only if name arg was not an explicit pathname. */ if (pathname != name) p->shortname = strrchr(p->name, '/')+1; @@ -1144,16 +1151,23 @@ static struct dso *load_library(const char *name, struct dso *needed_by) static void load_direct_deps(struct dso *p) { - size_t i, cnt; + size_t i, cnt=0; if (p->deps) return; - for (i=cnt=0; p->dynv[i]; i+=2) + /* For head, all preloads are direct pseudo-dependencies. + * Count and include them now to avoid realloc later. */ + if (p==head) for (struct dso *q=p->next; q; q=q->next) + cnt++; + for (i=0; p->dynv[i]; i+=2) if (p->dynv[i] == DT_NEEDED) cnt++; p->deps = calloc(cnt+1, sizeof *p->deps); if (!p->deps) { error("Error loading dependencies for %s", p->name); if (runtime) longjmp(*rtld_fail, 1); } - for (i=cnt=0; p->dynv[i]; i+=2) { + cnt=0; + if (p==head) for (struct dso *q=p->next; q; q=q->next) + p->deps[cnt++] = q; + for (i=0; p->dynv[i]; i+=2) { if (p->dynv[i] != DT_NEEDED) continue; struct dso *dep = load_library(p->strings + p->dynv[i+1], p); if (!dep) { @@ -1163,8 +1177,8 @@ static void load_direct_deps(struct dso *p) continue; } p->deps[cnt++] = dep; - p->deps[cnt] = 0; } + p->deps[cnt] = 0; p->ndeps_direct = cnt; } @@ -1180,6 +1194,10 @@ static void extend_bfs_deps(struct dso *p) size_t i, j, cnt, ndeps_all; struct dso **tmp; + /* Can't use realloc if the original p->deps was allocated at + * program entry and malloc has been replaced. */ + int no_realloc = __malloc_replaced && !p->runtime_loaded; + if (p->bfs_built) return; ndeps_all = p->ndeps_direct; @@ -1195,12 +1213,18 @@ static void extend_bfs_deps(struct dso *p) struct dso *dep = p->deps[i]; for (j=cnt=0; jndeps_direct; j++) if (!dep->deps[j]->mark) cnt++; - tmp = realloc(p->deps, sizeof(*p->deps) * (ndeps_all+cnt+1)); + tmp = no_realloc ? + malloc(sizeof(*tmp) * (ndeps_all+cnt+1)) : + realloc(p->deps, sizeof(*tmp) * (ndeps_all+cnt+1)); if (!tmp) { error("Error recording dependencies for %s", p->name); if (runtime) longjmp(*rtld_fail, 1); continue; } + if (no_realloc) { + memcpy(tmp, p->deps, sizeof(*tmp) * (ndeps_all+1)); + no_realloc = 0; + } p->deps = tmp; for (j=0; jndeps_direct; j++) { if (dep->deps[j]->mark) continue; @@ -1327,7 +1351,18 @@ void __libc_exit_fini() { struct dso *p; size_t dyn[DYN_CNT]; + int self = __pthread_self()->tid; + + /* Take both locks before setting shutting_down, so that + * either lock is sufficient to read its value. The lock + * order matches that in dlopen to avoid deadlock. */ + pthread_rwlock_wrlock(&lock); + pthread_mutex_lock(&init_fini_lock); + shutting_down = 1; + pthread_rwlock_unlock(&lock); for (p=fini_head; p; p=p->fini_next) { + while (p->ctor_visitor && p->ctor_visitor!=self) + pthread_cond_wait(&ctor_cond, &init_fini_lock); if (!p->constructed) continue; decode_vec(p->dynv, dyn, DYN_CNT); if (dyn[0] & (1<prev) { + size_t cnt, qpos, spos, i; + struct dso *p, **queue, **stack; + + if (ldd_mode) return 0; + + /* Bound on queue size is the total number of indirect deps. + * If a bfs deps list was built, we can use it. Otherwise, + * bound by the total number of DSOs, which is always safe and + * is reasonable we use it (for main app at startup). */ + if (dso->bfs_built) { + for (cnt=0; dso->deps[cnt]; cnt++) + dso->deps[cnt]->mark = 0; + cnt++; /* self, not included in deps */ + } else { + for (cnt=0, p=head; p; cnt++, p=p->next) + p->mark = 0; + } + cnt++; /* termination slot */ + stack = queue = calloc(cnt, sizeof *queue); + + if (!queue) { + error("Error allocating constructor queue: %m\n"); + if (runtime) longjmp(*rtld_fail, 1); + return 0; + } + + /* Opposite ends of the allocated buffer serve as an output queue + * and a working stack. Setup initial stack with just the argument + * dso and initial queue empty... */ + qpos = 0; + spos = cnt; + stack[--spos] = dso; + dso->next_dep = 0; + dso->mark = 1; + + /* Then perform pseudo-DFS sort, but ignoring circular deps. */ + while (sposnext_dep < p->ndeps_direct) { + if (p->deps[p->next_dep]->mark) { + p->next_dep++; + } else { + stack[--spos] = p; + p = p->deps[p->next_dep]; + p->next_dep = 0; + p->mark = 1; + } + } + queue[qpos++] = p; + } + queue[qpos] = 0; + for (i=0; imark = 0; + + return queue; +} + +static void do_init_fini(struct dso **queue) +{ + struct dso *p; + size_t dyn[DYN_CNT], i; + int self = __pthread_self()->tid; + + pthread_mutex_lock(&init_fini_lock); + for (i=0; (p=queue[i]); i++) { + while ((p->ctor_visitor && p->ctor_visitor!=self) || shutting_down) + pthread_cond_wait(&ctor_cond, &init_fini_lock); + if (p->ctor_visitor || p->constructed) + continue; if (p->constructed) continue; - p->constructed = 1; + p->ctor_visitor = self; + decode_vec(p->dynv, dyn, DYN_CNT); if (dyn[0] & ((1<fini_next = fini_head; fini_head = p; } + + pthread_mutex_unlock(&init_fini_lock); + #ifndef NO_LEGACY_INITFINI if ((dyn[0] & (1<ctor_visitor = 0; + p->constructed = 1; + pthread_cond_broadcast(&ctor_cond); } - if (need_locking) pthread_mutex_unlock(&init_fini_lock); + pthread_mutex_unlock(&init_fini_lock); } void __libc_start_init(void) { - do_init_fini(tail); + do_init_fini(main_ctor_queue); + if (!__malloc_replaced) free(main_ctor_queue); + main_ctor_queue = 0; } static void dl_debug_state(void) @@ -1746,6 +1849,14 @@ _Noreturn void __dls3(size_t *sp) } } + /* This must be done before final relocations, since it calls + * malloc, which may be provided by the application. Calling any + * application code prior to the jump to its entry point is not + * valid in our model and does not work with FDPIC, where there + * are additional relocation-like fixups that only the entry point + * code can see to perform. */ + main_ctor_queue = queue_ctors(&app); + /* The main program must be relocated LAST since it may contin * copy relocations which depend on libraries' relocations. */ reloc_all(app.next); @@ -1833,6 +1944,7 @@ void *dlopen(const char *file, int mode) size_t i; int cs; jmp_buf jb; + struct dso **volatile ctor_queue = 0; if (!file) return head; @@ -1841,6 +1953,10 @@ void *dlopen(const char *file, int mode) __inhibit_ptc(); p = 0; + if (shutting_down) { + error("Cannot dlopen while program is exiting."); + goto end; + } orig_tls_tail = tls_tail; orig_tls_cnt = tls_cnt; orig_tls_offset = tls_offset; @@ -1867,6 +1983,7 @@ void *dlopen(const char *file, int mode) free(p->deps); unmap_library(p); free(p); + free(ctor_queue); } if (!orig_tls_tail) libc.tls_head = 0; tls_tail = orig_tls_tail; @@ -1892,6 +2009,9 @@ void *dlopen(const char *file, int mode) /* First load handling */ load_deps(p); extend_bfs_deps(p); + pthread_mutex_lock(&init_fini_lock); + if (!p->constructed) ctor_queue = queue_ctors(p); + pthread_mutex_unlock(&init_fini_lock); if (!p->relocated && (mode & RTLD_LAZY)) { prepare_lazy(p); for (i=0; p->deps[i]; i++) @@ -1929,7 +2049,10 @@ end: __release_ptc(); if (p) gencnt++; pthread_rwlock_unlock(&lock); - if (p) do_init_fini(orig_tail); + if (ctor_queue) { + do_init_fini(ctor_queue); + free(ctor_queue); + } pthread_setcancelstate(cs, 0); return p; }