Clean ldso from using deprecated APIs

Issue #1987
This commit is contained in:
Norman Feske 2016-10-30 15:17:24 +01:00 committed by Christian Helmuth
parent 20faa8b84e
commit 784e728727
40 changed files with 1535 additions and 1136 deletions

View File

@ -87,6 +87,13 @@ namespace Genode {
*/
extern void (*call_component_construct)(Genode::Env &) __attribute__((weak));
/*
* This function is normally provided by the cxx library, which is not
* used for lx_hybrid programs. For lx_hybrid programs, the exception
* handling is initialized by the host system's regular startup code.
*/
void init_exception_handling(Env &) { }
}
static void lx_hybrid_component_construct(Genode::Env &env)

View File

@ -18,8 +18,8 @@
#include <base/entrypoint.h>
#include <ram_session/capability.h>
#include <cpu_session/capability.h>
#include <pd_session/capability.h>
#include <rm_session/rm_session.h>
#include <pd_session/pd_session.h>
/* maintain compatibility to deprecated API */
#include <deprecated/env.h>
@ -64,6 +64,15 @@ struct Genode::Env
virtual Ram_session_capability ram_session_cap() = 0;
virtual Cpu_session_capability cpu_session_cap() = 0;
/*
* XXX temporary
*
* The PD session capability is solely used for upgrading the PD session,
* e.g., when the dynamic linker attaches dataspaces to the linker area.
* Once we add 'Env::upgrade', we can remove this accessor.
*/
virtual Pd_session_capability pd_session_cap() = 0;
};
#endif /* _INCLUDE__BASE__ENV_H_ */

View File

@ -29,29 +29,36 @@ class Genode::Shared_object
private:
void *_handle = nullptr;
void *_lookup(const char *symbol) const;
Allocator &_md_alloc;
public:
class Invalid_file : public Genode::Exception { };
class Invalid_symbol : public Genode::Exception { };
class Invalid_rom_module : public Genode::Exception { };
class Invalid_symbol : public Genode::Exception { };
enum open_flags {
NOW = 0x1,
LAZY = 0x2,
KEEP = 0x4, /* do not unload on destruction */
};
enum Keep { DONT_KEEP, KEEP };
enum Bind { BIND_LAZY, BIND_NOW };
/**
* Load shared object and dependencies
*
* \param file Shared object to load
* \param flags LAZY for lazy function binding, NOW for immediate binding
* \param env Genode environment, needed to obtain the ROM module
* for the shared object and to populate the linker
* area of the component's address space
* \param md_alloc backing store for the linker's dynamically allocated
* meta data
* \param name ROM module name of shared object to load
* \param bind bind functions immediately (BIND_NOW) or on
* demand (BIND_LAZY)
* \param keep unload ELF object if no longer needed (DONT_KEEP),
* or keep ELF object loaded at all times (KEEP)
*
* \throw Invalid_file
* \throw Invalid_rom_module
*/
Shared_object(char const *file = nullptr, unsigned flags = LAZY);
Shared_object(Env &, Allocator &md_alloc, char const *name,
Bind bind, Keep keep);
/**
* Close and unload shared object
@ -78,16 +85,16 @@ class Genode::Shared_object
struct Link_map
{
Genode::addr_t addr; /* load address */
char const *path; /* object path */
void const *dynamic; /* pointer to DYNAMIC section */
Link_map *next;
Link_map *prev;
char const *path; /* object path */
void const *dynamic; /* pointer to DYNAMIC section */
Link_map const *next;
Link_map const *prev;
};
/**
* Return link map of shared object
*/
Link_map const * link_map() const;
Link_map const &link_map() const;
};

View File

@ -1,4 +1,5 @@
CXX_SRC_CC += misc.cc new_delete.cc malloc_free.cc exception.cc guard.cc
INC_DIR += $(REP_DIR)/src/include
# We need the libsupc++ include directory
STDINC = yes

View File

@ -4,7 +4,7 @@ DIR = $(REP_DIR)/src/lib/ldso
include $(BASE_DIR)/mk/base-libs.mk
LIBS = $(BASE_LIBS)
SRC_CC = main.cc test.cc exception.cc file.cc dependency.cc debug.cc \
SRC_CC = main.cc test.cc exception.cc dependency.cc debug.cc \
shared_object.cc
SRC_S = jmp_slot.s
INC_DIR += $(DIR)/include

View File

@ -26,6 +26,9 @@ namespace Genode {
extern Region_map *env_stack_area_region_map;
extern Ram_session *env_stack_area_ram_session;
void init_exception_handling(Env &);
void init_cxx_heap(Env &);
void init_ldso_phdr(Env &);
void init_signal_thread(Env &);
void init_log();
}

View File

