linker: handle R_ARM_COPY relocations in a proper way

If an executable contain copy relocations, other references
to the symbol it points to should be preempted and made to
point to the copy instead.

Also, the linker should make sure the target area has
sufficient space to contain the copy. It also checks
whether the library that supplies the symbol is built
with -Bsymbolic, and errors out if this is the case.

Change-Id: If135c83590092741cfd8f82f54816f363a4a4a3b
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@gmail.com>
This commit is contained in:
Ard Biesheuvel 2012-08-30 12:48:32 +02:00 committed by Elliott Hughes
parent 155e8d1df5
commit 5ae44f302b
3 changed files with 79 additions and 57 deletions

View File

@ -236,5 +236,7 @@ soinfo libdl_info = {
refcount: 0,
{ l_addr: 0, l_name: 0, l_ld: 0, l_next: 0, l_prev: 0, },
constructors_called: 0, load_bias: 0, has_text_relocations: 0,
constructors_called: 0, load_bias: 0,
has_text_relocations: false,
has_DT_SYMBOLIC: true,
};

View File

@ -51,7 +51,6 @@
#include "linker_format.h"
#include "linker_phdr.h"
#define ALLOW_SYMBOLS_FROM_MAIN 1
#define SO_MAX 128
/* Assume average path length of 64 and max 8 paths */
@ -89,9 +88,7 @@ static soinfo sopool[SO_MAX];
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
static char ldpaths_buf[LDPATH_BUFSIZE];
static const char *ldpaths[LDPATH_MAX + 1];
@ -421,15 +418,31 @@ static unsigned elfhash(const char *_name)
}
static Elf32_Sym *
soinfo_do_lookup(soinfo *si, const char *name, Elf32_Addr *offset,
soinfo *needed[], bool ignore_local)
soinfo_do_lookup(soinfo *si, const char *name, soinfo **lsi,
soinfo *needed[])
{
unsigned elf_hash = elfhash(name);
Elf32_Sym *s = NULL;
soinfo *lsi = si;
int i;
if (!ignore_local) {
if (si != NULL) {
/*
* If this object was built with symbolic relocations disabled, the
* first place to look to resolve external references is the main
* executable.
*/
if (!si->has_DT_SYMBOLIC) {
DEBUG("%5d %s: looking up %s in executable %s\n",
pid, si->name, name, somain->name);
s = soinfo_elf_lookup(somain, elf_hash, name);
if (s != NULL) {
*lsi = somain;
goto done;
}
}
/* Look for symbols in the local scope (the object who is
* searching). This happens with C++ templates on i386 for some
* reason.
@ -441,47 +454,37 @@ soinfo_do_lookup(soinfo *si, const char *name, Elf32_Addr *offset,
* Here we return the first definition found for simplicity. */
s = soinfo_elf_lookup(si, elf_hash, name);
if(s != NULL)
if (s != NULL) {
*lsi = si;
goto done;
}
}
/* Next, look for it in the preloads list */
for(i = 0; preloads[i] != NULL; i++) {
lsi = preloads[i];
s = soinfo_elf_lookup(lsi, elf_hash, name);
if(s != NULL)
s = soinfo_elf_lookup(preloads[i], elf_hash, name);
if(s != NULL) {
*lsi = preloads[i];
goto done;
}
}
for(i = 0; needed[i] != NULL; i++) {
lsi = needed[i];
DEBUG("%5d %s: looking up %s in %s\n",
pid, si->name, name, lsi->name);
s = soinfo_elf_lookup(lsi, elf_hash, name);
if (s != NULL)
pid, si->name, name, needed[i]->name);
s = soinfo_elf_lookup(needed[i], elf_hash, name);
if (s != NULL) {
*lsi = needed[i];
goto done;
}
}
#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 = soinfo_elf_lookup(lsi, elf_hash, name);
}
#endif
done:
if(s != NULL) {
TRACE_TYPE(LOOKUP, "%5d si %s sym %s s->st_value = 0x%08x, "
"found in %s, base = 0x%08x, load bias = 0x%08x\n",
pid, si->name, name, s->st_value,
lsi->name, lsi->base, lsi->load_bias);
*offset = lsi->load_bias;
(*lsi)->name, (*lsi)->base, (*lsi)->load_bias);
return s;
}
@ -863,13 +866,8 @@ soinfo *find_library(const char *name)
{
soinfo *si;
#if ALLOW_SYMBOLS_FROM_MAIN
if (name == NULL)
return somain;
#else
if (name == NULL)
return NULL;
#endif
si = find_loaded_library(name);
if (si != NULL) {
@ -933,8 +931,8 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count,
Elf32_Sym *symtab = si->symtab;
const char *strtab = si->strtab;
Elf32_Sym *s;
Elf32_Addr offset;
Elf32_Rel *start = rel;
soinfo *lsi;
for (size_t idx = 0; idx < count; ++idx, ++rel) {
unsigned type = ELF32_R_TYPE(rel->r_info);
@ -950,11 +948,7 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count,
}
if(sym != 0) {
sym_name = (char *)(strtab + symtab[sym].st_name);
bool ignore_local = false;
#if defined(ANDROID_ARM_LINKER)
ignore_local = (type == R_ARM_COPY);
#endif
s = soinfo_do_lookup(si, sym_name, &offset, needed, ignore_local);
s = soinfo_do_lookup(si, sym_name, &lsi, needed);
if(s == NULL) {
/* We only allow an undefined symbol if this is a weak
reference.. */
@ -1020,7 +1014,7 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count,
return -1;
}
#endif
sym_addr = (unsigned)(s->st_value + offset);
sym_addr = (unsigned)(s->st_value + lsi->load_bias);
}
count_relocation(kRelocSymbol);
} else {
@ -1154,10 +1148,27 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count,
TRACE_TYPE(RELO, "%5d RELO %08x <- %d @ %08x %s\n", pid,
reloc, s->st_size, sym_addr, sym_name);
if (reloc == sym_addr) {
DL_ERR("Internal linker error detected. reloc == symaddr");
Elf32_Sym *src = soinfo_do_lookup(NULL, sym_name, &lsi, needed);
if (src == NULL) {
DL_ERR("%s R_ARM_COPY relocation source cannot be resolved", si->name);
return -1;
}
if (lsi->has_DT_SYMBOLIC) {
DL_ERR("%s invalid R_ARM_COPY relocation against DT_SYMBOLIC shared "
"library %s (built with -Bsymbolic?)", si->name, lsi->name);
return -1;
}
if (s->st_size < src->st_size) {
DL_ERR("%s R_ARM_COPY relocation size mismatch (%d < %d)",
si->name, s->st_size, src->st_size);
return -1;
}
memcpy((void*)reloc, (void*)(src->st_value + lsi->load_bias), src->st_size);
} else {
DL_ERR("%s R_ARM_COPY relocation target cannot be resolved", si->name);
return -1;
}
memcpy((void*)reloc, (void*)sym_addr, s->st_size);
break;
#endif /* ANDROID_ARM_LINKER */
@ -1210,12 +1221,12 @@ static int mips_relocate_got(soinfo* si, soinfo* needed[]) {
got = si->plt_got + local_gotno;
for (g = gotsym; g < symtabno; g++, sym++, got++) {
const char *sym_name;
unsigned base;
Elf32_Sym *s;
soinfo *lsi;
/* This is an undefined reference... try to locate it */
sym_name = si->strtab + sym->st_name;
s = soinfo_do_lookup(si, sym_name, &base, needed, false);
s = soinfo_do_lookup(si, sym_name, &lsi, needed);
if (s == NULL) {
/* We only allow an undefined symbol if this is a weak
reference.. */
@ -1231,7 +1242,7 @@ static int mips_relocate_got(soinfo* si, soinfo* needed[]) {
* For reference see NetBSD link loader
* http://cvsweb.netbsd.org/bsdweb.cgi/src/libexec/ld.elf_so/arch/mips/mips_reloc.c?rev=1.53&content-type=text/x-cvsweb-markup
*/
*got = base + s->st_value;
*got = lsi->load_bias + s->st_value;
}
}
return 0;
@ -1537,6 +1548,19 @@ static int soinfo_link_image(soinfo *si)
case DT_TEXTREL:
si->has_text_relocations = true;
break;
case DT_SYMBOLIC:
si->has_DT_SYMBOLIC = true;
break;
#if defined(DT_FLAGS)
case DT_FLAGS:
if (*d & DF_TEXTREL) {
si->has_text_relocations = true;
}
if (*d & DF_SYMBOLIC) {
si->has_DT_SYMBOLIC = true;
}
break;
#endif
#if defined(ANDROID_MIPS_LINKER)
case DT_NEEDED:
case DT_STRSZ:
@ -1868,6 +1892,8 @@ static unsigned __linker_init_post_relocation(unsigned **elfdata, unsigned linke
parse_LD_LIBRARY_PATH(ldpath_env);
parse_LD_PRELOAD(ldpreload_env);
somain = si;
if(soinfo_link_image(si)) {
char errmsg[] = "CANNOT LINK EXECUTABLE\n";
write(2, __linker_dl_err_buf, strlen(__linker_dl_err_buf));
@ -1889,14 +1915,6 @@ static unsigned __linker_init_post_relocation(unsigned **elfdata, unsigned linke
map->l_addr = si->base;
soinfo_call_constructors(si);
#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) (

View File

@ -160,7 +160,9 @@ struct soinfo {
/* When you read a virtual address from the ELF file, add this
* value to get the corresponding address in the process' address space */
Elf32_Addr load_bias;
int has_text_relocations;
bool has_text_relocations;
bool has_DT_SYMBOLIC;
};