ldso: Dynamic_linker::respawn mechanism

This patch extends the interface of the dynamic linker with the ability
to replace the running binary executable by another one. It is
designated for the implementation of execve. The interface consists of
two new functions.

'Dynamic_linker::keep' marks the specified shared object as unloadable.
This can be used to pin a set of libraries (i.e., the libc) within the
local address space while replacing the binary and other higher-level
libraries.

'Dynamic_linker::respawn' unloads the current binary, loads the one
specifed as first argument, and looks up the entry point symbol of the
new binary, which would be "main" for POSIX programs.

In addition to implementing the new interface, the patch adjusts the
linker at various places that previously assumed the binary to be
constant over runtime.

Issue #3481
This commit is contained in:
Norman Feske 2019-08-20 12:18:05 +02:00 committed by Christian Helmuth
parent 808ff3714e
commit fa48054959
6 changed files with 92 additions and 10 deletions

View File

@ -149,6 +149,8 @@ class Genode::Dynamic_linker
static void _for_each_loaded_object(Env &, For_each_fn const &); static void _for_each_loaded_object(Env &, For_each_fn const &);
static void *_respawn(Env &, char const *, char const *);
public: public:
/** /**
@ -169,6 +171,31 @@ class Genode::Dynamic_linker
_for_each_loaded_object(env, wrapped_fn); _for_each_loaded_object(env, wrapped_fn);
} }
/**
* Prevent loaded shared object 'name' to be unloaded
*/
static void keep(Env &, char const *name);
typedef Shared_object::Invalid_rom_module Invalid_rom_module;
typedef Shared_object::Invalid_symbol Invalid_symbol;
/**
* Replace executable binary
*
* \param binary_name ROM module name of new executable binary
* \param start_symbol symbol name of the binary's entry point
*
* \return pointer to entry point of the new executable
*
* \throw Invalid_rom_module
* \throw Invalid_symbol
*/
template<typename T = void *>
static T respawn(Env &env, char const *binary_name, char const *entrypoint_name)
{
return reinterpret_cast<T>(_respawn(env, binary_name, entrypoint_name));
}
}; };
#endif /* _INCLUDE__BASE__SHARED_OBJECT_H_ */ #endif /* _INCLUDE__BASE__SHARED_OBJECT_H_ */

View File

@ -107,6 +107,8 @@ _ZN6Genode13Shared_objectD2Ev T
_ZN6Genode13sleep_foreverEv T _ZN6Genode13sleep_foreverEv T
_ZN6Genode14Capability_map6insertEmm T _ZN6Genode14Capability_map6insertEmm T
_ZN6Genode14Dynamic_linker23_for_each_loaded_objectERNS_3EnvERKNS0_11For_each_fnE T _ZN6Genode14Dynamic_linker23_for_each_loaded_objectERNS_3EnvERKNS0_11For_each_fnE T
_ZN6Genode14Dynamic_linker4keepERNS_3EnvEPKc T
_ZN6Genode14Dynamic_linker8_respawnERNS_3EnvEPKcS4_ T
_ZN6Genode14Rpc_entrypoint13_free_rpc_capERNS_10Pd_sessionENS_17Native_capabilityE T _ZN6Genode14Rpc_entrypoint13_free_rpc_capERNS_10Pd_sessionENS_17Native_capabilityE T
_ZN6Genode14Rpc_entrypoint14_alloc_rpc_capERNS_10Pd_sessionENS_17Native_capabilityEm T _ZN6Genode14Rpc_entrypoint14_alloc_rpc_capERNS_10Pd_sessionENS_17Native_capabilityEm T
_ZN6Genode14Rpc_entrypoint17_activation_entryEv T _ZN6Genode14Rpc_entrypoint17_activation_entryEv T

View File

