Skip to content

Commit

Permalink
Allow running non-PIE executables that do not collide with kernel
Browse files Browse the repository at this point in the history
This patch provides necessary changes to OSv dynamic linker
to allow running non-PIEs (= Position Dependant Executables)
as long as they do not collide with kernel.

Please note this patch does not fully address issue cloudius-systems#190
though it provides necessary groundwork to fully address it in future.
To truly support running unmodified arbitrary non-PIEs
(that typically load at 0x400000 = 4th MB) we need
to modify OSv to load kernel way higher that 0x200000.

There are 2 ways to run non-PIEs with OSv with this patch:

1) Link it by forcing text segment address so it does
   not collide with kernel using -Wl,-Ttext-segment,0x?????? option.

2) Change kernel_base in OSv makefile to a higher address that
   makes it not collide with the executable (for example 0xa00000);
   most non-PIEs by default load at 0x400000

Signed-off-by: Waldemar Kozaczuk <jwkozaczuk@gmail.com>
  • Loading branch information
wkozaczuk committed Jun 13, 2019
1 parent f161756 commit 1c0c401
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 13 deletions.
35 changes: 24 additions & 11 deletions core/elf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ TRACEPOINT(trace_elf_unload, "%s", const char *);
TRACEPOINT(trace_elf_lookup, "%s", const char *);
TRACEPOINT(trace_elf_lookup_addr, "%p", const void *);

extern void* elf_start;
extern size_t elf_size;

using namespace boost::range;

namespace {
Expand Down Expand Up @@ -270,15 +273,6 @@ void file::load_elf_header()
|| _ehdr.e_ident[EI_OSABI] == 0)) {
throw osv::invalid_elf_error("bad os abi");
}
// We currently only support running ET_DYN objects (shared library or
// position-independent executable). In the future we can add support for
// ET_EXEC (ordinary, position-dependent executables) but it will require
// loading them at their specified address and moving the kernel out of
// their way.
if (_ehdr.e_type != ET_DYN) {
throw osv::invalid_elf_error(
"bad executable type (only shared-object or PIE supported)");
}
}

void file::read(Elf64_Off offset, void* data, size_t size)
Expand All @@ -300,17 +294,35 @@ void* align(void* addr, ulong align, ulong offset)

}

static bool intersects_with_kernel(Elf64_Addr elf_addr)
{
void *addr = reinterpret_cast<void*>(elf_addr);
return addr >= elf_start && addr < elf_start + elf_size;
}

void object::set_base(void* base)
{
auto p = std::min_element(_phdrs.begin(), _phdrs.end(),
[](Elf64_Phdr a, Elf64_Phdr b)
{ return a.p_type == PT_LOAD
&& a.p_vaddr < b.p_vaddr; });
_base = align(base, p->p_align, p->p_vaddr & (p->p_align - 1)) - p->p_vaddr;
auto q = std::min_element(_phdrs.begin(), _phdrs.end(),
[](Elf64_Phdr a, Elf64_Phdr b)
{ return a.p_type == PT_LOAD
&& a.p_vaddr > b.p_vaddr; });

if (is_non_pie_executable() && base != ((void*)ELF_IMAGE_START)) {
// Verify non-PIE executable does not collide with the kernel
if (intersects_with_kernel(p->p_vaddr) || intersects_with_kernel(q->p_vaddr + q->p_memsz)) {
abort("Non-PIE executable [%s] collides with kernel: [%p-%p] !\n",
pathname().c_str(), p->p_vaddr, q->p_vaddr + q->p_memsz);
}
// The base for non-PIEs ( = Position Dependant Executables) should be set to 0
_base = 0x0;
} else {
_base = align(base, p->p_align, p->p_vaddr & (p->p_align - 1)) - p->p_vaddr;
}

_end = _base + q->p_vaddr + q->p_memsz;
}

Expand Down Expand Up @@ -1227,7 +1239,8 @@ program::load_object(std::string name, std::vector<std::string> extra_path,
_modules_rcu.assign(new_modules.release());
osv::rcu_dispose(old_modules);
ef->load_segments();
_next_alloc = ef->end();
if (!ef->is_non_pie_executable())
_next_alloc = ef->end();
add_debugger_obj(ef.get());
loaded_objects.push_back(ef);
ef->load_needed(loaded_objects);
Expand Down
1 change: 1 addition & 0 deletions include/osv/elf.hh
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ public:
void init_static_tls();
size_t initial_tls_size() { return _initial_tls_size; }
void* initial_tls() { return _initial_tls.get(); }
bool is_non_pie_executable() { return _ehdr.e_type == ET_EXEC; }
std::vector<ptrdiff_t>& initial_tls_offsets() { return _initial_tls_offsets; }
protected:
virtual void load_segment(const Elf64_Phdr& segment) = 0;
Expand Down
9 changes: 7 additions & 2 deletions modules/tests/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,12 @@ $(out)/tests/tst-getopt-pie.o: $(src)/tests/tst-getopt.cc
$(makedir)
$(call quiet, $(CXX) $(CXXFLAGS) -c -o $@ $<, CXX $*.cc)
$(out)/tests/tst-getopt-pie.so: $(out)/tests/tst-getopt-pie.o
$(call quiet, $(CXX) $(CXXFLAGS) -pie -o $@ $< $(LIBS), LD $*.so)
$(call quiet, $(CXX) $(CXXFLAGS) -pie -o $@ $< $(LIBS), LD $@)

comma := ,
$(out)/tests/tst-non-pie.so: CXXFLAGS:=$(subst -fPIC,-no-pie -Wl$(comma)-Ttext-segment$(comma)0xf000000,$(CXXFLAGS))
$(out)/tests/tst-non-pie.so: $(src)/tests/tst-non-pie.cc
$(call quiet, $(CXX) $(CXXFLAGS) -o $@ $< $(LIBS), LD $@)

# 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
Expand Down Expand Up @@ -124,7 +129,7 @@ tests := tst-pthread.so misc-ramdisk.so tst-vblk.so tst-bsd-evh.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-non-fpic.so tst-small-malloc.so \
tst-mmx-fpu.so tst-getopt.so tst-getopt-pie.so
tst-mmx-fpu.so tst-getopt.so tst-getopt-pie.so tst-non-pie.so
# libstatic-thread-variable.so tst-static-thread-variable.so \
tests += testrunner.so
Expand Down
6 changes: 6 additions & 0 deletions tests/tst-non-pie.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#include <stdio.h>

int main(){
printf("Hello from C code\n");
return 0;
}

0 comments on commit 1c0c401

Please sign in to comment.