summaryrefslogtreecommitdiffstats
path: root/linker/linker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'linker/linker.cpp')
-rw-r--r--linker/linker.cpp54
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__)