Merge "linker: handle R_ARM_COPY relocations in a proper way"
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp
index c77b9a3..c87d29a 100644
--- a/linker/dlfcn.cpp
+++ b/linker/dlfcn.cpp
@@ -236,5 +236,7 @@
 
     refcount: 0,
     { l_addr: 0, l_name: 0, l_ld: 0, l_next: 0, l_prev: 0, },
-    constructors_called: 0, load_bias: 0, has_text_relocations: 0,
+    constructors_called: 0, load_bias: 0,
+    has_text_relocations: false,
+    has_DT_SYMBOLIC: true,
 };
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 4c52f3d..9e4138e 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -51,7 +51,6 @@
 #include "linker_format.h"
 #include "linker_phdr.h"
 
-#define ALLOW_SYMBOLS_FROM_MAIN 1
 #define SO_MAX 128
 
 /* Assume average path length of 64 and max 8 paths */
@@ -89,9 +88,7 @@
 static soinfo *freelist = NULL;
 static soinfo *solist = &libdl_info;
 static soinfo *sonext = &libdl_info;
-#if ALLOW_SYMBOLS_FROM_MAIN
 static soinfo *somain; /* main process, always the one after libdl_info */
-#endif
 
 static char ldpaths_buf[LDPATH_BUFSIZE];
 static const char *ldpaths[LDPATH_MAX + 1];
@@ -421,15 +418,31 @@
 }
 
 static Elf32_Sym *
-soinfo_do_lookup(soinfo *si, const char *name, Elf32_Addr *offset,
-                 soinfo *needed[], bool ignore_local)
+soinfo_do_lookup(soinfo *si, const char *name, soinfo **lsi,
+                 soinfo *needed[])
 {
     unsigned elf_hash = elfhash(name);
     Elf32_Sym *s = NULL;
-    soinfo *lsi = si;
     int i;
 
-    if (!ignore_local) {
+    if (si != NULL) {
+
+        /*
+         * If this object was built with symbolic relocations disabled, the
+         * first place to look to resolve external references is the main
+         * executable.
+         */
+
+        if (!si->has_DT_SYMBOLIC) {
+            DEBUG("%5d %s: looking up %s in executable %s\n",
+                  pid, si->name, name, somain->name);
+            s = soinfo_elf_lookup(somain, elf_hash, name);
+            if (s != NULL) {
+                *lsi = somain;
+                goto done;
+            }
+        }
+
         /* Look for symbols in the local scope (the object who is
          * searching). This happens with C++ templates on i386 for some
          * reason.
@@ -441,47 +454,37 @@
          * Here we return the first definition found for simplicity.  */
 
         s = soinfo_elf_lookup(si, elf_hash, name);
-        if(s != NULL)
+        if (s != NULL) {
+            *lsi = si;
             goto done;
+        }
     }
 
     /* Next, look for it in the preloads list */
     for(i = 0; preloads[i] != NULL; i++) {
-        lsi = preloads[i];
-        s = soinfo_elf_lookup(lsi, elf_hash, name);
-        if(s != NULL)
+        s = soinfo_elf_lookup(preloads[i], elf_hash, name);
+        if(s != NULL) {
+            *lsi = preloads[i];
             goto done;
+        }
     }
 
     for(i = 0; needed[i] != NULL; i++) {
-        lsi = needed[i];
         DEBUG("%5d %s: looking up %s in %s\n",
-              pid, si->name, name, lsi->name);
-        s = soinfo_elf_lookup(lsi, elf_hash, name);
-        if (s != NULL)
+              pid, si->name, name, needed[i]->name);
+        s = soinfo_elf_lookup(needed[i], elf_hash, name);
+        if (s != NULL) {
+            *lsi = needed[i];
             goto done;
+        }
     }
 
-#if ALLOW_SYMBOLS_FROM_MAIN
-    /* If we are resolving relocations while dlopen()ing a library, it's OK for
-     * the library to resolve a symbol that's defined in the executable itself,
-     * although this is rare and is generally a bad idea.
-     */
-    if (somain) {
-        lsi = somain;
-        DEBUG("%5d %s: looking up %s in executable %s\n",
-              pid, si->name, name, lsi->name);
-        s = soinfo_elf_lookup(lsi, elf_hash, name);
-    }
-#endif
-
 done:
     if(s != NULL) {
         TRACE_TYPE(LOOKUP, "%5d si %s sym %s s->st_value = 0x%08x, "
                    "found in %s, base = 0x%08x, load bias = 0x%08x\n",
                    pid, si->name, name, s->st_value,
-                   lsi->name, lsi->base, lsi->load_bias);
-        *offset = lsi->load_bias;
+                   (*lsi)->name, (*lsi)->base, (*lsi)->load_bias);
         return s;
     }
 
