From: Waldemar Kozaczuk <[email protected]>
Committer: Nadav Har'El <[email protected]>
Branch: master
Support non-fPIC shared objects
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]>
Message-Id: <[email protected]>
---
diff --git a/core/elf.cc b/core/elf.cc
--- 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
--- 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
--- 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
--- a/tests/tst-non-fpic.cc
+++ 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");
+}
--
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.