From: Waldemar Kozaczuk <jwkozac...@gmail.com>
Committer: Nadav Har'El <n...@scylladb.com>
Branch: master

dynamic linker: handle *IRELATIVE relocations

The OSv dynamic linker handles X_*_RELATIVE relocations which
are resolved as base + addend. The X_*_IRELATIVE relocations on other
hand need to be resolved by calling a function located at "base + addend".

So this patch enhances the dynamic linker to resolve the IRELATIVE
relocations when processing both PLT and RELA sections according to this 
formula:

*static_cast<void**>(addr) = reinterpret_cast<void *(*)()>(_base + addend)()

Sometimes the function invoked to calculate the address when resolving an
IRELATIVE relocation has to be relocated before. For that reason we change
the order in relocate() and relocate PLT before RELA and also set 
elf_resolve_pltgot
pointer before processing the relocations in relocate_pltgot().

This patch also adds a unit test which in built as pie and contains IRELATIVE
relocations:

Relocation section '.rela.dyn' at offset 0x570 contains 9 entries:
    Offset             Info             Type               Symbol's Value  
Symbol's Name + Addend
...
0000000000004010  0000000000000025 R_X86_64_IRELATIVE                        
11d0

Relocation section '.rela.plt' at offset 0x648 contains 3 entries:
    Offset             Info             Type               Symbol's Value  
Symbol's Name + Addend
...
0000000000003fd0  0000000000000025 R_X86_64_IRELATIVE                        
11d0

The unit test is based on the example from here - 
https://sourceware.org/bugzilla/show_bug.cgi?id=28218
- which explains some edge cases. There is also this one - 
https://sourceware.org/bugzilla/show_bug.cgi?id=13302.

Finally, please note we are adding IRELATIVE handling logic primarily in 
anticipation of supporting
statically linked executables. But also one of the users tried to run a 
dynamically linked executable
that had IRELATIVE relocation.

Signed-off-by: Waldemar Kozaczuk <jwkozac...@gmail.com>

Closes #1245

