diff --git a/linker/linker.cpp b/linker/linker.cpp index 59b789314..e599b5b23 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -93,12 +93,6 @@ static soinfo *somain; /* main process, always the one after libdl_info */ #endif -static inline int validate_soinfo(soinfo *si) -{ - return (si >= sopool && si < sopool + SO_MAX) || - si == &libdl_info; -} - static char ldpaths_buf[LDPATH_BUFSIZE]; static const char *ldpaths[LDPATH_MAX + 1]; @@ -427,11 +421,11 @@ static unsigned elfhash(const char *_name) } static Elf32_Sym * -soinfo_do_lookup(soinfo *si, const char *name, Elf32_Addr *offset) +soinfo_do_lookup(soinfo *si, const char *name, Elf32_Addr *offset, + soinfo *needed[]) { unsigned elf_hash = elfhash(name); Elf32_Sym *s; - unsigned *d; soinfo *lsi = si; int i; @@ -457,20 +451,13 @@ soinfo_do_lookup(soinfo *si, const char *name, Elf32_Addr *offset) goto done; } - for(d = si->dynamic; *d; d += 2) { - if(d[0] == DT_NEEDED){ - lsi = (soinfo *)d[1]; - if (!validate_soinfo(lsi)) { - DL_ERR("bad DT_NEEDED pointer in \"%s\"", lsi->name); - return NULL; - } - - 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) - 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) + goto done; } #if ALLOW_SYMBOLS_FROM_MAIN @@ -857,11 +844,29 @@ init_library(soinfo *si) return si; } -soinfo *find_library(const char *name) +static soinfo *find_loaded_library(const char *name) { soinfo *si; const char *bname; + // TODO: don't use basename only for determining libraries + // http://code.google.com/p/android/issues/detail?id=6670 + + bname = strrchr(name, '/'); + bname = bname ? bname + 1 : name; + + for(si = solist; si != NULL; si = si->next){ + if(!strcmp(bname, si->name)) { + return si; + } + } + return NULL; +} + +soinfo *find_library(const char *name) +{ + soinfo *si; + #if ALLOW_SYMBOLS_FROM_MAIN if (name == NULL) return somain; @@ -870,19 +875,15 @@ soinfo *find_library(const char *name) return NULL; #endif - bname = strrchr(name, '/'); - bname = bname ? bname + 1 : name; - - for(si = solist; si != 0; si = si->next){ - if(!strcmp(bname, si->name)) { - if(si->flags & FLAG_ERROR) { - DL_ERR("\"%s\" failed to load previously", bname); - return NULL; - } - if(si->flags & FLAG_LINKED) return si; - DL_ERR("OOPS: recursive link to \"%s\"", si->name); + si = find_loaded_library(name); + if (si != NULL) { + if(si->flags & FLAG_ERROR) { + DL_ERR("\"%s\" failed to load previously", name); return NULL; } + if(si->flags & FLAG_LINKED) return si; + DL_ERR("OOPS: recursive link to \"%s\"", si->name); + return NULL; } TRACE("[ %5d '%s' has not been loaded yet. Locating...]\n", pid, name); @@ -903,27 +904,11 @@ unsigned soinfo_unload(soinfo *si) TRACE("%5d unloading '%s'\n", pid, si->name); call_destructors(si); - /* - * Make sure that we undo the PT_GNU_RELRO protections we added - * in soinfo_link_image. This is needed to undo the DT_NEEDED hack below. - */ - if (phdr_table_unprotect_gnu_relro(si->phdr, si->phnum, - si->load_bias) < 0) { - DL_ERR("%s: could not undo GNU_RELRO protections. " - "Expect a crash soon. errno=%d (%s)", - si->name, errno, strerror(errno)); - } - for(d = si->dynamic; *d; d += 2) { if(d[0] == DT_NEEDED){ - soinfo *lsi = (soinfo *)d[1]; + soinfo *lsi = find_loaded_library(si->strtab + d[1]); - // The next line will segfault if the we don't undo the - // PT_GNU_RELRO protections (see comments above and in - // soinfo_link_image(). - d[1] = 0; - - if (validate_soinfo(lsi)) { + if (lsi) { TRACE("%5d %s needs to unload %s\n", pid, si->name, lsi->name); soinfo_unload(lsi); @@ -951,7 +936,8 @@ unsigned soinfo_unload(soinfo *si) * ideal. They should probably be either uint32_t, Elf32_Addr, or unsigned * long. */ -static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count) +static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count, + soinfo *needed[]) { Elf32_Sym *symtab = si->symtab; const char *strtab = si->strtab; @@ -973,7 +959,7 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count) } if(sym != 0) { sym_name = (char *)(strtab + symtab[sym].st_name); - s = soinfo_do_lookup(si, sym_name, &offset); + s = soinfo_do_lookup(si, sym_name, &offset, needed); if(s == NULL) { /* We only allow an undefined symbol if this is a weak reference.. */ @@ -1171,7 +1157,7 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count) } #ifdef ANDROID_MIPS_LINKER -int mips_relocate_got(struct soinfo *si) +int mips_relocate_got(struct soinfo *si, soinfo *needed[]) { unsigned *got; unsigned local_gotno, gotsym, symtabno; @@ -1216,7 +1202,7 @@ int mips_relocate_got(struct soinfo *si) /* This is an undefined reference... try to locate it */ sym_name = si->strtab + sym->st_name; - s = soinfo_do_lookup(si, sym_name, &base); + s = soinfo_do_lookup(si, sym_name, &base, needed); if (s == NULL) { /* We only allow an undefined symbol if this is a weak reference.. */ @@ -1312,9 +1298,10 @@ void soinfo_call_constructors(soinfo *si) unsigned *d; for(d = si->dynamic; *d; d += 2) { if(d[0] == DT_NEEDED){ - soinfo* lsi = (soinfo *)d[1]; - if (!validate_soinfo(lsi)) { - DL_ERR("bad DT_NEEDED pointer in \"%s\"", si->name); + soinfo* lsi = find_loaded_library(si->strtab + d[1]); + if (!lsi) { + DL_ERR("\"%s\": could not initialize dependent library", + si->name); } else { soinfo_call_constructors(lsi); } @@ -1425,6 +1412,8 @@ static int soinfo_link_image(soinfo *si) const Elf32_Phdr *phdr = si->phdr; int phnum = si->phnum; int relocating_linker = (si->flags & FLAG_LINKER) != 0; + soinfo **needed, **pneeded; + size_t dynamic_count; /* We can't debug anything until the linker is relocated */ if (!relocating_linker) { @@ -1434,7 +1423,8 @@ static int soinfo_link_image(soinfo *si) } /* Extract dynamic section */ - si->dynamic = phdr_table_get_dynamic_section(phdr, phnum, base); + phdr_table_get_dynamic_section(phdr, phnum, base, &si->dynamic, + &dynamic_count); if (si->dynamic == NULL) { if (!relocating_linker) { DL_ERR("missing PT_DYNAMIC?!"); @@ -1607,6 +1597,9 @@ static int soinfo_link_image(soinfo *si) } } + /* dynamic_count is an upper bound for the number of needed libs */ + pneeded = needed = (soinfo**) alloca((1 + dynamic_count) * sizeof(soinfo*)); + for(d = si->dynamic; *d; d += 2) { if(d[0] == DT_NEEDED){ DEBUG("%5d %s needs %s\n", pid, si->name, si->strtab + d[1]); @@ -1617,17 +1610,11 @@ static int soinfo_link_image(soinfo *si) si->strtab + d[1], si->name, tmp_err_buf); goto fail; } - /* Save the soinfo of the loaded DT_NEEDED library in the payload - of the DT_NEEDED entry itself, so that we can retrieve the - soinfo directly later from the dynamic segment. This is a hack, - but it allows us to map from DT_NEEDED to soinfo efficiently - later on when we resolve relocations, trying to look up a symbol - with dlsym(). - */ - d[1] = (unsigned)lsi; + *pneeded++ = lsi; lsi->refcount++; } } + *pneeded = NULL; if (si->has_text_relocations) { /* Unprotect the segments, i.e. make them writable, to allow @@ -1644,17 +1631,17 @@ static int soinfo_link_image(soinfo *si) if(si->plt_rel) { DEBUG("[ %5d relocating %s plt ]\n", pid, si->name ); - if(soinfo_relocate(si, si->plt_rel, si->plt_rel_count)) + if(soinfo_relocate(si, si->plt_rel, si->plt_rel_count, needed)) goto fail; } if(si->rel) { DEBUG("[ %5d relocating %s ]\n", pid, si->name ); - if(soinfo_relocate(si, si->rel, si->rel_count)) + if(soinfo_relocate(si, si->rel, si->rel_count, needed)) goto fail; } #ifdef ANDROID_MIPS_LINKER - if(mips_relocate_got(si)) { + if(mips_relocate_got(si, needed)) { goto fail; } #endif @@ -1849,8 +1836,8 @@ sanitize: Elf32_Ehdr *elf_hdr = (Elf32_Ehdr *) linker_base; Elf32_Phdr *phdr = (Elf32_Phdr *)((unsigned char *) linker_base + elf_hdr->e_phoff); - linker_soinfo.dynamic = - phdr_table_get_dynamic_section(phdr, elf_hdr->e_phnum, linker_base); + phdr_table_get_dynamic_section(phdr, elf_hdr->e_phnum, linker_base, + &linker_soinfo.dynamic, NULL); insert_soinfo_into_debug_map(&linker_soinfo); /* extract information passed from the kernel */ diff --git a/linker/linker_phdr.c b/linker/linker_phdr.c index beb756fef..19ed7ba08 100644 --- a/linker/linker_phdr.c +++ b/linker/linker_phdr.c @@ -468,11 +468,9 @@ _phdr_table_set_gnu_relro_prot(const Elf32_Phdr* phdr_table, * specified by one or more PT_GNU_RELRO segments. This must be always * performed after relocations. * - * NOTE: One must call phdr_table_unprotect_gnu_relro() before calling - * the library's destructors, in order to ensure that the .dynamic - * section is writable (as well as the .data.relro section that - * might contain the content of static constant C++ objects that - * needs to be destroyed). + * The areas typically covered are .got and .data.rel.ro, these are + * read-only from the program's POV, but contain absolute addresses + * that need to be relocated before use. * * Input: * phdr_table -> program header table @@ -492,27 +490,6 @@ phdr_table_protect_gnu_relro(const Elf32_Phdr* phdr_table, PROT_READ); } -/* Un-apply GNU relro protection if specified by the program header. - * See comment for phdr_table_protect_gnu_relro. - * - * Input: - * phdr_table -> program header table - * phdr_count -> number of entires in tables - * load_bias -> load bias - * Return: - * 0 on error, -1 on failure (error code in errno). - */ -int -phdr_table_unprotect_gnu_relro(const Elf32_Phdr* phdr_table, - int phdr_count, - Elf32_Addr load_bias) -{ - return _phdr_table_set_gnu_relro_prot(phdr_table, - phdr_count, - load_bias, - PROT_READ|PROT_WRITE); -} - #ifdef ANDROID_ARM_LINKER # ifndef PT_ARM_EXIDX @@ -556,30 +533,44 @@ phdr_table_get_arm_exidx(const Elf32_Phdr* phdr_table, } #endif /* ANDROID_ARM_LINKER */ -/* Return the address of the ELF file's .dynamic section in memory, +/* Return the address and size of the ELF file's .dynamic section in memory, * or NULL if missing. * * Input: * phdr_table -> program header table * phdr_count -> number of entires in tables * load_bias -> load bias + * Output: + * dynamic -> address of table in memory (NULL on failure). + * dynamic_count -> number of items in table (0 on failure). * Return: - * 0 on error, -1 on failure (_no_ error code in errno) + * void */ -Elf32_Addr* +void phdr_table_get_dynamic_section(const Elf32_Phdr* phdr_table, int phdr_count, - Elf32_Addr load_bias) + Elf32_Addr load_bias, + Elf32_Addr** dynamic, + size_t* dynamic_count) { const Elf32_Phdr* phdr = phdr_table; const Elf32_Phdr* phdr_limit = phdr + phdr_count; for (phdr = phdr_table; phdr < phdr_limit; phdr++) { - if (phdr->p_type == PT_DYNAMIC) { - return (Elf32_Addr*)(load_bias + phdr->p_vaddr); + if (phdr->p_type != PT_DYNAMIC) { + continue; } + + *dynamic = (Elf32_Addr*)(load_bias + phdr->p_vaddr); + if (dynamic_count) { + *dynamic_count = (unsigned)(phdr->p_memsz / 8); + } + return; + } + *dynamic = NULL; + if (dynamic_count) { + *dynamic_count = 0; } - return NULL; } /* Return the address of the program header table as it appears in the loaded diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h index 753c7e76d..a75926298 100644 --- a/linker/linker_phdr.h +++ b/linker/linker_phdr.h @@ -86,11 +86,6 @@ phdr_table_protect_gnu_relro(const Elf32_Phdr* phdr_table, int phdr_count, Elf32_Addr load_bias); -int -phdr_table_unprotect_gnu_relro(const Elf32_Phdr* phdr_table, - int phdr_count, - Elf32_Addr load_bias); - const Elf32_Phdr* phdr_table_get_loaded_phdr(const Elf32_Phdr* phdr_table, int phdr_count, @@ -105,10 +100,12 @@ phdr_table_get_arm_exidx(const Elf32_Phdr* phdr_table, unsigned* arm_exidix_count); #endif -Elf32_Addr* +void phdr_table_get_dynamic_section(const Elf32_Phdr* phdr_table, int phdr_count, - Elf32_Addr load_bias); + Elf32_Addr load_bias, + Elf32_Addr** dynamic, + size_t* dynamic_count); #ifdef __cplusplus };