diff options
author | Brigid Smith <brigidsmith@google.com> | 2014-07-23 11:22:25 -0700 |
---|---|---|
committer | Brigid Smith <brigidsmith@google.com> | 2014-08-08 11:29:35 -0700 |
commit | c5a13efa9bc4264be0a9a9e37c00633af01584ed (patch) | |
tree | 4a1678bd154220d7e8d13e6739037fb48635006a /linker/linker.cpp | |
parent | f2d8c357eec1bbc4e7441942dfc338ad1d9a207a (diff) | |
download | android_bionic-c5a13efa9bc4264be0a9a9e37c00633af01584ed.tar.gz android_bionic-c5a13efa9bc4264be0a9a9e37c00633af01584ed.tar.bz2 android_bionic-c5a13efa9bc4264be0a9a9e37c00633af01584ed.zip |
Added test for ifunc support in dynamic linker.
ifuncs now work in i386 and x86_64 when called in the same library as
well as in a different library.
Bug:6657325
Change-Id: Ic0c48b1b0a76cb90f36c20c79f68294cc3fd44a1
Diffstat (limited to 'linker/linker.cpp')
-rw-r--r-- | linker/linker.cpp | 114 |
1 files changed, 112 insertions, 2 deletions
diff --git a/linker/linker.cpp b/linker/linker.cpp index f8b35d7a8..b0f0e4dfe 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -476,6 +476,29 @@ static ElfW(Sym)* soinfo_elf_lookup(soinfo* si, unsigned hash, const char* name, return NULL; } +static void resolve_ifunc_symbols(soinfo* si) { + + phdr_table_unprotect_segments(si->phdr, si->phnum, si->load_bias); + + TRACE_TYPE(IFUNC, "CHECKING FOR IFUNCS AND PERFORMING SYMBOL UPDATES"); + + for (size_t i = 0; i < si->nchain; ++i) { + ElfW(Sym)* s = &si->symtab[i]; + if (ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC) { + // The address of the ifunc in the symbol table is the address of the + // function that chooses the function to which the ifunc will refer. + // In order to return the proper value, we run the choosing function + // in the linker and then return its result (minus the base offset). + TRACE_TYPE(IFUNC, "FOUND IFUNC"); + ElfW(Addr) (*ifunc_ptr)(); + ifunc_ptr = reinterpret_cast<ElfW(Addr)(*)()>(s->st_value + si->base); + s->st_value = (ifunc_ptr() - si->base); + TRACE_TYPE(IFUNC, "NEW VALUE IS %p", (void*)s->st_value); + } + } + phdr_table_protect_segments(si->phdr, si->phnum, si->load_bias); +} + static unsigned elfhash(const char* _name) { const unsigned char* name = reinterpret_cast<const unsigned char*>(_name); unsigned h = 0, g; @@ -790,6 +813,10 @@ static soinfo* load_library(const char* name, int dlflags, const android_dlextin return NULL; } + // if the library has any ifuncs, we will need to resolve them so that dlsym + // can handle them properly + resolve_ifunc_symbols(si); + return si; } @@ -903,6 +930,53 @@ void do_dlclose(soinfo* si) { protect_data(PROT_READ); } +// ifuncs are only defined for x86 +#if defined(__i386__) +static void soinfo_ifunc_relocate(soinfo* si, ElfW(Rel)* rel, unsigned count, soinfo* needed[]) { + for (size_t idx = 0; idx < count; ++idx, ++rel) { + ElfW(Sym)* s; + soinfo* lsi; + unsigned type = ELFW(R_TYPE)(rel->r_info); + unsigned sym = ELFW(R_SYM)(rel->r_info); + ElfW(Addr) reloc = static_cast<ElfW(Addr)>(rel->r_offset + si->load_bias); + ElfW(Addr) sym_addr = 0; + const char* sym_name = NULL; + sym_name = reinterpret_cast<const char*>(si->strtab + si->symtab[sym].st_name); + s = soinfo_do_lookup(si, sym_name, &lsi, needed); + + if (ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC && type == R_386_JMP_SLOT) { + TRACE("IFUNC RELOCATION, PASS 2: %p", (void*)(sym_addr)); + ElfW(Addr) (*ifunc_ptr)(); + ifunc_ptr = reinterpret_cast<ElfW(Addr)(*)()>(s->st_value + si->base); + *reinterpret_cast<ElfW(Addr)*>(reloc) = ifunc_ptr(); + } + } +} +#endif + +#if defined(__x86_64__) +static void soinfo_ifunc_relocate(soinfo* si, ElfW(Rela)* rela, unsigned count, soinfo* needed[]) { + for (size_t idx = 0; idx < count; ++idx, ++rela) { + ElfW(Sym)* s; + soinfo* lsi; + unsigned type = ELFW(R_TYPE)(rela->r_info); + unsigned sym = ELFW(R_SYM)(rela->r_info); + ElfW(Addr) reloc = static_cast<ElfW(Addr)>(rela->r_offset + si->load_bias); + ElfW(Addr) sym_addr = 0; + const char* sym_name = NULL; + sym_name = reinterpret_cast<const char*>(si->strtab + si->symtab[sym].st_name); + s = soinfo_do_lookup(si, sym_name, &lsi, needed); + + if (ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC && type == R_X86_64_JUMP_SLOT) { + TRACE("IFUNC RELOCATION, PASS 2: %p", (void*)(sym_addr + rela->r_addend)); + ElfW(Addr) (*ifunc_ptr)(); + ifunc_ptr = reinterpret_cast<ElfW(Addr)(*)()>(s->st_value + si->base); + *reinterpret_cast<ElfW(Addr)*>(reloc) = ifunc_ptr(); + } + } +} +#endif + #if defined(USE_RELA) static int soinfo_relocate(soinfo* si, ElfW(Rela)* rela, unsigned count, soinfo* needed[]) { ElfW(Sym)* s; @@ -1114,7 +1188,11 @@ static int soinfo_relocate(soinfo* si, ElfW(Rela)* rela, unsigned count, soinfo* MARK(rela->r_offset); TRACE_TYPE(RELO, "RELO JMP_SLOT %08zx <- %08zx %s", static_cast<size_t>(reloc), static_cast<size_t>(sym_addr + rela->r_addend), sym_name); - *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + rela->r_addend; + if (ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC) { + si->set_has_ifuncs(true); + } else { + *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + rela->r_addend; + } break; case R_X86_64_GLOB_DAT: count_relocation(kRelocAbsolute); @@ -1293,7 +1371,11 @@ static int soinfo_relocate(soinfo* si, ElfW(Rel)* rel, unsigned count, soinfo* n count_relocation(kRelocAbsolute); MARK(rel->r_offset); TRACE_TYPE(RELO, "RELO JMP_SLOT %08x <- %08x %s", reloc, sym_addr, sym_name); - *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr; + if (ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC) { + si->set_has_ifuncs(true); + } else { + *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr; + } break; case R_386_GLOB_DAT: count_relocation(kRelocAbsolute); @@ -1553,6 +1635,14 @@ void soinfo::set_st_ino(ino_t ino) { st_ino = ino; } +void soinfo::set_has_ifuncs(bool ifuncs) { + if ((this->flags & FLAG_NEW_SOINFO) == 0) { + return; + } + + has_ifuncs = ifuncs; +} + dev_t soinfo::get_st_dev() { if ((this->flags & FLAG_NEW_SOINFO) == 0) { return 0; @@ -1569,6 +1659,14 @@ ino_t soinfo::get_st_ino() { return st_ino; } +bool soinfo::get_has_ifuncs() { + if ((this->flags & FLAG_NEW_SOINFO) == 0) { + return false; + } + + return has_ifuncs; +} + // This is a return on get_children() in case // 'this->flags' does not have FLAG_NEW_SOINFO set. static soinfo::soinfo_list_t g_empty_list; @@ -1953,6 +2051,18 @@ static bool soinfo_link_image(soinfo* si, const android_dlextinfo* extinfo) { } #endif + // if there are ifuncs, we need to do an additional relocation pass. + // they cannot be resolved until the rest of the relocations are done + // because we need to call the resolution function which may be waiting + // on relocations. + if(si->get_has_ifuncs()) { +#if defined(__i386__) + soinfo_ifunc_relocate(si, si->plt_rel, si->plt_rel_count, needed); +#elif defined(__x86_64__) + soinfo_ifunc_relocate(si, si->plt_rela, si->plt_rela_count, needed); +#endif + } + #if defined(__mips__) if (!mips_relocate_got(si, needed)) { return false; |