diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp index c77b9a38b..c87d29a60 100644 --- a/linker/dlfcn.cpp +++ b/linker/dlfcn.cpp @@ -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, }; diff --git a/linker/linker.cpp b/linker/linker.cpp index 4c52f3d58..9e4138e15 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -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) ( diff --git a/linker/linker.h b/linker/linker.h index 54563bb80..cb33602bf 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -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; };