summaryrefslogtreecommitdiffstats
path: root/linker/linker.c
diff options
context:
space:
mode:
Diffstat (limited to 'linker/linker.c')
-rw-r--r--linker/linker.c142
1 files changed, 118 insertions, 24 deletions
diff --git a/linker/linker.c b/linker/linker.c
index 1e35f87..883da3c 100644
--- a/linker/linker.c
+++ b/linker/linker.c
@@ -313,15 +313,6 @@ static void free_info(soinfo *si)
freelist = si;
}
-#ifndef LINKER_TEXT_BASE
-#error "linker's makefile must define LINKER_TEXT_BASE"
-#endif
-#ifndef LINKER_AREA_SIZE
-#error "linker's makefile must define LINKER_AREA_SIZE"
-#endif
-#define LINKER_BASE ((LINKER_TEXT_BASE) & 0xfff00000)
-#define LINKER_TOP (LINKER_BASE + (LINKER_AREA_SIZE))
-
const char *addr_to_name(unsigned addr)
{
soinfo *si;
@@ -332,10 +323,6 @@ const char *addr_to_name(unsigned addr)
}
}
- if((addr >= LINKER_BASE) && (addr < LINKER_TOP)){
- return "linker";
- }
-
return "";
}
@@ -354,12 +341,10 @@ _Unwind_Ptr dl_unwind_find_exidx(_Unwind_Ptr pc, int *pcount)
soinfo *si;
unsigned addr = (unsigned)pc;
- if ((addr < LINKER_BASE) || (addr >= LINKER_TOP)) {
- for (si = solist; si != 0; si = si->next){
- if ((addr >= si->base) && (addr < (si->base + si->size))) {
- *pcount = si->ARM_exidx_count;
- return (_Unwind_Ptr)(si->base + (unsigned long)si->ARM_exidx);
- }
+ for (si = solist; si != 0; si = si->next){
+ if ((addr >= si->base) && (addr < (si->base + si->size))) {
+ *pcount = si->ARM_exidx_count;
+ return (_Unwind_Ptr)(si->base + (unsigned long)si->ARM_exidx);
}
}
*pcount = 0;
@@ -420,6 +405,33 @@ static Elf32_Sym *_elf_lookup(soinfo *si, unsigned hash, const char *name)
return NULL;
}
+/*
+ * Essentially the same method as _elf_lookup() above, but only
+ * searches for LOCAL symbols
+ */
+static Elf32_Sym *_elf_lookup_local(soinfo *si, unsigned hash, const char *name)
+{
+ Elf32_Sym *symtab = si->symtab;
+ const char *strtab = si->strtab;
+ unsigned n = hash % si->nbucket;;
+
+ TRACE_TYPE(LOOKUP, "%5d LOCAL SEARCH %s in %s@0x%08x %08x %d\n", pid,
+ name, si->name, si->base, hash, hash % si->nbucket);
+ for(n = si->bucket[hash % si->nbucket]; n != 0; n = si->chain[n]){
+ Elf32_Sym *s = symtab + n;
+ if (strcmp(strtab + s->st_name, name)) continue;
+ if (ELF32_ST_BIND(s->st_info) != STB_LOCAL) continue;
+ /* no section == undefined */
+ if(s->st_shndx == 0) continue;
+
+ TRACE_TYPE(LOOKUP, "%5d FOUND LOCAL %s in %s (%08x) %d\n", pid,
+ name, si->name, s->st_value, s->st_size);
+ return s;
+ }
+
+ return NULL;
+}
+
static unsigned elfhash(const char *_name)
{
const unsigned char *name = (const unsigned char *) _name;
@@ -443,7 +455,17 @@ _do_lookup(soinfo *si, const char *name, unsigned *base)
soinfo *lsi = si;
int i;
- /* Look for symbols in the local scope first (the object who is
+ /* If we are trying to find a symbol for the linker itself, look
+ * for LOCAL symbols first. Avoid using LOCAL symbols for other
+ * shared libraries until we have a better understanding of what
+ * might break by doing so. */
+ if (si->flags & FLAG_LINKER) {
+ s = _elf_lookup_local(si, elf_hash, name);
+ if(s != NULL)
+ goto done;
+ }
+
+ /* Look for symbols in the local scope (the object who is
* searching). This happens with C++ templates on i386 for some
* reason.
*
@@ -452,6 +474,7 @@ _do_lookup(soinfo *si, const char *name, unsigned *base)
* dynamic linking. Some systems return the first definition found
* and some the first non-weak definition. This is system dependent.
* Here we return the first definition found for simplicity. */
+
s = _elf_lookup(si, elf_hash, name);
if(s != NULL)
goto done;
@@ -1628,10 +1651,10 @@ static int link_image(soinfo *si, unsigned wr_offset)
DEBUG("%5d si->base = 0x%08x si->flags = 0x%08x\n", pid,
si->base, si->flags);
- if (si->flags & FLAG_EXE) {
+ if (si->flags & (FLAG_EXE | FLAG_LINKER)) {
/* Locate the needed program segments (DYNAMIC/ARM_EXIDX) for
- * linkage info if this is the executable. If this was a
- * dynamic lib, that would have been done at load time.
+ * linkage info if this is the executable or the linker itself.
+ * If this was a dynamic lib, that would have been done at load time.
*
* TODO: It's unfortunate that small pieces of this are
* repeated from the load_library routine. Refactor this just
@@ -1958,7 +1981,12 @@ int main(int argc, char **argv)
static void * __tls_area[ANDROID_TLS_SLOTS];
-unsigned __linker_init(unsigned **elfdata)
+/*
+ * This code is called after the linker has linked itself and
+ * fixed it's own GOT. It is safe to make references to externs
+ * and other non-local data at this point.
+ */
+static unsigned __linker_init_post_relocation(unsigned **elfdata)
{
static soinfo linker_soinfo;
@@ -2158,3 +2186,69 @@ sanitize:
si->entry);
return si->entry;
}
+
+/*
+ * Find the value of AT_BASE passed to us by the kernel. This is the load
+ * location of the linker.
+ */
+static unsigned find_linker_base(unsigned **elfdata) {
+ int argc = (int) *elfdata;
+ char **argv = (char**) (elfdata + 1);
+ unsigned *vecs = (unsigned*) (argv + argc + 1);
+ while (vecs[0] != 0) {
+ vecs++;
+ }
+
+ /* The end of the environment block is marked by two NULL pointers */
+ vecs++;
+
+ while(vecs[0]) {
+ if (vecs[0] == AT_BASE) {
+ return vecs[1];
+ }
+ vecs += 2;
+ }
+
+ return 0; // should never happen
+}
+
+/*
+ * This is the entry point for the linker, called from begin.S. This
+ * method is responsible for fixing the linker's own relocations, and
+ * then calling __linker_init_post_relocation().
+ *
+ * Because this method is called before the linker has fixed it's own
+ * relocations, any attempt to reference an extern variable, extern
+ * function, or other GOT reference will generate a segfault.
+ */
+unsigned __linker_init(unsigned **elfdata) {
+ unsigned linker_addr = find_linker_base(elfdata);
+ Elf32_Ehdr *elf_hdr = (Elf32_Ehdr *) linker_addr;
+ Elf32_Phdr *phdr =
+ (Elf32_Phdr *)((unsigned char *) linker_addr + elf_hdr->e_phoff);
+
+ soinfo linker_so;
+ memset(&linker_so, 0, sizeof(soinfo));
+
+ linker_so.base = linker_addr;
+ linker_so.dynamic = (unsigned *) -1;
+ linker_so.phdr = phdr;
+ linker_so.phnum = elf_hdr->e_phnum;
+ linker_so.flags |= FLAG_LINKER;
+ linker_so.wrprotect_start = 0xffffffff;
+ linker_so.wrprotect_end = 0;
+
+ if (link_image(&linker_so, 0)) {
+ // It would be nice to print an error message, but if the linker
+ // can't link itself, there's no guarantee that we'll be able to
+ // call write() (because it involves a GOT reference).
+ //
+ // This situation should never occur unless the linker itself
+ // is corrupt.
+ exit(-1);
+ }
+
+ // We have successfully fixed our own relocations. It's safe to run
+ // the main part of the linker now.
+ return __linker_init_post_relocation(elfdata);
+}