@@ -863,13 +866,8 @@
 {
     soinfo *si;
 
-#if ALLOW_SYMBOLS_FROM_MAIN
     if (name == NULL)
         return somain;
-#else
-    if (name == NULL)
-        return NULL;
-#endif
 
     si = find_loaded_library(name);
     if (si != NULL) {
@@ -933,8 +931,8 @@
     Elf32_Sym *symtab = si->symtab;
     const char *strtab = si->strtab;
     Elf32_Sym *s;
-    Elf32_Addr offset;
     Elf32_Rel *start = rel;
+    soinfo *lsi;
 
     for (size_t idx = 0; idx < count; ++idx, ++rel) {
         unsigned type = ELF32_R_TYPE(rel->r_info);
@@ -950,11 +948,7 @@
         }
         if(sym != 0) {
             sym_name = (char *)(strtab + symtab[sym].st_name);
-            bool ignore_local = false;
-#if defined(ANDROID_ARM_LINKER)
-            ignore_local = (type == R_ARM_COPY);
-#endif
-            s = soinfo_do_lookup(si, sym_name, &offset, needed, ignore_local);
+            s = soinfo_do_lookup(si, sym_name, &lsi, needed);
             if(s == NULL) {
                 /* We only allow an undefined symbol if this is a weak
                    reference..   */
@@ -1020,7 +1014,7 @@
                     return -1;
                 }
 #endif
-                sym_addr = (unsigned)(s->st_value + offset);
+                sym_addr = (unsigned)(s->st_value + lsi->load_bias);
             }
             count_relocation(kRelocSymbol);
         } else {
@@ -1154,10 +1148,27 @@
             TRACE_TYPE(RELO, "%5d RELO %08x <- %d @ %08x %s\n", pid,
                        reloc, s->st_size, sym_addr, sym_name);
             if (reloc == sym_addr) {
-                DL_ERR("Internal linker error detected. reloc == symaddr");
+                Elf32_Sym *src = soinfo_do_lookup(NULL, sym_name, &lsi, needed);
+
+                if (src == NULL) {
+                    DL_ERR("%s R_ARM_COPY relocation source cannot be resolved", si->name);
+                    return -1;
+                }
+                if (lsi->has_DT_SYMBOLIC) {
+                    DL_ERR("%s invalid R_ARM_COPY relocation against DT_SYMBOLIC shared "
+                           "library %s (built with -Bsymbolic?)", si->name, lsi->name);
+                    return -1;
+                }
+                if (s->st_size < src->st_size) {
+                    DL_ERR("%s R_ARM_COPY relocation size mismatch (%d < %d)",
+                           si->name, s->st_size, src->st_size);
+                    return -1;
+                }
+                memcpy((void*)reloc, (void*)(src->st_value + lsi->load_bias), src->st_size);
+            } else {
+                DL_ERR("%s R_ARM_COPY relocation target cannot be resolved", si->name);
                 return -1;
             }
-            memcpy((void*)reloc, (void*)sym_addr, s->st_size);
             break;
 #endif /* ANDROID_ARM_LINKER */
 
@@ -1210,12 +1221,12 @@
     got = si->plt_got + local_gotno;
     for (g = gotsym; g < symtabno; g++, sym++, got++) {
         const char *sym_name;
-        unsigned base;
         Elf32_Sym *s;
+        soinfo *lsi;
 
         /* This is an undefined reference... try to locate it */
         sym_name = si->strtab + sym->st_name;
-        s = soinfo_do_lookup(si, sym_name, &base, needed, false);
+        s = soinfo_do_lookup(si, sym_name, &lsi, needed);
         if (s == NULL) {
             /* We only allow an undefined symbol if this is a weak
                reference..   */
@@ -1231,7 +1242,7 @@
              * For reference see NetBSD link loader
              * http://cvsweb.netbsd.org/bsdweb.cgi/src/libexec/ld.elf_so/arch/mips/mips_reloc.c?rev=1.53&content-type=text/x-cvsweb-markup
              */
-             *got = base + s->st_value;
+             *got = lsi->load_bias + s->st_value;
         }
     }
     return 0;
@@ -1537,6 +1548,19 @@
         case DT_TEXTREL:
             si->has_text_relocations = true;
             break;
+        case DT_SYMBOLIC:
+            si->has_DT_SYMBOLIC = true;
+            break;
+#if defined(DT_FLAGS)
+        case DT_FLAGS:
+            if (*d & DF_TEXTREL) {
+                si->has_text_relocations = true;
+            }
+            if (*d & DF_SYMBOLIC) {
+                si->has_DT_SYMBOLIC = true;
+            }
+            break;
+#endif
 #if defined(ANDROID_MIPS_LINKER)
         case DT_NEEDED:
         case DT_STRSZ:
@@ -1868,6 +1892,8 @@
     parse_LD_LIBRARY_PATH(ldpath_env);
     parse_LD_PRELOAD(ldpreload_env);
 
+    somain = si;
+
     if(soinfo_link_image(si)) {
         char errmsg[] = "CANNOT LINK EXECUTABLE\n";
         write(2, __linker_dl_err_buf, strlen(__linker_dl_err_buf));
@@ -1889,14 +1915,6 @@
     map->l_addr = si->base;
     soinfo_call_constructors(si);
 
-#if ALLOW_SYMBOLS_FROM_MAIN
-    /* Set somain after we've loaded all the libraries in order to prevent
-     * linking of symbols back to the main image, which is not set up at that
-     * point yet.
-     */
-    somain = si;
-#endif
-
 #if TIMING
     gettimeofday(&t1,NULL);
     PRINT("LINKER TIME: %s: %d microseconds\n", argv[0], (int) (
diff --git a/linker/linker.h b/linker/linker.h
index 54563bb..cb33602 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -160,7 +160,9 @@
     /* When you read a virtual address from the ELF file, add this
      * value to get the corresponding address in the process' address space */
     Elf32_Addr load_bias;
-    int has_text_relocations;
+
+    bool has_text_relocations;
+    bool has_DT_SYMBOLIC;
 };