X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=ldso%2Fdynlink.c;h=e4829c3ae4bc17ecba4b11f3f89b7d995bf4c138;hb=444a1b21a1bd12b76f93155ea7db3f155710b37b;hp=d20dbd87bc14ed635b8783374b937d38e47a8c61;hpb=0c53178ec09478ca5f6ca6b5ad09d50a10c8f19d;p=musl diff --git a/ldso/dynlink.c b/ldso/dynlink.c index d20dbd87..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; @@ -129,14 +123,15 @@ 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; -__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); @@ -157,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 @@ -286,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; @@ -365,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; @@ -418,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( @@ -475,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) @@ -499,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; @@ -694,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); @@ -724,7 +730,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: @@ -807,7 +812,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; @@ -1043,6 +1060,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 @@ -1125,6 +1146,7 @@ static void load_deps(struct dso *p) } } } + if (!*deps) *deps = (struct dso **)&nodeps_dummy; } static void load_preload(char *s) @@ -1297,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(); @@ -1366,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]; @@ -1435,6 +1455,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); @@ -1519,6 +1540,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; } @@ -1529,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); } @@ -1538,13 +1563,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]; @@ -1563,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 @@ -1663,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. */ @@ -1675,6 +1705,8 @@ _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); @@ -1742,7 +1774,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); } @@ -1768,19 +1801,24 @@ void *dlopen(const char *file, int mode) } /* First load handling */ - if (!p->deps) { + int first_load = !p->deps; + if (first_load) { load_deps(p); if (!p->relocated && (mode & RTLD_LAZY)) { prepare_lazy(p); - if (p->deps) for (i=0; p->deps[i]; i++) + 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); } @@ -1807,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; @@ -1833,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; @@ -1878,7 +1922,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); @@ -1898,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; @@ -1917,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; } } @@ -1929,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->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); @@ -1988,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;