/**
* \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
#include
#include
#include
#include
#include
#include
using namespace Linker;
namespace Linker {
struct Dynamic;
struct Ld;
struct Ld_vtable;
struct Binary;
struct Link_map;
struct Debug;
};
/**
* Genode args to the 'main' function
*/
extern char **genode_argv;
extern int genode_argc;
extern char **genode_envp;
static Binary *binary = 0;
bool Linker::bind_now = false;
Link_map *Link_map::first;
/**
* Registers dtors
*/
int genode_atexit(Linker::Func);
/**************************************************************
** ELF object types (shared object, dynamic binaries, ldso **
**************************************************************/
/**
* The actual ELF object, one per file
*/
struct Linker::Elf_object : Object, Genode::Fifo::Element
{
Dynamic dyn;
Link_map map;
unsigned ref_count = 1;
unsigned flags = 0;
bool relocated = false;
Elf_object(Dependency const *dep, Elf::Addr reloc_base)
: Object(reloc_base), dyn(dep)
{ }
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);
/* 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);
}
/**
* 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])
{
/* 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;
}
return nullptr;
}
/**
* 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;
Link_map::add(&map);
};
Link_map *link_map() override { return ↦ }
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::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();
}
/**
* Next in initializion list
*/
Object *next_init() const override {
return Genode::List