Bug 651889 - Add support for dynamic symbol lookup in the Android dynamic linker. r=mwu
authorMike Hommey <mh+mozilla@glandium.org>
Tue, 09 Aug 2011 13:35:52 +0200
changeset 75233 d0c859f6ffce83cbed91c68ef5a526540e3104bc
parent 75232 ca13b9114ce6f93aa74da351496d8a4752874195
child 75235 8d7c38e31bdc68f3ab8fa171512fc7962ffbf432
push idunknown
push userunknown
push dateunknown
reviewersmwu
bugs651889
milestone8.0a1
Bug 651889 - Add support for dynamic symbol lookup in the Android dynamic linker. r=mwu
other-licenses/android/linker.c
--- a/other-licenses/android/linker.c
+++ b/other-licenses/android/linker.c
@@ -1423,19 +1423,17 @@ static int reloc_library(soinfo *si, Elf
             PFLAGS_TO_PROT(phdr->p_flags) & PROT_WRITE)
             continue;
 
         if (si->base + phdr->p_vaddr + phdr->p_filesz > ro_region_end)
             ro_region_end = si->base + phdr->p_vaddr + phdr->p_filesz;
     }
 
     void * remapped_page = NULL;
-    void * copy_page = mmap(NULL, PAGE_SIZE,
-                            PROT_READ | PROT_WRITE,
-                            MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+    void * copy_page = NULL;
 
     for (idx = 0; idx < count; ++idx) {
         unsigned type = ELF32_R_TYPE(rel->r_info);
         unsigned sym = ELF32_R_SYM(rel->r_info);
         unsigned reloc = (unsigned)(rel->r_offset + si->base);
         unsigned sym_addr = 0;
         char *sym_name = NULL;
 
@@ -1516,16 +1514,20 @@ static int reloc_library(soinfo *si, Elf
         } else {
             s = NULL;
         }
 
         /* crappy hack part 2: make this page writable */
         void * reloc_page = reloc & ~PAGE_MASK;
         if ((si->flags & FLAG_MMAPPED) &&
             (reloc < ro_region_end && reloc_page != remapped_page)) {
+            if (copy_page == NULL)
+                copy_page = mmap(NULL, PAGE_SIZE,
+                                 PROT_READ | PROT_WRITE,
+                                 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
             if (remapped_page != NULL)
                 mprotect(remapped_page, PAGE_SIZE, PROT_READ | PROT_EXEC);
             memcpy(copy_page, reloc_page, PAGE_SIZE);
             munmap(reloc_page, PAGE_SIZE);
             if (mmap(reloc_page, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
                      MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
                      -1, 0) == MAP_FAILED)
                 DL_ERR("failed to map page for %s at 0x%08x! errno=%d",
@@ -1635,17 +1637,18 @@ static int reloc_library(soinfo *si, Elf
 
         default:
             DL_ERR("%5d unknown reloc type %d @ %p (%d)",
                   pid, type, rel, (int) (rel - start));
             return -1;
         }
         rel++;
     }
-    munmap(copy_page, PAGE_SIZE);
+    if (copy_page)
+        munmap(copy_page, PAGE_SIZE);
     return 0;
 }
 
 #if defined(ANDROID_SH_LINKER)
 static int reloc_library_a(soinfo *si, Elf32_Rela *rela, unsigned count)
 {
     Elf32_Sym *symtab = si->symtab;
     const char *strtab = si->strtab;
@@ -1890,16 +1893,76 @@ static int nullify_closed_stdio (void)
             DL_ERR("nullify_stdio: close error %s", strerror(errno));
             return_value = -1;
         }
     }
 
     return return_value;
 }
 
+/* Performs a single (plt) relocation for the relocation at index num, and
+ * returns the resulting relocated address */
+static unsigned int plt_reloc(soinfo *si, unsigned int num)
+{
+    if (si->plt_rel) {
+      reloc_library(si, &si->plt_rel[num], 1);
+      return *(unsigned int *)(si->base + si->plt_rel[num].r_offset);
+    }
+#ifdef ANDROID_SH_LINKER
+    if (si->plt_rela) {
+      reloc_library_a(si, &si->plt_rela[num], 1);
+      return *(unsigned int *)(si->base + si->plt_rela[num].r_offset);
+    }
+#endif
+}
+
+/* As this function is only referenced from assembly, we need to tell the
+ * compiler not to throw it away */
+static unsigned int plt_reloc(soinfo *si, unsigned int num) __attribute__((used));
+
+#ifdef ANDROID_ARM_LINKER
+/* On ARM, the runtime symbol resolution function is called with
+ * - ip pointing to the function pointer to update in PLT_GOT
+ * - lr pointing to &PLT_GOT[2]
+ * - a pointer to the return address on top of the stack.
+ * Note that the stack, when this function is called, in unaligned
+ */
+__asm__ (
+    ".text\n"
+    ".align 2\n"
+    ".type runtime_reloc, %function\n"
+"runtime_reloc:\n"
+    /* We need to save r0-r3 because they are arguments we will be passing
+     * to the resolved function, and r4 to realign the stack */
+    "stmfd  sp!, {r0-r4}\n"
+    /* Prepare the call to plt_reloc.
+     * The first argument is the soinfo pointer, which we stored at
+     * PLT_GOT[1], which is at lr - 4. */
+    "ldr r0, [lr, #-4]\n"
+    /* The second argument is the index of the plt entry, starting from
+       PLT_GOT[3]. This is (ip - (lr + 4)) / 4. */
+    "sub r1, ip, lr\n"
+    "sub r1, #4\n"
+    "lsr r1, r1, #2\n"
+    /* Call plt_reloc */
+    "bl plt_reloc\n"
+    /* Store the resolved function address to ip to use after overwriting
+       r0. */
+    "mov ip, r0\n"
+    /* Restore r0-r3 to be used as arguments when calling the resolved
+     * function, r4 which we saved to realign the stack and lr which
+     * was saved by the plt trampoline. */
+    "ldmia sp!, {r0-r4,lr}\n"
+    /* Jump to the resolved function. */
+    "bx ip\n"
+);
+#endif
+
+static void runtime_reloc() __attribute__((used));
+
 static int link_image(soinfo *si, unsigned wr_offset)
 {
     unsigned *d;
     Elf32_Phdr *phdr = si->phdr;
     int phnum = si->phnum;
 
     INFO("[ %5d linking %s ]\n", pid, si->name);
     DEBUG("%5d si->base = 0x%08x si->flags = 0x%08x\n", pid,
@@ -2133,32 +2196,39 @@ static int link_image(soinfo *si, unsign
                later on when we resolve relocations, trying to look up a symgol
                with dlsym().
             */
             d[1] = (unsigned)lsi;
             lsi->refcount++;
         }
     }
 
+    /* Initialize GOT[1] and GOT[2], which are used by the PLT trampoline */
+    Elf32_Addr *got = (Elf32_Addr *)si->plt_got;
+    got[1] = (Elf32_Addr) si;
+    got[2] = (Elf32_Addr) runtime_reloc;
+
     if(si->plt_rel) {
         DEBUG("[ %5d relocating %s plt ]\n", pid, si->name );
-        if(reloc_library(si, si->plt_rel, si->plt_rel_count))
-            goto fail;
+        /* Relocate PLT GOT */
+        for (d = got + 3; d < got + 3 + si->plt_rel_count; d++)
+            *d += si->base;
     }
     if(si->rel) {
         DEBUG("[ %5d relocating %s ]\n", pid, si->name );
         if(reloc_library(si, si->rel, si->rel_count))
             goto fail;
     }
 
 #ifdef ANDROID_SH_LINKER
     if(si->plt_rela) {
         DEBUG("[ %5d relocating %s plt ]\n", pid, si->name );
-        if(reloc_library_a(si, si->plt_rela, si->plt_rela_count))
-            goto fail;
+        /* Relocate PLT GOT */
+        for (d = got + 3; d < got + 3 + si->plt_rela_count; d++)
+            *d += si->base;
     }
     if(si->rela) {
         DEBUG("[ %5d relocating %s ]\n", pid, si->name );
         if(reloc_library_a(si, si->rela, si->rela_count))
             goto fail;
     }
 #endif /* ANDROID_SH_LINKER */