ldso: Make truly self relocatable

On Linux the linker can now be loaded at arbitrary addresses, this became
necessary for newer kernel versions. The 'linux_arm' target is not supported.

Issue #1728
This commit is contained in:
Sebastian Sumpf
2015-11-26 10:30:23 +01:00
committed by Christian Helmuth
parent e74b53d5dd
commit 41b9f6bd03
13 changed files with 144 additions and 31 deletions

View File

@ -21,13 +21,6 @@ namespace Linker {
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
*/
@ -108,7 +101,7 @@ struct Linker::Dynamic
Dynamic(Dependency const *dep)
:
dep(dep), obj(dep->obj), dynamic((Elf::Dyn *)(obj->reloc_base() + &_DYNAMIC))
dep(dep), obj(dep->obj), dynamic((Elf::Dyn *)dynamic_address())
{
init();
}

View File

@ -34,6 +34,11 @@ constexpr bool verbose_exception = false;
constexpr bool verbose_shared = false;
constexpr bool verbose_loading = false;
extern const unsigned long _GLOBAL_OFFSET_TABLE_[] __attribute__((visibility("hidden")));
extern unsigned long _DYNAMIC[] __attribute__((visibility("hidden")));
extern Elf::Addr etext;
/**
* Forward declartions and helpers
*/
@ -126,6 +131,32 @@ namespace Linker {
*/
constexpr char const *binary_name() { return "binary"; }
constexpr char const *linker_name() { return "ld.lib.so"; }
/**
* Address of .dynamic section in GOT
*/
static inline unsigned long dynamic_address_got()
{
return _GLOBAL_OFFSET_TABLE_[0];
}
/**
* Address of .dynamic section from symbol
*/
static inline unsigned long dynamic_address()
{
return (unsigned long)&_DYNAMIC;
}
/**
* Return the run-time load address of the shared object.
*/
static inline unsigned long relocation_address(void)
{
return dynamic_address() < dynamic_address_got() ?
dynamic_address_got() - dynamic_address() :
dynamic_address() - dynamic_address_got();
}
}
@ -141,12 +172,13 @@ class Linker::Object : public Genode::Fifo<Object>::Element,
char _name[MAX_PATH];
File const *_file = nullptr;
Elf::Addr _reloc_base = 0;
public:
Object() { }
Object(Elf::Addr reloc_base) : _reloc_base(reloc_base) { }
Object(char const *path, File const *file)
: _file(file)
: _file(file), _reloc_base(file->reloc_base)
{
Genode::strncpy(_name, Linker::file(path), MAX_PATH);
}
@ -157,8 +189,8 @@ class Linker::Object : public Genode::Fifo<Object>::Element,
destroy(Genode::env()->heap(), const_cast<File *>(_file));
}
Elf::Addr reloc_base() const { return _file ? _file->reloc_base : 0; }
char const *name() const { return _name; }
Elf::Addr reloc_base() const { return _reloc_base; }
char const *name() const { return _name; }
File const *file() { return _file; }
Elf::Size const size() const { return _file ? _file->size : 0; }
@ -168,7 +200,7 @@ class Linker::Object : public Genode::Fifo<Object>::Element,
virtual void relocate() = 0;
virtual void load() = 0;
virtual void load() = 0;
virtual bool unload() { return false;}
/**

View File

@ -0,0 +1,20 @@
/**
* \brief Linker script for Linux
* \author Sebastian Sumpf
* \date 2015-12-08
*
* On Linux 32 bit, we remove the ".text.crt0" section because it contains a
* text relocation and is not used as startup code for the dynamic linker.
*/
/*
* Copyright (C) 2009-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.
*/
SECTIONS
{
/DISCARD/ : { *(.text.crt0) }
}

View File

@ -64,7 +64,8 @@ struct Linker::Elf_object : Object, Genode::Fifo<Elf_object>::Element
unsigned flags = 0;
bool relocated = false;
Elf_object(Dependency const *dep) : dyn(dep)
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)
@ -248,7 +249,8 @@ struct Linker::Elf_object : Object, Genode::Fifo<Elf_object>::Element
*/
struct Linker::Ld : Dependency, Elf_object
{
Ld() : Dependency(this, nullptr), Elf_object(this)
Ld()
: Dependency(this, nullptr), Elf_object(this, relocation_address())
{
Genode::strncpy(_name, linker_name(), Object::MAX_PATH);
}
@ -525,7 +527,7 @@ extern "C" void init_rtld()
linker_stack.ref_count++;
/*
* Create actual linker object with different vtable type and set PLT to new
* Create actual linker object with different vtable type and set PLT to new
* DAG.
*/
Ld::linker()->dynamic()->plt_setup();
@ -569,8 +571,12 @@ int main()
/* print loaded object information */
try {
if (Genode::config()->xml_node().attribute("ld_verbose").has_value("yes"))
if (Genode::config()->xml_node().attribute("ld_verbose").has_value("yes")) {
PINF(" %lx .. %lx: context area", Genode::Native_config::context_area_virtual_base(),
Genode::Native_config::context_area_virtual_base() +
Genode::Native_config::context_area_virtual_size() - 1);
dump_loaded();
}
} catch (...) { }
Link_map::dump();