Make the linker relocatable.

Previously, the linker always loaded itself into the same
location in memory, which inhibited the effectiveness of Android's
ASLR implementation. Modify the linker code so it can be relocatable
and link itself at runtime.

Change-Id: I90d064743abdd29450ac0482ed28752b2196286c
This commit is contained in:
Nick Kralevich 2011-11-01 10:51:22 -07:00
parent 29992cf978
commit 994e9a5ed1
3 changed files with 129 additions and 41 deletions

View File

@ -10,27 +10,9 @@ LOCAL_SRC_FILES:= \
dlfcn.c \
debugger.c
ifeq ($(TARGET_ARCH),sh)
# SH-4A series virtual address range from 0x00000000 to 0x7FFFFFFF.
LINKER_TEXT_BASE := 0x70000100
else
# This is aligned to 4K page boundary so that both GNU ld and gold work. Gold
# actually produces a correct binary with starting address 0xB0000100 but the
# extra objcopy step to rename symbols causes the resulting binary to be misaligned
# and unloadable. Increasing the alignment adds an extra 3840 bytes in padding
# but switching to gold saves about 1M of space.
LINKER_TEXT_BASE := 0xB0001000
endif
LOCAL_LDFLAGS := -shared
# The maximum size set aside for the linker, from
# LINKER_TEXT_BASE rounded down to a megabyte.
LINKER_AREA_SIZE := 0x01000000
LOCAL_LDFLAGS := -Wl,-Ttext,$(LINKER_TEXT_BASE)
LOCAL_CFLAGS += -DPRELINK
LOCAL_CFLAGS += -DLINKER_TEXT_BASE=$(LINKER_TEXT_BASE)
LOCAL_CFLAGS += -DLINKER_AREA_SIZE=$(LINKER_AREA_SIZE)
LOCAL_CFLAGS += -fno-stack-protector
# Set LINKER_DEBUG to either 1 or 0
#

View File

@ -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;
@ -2084,7 +2107,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;
@ -2261,3 +2289,80 @@ unsigned __linker_init(unsigned **elfdata)
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;
/*
* Find the DYNAMIC section of the linker itself, so we can
* fix our own relocations.
*/
int i;
for (i = 0; i < linker_so.phnum; i++) {
if (phdr[i].p_type == PT_DYNAMIC) {
linker_so.dynamic = (unsigned *)(linker_so.base + phdr[i].p_vaddr);
break;
}
}
if ((linker_so.dynamic != (unsigned char*) -1)
&& 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);
}

View File

@ -83,6 +83,7 @@ typedef struct soinfo soinfo;
#define FLAG_LINKED 0x00000001
#define FLAG_ERROR 0x00000002
#define FLAG_EXE 0x00000004 // The main executable
#define FLAG_LINKER 0x00000010 // The linker itself
#define SOINFO_NAME_LEN 128