From ec83a61c8b5e00c67c35c9b8f72031c55e7868b9 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Sun, 26 Jul 2015 07:37:09 -0700 Subject: [PATCH] Restore protection flags for ifunc during relocs. IFUNC relocations require executable flag for the load segment containing .text. When dso has text relocs linker removes exec which causes crash during ifunc relocations. This patch fixes this problem by restoring segments protection for ifunc relocs. Bug: http://b/22611399 Change-Id: Icbf3be0fec0e42bf805bcad7533e2032a2e11b9c (cherry picked from commit de0fb393ae8136a5958fe17eee0c6285e2f7f91a) --- linker/linker.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/linker/linker.cpp b/linker/linker.cpp index 0718efcd7..375b53419 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -1999,9 +1999,32 @@ bool soinfo::relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& r DL_ERR("unknown weak reloc type %d @ %p (%zu)", type, rel, idx); return false; } - } else { - // We got a definition. + } else { // We got a definition. +#if !defined(__LP64__) + // When relocating dso with text_relocation .text segment is + // not executable. We need to restore elf flags before resolving + // STT_GNU_IFUNC symbol. + bool protect_segments = has_text_relocations && + lsi == this && + ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC; + if (protect_segments) { + if (phdr_table_protect_segments(phdr, phnum, load_bias) < 0) { + DL_ERR("can't protect segments for \"%s\": %s", + get_realpath(), strerror(errno)); + return false; + } + } +#endif sym_addr = lsi->resolve_symbol_address(s); +#if !defined(__LP64__) + if (protect_segments) { + if (phdr_table_unprotect_segments(phdr, phnum, load_bias) < 0) { + DL_ERR("can't unprotect loadable segments for \"%s\": %s", + get_realpath(), strerror(errno)); + return false; + } + } +#endif } count_relocation(kRelocSymbol); } @@ -2038,7 +2061,32 @@ bool soinfo::relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& r TRACE_TYPE(RELO, "RELO IRELATIVE %16p <- %16p\n", reinterpret_cast(reloc), reinterpret_cast(load_bias + addend)); - *reinterpret_cast(reloc) = call_ifunc_resolver(load_bias + addend); + { +#if !defined(__LP64__) + // When relocating dso with text_relocation .text segment is + // not executable. We need to restore elf flags for this + // particular call. + if (has_text_relocations) { + if (phdr_table_protect_segments(phdr, phnum, load_bias) < 0) { + DL_ERR("can't protect segments for \"%s\": %s", + get_realpath(), strerror(errno)); + return false; + } + } +#endif + ElfW(Addr) ifunc_addr = call_ifunc_resolver(load_bias + addend); +#if !defined(__LP64__) + // Unprotect it afterwards... + if (has_text_relocations) { + if (phdr_table_unprotect_segments(phdr, phnum, load_bias) < 0) { + DL_ERR("can't unprotect loadable segments for \"%s\": %s", + get_realpath(), strerror(errno)); + return false; + } + } +#endif + *reinterpret_cast(reloc) = ifunc_addr; + } break; #if defined(__aarch64__)