eclair snapshot
diff --git a/linker/linker.c b/linker/linker.c
index 789a828..5090b11 100644
--- a/linker/linker.c
+++ b/linker/linker.c
@@ -51,6 +51,7 @@
 
 #include "ba.h"
 
+#define ALLOW_SYMBOLS_FROM_MAIN 1
 #define SO_MAX 96
 
 /* Assume average path length of 64 and max 8 paths */
@@ -86,6 +87,27 @@
 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
+
+
+/* Set up for the buddy allocator managing the prelinked libraries. */
+static struct ba_bits ba_prelink_bitmap[(LIBLAST - LIBBASE) / LIBINC];
+static struct ba ba_prelink = {
+    .base = LIBBASE,
+    .size = LIBLAST - LIBBASE,
+    .min_alloc = LIBINC,
+    /* max_order will be determined automatically */
+    .bitmap = ba_prelink_bitmap,
+    .num_entries = sizeof(ba_prelink_bitmap)/sizeof(ba_prelink_bitmap[0]),
+};
+
+static inline int validate_soinfo(soinfo *si)
+{
+    return (si >= sopool && si < sopool + SO_MAX) ||
+        si == &libdl_info;
+}
 
 static char ldpaths_buf[LDPATH_BUFSIZE];
 static const char *ldpaths[LDPATH_MAX + 1];
@@ -421,53 +443,98 @@
     return _elf_lookup (si, *elf_hash, name);
 }
 
-/* This is used by dl_sym() */
-Elf32_Sym *lookup_in_library(soinfo *si, const char *name)
-{
-    unsigned unused = 0;
-    return _do_lookup_in_so(si, name, &unused);
-}
-
 static Elf32_Sym *
