ldso: Refactor dynamic linker

Issue #1349
This commit is contained in:
Sebastian Sumpf 2015-03-10 11:16:03 +01:00 committed by Christian Helmuth
parent c94145f74d
commit 36e01b720e
17 changed files with 1266 additions and 999 deletions

View File

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

View File

@ -43,12 +43,11 @@ class Linker::Reloc_non_plt : public Reloc_non_plt_generic
Elf::Addr reloc_base;
Elf::Sym const *sym;
if (!(sym = locate_symbol(rel->sym(), _dag, &reloc_base)))
if (!(sym = lookup_symbol(rel->sym(), _dep, &reloc_base)))
return;
/* S + A - P */
*addr = reloc_base + sym->st_value - (Elf::Addr)addr + *addr;
trace("REL32", (unsigned long)addr, *addr, 0);
}
void _glob_dat(Elf::Rel const *rel, Elf::Addr *addr, bool no_addend)
@ -56,14 +55,13 @@ class Linker::Reloc_non_plt : public Reloc_non_plt_generic
Elf::Addr reloc_base;
Elf::Sym const *sym;
if (!(sym = locate_symbol(rel->sym(), _dag, &reloc_base)))
if (!(sym = lookup_symbol(rel->sym(), _dep, &reloc_base)))
return;
Elf::Addr addend = no_addend ? 0 : *addr;
/* S + A */
*addr = addend + reloc_base + sym->st_value;
trace("GLOB_DAT", (unsigned long)addr, *addr, 0);
}
void _relative(Elf::Addr *addr)
@ -73,25 +71,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 (_dag->obj->reloc_base())
*addr += _dag->obj->reloc_base();
if (_dep->obj->reloc_base())
*addr += _dep->obj->reloc_base();
}
public:
Reloc_non_plt(Dag const *dag, Elf::Rela const *, unsigned long)
: Reloc_non_plt_generic(dag)
Reloc_non_plt(Dependency const *dep, Elf::Rela const *, unsigned long)
: Reloc_non_plt_generic(dep)
{
PERR("LD: DT_RELA not supported");
throw Incompatible();
}
Reloc_non_plt(Dag const *dag, Elf::Rel const *rel, unsigned long size, bool second_pass)
: Reloc_non_plt_generic(dag)
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 *)(_dag->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 +103,7 @@ 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:
trace("UNKREL", rel->type(), 0, 0);
if (_dag->root) {
if (_dep->root) {
PWRN("LD: Unkown relocation %u", rel->type());
throw Incompatible();
}

View File

@ -0,0 +1,19 @@
/**
* \brief GDB debugging support
* \author Sebastian Sumpf
* \date 2015-03-12
*/
/*
* Copyright (C) 2015 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 <debug.h>
/**
* C-break function for GDB
*/
extern "C" void brk(Linker::Debug *, Linker::Link_map *) { }

View File

@ -0,0 +1,81 @@
/**
* \brief Manage object dependencies
* \author Sebastian Sumpf
* \date 2015-03-12
*/
/*
* Copyright (C) 2015 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 <linker.h>
#include <dynamic.h>
#include <init.h>
/**
* 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)
{
dep->enqueue(this);
load_needed(dep, flags);
}
Linker::Dependency::~Dependency()
{
if (obj->unload()) {
if (verbose_loading)
PDBG("Destroy: %s\n", obj->name());
destroy(Genode::env()->heap(), obj);
}
}
bool Linker::Dependency::in_dep(char const *file,
Genode::Fifo<Dependency> * const dep)
{
for (Dependency *d = dep->head(); d; d = d->next())
if (!Genode::strcmp(file, d->obj->name()))
return true;
return false;
}
void Linker::Dependency::load_needed(Genode::Fifo<Dependency> * const dep,
unsigned flags)
{
for (Dynamic::Needed *n = obj->dynamic()->needed.head(); n; n = n->next()) {
char const *path = n->path(obj->dynamic()->strtab);
Object *o;
if (!in_dep(Linker::file(path), dep))
new (Genode::env()->heap()) Dependency(path, root, dep, flags);
/* re-order initializer list, if needed object has been already added */
else if ((o = Init::list()->contains(Linker::file(path))))
Init::list()->reorder(o);
}
}
Linker::Root_object::Root_object(char const *path, unsigned flags)
{
new (Genode::env()->heap()) Dependency(path, this, &dep, flags);
/* provide Genode base library access */
new (Genode::env()->heap()) Dependency(linker_name(), this, &dep);;
/* relocate and call constructors */
Init::list()->initialize();
}

