/** * \brief ELF-dynamic section (see ELF ABI) * \author Sebastian Sumpf * \date 2015-03-12 */ /* * Copyright (C) 2015-2019 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. */ #ifndef _INCLUDE__DYNAMIC_H_ #define _INCLUDE__DYNAMIC_H_ /* local includes */ #include #include namespace Linker { struct Hash_table; struct 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 */ class Linker::Dynamic { public: class Dynamic_section_missing { }; private: /* * Noncopyable */ Dynamic(Dynamic const &); Dynamic &operator = (Dynamic const &); struct Needed : Fifo::Element { 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(); } void _section_dt_needed(Elf::Dyn const *d) { if (!_md_alloc) { error("unexpected call of section_dt_needed"); throw Fatal(); } Needed *n = new (*_md_alloc) 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() SELF_RELOC { 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; } } } public: 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; _needed.dequeue_all([&] (Needed &n) { 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 { _needed.for_each([&] (Needed &n) { fn(n.path(_strtab)); }); } void relocate(Bind bind) SELF_RELOC { plt_setup(); if (_pltrel_size) { switch (_pltrel_type) { case DT_RELA: case DT_REL: Reloc_plt(_obj, _pltrel_type, _pltrel, _pltrel_size); break; default: error("LD: Invalid PLT relocation ", (int)_pltrel_type); throw Incompatible(); } } relocate_non_plt(bind, FIRST_PASS); } 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, pass == SECOND_PASS); if (_rel) Reloc_non_plt r(*_dep, _rel, _rel_size, pass == SECOND_PASS); if (bind == 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_ */