@ -35,6 +35,9 @@ Linker::Dependency::Dependency(Env &env, Allocator &md_alloc,
Linker::Dependency::~Dependency() Linker::Dependency::~Dependency()
{ {
if (!_unload_on_destruct)
return;
if (!_obj.unload()) if (!_obj.unload())
return; return;

View File

@ -103,6 +103,11 @@ struct Linker::Link_map
static void add(Link_map *map) static void add(Link_map *map)
{ {
/* prevent duplicates */
for (Link_map *m = first; m; m = m->next)
if (m == map)
return;
map->next = nullptr; map->next = nullptr;
if (!first) { if (!first) {
first = map; first = map;

View File

@ -166,6 +166,8 @@ class Linker::Object : private Fifo<Object>::Element,
virtual void relocate(Bind) = 0; virtual void relocate(Bind) = 0;
virtual bool keep() const = 0;
virtual void load() = 0; virtual void load() = 0;
virtual bool unload() { return false;} virtual bool unload() { return false;}
@ -219,6 +221,7 @@ class Linker::Dependency : public Fifo<Dependency>::Element, Noncopyable
Object &_obj; Object &_obj;
Root_object *_root = nullptr; Root_object *_root = nullptr;
Allocator *_md_alloc = nullptr; Allocator *_md_alloc = nullptr;
bool const _unload_on_destruct = true;
/** /**
* Check if file is in this dependency tree * Check if file is in this dependency tree
@ -232,7 +235,10 @@ class Linker::Dependency : public Fifo<Dependency>::Element, Noncopyable
/* /*
* Called by 'Ld' constructor * Called by 'Ld' constructor
*/ */
Dependency(Object &obj, Root_object *root) : _obj(obj), _root(root) { } Dependency(Object &obj, Root_object *root)
:
_obj(obj), _root(root), _unload_on_destruct(false)
{ }
Dependency(Env &, Allocator &, char const *path, Root_object *, Dependency(Env &, Allocator &, char const *path, Root_object *,
Fifo<Dependency> &, Keep); Fifo<Dependency> &, Keep);
@ -282,7 +288,8 @@ class Linker::Root_object
~Root_object() ~Root_object()
{ {
_deps.dequeue_all([&] (Dependency &d) { _deps.dequeue_all([&] (Dependency &d) {
destroy(_md_alloc, &d); }); if (!d.obj().keep())
destroy(_md_alloc, &d); });
} }
Link_map const &link_map() const Link_map const &link_map() const
@ -303,6 +310,8 @@ class Linker::Root_object
void enqueue(Dependency &dep) { _deps.enqueue(dep); } void enqueue(Dependency &dep) { _deps.enqueue(dep); }
void remove_dependency(Dependency &dep) { _deps.remove(dep); }
Fifo<Dependency> &deps() { return _deps; } Fifo<Dependency> &deps() { return _deps; }
}; };

View File

@ -84,10 +84,10 @@ class Linker::Elf_object : public Object, private Fifo<Elf_object>::Element
friend class Fifo<Elf_object>; friend class Fifo<Elf_object>;
Link_map _map { }; Link_map _map { };
unsigned _ref_count { 1 }; unsigned _ref_count { 1 };
unsigned const _keep { KEEP }; Keep _keep { KEEP };
bool _relocated { false }; bool _relocated { false };
/* /*
* Optional ELF file, skipped for initial 'Ld' initialization * Optional ELF file, skipped for initial 'Ld' initialization
@ -153,6 +153,7 @@ class Linker::Elf_object : public Object, private Fifo<Elf_object>::Element
/* remove from loaded objects list */ /* remove from loaded objects list */
obj_list()->remove(*this); obj_list()->remove(*this);
Init::list()->remove(this);
} }
/** /**
@ -191,6 +192,8 @@ class Linker::Elf_object : public Object, private Fifo<Elf_object>::Element
Link_map::add(&_map); Link_map::add(&_map);
}; };
void force_keep() { _keep = KEEP; }
Link_map const &link_map() const override { return _map; } Link_map const &link_map() const override { return _map; }
Dynamic const &dynamic() const override { return _dyn; } Dynamic const &dynamic() const override { return _dyn; }
@ -241,6 +244,8 @@ class Linker::Elf_object : public Object, private Fifo<Elf_object>::Element
void load() override { _ref_count++; } void load() override { _ref_count++; }
bool unload() override { return (_keep == DONT_KEEP) && !(--_ref_count); } bool unload() override { return (_keep == DONT_KEEP) && !(--_ref_count); }
bool keep() const override { return _keep == KEEP; }
bool is_linker() const override { return false; } bool is_linker() const override { return false; }
bool is_binary() const override { return false; } bool is_binary() const override { return false; }
}; };
@ -272,6 +277,8 @@ struct Linker::Ld : private Dependency, Elf_object
bool is_linker() const override { return true; } bool is_linker() const override { return true; }
bool keep() const override { return true; }
/** /**
* Entry point for jump relocations, it is called from assembly code and is implemented * Entry point for jump relocations, it is called from assembly code and is implemented
* right below) * right below)
@ -349,11 +356,11 @@ struct Linker::Binary : private Root_object, public Elf_object
bool static_construction_finished = false; bool static_construction_finished = false;
Binary(Env &env, Allocator &md_alloc, Config const &config) Binary(Env &env, Allocator &md_alloc, Config const &config, char const *name)
: :
Root_object(md_alloc), Root_object(md_alloc),
Elf_object(env, md_alloc, binary_name(), Elf_object(env, md_alloc, name,
*new (md_alloc) Dependency(*this, this), KEEP), *new (md_alloc) Dependency(*this, this), DONT_KEEP),
_check_ctors(config.check_ctors()) _check_ctors(config.check_ctors())
{ {
/* create dep for binary and linker */ /* create dep for binary and linker */
@ -697,6 +704,35 @@ void Genode::Dynamic_linker::_for_each_loaded_object(Env &, For_each_fn const &f
} }
void Dynamic_linker::keep(Env &, char const *binary)
{
Elf_object::obj_list()->for_each([&] (Elf_object &obj) {
if (Object::Name(binary) == obj.name())
obj.force_keep(); });
}
void *Dynamic_linker::_respawn(Env &env, char const *binary, char const *entry_name)
{
Object::Name const name(binary);
/* unload original binary */
binary_ptr->~Binary();
Config const config(env);
/* load new binary */
construct_at<Binary>(binary_ptr, env, *heap(), config, name.string());
try {
return (void *)binary_ptr->lookup_symbol(entry_name);
}
catch (...) { }
throw Dynamic_linker::Invalid_symbol();
}
void Component::construct(Genode::Env &env) void Component::construct(Genode::Env &env)
{ {
/* read configuration */ /* read configuration */
@ -706,7 +742,7 @@ void Component::construct(Genode::Env &env)
/* load binary and all dependencies */ /* load binary and all dependencies */
try { try {
binary_ptr = unmanaged_singleton<Binary>(env, *heap(), config); binary_ptr = unmanaged_singleton<Binary>(env, *heap(), config, binary_name());
} catch(Linker::Not_found &symbol) { } catch(Linker::Not_found &symbol) {
error("LD: symbol not found: '", symbol, "'"); error("LD: symbol not found: '", symbol, "'");
throw; throw;