X-Git-Url: http://nsz.repo.hu/git/?p=musl;a=blobdiff_plain;f=src%2Fldso%2Fdynlink.c;h=8ff8e6965f34aae9733bc01c210aed846d1bbd2b;hp=a171df977196c708c65819d5de885112d2f98644;hb=c91aa03d2488ef2c48276510dec360ed9582e861;hpb=649cec5f9868070b4d350b861ee7f68b03a552a8 diff --git a/src/ldso/dynlink.c b/src/ldso/dynlink.c index a171df97..8ff8e696 100644 --- a/src/ldso/dynlink.c +++ b/src/ldso/dynlink.c @@ -1,3 +1,4 @@ +#define _GNU_SOURCE #include #include #include @@ -16,14 +17,15 @@ #include #include #include +#include "pthread_impl.h" +#include "libc.h" +#undef libc static int errflag; static char errbuf[128]; #ifdef SHARED -#include "reloc.h" - #if ULONG_MAX == 0xffffffff typedef Elf32_Ehdr Ehdr; typedef Elf32_Phdr Phdr; @@ -55,6 +57,7 @@ struct dso { int refcnt; Sym *syms; uint32_t *hashtab; + uint32_t *ghashtab; char *strings; unsigned char *map; size_t map_len; @@ -64,21 +67,28 @@ struct dso { char relocated; char constructed; struct dso **deps; + void *tls_image; + size_t tls_len, tls_size, tls_align, tls_id; char *shortname; char buf[]; }; +#include "reloc.h" + void __init_ssp(size_t *); +void *__install_initial_tls(void *); static struct dso *head, *tail, *libc; static char *env_path, *sys_path, *r_path; -static int rtld_used; static int ssp_used; static int runtime; static int ldd_mode; +static int ldso_fail; static jmp_buf rtld_fail; static pthread_rwlock_t lock; static struct debug debug; +static size_t *auxv; +static size_t tls_cnt, tls_size; struct debug *_dl_debug_addr = &debug; @@ -94,7 +104,15 @@ static void decode_vec(size_t *v, size_t *a, size_t cnt) } } -static uint32_t hash(const char *s0) +static int search_vec(size_t *v, size_t *r, size_t key) +{ + for (; v[0]!=key; v+=2) + if (!v[0]) return 0; + *r = v[1]; + return 1; +} + +static uint32_t sysv_hash(const char *s0) { const unsigned char *s = (void *)s0; uint_fast32_t h = 0; @@ -105,9 +123,21 @@ static uint32_t hash(const char *s0) return h & 0xfffffff; } -static Sym *lookup(const char *s, uint32_t h, Sym *syms, uint32_t *hashtab, char *strings) +static uint32_t gnu_hash(const char *s0) +{ + const unsigned char *s = (void *)s0; + uint_fast32_t h = 5381; + for (; *s; s++) + h = h*33 + *s; + return h; +} + +static Sym *sysv_lookup(const char *s, uint32_t h, struct dso *dso) { size_t i; + Sym *syms = dso->syms; + uint32_t *hashtab = dso->hashtab; + char *strings = dso->strings; for (i=hashtab[2+h%hashtab[0]]; i; i=hashtab[2+hashtab[0]+i]) { if (!strcmp(s, strings+syms[i].st_name)) return syms+i; @@ -115,20 +145,57 @@ static Sym *lookup(const char *s, uint32_t h, Sym *syms, uint32_t *hashtab, char return 0; } +static Sym *gnu_lookup(const char *s, uint32_t h1, struct dso *dso) +{ + Sym *sym; + char *strings; + uint32_t *hashtab = dso->ghashtab; + uint32_t nbuckets = hashtab[0]; + uint32_t *buckets = hashtab + 4 + hashtab[2]*(sizeof(size_t)/4); + uint32_t h2; + uint32_t *hashval; + uint32_t n = buckets[h1 % nbuckets]; + + if (!n) return 0; + + strings = dso->strings; + sym = dso->syms + n; + hashval = buckets + nbuckets + (n - hashtab[1]); + + for (h1 |= 1; ; sym++) { + h2 = *hashval++; + if ((h1 == (h2|1)) && !strcmp(s, strings + sym->st_name)) + return sym; + if (h2 & 1) break; + } + + return 0; +} + #define OK_TYPES (1<ghashtab) { + gh = gnu_hash(s); + if (gh == 0x1f4039c9 && !strcmp(s, "__stack_chk_fail")) ssp_used = 1; + } else { + h = sysv_hash(s); + if (h == 0x595a4cc && !strcmp(s, "__stack_chk_fail")) ssp_used = 1; + } for (; dso; dso=dso->next) { Sym *sym; if (!dso->global) continue; - sym = lookup(s, h, dso->syms, dso->hashtab, dso->strings); + if (dso->ghashtab) { + if (!gh) gh = gnu_hash(s); + sym = gnu_lookup(s, gh, dso); + } else { + if (!h) h = sysv_hash(s); + sym = sysv_lookup(s, h, dso); + } if (sym && (!need_def || sym->st_shndx) && sym->st_value && (1<<(sym->st_info&0xf) & OK_TYPES) && (1<<(sym->st_info>>4) & OK_BINDS)) { @@ -140,8 +207,11 @@ static void *find_sym(struct dso *dso, const char *s, int need_def) return def; } -static void do_relocs(unsigned char *base, size_t *rel, size_t rel_size, size_t stride, Sym *syms, char *strings, struct dso *dso) +static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stride) { + unsigned char *base = dso->base; + Sym *syms = dso->syms; + char *strings = dso->strings; Sym *sym; const char *name; size_t sym_val, sym_size; @@ -157,7 +227,7 @@ static void do_relocs(unsigned char *base, size_t *rel, size_t rel_size, size_t if (sym_index) { sym = syms + sym_index; name = strings + sym->st_name; - ctx = IS_COPY(type) ? dso->next : dso; + ctx = IS_COPY(type) ? head->next : head; sym_val = (size_t)find_sym(ctx, name, IS_PLT(type)); if (!sym_val && sym->st_info>>4 != STB_WEAK) { snprintf(errbuf, sizeof errbuf, @@ -165,9 +235,12 @@ static void do_relocs(unsigned char *base, size_t *rel, size_t rel_size, size_t dso->name, name); if (runtime) longjmp(rtld_fail, 1); dprintf(2, "%s\n", errbuf); - _exit(127); + ldso_fail = 1; + continue; } sym_size = sym->st_size; + } else { + sym_val = sym_size = 0; } do_single_reloc(reloc_addr, type, sym_val, sym_size, base, rel[2]); } @@ -204,7 +277,7 @@ static void reclaim_gaps(unsigned char *base, Phdr *ph, size_t phent, size_t phc } } -static void *map_library(int fd, size_t *lenp, unsigned char **basep, size_t *dynp) +static void *map_library(int fd, struct dso *dso) { Ehdr buf[(896+sizeof(Ehdr))/sizeof(Ehdr)]; size_t phsize; @@ -216,6 +289,7 @@ static void *map_library(int fd, size_t *lenp, unsigned char **basep, size_t *dy unsigned prot; unsigned char *map, *base; size_t dyn; + size_t tls_image=0; size_t i; ssize_t l = read(fd, buf, sizeof buf); @@ -232,6 +306,12 @@ static void *map_library(int fd, size_t *lenp, unsigned char **basep, size_t *dy for (i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) { if (ph->p_type == PT_DYNAMIC) dyn = ph->p_vaddr; + if (ph->p_type == PT_TLS) { + tls_image = ph->p_vaddr; + dso->tls_align = ph->p_align; + dso->tls_len = ph->p_filesz; + dso->tls_size = ph->p_memsz; + } if (ph->p_type != PT_LOAD) continue; if (ph->p_vaddr < addr_min) { addr_min = ph->p_vaddr; @@ -286,9 +366,11 @@ static void *map_library(int fd, size_t *lenp, unsigned char **basep, size_t *dy } if (!runtime) reclaim_gaps(base, (void *)((char *)buf + eh->e_phoff), eh->e_phentsize, eh->e_phnum); - *lenp = map_len; - *basep = base; - *dynp = dyn; + dso->map = map; + dso->map_len = map_len; + dso->base = base; + dso->dynv = (void *)(base+dyn); + if (dso->tls_size) dso->tls_image = (void *)(base+tls_image); return map; error: munmap(map, map_len); @@ -305,7 +387,7 @@ static int path_open(const char *name, const char *search, char *buf, size_t buf z = strchr(s, ':'); l = z ? z-s : strlen(s); snprintf(buf, buf_size, "%.*s/%s", l, s, name); - if ((fd = open(buf, O_RDONLY))>=0) return fd; + if ((fd = open(buf, O_RDONLY|O_CLOEXEC))>=0) return fd; s += l; } } @@ -315,8 +397,11 @@ static void decode_dyn(struct dso *p) size_t dyn[DYN_CNT] = {0}; decode_vec(p->dynv, dyn, DYN_CNT); p->syms = (void *)(p->base + dyn[DT_SYMTAB]); - p->hashtab = (void *)(p->base + dyn[DT_HASH]); p->strings = (void *)(p->base + dyn[DT_STRTAB]); + if (dyn[0]&(1<hashtab = (void *)(p->base + dyn[DT_HASH]); + if (search_vec(p->dynv, dyn, DT_GNU_HASH)) + p->ghashtab = (void *)(p->base + *dyn); } static struct dso *load_library(const char *name) @@ -325,7 +410,7 @@ static struct dso *load_library(const char *name) const char *pathname; unsigned char *base, *map; size_t dyno, map_len; - struct dso *p; + struct dso *p, temp_dso = {0}; int fd; struct stat st; @@ -349,7 +434,7 @@ static struct dso *load_library(const char *name) } if (strchr(name, '/')) { pathname = name; - fd = open(name, O_RDONLY); + fd = open(name, O_RDONLY|O_CLOEXEC); } else { /* Search for the name to see if it's already loaded */ for (p=head->next; p; p=p->next) { @@ -364,7 +449,7 @@ static struct dso *load_library(const char *name) if (fd < 0 && env_path) fd = path_open(name, env_path, buf, sizeof buf); if (fd < 0) { if (!sys_path) { - FILE *f = fopen(ETC_LDSO_PATH, "r"); + FILE *f = fopen(ETC_LDSO_PATH, "rbe"); if (f) { if (getline(&sys_path, (size_t[1]){0}, f) > 0) sys_path[strlen(sys_path)-1]=0; @@ -392,21 +477,21 @@ static struct dso *load_library(const char *name) return p; } } - map = map_library(fd, &map_len, &base, &dyno); + map = map_library(fd, &temp_dso); close(fd); if (!map) return 0; - p = calloc(1, sizeof *p + strlen(pathname) + 1); + p = malloc(sizeof *p + strlen(pathname) + 1); if (!p) { munmap(map, map_len); return 0; } - - p->map = map; - p->map_len = map_len; - p->base = base; - p->dynv = (void *)(base + dyno); + memcpy(p, &temp_dso, sizeof temp_dso); decode_dyn(p); - + if (p->tls_image) { + p->tls_id = ++tls_cnt; + tls_size += p->tls_size + p->tls_align + 8*sizeof(size_t) - 1 + & -4*sizeof(size_t); + } p->dev = st.st_dev; p->ino = st.st_ino; p->refcnt = 1; @@ -442,7 +527,8 @@ static void load_deps(struct dso *p) p->strings + p->dynv[i+1], p->name); if (runtime) longjmp(rtld_fail, 1); dprintf(2, "%s\n", errbuf); - _exit(127); + ldso_fail = 1; + continue; } if (runtime) { tmp = realloc(*deps, sizeof(*tmp)*(ndeps+2)); @@ -481,12 +567,13 @@ static void reloc_all(struct dso *p) for (; p; p=p->next) { if (p->relocated) continue; decode_vec(p->dynv, dyn, DYN_CNT); - do_relocs(p->base, (void *)(p->base+dyn[DT_JMPREL]), dyn[DT_PLTRELSZ], - 2+(dyn[DT_PLTREL]==DT_RELA), p->syms, p->strings, head); - do_relocs(p->base, (void *)(p->base+dyn[DT_REL]), dyn[DT_RELSZ], - 2, p->syms, p->strings, head); - do_relocs(p->base, (void *)(p->base+dyn[DT_RELA]), dyn[DT_RELASZ], - 3, p->syms, p->strings, head); +#ifdef NEED_ARCH_RELOCS + do_arch_relocs(p, head); +#endif + do_relocs(p, (void *)(p->base+dyn[DT_JMPREL]), dyn[DT_PLTRELSZ], + 2+(dyn[DT_PLTREL]==DT_RELA)); + do_relocs(p, (void *)(p->base+dyn[DT_REL]), dyn[DT_RELSZ], 2); + do_relocs(p, (void *)(p->base+dyn[DT_RELA]), dyn[DT_RELASZ], 3); p->relocated = 1; } } @@ -496,7 +583,7 @@ static void free_all(struct dso *p) struct dso *n; while (p) { n = p->next; - if (p->map) free(p); + if (p->map && p!=libc && p!=head) free(p); p = n; } } @@ -509,6 +596,22 @@ static size_t find_dyn(Phdr *ph, size_t cnt, size_t stride) return 0; } +static void find_map_range(Phdr *ph, size_t cnt, size_t stride, struct dso *p) +{ + size_t min_addr = -1, max_addr = 0; + for (; cnt--; ph = (void *)((char *)ph + stride)) { + if (ph->p_type != PT_LOAD) continue; + if (ph->p_vaddr < min_addr) + min_addr = ph->p_vaddr; + if (ph->p_vaddr+ph->p_memsz > max_addr) + max_addr = ph->p_vaddr+ph->p_memsz; + } + min_addr &= -PAGE_SIZE; + max_addr = (max_addr + PAGE_SIZE-1) & -PAGE_SIZE; + p->map = p->base + min_addr; + p->map_len = max_addr - min_addr; +} + static void do_init_fini(struct dso *p) { size_t dyn[DYN_CNT] = {0}; @@ -527,9 +630,38 @@ void _dl_debug_state(void) { } +void *__copy_tls(unsigned char *mem, size_t cnt) +{ + struct dso *p; + void **dtv = (void *)mem; + dtv[0] = (void *)cnt; + mem = (void *)(dtv + cnt + 1); + for (p=tail; p; p=p->prev) { + if (p->tls_id-1 >= cnt) continue; + mem += -p->tls_len & (4*sizeof(size_t)-1); + mem += ((uintptr_t)p->tls_image - (uintptr_t)mem) + & (p->tls_align-1); + dtv[p->tls_id] = mem; + memcpy(mem, p->tls_image, p->tls_len); + mem += p->tls_size; + } + ((pthread_t)mem)->dtv = dtv; + return mem; +} + +void *__tls_get_addr(size_t *p) +{ + pthread_t self = __pthread_self(); + if ((size_t)self->dtv[0] < p[0]) { + // FIXME: obtain new DTV and TLS from the DSO + a_crash(); + } + return (char *)self->dtv[p[0]] + p[1]; +} + void *__dynlink(int argc, char **argv) { - size_t *auxv, aux[AUX_CNT] = {0}; + size_t aux[AUX_CNT] = {0}; size_t i; Phdr *phdr; Ehdr *ehdr; @@ -538,6 +670,7 @@ void *__dynlink(int argc, char **argv) struct dso *const lib = builtin_dsos+1; struct dso *const vdso = builtin_dsos+2; char *env_preload=0; + size_t vdso_base; /* Find aux vector just past environ[] */ for (i=argc+1; argv[i]; i++) @@ -571,6 +704,8 @@ void *__dynlink(int argc, char **argv) lib->name = lib->shortname = "libc.so"; lib->global = 1; ehdr = (void *)lib->base; + find_map_range((void *)(aux[AT_BASE]+ehdr->e_phoff), + ehdr->e_phnum, ehdr->e_phentsize, lib); lib->dynv = (void *)(lib->base + find_dyn( (void *)(aux[AT_BASE]+ehdr->e_phoff), ehdr->e_phnum, ehdr->e_phentsize)); @@ -578,6 +713,7 @@ void *__dynlink(int argc, char **argv) if (aux[AT_PHDR]) { size_t interp_off = 0; + size_t tls_image = 0; /* Find load address of the main program, via AT_PHDR vs PT_PHDR. */ phdr = (void *)aux[AT_PHDR]; for (i=aux[AT_PHNUM]; i; i--, phdr=(void *)((char *)phdr + aux[AT_PHENT])) { @@ -585,11 +721,20 @@ void *__dynlink(int argc, char **argv) app->base = (void *)(aux[AT_PHDR] - phdr->p_vaddr); else if (phdr->p_type == PT_INTERP) interp_off = (size_t)phdr->p_vaddr; + else if (phdr->p_type == PT_TLS) { + tls_image = phdr->p_vaddr; + app->tls_len = phdr->p_filesz; + app->tls_size = phdr->p_memsz; + app->tls_align = phdr->p_align; + } } + if (app->tls_size) app->tls_image = (char *)app->base + tls_image; if (interp_off) lib->name = (char *)app->base + interp_off; app->name = argv[0]; app->dynv = (void *)(app->base + find_dyn( (void *)aux[AT_PHDR], aux[AT_PHNUM], aux[AT_PHENT])); + find_map_range((void *)aux[AT_PHDR], + aux[AT_PHNUM], aux[AT_PHENT], app); } else { int fd; char *ldname = argv[0]; @@ -609,7 +754,7 @@ void *__dynlink(int argc, char **argv) _exit(1); } runtime = 1; - ehdr = (void *)map_library(fd, &app->map_len, &app->base, &dyno); + ehdr = (void *)map_library(fd, app); if (!ehdr) { dprintf(2, "%s: %s: Not a valid dynamic program\n", ldname, argv[0]); _exit(1); @@ -618,17 +763,19 @@ void *__dynlink(int argc, char **argv) close(fd); lib->name = ldname; app->name = argv[0]; - app->dynv = (void *)(app->base + dyno); aux[AT_ENTRY] = ehdr->e_entry; } + if (app->tls_size) { + app->tls_id = ++tls_cnt; + tls_size += app->tls_size+app->tls_align + 8*sizeof(size_t)-1 + & -4*sizeof(size_t); + } app->global = 1; app->constructed = 1; decode_dyn(app); /* Attach to vdso, if provided by the kernel */ - for (i=0; auxv[i]; i+=2) { - size_t vdso_base = auxv[i+1]; - if (auxv[i] != AT_SYSINFO_EHDR) continue; + if (search_vec(auxv, &vdso_base, AT_SYSINFO_EHDR)) { ehdr = (void *)vdso_base; phdr = (void *)(vdso_base + ehdr->e_phoff); for (i=ehdr->e_phnum; i; i--, phdr=(void *)((char *)phdr + ehdr->e_phentsize)) { @@ -642,7 +789,6 @@ void *__dynlink(int argc, char **argv) decode_dyn(vdso); vdso->prev = lib; lib->next = vdso; - break; } /* Initial dso chain consists only of the app. We temporarily @@ -673,6 +819,7 @@ void *__dynlink(int argc, char **argv) reloc_all(app->next); reloc_all(app); + if (ldso_fail) _exit(127); if (ldd_mode) _exit(0); /* Switch to runtime mode: any further failures in the dynamic @@ -681,9 +828,11 @@ void *__dynlink(int argc, char **argv) * all memory used by the dynamic linker. */ runtime = 1; +#ifndef DYNAMIC_IS_RO for (i=0; app->dynv[i]; i+=2) if (app->dynv[i]==DT_DEBUG) app->dynv[i+1] = (size_t)&debug; +#endif debug.ver = 1; debug.bp = _dl_debug_state; debug.head = head; @@ -691,16 +840,23 @@ void *__dynlink(int argc, char **argv) debug.state = 0; _dl_debug_state(); + tls_size += sizeof(struct pthread) + 4*sizeof(size_t); + __libc.tls_size = tls_size; + __libc.tls_cnt = tls_cnt; + if (tls_cnt) { + void *mem = mmap(0, __libc.tls_size, PROT_READ|PROT_WRITE, + MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); + if (mem==MAP_FAILED || + !__install_initial_tls(__copy_tls(mem, tls_cnt))) { + dprintf(2, "%s: Error getting %zu bytes thread-local storage: %m\n", + argv[0], tls_size); + _exit(127); + } + } if (ssp_used) __init_ssp(auxv); do_init_fini(tail); - if (!rtld_used) { - free_all(head); - free(sys_path); - reclaim((void *)builtin_dsos, 0, sizeof builtin_dsos); - } - errno = 0; return (void *)aux[AT_ENTRY]; } @@ -761,6 +917,8 @@ void *dlopen(const char *file, int mode) p->global = 1; } + if (ssp_used) __init_ssp(auxv); + _dl_debug_state(); do_init_fini(tail); @@ -773,7 +931,7 @@ end: static void *do_dlsym(struct dso *p, const char *s, void *ra) { size_t i; - uint32_t h; + uint32_t h = 0, gh = 0; Sym *sym; if (p == RTLD_NEXT) { for (p=head; p && (unsigned char *)ra-p->map>p->map_len; p=p->next); @@ -787,13 +945,23 @@ static void *do_dlsym(struct dso *p, const char *s, void *ra) if (!res) goto failed; return res; } - h = hash(s); - sym = lookup(s, h, p->syms, p->hashtab, p->strings); + if (p->ghashtab) { + gh = gnu_hash(s); + sym = gnu_lookup(s, gh, p); + } else { + h = sysv_hash(s); + sym = sysv_lookup(s, h, p); + } if (sym && sym->st_value && (1<<(sym->st_info&0xf) & OK_TYPES)) return p->base + sym->st_value; if (p->deps) for (i=0; p->deps[i]; i++) { - sym = lookup(s, h, p->deps[i]->syms, - p->deps[i]->hashtab, p->deps[i]->strings); + if (p->deps[i]->ghashtab) { + if (!gh) gh = gnu_hash(s); + sym = gnu_lookup(s, gh, p->deps[i]); + } else { + if (!h) h = sysv_hash(s); + sym = sysv_lookup(s, h, p->deps[i]); + } if (sym && sym->st_value && (1<<(sym->st_info&0xf) & OK_TYPES)) return p->deps[i]->base + sym->st_value; } @@ -803,7 +971,68 @@ failed: return 0; } -void *__dlsym(void *p, const char *s, void *ra) +int __dladdr(void *addr, Dl_info *info) +{ + struct dso *p; + Sym *sym; + uint32_t nsym; + char *strings; + size_t i; + void *best = 0; + char *bestname; + + pthread_rwlock_rdlock(&lock); + for (p=head; p && (unsigned char *)addr-p->map>p->map_len; p=p->next); + pthread_rwlock_unlock(&lock); + + if (!p) return 0; + + sym = p->syms; + strings = p->strings; + if (p->hashtab) { + nsym = p->hashtab[1]; + } else { + uint32_t *buckets; + uint32_t *hashval; + buckets = p->ghashtab + 4 + (p->ghashtab[2]*sizeof(size_t)/4); + sym += p->ghashtab[1]; + for (i = 0; i < p->ghashtab[0]; i++) { + if (buckets[i] > nsym) + nsym = buckets[i]; + } + if (nsym) { + nsym -= p->ghashtab[1]; + hashval = buckets + p->ghashtab[0] + nsym; + do nsym++; + while (!(*hashval++ & 1)); + } + } + + for (; nsym; nsym--, sym++) { + if (sym->st_shndx && sym->st_value + && (1<<(sym->st_info&0xf) & OK_TYPES) + && (1<<(sym->st_info>>4) & OK_BINDS)) { + void *symaddr = p->base + sym->st_value; + if (symaddr > addr || symaddr < best) + continue; + best = symaddr; + bestname = strings + sym->st_name; + if (addr == symaddr) + break; + } + } + + if (!best) return 0; + + info->dli_fname = p->name; + info->dli_fbase = p->base; + info->dli_sname = bestname; + info->dli_saddr = best; + + return 1; +} + +void *__dlsym(void *restrict p, const char *restrict s, void *restrict ra) { void *res; pthread_rwlock_rdlock(&lock); @@ -816,7 +1045,11 @@ void *dlopen(const char *file, int mode) { return 0; } -void *__dlsym(void *p, const char *s, void *ra) +void *__dlsym(void *restrict p, const char *restrict s, void *restrict ra) +{ + return 0; +} +int __dladdr (void *addr, Dl_info *info) { return 0; }