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.

Reply via email to