fix regression in pre-v7 arm on kernels with kuser helper removed
[musl] / src / ldso / dynlink.c
index 3842aeb..71252d0 100644 (file)
@@ -74,7 +74,6 @@ struct dso {
        volatile int new_dtv_idx, new_tls_idx;
        struct td_index *td_index;
        struct dso *fini_next;
-       int rel_early_relative, rel_update_got;
        char *shortname;
        char buf[];
 };
@@ -96,6 +95,9 @@ static struct builtin_tls {
 } builtin_tls[1];
 #define MIN_TLS_ALIGN offsetof(struct builtin_tls, pt)
 
+#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;
@@ -256,10 +258,19 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri
        size_t sym_val;
        size_t tls_val;
        size_t addend;
+       int skip_relative = 0, reuse_addends = 0, save_slot = 0;
+
+       if (dso == &ldso) {
+               /* Only ldso's REL table needs addend saving/reuse. */
+               if (rel == apply_addends_to)
+                       reuse_addends = 1;
+               skip_relative = 1;
+       }
 
        for (; rel_size; rel+=stride, rel_size-=stride*sizeof(size_t)) {
-               if (dso->rel_early_relative && IS_RELATIVE(rel[1])) 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) {
@@ -280,12 +291,20 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri
                        def.dso = dso;
                }
 
-               int gotplt = (type == REL_GOT || type == REL_PLT);
-               if (dso->rel_update_got && !gotplt && stride==2) continue;
-
-               addend = stride>2 ? rel[2]
-                       : gotplt || type==REL_COPY ? 0
-                       : *reloc_addr;
+               if (stride > 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;
@@ -879,7 +898,7 @@ 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->rel_early_relative) {
+       if (p==&ldso) {
                got += i;
        } else {
                while (i--) *got++ += (size_t)base;
@@ -1125,15 +1144,29 @@ void __dls2(unsigned char *base, size_t *sp)
        ldso.phnum = ehdr->e_phnum;
        ldso.phdr = (void *)(base + ehdr->e_phoff);
        ldso.phentsize = ehdr->e_phentsize;
-       ldso.rel_early_relative = 1;
        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;
-       ldso.rel_update_got = 1;
 
        /* Call dynamic linker stage-3, __dls3, looking it up
         * symbolically as a barrier against moving the address
@@ -1159,6 +1192,17 @@ _Noreturn void __dls3(size_t *sp)
        char **argv_orig = argv;
        char **envp = argv+argc+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. */
@@ -1167,25 +1211,11 @@ _Noreturn void __dls3(size_t *sp)
                a_crash();
        }
 
-       /* 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);
-
-       decode_vec(auxv, aux, AUX_CNT);
-
        /* 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;
+       if (!libc.secure) {
+               env_path = getenv("LD_LIBRARY_PATH");
+               env_preload = getenv("LD_PRELOAD");
        }
-       libc.page_size = aux[AT_PAGESZ];
-       libc.auxv = auxv;
 
        /* If the main program was already loaded by the kernel,
         * AT_PHDR will point to some location other than the dynamic