Merge "Make the linker relocatable."
This commit is contained in:
commit
7e2daefe6c
@ -10,27 +10,9 @@ LOCAL_SRC_FILES:= \
|
|||||||
dlfcn.c \
|
dlfcn.c \
|
||||||
debugger.c
|
debugger.c
|
||||||
|
|
||||||
ifeq ($(TARGET_ARCH),sh)
|
LOCAL_LDFLAGS := -shared
|
||||||
# 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
|
|
||||||
|
|
||||||
# The maximum size set aside for the linker, from
|
LOCAL_CFLAGS += -fno-stack-protector
|
||||||
# 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)
|
|
||||||
|
|
||||||
# Set LINKER_DEBUG to either 1 or 0
|
# Set LINKER_DEBUG to either 1 or 0
|
||||||
#
|
#
|
||||||
|
142
linker/linker.c
142
linker/linker.c
@ -313,15 +313,6 @@ static void free_info(soinfo *si)
|
|||||||
freelist = 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)
|
const char *addr_to_name(unsigned addr)
|
||||||
{
|
{
|
||||||
soinfo *si;
|
soinfo *si;
|
||||||
@ -332,10 +323,6 @@ const char *addr_to_name(unsigned addr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if((addr >= LINKER_BASE) && (addr < LINKER_TOP)){
|
|
||||||
return "linker";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,12 +341,10 @@ _Unwind_Ptr dl_unwind_find_exidx(_Unwind_Ptr pc, int *pcount)
|
|||||||
soinfo *si;
|
soinfo *si;
|
||||||
unsigned addr = (unsigned)pc;
|
unsigned addr = (unsigned)pc;
|
||||||
|
|
||||||
if ((addr < LINKER_BASE) || (addr >= LINKER_TOP)) {
|
for (si = solist; si != 0; si = si->next){
|
||||||
for (si = solist; si != 0; si = si->next){
|
if ((addr >= si->base) && (addr < (si->base + si->size))) {
|
||||||
if ((addr >= si->base) && (addr < (si->base + si->size))) {
|
*pcount = si->ARM_exidx_count;
|
||||||
*pcount = si->ARM_exidx_count;
|
return (_Unwind_Ptr)(si->base + (unsigned long)si->ARM_exidx);
|
||||||
return (_Unwind_Ptr)(si->base + (unsigned long)si->ARM_exidx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*pcount = 0;
|
*pcount = 0;
|
||||||
@ -420,6 +405,33 @@ static Elf32_Sym *_elf_lookup(soinfo *si, unsigned hash, const char *name)
|
|||||||
return NULL;
|
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)
|
static unsigned elfhash(const char *_name)
|
||||||
{
|
{
|
||||||
const unsigned char *name = (const unsigned 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;
|
soinfo *lsi = si;
|
||||||
int i;
|
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
|
* searching). This happens with C++ templates on i386 for some
|
||||||
* reason.
|
* reason.
|
||||||
*
|
*
|
||||||
@ -452,6 +474,7 @@ _do_lookup(soinfo *si, const char *name, unsigned *base)
|
|||||||
* dynamic linking. Some systems return the first definition found
|
* dynamic linking. Some systems return the first definition found
|
||||||
* and some the first non-weak definition. This is system dependent.
|
* and some the first non-weak definition. This is system dependent.
|
||||||
* Here we return the first definition found for simplicity. */
|
* Here we return the first definition found for simplicity. */
|
||||||
|
|
||||||
s = _elf_lookup(si, elf_hash, name);
|
s = _elf_lookup(si, elf_hash, name);
|
||||||
if(s != NULL)
|
if(s != NULL)
|
||||||
goto done;
|
goto done;
|
||||||
@ -1719,10 +1742,10 @@ static int link_image(soinfo *si, unsigned wr_offset)
|
|||||||
DEBUG("%5d si->base = 0x%08x si->flags = 0x%08x\n", pid,
|
DEBUG("%5d si->base = 0x%08x si->flags = 0x%08x\n", pid,
|
||||||
si->base, si->flags);
|
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
|
/* Locate the needed program segments (DYNAMIC/ARM_EXIDX) for
|
||||||
* linkage info if this is the executable. If this was a
|
* linkage info if this is the executable or the linker itself.
|
||||||
* dynamic lib, that would have been done at load time.
|
* If this was a dynamic lib, that would have been done at load time.
|
||||||
*
|
*
|
||||||
* TODO: It's unfortunate that small pieces of this are
|
* TODO: It's unfortunate that small pieces of this are
|
||||||
* repeated from the load_library routine. Refactor this just
|
* repeated from the load_library routine. Refactor this just
|
||||||
@ -2084,7 +2107,12 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
static void * __tls_area[ANDROID_TLS_SLOTS];
|
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;
|
static soinfo linker_soinfo;
|
||||||
|
|
||||||
@ -2272,3 +2300,69 @@ unsigned __linker_init(unsigned **elfdata)
|
|||||||
si->entry);
|
si->entry);
|
||||||
return 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);
|
||||||
|
}
|
||||||
|
@ -83,6 +83,7 @@ typedef struct soinfo soinfo;
|
|||||||
#define FLAG_LINKED 0x00000001
|
#define FLAG_LINKED 0x00000001
|
||||||
#define FLAG_ERROR 0x00000002
|
#define FLAG_ERROR 0x00000002
|
||||||
#define FLAG_EXE 0x00000004 // The main executable
|
#define FLAG_EXE 0x00000004 // The main executable
|
||||||
|
#define FLAG_LINKER 0x00000010 // The linker itself
|
||||||
|
|
||||||
#define SOINFO_NAME_LEN 128
|
#define SOINFO_NAME_LEN 128
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user