From fa48054959198270522d7f08d99f8f4807073a55 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Tue, 20 Aug 2019 12:18:05 +0200 Subject: [PATCH] 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 --- repos/base/include/base/shared_object.h | 27 ++++++++++++ repos/base/lib/symbols/ld | 2 + repos/base/src/lib/ldso/dependency.cc | 3 ++ repos/base/src/lib/ldso/include/debug.h | 5 +++ repos/base/src/lib/ldso/include/linker.h | 13 +++++- repos/base/src/lib/ldso/main.cc | 52 ++++++++++++++++++++---- 6 files changed, 92 insertions(+), 10 deletions(-) diff --git a/repos/base/include/base/shared_object.h b/repos/base/include/base/shared_object.h index fc24b71cf9..f4dd1622fe 100644 --- a/repos/base/include/base/shared_object.h +++ b/repos/base/include/base/shared_object.h @@ -149,6 +149,8 @@ class Genode::Dynamic_linker static void _for_each_loaded_object(Env &, For_each_fn const &); + static void *_respawn(Env &, char const *, char const *); + public: /** @@ -169,6 +171,31 @@ class Genode::Dynamic_linker _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 + static T respawn(Env &env, char const *binary_name, char const *entrypoint_name) + { + return reinterpret_cast(_respawn(env, binary_name, entrypoint_name)); + } }; #endif /* _INCLUDE__BASE__SHARED_OBJECT_H_ */ diff --git a/repos/base/lib/symbols/ld b/repos/base/lib/symbols/ld index 0afaeb5f61..aebb9d3dc0 100644 --- a/repos/base/lib/symbols/ld +++ b/repos/base/lib/symbols/ld @@ -107,6 +107,8 @@ _ZN6Genode13Shared_objectD2Ev T _ZN6Genode13sleep_foreverEv T _ZN6Genode14Capability_map6insertEmm 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_entrypoint14_alloc_rpc_capERNS_10Pd_sessionENS_17Native_capabilityEm T _ZN6Genode14Rpc_entrypoint17_activation_entryEv T diff --git a/repos/base/src/lib/ldso/dependency.cc b/repos/base/src/lib/ldso/dependency.cc index cb66211c4e..50d8dafaac 100644 --- a/repos/base/src/lib/ldso/dependency.cc +++ b/repos/base/src/lib/ldso/dependency.cc @@ -35,6 +35,9 @@ Linker::Dependency::Dependency(Env &env, Allocator &md_alloc, Linker::Dependency::~Dependency() { + if (!_unload_on_destruct) + return; + if (!_obj.unload()) return; diff --git a/repos/base/src/lib/ldso/include/debug.h b/repos/base/src/lib/ldso/include/debug.h index e3099c6222..c59c793dce 100644 --- a/repos/base/src/lib/ldso/include/debug.h +++ b/repos/base/src/lib/ldso/include/debug.h @@ -103,6 +103,11 @@ struct Linker::Link_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; if (!first) { first = map; diff --git a/repos/base/src/lib/ldso/include/linker.h b/repos/base/src/lib/ldso/include/linker.h index 8ce581902c..3dab1fef28 100644 --- a/repos/base/src/lib/ldso/include/linker.h +++ b/repos/base/src/lib/ldso/include/linker.h @@ -166,6 +166,8 @@ class Linker::Object : private Fifo::Element, virtual void relocate(Bind) = 0; + virtual bool keep() const = 0; + virtual void load() = 0; virtual bool unload() { return false;} @@ -219,6 +221,7 @@ class Linker::Dependency : public Fifo::Element, Noncopyable Object &_obj; Root_object *_root = nullptr; Allocator *_md_alloc = nullptr; + bool const _unload_on_destruct = true; /** * Check if file is in this dependency tree @@ -232,7 +235,10 @@ class Linker::Dependency : public Fifo::Element, Noncopyable /* * 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 *, Fifo &, Keep); @@ -282,7 +288,8 @@ class Linker::Root_object ~Root_object() { _deps.dequeue_all([&] (Dependency &d) { - destroy(_md_alloc, &d); }); + if (!d.obj().keep()) + destroy(_md_alloc, &d); }); } Link_map const &link_map() const @@ -303,6 +310,8 @@ class Linker::Root_object void enqueue(Dependency &dep) { _deps.enqueue(dep); } + void remove_dependency(Dependency &dep) { _deps.remove(dep); } + Fifo &deps() { return _deps; } }; diff --git a/repos/base/src/lib/ldso/main.cc b/repos/base/src/lib/ldso/main.cc index 69fac32a1b..c639c7ae65 100644 --- a/repos/base/src/lib/ldso/main.cc +++ b/repos/base/src/lib/ldso/main.cc @@ -84,10 +84,10 @@ class Linker::Elf_object : public Object, private Fifo::Element friend class Fifo; - Link_map _map { }; - unsigned _ref_count { 1 }; - unsigned const _keep { KEEP }; - bool _relocated { false }; + Link_map _map { }; + unsigned _ref_count { 1 }; + Keep _keep { KEEP }; + bool _relocated { false }; /* * Optional ELF file, skipped for initial 'Ld' initialization @@ -153,6 +153,7 @@ class Linker::Elf_object : public Object, private Fifo::Element /* remove from loaded objects list */ obj_list()->remove(*this); + Init::list()->remove(this); } /** @@ -191,6 +192,8 @@ class Linker::Elf_object : public Object, private Fifo::Element Link_map::add(&_map); }; + void force_keep() { _keep = KEEP; } + Link_map const &link_map() const override { return _map; } Dynamic const &dynamic() const override { return _dyn; } @@ -241,6 +244,8 @@ class Linker::Elf_object : public Object, private Fifo::Element void load() override { _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_binary() const override { return false; } }; @@ -272,6 +277,8 @@ struct Linker::Ld : private Dependency, Elf_object 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 * right below) @@ -349,11 +356,11 @@ struct Linker::Binary : private Root_object, public Elf_object 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), - Elf_object(env, md_alloc, binary_name(), - *new (md_alloc) Dependency(*this, this), KEEP), + Elf_object(env, md_alloc, name, + *new (md_alloc) Dependency(*this, this), DONT_KEEP), _check_ctors(config.check_ctors()) { /* 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_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) { /* read configuration */ @@ -706,7 +742,7 @@ void Component::construct(Genode::Env &env) /* load binary and all dependencies */ try { - binary_ptr = unmanaged_singleton(env, *heap(), config); + binary_ptr = unmanaged_singleton(env, *heap(), config, binary_name()); } catch(Linker::Not_found &symbol) { error("LD: symbol not found: '", symbol, "'"); throw;