/** * \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 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::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; 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(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; 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_ */