---
diff --git a/arch/aarch64/arch-elf.cc b/arch/aarch64/arch-elf.cc
--- a/arch/aarch64/arch-elf.cc
+++ b/arch/aarch64/arch-elf.cc
@@ -59,6 +59,9 @@ bool object::arch_relocate_rela(u32 type, u32 sym, void *addr,
     case R_AARCH64_RELATIVE:
         *static_cast<void**>(addr) = _base + addend;
         break;
+    case R_AARCH64_IRELATIVE:
+        *static_cast<void**>(addr) = reinterpret_cast<void *(*)()>(_base + 
addend)();
+        break;
     case R_AARCH64_TLS_TPREL64:
         if (sym) {
             auto sm = symbol(sym);
diff --git a/arch/aarch64/arch-elf.hh b/arch/aarch64/arch-elf.hh
--- a/arch/aarch64/arch-elf.hh
+++ b/arch/aarch64/arch-elf.hh
@@ -19,6 +19,7 @@ enum {
 /* for pltgot relocation */
 #define ARCH_JUMP_SLOT R_AARCH64_JUMP_SLOT
 #define ARCH_TLSDESC R_AARCH64_TLSDESC
+#define ARCH_IRELATIVE R_AARCH64_IRELATIVE
 
 #define ELF_KERNEL_MACHINE_TYPE 183
 
diff --git a/arch/x64/arch-elf.cc b/arch/x64/arch-elf.cc
--- a/arch/x64/arch-elf.cc
+++ b/arch/x64/arch-elf.cc
@@ -88,6 +88,9 @@ bool object::arch_relocate_rela(u32 type, u32 sym, void *addr,
     case R_X86_64_RELATIVE:
         *static_cast<void**>(addr) = _base + addend;
         break;
+    case R_X86_64_IRELATIVE:
+        *static_cast<void**>(addr) = reinterpret_cast<void *(*)()>(_base + 
addend)();
+        break;
     case R_X86_64_JUMP_SLOT:
     case R_X86_64_GLOB_DAT: {
         auto _sym = symbol(sym, true);
diff --git a/arch/x64/arch-elf.hh b/arch/x64/arch-elf.hh
--- a/arch/x64/arch-elf.hh
+++ b/arch/x64/arch-elf.hh
@@ -38,6 +38,7 @@ enum {
 /* for pltgot relocation */
 #define ARCH_JUMP_SLOT R_X86_64_JUMP_SLOT
 #define ARCH_TLSDESC R_X86_64_TLSDESC
+#define ARCH_IRELATIVE R_X86_64_IRELATIVE
 
 #define ELF_KERNEL_MACHINE_TYPE 62
 
diff --git a/core/elf.cc b/core/elf.cc
--- a/core/elf.cc
+++ b/core/elf.cc
@@ -783,6 +783,14 @@ void object::relocate_pltgot()
 #endif /* AARCH64_PORT_STUB */
         original_plt = static_cast<void*>(_base + (u64)pltgot[1]);
     }
+    // PLTGOT resolution has a special calling convention,
+    // for x64 the symbol index and some word is pushed on the stack,
+    // for AArch64 &pltgot[n] and LR are pushed on the stack,
+    // so we need an assembly stub to convert it back to the
+    // standard calling convention.
+    pltgot[1] = this;
+    pltgot[2] = reinterpret_cast<void*>(__elf_resolve_pltgot);
+
     bool bind_now = dynamic_exists(DT_BIND_NOW) || mlocked() ||
         (dynamic_exists(DT_FLAGS) && (dynamic_val(DT_FLAGS) & DF_BIND_NOW)) ||
         (dynamic_exists(DT_FLAGS_1) && (dynamic_val(DT_FLAGS_1) & DF_1_NOW));
@@ -793,7 +801,7 @@ void object::relocate_pltgot()
         auto info = p->r_info;
         u32 type = info & 0xffffffff;
         void *addr = _base + p->r_offset;
-        assert(type == ARCH_JUMP_SLOT || type == ARCH_TLSDESC);
+        assert(type == ARCH_JUMP_SLOT || type == ARCH_TLSDESC || type == 
ARCH_IRELATIVE);
         if (type == ARCH_JUMP_SLOT) {
             if (bind_now) {
                 // If on-load binding is requested (instead of the default lazy
@@ -814,20 +822,14 @@ void object::relocate_pltgot()
                 // make sure it is relocated relative to the object base.
                 *static_cast<u64*>(addr) += reinterpret_cast<u64>(_base);
             }
+        } else if (type == ARCH_IRELATIVE) {
+            *static_cast<void**>(addr) = reinterpret_cast<void *(*)()>(_base + 
p->r_addend)();
         } else {
             u32 sym = info >> 32;
             arch_relocate_tls_desc(sym, addr, p->r_addend);
         }
     }
     elf_debug("Relocated %d PLT symbols\n", nrel);
-
-    // PLTGOT resolution has a special calling convention,
-    // for x64 the symbol index and some word is pushed on the stack,
-    // for AArch64 &pltgot[n] and LR are pushed on the stack,
-    // so we need an assembly stub to convert it back to the
-    // standard calling convention.
-    pltgot[1] = this;
-    pltgot[2] = reinterpret_cast<void*>(__elf_resolve_pltgot);
 }
 
 void* object::resolve_pltgot(unsigned index)
@@ -859,12 +861,12 @@ void* object::resolve_pltgot(unsigned index)
 void object::relocate()
 {
     assert(!dynamic_exists(DT_REL));
-    if (dynamic_exists(DT_RELA)) {
-        relocate_rela();
-    }
     if (dynamic_exists(DT_JMPREL)) {
         relocate_pltgot();
     }
+    if (dynamic_exists(DT_RELA)) {
+        relocate_rela();
+    }
 }
 
 unsigned long
diff --git a/modules/tests/Makefile b/modules/tests/Makefile
--- a/modules/tests/Makefile
+++ b/modules/tests/Makefile
@@ -60,6 +60,10 @@ $(out)/tests/options.o: $(src)/core/options.cc
 $(out)/tests/tst-options.so: $(out)/tests/tst-options.o $(out)/tests/options.o
        $(call quiet, $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared -o $@ $< 
$(out)/tests/options.o $(LIBS), LD tests/tst-options.so)
 
+$(out)/tests/tst-reloc.o: CFLAGS:=$(subst -fPIC,-fpie,$(CFLAGS))
+$(out)/tests/tst-reloc.so: $(src)/tests/tst-reloc.c
+       $(call quiet, $(CC) $(CFLAGS) $(LDFLAGS) -pie -o $@ $< $(LIBS), LD 
tests/tst-reloc.so)
+
 $(out)/tests/commands.o: $(src)/core/commands.cc
        $(makedir)
        $(call quiet, $(CXX) $(CXXFLAGS) -c -o $@ $<, CXX $*.cc)
@@ -135,7 +139,7 @@ tests := tst-pthread.so misc-ramdisk.so tst-vblk.so 
tst-bsd-evh.so \
        libtls.so libtls_gold.so tst-tls.so tst-tls-gold.so tst-tls-pie.so \
        tst-sigaction.so tst-syscall.so tst-ifaddrs.so tst-getdents.so \
        tst-netlink.so misc-zfs-io.so misc-zfs-arc.so tst-pthread-create.so \
-       misc-futex-perf.so misc-syscall-perf.so tst-brk.so
+       misc-futex-perf.so misc-syscall-perf.so tst-brk.so tst-reloc.so
 #      libstatic-thread-variable.so tst-static-thread-variable.so \
 
 ifeq ($(arch),x64)
diff --git a/tests/tst-reloc.c b/tests/tst-reloc.c
--- a/tests/tst-reloc.c
+++ b/tests/tst-reloc.c
@@ -0,0 +1,28 @@
+#include <stdio.h>
+
+int func_impl()
+{
+   return 42;
+}
+
+void *func_resolver() {
+    puts("func_resolver");
+    return (void *)func_impl;
+}
+
+int func() __attribute__((ifunc("func_resolver")));
+
+// .rela.dyn.rel => R_X86_64_64 referencing STT_GNU_IFUNC in .rela.dyn
+int (*fptr_func)() = func;
+
+static void test_indirect_relocations()
+{
+#ifdef __x86_64__
+    printf("%d\n", func());
+#endif
+}
+
+int main()
+{
+    test_indirect_relocations();
+}

-- 
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to osv-dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/osv-dev/000000000000adcd3d0602dc963f%40google.com.

Reply via email to