genode/repos/base/src/lib/ldso/main.cc

1231 lines
27 KiB
C++

/**
* \brief Genode's dynamic linker
* \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.
*/
#include <base/env.h>
#include <base/printf.h>
#include <base/shared_object.h>
#include <os/config.h>
#include <util/list.h>
#include <util/string.h>
#include <relocation.h>
using namespace Linker;
/**
* Offset of dynamic section of this ELF is filled out during linkage
*/
extern Genode::addr_t _DYNAMIC;
/**
* Genode args to the 'main' function
*/
extern char **genode_argv;
extern int genode_argc;
extern char **genode_envp;
namespace Linker {
struct Dynamic;
struct Hash_table;
struct Ld;
struct Ld_vtable;
struct Binary;
struct Link_map;
struct Debug;
typedef void (*Func)(void);
};
static bool bind_now = false;
static Binary *binary = 0;
/**
* Registers dtors
*/
int genode_atexit(Linker::Func);
/**
* LIBC debug support
*/
extern "C" void brk(Debug *, Link_map *) { }
struct Linker::Debug
{
/*
* This state value describes the mapping change taking place when
* the brk address is called.
*/
enum State {
CONSISTENT, /* mapping change is complete */
ADD, /* beginning to add a new object */
DELETE /* beginning to remove an object mapping */
};
Debug() : Brk(brk) { }
int version = 1; /* unused */
struct Link_map *map = nullptr;; /* start of link map */
/*
* This is the address of a function internal to the run-time linker, that
* will always be called when the linker begins to map in a library or unmap
* it, and again when the mapping change is complete. The debugger can set a
* breakpoint at this address if it wants to notice shared object mapping
* changes.
*/
void (*Brk)(Debug *, Link_map *);
State state = CONSISTENT;
static void state_change(State s, Link_map *m)
{
d()->state = s;
d()->Brk(d(), m);
}
static Debug *d()
{
static Debug _d;
return &_d;
}
};
/**
* Link map
*/
struct Linker::Link_map
{
Elf::Addr addr; /* base address of library */
char const *path; /* path */
void const *dynamic; /* DYNAMIC section */
Link_map *next = nullptr;
Link_map *prev = nullptr;
static Link_map *first;
static void add(Link_map *map)
{
map->next = nullptr;;
if (!first) {
first = map;
Debug::d()->map = map;
return;
}
Link_map *m;
for (m = first; m->next; m = m->next) ;
m->next = map;
map->prev = m;
}
static void remove(Link_map *map)
{
if (map->prev)
map->prev->next = map->next;
if (map->next)
map->next->prev = map->prev;
if (map == first)
first = map->next;
}
static void dump()
{
if (!verbose_link_map)
return;
for (Link_map *m = first; m; m = m->next)
PINF("MAP: addr: " EFMT " dynamic: %p %s m: %p p: %p n: %p",
m->addr, m->dynamic, m->path, m, m->prev, m->next);
}
};
Link_map *Link_map::first;
/**
* ELF hash table and hash function
*/
struct Linker::Hash_table
{
unsigned long nbuckets() const { return *((Elf::Hashelt *)this); }
unsigned long nchains() const { return *(((Elf::Hashelt *)this) + 1); }
Elf::Hashelt const *buckets() { return ((Elf::Hashelt *)this + 2); }
Elf::Hashelt const *chains() { return buckets() + nbuckets(); }
/**
* ELF hash function Figure 5.12 of the 'System V ABI'
*/
static unsigned long hash(char const *name)
{
unsigned const char *p = (unsigned char const *)name;
unsigned long h = 0, g;
while (*p) {
h = (h << 4) + *p++;
if ((g = h & 0xf0000000) != 0)
h ^= g >> 24;
h &= ~g;
}
return h;
}
};
/**
* .dynamic section entries
*/
struct Linker::Dynamic
{
struct Needed : Genode::Fifo<Needed>::Element
{
Genode::off_t offset;
Needed(Genode::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));
}
};
Dag const *dag;
Object const *obj;
Elf::Dyn const *dynamic;
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;
Genode::Fifo<Needed> needed;
Dynamic(Dag const *dag)
:
dag(dag), obj(dag->obj), dynamic((Elf::Dyn *)(obj->reloc_base() + &_DYNAMIC))
{
init();
}
Dynamic(Dag const *dag, Object const *obj, Linker::Phdr const *phdr)
:
dag(dag), 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;
default:
trace("DT", d->tag, 0, 0);
break;
}
}
}
void relocate()
{
plt_setup();
if (pltrel_size) {
switch (pltrel_type) {
case DT_RELA:
case DT_REL:
Reloc_plt(obj, pltrel_type, pltrel, pltrel_size);
break;
default:
PERR("LD: Invalid PLT relocation %u", pltrel_type);
throw Incompatible();
}
}
relocate_non_plt();
}
void plt_setup()
{
if (pltgot)
Plt_got r(dag, pltgot);
}
void relocate_non_plt(bool second_pass = false)
{
if (reloca)
Reloc_non_plt r(dag, reloca, reloca_size);
if (rel)
Reloc_non_plt r(dag, rel, rel_size, second_pass);
if (bind_now)
Reloc_bind_now r(dag, pltrel, pltrel_size);
}
};
static void register_initializer(Elf_object *elf);
/**
* The actual ELF object, one per file
*/
struct Linker::Elf_object : Object, Genode::List<Elf_object>::Element,
Genode::Fifo<Elf_object>::Element
{
Dynamic dynamic;
Link_map map;
unsigned ref_count = 1;
unsigned flags = 0;
bool relocated = false;
Elf_object(Dag const *dag) : dynamic(dag)
{ }
Elf_object(char const *path, Dag const *dag, unsigned flags = 0)
:
Object(path, Linker::load(Linker::file(path))), dynamic(dag, this, &_file->phdr),
flags(flags)
{
/* register for static construction and relocation */
register_initializer(this);
obj_list()->enqueue(this);
/* 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)
PDBG("destroy: %s", 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);
}
void setup_link_map()
{
map.addr = _file ? _file->start + reloc_base() : reloc_base();
map.path = name();
map.dynamic = (void *)dynamic.dynamic;
Link_map::add(&map);
};
virtual void relocate()
{
if (!relocated)
dynamic.relocate();
relocated = true;
}
/**
* Return symbol of given number from ELF
*/
Elf::Sym const * symbol(unsigned sym_index) const
{
if (sym_index > dynamic.hash_table->nchains())
return 0;
return dynamic.symtab + sym_index;
}
/**
* Return name of given symbol
*/
char const *symbol_name(Elf::Sym const *sym) const
{
return dynamic.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 = dynamic.hash_table;
if (!h->buckets())
return 0;
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 0;
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;
}
return 0;
}
/**
* Search for symbol at addr
*/
void info(Genode::addr_t addr, Genode::Address_info &info) const
{
info.path = name();
info.base = map.addr;
info.addr = 0;
Hash_table *h = dynamic.hash_table;
for (unsigned long sym_index = 0; sym_index < h->nchains(); sym_index++)
{
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;
}
if (!info.addr)
throw Genode::Address_info::Invalid_address();
}
Elf_object *init_next() const
{
return Genode::List<Elf_object>::Element::next();
}
Elf_object *obj_next() const
{
return Genode::Fifo<Elf_object>::Element::next();
}
static Genode::Fifo<Elf_object> *obj_list()
{
static Genode::Fifo<Elf_object> _list;
return &_list;
}
static Elf_object *find_obj(Genode::addr_t addr)
{
for (Elf_object *e = obj_list()->head(); e; e = e->obj_next())
if (addr >= e->map.addr && addr < e->map.addr + e->size())
return e;
throw Genode::Address_info::Invalid_address();
}
static Elf_object *load(char const *path, Dag *dag, unsigned flags = 0)
{
for (Elf_object *e = obj_list()->head(); e; e = e->obj_next()) {
if (verbose_loading)
PDBG("LOAD: %s == %s", Linker::file(path), e->name());
if (!Genode::strcmp(Linker::file(path), e->name())) {
e->ref_count++;
return e;
}
}
Elf_object *e = new (Genode::env()->heap()) Elf_object(path, dag, flags);
dag->obj = e;
return e;
}
bool unload() { return !(--ref_count) && !(flags & Genode::Shared_object::KEEP); }
static Genode::Lock & lock()
{
static Genode::Lock _lock;
return _lock;
}
Elf::Phdr const *phdr_exidx() const
{
for (unsigned i = 0; i < _file->elf_phdr_count(); i++) {
Elf::Phdr const *ph = _file->elf_phdr(i);
if (ph->p_type == PT_ARM_EXIDX)
return ph;
}
return 0;
}
bool is_linker() const override { return false; }
bool is_binary() const override { return false; }
};
/**
* Handle static construction and relocation of ELF files
*/
struct Init : Genode::List<Elf_object>
{
bool in_progress = false;
bool restart = false;
static Init *list()
{
static Init _list;
return &_list;
}
Elf_object *contains(char const *file)
{
for (Elf_object *e = first(); e; e = e->init_next())
if (!Genode::strcmp(file, e->name()))
return e;
return nullptr;
}
void reorder(Elf_object const *elf)
{
/* put in front of initializer list */
remove(elf);
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);
Elf_object *e;
if ((e = contains(Linker::file(path))))
reorder(e);
}
}
void initialize()
{
Elf_object *obj = first();
/* relocate */
for (; obj; obj = obj->init_next()) {
if (verbose_relocation)
PDBG("Relocate %s", obj->name());
obj->relocate();
}
/*
* Recursive initialization call is not allowed here. This might happend
* when Shared_objects (e.g. dlopen and friends) are constructed from within
* global constructors (ctors).
*/
if (in_progress) {
restart = true;
return;
}
in_progress = true;
/* call static constructors */
obj = first();
while (obj) {
Elf_object *next = obj->init_next();
remove(obj);
if (obj->dynamic.init_function) {
if (verbose_relocation)
PDBG("%s init func %p", obj->name(), obj->dynamic.init_function);
obj->dynamic.init_function();
}
obj = restart ? first() : next;
restart = false;
}
in_progress = false;
}
};
void register_initializer(Elf_object *elf)
{
Init::list()->insert(elf);
}
/**
* Dag node
*/
Linker::Dag::Dag(char const *path, Root_object *root, Genode::Fifo<Dag> * const dag, unsigned flags)
: obj(Elf_object::load(path, this, flags)), root(root)
{
dag->enqueue(this);
load_needed(dag, flags);
}
Linker::Dag::~Dag()
{
if ((static_cast<Elf_object *>(obj))->unload()) {
if (verbose_loading)
PDBG("Destroy: %s\n", obj->name());
destroy(Genode::env()->heap(), obj);
}
}
bool Linker::Dag::in_dag(char const *file, Genode::Fifo<Dag> * const dag)
{
for (Dag *d = dag->head(); d; d = d->next())
if (!Genode::strcmp(file, d->obj->name()))
return true;
return false;
}
void Linker::Dag::load_needed(Genode::Fifo<Dag> * const dag, unsigned flags)
{
Elf_object *elf = static_cast<Elf_object *>(obj);
for (Dynamic::Needed *n = elf->dynamic.needed.head(); n; n = n->next()) {
char const *path = n->path(elf->dynamic.strtab);
Elf_object *e;
if (!in_dag(Linker::file(path), dag))
new (Genode::env()->heap()) Dag(path, root, dag, flags);
/* re-order initializer list, if needed object has been already added */
else if ((e = Init::list()->contains(Linker::file(path))))
Init::list()->reorder(e);
}
}
/**
* The dynamic linker has its own ELF ojbect type
*/
struct Linker::Ld : Dag, Elf_object
{
Ld() : Dag(this, nullptr), Elf_object(this)
{
Genode::strncpy(_name, linker_name(), Object::MAX_PATH);
trace("LD", 0, 0, 0);
}
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);
}
void load_phdr()
{
_file = Linker::load(name(), false);
}
void relocate_global() { dynamic.relocate_non_plt(true); }
void update_dag(Dag const *dag) { dynamic.dag = dag; }
static Ld *linker();
bool is_linker() const override { return true; }
/**
* Entry point for jump relocations, it is called from assembly
*/
static Elf::Addr jmp_slot(Dag const *dag, Elf::Size offset) asm("jmp_slot");
};
/**
* Ld object with different vtable typeinfo
*/
struct Linker::Ld_vtable : Ld
{
Ld_vtable()
{
Elf_object::obj_list()->enqueue(this);
}
};
Linker::Ld *Linker::Ld::linker()
{
static Ld_vtable _linker;
return &_linker;
}
Elf::Addr Ld::jmp_slot(Dag const *dag, Elf::Size index)
{
Elf_object const *obj = static_cast<Elf_object *>(dag->obj);
Genode::Lock::Guard guard(Elf_object::lock());
if (verbose_relocation)
PDBG("SLOT %p " EFMT, obj, index);
try {
Reloc_jmpslot slot(dag, obj->dynamic.pltrel_type, obj->dynamic.pltrel, index);
return slot.target_addr();
} catch (...) { PERR("LD: Jump slot relocation failed. FATAL!"); }
return 0;
}
struct Linker::Root_object
{
Genode::Fifo<Dag> dag;
/* main root */
Root_object() { };
/* runtime loaded root components */
Root_object(char const *path, unsigned flags = 0)
{
new (Genode::env()->heap()) Dag(path, this, &dag, flags);
/* provide Genode base library access */
new (Genode::env()->heap()) Dag(linker_name(), this, &dag);;
/* relocate and call constructors */
Init::list()->initialize();
}
~Root_object()
{
Dag *d;
while ((d = dag.dequeue()))
destroy(Genode::env()->heap(), d);
}
Link_map const *link_map() const
{
Elf_object const *obj = static_cast<Elf_object *>(dag.head()->obj);
return obj ? &obj->map : 0;
}
};
/**
* The program to load has its own ELF object type
*/
struct Linker::Binary : Root_object, Elf_object
{
Binary()
: Elf_object(binary_name(), new (Genode::env()->heap()) Dag(this, this))
{
/* create dag for binary and linker */
Dag *binary = const_cast<Dag *>(dynamic.dag);
dag.enqueue(binary);
Dag *linker = new (Genode::env()->heap()) Dag(Ld::linker(), this);
dag.enqueue(linker);
/* update linker dag */
Ld::linker()->update_dag(linker);
/* place linker on second place in link map as well */
Ld::linker()->setup_link_map();
/* load dependencies */
binary->load_needed(&dag);
/* relocate and call constructors */
Init::list()->initialize();
}
Elf::Addr lookup_symbol(char const *name)
{
Elf::Sym const *symbol = 0;
if ((symbol = Elf_object::lookup_symbol(name, Hash_table::hash(name))))
return reloc_base() + symbol->st_value;
return 0;
}
int call_entry_point()
{
/* call static construtors and register destructors */
Func * const ctors_start = (Func *)lookup_symbol("_ctors_start");
Func * const ctors_end = (Func *)lookup_symbol("_ctors_end");
for (Func * ctor = ctors_end; ctor != ctors_start; (*--ctor)());
Func * const dtors_start = (Func *)lookup_symbol("_dtors_start");
Func * const dtors_end = (Func *)lookup_symbol("_dtors_end");
for (Func * dtor = dtors_start; dtor != dtors_end; genode_atexit(*dtor++));
/* call main function of the program */
typedef int (*Main)(int, char **, char **);
Main const main = reinterpret_cast<Main>(_file->entry);
return main(genode_argc, genode_argv, genode_envp);
}
void relocate() override
{
/* relocate ourselves */
Elf_object::relocate();
/*
* After having loaded the main program, we relocate the linker's
* symbols again such that, for example type informations, which are
* also present within the main program, become relocated to the correct
* positions
*/
Ld::linker()->relocate_global();
}
bool is_binary() const override { return true; }
};
/**
* This is called mostly from the relocation code
* XXX: 'undef' needs to be true for dlsym
*/
Elf::Sym const *Linker::locate_symbol(unsigned sym_index, Dag const *dag, Elf::Addr *base, bool undef, bool other)
{
Elf_object const *e = static_cast<Elf_object *>(dag->obj);
Elf::Sym const *symbol = e->symbol(sym_index);
if (!symbol) {
PWRN("LD: Unkown symbol index %x", sym_index);
return 0;
}
if (symbol->bind() == STB_LOCAL) {
trace("NONLOCAL", symbol->bind(), 0, 0);
*base = dag->obj->reloc_base();
return symbol;
}
return search_symbol(e->symbol_name(symbol), dag, base, undef, other);
}
Elf::Sym const *Linker::search_symbol(char const *name, Dag const *dag, Elf::Addr *base, bool undef, bool other)
{
Dag const *curr = dag->root ? dag->root->dag.head() : dag;
unsigned long hash = Hash_table::hash(name);
Elf::Sym const *weak_symbol = 0;
Elf::Addr weak_base = 0;
Elf::Sym const *symbol = 0;
//TODO: handle vertab and search in object list
for (;curr; curr = curr->next()) {
if (other && curr == dag)
continue;
Elf_object const *elf = static_cast<Elf_object *>(curr->obj);
if ((symbol = elf->lookup_symbol(name, hash)) && (symbol->st_value || undef)) {
if (dag->root && verbose_lookup)
PINF("Lookup %s obj_src %s st %p info %x weak: %u", name, elf->name(), symbol, symbol->st_info, symbol->weak());
if (!undef && symbol->st_shndx == SHN_UNDEF)
continue;
if (!symbol->weak() && symbol->st_shndx != SHN_UNDEF) {
*base = elf->reloc_base();
return symbol;
}
if (!weak_symbol) {
weak_symbol = symbol;
weak_base = elf->reloc_base();
}
}
}
/* try searching binary's DAG */
if (!weak_symbol && dag->root && dag != binary->dag.head())
return search_symbol(name, binary->dag.head(), base, undef, other);
if (dag->root && verbose_lookup)
PDBG("Return %p", weak_symbol);
if (!weak_symbol)
throw Not_found();
*base = weak_base;
return weak_symbol;
}
/**
* Called before anything else, even '_main', we cannot access any global data
* here, we have to relocate our own ELF first
*/
extern "C" void init_rtld()
{
trace("init_rtld", 0, 0, 0);
/*
* 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
*/
Ld linker_stack;
linker_stack.relocate();
/* make sure this does not get destroyed the usual way */
linker_stack.ref_count++;
trace("init_rtld finished", 0, 0, 0);
/*
* Create actual linker object with different vtable type and set PLT to new
* DAG.
*/
Ld::linker()->dynamic.plt_setup();
}
/**
* "Walk through shared objects" support, see man page of 'dl_iterate_phdr'
*/
struct Phdr_info
{
Elf::Addr addr; /* module relocation base */
char const *name; /* module name */
Elf::Phdr const *phdr; /* pointer to module's phdr */
Elf::Half phnum; /* number of entries in phdr */
};
extern "C" int dl_iterate_phdr(int (*callback) (Phdr_info *info, Genode::size_t size, void *data), void *data)
{
int err = 0;
Phdr_info info;
Genode::Lock::Guard guard(Elf_object::lock());
Elf_object *e = Elf_object::obj_list()->head();
for (;e; e = e->obj_next()) {
info.addr = e->reloc_base();
info.name = e->name();
info.phdr = e->file()->phdr.phdr;
info.phnum = e->file()->phdr.count;
if (verbose_exception)
PDBG("%s reloc " EFMT, e->name(), e->reloc_base());
if ((err = callback(&info, sizeof(Phdr_info), data)))
break;
}
return err;
}
/**
* Find ELF and exceptions table segment that that is located under 'pc',
* address of excption table and number of entries 'pcount'
*/
extern "C" unsigned long dl_unwind_find_exidx(unsigned long pc, int *pcount)
{
/* size of exception table entries */
enum { EXIDX_ENTRY_SIZE = 8 };
*pcount = 0;
/*
* Since this function may be called before the main function, load linker's
* program header now
*/
if (!Ld::linker()->file())
Ld::linker()->load_phdr();
Elf_object *e = Elf_object::obj_list()->head();
for (; e; e = e->obj_next())
{
/* address of first PT_LOAD header */
Genode::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))
continue;
/* retrieve PHDR of exception-table segment */
Elf::Phdr const *exidx = e->phdr_exidx();
if (!exidx)
continue;
*pcount = exidx->p_memsz / EXIDX_ENTRY_SIZE;
return exidx->p_vaddr + e->reloc_base();
}
return 0;
}
/*****************************
** Shared object interface **
*****************************/
static Root_object *to_root(void *h)
{
return static_cast<Root_object *>(h);
}
/**
* Needed during shared object creation and destruction, since global lists are
* manipulated
*/
static Genode::Lock & shared_object_lock()
{
static Genode::Lock _lock;
return _lock;
}
Genode::Shared_object::Shared_object(char const *file, unsigned flags)
{
using namespace Linker;
if (verbose_shared)
PDBG("open '%s'", file ? file : "binary");
try {
Genode::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);
} catch (...) { throw Invalid_file(); }
}
void *Genode::Shared_object::_lookup(const char *name) const
{
using namespace Linker;
if (verbose_shared)
PDBG("lookup '%s'", name);
try {
Genode::Lock::Guard guard(Elf_object::lock());
Elf::Addr base;
Root_object *root = to_root(_handle);
Elf::Sym const *symbol = search_symbol(name, root->dag.head(), &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
{
return (Link_map const *)to_root(_handle)->link_map();
}
Genode::Shared_object::~Shared_object()
{
using namespace Linker;
if (verbose_shared)
PDBG("close");
Genode::Lock::Guard guard(shared_object_lock());
destroy(Genode::env()->heap(), to_root(_handle));
}
Genode::Address_info::Address_info(Genode::addr_t address)
{
using namespace Genode;
if (verbose_shared)
PDBG("request: %lx", address);
Elf_object *e = Elf_object::find_obj(address);
e->info(address, *this);
if (verbose_shared)
PDBG("Found: obj: %s sym: %s addr: %lx", path, name, addr);
}
static void dump_loaded()
{
Elf_object *o = Elf_object::obj_list()->head();
for(; o; o = o->obj_next()) {
if (o->is_binary())
continue;
Elf_object *e = static_cast<Elf_object *>(o);
Genode::printf(" " EFMT " .. " EFMT ": %s\n",
e->map.addr, e->map.addr + e->size() - 1, e->name());
}
}
int main()
{
/* load program headers of linker now */
if (!Ld::linker()->file())
Ld::linker()->load_phdr();
/* read configuration */
try {
/* bind immediately */
bind_now = Genode::config()->xml_node().attribute("ld_bind_now").has_value("yes");
} catch (...) { }
/* load binary and all dependencies */
try {
binary = new(Genode::env()->heap()) Binary();
} catch (...) {
PERR("LD: Failed to load program");
return -1;
}
/* print loaded object information */
try {
if (Genode::config()->xml_node().attribute("ld_verbose").has_value("yes"))
dump_loaded();
} catch (...) { }
Link_map::dump();
/* start binary */
return binary->call_entry_point();
}