X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=src%2Fldso%2Fdynlink.c;h=b77c6f6b9c8c1dcce7acea8d5bee7128f501ef6d;hb=ce337daa00e42d4f2d9a4d9ae0ed51b20249d924;hp=b0d5ff463a72118f6e3a5fd40d4f525b5c4ab818;hpb=204a69d2d917f413eacb086020b63d6dc6da0672;p=musl diff --git a/src/ldso/dynlink.c b/src/ldso/dynlink.c index b0d5ff46..b77c6f6b 100644 --- a/src/ldso/dynlink.c +++ b/src/ldso/dynlink.c @@ -19,26 +19,12 @@ #include #include "pthread_impl.h" #include "libc.h" +#include "dynlink.h" -static int errflag; -static char errbuf[128]; +static void error(const char *, ...); #ifdef SHARED -#if ULONG_MAX == 0xffffffff -typedef Elf32_Ehdr Ehdr; -typedef Elf32_Phdr Phdr; -typedef Elf32_Sym Sym; -#define R_TYPE(x) ((x)&255) -#define R_SYM(x) ((x)>>8) -#else -typedef Elf64_Ehdr Ehdr; -typedef Elf64_Phdr Phdr; -typedef Elf64_Sym Sym; -#define R_TYPE(x) ((x)&0xffffffff) -#define R_SYM(x) ((x)>>32) -#endif - #define MAXP2(a,b) (-(-(a)&-(b))) #define ALIGN(x,y) ((x)+(y)-1 & -(y)) @@ -97,26 +83,6 @@ 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 *); void __init_libc(char **, char *); @@ -129,7 +95,11 @@ static struct builtin_tls { } builtin_tls[1]; #define MIN_TLS_ALIGN offsetof(struct builtin_tls, pt) -static struct dso *head, *tail, *ldso, *fini_head; +#define ADDEND_LIMIT 4096 +static size_t *saved_addends, *apply_addends_to; + +static struct dso ldso; +static struct dso *head, *tail, *fini_head; static char *env_path, *sys_path; static unsigned long long gencnt; static int runtime; @@ -145,14 +115,19 @@ static pthread_mutex_t init_fini_lock = { ._m_type = PTHREAD_MUTEX_RECURSIVE }; struct debug *_dl_debug_addr = &debug; -#define AUX_CNT 38 -#define DYN_CNT 34 +static int dl_strcmp(const char *l, const char *r) +{ + for (; *l==*r && *l; l++, r++); + return *(unsigned char *)l - *(unsigned char *)r; +} +#define strcmp(l,r) dl_strcmp(l,r) static void decode_vec(size_t *v, size_t *a, size_t cnt) { - memset(a, 0, cnt*sizeof(size_t)); - for (; v[0]; v+=2) if (v[0]name, astype); - continue; - } + if (skip_relative && IS_RELATIVE(rel[1])) continue; + type = R_TYPE(rel[1]); + if (type == REL_NONE) continue; sym_index = R_SYM(rel[1]); reloc_addr = (void *)(base + rel[0]); if (sym_index) { @@ -316,6 +282,7 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri || sym->st_info>>4 != STB_WEAK)) { error("Error relocating %s: %s: symbol not found", dso->name, name); + if (runtime) longjmp(*rtld_fail, 1); continue; } } else { @@ -324,14 +291,27 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri def.dso = dso; } - addend = stride>2 ? rel[2] - : (1< 2) { + addend = rel[2]; + } else if (type==REL_GOT || type==REL_PLT|| type==REL_COPY) { + addend = 0; + } else if (reuse_addends) { + /* Save original addend in stage 2 where the dso + * chain consists of just ldso; otherwise read back + * saved addend since the inline one was clobbered. */ + if (head==&ldso) + saved_addends[save_slot] = *reloc_addr; + addend = saved_addends[save_slot++]; + } else { + addend = *reloc_addr; + } sym_val = def.sym ? (size_t)def.dso->base+def.sym->st_value : 0; tls_val = def.sym ? def.sym->st_value : 0; switch(type) { + case REL_NONE: + break; case REL_OFFSET: addend -= (size_t)reloc_addr; case REL_SYMBOLIC: @@ -375,9 +355,12 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri 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( + if (!new) { + error( "Error relocating %s: cannot allocate TLSDESC for %s", dso->name, sym ? name : "(local)" ); + longjmp(*rtld_fail, 1); + } new->next = dso->td_index; dso->td_index = new; new->args[0] = def.dso->tls_id; @@ -395,6 +378,11 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri #endif } break; + default: + error("Error relocating %s: unsupported relocation type %d", + dso->name, type); + if (runtime) longjmp(*rtld_fail, 1); + continue; } } } @@ -435,6 +423,28 @@ static void reclaim_gaps(struct dso *dso) } } +static void *mmap_fixed(void *p, size_t n, int prot, int flags, int fd, off_t off) +{ + char *q = mmap(p, n, prot, flags, fd, off); + if (q != MAP_FAILED || errno != EINVAL) return q; + /* Fallbacks for MAP_FIXED failure on NOMMU kernels. */ + if (flags & MAP_ANONYMOUS) { + memset(p, 0, n); + return p; + } + ssize_t r; + if (lseek(fd, off, SEEK_SET) < 0) return MAP_FAILED; + for (q=p; n; q+=r, off+=r, n-=r) { + r = read(fd, q, n); + if (r < 0 && errno != EINTR) return MAP_FAILED; + if (!r) { + memset(q, 0, n); + break; + } + } + return p; +} + static void *map_library(int fd, struct dso *dso) { Ehdr buf[(896+sizeof(Ehdr))/sizeof(Ehdr)]; @@ -536,19 +546,20 @@ static void *map_library(int fd, struct dso *dso) 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(base+this_min, this_max-this_min, prot, MAP_PRIVATE|MAP_FIXED, fd, off_start) == MAP_FAILED) + 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) { 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); - if (pgbrk-(size_t)base < this_max && mmap((void *)pgbrk, (size_t)base+this_max-pgbrk, prot, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) == MAP_FAILED) + if (pgbrk-(size_t)base < this_max && mmap_fixed((void *)pgbrk, (size_t)base+this_max-pgbrk, prot, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) == MAP_FAILED) goto error; } } for (i=0; ((size_t *)(base+dyn))[i]; i+=2) if (((size_t *)(base+dyn))[i]==DT_TEXTREL) { - if (mprotect(map, map_len, PROT_READ|PROT_WRITE|PROT_EXEC) < 0) + if (mprotect(map, map_len, PROT_READ|PROT_WRITE|PROT_EXEC) + && errno != ENOSYS) goto error; break; } @@ -576,9 +587,20 @@ static int path_open(const char *name, const char *s, char *buf, size_t buf_size s += strspn(s, ":\n"); l = strcspn(s, ":\n"); if (l-1 >= INT_MAX) return -1; - if (snprintf(buf, buf_size, "%.*s/%s", (int)l, s, name) >= buf_size) - continue; - if ((fd = open(buf, O_RDONLY|O_CLOEXEC))>=0) return fd; + if (snprintf(buf, buf_size, "%.*s/%s", (int)l, s, name) < buf_size) { + if ((fd = open(buf, O_RDONLY|O_CLOEXEC))>=0) return fd; + switch (errno) { + case ENOENT: + case ENOTDIR: + case EACCES: + case ENAMETOOLONG: + break; + default: + /* Any negative value but -1 will inhibit + * futher path search. */ + return -2; + } + } s += l; } } @@ -588,8 +610,7 @@ static int fixup_rpath(struct dso *p, char *buf, size_t buf_size) size_t n, l; const char *s, *t, *origin; char *d; - if (p->rpath) return 0; - if (!p->rpath_orig) return -1; + if (p->rpath || !p->rpath_orig) return 0; if (!strchr(p->rpath_orig, '$')) { p->rpath = p->rpath_orig; return 0; @@ -598,11 +619,11 @@ static int fixup_rpath(struct dso *p, char *buf, size_t buf_size) s = p->rpath_orig; while ((t=strchr(s, '$'))) { if (strncmp(t, "$ORIGIN", 7) && strncmp(t, "${ORIGIN}", 9)) - return -1; + return 0; s = t+1; n++; } - if (n > SSIZE_MAX/PATH_MAX) return -1; + if (n > SSIZE_MAX/PATH_MAX) return 0; if (p->kernel_mapped) { /* $ORIGIN searches cannot be performed for the main program @@ -612,10 +633,18 @@ static int fixup_rpath(struct dso *p, char *buf, size_t buf_size) * since the library's pathname came from a trusted source * (either system paths or a call to dlopen). */ if (libc.secure) - return -1; + return 0; l = readlink("/proc/self/exe", buf, buf_size); - if (l >= buf_size) + if (l == -1) switch (errno) { + case ENOENT: + case ENOTDIR: + case EACCES: + break; + default: return -1; + } + if (l >= buf_size) + return 0; buf[l] = 0; origin = buf; } else { @@ -643,7 +672,7 @@ static int fixup_rpath(struct dso *p, char *buf, size_t buf_size) static void decode_dyn(struct dso *p) { - size_t dyn[DYN_CNT] = {0}; + size_t dyn[DYN_CNT]; decode_vec(p->dynv, dyn, DYN_CNT); p->syms = (void *)(p->base + dyn[DT_SYMTAB]); p->strings = (void *)(p->base + dyn[DT_STRTAB]); @@ -693,22 +722,22 @@ static struct dso *load_library(const char *name, struct dso *needed_by) if (!(reported & mask)) { reported |= mask; dprintf(1, "\t%s => %s (%p)\n", - name, ldso->name, - ldso->base); + name, ldso.name, + ldso.base); } } is_self = 1; } } } - if (!strcmp(name, ldso->name)) is_self = 1; + if (!strcmp(name, ldso.name)) is_self = 1; if (is_self) { - if (!ldso->prev) { - tail->next = ldso; - ldso->prev = tail; - tail = ldso->next ? ldso->next : ldso; + if (!ldso.prev) { + tail->next = &ldso; + ldso.prev = tail; + tail = ldso.next ? ldso.next : &ldso; } - return ldso; + return &ldso; } if (strchr(name, '/')) { pathname = name; @@ -724,20 +753,23 @@ static struct dso *load_library(const char *name, struct dso *needed_by) if (strlen(name) > NAME_MAX) return 0; fd = -1; if (env_path) fd = path_open(name, env_path, buf, sizeof buf); - for (p=needed_by; fd < 0 && p; p=p->needed_by) - if (!fixup_rpath(p, buf, sizeof buf)) + for (p=needed_by; fd == -1 && p; p=p->needed_by) { + if (fixup_rpath(p, buf, sizeof buf) < 0) + fd = -2; /* Inhibit further search. */ + if (p->rpath) fd = path_open(name, p->rpath, buf, sizeof buf); - if (fd < 0) { + } + if (fd == -1) { if (!sys_path) { char *prefix = 0; size_t prefix_len; - if (ldso->name[0]=='/') { + if (ldso.name[0]=='/') { char *s, *t, *z; - for (s=t=z=ldso->name; *s; s++) + for (s=t=z=ldso.name; *s; s++) if (*s=='/') z=t, t=s; - prefix_len = z-ldso->name; + prefix_len = z-ldso.name; if (prefix_len < PATH_MAX) - prefix = ldso->name; + prefix = ldso.name; } if (!prefix) { prefix = ""; @@ -814,12 +846,6 @@ static struct dso *load_library(const char *name, struct dso *needed_by) /* Add a shortname only if name arg was not an explicit pathname. */ if (pathname != name) p->shortname = strrchr(p->name, '/')+1; if (p->tls_image) { - if (runtime && !libc.has_thread_pointer) { - munmap(map, p->map_len); - free(p); - errno = ENOSYS; - return 0; - } p->tls_id = ++tls_cnt; tls_align = MAXP2(tls_align, p->tls_align); #ifdef TLS_ABOVE_TP @@ -857,6 +883,7 @@ static void load_deps(struct dso *p) if (!dep) { error("Error loading shared library %s: %m (needed by %s)", p->strings + p->dynv[i+1], p->name); + if (runtime) longjmp(*rtld_fail, 1); continue; } if (runtime) { @@ -889,24 +916,45 @@ static void make_global(struct dso *p) for (; p; p=p->next) p->global = 1; } +static void do_mips_relocs(struct dso *p, size_t *got) +{ + size_t i, j, rel[2]; + unsigned char *base = p->base; + i=0; search_vec(p->dynv, &i, DT_MIPS_LOCAL_GOTNO); + if (p==&ldso) { + got += i; + } else { + while (i--) *got++ += (size_t)base; + } + j=0; search_vec(p->dynv, &j, DT_MIPS_GOTSYM); + i=0; search_vec(p->dynv, &i, DT_MIPS_SYMTABNO); + Sym *sym = p->syms + j; + rel[0] = (unsigned char *)got - base; + for (i-=j; i; i--, sym++, rel[0]+=sizeof(size_t)) { + rel[1] = sym-p->syms << 8 | R_MIPS_JUMP_SLOT; + do_relocs(p, rel, sizeof rel, 2); + } +} + static void reloc_all(struct dso *p) { - size_t dyn[DYN_CNT] = {0}; + size_t dyn[DYN_CNT]; for (; p; p=p->next) { if (p->relocated) continue; decode_vec(p->dynv, dyn, DYN_CNT); -#ifdef NEED_ARCH_RELOCS - do_arch_relocs(p, head); -#endif + if (NEED_MIPS_GOT_RELOCS) + do_mips_relocs(p, (void *)(p->base+dyn[DT_PLTGOT])); 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); - if (p->relro_start != p->relro_end && - mprotect(p->base+p->relro_start, p->relro_end-p->relro_start, PROT_READ) < 0) { + if (head != &ldso && p->relro_start != p->relro_end && + mprotect(p->base+p->relro_start, p->relro_end-p->relro_start, PROT_READ) + && errno != ENOSYS) { error("Error relocating %s: RELRO protection failed: %m", p->name); + if (runtime) longjmp(*rtld_fail, 1); } p->relocated = 1; @@ -940,7 +988,7 @@ static void kernel_mapped_dso(struct dso *p) static void do_fini() { struct dso *p; - size_t dyn[DYN_CNT] = {0}; + size_t dyn[DYN_CNT]; for (p=fini_head; p; p=p->fini_next) { if (!p->constructed) continue; decode_vec(p->dynv, dyn, DYN_CNT); @@ -958,7 +1006,7 @@ static void do_fini() static void do_init_fini(struct dso *p) { - size_t dyn[DYN_CNT] = {0}; + size_t dyn[DYN_CNT]; int need_locking = libc.threads_minus_1; /* Allow recursive calls that arise when a library calls * dlopen from one of its constructors, but block any @@ -989,10 +1037,12 @@ static void do_init_fini(struct dso *p) if (need_locking) pthread_mutex_unlock(&init_fini_lock); } -void _dl_debug_state(void) +static void dl_debug_state(void) { } +weak_alias(dl_debug_state, _dl_debug_state); + void __reset_tls() { pthread_t self = __pthread_self(); @@ -1010,17 +1060,11 @@ void *__copy_tls(unsigned char *mem) { pthread_t td; struct dso *p; - - void **dtv = (void *)mem; - dtv[0] = (void *)tls_cnt; - if (!tls_cnt) { - td = (void *)(dtv+1); - td->dtv = td->dtv_copy = dtv; - return td; - } + void **dtv; #ifdef TLS_ABOVE_TP - mem += sizeof(void *) * (tls_cnt+1); + dtv = (void **)(mem + libc.tls_size) - (tls_cnt + 1); + mem += -((uintptr_t)mem + sizeof(struct pthread)) & (tls_align-1); td = (pthread_t)mem; mem += sizeof(struct pthread); @@ -1031,6 +1075,8 @@ void *__copy_tls(unsigned char *mem) memcpy(dtv[p->tls_id], p->tls_image, p->tls_len); } #else + dtv = (void **)mem; + mem += libc.tls_size - sizeof(struct pthread); mem -= (uintptr_t)mem & (tls_align-1); td = (pthread_t)mem; @@ -1041,10 +1087,12 @@ void *__copy_tls(unsigned char *mem) memcpy(dtv[p->tls_id], p->tls_image, p->tls_len); } #endif + dtv[0] = (void *)tls_cnt; td->dtv = td->dtv_copy = dtv; return td; } +__attribute__((__visibility__("hidden"))) void *__tls_get_new(size_t *v) { pthread_t self = __pthread_self(); @@ -1100,96 +1148,135 @@ static void update_tls_size() tls_align); } -void *__dynlink(int argc, char **argv) +/* Stage 1 of the dynamic linker is defined in dlstart.c. It calls the + * following stage 2 and stage 3 functions via primitive symbolic lookup + * since it does not have access to their addresses to begin with. */ + +/* Stage 2 of the dynamic linker is called after relative relocations + * have been processed. It can make function calls to static functions + * and access string literals and static data, but cannot use extern + * symbols. Its job is to perform symbolic relocations on the dynamic + * linker itself, but some of the relocations performed may need to be + * replaced later due to copy relocations in the main program. */ + +void __dls2(unsigned char *base, size_t *sp) { - size_t aux[AUX_CNT] = {0}; + Ehdr *ehdr = (void *)base; + ldso.base = base; + ldso.name = ldso.shortname = "libc.so"; + ldso.global = 1; + ldso.phnum = ehdr->e_phnum; + ldso.phdr = (void *)(base + ehdr->e_phoff); + ldso.phentsize = ehdr->e_phentsize; + kernel_mapped_dso(&ldso); + decode_dyn(&ldso); + + /* Prepare storage for to save clobbered REL addends so they + * can be reused in stage 3. There should be very few. If + * something goes wrong and there are a huge number, abort + * instead of risking stack overflow. */ + size_t dyn[DYN_CNT]; + decode_vec(ldso.dynv, dyn, DYN_CNT); + size_t *rel = (void *)(base+dyn[DT_REL]); + size_t rel_size = dyn[DT_RELSZ]; + size_t symbolic_rel_cnt = 0; + apply_addends_to = rel; + for (; rel_size; rel+=2, rel_size-=2*sizeof(size_t)) + if (!IS_RELATIVE(rel[1])) symbolic_rel_cnt++; + if (symbolic_rel_cnt >= ADDEND_LIMIT) a_crash(); + size_t addends[symbolic_rel_cnt+1]; + saved_addends = addends; + + head = &ldso; + reloc_all(&ldso); + + ldso.relocated = 0; + + /* Call dynamic linker stage-3, __dls3, looking it up + * symbolically as a barrier against moving the address + * load across the above relocation processing. */ + struct symdef dls3_def = find_sym(&ldso, "__dls3", 0); + ((stage3_func)(ldso.base+dls3_def.sym->st_value))(sp); +} + +/* Stage 3 of the dynamic linker is called with the dynamic linker/libc + * fully functional. Its job is to load (if not already loaded) and + * process dependencies and relocations for the main application and + * transfer control to its entry point. */ + +_Noreturn void __dls3(size_t *sp) +{ + static struct dso app, vdso; + size_t aux[AUX_CNT], *auxv; size_t i; - Phdr *phdr; - Ehdr *ehdr; - static struct dso builtin_dsos[3]; - struct dso *const app = builtin_dsos+0; - struct dso *const lib = builtin_dsos+1; - struct dso *const vdso = builtin_dsos+2; char *env_preload=0; size_t vdso_base; - size_t *auxv; + int argc = *sp; + char **argv = (void *)(sp+1); + char **argv_orig = argv; char **envp = argv+argc+1; - void *initial_tls; - - /* Find aux vector just past environ[] */ - for (i=argc+1; argv[i]; i++) - if (!memcmp(argv[i], "LD_LIBRARY_PATH=", 16)) - env_path = argv[i]+16; - else if (!memcmp(argv[i], "LD_PRELOAD=", 11)) - env_preload = argv[i]+11; - auxv = (void *)(argv+i+1); + /* Find aux vector just past environ[] and use it to initialize + * global data that may be needed before we can make syscalls. */ + __environ = envp; + for (i=argc+1; argv[i]; i++); + libc.auxv = auxv = (void *)(argv+i+1); decode_vec(auxv, aux, AUX_CNT); + __hwcap = aux[AT_HWCAP]; + libc.page_size = aux[AT_PAGESZ]; + libc.secure = ((aux[0]&0x7800)!=0x7800 || aux[AT_UID]!=aux[AT_EUID] + || aux[AT_GID]!=aux[AT_EGID] || aux[AT_SECURE]); + + /* Setup early thread pointer in builtin_tls for ldso/libc itself to + * use during dynamic linking. If possible it will also serve as the + * thread pointer at runtime. */ + libc.tls_size = sizeof builtin_tls; + if (__init_tp(__copy_tls((void *)builtin_tls)) < 0) { + a_crash(); + } /* Only trust user/env if kernel says we're not suid/sgid */ - if ((aux[0]&0x7800)!=0x7800 || aux[AT_UID]!=aux[AT_EUID] - || aux[AT_GID]!=aux[AT_EGID] || aux[AT_SECURE]) { - env_path = 0; - env_preload = 0; - 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 - * the start of the page containing the PHDRs; I don't know any - * better approach... */ - if (!aux[AT_BASE]) { - aux[AT_BASE] = aux[AT_PHDR] & -PAGE_SIZE; - aux[AT_PHDR] = aux[AT_PHENT] = aux[AT_PHNUM] = 0; + if (!libc.secure) { + env_path = getenv("LD_LIBRARY_PATH"); + env_preload = getenv("LD_PRELOAD"); } - /* The dynamic linker load address is passed by the kernel - * in the AUX vector, so this is easy. */ - lib->base = (void *)aux[AT_BASE]; - lib->name = lib->shortname = "libc.so"; - lib->global = 1; - ehdr = (void *)lib->base; - lib->phnum = ehdr->e_phnum; - lib->phdr = (void *)(aux[AT_BASE]+ehdr->e_phoff); - lib->phentsize = ehdr->e_phentsize; - kernel_mapped_dso(lib); - decode_dyn(lib); - - if (aux[AT_PHDR]) { + /* If the main program was already loaded by the kernel, + * AT_PHDR will point to some location other than the dynamic + * linker's program headers. */ + if (aux[AT_PHDR] != (size_t)ldso.phdr) { size_t interp_off = 0; size_t tls_image = 0; /* Find load address of the main program, via AT_PHDR vs PT_PHDR. */ - app->phdr = phdr = (void *)aux[AT_PHDR]; - app->phnum = aux[AT_PHNUM]; - app->phentsize = aux[AT_PHENT]; + Phdr *phdr = app.phdr = (void *)aux[AT_PHDR]; + app.phnum = aux[AT_PHNUM]; + app.phentsize = aux[AT_PHENT]; for (i=aux[AT_PHNUM]; i; i--, phdr=(void *)((char *)phdr + aux[AT_PHENT])) { if (phdr->p_type == PT_PHDR) - app->base = (void *)(aux[AT_PHDR] - phdr->p_vaddr); + 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; + 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; + if (app.tls_size) app.tls_image = (char *)app.base + tls_image; + if (interp_off) ldso.name = (char *)app.base + interp_off; if ((aux[0] & (1UL<name = (char *)aux[AT_EXECFN]; + app.name = (char *)aux[AT_EXECFN]; else - app->name = argv[0]; - kernel_mapped_dso(app); + app.name = argv[0]; + kernel_mapped_dso(&app); } else { int fd; char *ldname = argv[0]; size_t l = strlen(ldname); if (l >= 3 && !strcmp(ldname+l-3, "ldd")) ldd_mode = 1; - *argv++ = (void *)-1; + argv++; while (argv[0] && argv[0][0]=='-' && argv[0][1]=='-') { char *opt = argv[0]+2; *argv++ = (void *)-1; @@ -1208,8 +1295,8 @@ void *__dynlink(int argc, char **argv) } else { argv[0] = 0; } - argv[-1] = (void *)-1; } + argv[-1] = (void *)(argc - (argv-argv_orig)); if (!argv[0]) { dprintf(2, "musl libc\n" "Version %s\n" @@ -1225,111 +1312,109 @@ void *__dynlink(int argc, char **argv) _exit(1); } runtime = 1; - ehdr = (void *)map_library(fd, app); + 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); - lib->name = ldname; - app->name = argv[0]; - aux[AT_ENTRY] = (size_t)app->base + ehdr->e_entry; + ldso.name = ldname; + app.name = argv[0]; + aux[AT_ENTRY] = (size_t)app.base + ehdr->e_entry; /* Find the name that would have been used for the dynamic * linker had ldd not taken its place. */ if (ldd_mode) { - for (i=0; iphnum; i++) { - if (app->phdr[i].p_type == PT_INTERP) - lib->name = (void *)(app->base - + app->phdr[i].p_vaddr); + for (i=0; iname, lib->base); + dprintf(1, "\t%s (%p)\n", ldso.name, ldso.base); } } - if (app->tls_size) { - app->tls_id = tls_cnt = 1; + if (app.tls_size) { + app.tls_id = tls_cnt = 1; #ifdef TLS_ABOVE_TP - app->tls_offset = 0; - tls_offset = app->tls_size - + ( -((uintptr_t)app->tls_image + app->tls_size) - & (app->tls_align-1) ); + app.tls_offset = 0; + tls_offset = app.tls_size + + ( -((uintptr_t)app.tls_image + app.tls_size) + & (app.tls_align-1) ); #else - tls_offset = app->tls_offset = app->tls_size - + ( -((uintptr_t)app->tls_image + app->tls_size) - & (app->tls_align-1) ); + tls_offset = app.tls_offset = app.tls_size + + ( -((uintptr_t)app.tls_image + app.tls_size) + & (app.tls_align-1) ); #endif - tls_align = MAXP2(tls_align, app->tls_align); + tls_align = MAXP2(tls_align, app.tls_align); } - app->global = 1; - decode_dyn(app); + app.global = 1; + decode_dyn(&app); /* Attach to vdso, if provided by the kernel */ if (search_vec(auxv, &vdso_base, AT_SYSINFO_EHDR)) { - ehdr = (void *)vdso_base; - vdso->phdr = phdr = (void *)(vdso_base + ehdr->e_phoff); - vdso->phnum = ehdr->e_phnum; - vdso->phentsize = ehdr->e_phentsize; + Ehdr *ehdr = (void *)vdso_base; + Phdr *phdr = vdso.phdr = (void *)(vdso_base + ehdr->e_phoff); + vdso.phnum = ehdr->e_phnum; + vdso.phentsize = ehdr->e_phentsize; for (i=ehdr->e_phnum; i; i--, phdr=(void *)((char *)phdr + ehdr->e_phentsize)) { if (phdr->p_type == PT_DYNAMIC) - vdso->dynv = (void *)(vdso_base + phdr->p_offset); + vdso.dynv = (void *)(vdso_base + phdr->p_offset); if (phdr->p_type == PT_LOAD) - vdso->base = (void *)(vdso_base - phdr->p_vaddr + phdr->p_offset); + vdso.base = (void *)(vdso_base - phdr->p_vaddr + phdr->p_offset); } - vdso->name = ""; - vdso->shortname = "linux-gate.so.1"; - vdso->global = 1; - decode_dyn(vdso); - vdso->prev = lib; - lib->next = vdso; + vdso.name = ""; + vdso.shortname = "linux-gate.so.1"; + vdso.global = 1; + vdso.relocated = 1; + decode_dyn(&vdso); + vdso.prev = &ldso; + ldso.next = &vdso; } - /* Initial dso chain consists only of the app. We temporarily - * append the dynamic linker/libc so we can relocate it, then - * restore the initial chain in preparation for loading third - * party libraries (preload/needed). */ - head = tail = app; - ldso = lib; - app->next = lib; - reloc_all(lib); - app->next = 0; - - /* PAST THIS POINT, ALL LIBC INTERFACES ARE FULLY USABLE. */ + /* Initial dso chain consists only of the app. */ + head = tail = &app; /* Donate unused parts of app and library mapping to malloc */ - reclaim_gaps(app); - reclaim_gaps(lib); + reclaim_gaps(&app); + reclaim_gaps(&ldso); /* Load preload/needed libraries, add their symbols to the global - * namespace, and perform all remaining relocations. The main - * program must be relocated LAST since it may contain copy - * relocations which depend on libraries' relocations. */ + * namespace, and perform all remaining relocations. */ if (env_preload) load_preload(env_preload); - load_deps(app); - make_global(app); + load_deps(&app); + make_global(&app); #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; + for (i=0; app.dynv[i]; i+=2) + if (app.dynv[i]==DT_DEBUG) + app.dynv[i+1] = (size_t)&debug; #endif - reloc_all(app->next); - reloc_all(app); + /* The main program must be relocated LAST since it may contin + * copy relocations which depend on libraries' relocations. */ + reloc_all(app.next); + reloc_all(&app); update_tls_size(); - if (libc.tls_size > sizeof builtin_tls) { - initial_tls = calloc(libc.tls_size, 1); + if (libc.tls_size > sizeof builtin_tls || tls_align > MIN_TLS_ALIGN) { + void *initial_tls = calloc(libc.tls_size, 1); if (!initial_tls) { dprintf(2, "%s: Error getting %zu bytes thread-local storage: %m\n", argv[0], libc.tls_size); _exit(127); } + if (__init_tp(__copy_tls(initial_tls)) < 0) { + a_crash(); + } } else { - initial_tls = builtin_tls; - } - if (__init_tp(__copy_tls(initial_tls)) < 0 && tls_cnt) { - dprintf(2, "%s: Thread-local storage not supported by kernel.\n", argv[0]); - _exit(127); + size_t tmp_tls_size = libc.tls_size; + pthread_t self = __pthread_self(); + /* Temporarily set the tls size to the full size of + * builtin_tls so that __copy_tls will use the same layout + * as it did for before. Then check, just to be safe. */ + libc.tls_size = sizeof builtin_tls; + if (__copy_tls((void*)builtin_tls) != self) a_crash(); + libc.tls_size = tmp_tls_size; } static_tls_cnt = tls_cnt; @@ -1338,14 +1423,13 @@ void *__dynlink(int argc, char **argv) /* Switch to runtime mode: any further failures in the dynamic * linker are a reportable failure rather than a fatal startup - * error. If the dynamic loader (dlopen) will not be used, free - * all memory used by the dynamic linker. */ + * error. */ runtime = 1; debug.ver = 1; - debug.bp = _dl_debug_state; + debug.bp = dl_debug_state; debug.head = head; - debug.base = lib->base; + debug.base = ldso.base; debug.state = 0; _dl_debug_state(); @@ -1354,7 +1438,8 @@ void *__dynlink(int argc, char **argv) errno = 0; do_init_fini(tail); - return (void *)aux[AT_ENTRY]; + CRTJMP((void *)aux[AT_ENTRY], argv-1); + for(;;); } void *dlopen(const char *file, int mode) @@ -1392,6 +1477,8 @@ void *dlopen(const char *file, int mode) free(p->td_index); p->td_index = tmp; } + if (p->rpath != p->rpath_orig) + free(p->rpath); free(p->deps); free(p); } @@ -1401,16 +1488,14 @@ void *dlopen(const char *file, int mode) tail = orig_tail; tail->next = 0; p = 0; - errflag = 1; goto end; } else p = load_library(file, head); if (!p) { - snprintf(errbuf, sizeof errbuf, noload ? + error(noload ? "Library %s is not already loaded" : "Error loading shared library %s: %m", file); - errflag = 1; goto end; } @@ -1450,8 +1535,7 @@ static int invalid_dso_handle(void *h) { struct dso *p; for (p=head; p; p=p->next) if (h==p) return 0; - snprintf(errbuf, sizeof errbuf, "Invalid library handle %p", (void *)h); - errflag = 1; + error("Invalid library handle %p", (void *)h); return 1; } @@ -1476,7 +1560,7 @@ static void *do_dlsym(struct dso *p, const char *s, void *ra) return __tls_get_addr((size_t []){def.dso->tls_id, def.sym->st_value}); return def.dso->base + def.sym->st_value; } - if (p != RTLD_DEFAULT && p != RTLD_NEXT && invalid_dso_handle(p)) + if (invalid_dso_handle(p)) return 0; if (p->ghashtab) { gh = gnu_hash(s); @@ -1503,8 +1587,7 @@ static void *do_dlsym(struct dso *p, const char *s, void *ra) return p->deps[i]->base + sym->st_value; } failed: - errflag = 1; - snprintf(errbuf, sizeof errbuf, "Symbol not found: %s", s); + error("Symbol not found: %s", s); return 0; } @@ -1569,6 +1652,7 @@ int __dladdr(const void *addr, Dl_info *info) return 1; } +__attribute__((__visibility__("hidden"))) void *__dlsym(void *restrict p, const char *restrict s, void *restrict ra) { void *res; @@ -1606,20 +1690,17 @@ int dl_iterate_phdr(int(*callback)(struct dl_phdr_info *info, size_t size, void #else static int invalid_dso_handle(void *h) { - snprintf(errbuf, sizeof errbuf, "Invalid library handle %p", (void *)h); - errflag = 1; + error("Invalid library handle %p", (void *)h); return 1; } void *dlopen(const char *file, int mode) { - strcpy(errbuf, "Dynamic loading not supported"); - errflag = 1; + error("Dynamic loading not supported"); 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); + error("Symbol not found: %s", s); return 0; } int __dladdr (const void *addr, Dl_info *info) @@ -1632,8 +1713,7 @@ int __dlinfo(void *dso, int req, void *res) { if (invalid_dso_handle(dso)) return -1; if (req != RTLD_DI_LINKMAP) { - snprintf(errbuf, sizeof errbuf, "Unsupported request %d", req); - errflag = 1; + error("Unsupported request %d", req); return -1; } *(struct link_map **)res = dso; @@ -1642,12 +1722,54 @@ int __dlinfo(void *dso, int req, void *res) char *dlerror() { - if (!errflag) return 0; - errflag = 0; - return errbuf; + pthread_t self = __pthread_self(); + if (!self->dlerror_flag) return 0; + self->dlerror_flag = 0; + char *s = self->dlerror_buf; + if (s == (void *)-1) + return "Dynamic linker failed to allocate memory for error message"; + else + return s; } int dlclose(void *p) { return invalid_dso_handle(p); } + +void __dl_thread_cleanup(void) +{ + pthread_t self = __pthread_self(); + if (self->dlerror_buf != (void *)-1) + free(self->dlerror_buf); +} + +static void error(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); +#ifdef SHARED + if (!runtime) { + vdprintf(2, fmt, ap); + dprintf(2, "\n"); + ldso_fail = 1; + va_end(ap); + return; + } +#endif + pthread_t self = __pthread_self(); + if (self->dlerror_buf != (void *)-1) + free(self->dlerror_buf); + size_t len = vsnprintf(0, 0, fmt, ap); + va_end(ap); + char *buf = malloc(len+1); + if (buf) { + va_start(ap, fmt); + vsnprintf(buf, len+1, fmt, ap); + va_end(ap); + } else { + buf = (void *)-1; + } + self->dlerror_buf = buf; + self->dlerror_flag = 1; +}