X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=ldso%2Fdynlink.c;h=7200c8178ec114a8efd2217f885f699cfbc3acc5;hb=4de46b5ab51fd74552fa58a6721c78e10c9de975;hp=35a90aef849c1c3f139c5f9a8639dbc123b25461;hpb=f3055e0163aad1cebb0867078643b36643c5d95f;p=musl diff --git a/ldso/dynlink.c b/ldso/dynlink.c index 35a90aef..7200c817 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,12 +96,7 @@ 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; @@ -133,11 +129,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; + +hidden void (*const __init_array_start)(void)=0, (*const __fini_array_start)(void)=0; -__attribute__((__visibility__("hidden"))) -extern void (*const __init_array_end)(void), (*const __fini_array_end)(void); +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 +154,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 +299,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 +375,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 +436,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 +493,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 +509,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 +703,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); @@ -725,7 +732,6 @@ done_mapping: dso->base = base; dso->dynv = laddr(dso, dyn); if (dso->tls.size) dso->tls.image = laddr(dso, tls_image); - if (!runtime) reclaim_gaps(dso); free(allocated_buf); return map; noexec: @@ -808,7 +814,19 @@ static int fixup_rpath(struct dso *p, char *buf, size_t buf_size) origin = p->name; } t = strrchr(origin, '/'); - l = t ? t-origin : 0; + if (t) { + l = t-origin; + } else { + /* Normally p->name will always be an absolute or relative + * pathname containing at least one '/' character, but in the + * case where ldso was invoked as a command to execute a + * program in the working directory, app.name may not. Fix. */ + origin = "."; + l = 1; + } + /* Disallow non-absolute origins for suid/sgid/AT_SECURE. */ + if (libc.secure && *origin != '/') + return 0; p->rpath = malloc(strlen(p->rpath_orig) + n*l + 1); if (!p->rpath) return -1; @@ -1044,6 +1062,10 @@ static struct dso *load_library(const char *name, struct dso *needed_by) unmap_library(&temp_dso); return load_library("libc.so", needed_by); } + /* Past this point, if we haven't reached runtime yet, ldso has + * committed either to use the mapped library or to abort execution. + * Unmapping is not possible, so we can safely reclaim gaps. */ + if (!runtime) reclaim_gaps(&temp_dso); /* Allocate storage for the new DSO. When there is TLS, this * storage must include a reservation for all pre-existing @@ -1299,8 +1321,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(); @@ -1368,8 +1389,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]; @@ -1536,7 +1556,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); } @@ -1545,13 +1565,11 @@ _Noreturn void __dls3(size_t *sp) dprintf(2, "%s: cannot load %s: %s\n", ldname, argv[0], strerror(errno)); _exit(1); } - runtime = 1; Ehdr *ehdr = (void *)map_library(fd, &app); if (!ehdr) { dprintf(2, "%s: %s: Not a valid dynamic program\n", ldname, argv[0]); _exit(1); } - runtime = 0; close(fd); ldso.name = ldname; app.name = argv[0]; @@ -1570,8 +1588,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 @@ -1670,6 +1689,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. */ @@ -1822,8 +1847,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; @@ -1848,15 +1872,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; @@ -1913,16 +1944,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; @@ -1932,11 +1965,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; } } @@ -1944,31 +1978,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->base; + 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); @@ -2003,9 +2046,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;