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.