diff options
-rw-r--r-- | linker/linker.cpp | 54 |
1 files changed, 51 insertions, 3 deletions
diff --git a/linker/linker.cpp b/linker/linker.cpp index e84036a..b860f70 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -1921,9 +1921,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); } @@ -1960,7 +1983,32 @@ bool soinfo::relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& r TRACE_TYPE(RELO, "RELO IRELATIVE %16p <- %16p\n", reinterpret_cast<void*>(reloc), reinterpret_cast<void*>(load_bias + addend)); - *reinterpret_cast<ElfW(Addr)*>(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<ElfW(Addr)*>(reloc) = ifunc_addr; + } break; #if defined(__aarch64__) |