This patch enhances ELF dynamic loader to support position dependent shared libraries. It does it by detecting presence of DT_TEXTREL marker and temporarilily making PT_LOAD sections writable so that corresponding references in code get updated to point to correct addresses in memory. Eventually it fixes permissions of PT_LOAD sections to make it non-writable.
This patch most notably allows to run GraalVM generated Java apps. Fixes #1004 Signed-off-by: Waldemar Kozaczuk <[email protected]> --- core/elf.cc | 47 +++++++++++++++++++++++++++++++++++------- include/osv/elf.hh | 5 ++++- modules/tests/Makefile | 9 +++++++- tests/tst-non-fpic.cc | 11 ++++++++++ 4 files changed, 63 insertions(+), 9 deletions(-) create mode 100644 tests/tst-non-fpic.cc diff --git a/core/elf.cc b/core/elf.cc index b0e3c1aa..c0dfbbb9 100644 --- a/core/elf.cc +++ b/core/elf.cc @@ -325,13 +325,7 @@ void file::load_segment(const Elf64_Phdr& phdr) ulong filesz = align_up(filesz_unaligned, mmu::page_size); ulong memsz = align_up(phdr.p_vaddr + phdr.p_memsz, mmu::page_size) - vstart; - unsigned perm = 0; - if (phdr.p_flags & PF_X) - perm |= mmu::perm_exec; - if (phdr.p_flags & PF_W) - perm |= mmu::perm_write; - if (phdr.p_flags & PF_R) - perm |= mmu::perm_read; + unsigned perm = get_segment_mmap_permissions(phdr); auto flag = mmu::mmap_fixed | (mlocked() ? mmu::mmap_populate : 0); mmu::map_file(_base + vstart, filesz, flag, perm, _f, align_down(phdr.p_offset, mmu::page_size)); @@ -354,6 +348,11 @@ bool object::mlocked() return false; } +bool object::has_non_writable_text_relocations() +{ + return dynamic_exists(DT_TEXTREL); +} + Elf64_Note::Elf64_Note(void *_base, char *str) { Elf64_Word *base = reinterpret_cast<Elf64_Word *>(_base); @@ -468,8 +467,24 @@ void object::unload_segments() } } +unsigned object::get_segment_mmap_permissions(const Elf64_Phdr& phdr) +{ + unsigned perm = 0; + if (phdr.p_flags & PF_X) + perm |= mmu::perm_exec; + if (phdr.p_flags & PF_W) + perm |= mmu::perm_write; + if (phdr.p_flags & PF_R) + perm |= mmu::perm_read; + return perm; +} + void object::fix_permissions() { + if(has_non_writable_text_relocations()) { + make_text_writable(false); + } + for (auto&& phdr : _phdrs) { if (phdr.p_type != PT_GNU_RELRO) continue; @@ -482,6 +497,20 @@ void object::fix_permissions() } } +void object::make_text_writable(bool flag) +{ + for (auto&& phdr : _phdrs) { + if (phdr.p_type != PT_LOAD) + continue; + + ulong vstart = align_down(phdr.p_vaddr, mmu::page_size); + ulong memsz = align_up(phdr.p_vaddr + phdr.p_memsz, mmu::page_size) - vstart; + + unsigned perm = get_segment_mmap_permissions(phdr); + mmu::mprotect(_base + vstart, memsz, flag ? perm | mmu::perm_write : perm); + } +} + template <typename T> T* object::dynamic_ptr(unsigned tag) { @@ -600,6 +629,10 @@ symbol_module object::symbol_other(unsigned idx) void object::relocate_rela() { + if(has_non_writable_text_relocations()) { + make_text_writable(true); + } + auto rela = dynamic_ptr<Elf64_Rela>(DT_RELA); assert(dynamic_val(DT_RELAENT) == sizeof(Elf64_Rela)); unsigned nb = dynamic_val(DT_RELASZ) / sizeof(Elf64_Rela); diff --git a/include/osv/elf.hh b/include/osv/elf.hh index 5449f98f..19b24eec 100644 --- a/include/osv/elf.hh +++ b/include/osv/elf.hh @@ -177,7 +177,7 @@ enum { DT_PLTREL = 20, // d_val Type of relocation entry used for the procedure linkage // table. The d_val member contains either DT_REL or DT_RELA. DT_DEBUG = 21, // d_ptr Reserved for debugger use. - DT_TEXTREL = 22, // ignored The presence of this dynamic table entry signals that the + DT_TEXTREL = 22, // The presence of this dynamic table entry signals that the // relocation table contains relocations for a non-writable // segment. DT_JMPREL = 23, // d_ptr Address of the relocations associated with the procedure @@ -367,6 +367,8 @@ protected: virtual void unload_segment(const Elf64_Phdr& segment) = 0; virtual void read(Elf64_Off offset, void* data, size_t len) = 0; bool mlocked(); + bool has_non_writable_text_relocations(); + unsigned get_segment_mmap_permissions(const Elf64_Phdr& phdr); private: Elf64_Sym* lookup_symbol_old(const char* name); Elf64_Sym* lookup_symbol_gnu(const char* name); @@ -388,6 +390,7 @@ private: void collect_dependencies(std::unordered_set<elf::object*>& ds); void prepare_initial_tls(void* buffer, size_t size, std::vector<ptrdiff_t>& offsets); void alloc_static_tls(); + void make_text_writable(bool flag); protected: program& _prog; std::string _pathname; diff --git a/modules/tests/Makefile b/modules/tests/Makefile index cec4febe..88a9c856 100644 --- a/modules/tests/Makefile +++ b/modules/tests/Makefile @@ -60,6 +60,13 @@ $(out)/tests/rofs/%.o: $(src)/tests/%.c $(out)/%.so: $(out)/%.o $(call quiet, $(CXX) $(CXXFLAGS) -shared -o $@ $< $(LIBS), LD $*.so) +NON_FPIC_CXXFLAGS = $(autodepend) $(INCLUDES) -g -O2 -mcmodel=large -fno-pie -DBOOST_TEST_DYN_LINK \ + -U _FORTIFY_SOURCE -D_KERNEL -D__OSV__ -DCONF_debug_memory=0 \ + -Wall -Wno-pointer-arith -Wformat=0 -Wno-format-security +$(out)/tests/tst-non-fpic.so: $(src)/tests/tst-non-fpic.cc + $(makedir) + $(call quiet, $(CXX) -shared $(NON_FPIC_CXXFLAGS) -o $@ $<, LD $*.so) + # The rofs test image mounts /tmp as ramfs and 4 tests that exercise file system # fail due to some unresolved bugs or other shortcomings of the ramfs implementation # and are temporarily removed from the rofs-only-tests list. The tests tst-readdir.so @@ -114,7 +121,7 @@ tests := tst-pthread.so misc-ramdisk.so tst-vblk.so tst-bsd-evh.so \ tst-ifaddrs.so tst-pthread-affinity-inherit.so tst-sem-timed-wait.so \ tst-ttyname.so tst-pthread-barrier.so tst-feexcept.so tst-math.so \ tst-sigaltstack.so tst-fread.so tst-tcp-cork.so tst-tcp-v6.so \ - tst-calloc.so tst-crypt.so + tst-calloc.so tst-crypt.so tst-non-fpic.so # libstatic-thread-variable.so tst-static-thread-variable.so \ diff --git a/tests/tst-non-fpic.cc b/tests/tst-non-fpic.cc new file mode 100644 index 00000000..01764a0d --- /dev/null +++ b/tests/tst-non-fpic.cc @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2018 Waldemar Kozaczuk + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ +#include <stdio.h> + +int main() { + printf("Hello\n"); +} -- 2.19.1 -- 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 [email protected]. For more options, visit https://groups.google.com/d/optout.
