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:
parent
155e8d1df5
commit
5ae44f302b
@ -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,
|
||||
};
|
||||
|
@ -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) (
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user