X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=ldso%2Fdynlink.c;h=e4829c3ae4bc17ecba4b11f3f89b7d995bf4c138;hb=c04c6993dc87d596e47f2d8a66ef9252890b7468;hp=9bf6924b8259716f6b312ec4fe96ba0c406fc61b;hpb=376b3c54bba7d235e5a8e2839bb333a826b19636;p=musl diff --git a/ldso/dynlink.c b/ldso/dynlink.c index 9bf6924b..e4829c3a 100644 --- a/ldso/dynlink.c +++ b/ldso/dynlink.c @@ -20,6 +20,7 @@ #include "pthread_impl.h" #include "libc.h" #include "dynlink.h" +#include "malloc_impl.h" static void error(const char *, ...); @@ -95,13 +96,6 @@ struct symdef { struct dso *dso; }; -int __init_tp(void *); -void __init_libc(char **, char *); -void *__copy_tls(unsigned char *); - -__attribute__((__visibility__("hidden"))) -const char *__libc_get_version(void); - static struct builtin_tls { char c; struct pthread pt; @@ -133,11 +127,11 @@ static struct dso *const nodeps_dummy; struct debug *_dl_debug_addr = &debug; -__attribute__((__visibility__("hidden"))) -void (*const __init_array_start)(void)=0, (*const __fini_array_start)(void)=0; +extern hidden int __malloc_replaced; -__attribute__((__visibility__("hidden"))) -extern void (*const __init_array_end)(void), (*const __fini_array_end)(void); +hidden void (*const __init_array_start)(void)=0, (*const __fini_array_start)(void)=0; + +extern hidden void (*const __init_array_end)(void), (*const __fini_array_end)(void); weak_alias(__init_array_start, __init_array_end); weak_alias(__fini_array_start, __fini_array_end); @@ -158,10 +152,26 @@ static void *laddr(const struct dso *p, size_t v) for (j=0; v-p->loadmap->segs[j].p_vaddr >= p->loadmap->segs[j].p_memsz; j++); return (void *)(v - p->loadmap->segs[j].p_vaddr + p->loadmap->segs[j].addr); } +static void *laddr_pg(const struct dso *p, size_t v) +{ + size_t j=0; + size_t pgsz = PAGE_SIZE; + if (!p->loadmap) return p->base + v; + for (j=0; ; j++) { + size_t a = p->loadmap->segs[j].p_vaddr; + size_t b = a + p->loadmap->segs[j].p_memsz; + a &= -pgsz; + b += pgsz-1; + b &= -pgsz; + if (v-aloadmap->segs[j].p_vaddr + p->loadmap->segs[j].addr); +} #define fpaddr(p, v) ((void (*)())&(struct funcdesc){ \ laddr(p, v), (p)->got }) #else #define laddr(p, v) (void *)((p)->base + (v)) +#define laddr_pg(p, v) laddr(p, v) #define fpaddr(p, v) ((void (*)())laddr(p, v)) #endif @@ -287,9 +297,6 @@ static struct symdef find_sym(struct dso *dso, const char *s, int need_def) return def; } -__attribute__((__visibility__("hidden"))) -ptrdiff_t __tlsdesc_static(), __tlsdesc_dynamic(); - static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stride) { unsigned char *base = dso->base; @@ -366,6 +373,14 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri sym_val = def.sym ? (size_t)laddr(def.dso, def.sym->st_value) : 0; tls_val = def.sym ? def.sym->st_value : 0; + if ((type == REL_TPOFF || type == REL_TPOFF_NEG) + && runtime && def.dso->tls_id > static_tls_cnt) { + error("Error relocating %s: %s: initial-exec TLS " + "resolves to dynamic definition in %s", + dso->name, name, def.dso->name); + longjmp(*rtld_fail, 1); + } + switch(type) { case REL_NONE: break; @@ -419,7 +434,7 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri #endif case REL_TLSDESC: if (stride<3) addend = reloc_addr[1]; - if (runtime && def.dso->tls_id >= static_tls_cnt) { + if (runtime && def.dso->tls_id > static_tls_cnt) { struct td_index *new = malloc(sizeof *new); if (!new) { error( @@ -476,23 +491,15 @@ static void redo_lazy_relocs() /* 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 - * and "donate" them to the heap by setting up minimal malloc - * structures and then freeing them. */ + * and "donate" them to the heap. */ static void reclaim(struct dso *dso, size_t start, size_t end) { - size_t *a, *z; if (start >= dso->relro_start && start < dso->relro_end) start = dso->relro_end; if (end >= dso->relro_start && end < dso->relro_end) end = dso->relro_start; - start = start + 6*sizeof(size_t)-1 & -4*sizeof(size_t); - end = (end & -4*sizeof(size_t)) - 2*sizeof(size_t); - if (start>end || end-start < 4*sizeof(size_t)) return; - a = laddr(dso, start); - z = laddr(dso, end); - a[-2] = 1; - a[-1] = z[0] = end-start + 2*sizeof(size_t) | 1; - z[1] = 1; - free(a); + if (start >= end) return; + char *base = laddr_pg(dso, start); + __malloc_donate(base, base+(end-start)); } static void reclaim_gaps(struct dso *dso) @@ -500,7 +507,6 @@ static void reclaim_gaps(struct dso *dso) Phdr *ph = dso->phdr; size_t phcnt = dso->phnum; - if (DL_FDPIC) return; // FIXME for (; phcnt--; ph=(void *)((char *)ph+dso->phentsize)) { if (ph->p_type!=PT_LOAD) continue; if ((ph->p_flags&(PF_R|PF_W))!=(PF_R|PF_W)) continue; @@ -695,18 +701,17 @@ static void *map_library(int fd, struct dso *dso) dso->phnum = eh->e_phnum; dso->phentsize = eh->e_phentsize; } - /* Reuse the existing mapping for the lowest-address LOAD */ - if ((ph->p_vaddr & -PAGE_SIZE) == addr_min && !DL_NOMMU_SUPPORT) - continue; this_min = ph->p_vaddr & -PAGE_SIZE; this_max = ph->p_vaddr+ph->p_memsz+PAGE_SIZE-1 & -PAGE_SIZE; off_start = ph->p_offset & -PAGE_SIZE; prot = (((ph->p_flags&PF_R) ? PROT_READ : 0) | ((ph->p_flags&PF_W) ? PROT_WRITE: 0) | ((ph->p_flags&PF_X) ? PROT_EXEC : 0)); - if (mmap_fixed(base+this_min, this_max-this_min, prot, MAP_PRIVATE|MAP_FIXED, fd, off_start) == MAP_FAILED) - goto error; - if (ph->p_memsz > ph->p_filesz) { + /* Reuse the existing mapping for the lowest-address LOAD */ + if ((ph->p_vaddr & -PAGE_SIZE) != addr_min || DL_NOMMU_SUPPORT) + if (mmap_fixed(base+this_min, this_max-this_min, prot, MAP_PRIVATE|MAP_FIXED, fd, off_start) == MAP_FAILED) + goto error; + if (ph->p_memsz > ph->p_filesz && (ph->p_flags&PF_W)) { size_t brk = (size_t)base+ph->p_vaddr+ph->p_filesz; size_t pgbrk = brk+PAGE_SIZE-1 & -PAGE_SIZE; memset((void *)brk, 0, pgbrk-brk & PAGE_SIZE-1); @@ -1314,8 +1319,7 @@ void __init_tls(size_t *auxv) { } -__attribute__((__visibility__("hidden"))) -void *__tls_get_new(tls_mod_off_t *v) +hidden void *__tls_get_new(tls_mod_off_t *v) { pthread_t self = __pthread_self(); @@ -1383,8 +1387,7 @@ static void update_tls_size() * linker itself, but some of the relocations performed may need to be * replaced later due to copy relocations in the main program. */ -__attribute__((__visibility__("hidden"))) -void __dls2(unsigned char *base, size_t *sp) +hidden void __dls2(unsigned char *base, size_t *sp) { if (DL_FDPIC) { void *p1 = (void *)sp[-2]; @@ -1551,7 +1554,7 @@ _Noreturn void __dls3(size_t *sp) "Version %s\n" "Dynamic Program Loader\n" "Usage: %s [options] [--] pathname%s\n", - __libc_get_version(), ldname, + __libc_version, ldname, ldd_mode ? "" : " [args]"); _exit(1); } @@ -1583,8 +1586,9 @@ _Noreturn void __dls3(size_t *sp) libc.tls_head = tls_tail = &app.tls; app.tls_id = tls_cnt = 1; #ifdef TLS_ABOVE_TP - app.tls.offset = 0; - tls_offset = app.tls.size + app.tls.offset = GAP_ABOVE_TP; + app.tls.offset += -GAP_ABOVE_TP & (app.tls.align-1); + tls_offset = app.tls.offset + app.tls.size + ( -((uintptr_t)app.tls.image + app.tls.size) & (app.tls.align-1) ); #else @@ -1683,6 +1687,12 @@ _Noreturn void __dls3(size_t *sp) if (ldso_fail) _exit(127); if (ldd_mode) _exit(0); + /* Determine if malloc was interposed by a replacement implementation + * so that calloc and the memalign family can harden against the + * possibility of incomplete replacement. */ + if (find_sym(head, "malloc", 1).dso != &ldso) + __malloc_replaced = 1; + /* Switch to runtime mode: any further failures in the dynamic * linker are a reportable failure rather than a fatal startup * error. */ @@ -1835,8 +1845,7 @@ end: return p; } -__attribute__((__visibility__("hidden"))) -int __dl_invalid_handle(void *h) +hidden int __dl_invalid_handle(void *h) { struct dso *p; for (p=head; p; p=p->next) if (h==p) return 0; @@ -1861,15 +1870,22 @@ static void *addr2dso(size_t a) return p; } } else { + Phdr *ph = p->phdr; + size_t phcnt = p->phnum; + size_t entsz = p->phentsize; + size_t base = (size_t)p->base; + for (; phcnt--; ph=(void *)((char *)ph+entsz)) { + if (ph->p_type != PT_LOAD) continue; + if (a-base-ph->p_vaddr < ph->p_memsz) + return p; + } if (a-(size_t)p->map < p->map_len) - return p; + return 0; } } return 0; } -void *__tls_get_addr(tls_mod_off_t *); - static void *do_dlsym(struct dso *p, const char *s, void *ra) { size_t i; @@ -1926,16 +1942,18 @@ failed: return 0; } -int dladdr(const void *addr, Dl_info *info) +int dladdr(const void *addr_arg, Dl_info *info) { + size_t addr = (size_t)addr_arg; struct dso *p; Sym *sym, *bestsym; uint32_t nsym; char *strings; - void *best = 0; + size_t best = 0; + size_t besterr = -1; pthread_rwlock_rdlock(&lock); - p = addr2dso((size_t)addr); + p = addr2dso(addr); pthread_rwlock_unlock(&lock); if (!p) return 0; @@ -1945,11 +1963,12 @@ int dladdr(const void *addr, Dl_info *info) nsym = count_syms(p); if (DL_FDPIC) { - size_t idx = ((size_t)addr-(size_t)p->funcdescs) + size_t idx = (addr-(size_t)p->funcdescs) / sizeof(*p->funcdescs); if (idx < nsym && (sym[idx].st_info&0xf) == STT_FUNC) { - best = p->funcdescs + idx; + best = (size_t)(p->funcdescs + idx); bestsym = sym + idx; + besterr = 0; } } @@ -1957,31 +1976,40 @@ int dladdr(const void *addr, Dl_info *info) if (sym->st_value && (1<<(sym->st_info&0xf) & OK_TYPES) && (1<<(sym->st_info>>4) & OK_BINDS)) { - void *symaddr = laddr(p, sym->st_value); - if (symaddr > addr || symaddr < best) + size_t symaddr = (size_t)laddr(p, sym->st_value); + if (symaddr > addr || symaddr <= best) continue; best = symaddr; bestsym = sym; + besterr = addr - symaddr; if (addr == symaddr) break; } } - if (!best) return 0; - - if (DL_FDPIC && (bestsym->st_info&0xf) == STT_FUNC) - best = p->funcdescs + (bestsym - p->syms); + if (bestsym && besterr > bestsym->st_size-1) { + best = 0; + bestsym = 0; + } info->dli_fname = p->name; info->dli_fbase = p->map; + + if (!best) { + info->dli_sname = 0; + info->dli_saddr = 0; + return 1; + } + + if (DL_FDPIC && (bestsym->st_info&0xf) == STT_FUNC) + best = (size_t)(p->funcdescs + (bestsym - p->syms)); info->dli_sname = strings + bestsym->st_name; - info->dli_saddr = best; + info->dli_saddr = (void *)best; return 1; } -__attribute__((__visibility__("hidden"))) -void *__dlsym(void *restrict p, const char *restrict s, void *restrict ra) +hidden void *__dlsym(void *restrict p, const char *restrict s, void *restrict ra) { void *res; pthread_rwlock_rdlock(&lock); @@ -2016,9 +2044,6 @@ int dl_iterate_phdr(int(*callback)(struct dl_phdr_info *info, size_t size, void return ret; } -__attribute__((__visibility__("hidden"))) -void __dl_vseterr(const char *, va_list); - static void error(const char *fmt, ...) { va_list ap;