-_do_lookup(soinfo *user_si, const char *name, unsigned *base)
+_do_lookup(soinfo *si, const char *name, unsigned *base)
 {
     unsigned elf_hash = 0;
-    Elf32_Sym *s = NULL;
-    soinfo *si;
+    Elf32_Sym *s;
+    unsigned *d;
+    soinfo *lsi = si;
 
     /* Look for symbols in the local scope first (the object who is
      * searching). This happens with C++ templates on i386 for some
      * reason. */
-    if (user_si) {
-        s = _do_lookup_in_so(user_si, name, &elf_hash);
-        if (s != NULL)
-            *base = user_si->base;
-    }
+    s = _do_lookup_in_so(si, name, &elf_hash);
+    if(s != NULL)
+        goto done;
 
-    for(si = solist; (s == NULL) && (si != NULL); si = si->next)
-    {
-        if((si->flags & FLAG_ERROR) || (si == user_si))
-            continue;
-        s = _do_lookup_in_so(si, name, &elf_hash);
-        if (s != NULL) {
-            *base = si->base;
-            break;
+    for(d = si->dynamic; *d; d += 2) {
+        if(d[0] == DT_NEEDED){
+            lsi = (soinfo *)d[1];
+            if (!validate_soinfo(lsi)) {
+                DL_ERR("%5d bad DT_NEEDED pointer in %s",
+                       pid, si->name);
+                return 0;
+            }
+
+            DEBUG("%5d %s: looking up %s in %s\n",
+                  pid, si->name, name, lsi->name);
+            s = _do_lookup_in_so(lsi, name, &elf_hash);
+            if(s != NULL)
+                goto done;
         }
     }
 
-    if (s != NULL) {
-        TRACE_TYPE(LOOKUP, "%5d %s s->st_value = 0x%08x, "
-                   "si->base = 0x%08x\n", pid, name, s->st_value, si->base);
+#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 = _do_lookup_in_so(lsi, name, &elf_hash);
+    }
+#endif
+
+done:
+    if(s != NULL) {
+        TRACE_TYPE(LOOKUP, "%5d si %s sym %s s->st_value = 0x%08x, "
+                   "found in %s, base = 0x%08x\n",
+                   pid, si->name, name, s->st_value, lsi->name, lsi->base);
+        *base = lsi->base;
         return s;
     }
 
     return 0;
 }
 
-/* This is used by dl_sym() */
-Elf32_Sym *lookup(const char *name, unsigned *base)
+/* This is used by dl_sym().  It performs symbol lookup only within the
+   specified soinfo object and not in any of its dependencies.
+ */
+Elf32_Sym *lookup_in_library(soinfo *si, const char *name)
 {
-    return _do_lookup(NULL, name, base);
+    unsigned unused = 0;
+    return _do_lookup_in_so(si, name, &unused);
+}
+
+/* This is used by dl_sym().  It performs a global symbol lookup.
+ */
+Elf32_Sym *lookup(const char *name, soinfo **found)
+{
+    unsigned elf_hash = 0;
+    Elf32_Sym *s = NULL;
+    soinfo *si;
+
+    for(si = solist; (s == NULL) && (si != NULL); si = si->next)
+    {
+        if(si->flags & FLAG_ERROR)
+            continue;
+        s = _do_lookup_in_so(si, name, &elf_hash);
+        if (s != NULL) {
+            *found = si;
+            break;
+        }
+    }
+
+    if(s != NULL) {
+        TRACE_TYPE(LOOKUP, "%5d %s s->st_value = 0x%08x, "
+                   "si->base = 0x%08x\n", pid, name, s->st_value, si->base);
+        return s;
+    }
+
+    return 0;
 }
 
 #if 0
@@ -728,14 +795,14 @@
        for it from the buddy allocator, which manages the area between
        LIBBASE and LIBLAST.
     */
-    si->ba_index = ba_allocate(si->size);
+    si->ba_index = ba_allocate(&ba_prelink, si->size);
     if(si->ba_index >= 0) {
-        si->base = ba_start_addr(si->ba_index);
+        si->base = ba_start_addr(&ba_prelink, si->ba_index);
         PRINT("%5d mapping library '%s' at %08x (index %d) " \
               "through buddy allocator.\n",
               pid, si->name, si->base, si->ba_index);
         if (reserve_mem_region(si) < 0) {
-            ba_free(si->ba_index);
+            ba_free(&ba_prelink, si->ba_index);
             si->ba_index = -1;
             si->base = 0;
             goto err;
@@ -1031,7 +1098,7 @@
     /* Now actually load the library's segments into right places in memory */
     if (load_segments(fd, &__header[0], si) < 0) {
         if (si->ba_index >= 0) {
-            ba_free(si->ba_index);
+            ba_free(&ba_prelink, si->ba_index);
             si->ba_index = -1;
         }
         goto fail;
@@ -1085,7 +1152,10 @@
 
     for(si = solist; si != 0; si = si->next){
         if(!strcmp(bname, si->name)) {
-            if(si->flags & FLAG_ERROR) return 0;
+            if(si->flags & FLAG_ERROR) {
+                DL_ERR("%5d '%s' failed to load previously", pid, bname);
+                return NULL;
+            }
             if(si->flags & FLAG_LINKED) return si;
             DL_ERR("OOPS: %5d recursive link to '%s'", pid, si->name);
             return NULL;
@@ -1113,14 +1183,16 @@
 
         for(d = si->dynamic; *d; d += 2) {
             if(d[0] == DT_NEEDED){
-                TRACE("%5d %s needs to unload %s\n", pid,
-                      si->name, si->strtab + d[1]);
-                soinfo *lsi = find_library(si->strtab + d[1]);
-                if(lsi)
+                soinfo *lsi = (soinfo *)d[1];
+                d[1] = 0;
+                if (validate_soinfo(lsi)) {
+                    TRACE("%5d %s needs to unload %s\n", pid,
+                          si->name, lsi->name);
                     unload_library(lsi);
+                }
                 else
-                    DL_ERR("%5d could not unload '%s'",
-                          pid, si->strtab + d[1]);
+                    DL_ERR("%5d %s: could not unload dependent library",
+                           pid, si->name);
             }
         }
 
@@ -1129,7 +1201,7 @@
             PRINT("%5d releasing library '%s' address space at %08x "\
                   "through buddy allocator.\n",
                   pid, si->name, si->base);
-            ba_free(si->ba_index);
+            ba_free(&ba_prelink, si->ba_index);
         }
         notify_gdb_of_unload(si);
         free_info(si);
@@ -1180,9 +1252,13 @@
                 return -1;
             }
 #endif
-            if ((s->st_shndx == SHN_UNDEF) && (s->st_value != 0)) {
-                DL_ERR("%5d In '%s', shndx=%d && value=0x%08x. We do not "
-                      "handle this yet", pid, si->name, s->st_shndx,
+            // st_shndx==SHN_UNDEF means an undefined symbol.
+            // st_value should be 0 then, except that the low bit of st_value is
+            // used to indicate whether the symbol points to an ARM or thumb function,
+            // and should be ignored in the following check.
+            if ((s->st_shndx == SHN_UNDEF) && ((s->st_value & ~1) != 0)) {
+                DL_ERR("%5d In '%s', symbol=%s shndx=%d && value=0x%08x. We do not "
+                      "handle this yet", pid, si->name, sym_name, s->st_shndx,
                       s->st_value);
                 return -1;
             }
@@ -1638,6 +1714,14 @@
                        pid, si->strtab + d[1], si->name, tmp_err_buf);
                 goto fail;
             }
+            /* Save the soinfo of the loaded DT_NEEDED library in the payload
+               of the DT_NEEDED entry itself, so that we can retrieve the
+               soinfo directly later from the dynamic segment.  This is a hack,
+               but it allows us to map from DT_NEEDED to soinfo efficiently
+               later on when we resolve relocations, trying to look up a symgol
+               with dlsym().
+            */
+            d[1] = (unsigned)lsi;
             lsi->refcount++;
         }
     }
@@ -1825,7 +1909,7 @@
         vecs += 2;
     }
 
-    ba_init();
+    ba_init(&ba_prelink);
 
     si->base = 0;
     si->dynamic = (unsigned *)-1;
@@ -1843,6 +1927,14 @@
         exit(-1);
     }
 
+#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) (