X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=src%2Fldso%2Fdynlink.c;h=00af886512da72e383979a4faaa4cc76d3ea3c90;hb=c7b05bc81732e9471e8f61e2dc48ff590572724e;hp=1517281a838be2ae2c835fc7d0769708eae215fc;hpb=2b74315d8a31ad8fbcd369116c82e055e0ec3fb7;p=musl diff --git a/src/ldso/dynlink.c b/src/ldso/dynlink.c index 1517281a..00af8865 100644 --- a/src/ldso/dynlink.c +++ b/src/ldso/dynlink.c @@ -1,6 +1,8 @@ #define _GNU_SOURCE #include #include +#include +#include #include #include #include @@ -48,6 +50,11 @@ struct debug { void *base; }; +struct td_index { + size_t args[2]; + struct td_index *next; +}; + struct dso { unsigned char *base; char *name; @@ -79,6 +86,7 @@ struct dso { void **new_dtv; unsigned char *new_tls; int new_dtv_idx, new_tls_idx; + struct td_index *td_index; struct dso *fini_next; char *shortname; char buf[]; @@ -89,6 +97,24 @@ struct symdef { struct dso *dso; }; +enum { + REL_ERR, + REL_SYMBOLIC, + REL_GOT, + REL_PLT, + REL_RELATIVE, + REL_OFFSET, + REL_OFFSET32, + REL_COPY, + REL_SYM_OR_REL, + REL_TLS, /* everything past here is TLS */ + REL_DTPMOD, + REL_DTPOFF, + REL_TPOFF, + REL_TPOFF_NEG, + REL_TLSDESC, +}; + #include "reloc.h" int __init_tp(void *); @@ -107,6 +133,7 @@ static jmp_buf *rtld_fail; static pthread_rwlock_t lock; static struct debug debug; static size_t tls_cnt, tls_offset, tls_align = 4*sizeof(size_t); +static size_t static_tls_cnt; static pthread_mutex_t init_fini_lock = { ._m_type = PTHREAD_MUTEX_RECURSIVE }; static long long builtin_tls[(sizeof(struct pthread) + 64)/sizeof(long long)]; @@ -132,6 +159,17 @@ static int search_vec(size_t *v, size_t *r, size_t key) return 1; } +static void error(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vsnprintf(errbuf, sizeof errbuf, fmt, ap); + va_end(ap); + if (runtime) longjmp(*rtld_fail, 1); + dprintf(2, "%s\n", errbuf); + ldso_fail = 1; +} + static uint32_t sysv_hash(const char *s0) { const unsigned char *s = (void *)s0; @@ -195,6 +233,10 @@ static Sym *gnu_lookup(const char *s, uint32_t h1, struct dso *dso) #define OK_TYPES (1<st_shndx) - if (need_def || (sym->st_info&0xf) == STT_TLS) + if (need_def || (sym->st_info&0xf) == STT_TLS + || ARCH_SYM_REJECT_UND(sym)) continue; if (!sym->st_value) if ((sym->st_info&0xf) != STT_TLS) @@ -227,6 +270,10 @@ static struct symdef find_sym(struct dso *dso, const char *s, int need_def) return def; } +#define NO_INLINE_ADDEND (1<base; @@ -235,36 +282,114 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri Sym *sym; const char *name; void *ctx; - int type; + int astype, type; int sym_index; struct symdef def; + size_t *reloc_addr; + size_t sym_val; + size_t tls_val; + size_t addend; for (; rel_size; rel+=stride, rel_size-=stride*sizeof(size_t)) { - type = R_TYPE(rel[1]); + astype = R_TYPE(rel[1]); + if (!astype) continue; + type = remap_rel(astype); + if (!type) { + error("Error relocating %s: unsupported relocation type %d", + dso->name, astype); + continue; + } sym_index = R_SYM(rel[1]); + reloc_addr = (void *)(base + rel[0]); if (sym_index) { sym = syms + sym_index; name = strings + sym->st_name; - ctx = IS_COPY(type) ? head->next : head; - def = find_sym(ctx, name, IS_PLT(type)); + ctx = type==REL_COPY ? head->next : head; + def = find_sym(ctx, name, type==REL_PLT); if (!def.sym && (sym->st_shndx != SHN_UNDEF || sym->st_info>>4 != STB_WEAK)) { - snprintf(errbuf, sizeof errbuf, - "Error relocating %s: %s: symbol not found", + error("Error relocating %s: %s: symbol not found", dso->name, name); - if (runtime) longjmp(*rtld_fail, 1); - dprintf(2, "%s\n", errbuf); - ldso_fail = 1; continue; } } else { sym = 0; def.sym = 0; - def.dso = 0; + def.dso = dso; + } + + addend = stride>2 ? rel[2] + : (1<base+def.sym->st_value : 0; + tls_val = def.sym ? def.sym->st_value : 0; + + switch(type) { + case REL_OFFSET: + addend -= (size_t)reloc_addr; + case REL_SYMBOLIC: + case REL_GOT: + case REL_PLT: + *reloc_addr = sym_val + addend; + break; + case REL_RELATIVE: + *reloc_addr = (size_t)base + addend; + break; + case REL_SYM_OR_REL: + if (sym) *reloc_addr = sym_val + addend; + else *reloc_addr = (size_t)base + addend; + break; + case REL_COPY: + memcpy(reloc_addr, (void *)sym_val, sym->st_size); + break; + case REL_OFFSET32: + *(uint32_t *)reloc_addr = sym_val + addend + - (size_t)reloc_addr; + break; + case REL_DTPMOD: + *reloc_addr = def.dso->tls_id; + break; + case REL_DTPOFF: + *reloc_addr = tls_val + addend; + break; +#ifdef TLS_ABOVE_TP + case REL_TPOFF: + *reloc_addr = tls_val + def.dso->tls_offset + TPOFF_K + addend; + break; +#else + case REL_TPOFF: + *reloc_addr = tls_val - def.dso->tls_offset + addend; + break; + case REL_TPOFF_NEG: + *reloc_addr = def.dso->tls_offset - tls_val + addend; + break; +#endif + case REL_TLSDESC: + if (stride<3) addend = reloc_addr[1]; + if (runtime && def.dso->tls_id >= static_tls_cnt) { + struct td_index *new = malloc(sizeof *new); + if (!new) error( + "Error relocating %s: cannot allocate TLSDESC for %s", + dso->name, sym ? name : "(local)" ); + new->next = dso->td_index; + dso->td_index = new; + new->args[0] = def.dso->tls_id; + new->args[1] = tls_val + addend; + reloc_addr[0] = (size_t)__tlsdesc_dynamic; + reloc_addr[1] = (size_t)new; + } else { + reloc_addr[0] = (size_t)__tlsdesc_static; +#ifdef TLS_ABOVE_TP + reloc_addr[1] = tls_val + def.dso->tls_offset + + TPOFF_K + addend; +#else + reloc_addr[1] = tls_val - def.dso->tls_offset + + addend; +#endif + } + break; } - do_single_reloc(dso, base, (void *)(base + rel[0]), type, - stride>2 ? rel[2] : 0, sym, sym?sym->st_size:0, def, - def.sym?(size_t)(def.dso->base+def.sym->st_value):0); } } @@ -520,6 +645,8 @@ static void decode_dyn(struct dso *p) p->hashtab = (void *)(p->base + dyn[DT_HASH]); if (dyn[0]&(1<rpath_orig = (void *)(p->strings + dyn[DT_RPATH]); + if (dyn[0]&(1<rpath_orig = (void *)(p->strings + dyn[DT_RUNPATH]); if (search_vec(p->dynv, dyn, DT_GNU_HASH)) p->ghashtab = (void *)(p->base + *dyn); if (search_vec(p->dynv, dyn, DT_VERSYM)) @@ -538,6 +665,11 @@ static struct dso *load_library(const char *name, struct dso *needed_by) int n_th = 0; int is_self = 0; + if (!*name) { + errno = EINVAL; + return 0; + } + /* Catch and block attempts to reload the implementation itself */ if (name[0]=='l' && name[1]=='i' && name[2]=='b') { static const char *rp, reserved[] = @@ -717,12 +849,8 @@ static void load_deps(struct dso *p) if (p->dynv[i] != DT_NEEDED) continue; dep = load_library(p->strings + p->dynv[i+1], p); if (!dep) { - snprintf(errbuf, sizeof errbuf, - "Error loading shared library %s: %m (needed by %s)", + error("Error loading shared library %s: %m (needed by %s)", p->strings + p->dynv[i+1], p->name); - if (runtime) longjmp(*rtld_fail, 1); - dprintf(2, "%s\n", errbuf); - ldso_fail = 1; continue; } if (runtime) { @@ -741,8 +869,8 @@ static void load_preload(char *s) int tmp; char *z; for (z=s; *z; s=z) { - for ( ; *s && isspace(*s); s++); - for (z=s; *z && !isspace(*z); z++); + for ( ; *s && (isspace(*s) || *s==':'); s++); + for (z=s; *z && !isspace(*z) && *z!=':'; z++); tmp = *z; *z = 0; load_library(s, 0); @@ -771,12 +899,8 @@ static void reloc_all(struct dso *p) if (p->relro_start != p->relro_end && mprotect(p->base+p->relro_start, p->relro_end-p->relro_start, PROT_READ) < 0) { - snprintf(errbuf, sizeof errbuf, - "Error relocating %s: RELRO protection failed: %m", + error("Error relocating %s: RELRO protection failed: %m", p->name); - if (runtime) longjmp(*rtld_fail, 1); - dprintf(2, "%s\n", errbuf); - ldso_fail = 1; } p->relocated = 1; @@ -915,17 +1039,15 @@ void *__copy_tls(unsigned char *mem) return td; } -void *__tls_get_addr(size_t *v) +void *__tls_get_new(size_t *v) { pthread_t self = __pthread_self(); - if (v[0]<=(size_t)self->dtv[0] && self->dtv[v[0]]) - return (char *)self->dtv[v[0]]+v[1]; /* Block signals to make accessing new TLS async-signal-safe */ sigset_t set; - pthread_sigmask(SIG_BLOCK, SIGALL_SET, &set); - if (v[0]<=(size_t)self->dtv[0] && self->dtv[v[0]]) { - pthread_sigmask(SIG_SETMASK, &set, 0); + __block_all_sigs(&set); + if (v[0]<=(size_t)self->dtv[0]) { + __restore_sigs(&set); return (char *)self->dtv[v[0]]+v[1]; } @@ -946,13 +1068,19 @@ void *__tls_get_addr(size_t *v) self->dtv = newdtv; } - /* Get new TLS memory from new DSO */ - unsigned char *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[v[0]] = mem; - memcpy(mem, p->tls_image, p->tls_len); - pthread_sigmask(SIG_SETMASK, &set, 0); + /* 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]; } @@ -1000,6 +1128,7 @@ void *__dynlink(int argc, char **argv) libc.secure = 1; } libc.page_size = aux[AT_PAGESZ]; + libc.auxv = auxv; /* If the dynamic linker was invoked as a program itself, AT_BASE * will not be set. In that case, we assume the base address is @@ -1055,12 +1184,31 @@ void *__dynlink(int argc, char **argv) size_t l = strlen(ldname); if (l >= 3 && !strcmp(ldname+l-3, "ldd")) ldd_mode = 1; *argv++ = (void *)-1; - if (argv[0] && !strcmp(argv[0], "--")) *argv++ = (void *)-1; + while (argv[0] && argv[0][0]=='-' && argv[0][1]=='-') { + char *opt = argv[0]+2; + *argv++ = (void *)-1; + if (!*opt) { + break; + } else if (!memcmp(opt, "list", 5)) { + ldd_mode = 1; + } else if (!memcmp(opt, "library-path", 12)) { + if (opt[12]=='=') env_path = opt+13; + else if (opt[12]) *argv = 0; + else if (*argv) env_path = *argv++; + } else if (!memcmp(opt, "preload", 7)) { + if (opt[7]=='=') env_preload = opt+8; + else if (opt[7]) *argv = 0; + else if (*argv) env_preload = *argv++; + } else { + argv[0] = 0; + } + argv[-1] = (void *)-1; + } if (!argv[0]) { dprintf(2, "musl libc\n" "Version %s\n" "Dynamic Program Loader\n" - "Usage: %s [--] pathname%s\n", + "Usage: %s [options] [--] pathname%s\n", __libc_get_version(), ldname, ldd_mode ? "" : " [args]"); _exit(1); @@ -1177,6 +1325,7 @@ void *__dynlink(int argc, char **argv) dprintf(2, "%s: Thread-local storage not supported by kernel.\n", argv[0]); _exit(127); } + static_tls_cnt = tls_cnt; if (ldso_fail) _exit(127); if (ldd_mode) _exit(0); @@ -1232,6 +1381,11 @@ void *dlopen(const char *file, int mode) for (p=orig_tail->next; p; p=next) { next = p->next; munmap(p->map, p->map_len); + while (p->td_index) { + void *tmp = p->td_index->next; + free(p->td_index); + p->td_index = tmp; + } free(p->deps); free(p); } @@ -1295,6 +1449,8 @@ static int invalid_dso_handle(void *h) return 1; } +void *__tls_get_addr(size_t *); + static void *do_dlsym(struct dso *p, const char *s, void *ra) { size_t i; @@ -1450,10 +1606,14 @@ static int invalid_dso_handle(void *h) } void *dlopen(const char *file, int mode) { + strcpy(errbuf, "Dynamic loading not supported"); + errflag = 1; return 0; } void *__dlsym(void *restrict p, const char *restrict s, void *restrict ra) { + errflag = 1; + snprintf(errbuf, sizeof errbuf, "Symbol not found: %s", s); return 0; } int __dladdr (const void *addr, Dl_info *info)