View File

@ -0,0 +1,116 @@
/**
* \brief GCC excption handling support
* \author Sebastian Sumpf
* \date 2015-03-12
*/
/*
* Copyright (C) 2015 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 <linker.h>
using namespace Linker;
/*********
** x86 **
*********/
/**
* "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(Object::lock());
for (Object *e = obj_list_head();e; e = e->next_obj()) {
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;
}
/*********
** ARM **
*********/
/**
* Return EXIDX program header
*/
static Elf::Phdr const *phdr_exidx(File const *file)
{
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;
}
/**
* Find ELF and exceptions table segment that that is located under 'pc',
* address of exception 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
*/
load_linker_phdr();
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;
/* 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 = phdr_exidx(e->file());
if (!exidx)
continue;
*pcount = exidx->p_memsz / EXIDX_ENTRY_SIZE;
return exidx->p_vaddr + e->reloc_base();
}
return 0;
}

View File

@ -0,0 +1,126 @@
/**
* \brief Debugger support
* \author Sebastian Sumpf
* \date 2015-03-10
*/
/*
* Copyright (C) 2015 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__DEBUG_H_
#define _INCLUDE__DEBUG_H_
#include <base/printf.h>
#include <elf.h>
constexpr bool verbose_link_map = false;
namespace Linker {
struct Debug;
struct Link_map;
}
/**
* LIBC debug support
*/
extern "C" void brk(Linker::Debug *, Linker::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);
}
};
#endif /* _INCLUDE__DEBUG_H_ */

View File

@ -0,0 +1,222 @@
/**
* \brief ELF-dynamic section (see ELF ABI)
* \author Sebastian Sumpf
* \date 2015-03-12
*/
/*
* Copyright (C) 2015 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__DYNAMIC_H_
#define _INCLUDE__DYNAMIC_H_
#include <relocation.h>
namespace Linker {
struct Hash_table;
struct Dynamic;
}
/**
* Offset of dynamic section of this ELF. This is filled out during linkage by
* static linker.
*/
extern Genode::addr_t _DYNAMIC;
/**
* 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));
}
};
Dependency const *dep;
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(Dependency const *dep)
:
dep(dep), obj(dep->obj), dynamic((Elf::Dyn *)(obj->reloc_base() + &_DYNAMIC))
{
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;
default:
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(dep, pltgot);
}
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, second_pass);
if (bind_now)
Reloc_bind_now r(dep, pltrel, pltrel_size);
}
};
#endif /* _INCLUDE__DYNAMIC_H_ */

View File

@ -0,0 +1,75 @@
/**
* \brief ELF file setup
* \author Sebastian Sumpf
* \date 2015-03-12
*/
/*
* Copyright (C) 2015 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__FILE_H_
#define _INCLUDE__FILE_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);
}
/**
* Program header
*/
struct Linker::Phdr
{
enum { MAX_PHDR = 10 };
Elf::Phdr phdr[MAX_PHDR];
unsigned count = 0;
};
/**
* Loaded ELF file
*/
struct Linker::File
{
typedef void (*Entry)(void);
Phdr phdr;
Entry entry;
Elf::Addr reloc_base = 0;
Elf::Addr start = 0;
Elf::Size size = 0;
virtual ~File() { }
Elf::Phdr const *elf_phdr(unsigned index) const
{
if (index < phdr.count)
return &phdr.phdr[index];
return 0;
}
unsigned elf_phdr_count() const { return phdr.count; }
};
#endif /* _INCLUDE__FILE_H_ */

View File

