diff --git a/repos/base-linux/src/lib/lx_hybrid/lx_hybrid.cc b/repos/base-linux/src/lib/lx_hybrid/lx_hybrid.cc
index a97645269..485581f64 100644
--- a/repos/base-linux/src/lib/lx_hybrid/lx_hybrid.cc
+++ b/repos/base-linux/src/lib/lx_hybrid/lx_hybrid.cc
@@ -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)
diff --git a/repos/base/include/base/env.h b/repos/base/include/base/env.h
index 8ce8820ed..421130489 100644
--- a/repos/base/include/base/env.h
+++ b/repos/base/include/base/env.h
@@ -18,8 +18,8 @@
#include
#include
#include
+#include
#include
-#include
/* maintain compatibility to deprecated API */
#include
@@ -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_ */
diff --git a/repos/base/include/base/shared_object.h b/repos/base/include/base/shared_object.h
index 3fdd08519..b04a1171e 100644
--- a/repos/base/include/base/shared_object.h
+++ b/repos/base/include/base/shared_object.h
@@ -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;
};
diff --git a/repos/base/lib/mk/cxx.mk b/repos/base/lib/mk/cxx.mk
index 7870d2f91..bcc546dff 100644
--- a/repos/base/lib/mk/cxx.mk
+++ b/repos/base/lib/mk/cxx.mk
@@ -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
diff --git a/repos/base/lib/mk/ldso.inc b/repos/base/lib/mk/ldso.inc
index 24a6a4477..8c19be304 100644
--- a/repos/base/lib/mk/ldso.inc
+++ b/repos/base/lib/mk/ldso.inc
@@ -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
diff --git a/repos/base/src/include/base/internal/globals.h b/repos/base/src/include/base/internal/globals.h
index 9b279256f..e3975b7cc 100644
--- a/repos/base/src/include/base/internal/globals.h
+++ b/repos/base/src/include/base/internal/globals.h
@@ -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();
}
diff --git a/repos/base/src/lib/base/component.cc b/repos/base/src/lib/base/component.cc
index 9a10cb03e..d666d66d3 100644
--- a/repos/base/src/lib/base/component.cc
+++ b/repos/base/src/lib/base/component.cc
@@ -16,6 +16,9 @@
#include
#include
+/* base-internal includes */
+#include
+
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.
*/
diff --git a/repos/base/src/lib/cxx/exception.cc b/repos/base/src/lib/cxx/exception.cc
index 0408c99cb..5c849333b 100644
--- a/repos/base/src/lib/cxx/exception.cc
+++ b/repos/base/src/lib/cxx/exception.cc
@@ -19,6 +19,9 @@
#include
#include
+/* base-internal includes */
+#include
+
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 (...) { }
}
diff --git a/repos/base/src/lib/cxx/malloc_free.cc b/repos/base/src/lib/cxx/malloc_free.cc
index 6dcfaa8b7..81eda0c31 100644
--- a/repos/base/src/lib/cxx/malloc_free.cc
+++ b/repos/base/src/lib/cxx/malloc_free.cc
@@ -14,37 +14,44 @@
* under the terms of the GNU General Public License version 2.
*/
+/* Genode includes */
#include
#include
#include
+#include
+
+/* base-internal includes */
+#include
using namespace Genode;
+static Lazy_volatile_object &cxx_heap()
+{
+ static Lazy_volatile_object 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));
}
diff --git a/repos/base/src/lib/ldso/debug.cc b/repos/base/src/lib/ldso/debug.cc
index 021c5e288..da33959f5 100644
--- a/repos/base/src/lib/ldso/debug.cc
+++ b/repos/base/src/lib/ldso/debug.cc
@@ -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());
}
}
diff --git a/repos/base/src/lib/ldso/dependency.cc b/repos/base/src/lib/ldso/dependency.cc
index 191e7885c..83d70b64e 100644
--- a/repos/base/src/lib/ldso/dependency.cc
+++ b/repos/base/src/lib/ldso/dependency.cc
@@ -19,63 +19,77 @@
/**
* Dependency node
*/
-Linker::Dependency::Dependency(char const *path, Root_object *root,
- Genode::Fifo * 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 &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 * const dep)
+bool Linker::Dependency::in_dep(char const *file, Fifo 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 * const dep,
- unsigned flags)
+void Linker::Dependency::load_needed(Env &env, Allocator &md_alloc,
+ Fifo &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);
}
diff --git a/repos/base/src/lib/ldso/exception.cc b/repos/base/src/lib/ldso/exception.cc
index ea055b23d..bdfb6685c 100644
--- a/repos/base/src/lib/ldso/exception.cc
+++ b/repos/base/src/lib/ldso/exception.cc
@@ -11,6 +11,7 @@
* under the terms of the GNU General Public License version 2.
*/
+#include
#include
@@ -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))
diff --git a/repos/base/src/lib/ldso/file.cc b/repos/base/src/lib/ldso/file.cc
deleted file mode 100644
index 8f9ebce24..000000000
--- a/repos/base/src/lib/ldso/file.cc
+++ /dev/null
@@ -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
-#include
-
-/* Genode includes */
-#include
-#include
-#include
-#include
-#include
-#include
-
-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(placeholder, base);
- constructed = 1;
- }
- return reinterpret_cast(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(
- [&] () {
- 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(
- [&] () {
- 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;
-}
-
diff --git a/repos/base/src/lib/ldso/include/debug.h b/repos/base/src/lib/ldso/include/debug.h
index 9e11b9879..62d6e8001 100644
--- a/repos/base/src/lib/ldso/include/debug.h
+++ b/repos/base/src/lib/ldso/include/debug.h
@@ -18,14 +18,21 @@
#include
#include
-constexpr bool verbose_link_map = false;
+/* local includes */
+#include
+
+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);
}
};
diff --git a/repos/base/src/lib/ldso/include/dynamic.h b/repos/base/src/lib/ldso/include/dynamic.h
index 1077211db..bc96fb51c 100644
--- a/repos/base/src/lib/ldso/include/dynamic.h
+++ b/repos/base/src/lib/ldso/include/dynamic.h
@@ -14,6 +14,8 @@
#ifndef _INCLUDE__DYNAMIC_H_
#define _INCLUDE__DYNAMIC_H_
+/* local includes */
+#include
#include
namespace Linker {
@@ -54,162 +56,297 @@ struct Linker::Hash_table
/**
* .dynamic section entries
*/
-struct Linker::Dynamic
+class Linker::Dynamic
{
- struct Needed : Genode::Fifo::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::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;
+
+ /**
+ * \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
+ (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
+ 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(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;
-
- 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(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
- 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(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(&pltgot, d); break;
- case DT_HASH : section(&hash_table, d); break;
- case DT_RELA : section(&reloca, d); break;
- case DT_RELASZ : reloca_size = d->un.val; break;
- case DT_SYMTAB : section(&symtab, d); break;
- case DT_STRTAB : section(&strtab, d); break;
- case DT_STRSZ : strtab_size = d->un.val; break;
- case DT_INIT : section(&init_function, d); break;
- case DT_PLTREL : pltrel_type = (D_tag)d->un.val; break;
- case DT_JMPREL : section(&pltrel, d); break;
- case DT_REL : section(&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(&_pltgot, d); break;
+ case DT_HASH : _section(&_hash_table, d); break;
+ case DT_RELA : _section(&_reloca, d); break;
+ case DT_RELASZ : _reloca_size = d->un.val; break;
+ case DT_SYMTAB : _section(&_symtab, d); break;
+ case DT_STRTAB : _section(&_strtab, d); break;
+ case DT_STRSZ : _strtab_size = d->un.val; break;
+ case DT_INIT : _section(&_init_function, d); break;
+ case DT_PLTREL : _pltrel_type = (D_tag)d->un.val; break;
+ case DT_JMPREL : _section(&_pltrel, d); break;
+ case DT_REL : _section(&_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
+ 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_ */
diff --git a/repos/base/src/lib/ldso/include/file.h b/repos/base/src/lib/ldso/include/file.h
index 4994ecb47..2400e24c5 100644
--- a/repos/base/src/lib/ldso/include/file.h
+++ b/repos/base/src/lib/ldso/include/file.h
@@ -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
+
+/* local includes */
+#include
+#include
+#include
+
+
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();
+
+ 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_ */
diff --git a/repos/base/src/lib/ldso/include/init.h b/repos/base/src/lib/ldso/include/init.h
index 2c180911a..54c49d7d5 100644
--- a/repos/base/src/lib/ldso/include/init.h
+++ b/repos/base/src/lib/ldso/include/init.h
@@ -25,7 +25,7 @@ namespace Linker {
/**
* Handle static construction and relocation of ELF files
*/
-struct Linker::Init : Genode::List