X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=ldso%2Fdynlink.c;h=35a90aef849c1c3f139c5f9a8639dbc123b25461;hb=dc2f368e565c37728b0d620380b849c3a1ddd78f;hp=0bd9d50ce36031ba836d7a1fb7cb168289957e00;hpb=4823b13a75b40c4408c1101b363ab00fd118fb27;p=musl diff --git a/ldso/dynlink.c b/ldso/dynlink.c index 0bd9d50c..35a90aef 100644 --- a/ldso/dynlink.c +++ b/ldso/dynlink.c @@ -52,13 +52,13 @@ struct dso { Phdr *phdr; int phnum; size_t phentsize; - int refcnt; Sym *syms; Elf_Symndx *hashtab; uint32_t *ghashtab; int16_t *versym; char *strings; - struct dso *syms_next; + struct dso *syms_next, *lazy_next; + size_t *lazy, lazy_cnt; unsigned char *map; size_t map_len; dev_t dev; @@ -113,7 +113,7 @@ static struct builtin_tls { static size_t *saved_addends, *apply_addends_to; static struct dso ldso; -static struct dso *head, *tail, *fini_head, *syms_tail; +static struct dso *head, *tail, *fini_head, *syms_tail, *lazy_head; static char *env_path, *sys_path; static unsigned long long gencnt; static int runtime; @@ -129,6 +129,7 @@ static size_t static_tls_cnt; static pthread_mutex_t init_fini_lock = { ._m_type = PTHREAD_MUTEX_RECURSIVE }; static struct fdpic_loadmap *app_loadmap; static struct fdpic_dummy_loadmap app_dummy_loadmap; +static struct dso *const nodeps_dummy; struct debug *_dl_debug_addr = &debug; @@ -258,18 +259,12 @@ static Sym *gnu_lookup_filtered(uint32_t h1, uint32_t *hashtab, struct dso *dso, static struct symdef find_sym(struct dso *dso, const char *s, int need_def) { - uint32_t h = 0, gh, gho, *ght; - size_t ghm = 0; + uint32_t h = 0, gh = gnu_hash(s), gho = gh / (8*sizeof(size_t)), *ght; + size_t ghm = 1ul << gh % (8*sizeof(size_t)); struct symdef def = {0}; for (; dso; dso=dso->syms_next) { Sym *sym; if ((ght = dso->ghashtab)) { - if (!ghm) { - gh = gnu_hash(s); - int maskbits = 8 * sizeof ghm; - gho = gh / maskbits; - ghm = 1ul << gh % maskbits; - } sym = gnu_lookup_filtered(gh, ght, dso, s, gho, ghm); } else { if (!h) h = sysv_hash(s); @@ -350,6 +345,13 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri : find_sym(ctx, name, type==REL_PLT); if (!def.sym && (sym->st_shndx != SHN_UNDEF || sym->st_info>>4 != STB_WEAK)) { + if (dso->lazy && (type==REL_PLT || type==REL_GOT)) { + dso->lazy[3*dso->lazy_cnt+0] = rel[0]; + dso->lazy[3*dso->lazy_cnt+1] = rel[1]; + dso->lazy[3*dso->lazy_cnt+2] = addend; + dso->lazy_cnt++; + continue; + } error("Error relocating %s: %s: symbol not found", dso->name, name); if (runtime) longjmp(*rtld_fail, 1); @@ -451,6 +453,26 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri } } +static void redo_lazy_relocs() +{ + struct dso *p = lazy_head, *next; + lazy_head = 0; + for (; p; p=next) { + next = p->lazy_next; + size_t size = p->lazy_cnt*3*sizeof(size_t); + p->lazy_cnt = 0; + do_relocs(p, p->lazy, size, 3); + if (p->lazy_cnt) { + p->lazy_next = lazy_head; + lazy_head = p; + } else { + free(p->lazy); + p->lazy = 0; + p->lazy_next = 0; + } + } +} + /* A huge hack: to make up for the wastefulness of shared libraries * needing at least a page of dirty memory even if they have no global * data, we reclaim the gaps at the beginning and end of writable maps @@ -943,7 +965,6 @@ static struct dso *load_library(const char *name, struct dso *needed_by) /* Search for the name to see if it's already loaded */ for (p=head->next; p; p=p->next) { if (p->shortname && !strcmp(p->shortname, name)) { - p->refcnt++; return p; } } @@ -1006,7 +1027,6 @@ static struct dso *load_library(const char *name, struct dso *needed_by) if (!p->shortname && pathname != name) p->shortname = strrchr(p->name, '/')+1; close(fd); - p->refcnt++; return p; } } @@ -1014,6 +1034,17 @@ static struct dso *load_library(const char *name, struct dso *needed_by) close(fd); if (!map) return 0; + /* Avoid the danger of getting two versions of libc mapped into the + * same process when an absolute pathname was used. The symbols + * checked are chosen to catch both musl and glibc, and to avoid + * false positives from interposition-hack libraries. */ + decode_dyn(&temp_dso); + if (find_sym(&temp_dso, "__libc_start_main", 1).sym && + find_sym(&temp_dso, "stdin", 1).sym) { + unmap_library(&temp_dso); + return load_library("libc.so", needed_by); + } + /* Allocate storage for the new DSO. When there is TLS, this * storage must include a reservation for all pre-existing * threads to obtain copies of both the new TLS, and an @@ -1033,10 +1064,8 @@ static struct dso *load_library(const char *name, struct dso *needed_by) return 0; } memcpy(p, &temp_dso, sizeof temp_dso); - decode_dyn(p); p->dev = st.st_dev; p->ino = st.st_ino; - p->refcnt = 1; p->needed_by = needed_by; p->name = p->buf; strcpy(p->name, pathname); @@ -1097,6 +1126,7 @@ static void load_deps(struct dso *p) } } } + if (!*deps) *deps = (struct dso **)&nodeps_dummy; } static void load_preload(char *s) @@ -1407,6 +1437,7 @@ _Noreturn void __dls3(size_t *sp) size_t aux[AUX_CNT], *auxv; size_t i; char *env_preload=0; + char *replace_argv0=0; size_t vdso_base; int argc = *sp; char **argv = (void *)(sp+1); @@ -1491,6 +1522,10 @@ _Noreturn void __dls3(size_t *sp) if (opt[7]=='=') env_preload = opt+8; else if (opt[7]) *argv = 0; else if (*argv) env_preload = *argv++; + } else if (!memcmp(opt, "argv0", 5)) { + if (opt[5]=='=') replace_argv0 = opt+6; + else if (opt[5]) *argv = 0; + else if (*argv) replace_argv0 = *argv++; } else { argv[0] = 0; } @@ -1647,15 +1682,39 @@ _Noreturn void __dls3(size_t *sp) debug.state = 0; _dl_debug_state(); + if (replace_argv0) argv[0] = replace_argv0; + errno = 0; CRTJMP((void *)aux[AT_ENTRY], argv-1); for(;;); } +static void prepare_lazy(struct dso *p) +{ + size_t dyn[DYN_CNT], n, flags1=0; + decode_vec(p->dynv, dyn, DYN_CNT); + search_vec(p->dynv, &flags1, DT_FLAGS_1); + if (dyn[DT_BIND_NOW] || (dyn[DT_FLAGS] & DF_BIND_NOW) || (flags1 & DF_1_NOW)) + return; + n = dyn[DT_RELSZ]/2 + dyn[DT_RELASZ]/3 + dyn[DT_PLTRELSZ]/2 + 1; + if (NEED_MIPS_GOT_RELOCS) { + size_t j=0; search_vec(p->dynv, &j, DT_MIPS_GOTSYM); + size_t i=0; search_vec(p->dynv, &i, DT_MIPS_SYMTABNO); + n += i-j; + } + p->lazy = calloc(n, 3*sizeof(size_t)); + if (!p->lazy) { + error("Error preparing lazy relocation for %s: %m", p->name); + longjmp(*rtld_fail, 1); + } + p->lazy_next = lazy_head; + lazy_head = p; +} + void *dlopen(const char *file, int mode) { - struct dso *volatile p, *orig_tail, *orig_syms_tail, *next; + struct dso *volatile p, *orig_tail, *orig_syms_tail, *orig_lazy_head, *next; struct tls_module *orig_tls_tail; size_t orig_tls_cnt, orig_tls_offset, orig_tls_align; size_t i; @@ -1673,6 +1732,7 @@ void *dlopen(const char *file, int mode) orig_tls_cnt = tls_cnt; orig_tls_offset = tls_offset; orig_tls_align = tls_align; + orig_lazy_head = lazy_head; orig_syms_tail = syms_tail; orig_tail = tail; noload = mode & RTLD_NOLOAD; @@ -1691,7 +1751,8 @@ void *dlopen(const char *file, int mode) free(p->funcdescs); if (p->rpath != p->rpath_orig) free(p->rpath); - free(p->deps); + if (p->deps != &nodeps_dummy) + free(p->deps); unmap_library(p); free(p); } @@ -1701,6 +1762,7 @@ void *dlopen(const char *file, int mode) tls_cnt = orig_tls_cnt; tls_offset = orig_tls_offset; tls_align = orig_tls_align; + lazy_head = orig_lazy_head; tail = orig_tail; tail->next = 0; p = 0; @@ -1716,13 +1778,24 @@ void *dlopen(const char *file, int mode) } /* First load handling */ - if (!p->relocated) { + int first_load = !p->deps; + if (first_load) { load_deps(p); + if (!p->relocated && (mode & RTLD_LAZY)) { + prepare_lazy(p); + for (i=0; p->deps[i]; i++) + if (!p->deps[i]->relocated) + prepare_lazy(p->deps[i]); + } + } + if (first_load || (mode & RTLD_GLOBAL)) { /* Make new symbols global, at least temporarily, so we can do * relocations. If not RTLD_GLOBAL, this is reverted below. */ add_syms(p); - if (p->deps) for (i=0; p->deps[i]; i++) + for (i=0; p->deps[i]; i++) add_syms(p->deps[i]); + } + if (first_load) { reloc_all(p); } @@ -1732,6 +1805,11 @@ void *dlopen(const char *file, int mode) if (!(mode & RTLD_GLOBAL)) revert_syms(orig_syms_tail); + /* Processing of deferred lazy relocations must not happen until + * the new libraries are committed; otherwise we could end up with + * relocations resolved to symbol definitions that get removed. */ + redo_lazy_relocs(); + update_tls_size(); _dl_debug_state(); orig_tail = tail; @@ -1815,7 +1893,7 @@ static void *do_dlsym(struct dso *p, const char *s, void *ra) return p->funcdescs + (sym - p->syms); if (sym && sym->st_value && (1<<(sym->st_info&0xf) & OK_TYPES)) return laddr(p, sym->st_value); - if (p->deps) for (i=0; p->deps[i]; i++) { + for (i=0; p->deps[i]; i++) { if ((ght = p->deps[i]->ghashtab)) { if (!gh) gh = gnu_hash(s); sym = gnu_lookup(gh, ght, p->deps[i], s);