mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-23 15:32:25 +00:00
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:
parent
808ff3714e
commit
fa48054959
@ -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_ */
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user