@ -0,0 +1,110 @@
/**
* \brief Initialization list (calls ctors)
* \author Sebastian Sumpf
* \date 2015-03-12
*/
/*
* Copyright (C) 2015 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__INIT_H_
#define _INCLUDE__INIT_H_
#include <linker.h>
namespace Linker {
struct Init;
}
/**
* Handle static construction and relocation of ELF files
*/
struct Linker::Init : Genode::List<Object>
{
bool in_progress = false;
bool restart = false;
static Init *list()
{
static Init _list;
return &_list;
}
Object *contains(char const *file)
{
for (Object *elf = first(); elf; elf = elf->next_init())
if (!Genode::strcmp(file, elf->name()))
return elf;
return nullptr;
}
void reorder(Object *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);
Object *e;
if ((e = contains(Linker::file(path))))
reorder(e);
}
}
void initialize()
{
Object *obj = first();
/* relocate */
for (; obj; obj = obj->next_init()) {
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) {
Object *next = obj->next_init();
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;
}
};
#endif /* _INCLUDE__INIT_H_ */

View File

@ -16,18 +16,20 @@
#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 <debug.h>
#include <elf.h>
#include <trace.h>
#include <file.h>
#include <util.h>
/**
* Debugging
*/
constexpr bool verbose_lookup = false;
constexpr bool verbose_link_map = false;
constexpr bool verbose_relocation = false;
constexpr bool verbose_exception = false;
constexpr bool verbose_shared = false;
constexpr bool verbose_loading = false;
@ -35,20 +37,25 @@ constexpr bool verbose_loading = false;
/**
* Forward declartions and helpers
*/
namespace Linker
{
namespace Linker {
class Object;
struct Phdr;
struct File;
struct Root_object;
struct Dag;
struct Dependency;
struct Elf_object;
struct Dynamic;
typedef void (*Func)(void);
/**
* Eager binding enable
*/
extern bool bind_now;
/**
* Find symbol via index
*
* \param sym_index Symbol index within object
* \param dag Directed acyclic graph of object
* \param dep Dependency of object
* \param base Returned address of symbol
* \param undef True, return undefined symbol; False return defined
* symbols only
@ -59,15 +66,14 @@ namespace Linker
*
* \return Symbol information
*/
Elf::Sym const *locate_symbol(unsigned sym_index, Dag const *, Elf::Addr *base,
Elf::Sym const *lookup_symbol(unsigned sym_index, Dependency const *, Elf::Addr *base,
bool undef = false, bool other = false);
/**
* Find symbol via name
*
* \param name Symbol name
* \param dag Directed acyclic graph of object
* \param dep Dependency of object
* \param base Returned address of symbol
* \param undef True, return undefined symbol; False return defined
* symbols only
@ -78,22 +84,35 @@ namespace Linker
*
* \return Symbol information
*/
Elf::Sym const *search_symbol(char const *name, Dag const *dag, Elf::Addr *base,
Elf::Sym const *lookup_symbol(char const *name, Dependency const *dep, Elf::Addr *base,
bool undef = false, bool other = false);
/**
* Load object
* Load an ELF (setup segments and map program header)
*
* \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
* \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
*
* \return File descriptor
* \return Linker::Object
*/
File const *load(char const *path, bool load = true);
Object *load(char const *path, Dependency *dep, unsigned flags = 0);
/**
* Returns the head of the global object list
*/
Object *obj_list_head();
/**
* Returns the root-dependeny of the dynamic binary
*/
Dependency *binary_root_dep();
/**
* Force to map the program header of the dynamic linker
*/
void load_linker_phdr();
/**
* Exceptions
@ -102,30 +121,6 @@ namespace Linker
class Invalid_file : Genode::Exception { };
class Not_found : Genode::Exception { };
/**
* Page handling
*/
template <typename T>
static inline T trunc_page(T addr) {
return addr & Genode::_align_mask((T)12); }
template <typename T>
static inline T round_page(T addr) {
return Genode::align_addr(addr, (T)12); }
/**
* Extract file name from path
*/
inline char const *file(char const *path)
{
/* strip directories */
char const *f, *r = path;
for (f = r; *f; f++)
if (*f == '/')
r = f + 1;
return r;
}
/**
* Invariants
*/
@ -134,41 +129,11 @@ namespace Linker
}
struct Linker::Phdr
{
enum { MAX_PHDR = 10 };
Elf::Phdr phdr[MAX_PHDR];
unsigned count = 0;
};
struct Linker::File
{
typedef void (*Entry)(void);
Phdr phdr;
Entry entry;
Elf::Addr reloc_base = 0;
Elf::Addr start = 0;
Elf::Size size = 0;
virtual ~File() { }
Elf::Phdr const *elf_phdr(unsigned index) const
{
if (index < phdr.count)
return &phdr.phdr[index];
return 0;
}
unsigned elf_phdr_count() const { return phdr.count; }
};
class Linker::Object : public Genode::Fifo<Object>::Element
/**
* Shared object or binary
*/
class Linker::Object : public Genode::Fifo<Object>::Element,
public Genode::List<Object>::Element
{
protected:
@ -195,36 +160,103 @@ class Linker::Object : public Genode::Fifo<Object>::Element
Elf::Addr reloc_base() const { return _file ? _file->reloc_base : 0; }
char const *name() const { return _name; }
File const *file() { return _file; }
File const *file() { return _file; }
Elf::Size const size() const { return _file ? _file->size : 0; }
virtual bool is_linker() const = 0;
virtual bool is_binary() const = 0;
Elf::Size const size() const { return _file ? _file->size : 0; }
virtual void relocate() = 0;
virtual void load() = 0;
virtual bool unload() { return false;}
/**
* Next object in global object list
*/
virtual Object *next_obj() const = 0;
/**
* Next object in initialization list
*/
virtual Object *next_init() const = 0;
/**
* Return dynamic section of ELF
*/
virtual Dynamic *dynamic() = 0;
/**
* Return link map for ELF
*/
virtual Link_map *link_map() = 0;
/**
* 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;
}
};
struct Linker::Dag : Genode::Fifo<Dag>::Element
/**
* Dependency of object
*/
struct Linker::Dependency : Genode::Fifo<Dependency>::Element
{
Object *obj = nullptr;
Object *obj = nullptr;
Root_object *root = nullptr;
Dag(Object *obj, Root_object *root) : obj(obj), root(root) { }
Dependency(Object *obj, Root_object *root) : obj(obj), root(root) { }
Dag(char const *path, Root_object *root, Genode::Fifo<Dag> * const dag,
Dependency(char const *path, Root_object *root, Genode::Fifo<Dependency> * const dep,
unsigned flags = 0);
~Dag();
~Dependency();
void load_needed(Genode::Fifo<Dag> * const dag, unsigned flags = 0);
bool in_dag(char const *file, Genode::Fifo<Dag> *const dag);
/**
* Load dependent ELF object
*/
void load_needed(Genode::Fifo<Dependency> * const dep, unsigned flags = 0);
/**
* Check if file is in this dependency tree
*/
bool in_dep(char const *file, Genode::Fifo<Dependency> *const dep);
};
static inline bool verbose_reloc(Linker::Dag const *d)
/**
* Root of dependencies
*/
struct Linker::Root_object
{
return d->root && verbose_relocation;
}
Genode::Fifo<Dependency> dep;
extern "C" void _jmp_slot(void);
/* main root */
Root_object() { };
/* runtime loaded root components */
Root_object(char const *path, unsigned flags = 0);
~Root_object()
{
Dependency *d;
while ((d = dep.dequeue()))
destroy(Genode::env()->heap(), d);
}
Link_map const *link_map() const
{
return dep.head()->obj->link_map();
}
};
#endif /* _INCLUDE__LINKER_H_ */

View File

@ -16,6 +16,19 @@
#include <linker.h>
constexpr bool verbose_relocation = false;
static inline bool verbose_reloc(Linker::Dependency const *d)
{
return d->root && verbose_relocation;
}
/**
* Low level linker entry for jump slot relocations
*/
extern "C" void _jmp_slot(void);
namespace Linker
{
struct Plt_got;
@ -32,12 +45,12 @@ namespace Linker
*/
struct Linker::Plt_got
{
Plt_got(Dag const *dag, Elf::Addr *pltgot)
Plt_got(Dependency const *dep, Elf::Addr *pltgot)
{
if (verbose_relocation)
PDBG("OBJ: %s (%p)", dag->obj->name(), dag);
PDBG("OBJ: %s (%p)", dep->obj->name(), dep);
pltgot[1] = (Elf::Addr) dag; /* ELF object */
pltgot[1] = (Elf::Addr) dep; /* ELF object */
pltgot[2] = (Elf::Addr) &_jmp_slot; /* Linker entry */
}
};
@ -78,7 +91,7 @@ class Linker::Reloc_non_plt_generic
{
protected:
Dag const *_dag;
Dependency const *_dep;
/**
* Copy relocations, these are just for the main program, we can do them
@ -88,8 +101,8 @@ class Linker::Reloc_non_plt_generic
template <typename REL>
void _copy(REL const *rel, Elf::Addr *addr)
{
if (!_dag->obj->is_binary()) {
PERR("LD: Copy relocation in DSO (%s at %p)", _dag->obj->name(), addr);
if (!_dep->obj->is_binary()) {
PERR("LD: Copy relocation in DSO (%s at %p)", _dep->obj->name(), addr);
throw Incompatible();
}
@ -97,7 +110,7 @@ class Linker::Reloc_non_plt_generic
Elf::Addr reloc_base;
/* search symbol in other objects, do not return undefined symbols */
if (!(sym = locate_symbol(rel->sym(), _dag, &reloc_base, false, true))) {
if (!(sym = lookup_symbol(rel->sym(), _dep, &reloc_base, false, true))) {
PWRN("LD: Symbol not found");
return;
}
@ -112,7 +125,7 @@ class Linker::Reloc_non_plt_generic
public:
Reloc_non_plt_generic(Dag const *dag) : _dag(dag) { }
Reloc_non_plt_generic(Dependency const *dep) : _dep(dep) { }
};
@ -126,7 +139,7 @@ class Linker::Reloc_jmpslot_generic
public:
Reloc_jmpslot_generic(Dag const *dag, 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) {
@ -138,13 +151,13 @@ class Linker::Reloc_jmpslot_generic
Elf::Sym const *sym;
Elf::Addr reloc_base;
if (!(sym = locate_symbol(rel->sym(), dag, &reloc_base))) {
if (!(sym = lookup_symbol(rel->sym(), dep, &reloc_base))) {
PWRN("LD: Symbol not found");
return;
}
/* write address of symbol to jump slot */
_addr = (Elf::Addr *)(dag->obj->reloc_base() + rel->offset);
_addr = (Elf::Addr *)(dep->obj->reloc_base() + rel->offset);
*_addr = reloc_base + sym->st_value;
@ -164,12 +177,12 @@ class Linker::Reloc_jmpslot_generic
template <typename REL, unsigned TYPE>
struct Linker::Reloc_bind_now_generic
{
Reloc_bind_now_generic(Dag const *dag, 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);
for (Elf::Size index = 0; index < last_index; index++)
Reloc_jmpslot_generic<REL, TYPE, false> reloc(dag, TYPE, pltrel, index);
Reloc_jmpslot_generic<REL, TYPE, false> reloc(dep, TYPE, pltrel, index);
}
};

View File

@ -1,38 +0,0 @@
/**
* \brief Trace support for linker intialization
* \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.
*/
#ifndef _INCLUDE__TRACE_H_
#define _INCLUDE__TRACE_H_
#if 0
typedef Genode::addr_t l4_umword_t;
typedef Genode::addr_t l4_addr_t;
namespace Fiasco {
#include <l4/sys/ktrace.h>
}
#else
namespace Fiasco {
inline void fiasco_tbuf_log_3val(char const *,unsigned, unsigned, unsigned) { }
}
extern "C" void wait_for_continue();
#endif
namespace Linker {
inline void trace(char const *str, unsigned v1, unsigned v2, unsigned v3)
{
Fiasco::fiasco_tbuf_log_3val(str, v1, v2, v3);
}
}
#endif /* _INCLUDE__TRACE_H_ */

View File

@ -0,0 +1,44 @@
/**
* \brief Helper functions
* \author Sebastian Sumpf
* \date 2015-03-12
*/
/*
* Copyright (C) 2015 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__UTIL_H_
#define _INCLUDE__UTIL_H_
namespace Linker {
/**
* Page handling
*/
template <typename T>
static inline T trunc_page(T addr) {
return addr & Genode::_align_mask((T)12); }
template <typename T>
static inline T round_page(T addr) {
return Genode::align_addr(addr, (T)12); }
/**
* Extract file name from path
*/
inline char const *file(char const *path)
{
/* strip directories */
char const *f, *r = path;
for (f = r; *f; f++)
if (*f == '/')
r = f + 1;
return r;
}
}
#endif /* _INCLUDE__UTIL_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,121 @@
/**
* \brief Implentation of Genode's shared-object interface
* \author Sebastian Sumpf
* \date 2015-03-11
*/
/*
* Copyright (C) 2015 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 <linker.h>
/*************
** Helpers **
*************/
static Linker::Root_object *to_root(void *h)
{
return static_cast<Linker::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;
}
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())
return e;
throw Genode::Address_info::Invalid_address();
}
/*********
** API **
*********/
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(Object::lock());
Elf::Addr base;
Root_object *root = to_root(_handle);
Elf::Sym const *symbol = lookup_symbol(name, root->dep.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);
Linker::Object *e = find_obj(address);
e->info(address, *this);
if (verbose_shared)
PDBG("Found: obj: %s sym: %s addr: %lx", path, name, addr);
}

View File

@ -43,36 +43,35 @@ class Linker::Reloc_non_plt : public Reloc_non_plt_generic
Elf::Addr reloc_base;
Elf::Sym const *sym;
if (!(sym = locate_symbol(rel->sym(), _dag, &reloc_base)))
if (!(sym = lookup_symbol(rel->sym(), _dep, &reloc_base)))
return;
*addr = (addend ? *addr : 0) + reloc_base + sym->st_value;
trace("REL32", (unsigned long)addr, *addr, 0);
}
void _relative(Elf::Rel const *rel, Elf::Addr *addr)
{
if (_dag->obj->reloc_base())
*addr += _dag->obj->reloc_base();
if (_dep->obj->reloc_base())
*addr += _dep->obj->reloc_base();
}
public:
Reloc_non_plt(Dag const *dag, Elf::Rela const *, unsigned long)
: Reloc_non_plt_generic(dag)
Reloc_non_plt(Dependency const *dep, Elf::Rela const *, unsigned long)
: Reloc_non_plt_generic(dep)
{
PERR("LD: DT_RELA not supported");
trace("Non_plt", 0, 0, 0);
throw Incompatible();
}
Reloc_non_plt(Dag const *dag, Elf::Rel const *rel, unsigned long size, bool second_pass)
: Reloc_non_plt_generic(dag)
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 *)(_dag->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 +83,7 @@ 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:
trace("UNKREL", rel->type(), 0, 0);
if (_dag->root) {
if (_dep->root) {
PWRN("LD: Unkown relocation %u", rel->type());
throw Incompatible();
}

View File

@ -44,8 +44,7 @@ class Linker::Reloc_non_plt : public Reloc_non_plt_generic
*/
void _relative(Elf::Rela const *rel, Elf::Addr *addr)
{
trace("64", _dag->obj->reloc_base(), rel->addend, 0);
*addr = _dag->obj->reloc_base() + rel->addend;
*addr = _dep->obj->reloc_base() + rel->addend;
}
/**
@ -57,24 +56,24 @@ class Linker::Reloc_non_plt : public Reloc_non_plt_generic
Elf::Addr reloc_base;
Elf::Sym const *sym;
if (!(sym = locate_symbol(rel->sym(), _dag, &reloc_base)))
if (!(sym = lookup_symbol(rel->sym(), _dep, &reloc_base)))
return;
*addr = reloc_base + sym->st_value + (addend ? rel->addend : 0);
if (verbose_reloc(_dag))
if (verbose_reloc(_dep))
PDBG("GLOB DAT %p -> %llx r %llx v %llx", addr, *addr, reloc_base,
sym->st_value);
}
public:
Reloc_non_plt(Dag const *dag, Elf::Rela const *rel, unsigned long size)
: Reloc_non_plt_generic(dag)
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 *)(_dag->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 +82,7 @@ class Linker::Reloc_non_plt : public Reloc_non_plt_generic
case R_RELATIVE: _relative(rel, addr); break;
default:
trace("UNKRELA", rel->type(), 0, 0);
if (!_dag->obj->is_linker()) {
if (!_dep->obj->is_linker()) {
PWRN("LD: Unkown relocation %u", rel->type());
throw Incompatible();
}
@ -93,8 +91,8 @@ class Linker::Reloc_non_plt : public Reloc_non_plt_generic
}
}
Reloc_non_plt(Dag const *dag, Elf::Rel const *, unsigned long, bool)
: Reloc_non_plt_generic(dag)
Reloc_non_plt(Dependency const *dep, Elf::Rel const *, unsigned long, bool)
: Reloc_non_plt_generic(dep)
{
PERR("LD: DT_REL not supported");
throw Incompatible();