@ -16,6 +16,9 @@
#include <base/component.h>
#include <base/env.h>
/* base-internal includes */
#include <base/internal/globals.h>
namespace {
@ -41,6 +44,11 @@ namespace {
{
return Genode::env()->cpu_session_cap();
}
Genode::Pd_session_capability pd_session_cap() override
{
return Genode::env()->pd_session_cap();
}
};
}
@ -61,6 +69,8 @@ struct Genode::Startup
{
::Env env { ep };
bool const exception_handling = (init_exception_handling(env), true);
/*
* The construction of the main entrypoint does never return.
*/

View File

@ -19,6 +19,9 @@
#include <base/log.h>
#include <util/string.h>
/* base-internal includes */
#include <base/internal/globals.h>
extern "C" char __eh_frame_start__[]; /* from linker script */
extern "C" void __register_frame (const void *begin); /* from libgcc_eh */
extern "C" char *__cxa_demangle(const char *mangled_name,
@ -69,13 +72,46 @@ void terminate_handler()
}
/**
* Init program headers of the dynamic linker
*
* The weak function is used for statically linked binaries. The
* dynamic linker provides an implementation that loads the program
* headers of the linker. This must be done before the first exception
* is thrown.
*/
void Genode::init_ldso_phdr(Env &) __attribute__((weak));
void Genode::init_ldso_phdr(Env &) { }
/*
* Initialization
*/
void init_exception_handling()
void Genode::init_exception_handling(Env &env)
{
init_ldso_phdr(env);
init_cxx_heap(env);
__register_frame(__eh_frame_start__);
std::set_terminate(terminate_handler);
/*
* Trigger first exception. This step has two purposes.
* First, it enables us to detect problems related to exception handling as
* early as possible. If there are problems with the C++ support library,
* it is much easier to debug them at this early stage. Otherwise problems
* with half-working exception handling cause subtle failures that are hard
* to interpret.
*
* Second, the C++ support library allocates data structures lazily on the
* first occurrence of an exception. In some corner cases, this allocation
* consumes several KB of stack. This is usually not a problem when the
* first exception is triggered from the main thread but it becomes an
* issue when the first exception is thrown from the stack of a thread with
* a specially tailored (and otherwise sufficient) stack size. By throwing
* an exception here, we mitigate this issue by eagerly performing those
* allocations.
*/
try { throw 1; } catch (...) { }
}

View File

@ -14,37 +14,44 @@
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <base/env.h>
#include <base/heap.h>
#include <util/string.h>
#include <util/volatile_object.h>
/* base-internal includes */
#include <base/internal/globals.h>
using namespace Genode;
static Lazy_volatile_object<Heap> &cxx_heap()
{
static Lazy_volatile_object<Heap> heap;
return heap;
}
/**
* Return heap partition for the private use within the cxx library.
*
* If we used the 'env()->heap()' with the C++ runtime, we would run into a
* deadlock when a 'Ram_session::Alloc_failed' exception is thrown from within
* 'Heap::alloc'. For creating the exception object, the C++ runtime calls
* '__cxa_allocate_exception', which, in turn, calls 'malloc'. If our 'malloc'
* implementation called 'env()->heap()->alloc()', we would end up in a
* recursive attempt to obtain the 'env()->heap()' lock.
*
* By using a dedicated heap instance for the cxx library, we avoid this
* circular condition.
* For creating the exception object, the C++ runtime calls
* '__cxa_allocate_exception', which, in turn, calls 'malloc'. The cxx library
* uses a local implementation of 'malloc' using a dedicated heap instance.
*/
static Heap *cxx_heap()
void Genode::init_cxx_heap(Env &env)
{
if (cxx_heap().constructed()) return;
/*
* Exception frames are small. Hence, a small static backing store suffices
* for the cxx heap partition in the normal case. The 'env()->ram_session'
* for the cxx heap partition in the normal case. The 'env.ram()' session
* is used only if the demand exceeds the capacity of the 'initial_block'.
*/
static char initial_block[1024*sizeof(long)];
static Heap heap(env()->ram_session(), env()->rm_session(),
Heap::UNLIMITED, initial_block, sizeof(initial_block));
return &heap;
cxx_heap().construct(&env.ram(), &env.rm(),
Heap::UNLIMITED, initial_block, sizeof(initial_block));
}

View File

@ -26,15 +26,15 @@ void binary_ready_hook_for_gdb() { }
extern "C" void brk(Linker::Debug *, Linker::Link_map *) { }
void Linker::dump_link_map(Object *o)
void Linker::dump_link_map(Object const &obj)
{
for (; o; o = o->next_obj()) {
for (Object const *o = &obj; o; o = o->next_obj()) {
if (o->is_binary())
continue;
Genode::log(" ", Genode::Hex(o->link_map()->addr),
" .. ", Genode::Hex(o->link_map()->addr + o->size() - 1),
": ", o->name());
log(" ", Hex(o->link_map().addr),
" .. ", Hex(o->link_map().addr + o->size() - 1),
": ", o->name());
}
}

View File

@ -19,63 +19,77 @@
/**
* Dependency node
*/
Linker::Dependency::Dependency(char const *path, Root_object *root,
Genode::Fifo<Dependency> * const dep,
unsigned flags)
: obj(load(path, this, flags)), root(root)
Linker::Dependency::Dependency(Env &env, Allocator &md_alloc,
char const *path, Root_object *root,
Fifo<Dependency> &deps,
Keep keep)
:
_obj(load(env, md_alloc, path, *this, keep)),
_root(root),
_md_alloc(&md_alloc)
{
dep->enqueue(this);
load_needed(dep, flags);
deps.enqueue(this);
load_needed(env, *_md_alloc, deps, keep);
}
Linker::Dependency::~Dependency()
{
if (obj->unload()) {
if (!_obj.unload())
return;
if (verbose_loading)
Genode::log("Destroy: ", obj->name());
if (verbose_loading)
log("Destroy: ", _obj.name());
destroy(Genode::env()->heap(), obj);
}
destroy(_md_alloc, &_obj);
}
bool Linker::Dependency::in_dep(char const *file,
Genode::Fifo<Dependency> * const dep)
bool Linker::Dependency::in_dep(char const *file, Fifo<Dependency> const &dep)
{
for (Dependency *d = dep->head(); d; d = d->next())
if (!Genode::strcmp(file, d->obj->name()))
for (Dependency const *d = dep.head(); d; d = d->next())
if (!strcmp(file, d->obj().name()))
return true;
return false;
}
void Linker::Dependency::load_needed(Genode::Fifo<Dependency> * const dep,
unsigned flags)
void Linker::Dependency::load_needed(Env &env, Allocator &md_alloc,
Fifo<Dependency> &deps, Keep keep)
{
for (Dynamic::Needed *n = obj->dynamic()->needed.head(); n; n = n->next()) {
char const *path = n->path(obj->dynamic()->strtab);
_obj.dynamic().for_each_dependency([&] (char const *path) {
Object *o;
if (!in_dep(Linker::file(path), dep))
new (Genode::env()->heap()) Dependency(path, root, dep, flags);
if (!in_dep(Linker::file(path), deps))
new (md_alloc) Dependency(env, md_alloc, path, _root, deps, keep);
/* re-order initializer list, if needed object has been already added */
else if ((o = Init::list()->contains(Linker::file(path))))
else if (Object *o = Init::list()->contains(Linker::file(path)))
Init::list()->reorder(o);
}
});
}
Linker::Root_object::Root_object(char const *path, unsigned flags)
Linker::Dependency const &Linker::Dependency::first() const
{
new (Genode::env()->heap()) Dependency(path, this, &dep, flags);
return _root ? *_root->first_dep() : *this;
}
Linker::Root_object::Root_object(Env &env, Allocator &md_alloc,
char const *path, Bind bind, Keep keep)
:
_md_alloc(md_alloc)
{
/*
* The life time of 'Dependency' objects is managed via reference
* counting. Hence, we don't need to remember them here.
*/
new (md_alloc) Dependency(env, md_alloc, path, this, _deps, keep);
/* provide Genode base library access */
new (Genode::env()->heap()) Dependency(linker_name(), this, &dep);
new (md_alloc) Dependency(env, md_alloc, linker_name(), this, _deps, DONT_KEEP);
/* relocate and call constructors */
Init::list()->initialize();
Init::list()->initialize(bind);
}

View File

@ -11,6 +11,7 @@
* under the terms of the GNU General Public License version 2.
*/
#include <base/log.h>
#include <linker.h>
@ -32,12 +33,12 @@ struct Phdr_info
};
extern "C" int dl_iterate_phdr(int (*callback) (Phdr_info *info, Genode::size_t size, void *data), void *data)
extern "C" int dl_iterate_phdr(int (*callback) (Phdr_info *info, size_t size, void *data), void *data)
{
int err = 0;
Phdr_info info;
Genode::Lock::Guard guard(Object::lock());
Lock::Guard guard(lock());
for (Object *e = obj_list_head();e; e = e->next_obj()) {
@ -47,7 +48,7 @@ extern "C" int dl_iterate_phdr(int (*callback) (Phdr_info *info, Genode::size_t
info.phnum = e->file()->phdr.count;
if (verbose_exception)
Genode::log(e->name(), " reloc ", Genode::Hex(e->reloc_base()));
log(e->name(), " reloc ", Hex(e->reloc_base()));
if ((err = callback(&info, sizeof(Phdr_info), data)))
break;
@ -87,16 +88,10 @@ extern "C" unsigned long dl_unwind_find_exidx(unsigned long pc, int *pcount)
enum { EXIDX_ENTRY_SIZE = 8 };
*pcount = 0;
/*
* Since this function may be called before the main function, load linker's
* program header now
*/
load_linker_phdr();
for (Object *e = obj_list_head(); e; e = e->next_obj()) {
for (Object *e = obj_list_head(); e; e = e->next_obj())
{
/* address of first PT_LOAD header */
Genode::addr_t base = e->reloc_base() + e->file()->start;
addr_t base = e->reloc_base() + e->file()->start;
/* is the 'pc' somewhere within this ELF image */
if ((pc < base) || (pc >= base + e->file()->size))

View File

@ -1,331 +0,0 @@
/**
* \brief ELF loading/unloading support
* \author Sebastian Sumpf
* \date 2014-10-26
*/
/*
* Copyright (C) 2014 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* local includes */
#include <linker.h>
#include <base/internal/page_size.h>
/* Genode includes */
#include <util/construct_at.h>
#include <base/allocator_avl.h>
#include <base/env.h>
#include <os/attached_rom_dataspace.h>
#include <region_map/client.h>
#include <util/retry.h>
char const *Linker::ELFMAG = "\177ELF";
using namespace Linker;
using namespace Genode;
namespace Linker
{
class Rm_area;
struct Elf_file;
}
/**
* Managed dataspace for ELF files (singelton)
*/
class Linker::Rm_area
{
public:
typedef Region_map_client::Local_addr Local_addr;
typedef Region_map_client::Region_conflict Region_conflict;
private:
Region_map_client _rm;
addr_t _base; /* base address of dataspace */
Allocator_avl _range; /* VM range allocator */
protected:
Rm_area(addr_t base)
: _rm(env()->pd_session()->linker_area()), _range(env()->heap())
{
_base = (addr_t) env()->rm_session()->attach_at(_rm.dataspace(), base);
_range.add_range(base, Pd_session::LINKER_AREA_SIZE);
}
public:
static Rm_area *r(addr_t base = 0)
{
/*
* The capabilities in this class become invalid when doing a
* fork in the noux environment. Hence avoid destruction of
* the singleton object as the destructor would try to access
* the capabilities also in the forked process.
*/
static bool constructed = 0;
static char placeholder[sizeof(Rm_area)];
if (!constructed) {
construct_at<Rm_area>(placeholder, base);
constructed = 1;
}
return reinterpret_cast<Rm_area *>(placeholder);
}
/**
* Reserve VM region of 'size' at 'vaddr'. Allocate any free region if
* 'vaddr' is zero
*/
addr_t alloc_region(size_t size, addr_t vaddr = 0)
{
addr_t addr = vaddr;
if (addr && (_range.alloc_addr(size, addr).error()))
throw Region_conflict();
else if (!addr &&
_range.alloc_aligned(size, (void **)&addr,
get_page_size_log2()).error())
{
throw Region_conflict();
}
return addr;
}
void free_region(addr_t vaddr) { _range.free((void *)vaddr); }
/**
* Overwritten from 'Region_map_client'
*/
Local_addr attach_at(Dataspace_capability ds, addr_t local_addr,
size_t size = 0, off_t offset = 0)
{
return retry<Region_map::Out_of_metadata>(
[&] () {
return _rm.attach_at(ds, local_addr - _base, size, offset);
},
[&] () { env()->parent()->upgrade(env()->pd_session_cap(), "ram_quota=8K"); });
}
/**
* Overwritten from 'Region_map_client'
*/
Local_addr attach_executable(Dataspace_capability ds, addr_t local_addr,
size_t size = 0, off_t offset = 0)
{
return retry<Region_map::Out_of_metadata>(
[&] () {
return _rm.attach_executable(ds, local_addr - _base, size, offset);
},
[&] () { env()->parent()->upgrade(env()->pd_session_cap(), "ram_quota=8K"); });
}
void detach(Local_addr local_addr) { _rm.detach((addr_t)local_addr - _base); }
};
/**
* Map ELF files
*/
struct Linker::Elf_file : File
{
Rom_connection rom;
Ram_dataspace_capability ram_cap[Phdr::MAX_PHDR];
bool loaded;
Elf_file(char const *name, bool load = true)
:
rom(name), loaded(load)
{
load_phdr();
if (load)
load_segments();
}
virtual ~Elf_file()
{
if (loaded)
unload_segments();
}
/**
* Check if ELF is sane
*/
bool check_compat(Elf::Ehdr const *ehdr)
{
if (memcmp(ehdr, ELFMAG, SELFMAG) != 0) {
Genode::error("LD: binary is not an ELF");
return false;
}
if (ehdr->e_ident[EI_CLASS] != ELFCLASS) {
Genode::error("LD: support for 32/64-bit objects only");
return false;
}
return true;
}
/**
* Copy program headers and read entry point
*/
void load_phdr()
{
Elf::Ehdr *ehdr = (Elf::Ehdr *)env()->rm_session()->attach(rom.dataspace(), 0x1000);
if (!check_compat(ehdr))
throw Incompatible();
/* set entry point and program header information */
phdr.count = ehdr->e_phnum;
entry = (Entry)ehdr->e_entry;
/* copy program headers */
addr_t header = (addr_t)ehdr + ehdr->e_phoff;
for (unsigned i = 0; i < phdr.count; i++, header += ehdr->e_phentsize)
memcpy(&phdr.phdr[i], (void *)header, ehdr->e_phentsize);
env()->rm_session()->detach(ehdr);
Phdr p;
loadable_segments(p);
/* start vaddr */
start = trunc_page(p.phdr[0].p_vaddr);
Elf::Phdr *ph = &p.phdr[p.count - 1];
/* size of lodable segments */
size = round_page(ph->p_vaddr + ph->p_memsz) - start;
}
/**
* Find PT_LOAD segemnts
*/
void loadable_segments(Phdr &result)
{
for (unsigned i = 0; i < phdr.count; i++) {
Elf::Phdr *ph = &phdr.phdr[i];
if (ph->p_type != PT_LOAD)
continue;
if (ph->p_align & (0x1000 - 1)) {
Genode::error("LD: Unsupported alignment ", (void *)ph->p_align);
throw Incompatible();
}
result.phdr[result.count++] = *ph;
}
}
bool is_rx(Elf::Phdr const &ph) {
return ((ph.p_flags & PF_MASK) == (PF_R | PF_X)); }
bool is_rw(Elf::Phdr const &ph) {
return ((ph.p_flags & PF_MASK) == (PF_R | PF_W)); }
/**
* Load PT_LOAD segments
*/
void load_segments()
{
Phdr p;
/* search for PT_LOAD */
loadable_segments(p);
/* allocate region */
reloc_base = Rm_area::r(start)->alloc_region(size, start);
reloc_base = (start == reloc_base) ? 0 : reloc_base;
if (verbose_loading)
Genode::log("LD: reloc_base: ", Genode::Hex(reloc_base),
" start: ", Genode::Hex(start),
" end: ", Genode::Hex(reloc_base + start + size));
for (unsigned i = 0; i < p.count; i++) {
Elf::Phdr *ph = &p.phdr[i];
if (is_rx(*ph))
load_segment_rx(*ph);
else if (is_rw(*ph))
load_segment_rw(*ph, i);
else {
Genode::error("LD: Non-RW/RX segment");
throw Invalid_file();
}
}
}
/**
* Map read-only segment
*/
void load_segment_rx(Elf::Phdr const &p)
{
Rm_area::r()->attach_executable(rom.dataspace(),
trunc_page(p.p_vaddr) + reloc_base,
round_page(p.p_memsz),
trunc_page(p.p_offset));
}
/**
* Copy read-write segment
*/
void load_segment_rw(Elf::Phdr const &p, int nr)
{
void *src = env()->rm_session()->attach(rom.dataspace(), 0, p.p_offset);
addr_t dst = p.p_vaddr + reloc_base;
ram_cap[nr] = env()->ram_session()->alloc(p.p_memsz);
Rm_area::r()->attach_at(ram_cap[nr], dst);
memcpy((void*)dst, src, p.p_filesz);
/* clear if file size < memory size */
if (p.p_filesz < p.p_memsz)
memset((void *)(dst + p.p_filesz), 0, p.p_memsz - p.p_filesz);
env()->rm_session()->detach(src);
}
/**
* Unmap segements, RM regions, and free allocated dataspaces
*/
void unload_segments()
{
Phdr p;
loadable_segments(p);
/* detach from RM area */
for (unsigned i = 0; i < p.count; i++)
Rm_area::r()->detach(trunc_page(p.phdr[i].p_vaddr) + reloc_base);
/* free region from RM area */
Rm_area::r()->free_region(trunc_page(p.phdr[0].p_vaddr) + reloc_base);
/* free ram of RW segments */
for (unsigned i = 0; i < Phdr::MAX_PHDR; i++)
if (ram_cap[i].valid()) {
env()->ram_session()->free(ram_cap[i]);
}
}
};
File const *Linker::load(char const *path, bool load)
{
if (verbose_loading)
Genode::log("LD loading: ", path, " "
"(PHDRS only: ", load ? "no" : "yes", ")");
Elf_file *file = new (env()->heap()) Elf_file(Linker::file(path), load);
return file;
}

View File

@ -18,14 +18,21 @@
#include <base/log.h>
#include <elf.h>
constexpr bool verbose_link_map = false;
/* local includes */
#include <types.h>
constexpr bool verbose_link_map = false;
constexpr bool verbose_lookup = false;
constexpr bool verbose_exception = false;
constexpr bool verbose_shared = false;
constexpr bool verbose_loading = false;
namespace Linker {
struct Debug;
struct Link_map;
struct Object;
void dump_link_map(Object *o);
void dump_link_map(Object const &);
}
/*
@ -128,9 +135,9 @@ struct Linker::Link_map
return;
for (Link_map *m = first; m; m = m->next)
Genode::log("MAP: addr: ", Genode::Hex(m->addr),
" dynamic: ", m->dynamic, " ", Genode::Cstring(m->path),
" m: ", m, " p: ", m->prev, " n: ", m->next);
log("MAP: addr: ", Hex(m->addr),
" dynamic: ", m->dynamic, " ", Cstring(m->path),
" m: ", m, " p: ", m->prev, " n: ", m->next);
}
};

View File

@ -14,6 +14,8 @@
#ifndef _INCLUDE__DYNAMIC_H_
#define _INCLUDE__DYNAMIC_H_
/* local includes */
#include <types.h>
#include <relocation.h>
namespace Linker {
@ -54,162 +56,297 @@ struct Linker::Hash_table
/**
* .dynamic section entries
*/
struct Linker::Dynamic
class Linker::Dynamic
{
struct Needed : Genode::Fifo<Needed>::Element
{
Genode::off_t offset;
public:
Needed(Genode::off_t offset) : offset(offset) { }
class Dynamic_section_missing { };
char const *path(char const *strtab)
private:
struct Needed : Fifo<Needed>::Element
{
return ((char const *)(strtab + offset));
off_t offset;
Needed(off_t offset) : offset(offset) { }
char const *path(char const *strtab)
{
return ((char const *)(strtab + offset));
}
char const *name(char const *strtab)
{
return file(path(strtab));
}
};
Dependency const *_dep;
Object const &_obj;
Elf::Dyn const &_dynamic;
Allocator *_md_alloc = nullptr;
Hash_table *_hash_table = nullptr;
Elf::Rela *_reloca = nullptr;
unsigned long _reloca_size = 0;
Elf::Sym *_symtab = nullptr;
char *_strtab = nullptr;
unsigned long _strtab_size = 0;
Elf::Addr *_pltgot = nullptr;
Elf::Rel *_pltrel = nullptr;
unsigned long _pltrel_size = 0;
D_tag _pltrel_type = DT_NULL;
Func _init_function = nullptr;
Elf::Rel *_rel = nullptr;
unsigned long _rel_size = 0;
Fifo<Needed> _needed;
/**
* \throw Dynamic_section_missing
*/
Elf::Dyn const &_find_dynamic(Linker::Phdr const *p)
{
for (unsigned i = 0; i < p->count; i++)
if (p->phdr[i].p_type == PT_DYNAMIC)
return *reinterpret_cast<Elf::Dyn const *>
(p->phdr[i].p_vaddr + _obj.reloc_base());
throw Dynamic_section_missing();
}
char const *name(char const *strtab)
void _section_dt_needed(Elf::Dyn const *d)
{
return file(path(strtab));
if (!_md_alloc) {
error("unexpected call of section_dt_needed");
return;
}
Needed *n = new (*_md_alloc) Needed(d->un.ptr);
_needed.enqueue(n);
}
};
Dependency const *dep;
Object const *obj;
Elf::Dyn const *dynamic;
template <typename T>
void _section(T *member, Elf::Dyn const *d)
{
*member = (T)(_obj.reloc_base() + d->un.ptr);
}
Hash_table *hash_table = nullptr;
void _section_dt_debug(Elf::Dyn const *d)
{
Elf::Dyn *_d = const_cast<Elf::Dyn *>(d);
_d->un.ptr = (Elf::Addr)Debug::d();
}
Elf::Rela *reloca = nullptr;
unsigned long reloca_size = 0;
Elf::Sym *symtab = nullptr;
char *strtab = nullptr;
unsigned long strtab_size = 0;
Elf::Addr *pltgot = nullptr;
Elf::Rel *pltrel = nullptr;
unsigned long pltrel_size = 0;
D_tag pltrel_type = DT_NULL;
Func init_function = nullptr;
Elf::Rel *rel = nullptr;
unsigned long rel_size = 0;
Genode::Fifo<Needed> needed;
Dynamic(Dependency const *dep)
:
dep(dep), obj(dep->obj), dynamic((Elf::Dyn *)dynamic_address())
{
init();
}
Dynamic(Dependency const *dep, Object const *obj, Linker::Phdr const *phdr)
:
dep(dep), obj(obj), dynamic(find_dynamic(phdr))
{
init();
}
~Dynamic()
{
Needed *n;
while ((n = needed.dequeue()))
destroy(Genode::env()->heap(), n);
}
Elf::Dyn const *find_dynamic(Linker::Phdr const *p)
{
for (unsigned i = 0; i < p->count; i++)
if (p->phdr[i].p_type == PT_DYNAMIC)
return reinterpret_cast<Elf::Dyn const *>(p->phdr[i].p_vaddr + obj->reloc_base());
return 0;
}
void section_dt_needed(Elf::Dyn const *d)
{
Needed *n = new(Genode::env()->heap()) Needed(d->un.ptr);
needed.enqueue(n);
}
template <typename T>
void section(T *member, Elf::Dyn const *d)
{
*member = (T)(obj->reloc_base() + d->un.ptr);
}
void section_dt_debug(Elf::Dyn const *d)
{
Elf::Dyn *_d = const_cast<Elf::Dyn *>(d);
_d->un.ptr = (Elf::Addr)Debug::d();
}
void init()
{
for (Elf::Dyn const *d = dynamic; d->tag != DT_NULL; d++) {
switch (d->tag) {
case DT_NEEDED : section_dt_needed(d); break;
case DT_PLTRELSZ: pltrel_size = d->un.val; break;
case DT_PLTGOT : section<typeof(pltgot)>(&pltgot, d); break;
case DT_HASH : section<typeof(hash_table)>(&hash_table, d); break;
case DT_RELA : section<typeof(reloca)>(&reloca, d); break;
case DT_RELASZ : reloca_size = d->un.val; break;
case DT_SYMTAB : section<typeof(symtab)>(&symtab, d); break;
case DT_STRTAB : section<typeof(strtab)>(&strtab, d); break;
case DT_STRSZ : strtab_size = d->un.val; break;
case DT_INIT : section<typeof(init_function)>(&init_function, d); break;
case DT_PLTREL : pltrel_type = (D_tag)d->un.val; break;
case DT_JMPREL : section<typeof(pltrel)>(&pltrel, d); break;
case DT_REL : section<typeof(rel)>(&rel, d); break;
case DT_RELSZ : rel_size = d->un.val; break;
case DT_DEBUG : section_dt_debug(d); break;
void _init()
{
for (Elf::Dyn const *d = &_dynamic; d->tag != DT_NULL; d++) {
switch (d->tag) {
case DT_NEEDED : _section_dt_needed(d); break;
case DT_PLTRELSZ: _pltrel_size = d->un.val; break;
case DT_PLTGOT : _section<typeof(_pltgot)>(&_pltgot, d); break;
case DT_HASH : _section<typeof(_hash_table)>(&_hash_table, d); break;
case DT_RELA : _section<typeof(_reloca)>(&_reloca, d); break;
case DT_RELASZ : _reloca_size = d->un.val; break;
case DT_SYMTAB : _section<typeof(_symtab)>(&_symtab, d); break;
case DT_STRTAB : _section<typeof(_strtab)>(&_strtab, d); break;
case DT_STRSZ : _strtab_size = d->un.val; break;
case DT_INIT : _section<typeof(_init_function)>(&_init_function, d); break;
case DT_PLTREL : _pltrel_type = (D_tag)d->un.val; break;
case DT_JMPREL : _section<typeof(_pltrel)>(&_pltrel, d); break;
case DT_REL : _section<typeof(_rel)>(&_rel, d); break;
case DT_RELSZ : _rel_size = d->un.val; break;
case DT_DEBUG : _section_dt_debug(d); break;
default:
break;
}
}
}
}
void relocate()
{
plt_setup();
public:
if (pltrel_size) {
switch (pltrel_type) {
enum Pass { FIRST_PASS, SECOND_PASS };
Dynamic(Dependency const &dep)
:
_dep(&dep), _obj(dep.obj()), _dynamic(*(Elf::Dyn *)dynamic_address())
{
_init();
}
Dynamic(Allocator &md_alloc, Dependency const &dep, Object const &obj,
Linker::Phdr const *phdr)
:
_dep(&dep), _obj(obj), _dynamic(_find_dynamic(phdr)), _md_alloc(&md_alloc)
{
_init();
}
~Dynamic()
{
if (!_md_alloc)
return;
while (Needed *n = _needed.dequeue())
destroy(*_md_alloc, n);
}
void call_init_function() const
{
if (!_init_function)
return;
if (verbose_relocation)
log(_obj.name(), " init func ", _init_function);
_init_function();
}
Elf::Sym const *symbol(unsigned sym_index) const
{
if (sym_index > _hash_table->nchains())
return nullptr;
return _symtab + sym_index;
}
char const *symbol_name(Elf::Sym const &sym) const
{
return _strtab + sym.st_name;
}
void const *dynamic_ptr() const { return &_dynamic; }
void dep(Dependency const &dep) { _dep = &dep; }
Dependency const &dep() const { return *_dep; }
/*
* Use DT_HASH table address for linker, assuming that it will always be at
* the beginning of the file
*/
Elf::Addr link_map_addr() const { return trunc_page((Elf::Addr)_hash_table); }
/**
* Lookup symbol name in this ELF
*/
Elf::Sym const *lookup_symbol(char const *name, unsigned long hash) const
{
Hash_table *h = _hash_table;
if (!h->buckets())
return nullptr;
unsigned long sym_index = h->buckets()[hash % h->nbuckets()];
/* traverse hash chain */
for (; sym_index != STN_UNDEF; sym_index = h->chains()[sym_index])
{
/* bad object */
if (sym_index > h->nchains())
return nullptr;
Elf::Sym const *sym = symbol(sym_index);
char const *sym_name = symbol_name(*sym);
/* this omitts everything but 'NOTYPE', 'OBJECT', and 'FUNC' */
if (sym->type() > STT_FUNC)
continue;
if (sym->st_value == 0)
continue;
/* check for symbol name */
if (name[0] != sym_name[0] || strcmp(name, sym_name))
continue;
return sym;
}
return nullptr;
}
/**
* \throw Address_info::Invalid_address
*/
Elf::Sym const &symbol_by_addr(addr_t addr) const
{
addr_t const reloc_base = _obj.reloc_base();
for (unsigned long i = 0; i < _hash_table->nchains(); i++)
{
Elf::Sym const *sym = symbol(i);
if (!sym)
continue;
/* skip */
if (sym->st_shndx == SHN_UNDEF || sym->st_shndx == SHN_COMMON)
continue;
addr_t const sym_addr = reloc_base + sym->st_value;
if (addr < sym_addr || addr >= sym_addr + sym->st_size)
continue;
return *sym;
}
throw Address_info::Invalid_address();
}
/**
* Call functor for each dependency, passing the path as argument
*/
template <typename FUNC>
void for_each_dependency(FUNC const &fn) const
{
for (Needed *n = _needed.head(); n; n = n->next())
fn(n->path(_strtab));
}
void relocate(Bind bind)
{
plt_setup();
if (_pltrel_size) {
switch (_pltrel_type) {
case DT_RELA:
case DT_REL:
Reloc_plt(obj, pltrel_type, pltrel, pltrel_size);
Reloc_plt(_obj, _pltrel_type, _pltrel, _pltrel_size);
break;
default:
Genode::error("LD: Invalid PLT relocation ", (int)pltrel_type);
error("LD: Invalid PLT relocation ", (int)_pltrel_type);
throw Incompatible();
}
}
relocate_non_plt(bind, FIRST_PASS);
}
relocate_non_plt();
}
void plt_setup()
{
if (_pltgot)
Plt_got r(*_dep, _pltgot);
}
void plt_setup()
{
if (pltgot)
Plt_got r(dep, pltgot);
}
void relocate_non_plt(Bind bind, Pass pass)
{
if (_reloca)
Reloc_non_plt r(*_dep, _reloca, _reloca_size);
void relocate_non_plt(bool second_pass = false)
{
if (reloca)
Reloc_non_plt r(dep, reloca, reloca_size);
if (_rel)
Reloc_non_plt r(*_dep, _rel, _rel_size, pass == SECOND_PASS);
if (rel)
Reloc_non_plt r(dep, rel, rel_size, second_pass);
if (bind == BIND_NOW)
Reloc_bind_now r(*_dep, _pltrel, _pltrel_size);
}
if (bind_now)
Reloc_bind_now r(dep, pltrel, pltrel_size);
}
Elf::Rel const *pltrel() const { return _pltrel; }
D_tag pltrel_type() const { return _pltrel_type; }
};
#endif /* _INCLUDE__DYNAMIC_H_ */

View File

@ -1,4 +1,4 @@
/**
/*
* \brief ELF file setup
* \author Sebastian Sumpf
* \date 2015-03-12
@ -14,23 +14,20 @@
#ifndef _INCLUDE__FILE_H_
#define _INCLUDE__FILE_H_
/* Genode includes */
#include <os/attached_rom_dataspace.h>
/* local includes */
#include <util.h>
#include <debug.h>
#include <region_map.h>
namespace Linker {
struct Phdr;
struct File;
/**
* Load object
*
* \param path Path of object
* \param load True, load binary; False, load ELF header only
*
* \throw Invalid_file Segment is neither read-only/executable or read/write
* \throw Region_conflict There is already something at the given address
* \throw Incompatible Not an ELF
*
* \return File descriptor
*/
File const *load(char const *path, bool load = true);
struct Elf_file;
}
@ -47,7 +44,7 @@ struct Linker::Phdr
/**
* Loaded ELF file
* ELF file info
*/
struct Linker::File
{
@ -72,4 +69,208 @@ struct Linker::File
unsigned elf_phdr_count() const { return phdr.count; }
};
/**
* Mapped ELF file
*/
struct Linker::Elf_file : File
{
Env &env;
Rom_connection rom;
Ram_dataspace_capability ram_cap[Phdr::MAX_PHDR];
bool const loaded;
Elf_file(Env &env, Allocator &md_alloc, char const *name, bool load)
:
env(env), rom(env, name), loaded(load)
{
load_phdr();
/*
* Initialize the linker area at the link address of the binary,
* which happens to be the first loaded 'Elf_file'.
*
* XXX Move this initialization to the linker's 'construct' function
* once we use relocatable binaries.
*/
if (load && !Region_map::r().constructed())
Region_map::r().construct(env, md_alloc, start);
if (load)
load_segments();
}
virtual ~Elf_file()
{
if (loaded)
unload_segments();
}
/**
* Check if ELF is sane
*/
bool check_compat(Elf::Ehdr const &ehdr)
{
char const * const ELFMAG = "\177ELF";
if (memcmp(&ehdr, ELFMAG, SELFMAG) != 0) {
error("LD: binary is not an ELF");
return false;
}
if (ehdr.e_ident[EI_CLASS] != ELFCLASS) {
error("LD: support for 32/64-bit objects only");
return false;
}
return true;
}
/**
* Copy program headers and read entry point
*/
void load_phdr()
{
{
/* temporary map the binary to read the program header */
Attached_dataspace ds(env.rm(), rom.dataspace());
Elf::Ehdr const &ehdr = *ds.local_addr<Elf::Ehdr>();
if (!check_compat(ehdr))
throw Incompatible();
/* set entry point and program header information */
phdr.count = ehdr.e_phnum;
entry = (Entry)ehdr.e_entry;
/* copy program headers */
addr_t header = (addr_t)&ehdr + ehdr.e_phoff;
for (unsigned i = 0; i < phdr.count; i++, header += ehdr.e_phentsize)
memcpy(&phdr.phdr[i], (void *)header, ehdr.e_phentsize);
}
Phdr p;
loadable_segments(p);
/* start vaddr */
start = trunc_page(p.phdr[0].p_vaddr);
Elf::Phdr *ph = &p.phdr[p.count - 1];
/* size of lodable segments */
size = round_page(ph->p_vaddr + ph->p_memsz) - start;
}
/**
* Find PT_LOAD segemnts
*/
void loadable_segments(Phdr &result)
{
for (unsigned i = 0; i < phdr.count; i++) {
Elf::Phdr *ph = &phdr.phdr[i];
if (ph->p_type != PT_LOAD)
continue;
if (ph->p_align & (0x1000 - 1)) {
error("LD: Unsupported alignment ", (void *)ph->p_align);
throw Incompatible();
}
result.phdr[result.count++] = *ph;
}
}
bool is_rx(Elf::Phdr const &ph) {
return ((ph.p_flags & PF_MASK) == (PF_R | PF_X)); }
bool is_rw(Elf::Phdr const &ph) {
return ((ph.p_flags & PF_MASK) == (PF_R | PF_W)); }
/**
* Load PT_LOAD segments
*/
void load_segments()
{
Phdr p;
/* search for PT_LOAD */
loadable_segments(p);
/* allocate region */
reloc_base = Region_map::r()->alloc_region(size, start);
reloc_base = (start == reloc_base) ? 0 : reloc_base;
if (verbose_loading)
log("LD: reloc_base: ", Hex(reloc_base),
" start: ", Hex(start),
" end: ", Hex(reloc_base + start + size));
for (unsigned i = 0; i < p.count; i++) {
Elf::Phdr *ph = &p.phdr[i];
if (is_rx(*ph))
load_segment_rx(*ph);
else if (is_rw(*ph))
load_segment_rw(*ph, i);
else {
error("LD: Non-RW/RX segment");
throw Invalid_file();
}
}
}
/**
* Map read-only segment
*/
void load_segment_rx(Elf::Phdr const &p)
{
Region_map::r()->attach_executable(rom.dataspace(),
trunc_page(p.p_vaddr) + reloc_base,
round_page(p.p_memsz),
trunc_page(p.p_offset));
}
/**
* Copy read-write segment
*/
void load_segment_rw(Elf::Phdr const &p, int nr)
{
void *src = env.rm().attach(rom.dataspace(), 0, p.p_offset);
addr_t dst = p.p_vaddr + reloc_base;
ram_cap[nr] = env.ram().alloc(p.p_memsz);
Region_map::r()->attach_at(ram_cap[nr], dst);
memcpy((void*)dst, src, p.p_filesz);
/* clear if file size < memory size */
if (p.p_filesz < p.p_memsz)
memset((void *)(dst + p.p_filesz), 0, p.p_memsz - p.p_filesz);
env.rm().detach(src);
}
/**
* Unmap segements, RM regions, and free allocated dataspaces
*/
void unload_segments()
{
Phdr p;
loadable_segments(p);
/* detach from RM area */
for (unsigned i = 0; i < p.count; i++)
Region_map::r()->detach(trunc_page(p.phdr[i].p_vaddr) + reloc_base);
/* free region from RM area */
Region_map::r()->free_region(trunc_page(p.phdr[0].p_vaddr) + reloc_base);
/* free ram of RW segments */
for (unsigned i = 0; i < Phdr::MAX_PHDR; i++)
if (ram_cap[i].valid()) {
env.ram().free(ram_cap[i]);
}
}
};
#endif /* _INCLUDE__FILE_H_ */

View File

@ -25,7 +25,7 @@ namespace Linker {
/**
* Handle static construction and relocation of ELF files
*/
struct Linker::Init : Genode::List<Object>
struct Linker::Init : List<Object>
{
bool in_progress = false;
bool restart = false;
@ -39,7 +39,7 @@ struct Linker::Init : Genode::List<Object>
Object *contains(char const *file)
{
for (Object *elf = first(); elf; elf = elf->next_init())
if (!Genode::strcmp(file, elf->name()))
if (!strcmp(file, elf->name()))
return elf;
return nullptr;
@ -52,24 +52,25 @@ struct Linker::Init : Genode::List<Object>
insert(elf);
/* re-order dependencies */
for (Dynamic::Needed *n = elf->dynamic()->needed.head(); n; n = n->next()) {
char const *path = n->path(elf->dynamic()->strtab);
Object *e;
elf->dynamic().for_each_dependency([&] (char const *path) {
if ((e = contains(Linker::file(path))))
// for (Dynamic::Needed *n = elf->dynamic().needed.head(); n; n = n->next()) {
// char const *path = n->path(elf->dynamic().strtab);
if (Object *e = contains(Linker::file(path)))
reorder(e);
}
});
}
void initialize()
void initialize(Bind bind)
{
Object *obj = first();
/* relocate */
for (; obj; obj = obj->next_init()) {
if (verbose_relocation)
Genode::log("Relocate ", obj->name());
obj->relocate();
log("Relocate ", obj->name());
obj->relocate(bind);
}
/*
@ -91,13 +92,7 @@ struct Linker::Init : Genode::List<Object>
Object *next = obj->next_init();
remove(obj);
if (obj->dynamic()->init_function) {
if (verbose_relocation)
Genode::log(obj->name(), " init func ", obj->dynamic()->init_function);
obj->dynamic()->init_function();
}
obj->dynamic().call_init_function();
obj = restart ? first() : next;
restart = false;

View File

@ -1,4 +1,4 @@
/**
/*
* \brief Generic linker definitions
* \author Sebastian Sumpf
* \date 2014-10-24
@ -14,28 +14,12 @@
#ifndef _INCLUDE__LINKER_H_
#define _INCLUDE__LINKER_H_
#include <base/exception.h>
#include <base/env.h>
#include <base/shared_object.h>
#include <util/fifo.h>
#include <util/misc_math.h>
#include <util/string.h>
#include <types.h>
#include <debug.h>
#include <elf.h>
#include <file.h>
#include <util.h>
/**
* Debugging
*/
constexpr bool verbose_lookup = false;
constexpr bool verbose_exception = false;
constexpr bool verbose_shared = false;
constexpr bool verbose_loading = false;
extern Elf::Addr etext;
/**
* Forward declartions and helpers
*/
@ -48,11 +32,6 @@ namespace Linker {
typedef void (*Func)(void);
/**
* Eager binding enable
*/
extern bool bind_now;
/**
* Print diagnostic information
*
@ -75,7 +54,7 @@ namespace Linker {
*
* \return Symbol information
*/
Elf::Sym const *lookup_symbol(unsigned sym_index, Dependency const *, Elf::Addr *base,
Elf::Sym const *lookup_symbol(unsigned sym_index, Dependency const &, Elf::Addr *base,
bool undef = false, bool other = false);
/**
@ -93,20 +72,22 @@ namespace Linker {
*
* \return Symbol information
*/
Elf::Sym const *lookup_symbol(char const *name, Dependency const *dep, Elf::Addr *base,
Elf::Sym const *lookup_symbol(char const *name, Dependency const &dep, Elf::Addr *base,
bool undef = false, bool other = false);
/**
* Load an ELF (setup segments and map program header)
*
* \param path File to load
* \param dep Dependency entry for new object
* \param flags 'Genode::Shared_object::KEEP' will not unload the ELF, if the
* reference count reaches zero
* \param md_alloc allocator used for dyamically allocater meta data
* \param path rom module to load
* \param dep dependency entry for new object
* \param flags 'Shared_object::KEEP' will not unload the ELF,
* if the reference count reaches zero
*
* \return Linker::Object
*/
Object *load(char const *path, Dependency *dep, unsigned flags = 0);
Object &load(Env &, Allocator &md_alloc, char const *path, Dependency &dep,
Keep keep);
/**
* Returns the head of the global object list
@ -119,16 +100,9 @@ namespace Linker {
Dependency *binary_root_dep();
/**
* Force to map the program header of the dynamic linker
* Global ELF access lock
*/
void load_linker_phdr();
/**
* Exceptions
*/
class Incompatible : Genode::Exception { };
class Invalid_file : Genode::Exception { };
class Not_found : Genode::Exception { };
Lock &lock();
/**
* Invariants
@ -141,42 +115,45 @@ namespace Linker {
/**
* Shared object or binary
*/
class Linker::Object : public Genode::Fifo<Object>::Element,
public Genode::List<Object>::Element
class Linker::Object : public Fifo<Object>::Element,
public List<Object>::Element
{
public:
typedef String<128> Name;
protected:
enum { MAX_PATH = 128 };
char _name[MAX_PATH];
Name _name;
File const *_file = nullptr;
Elf::Addr _reloc_base = 0;
public:
Object(Elf::Addr reloc_base) : _reloc_base(reloc_base) { }
Object(char const *path, File const *file)
: _file(file), _reloc_base(file->reloc_base)
void init(Name const &name, Elf::Addr reloc_base)
{
Genode::strncpy(_name, Linker::file(path), MAX_PATH);
_name = name;
_reloc_base = reloc_base;
}
virtual ~Object()
void init(Name const &name, File const &file)
{
if (_file)
destroy(Genode::env()->heap(), const_cast<File *>(_file));
_name = name;
_file = &file;
_reloc_base = file.reloc_base;
}
Elf::Addr reloc_base() const { return _reloc_base; }
char const *name() const { return _name; }
virtual ~Object() { }
File const *file() { return _file; }
Elf::Size const size() const { return _file ? _file->size : 0; }
Elf::Addr reloc_base() const { return _reloc_base; }
char const *name() const { return _name.string(); }
File const *file() const { return _file; }
Elf::Size const size() const { return _file ? _file->size : 0; }
virtual bool is_linker() const = 0;
virtual bool is_binary() const = 0;
virtual void relocate() = 0;
virtual void relocate(Bind) = 0;
virtual void load() = 0;
virtual bool unload() { return false;}
@ -184,7 +161,7 @@ class Linker::Object : public Genode::Fifo<Object>::Element,
/**
* Next object in global object list
*/
virtual Object *next_obj() const = 0;
virtual Object *next_obj() const = 0;
/**
* Next object in initialization list
@ -194,79 +171,104 @@ class Linker::Object : public Genode::Fifo<Object>::Element,
/**
* Return dynamic section of ELF
*/
virtual Dynamic *dynamic() = 0;
virtual Dynamic const &dynamic() const = 0;
/**
* Return link map for ELF
*/
virtual Link_map *link_map() = 0;
virtual Link_map const &link_map() const = 0;
/**
* Return type of 'symbol_at_address'
*/
struct Symbol_info { addr_t addr; char const *name; };
/**
* Return address info for symboal at addr
*/
virtual void info(Genode::addr_t addr, Genode::Address_info &info) = 0;
/**
* Global ELF access lock
*/
static Genode::Lock & lock()
{
static Genode::Lock _lock;
return _lock;
}
virtual Symbol_info symbol_at_address(addr_t addr) const = 0;
};
/**
* Dependency of object
*/
struct Linker::Dependency : Genode::Fifo<Dependency>::Element
class Linker::Dependency : public Fifo<Dependency>::Element, Noncopyable
{
Object *obj = nullptr;
Root_object *root = nullptr;
private:
Dependency(Object *obj, Root_object *root) : obj(obj), root(root) { }
Object &_obj;
Root_object *_root = nullptr;
Allocator *_md_alloc = nullptr;
Dependency(char const *path, Root_object *root, Genode::Fifo<Dependency> * const dep,
unsigned flags = 0);
~Dependency();
/**
* Check if file is in this dependency tree
*/
bool in_dep(char const *file, Fifo<Dependency> const &);
/**
* Load dependent ELF object
*/
void load_needed(Genode::Fifo<Dependency> * const dep, unsigned flags = 0);
public:
/**
* Check if file is in this dependency tree
*/
bool in_dep(char const *file, Genode::Fifo<Dependency> *const dep);
/*
* Called by 'Ld' constructor
*/
Dependency(Object &obj, Root_object *root) : _obj(obj), _root(root) { }
Dependency(Env &, Allocator &, char const *path, Root_object *,
Fifo<Dependency> &, Keep);
~Dependency();
/**
* Load dependent ELF object
*/
void load_needed(Env &, Allocator &, Fifo<Dependency> &, Keep);
bool root() const { return _root != nullptr; }
Object const &obj() const { return _obj; }
/**
* Return first element of dependency list
*/
Dependency const &first() const;
};
/**
* Root of dependencies
*/
struct Linker::Root_object
class Linker::Root_object
{
Genode::Fifo<Dependency> dep;
private:
/* main root */
Root_object() { };
Fifo<Dependency> _deps;
/* runtime loaded root components */
Root_object(char const *path, unsigned flags = 0);
Allocator &_md_alloc;
~Root_object()
{
Dependency *d;
while ((d = dep.dequeue()))
destroy(Genode::env()->heap(), d);
}
public:
Link_map const *link_map() const
{
return dep.head()->obj->link_map();
}
/* main root */
Root_object(Allocator &md_alloc) : _md_alloc(md_alloc) { };
/* runtime loaded root components */
Root_object(Env &, Allocator &, char const *path, Bind, Keep);
~Root_object()
{
while (Dependency *d = _deps.dequeue())
destroy(_md_alloc, d);
}
Link_map const &link_map() const
{
return _deps.head()->obj().link_map();
}
Dependency const *first_dep() const { return _deps.head(); }
void enqueue(Dependency &dep) { _deps.enqueue(&dep); }
Fifo<Dependency> &deps() { return _deps; }
};
#endif /* _INCLUDE__LINKER_H_ */

View File

@ -0,0 +1,118 @@
/*
* \brief Linker area
* \author Sebastian Sumpf
* \author Norman Feske
* \date 2015-03-12
*/
/*
* Copyright (C) 2015-2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__REGION_MAP_H_
#define _INCLUDE__REGION_MAP_H_
/* Genode includes */
#include <base/env.h>
#include <region_map/client.h>
#include <base/allocator_avl.h>
#include <util/retry.h>
#include <util/volatile_object.h>
/* base-internal includes */
#include <base/internal/page_size.h>
/* local includes */
#include <linker.h>
namespace Linker { class Region_map; }
/**
* Managed dataspace for ELF objects (singelton)
*/
class Linker::Region_map
{
public:
typedef Region_map_client::Local_addr Local_addr;
typedef Region_map_client::Region_conflict Region_conflict;
private:
Env &_env;
Region_map_client _rm { _env.pd().linker_area() };
Allocator_avl _range; /* VM range allocator */
addr_t const _base; /* base address of dataspace */
protected:
Region_map(Env &env, Allocator &md_alloc, addr_t base)
:
_env(env), _range(&md_alloc),
_base((addr_t)_env.rm().attach_at(_rm.dataspace(), base))
{
_range.add_range(base, Pd_session::LINKER_AREA_SIZE);
}
public:
typedef Lazy_volatile_object<Region_map> Constructible_region_map;
static Constructible_region_map &r();
/**
* Reserve VM region of 'size' at 'vaddr'. Allocate any free region if
* 'vaddr' is zero
*/
addr_t alloc_region(size_t size, addr_t vaddr = 0)
{
addr_t addr = vaddr;
if (addr && (_range.alloc_addr(size, addr).error()))
throw Region_conflict();
else if (!addr &&
_range.alloc_aligned(size, (void **)&addr,
get_page_size_log2()).error())
{
throw Region_conflict();
}
return addr;
}
void free_region(addr_t vaddr) { _range.free((void *)vaddr); }
/**
* Overwritten from 'Region_map_client'
*/
Local_addr attach_at(Dataspace_capability ds, addr_t local_addr,
size_t size = 0, off_t offset = 0)
{
return retry<Genode::Region_map::Out_of_metadata>(
[&] () {
return _rm.attach_at(ds, local_addr - _base, size, offset);
},
[&] () { _env.parent().upgrade(_env.pd_session_cap(), "ram_quota=8K"); });
}
/**
* Overwritten from 'Region_map_client'
*/
Local_addr attach_executable(Dataspace_capability ds, addr_t local_addr,
size_t size = 0, off_t offset = 0)
{
return retry<Genode::Region_map::Out_of_metadata>(
[&] () {
return _rm.attach_executable(ds, local_addr - _base, size, offset);
},
[&] () { _env.parent().upgrade(_env.pd_session_cap(), "ram_quota=8K"); });
}
void detach(Local_addr local_addr) { _rm.detach((addr_t)local_addr - _base); }
};
#endif /* _INCLUDE__REGION_MAP_H_ */

View File

@ -18,9 +18,9 @@
constexpr bool verbose_relocation = false;
static inline bool verbose_reloc(Linker::Dependency const *d)
static inline bool verbose_reloc(Linker::Dependency const &d)
{
return d->root && verbose_relocation;
return d.root() && verbose_relocation;
}
/**
@ -45,12 +45,12 @@ namespace Linker
*/
struct Linker::Plt_got
{
Plt_got(Dependency const *dep, Elf::Addr *pltgot)
Plt_got(Dependency const &dep, Elf::Addr *pltgot)
{
if (verbose_relocation)
Genode::log("OBJ: ", dep->obj->name(), " (", dep, ")");
log("OBJ: ", dep.obj().name(), " (", &dep, ")");
pltgot[1] = (Elf::Addr) dep; /* ELF object */
pltgot[1] = (Elf::Addr) &dep; /* ELF object */
pltgot[2] = (Elf::Addr) &_jmp_slot; /* Linker entry */
}
};
@ -62,11 +62,11 @@ struct Linker::Plt_got
template<typename REL, unsigned TYPE, unsigned JMPSLOT>
struct Linker::Reloc_plt_generic
{
Reloc_plt_generic(Object const *obj, D_tag const type,
Reloc_plt_generic(Object const &obj, D_tag const type,
Elf::Rel const *start, unsigned long size)
{
if (type != TYPE) {
Genode::error("LD: Unsupported PLT relocation type: ", (int)type);
error("LD: Unsupported PLT relocation type: ", (int)type);
throw Incompatible();
}
@ -75,13 +75,13 @@ struct Linker::Reloc_plt_generic
for (; rel < end; rel++) {
if (rel->type() != JMPSLOT) {
Genode::error("LD: Unsupported PLT relocation ", (int)rel->type());
error("LD: Unsupported PLT relocation ", (int)rel->type());
throw Incompatible();
}
/* find relocation address and add relocation base */
Elf::Addr *addr = (Elf::Addr *)(obj->reloc_base() + rel->offset);
*addr += obj->reloc_base();
Elf::Addr *addr = (Elf::Addr *)(obj.reloc_base() + rel->offset);
*addr += obj.reloc_base();
}
}
};
@ -91,7 +91,7 @@ class Linker::Reloc_non_plt_generic
{
protected:
Dependency const *_dep;
Dependency const &_dep;
/**
* Copy relocations, these are just for the main program, we can do them
@ -101,9 +101,9 @@ class Linker::Reloc_non_plt_generic
template <typename REL>
void _copy(REL const *rel, Elf::Addr *addr)
{
if (!_dep->obj->is_binary()) {
Genode::error("LD: copy relocation in DSO "
"(", _dep->obj->name(), " at ", addr, ")");
if (!_dep.obj().is_binary()) {
error("LD: copy relocation in DSO "
"(", _dep.obj().name(), " at ", addr, ")");
throw Incompatible();
}
@ -112,22 +112,22 @@ class Linker::Reloc_non_plt_generic
/* search symbol in other objects, do not return undefined symbols */
if (!(sym = lookup_symbol(rel->sym(), _dep, &reloc_base, false, true))) {
Genode::warning("LD: symbol not found");
warning("LD: symbol not found");
return;
}
Elf::Addr src = reloc_base + sym->st_value;
Genode::memcpy(addr, (void *)src, sym->st_size);
memcpy(addr, (void *)src, sym->st_size);
if (verbose_relocation)
Genode::log("Copy relocation: ", Genode::Hex(src),
" -> ", addr, " (", Genode::Hex(sym->st_size), " bytes)"
" val: ", Genode::Hex(sym->st_value));
log("Copy relocation: ", Hex(src),
" -> ", addr, " (", Hex(sym->st_size), " bytes)"
" val: ", Hex(sym->st_value));
}
public:
Reloc_non_plt_generic(Dependency const *dep) : _dep(dep) { }
Reloc_non_plt_generic(Dependency const &dep) : _dep(dep) { }
};
@ -141,11 +141,11 @@ class Linker::Reloc_jmpslot_generic
public:
Reloc_jmpslot_generic(Dependency const *dep, unsigned const type, Elf::Rel const* pltrel,
Reloc_jmpslot_generic(Dependency const &dep, unsigned const type, Elf::Rel const* pltrel,
Elf::Size const index)
{
if (type != TYPE) {
Genode::error("LD: unsupported JMP relocation type: ", (int)type);
error("LD: unsupported JMP relocation type: ", (int)type);
throw Incompatible();
}
@ -154,19 +154,19 @@ class Linker::Reloc_jmpslot_generic
Elf::Addr reloc_base;
if (!(sym = lookup_symbol(rel->sym(), dep, &reloc_base))) {
Genode::warning("LD: symbol not found");
warning("LD: symbol not found");
return;
}
/* write address of symbol to jump slot */
_addr = (Elf::Addr *)(dep->obj->reloc_base() + rel->offset);
_addr = (Elf::Addr *)(dep.obj().reloc_base() + rel->offset);
*_addr = reloc_base + sym->st_value;
if (verbose_relocation) {
Genode::log("jmp: rbase ", Genode::Hex(reloc_base),
" s: ", sym, " sval: ", Genode::Hex(sym->st_value));
Genode::log("jmp_slot at ", _addr, " -> ", *_addr);
log("jmp: rbase ", Hex(reloc_base),
" s: ", sym, " sval: ", Hex(sym->st_value));
log("jmp_slot at ", _addr, " -> ", *_addr);
}
}
@ -180,7 +180,7 @@ class Linker::Reloc_jmpslot_generic
template <typename REL, unsigned TYPE>
struct Linker::Reloc_bind_now_generic
{
Reloc_bind_now_generic(Dependency const *dep, Elf::Rel const *pltrel, unsigned long const size)
Reloc_bind_now_generic(Dependency const &dep, Elf::Rel const *pltrel, unsigned long const size)
{
Elf::Size last_index = size / sizeof(REL);

View File

@ -0,0 +1,43 @@
/*
* \brief Common types used within the linker
* \author Norman Feske
* \date 2016-10-27
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__TYPES_H_
#define _INCLUDE__TYPES_H_
#include <base/exception.h>
#include <base/env.h>
#include <base/shared_object.h>
#include <util/volatile_object.h>
#include <util/fifo.h>
#include <util/misc_math.h>
#include <util/string.h>
namespace Linker {
using namespace Genode;
/**
* Exceptions
*/
class Incompatible : Exception { };
class Invalid_file : Exception { };
class Not_found : Exception { };
enum Keep { DONT_KEEP = Shared_object::DONT_KEEP,
KEEP = Shared_object::KEEP };
enum Bind { BIND_LAZY = Shared_object::BIND_LAZY,
BIND_NOW = Shared_object::BIND_NOW };
}
#endif /* _INCLUDE__TYPES_H_ */

View File

@ -21,11 +21,11 @@ namespace Linker {
*/
template <typename T>
static inline T trunc_page(T addr) {
return addr & Genode::_align_mask((T)12); }
return addr & _align_mask((T)12); }
template <typename T>
static inline T round_page(T addr) {
return Genode::align_addr(addr, (T)12); }
return align_addr(addr, (T)12); }
/**
* Extract file name from path

View File

@ -18,10 +18,16 @@
#include <util/list.h>
#include <util/string.h>
#include <base/thread.h>
#include <base/heap.h>
/* base-internal includes */
#include <base/internal/unmanaged_singleton.h>
#include <base/internal/globals.h>
/* local includes */
#include <dynamic.h>
#include <init.h>
#include <region_map.h>
using namespace Linker;
@ -32,11 +38,10 @@ namespace Linker {
struct Binary;
struct Link_map;
struct Debug;
struct Config;
};
static Binary *binary = 0;
bool Linker::bind_now = false;
static Binary *binary_ptr = nullptr;
bool Linker::verbose = false;
Link_map *Link_map::first;
@ -46,6 +51,25 @@ Link_map *Link_map::first;
int genode_atexit(Linker::Func);
Linker::Region_map::Constructible_region_map &Linker::Region_map::r()
{
/*
* The capabilities in this class become invalid when doing a
* fork in the noux environment. Hence avoid destruction of
* the singleton object as the destructor would try to access
* the capabilities also in the forked process.
*/
return *unmanaged_singleton<Constructible_region_map>();
}
Genode::Lock &Linker::lock()
{
static Lock _lock;
return _lock;
}
/**************************************************************
** ELF object types (shared object, dynamic binaries, ldso **
**************************************************************/
@ -53,191 +77,165 @@ int genode_atexit(Linker::Func);
/**
* The actual ELF object, one per file
*/
struct Linker::Elf_object : Object, Genode::Fifo<Elf_object>::Element
class Linker::Elf_object : public Object, public Fifo<Elf_object>::Element
{
Dynamic dyn;
Link_map map;
unsigned ref_count = 1;
unsigned flags = 0;
bool relocated = false;
private:
Elf_object(Dependency const *dep, Elf::Addr reloc_base)
: Object(reloc_base), dyn(dep)
{ }
Link_map _map;
unsigned _ref_count = 1;
unsigned const _keep = KEEP;
bool _relocated = false;
Elf_object(char const *path, Dependency const *dep, unsigned flags = 0)
:
Object(path, Linker::load(Linker::file(path))), dyn(dep, this, &_file->phdr),
flags(flags)
{
/* register for static construction and relocation */
Init::list()->insert(this);
obj_list()->enqueue(this);
/*
* Optional ELF file, skipped for initial 'Ld' initialization
*/
Lazy_volatile_object<Elf_file> _elf_file;
/* add to link map */
Debug::state_change(Debug::ADD, nullptr);
setup_link_map();
Debug::state_change(Debug::CONSISTENT, &map);
}
virtual ~Elf_object()
{
if (!_file)
return;
if (verbose_loading)
Genode::log("LD: destroy ELF object: ", name());
/* remove from link map */
Debug::state_change(Debug::DELETE, &map);
Link_map::remove(&map);
Debug::state_change(Debug::CONSISTENT, nullptr);
/* remove from loaded objects list */
obj_list()->remove(this);
}
/**
* Return symbol of given number from ELF
*/
Elf::Sym const *symbol(unsigned sym_index) const
{
if (sym_index > dyn.hash_table->nchains())
return 0;
return dyn.symtab + sym_index;
}
/**
* Return name of given symbol
*/
char const *symbol_name(Elf::Sym const *sym) const {
return dyn.strtab + sym->st_name;
}
/**
* Lookup symbol name in this ELF
*/
Elf::Sym const *lookup_symbol(char const *name, unsigned long hash) const
{
Hash_table *h = dyn.hash_table;
if (!h->buckets())
return nullptr;
unsigned long sym_index = h->buckets()[hash % h->nbuckets()];
/* traverse hash chain */
for (; sym_index != STN_UNDEF; sym_index = h->chains()[sym_index])
bool _init_elf_file(Env &env, Allocator &md_alloc, char const *path)
{
/* bad object */
if (sym_index > h->nchains())
return nullptr;
Elf::Sym const *sym = symbol(sym_index);
char const *sym_name = symbol_name(sym);
/* this omitts everything but 'NOTYPE', 'OBJECT', and 'FUNC' */
if (sym->type() > STT_FUNC)
continue;
if (sym->st_value == 0)
continue;
/* check for symbol name */
if (name[0] != sym_name[0] || Genode::strcmp(name, sym_name))
continue;
return sym;
_elf_file.construct(env, md_alloc, Linker::file(path), true);
Object::init(Linker::file(path), *_elf_file);
return true;
}
return nullptr;
}
bool const _elf_file_initialized;
/**
* Fill-out link map infos for this ELF
*/
void setup_link_map()
{
map.addr = _file ? _file->start + reloc_base() : reloc_base();
map.path = name();
map.dynamic = (void *)dyn.dynamic;
Dynamic _dyn;
Link_map::add(&map);
};
public:
Link_map *link_map() override { return &map; }
Dynamic *dynamic() override { return &dyn; }
void relocate() override
{
if (!relocated)
dyn.relocate();
relocated = true;
}
void info(Genode::addr_t addr, Genode::Address_info &info) override
{
info.path = name();
info.base = map.addr;
info.addr = 0;
Hash_table *h = dyn.hash_table;
for (unsigned long sym_index = 0; sym_index < h->nchains(); sym_index++)
Elf_object(Dependency const &dep, Object::Name const &name,
Elf::Addr reloc_base)
:
_elf_file_initialized(false), _dyn(dep)
{
Elf::Sym const *sym = symbol(sym_index);
/* skip */
if (sym->st_shndx == SHN_UNDEF || sym->st_shndx == SHN_COMMON)
continue;
Genode::addr_t sym_addr = reloc_base() + sym->st_value;
if (sym_addr > addr || sym_addr < info.addr)
continue;
info.addr = sym_addr;
info.name = symbol_name(sym);
if (info.addr == addr)
break;
Object::init(name, reloc_base);
}
if (!info.addr)
throw Genode::Address_info::Invalid_address();
}
Elf_object(Env &env, Allocator &md_alloc, char const *path,
Dependency const &dep, Keep keep)
:
_keep(keep),
_elf_file_initialized(_init_elf_file(env, md_alloc, path)),
_dyn(md_alloc, dep, *this, &_elf_file->phdr)
{
/* register for static construction and relocation */
Init::list()->insert(this);
obj_list()->enqueue(this);
/**
* Next in initializion list
*/
Object *next_init() const override {
return Genode::List<Object>::Element::next();
}
/* add to link map */
Debug::state_change(Debug::ADD, nullptr);
setup_link_map();
Debug::state_change(Debug::CONSISTENT, &_map);
}
/**
* Next in object list
*/
Object *next_obj() const override {
return Genode::Fifo<Elf_object>::Element::next();
}
virtual ~Elf_object()
{
if (!_file)
return;
/**
* Object list
*/
static Genode::Fifo<Elf_object> *obj_list()
{
static Genode::Fifo<Elf_object> _list;
return &_list;
}
if (verbose_loading)
log("LD: destroy ELF object: ", name());
/* remove from link map */
Debug::state_change(Debug::DELETE, &_map);
Link_map::remove(&_map);
Debug::state_change(Debug::CONSISTENT, nullptr);
void load() override { ref_count++; }
bool unload() override { return !(--ref_count) && !(flags & Genode::Shared_object::KEEP); }
/* remove from loaded objects list */
obj_list()->remove(this);
}
bool is_linker() const override { return false; }
bool is_binary() const override { return false; }
/**
* Return symbol of given number from ELF
*/
Elf::Sym const *symbol(unsigned sym_index) const
{
return _dyn.symbol(sym_index);
}
// XXX remove this accessor?
void link_map_addr(addr_t addr) { _map.addr = addr; }
/**
* Return name of given symbol
*/
char const *symbol_name(Elf::Sym const &sym) const
{
return _dyn.symbol_name(sym);
}
Elf::Sym const *lookup_symbol(char const *name, unsigned long hash) const
{
return _dyn.lookup_symbol(name, hash);
}
/**
* Fill-out link map infos for this ELF
*/
void setup_link_map()
{
_map.addr = _file ? _file->start + reloc_base() : reloc_base();
_map.path = name();
_map.dynamic = _dyn.dynamic_ptr();
Link_map::add(&_map);
};
Link_map const &link_map() const override { return _map; }
Dynamic const &dynamic() const override { return _dyn; }
void relocate_global() { _dyn.relocate_non_plt(BIND_NOW, Dynamic::SECOND_PASS); }
void plt_setup() { _dyn.plt_setup(); }
void update_dependency(Dependency const &dep) { _dyn.dep(dep); }
void relocate(Bind bind) override
{
if (!_relocated)
_dyn.relocate(bind);
_relocated = true;
}
addr_t base_addr() const { return _map.addr; }
Symbol_info symbol_at_address(addr_t addr) const override
{
Elf::Sym const sym = _dyn.symbol_by_addr(addr);
return { _reloc_base + sym.st_value, _dyn.symbol_name(sym) };
}
/**
* Next in initializion list
*/
Object *next_init() const override {
return List<Object>::Element::next();
}
/**
* Next in object list
*/
Object *next_obj() const override {
return Fifo<Elf_object>::Element::next();
}
/**
* Object list
*/
static Fifo<Elf_object> *obj_list()
{
static Fifo<Elf_object> _list;
return &_list;
}
void load() override { _ref_count++; }
bool unload() override { return (_keep == DONT_KEEP) && !(--_ref_count); }
bool is_linker() const override { return false; }
bool is_binary() const override { return false; }
};
@ -246,32 +244,24 @@ struct Linker::Elf_object : Object, Genode::Fifo<Elf_object>::Element
*/
struct Linker::Ld : Dependency, Elf_object
{
Ld()
: Dependency(this, nullptr), Elf_object(this, relocation_address())
{
Genode::strncpy(_name, linker_name(), Object::MAX_PATH);
}
Ld() :
Dependency(*this, nullptr),
Elf_object(*this, linker_name(), relocation_address())
{ }
void setup_link_map()
{
Elf_object::setup_link_map();
/**
* Use DT_HASH table address for linker, assuming that it will always be at
* the beginning of the file
*/
map.addr = trunc_page((Elf::Addr)dynamic()->hash_table);
link_map_addr(dynamic().link_map_addr());
}
void load_phdr()
void load_phdr(Env &env, Allocator &md_alloc)
{
_file = Linker::load(name(), false);
_file = new (md_alloc) Elf_file(env, md_alloc, name(), false);
}
void relocate_global() { dynamic()->relocate_non_plt(true); }
void update_dependency(Dependency const *dep) { dynamic()->dep = dep; }
static Ld *linker();
static Ld &linker();
bool is_linker() const override { return true; }
@ -279,45 +269,45 @@ struct Linker::Ld : Dependency, Elf_object
* Entry point for jump relocations, it is called from assembly code and is implemented
* right below)
*/
static Elf::Addr jmp_slot(Dependency const *dep, Elf::Size index) asm("jmp_slot");
static Elf::Addr jmp_slot(Dependency const &dep, Elf::Size index) asm("jmp_slot");
};
Elf::Addr Ld::jmp_slot(Dependency const *dep, Elf::Size index)
Elf::Addr Ld::jmp_slot(Dependency const &dep, Elf::Size index)
{
Genode::Lock::Guard guard(Elf_object::lock());
Lock::Guard guard(lock());
if (verbose_relocation)
Genode::log("LD: SLOT ", dep->obj, " ", Genode::Hex(index));
log("LD: SLOT ", &dep.obj(), " ", Hex(index));
try {
Reloc_jmpslot slot(dep, dep->obj->dynamic()->pltrel_type,
dep->obj->dynamic()->pltrel, index);
Reloc_jmpslot slot(dep, dep.obj().dynamic().pltrel_type(),
dep.obj().dynamic().pltrel(), index);
return slot.target_addr();
} catch (...) { Genode::error("LD: jump slot relocation failed. FATAL!"); }
} catch (...) { error("LD: jump slot relocation failed. FATAL!"); }
return 0;
}
/**
* Ld object with different vtable typeinfo
*/
struct Linker::Ld_vtable : Ld
{
Ld_vtable()
{
Elf_object::obj_list()->enqueue(this);
}
};
/**
* Linker object used during bootstrapping on stack (see: 'init_rtld')
*/
Linker::Ld *Linker::Ld::linker()
Linker::Ld &Linker::Ld::linker()
{
/**
* Ld object with different vtable typeinfo
*/
struct Ld_vtable : Ld
{
Ld_vtable()
{
Elf_object::obj_list()->enqueue(this);
plt_setup();
}
};
static Ld_vtable _linker;
return &_linker;
return _linker;
}
@ -326,26 +316,28 @@ Linker::Ld *Linker::Ld::linker()
*/
struct Linker::Binary : Root_object, Elf_object
{
Binary()
: Elf_object(binary_name(), new (Genode::env()->heap()) Dependency(this, this))
Binary(Env &env, Allocator &md_alloc, Bind bind)
:
Root_object(md_alloc),
Elf_object(env, md_alloc, binary_name(),
*new (md_alloc) Dependency(*this, this), KEEP)
{
/* create dep for binary and linker */
Dependency *binary = const_cast<Dependency *>(dynamic()->dep);
dep.enqueue(binary);
Dependency *linker = new (Genode::env()->heap()) Dependency(Ld::linker(), this);
dep.enqueue(linker);
Dependency *binary = const_cast<Dependency *>(&dynamic().dep());
Root_object::enqueue(*binary);
Dependency *linker = new (md_alloc) Dependency(Ld::linker(), this);
Root_object::enqueue(*linker);
/* update linker dep */
Ld::linker()->update_dependency(linker);
Ld::linker().update_dependency(*linker);
/* place linker on second place in link map as well */
Ld::linker()->setup_link_map();
/* place linker on second place in link map */
Ld::linker().setup_link_map();
/* load dependencies */
binary->load_needed(&dep);
binary->load_needed(env, md_alloc, deps(), DONT_KEEP);
/* relocate and call constructors */
Init::list()->initialize();
Init::list()->initialize(bind);
}
Elf::Addr lookup_symbol(char const *name)
@ -358,7 +350,7 @@ struct Linker::Binary : Root_object, Elf_object
return 0;
}
void call_entry_point(Genode::Env &env)
void call_entry_point(Env &env)
{
/* call static construtors and register destructors */
Func * const ctors_start = (Func *)lookup_symbol("_ctors_start");
@ -372,16 +364,16 @@ struct Linker::Binary : Root_object, Elf_object
/* call component entry point */
/* XXX the function type for call_component_construct() is a candidate
* for a base-internal header */
typedef void (*Entry)(Genode::Env &);
typedef void (*Entry)(Env &);
Entry const entry = reinterpret_cast<Entry>(_file->entry);
entry(env);
}
void relocate() override
void relocate(Bind bind) override
{
/* relocate ourselves */
Elf_object::relocate();
Elf_object::relocate(bind);
/*
* After having loaded the main program, we relocate the linker's
@ -389,7 +381,7 @@ struct Linker::Binary : Root_object, Elf_object
* also present within the main program, become relocated to the correct
* positions
*/
Ld::linker()->relocate_global();
Ld::linker().relocate_global();
}
bool is_binary() const override { return true; }
@ -400,22 +392,21 @@ struct Linker::Binary : Root_object, Elf_object
** Global Linker namespace functions **
***************************************/
Object *Linker::load(char const *path, Dependency *dep, unsigned flags)
Object &Linker::load(Env &env, Allocator &md_alloc, char const *path,
Dependency &dep, Keep keep)
{
for (Object *e = Elf_object::obj_list()->head(); e; e = e->next_obj()) {
if (verbose_loading)
Genode::log("LOAD: ", Linker::file(path), " == ", e->name());
log("LOAD: ", Linker::file(path), " == ", e->name());
if (!Genode::strcmp(Linker::file(path), e->name())) {
if (!strcmp(Linker::file(path), e->name())) {
e->load();
return e;
return *e;
}
}
Elf_object *e = new (Genode::env()->heap()) Elf_object(path, dep, flags);
dep->obj = e;
return e;
return *new (md_alloc) Elf_object(env, md_alloc, path, dep, keep);
}
@ -425,30 +416,30 @@ Object *Linker::obj_list_head()
}
Elf::Sym const *Linker::lookup_symbol(unsigned sym_index, Dependency const *dep,
Elf::Sym const *Linker::lookup_symbol(unsigned sym_index, Dependency const &dep,
Elf::Addr *base, bool undef, bool other)
{
Elf_object const *e = static_cast<Elf_object *>(dep->obj);
Elf::Sym const *symbol = e->symbol(sym_index);
Elf_object const &elf = static_cast<Elf_object const &>(dep.obj());
Elf::Sym const *symbol = elf.symbol(sym_index);
if (!symbol) {
Genode::warning("LD: unknown symbol index ", Genode::Hex(sym_index));
warning("LD: unknown symbol index ", Hex(sym_index));
return 0;
}
if (symbol->bind() == STB_LOCAL) {
*base = dep->obj->reloc_base();
*base = dep.obj().reloc_base();
return symbol;
}
return lookup_symbol(e->symbol_name(symbol), dep, base, undef, other);
return lookup_symbol(elf.symbol_name(*symbol), dep, base, undef, other);
}
Elf::Sym const *Linker::lookup_symbol(char const *name, Dependency const *dep,
Elf::Sym const *Linker::lookup_symbol(char const *name, Dependency const &dep,
Elf::Addr *base, bool undef, bool other)
{
Dependency const *curr = dep->root ? dep->root->dep.head() : dep;
Dependency const *curr = &dep.first();
unsigned long hash = Hash_table::hash(name);
Elf::Sym const *weak_symbol = 0;
Elf::Addr weak_base = 0;
@ -457,45 +448,45 @@ Elf::Sym const *Linker::lookup_symbol(char const *name, Dependency const *dep,
//TODO: handle vertab and search in object list
for (;curr; curr = curr->next()) {
if (other && curr == dep)
if (other && curr == &dep)
continue;
Elf_object const *elf = static_cast<Elf_object *>(curr->obj);
Elf_object const &elf = static_cast<Elf_object const &>(curr->obj());
if ((symbol = elf->lookup_symbol(name, hash)) && (symbol->st_value || undef)) {
if ((symbol = elf.lookup_symbol(name, hash)) && (symbol->st_value || undef)) {
if (dep->root && verbose_lookup)
Genode::log("LD: lookup ", name, " obj_src ", elf->name(),
" st ", symbol, " info ", Genode::Hex(symbol->st_info),
" weak: ", symbol->weak());
if (dep.root() && verbose_lookup)
log("LD: lookup ", name, " obj_src ", elf.name(),
" st ", symbol, " info ", Hex(symbol->st_info),
" weak: ", symbol->weak());
if (!undef && symbol->st_shndx == SHN_UNDEF)
continue;
if (!symbol->weak() && symbol->st_shndx != SHN_UNDEF) {
*base = elf->reloc_base();
*base = elf.reloc_base();
return symbol;
}
if (!weak_symbol) {
weak_symbol = symbol;
weak_base = elf->reloc_base();
weak_base = elf.reloc_base();
}
}
}
/* try searching binary's dependencies */
if (!weak_symbol && dep->root) {
if (binary && dep != binary->dep.head()) {
return lookup_symbol(name, binary->dep.head(), base, undef, other);
if (!weak_symbol && dep.root()) {
if (binary_ptr && &dep != binary_ptr->first_dep()) {
return lookup_symbol(name, *binary_ptr->first_dep(), base, undef, other);
} else {
Genode::error("LD: could not lookup symbol \"", name, "\"");
error("LD: could not lookup symbol \"", name, "\"");
throw Not_found();
}
}
if (dep->root && verbose_lookup)
Genode::log("LD: return ", weak_symbol);
if (dep.root() && verbose_lookup)
log("LD: return ", weak_symbol);
if (!weak_symbol)
throw Not_found();
@ -505,13 +496,6 @@ Elf::Sym const *Linker::lookup_symbol(char const *name, Dependency const *dep,
}
void Linker::load_linker_phdr()
{
if (!Ld::linker()->file())
Ld::linker()->load_phdr();
}
/********************
** Initialization **
********************/
@ -524,49 +508,77 @@ extern "C" void init_rtld()
{
/*
* Allocate on stack, since the linker has not been relocated yet, the vtable
* type relocation might prdouce a wrong vtable pointer (at least on ARM), do
* not call any virtual funtions of this object
* type relocation might produce a wrong vtable pointer (at least on ARM), do
* not call any virtual funtions of this object.
*/
Ld linker_stack;
linker_stack.relocate();
/* make sure this does not get destroyed the usual way */
linker_stack.ref_count++;
Ld linker_on_stack;
linker_on_stack.relocate(BIND_LAZY);
/*
* Create actual linker object with different vtable type and set PLT to new
* DAG.
*/
Ld::linker()->dynamic()->plt_setup();
Ld::linker();
}
Genode::size_t Component::stack_size() { return 16*1024*sizeof(long); }
struct Failed_to_load_program { };
class Linker::Config
{
private:
Bind _bind = BIND_LAZY;
bool _verbose = false;
public:
Config(Env &env)
{
try {
Attached_rom_dataspace config(env, "config");
if (config.xml().attribute_value("ld_bind_now", false))
_bind = BIND_NOW;
_verbose = config.xml().attribute_value("ld_verbose", false);
} catch (Rom_connection::Rom_connection_failed) { }
}
Bind bind() const { return _bind; }
bool verbose() const { return _verbose; }
};
static Genode::Lazy_volatile_object<Heap> &heap()
{
return *unmanaged_singleton<Lazy_volatile_object<Heap>>();
}
void Genode::init_ldso_phdr(Env &env)
{
heap().construct(env.ram(), env.rm());
/* load program headers of linker now */
if (!Ld::linker().file())
Ld::linker().load_phdr(env, *heap());
}
void Component::construct(Genode::Env &env)
{
/* load program headers of linker now */
if (!Ld::linker()->file())
Ld::linker()->load_phdr();
/* read configuration, release ROM afterwards */
try {
Genode::Attached_rom_dataspace config(env, "config");
bind_now = config.xml().attribute_value("ld_bind_now", false);
verbose = config.xml().attribute_value("ld_verbose", false);
} catch (Genode::Rom_connection::Rom_connection_failed) { }
/* read configuration */
static Config config(env);
verbose = config.verbose();
/* load binary and all dependencies */
try {
binary = new(Genode::env()->heap()) Binary();
binary_ptr = unmanaged_singleton<Binary>(env, *heap(), config.bind());
} catch (...) {
Genode::error("LD: failed to load program");
throw Failed_to_load_program();
error("LD: failed to load program");
throw;
}
/* print loaded object information */
@ -577,7 +589,7 @@ void Component::construct(Genode::Env &env)
" .. ", Hex(Thread::stack_area_virtual_base() +
Thread::stack_area_virtual_size() - 1),
": stack area");
dump_link_map(Elf_object::obj_list()->head());
dump_link_map(*Elf_object::obj_list()->head());
}
} catch (...) { }
@ -586,5 +598,5 @@ void Component::construct(Genode::Env &env)
binary_ready_hook_for_gdb();
/* start binary */
binary->call_entry_point(env);
binary_ptr->call_entry_point(env);
}

View File

@ -19,9 +19,9 @@
** Helpers **
*************/
static Linker::Root_object *to_root(void *h)
static Linker::Root_object const &to_root(void *h)
{
return static_cast<Linker::Root_object *>(h);
return *static_cast<Linker::Root_object const *>(h);
}
@ -39,7 +39,7 @@ static Genode::Lock & shared_object_lock()
static Linker::Object *find_obj(Genode::addr_t addr)
{
for (Linker::Object *e = Linker::obj_list_head(); e; e = e->next_obj())
if (addr >= e->link_map()->addr && addr < e->link_map()->addr + e->size())
if (addr >= e->link_map().addr && addr < e->link_map().addr + e->size())
return e;
throw Genode::Address_info::Invalid_address();
@ -50,25 +50,28 @@ static Linker::Object *find_obj(Genode::addr_t addr)
** API **
*********/
Genode::Shared_object::Shared_object(char const *file, unsigned flags)
Genode::Shared_object::Shared_object(Env &env, Allocator &md_alloc,
char const *file, Bind bind, Keep keep)
:
_md_alloc(md_alloc)
{
using namespace Linker;
if (verbose_shared)
Genode::log("LD: open '", file ? file : "binary", "'");
log("LD: open '", file ? file : "binary", "'");
try {
Genode::Lock::Guard guard(shared_object_lock());
Lock::Guard guard(shared_object_lock());
/* update bind now variable */
bind_now = (flags & Shared_object::NOW) ? true : false;
_handle = (Root_object *)new (Genode::env()->heap()) Root_object(file ? file : binary_name(), flags);
_handle = new (md_alloc)
Root_object(env, md_alloc, file ? file : binary_name(),
bind == BIND_NOW ? Linker::BIND_NOW : Linker::BIND_LAZY,
keep == KEEP ? Linker::KEEP : Linker::DONT_KEEP);
/* print loaded object information */
try {
if (Linker::verbose)
Linker::dump_link_map(to_root(_handle)->dep.head()->obj);
Linker::dump_link_map(to_root(_handle).first_dep()->obj());
} catch (...) { }
} catch (...) { throw Invalid_file(); }
@ -80,23 +83,24 @@ void *Genode::Shared_object::_lookup(const char *name) const
using namespace Linker;
if (verbose_shared)
Genode::log("LD: shared object lookup '", name, "'");
log("LD: shared object lookup '", name, "'");
try {
Genode::Lock::Guard guard(Object::lock());
Lock::Guard guard(Linker::lock());
Elf::Addr base;
Root_object *root = to_root(_handle);
Elf::Sym const *symbol = lookup_symbol(name, root->dep.head(), &base, true);
Root_object const &root = to_root(_handle);
Elf::Addr base;
Elf::Sym const *symbol = lookup_symbol(name, *root.first_dep(), &base, true);
return (void *)(base + symbol->st_value);
} catch (...) { throw Shared_object::Invalid_symbol(); }
}
Genode::Shared_object::Link_map const * Genode::Shared_object::link_map() const
Genode::Shared_object::Link_map const &Genode::Shared_object::link_map() const
{
return (Link_map const *)to_root(_handle)->link_map();
return (Link_map const &)to_root(_handle).link_map();
}
@ -105,26 +109,33 @@ Genode::Shared_object::~Shared_object()
using namespace Linker;
if (verbose_shared)
Genode::log("LD: close shared object");
log("LD: close shared object");
Genode::Lock::Guard guard(shared_object_lock());
destroy(Genode::env()->heap(), to_root(_handle));
Lock::Guard guard(shared_object_lock());
destroy(_md_alloc, &const_cast<Root_object &>(to_root(_handle)));
}
Genode::Address_info::Address_info(Genode::addr_t address)
Genode::Address_info::Address_info(addr_t address)
{
using namespace Genode;
if (verbose_shared)
Genode::log("LD: address-info request: ", Genode::Hex(address));
log("LD: address-info request: ", Hex(address));
Linker::Object *e = find_obj(address);
e->info(address, *this);
path = e->name();
base = e->reloc_base();
Linker::Object::Symbol_info const symbol = e->symbol_at_address(address);
addr = symbol.addr;
name = symbol.name;
if (verbose_shared)
Genode::log("LD: found address info: obj: ", path, " sym: ", name,
" addr: ", Genode::Hex(addr));
log("LD: found address info: obj: ", path, " sym: ", name,
" addr: ", Hex(addr));
}

View File

@ -72,26 +72,26 @@ class Linker::Reloc_non_plt : public Reloc_non_plt_generic
* relocations within its text-segment (e.g., 'initial_sp' and friends), which
* we cannot write to from here).
*/
if (_dep->obj->reloc_base())
*addr += _dep->obj->reloc_base();
if (_dep.obj().reloc_base())
*addr += _dep.obj().reloc_base();
}
public:
Reloc_non_plt(Dependency const *dep, Elf::Rela const *, unsigned long)
Reloc_non_plt(Dependency const &dep, Elf::Rela const *, unsigned long)
: Reloc_non_plt_generic(dep)
{
Genode::error("LD: DT_RELA not supported");
error("LD: DT_RELA not supported");
throw Incompatible();
}
Reloc_non_plt(Dependency const *dep, Elf::Rel const *rel, unsigned long size,
Reloc_non_plt(Dependency const &dep, Elf::Rel const *rel, unsigned long size,
bool second_pass)
: Reloc_non_plt_generic(dep)
{
Elf::Rel const *end = rel + (size / sizeof(Elf::Rel));
for (; rel < end; rel++) {
Elf::Addr *addr = (Elf::Addr *)(_dep->obj->reloc_base() + rel->offset);
Elf::Addr *addr = (Elf::Addr *)(_dep.obj().reloc_base() + rel->offset);
if (second_pass && rel->type() != R_GLOB_DAT)
continue;
@ -104,8 +104,8 @@ class Linker::Reloc_non_plt : public Reloc_non_plt_generic
case R_GLOB_DAT: _glob_dat(rel, addr, second_pass); break;
case R_RELATIVE: _relative(addr); break;
default:
if (_dep->root) {
Genode::warning("LD: Unkown relocation ", (int)rel->type());
if (_dep.root()) {
warning("LD: Unkown relocation ", (int)rel->type());
throw Incompatible();
}
break;

View File

@ -51,7 +51,7 @@ class Linker::Reloc_non_plt : public Reloc_non_plt_generic
*/
void _relative(Elf::Rela const *rel, Elf::Addr *addr)
{
*addr = _dep->obj->reloc_base() + rel->addend;
*addr = _dep.obj().reloc_base() + rel->addend;
}
/**
@ -68,23 +68,23 @@ class Linker::Reloc_non_plt : public Reloc_non_plt_generic
*addr = reloc_base + sym->st_value + (addend ? rel->addend : 0);
if (verbose_reloc(_dep))
Genode::log("LD: GLOB DAT ", addr, " -> ", Genode::Hex(*addr),
" r ", Genode::Hex(reloc_base),
" v ", Genode::Hex(sym->st_value));
log("LD: GLOB DAT ", addr, " -> ", Hex(*addr),
" r ", Hex(reloc_base),
" v ", Hex(sym->st_value));
}
public:
Reloc_non_plt(Dependency const *dep, Elf::Rela const *rel, unsigned long size)
Reloc_non_plt(Dependency const &dep, Elf::Rela const *rel, unsigned long size)
: Reloc_non_plt_generic(dep)
{
Elf::Rela const *end = rel + (size / sizeof(Elf::Rela));
for (; rel < end; rel++) {
Elf::Addr *addr = (Elf::Addr *)(_dep->obj->reloc_base() + rel->offset);
Elf::Addr *addr = (Elf::Addr *)(_dep.obj().reloc_base() + rel->offset);
if (verbose_reloc(_dep))
Genode::log("LD: reloc: ", rel, " type: ", (int)rel->type());
log("LD: reloc: ", rel, " type: ", (int)rel->type());
switch(rel->type()) {
case R_JMPSLOT: _glob_dat_64(rel, addr, false); break;
@ -92,8 +92,8 @@ class Linker::Reloc_non_plt : public Reloc_non_plt_generic
case R_RELATIVE: _relative(rel, addr); break;
default:
if (!_dep->obj->is_linker()) {
Genode::warning("LD: unkown relocation ", (int)rel->type());
if (!_dep.obj().is_linker()) {
warning("LD: unkown relocation ", (int)rel->type());
throw Incompatible();
}
break;
@ -101,10 +101,10 @@ class Linker::Reloc_non_plt : public Reloc_non_plt_generic
}
}
Reloc_non_plt(Dependency const *dep, Elf::Rel const *, unsigned long, bool)
Reloc_non_plt(Dependency const &dep, Elf::Rel const *, unsigned long, bool)
: Reloc_non_plt_generic(dep)
{
Genode::error("LD: DT_REL not supported");
error("LD: DT_REL not supported");
throw Incompatible();
}
};

View File

@ -52,27 +52,27 @@ class Linker::Reloc_non_plt : public Reloc_non_plt_generic
void _relative(Elf::Rel const *rel, Elf::Addr *addr)
{
if (_dep->obj->reloc_base())
*addr += _dep->obj->reloc_base();
if (_dep.obj().reloc_base())
*addr += _dep.obj().reloc_base();
}
public:
Reloc_non_plt(Dependency const *dep, Elf::Rela const *, unsigned long)
Reloc_non_plt(Dependency const &dep, Elf::Rela const *, unsigned long)
: Reloc_non_plt_generic(dep)
{
Genode::error("LD: DT_RELA not supported");
error("LD: DT_RELA not supported");
throw Incompatible();
}
Reloc_non_plt(Dependency const *dep, Elf::Rel const *rel, unsigned long size,
Reloc_non_plt(Dependency const &dep, Elf::Rel const *rel, unsigned long size,
bool second_pass)
: Reloc_non_plt_generic(dep)
{
Elf::Rel const *end = rel + (size / sizeof(Elf::Rel));
for (; rel < end; rel++) {
Elf::Addr *addr = (Elf::Addr *)(_dep->obj->reloc_base() + rel->offset);
Elf::Addr *addr = (Elf::Addr *)(_dep.obj().reloc_base() + rel->offset);
if (second_pass && rel->type() != R_GLOB_DAT)
continue;
@ -84,8 +84,8 @@ class Linker::Reloc_non_plt : public Reloc_non_plt_generic
case R_COPY : _copy<Elf::Rel>(rel, addr); break;
case R_RELATIVE: _relative(rel, addr); break;
default:
if (_dep->root) {
Genode::warning("LD: Unkown relocation ", (int)rel->type());
if (_dep.root()) {
warning("LD: Unkown relocation ", (int)rel->type());
throw Incompatible();
}
break;

View File

@ -45,7 +45,7 @@ class Linker::Reloc_non_plt : public Reloc_non_plt_generic
*/
void _relative(Elf::Rela const *rel, Elf::Addr *addr)
{
*addr = _dep->obj->reloc_base() + rel->addend;
*addr = _dep.obj().reloc_base() + rel->addend;
}
/**
@ -62,19 +62,19 @@ class Linker::Reloc_non_plt : public Reloc_non_plt_generic
*addr = reloc_base + sym->st_value + (addend ? rel->addend : 0);
if (verbose_reloc(_dep))
Genode::log("GLOB DAT ", addr, " -> ", *addr,
" r ", reloc_base, " v ", sym->st_value);
log("GLOB DAT ", addr, " -> ", *addr,
" r ", reloc_base, " v ", sym->st_value);
}
public:
Reloc_non_plt(Dependency const *dep, Elf::Rela const *rel, unsigned long size)
Reloc_non_plt(Dependency const &dep, Elf::Rela const *rel, unsigned long size)
: Reloc_non_plt_generic(dep)
{
Elf::Rela const *end = rel + (size / sizeof(Elf::Rela));
for (; rel < end; rel++) {
Elf::Addr *addr = (Elf::Addr *)(_dep->obj->reloc_base() + rel->offset);
Elf::Addr *addr = (Elf::Addr *)(_dep.obj().reloc_base() + rel->offset);
switch(rel->type()) {
case R_64: _glob_dat_64(rel, addr, true); break;
@ -83,8 +83,8 @@ class Linker::Reloc_non_plt : public Reloc_non_plt_generic
case R_RELATIVE: _relative(rel, addr); break;
default:
if (!_dep->obj->is_linker()) {
Genode::warning("LD: Unkown relocation ", (int)rel->type());
if (!_dep.obj().is_linker()) {
warning("LD: Unkown relocation ", (int)rel->type());
throw Incompatible();
}
break;
@ -92,10 +92,10 @@ class Linker::Reloc_non_plt : public Reloc_non_plt_generic
}
}
Reloc_non_plt(Dependency const *dep, Elf::Rel const *, unsigned long, bool)
Reloc_non_plt(Dependency const &dep, Elf::Rel const *, unsigned long, bool)
: Reloc_non_plt_generic(dep)
{
Genode::error("LD: DT_REL not supported");
error("LD: DT_REL not supported");
throw Incompatible();
}
};

View File

@ -94,28 +94,6 @@ extern "C" void init_main_thread()
(void*)env();
init_log();
/* initialize exception handling */
init_exception_handling();
/*
* Trigger first exception. This step has two purposes.
* First, it enables us to detect problems related to exception handling as
* early as possible. If there are problems with the C++ support library,
* it is much easier to debug them at this early stage. Otherwise problems
* with half-working exception handling cause subtle failures that are hard
* to interpret.
*
* Second, the C++ support library allocates data structures lazily on the
* first occurrence of an exception. This allocation traverses into
* Genode's heap and, in some corner cases, consumes several KB of stack.
* This is usually not a problem when the first exception is triggered from
* the main thread but it becomes an issue when the first exception is
* thrown from the stack of a thread with a specially tailored (and
* otherwise sufficient) stack size. By throwing an exception here, we
* mitigate this issue by eagerly performing those allocations.
*/
try { throw 1; } catch (...) { }
/* create a thread object for the main thread */
main_thread();

View File

@ -0,0 +1,21 @@
/*
* \brief Rump initialization
* \author Norman Feske
* \date 2016-11-02
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__RUMP__BOOTSTRAP_H_
#define _INCLUDE__RUMP__BOOTSTRAP_H_
#include <base/env.h>
#include <base/allocator.h>
void rump_bootstrap_init(Genode::Env &env, Genode::Allocator &heap);
#endif /* _INCLUDE__RUMP__BOOTSTRAP_H_ */

View File

@ -1,4 +1,4 @@
/**
/*
* \brief Definitions for FS front-end
* \author Sebastian Sumpf
* \date 2014-01-22

View File

@ -17,7 +17,7 @@ extern "C" {
#include <elf.h>
}
#include <base/env.h>
#include <rump/bootstrap.h>
#include <base/log.h>
#include <base/shared_object.h>
#include <util/string.h>
@ -35,7 +35,46 @@ typedef Elf32_Sym Elf_Sym;
static bool const verbose = false;
static Genode::Shared_object *obj_main;
static Genode::Shared_object *obj_main;
static Genode::Env *env_ptr;
static Genode::Allocator *heap_ptr;
void rump_bootstrap_init(Genode::Env &env, Genode::Allocator &alloc)
{
/* ignore subsequent calls */
if (env_ptr)
return;
env_ptr = &env;
heap_ptr = &alloc;
}
/**
* Exception type
*/
class Missing_call_of_rump_bootstrap_init { };
static Genode::Env &env()
{
if (!env_ptr)
throw Missing_call_of_rump_bootstrap_init();
return *env_ptr;
}
static Genode::Allocator &heap()
{
if (!heap_ptr)
throw Missing_call_of_rump_bootstrap_init();
return *heap_ptr;
}
struct Sym_tab
{
@ -183,13 +222,17 @@ struct Sym_tab
* Call init functions of libraries
*/
static void _dl_init(Genode::Shared_object::Link_map const *map,
rump_modinit_fn mod_init,
rump_compload_fn comp_init)
rump_modinit_fn mod_init,
rump_compload_fn comp_init)
{
using namespace Genode;
Shared_object *obj;
try { obj = new (Genode::env()->heap()) Shared_object(map->path); }
catch (...) { error("Could not dlopen ", map->path); return; }
Shared_object *obj = nullptr;
try {
obj = new (heap()) Shared_object(::env(), heap(), map->path,
Shared_object::BIND_LAZY,
Shared_object::DONT_KEEP);
}
catch (...) { error("could not dlopen ", map->path); return; }
struct modinfo **mi_start, **mi_end;
struct rump_component **rc_start, **rc_end;
@ -218,9 +261,14 @@ void rumpuser_dl_bootstrap(rump_modinit_fn domodinit, rump_symload_fn symload,
/* open main program and request link map */
using namespace Genode;
obj_main = new (env()->heap()) Shared_object(nullptr, Shared_object::NOW);
try {
obj_main = new (heap()) Shared_object(::env(), heap(), nullptr,
Shared_object::BIND_NOW,
Shared_object::KEEP);
}
catch (...) { error("could not dlopen the main executable"); return; }
Shared_object::Link_map const *map = obj_main->link_map();
Shared_object::Link_map const *map = &obj_main->link_map();
for (; map->next; map = map->next) ;
Shared_object::Link_map const *curr_map;

View File

@ -13,11 +13,12 @@
#include "file_system.h"
#include <os/config.h>
#include <rump/bootstrap.h>
#include <rump_fs/fs.h>
#include <util/string.h>
#include <util/hard_context.h>
#include <base/log.h>
#include <base/attached_rom_dataspace.h>
/**
* We define our own fs arg structure to fit all sizes, we assume that 'fspec'
@ -31,28 +32,24 @@ struct fs_args
fs_args() { Genode::memset(pad, 0, sizeof(pad)); }
};
namespace File_system {
class Sync;
};
namespace File_system { class Sync; };
static char const *fs_types[] = { RUMP_MOUNT_CD9660, RUMP_MOUNT_EXT2FS,
RUMP_MOUNT_FFS, RUMP_MOUNT_MSDOS,
RUMP_MOUNT_NTFS, RUMP_MOUNT_UDF, 0 };
typedef Genode::String<16> Fs_type;
static Fs_type & fs_type()
{
static Fs_type inst = Genode::config()->xml_node().attribute_value("fs", Fs_type());
return inst;
}
static bool _supports_symlinks;
static bool _check_type(char const *type)
static bool _check_type(Fs_type const &type)
{
for (int i = 0; fs_types[i]; i++)
if (!Genode::strcmp(type, fs_types[i]))
if (!Genode::strcmp(type.string(), fs_types[i]))
return true;
return false;
}
@ -66,35 +63,32 @@ static void _print_types()
}
static bool check_symlinks()
static bool check_symlinks(Fs_type const &fs_type)
{
if (!Genode::strcmp(fs_type().string(), RUMP_MOUNT_EXT2FS))
return true;
if (!Genode::strcmp(fs_type().string(), RUMP_MOUNT_FFS))
return true;
return false;
return (fs_type == RUMP_MOUNT_EXT2FS)
|| (fs_type == RUMP_MOUNT_FFS);
}
static bool check_read_only()
static bool check_read_only(Fs_type const &fs_type)
{
if (!Genode::strcmp(fs_type().string(), RUMP_MOUNT_CD9660))
return true;
return false;
return fs_type == RUMP_MOUNT_CD9660;
}
void File_system::init()
void File_system::init(Genode::Env &env, Genode::Allocator &alloc, Genode::Xml_node config)
{
if (!_check_type(fs_type().string())) {
Fs_type const fs_type = config.attribute_value("fs", Fs_type());
if (!_check_type(fs_type)) {
Genode::error("Invalid or no file system given (use \'<config fs=\"<fs type>\"/>)");
_print_types();
throw Genode::Exception();
}
Genode::log("Using ", fs_type().string(), " as file system");
Genode::log("Using ", fs_type, " as file system");
/* make Genode env and heap known to the rump kernel */
rump_bootstrap_init(env, alloc);
/* start rump kernel */
rump_init();
@ -104,16 +98,16 @@ void File_system::init()
/* mount into extra-terrestrial-file system */
struct fs_args args;
int opts = check_read_only() ? RUMP_MNT_RDONLY : 0;
int opts = check_read_only(fs_type) ? RUMP_MNT_RDONLY : 0;
args.fspec = (char *)GENODE_DEVICE;
if (rump_sys_mount(fs_type().string(), "/", opts, &args, sizeof(args)) == -1) {
Genode::error("Mounting '", fs_type().string(), "' file system failed (errno ", errno, " )");
if (rump_sys_mount(fs_type.string(), "/", opts, &args, sizeof(args)) == -1) {
Genode::error("Mounting '", fs_type, "' file system failed (errno ", errno, " )");
throw Genode::Exception();
}
/* check support for symlinks */
_supports_symlinks = check_symlinks();
_supports_symlinks = check_symlinks(fs_type);
}

View File

@ -13,6 +13,11 @@
#ifndef _FILE_SYSTEM_H_
#define _FILE_SYSTEM_H_
/* Genode includes */
#include <util/xml_node.h>
#include <base/env.h>
#include <base/allocator.h>
extern "C" {
#include <sys/cdefs.h>
#include <sys/errno.h>
@ -26,7 +31,7 @@ extern "C" {
}
namespace File_system {
void init();
void init(Genode::Env &, Genode::Allocator &heap, Genode::Xml_node config);
bool supports_symlinks();
}

View File

@ -15,6 +15,7 @@
/* Genode includes */
#include <file_system/node_handle_registry.h>
#include <file_system_session/rpc_object.h>
#include <base/attached_rom_dataspace.h>
#include <timer_session/connection.h>
#include <os/session_policy.h>
#include <root/component.h>
@ -481,16 +482,20 @@ struct File_system::Main
Genode::Signal_handler<Main> sync_handler
{ env.ep(), *this, &Main::sync };
Heap heap { env.ram(), env.rm() };
/*
* Initialize root interface
*/
Sliced_heap sliced_heap = { env.ram(), env.rm() };
Sliced_heap sliced_heap { env.ram(), env.rm() };
Root fs_root = { env, sliced_heap };
Root fs_root { env, sliced_heap };
Attached_rom_dataspace config { env, "config" };
Main(Genode::Env &env) : env(env)
{
File_system::init();
File_system::init(env, heap, config.xml());
/* set all bits but the stickies */
rump_sys_umask(S_ISUID|S_ISGID|S_ISVTX);

View File

@ -11,6 +11,9 @@
#include <base/shared_object.h>
#include <base/snprintf.h>
/* libc-internal includes */
#include <libc_init.h>
extern "C" {
#include <dlfcn.h>
}
@ -26,6 +29,19 @@ char *dlerror(void)
}
static Genode::Env *genode_env = nullptr;
namespace Libc {
void init_dl(Genode::Env &env)
{
if (!genode_env)
genode_env = &env;
}
}
static Shared_object *to_object(void *handle)
{
return static_cast<Shared_object *>(handle);
@ -39,21 +55,28 @@ void *dlopen(const char *name, int mode)
/* error on unsupported mode values */
if (mode & ~supported) {
snprintf(err_str, MAX_ERR, "Unsupported mode 0x%x\n", mode & ~supported);
error("dlopen: ", Cstring(err_str));
error(__func__, ": ", Cstring(err_str));
return nullptr;
}
Shared_object *obj = 0;
unsigned flags = mode & RTLD_NOW ? Shared_object::NOW : Shared_object::LAZY;
flags |= mode & RTLD_NODELETE ? Shared_object::KEEP : 0;
Shared_object::Bind const bind =
(mode & RTLD_NOW) ? Shared_object::BIND_NOW : Shared_object::BIND_LAZY;
Shared_object::Keep const keep =
(mode & RTLD_NODELETE) ? Shared_object::KEEP : Shared_object::DONT_KEEP;
if (!genode_env) {
error(__func__, ": support for dynamic linking not initialized");
return nullptr;
}
try {
obj = new (env()->heap()) Shared_object(name, flags);
return new (env()->heap())
Shared_object(*genode_env, *env()->heap(), name, bind, keep);
} catch (...) {
snprintf(err_str, MAX_ERR, "Unable to open file %s\n", name);
}
return (void *)obj;
return nullptr;
}

View File

@ -0,0 +1,28 @@
/*
* \brief Interfaces for initializing libc subsystems
* \author Norman Feske
* \date 2016-10-27
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _LIBC_INIT_H_
#define _LIBC_INIT_H_
/* Genode includes */
#include <base/env.h>
namespace Libc {
/**
* Support for shared libraries
*/
void init_dl(Genode::Env &env);
}
#endif /* _LIBC_INIT_H_ */

View File

@ -23,6 +23,7 @@
#include <internal/call_func.h>
#include <base/internal/unmanaged_singleton.h>
#include "vfs_plugin.h"
#include "libc_init.h"
/* escape sequences for highlighting debug message prefixes */
@ -203,6 +204,9 @@ namespace Genode { extern void (*call_component_construct)(Genode::Env &); }
void Libc::call_component_construct(Genode::Env &env)
{
/* pass Genode::Env to libc subsystems that depend on it */
init_dl(env);
task = unmanaged_singleton<Libc::Task>(env);
task->run();
}

View File

@ -14,6 +14,8 @@
#include <base/printf.h>
#include <rom_session/connection.h>
#include <base/env.h>
#include <base/heap.h>
#include <base/component.h>
#include <base/shared_object.h>
using namespace Genode;
@ -166,7 +168,7 @@ static void test_dynamic_cast()
** Shared-object API **
***********************/
static void test_shared_object_api()
static void test_shared_object_api(Env &env, Allocator &alloc)
{
/*
* When loading the shared object, we expect the global constructor
@ -175,15 +177,18 @@ static void test_shared_object_api()
* 'lib_dl_so' is a local variable such that its destructor is called
* when leaving the scope of the function.
*/
Shared_object lib_dl_so("test-ldso_lib_dl.lib.so");
Shared_object lib_dl_so(env, alloc, "test-ldso_lib_dl.lib.so",
Shared_object::BIND_LAZY, Shared_object::DONT_KEEP);
}
/**
* Main function of LDSO test
*/
int main(int argc, char **argv)
void Component::construct(Genode::Env &env)
{
static Heap heap(env.ram(), env.rm());
printf("\n");
printf("Dynamic-linker test\n");
printf("===================\n");
@ -250,12 +255,11 @@ int main(int argc, char **argv)
printf("Shared-object API\n");
printf("-----------------\n");
test_shared_object_api();
test_shared_object_api(env, heap);
printf("\n");
printf("Destruction\n");
printf("-----------\n");
/* test if return value is propagated correctly by dynamic linker */
return 123;
Libc::exit(123);
}

View File

@ -132,14 +132,17 @@ class Default_file_system_factory : public Vfs::Global_file_system_factory
/**
* \throw Factory_not_available
*/
Vfs::File_system_factory &_load_factory(Genode::Allocator &alloc,
Vfs::File_system_factory &_load_factory(Genode::Env &env,
Genode::Allocator &alloc,
Library_name const &lib_name)
{
Genode::Shared_object *shared_object = nullptr;
try {
shared_object = new (alloc)
Genode::Shared_object(lib_name.string());
Genode::Shared_object(env, alloc, lib_name.string(),
Genode::Shared_object::BIND_LAZY,
Genode::Shared_object::DONT_KEEP);
typedef Vfs::File_system_factory *(*Query_fn)();
@ -147,7 +150,7 @@ class Default_file_system_factory : public Vfs::Global_file_system_factory
return *query_fn();
} catch (Genode::Shared_object::Invalid_file) {
} catch (Genode::Shared_object::Invalid_rom_module) {
PWRN("could not open '%s'", lib_name.string());
throw Factory_not_available();
@ -160,13 +163,15 @@ class Default_file_system_factory : public Vfs::Global_file_system_factory
}
}
bool _probe_external_factory(Genode::Allocator &alloc, Genode::Xml_node node)
bool _probe_external_factory(Genode::Env &env, Genode::Allocator &alloc,
Genode::Xml_node node)
{
Library_name const lib_name = _library_name(_node_name(node));
try {
_list.insert(new (alloc)
External_entry(_node_name(node).string(), _load_factory(alloc, lib_name)));
External_entry(_node_name(node).string(),
_load_factory(env, alloc, lib_name)));
return true;
} catch (Factory_not_available) { return false; }
@ -187,7 +192,7 @@ class Default_file_system_factory : public Vfs::Global_file_system_factory
try {
/* probe for file system implementation available as shared lib */
if (_probe_external_factory(alloc, node)) {
if (_probe_external_factory(env, alloc, node)) {
/* try again with the new file system type loaded */
if (Vfs::File_system *fs = _try_create(env, alloc, node))
return fs;