ldso: defer execution of static constructors

Ldso now does not automatically execute static constructors of the
binary and shared libraries the binary depends on. If static
construction is required (e.g., if a shared library with constructor is
used or a compilation unit contains global statics) the component needs
to execute the constructors explicitly in Component::construct() via
Genode::Env::exec_static_constructors().

In the case of libc components this is done by the libc startup code
(i.e., the Component::construct() implementation in the libc).

The loading of shared objects at runtime is not affected by this change
and constructors of those objects are executed immediately.

Fixes #2332
This commit is contained in:
Christian Helmuth 2017-03-15 15:40:55 +01:00
parent 67ac0dde6e
commit cb43e04691
36 changed files with 310 additions and 87 deletions

View File

@ -128,6 +128,17 @@ struct Genode::Env
* Close session and block until the session is gone
*/
virtual void close(Parent::Client::Id) = 0;
/**
* Excecute pending static constructors
*
* On component startup, the dynamic linker does not call possible static
* constructors in the binary and shared libraries the binary depends on. If
* the component requires static construction it needs to call this function
* at construction time explicitly. For example, the libc implementation
* executes this function before constructing libc components.
*/
virtual void exec_static_constructors() = 0;
};
#endif /* _INCLUDE__BASE__ENV_H_ */

View File

@ -35,6 +35,7 @@ namespace Genode {
void init_signal_thread(Env &);
void init_root_proxy(Env &);
void init_log();
void exec_static_constructors();
Id_space<Parent::Client> &env_session_id_space();
Env &internal_env();

View File

@ -27,6 +27,15 @@
*/
static Genode::Env *env_ptr = nullptr;
/**
* Excecute pending static constructors
*
* The weak function is used for statically linked binaries. The dynamic linker
* provides the real implementation for dynamically linked components.
*/
void Genode::exec_static_constructors() __attribute__((weak));
void Genode::exec_static_constructors() { }
namespace {
using namespace Genode;
@ -184,6 +193,11 @@ namespace {
if (_parent.close(id) == Parent::CLOSE_PENDING)
_block_for_session();
}
void exec_static_constructors() override
{
Genode::exec_static_constructors();
}
};
}

View File

@ -91,5 +91,5 @@ Linker::Root_object::Root_object(Env &env, Allocator &md_alloc,
new (md_alloc) Dependency(env, md_alloc, linker_name(), this, _deps, DONT_KEEP);
/* relocate and call constructors */
Init::list()->initialize(bind);
Init::list()->initialize(bind, STAGE_SO);
}

View File

@ -126,7 +126,7 @@ class Linker::Dynamic
{
if (!_md_alloc) {
error("unexpected call of section_dt_needed");
return;
throw Fatal();
}
Needed *n = new (*_md_alloc) Needed(d->un.ptr);
_needed.enqueue(n);

View File

@ -36,6 +36,18 @@ struct Linker::Init : List<Object>
return &_list;
}
bool contains_deps() const
{
for (Object const *obj = first(); obj; obj = obj->next_init()) {
if (obj->is_linker()) continue;
if (obj->is_binary()) continue;
return true;
}
return false;
}
Object *contains(char const *file)
{
for (Object *elf = first(); elf; elf = elf->next_init())
@ -62,7 +74,7 @@ struct Linker::Init : List<Object>
});
}
void initialize(Bind bind)
void initialize(Bind bind, Stage stage)
{
Object *obj = first();
@ -74,7 +86,7 @@ struct Linker::Init : List<Object>
}
/*
* Recursive initialization call is not allowed here. This might happend
* Recursive initialization call is not allowed here. This might happen
* when Shared_objects (e.g. dlopen and friends) are constructed from within
* global constructors (ctors).
*/
@ -83,10 +95,21 @@ struct Linker::Init : List<Object>
return;
}
/*
* We do not call static constructors in the binary stage as this must
* be done by the component itself. Later for shared objects, the
* constructors are executed immediately.
*/
if (stage != STAGE_BINARY)
exec_static_constructors();
}
void exec_static_constructors()
{
in_progress = true;
/* call static constructors */
obj = first();
Object *obj = first();
while (obj) {
Object *next = obj->next_init();

View File

@ -32,6 +32,7 @@ namespace Linker {
class Incompatible : Exception { };
class Invalid_file : Exception { };
class Not_found : Exception { };
class Fatal : Exception { };
enum Keep { DONT_KEEP = Shared_object::DONT_KEEP,
KEEP = Shared_object::KEEP };
@ -39,6 +40,8 @@ namespace Linker {
enum Bind { BIND_LAZY = Shared_object::BIND_LAZY,
BIND_NOW = Shared_object::BIND_NOW };
enum Stage { STAGE_BINARY, STAGE_SO };
/**
* Invariants
*/

View File

@ -290,7 +290,10 @@ Elf::Addr Ld::jmp_slot(Dependency const &dep, Elf::Size index)
Reloc_jmpslot slot(dep, dep.obj().dynamic().pltrel_type(),
dep.obj().dynamic().pltrel(), index);
return slot.target_addr();
} catch (...) { error("LD: jump slot relocation failed. FATAL!"); }
} catch (...) {
error("LD: jump slot relocation failed. FATAL!");
throw;
}
return 0;
}
@ -336,6 +339,8 @@ static void exit_on_suspended() { genode_exit(exit_status); }
*/
struct Linker::Binary : Root_object, Elf_object
{
bool static_construction_finished = false;
Binary(Env &env, Allocator &md_alloc, Bind bind)
:
Root_object(md_alloc),
@ -357,7 +362,7 @@ struct Linker::Binary : Root_object, Elf_object
binary->load_needed(env, md_alloc, deps(), DONT_KEEP);
/* relocate and call constructors */
Init::list()->initialize(bind);
Init::list()->initialize(bind, STAGE_BINARY);
}
Elf::Addr lookup_symbol(char const *name)
@ -370,6 +375,32 @@ struct Linker::Binary : Root_object, Elf_object
catch (Linker::Not_found) { return 0; }
}
bool static_construction_pending()
{
if (static_construction_finished) return false;
Func * const ctors_start = (Func *)lookup_symbol("_ctors_start");
Func * const ctors_end = (Func *)lookup_symbol("_ctors_end");
return (ctors_end != ctors_start) || Init::list()->contains_deps();
}
void finish_static_construction()
{
Init::list()->exec_static_constructors();
/* call static construtors and register destructors */
Func * const ctors_start = (Func *)lookup_symbol("_ctors_start");
Func * const ctors_end = (Func *)lookup_symbol("_ctors_end");
for (Func * ctor = ctors_end; ctor != ctors_start; (*--ctor)());
Func * const dtors_start = (Func *)lookup_symbol("_dtors_start");
Func * const dtors_end = (Func *)lookup_symbol("_dtors_end");
for (Func * dtor = dtors_start; dtor != dtors_end; genode_atexit(*dtor++));
static_construction_finished = true;
}
void call_entry_point(Env &env)
{
/* apply the component-provided stack size */
@ -382,18 +413,16 @@ struct Linker::Binary : Root_object, Elf_object
Thread::myself()->stack_size(stack_size);
}
/* call static construtors and register destructors */
Func * const ctors_start = (Func *)lookup_symbol("_ctors_start");
Func * const ctors_end = (Func *)lookup_symbol("_ctors_end");
for (Func * ctor = ctors_end; ctor != ctors_start; (*--ctor)());
Func * const dtors_start = (Func *)lookup_symbol("_dtors_start");
Func * const dtors_end = (Func *)lookup_symbol("_dtors_end");
for (Func * dtor = dtors_start; dtor != dtors_end; genode_atexit(*dtor++));
/* call 'Component::construct' function if present */
if (Elf::Addr addr = lookup_symbol("_ZN9Component9constructERN6Genode3EnvE")) {
((void(*)(Env &))addr)(env);
if (static_construction_pending()) {
error("Component::construct() returned without executing "
"pending static constructors (fix by calling "
"Genode::Env::exec_static_constructors())");
throw Fatal();
}
return;
}
@ -406,6 +435,9 @@ struct Linker::Binary : Root_object, Elf_object
if (Elf::Addr addr = lookup_symbol("main")) {
warning("using legacy main function, please convert to 'Component::construct'");
/* execute static constructors before calling legacy 'main' */
finish_static_construction();
exit_status = ((int (*)(int, char **, char **))addr)(genode_argc,
genode_argv,
genode_envp);
@ -418,6 +450,7 @@ struct Linker::Binary : Root_object, Elf_object
}
error("dynamic linker: component-entrypoint lookup failed");
throw Fatal();
}
void relocate(Bind bind) override
@ -612,6 +645,11 @@ void Genode::init_ldso_phdr(Env &env)
Ld::linker().load_phdr(env, *heap());
}
void Genode::exec_static_constructors()
{
binary_ptr->finish_static_construction();
}
void Component::construct(Genode::Env &env)
{

View File

@ -532,8 +532,10 @@ struct Main
};
/***************
** Component **
***************/
void Component::construct(Genode::Env &env)
{
/* XXX execute constructors of global statics */
env.exec_static_constructors();
void Component::construct(Genode::Env &env) { static Main server(env); }
static Main server(env);
}

View File

@ -160,4 +160,10 @@ struct Main
}
};
void Component::construct(Genode::Env &env) { static Main main(env); }
void Component::construct(Genode::Env &env)
{
/* XXX execute constructors of global statics */
env.exec_static_constructors();
static Main main(env);
}

View File

@ -121,4 +121,10 @@ static void run_linux(void * m)
}
void Component::construct(Genode::Env &env) { static Main m(env); }
void Component::construct(Genode::Env &env)
{
/* XXX execute constructors of global statics */
env.exec_static_constructors();
static Main m(env);
}

View File

@ -16,4 +16,10 @@
extern void start_usb_driver(Genode::Env &env);
void Component::construct(Genode::Env &env) { start_usb_driver(env); }
void Component::construct(Genode::Env &env)
{
/* XXX execute constructors of global statics */
env.exec_static_constructors();
start_usb_driver(env);
}

View File

@ -49,4 +49,10 @@ struct Main
};
void Component::construct(Genode::Env &env) { static Main inst(env); }
void Component::construct(Genode::Env &env)
{
/* XXX execute constructors of global statics */
env.exec_static_constructors();
static Main inst(env);
}

View File

@ -521,4 +521,10 @@ struct File_system::Main
};
void Component::construct(Genode::Env &env) { static File_system::Main inst(env); }
void Component::construct(Genode::Env &env)
{
/* XXX execute constructors of global statics (uses shared objects) */
env.exec_static_constructors();
static File_system::Main inst(env);
}

View File

@ -160,4 +160,10 @@ struct Main : Scout::Event_handler
};
void Component::construct(Genode::Env &env) { static Main main(env); }
void Component::construct(Genode::Env &env)
{
/* XXX execute constructors of global statics */
env.exec_static_constructors();
static Main main(env);
}

View File

@ -168,4 +168,10 @@ struct Scout::Main : Scout::Event_handler
};
void Component::construct(Genode::Env &env) { static Scout::Main main(env); }
void Component::construct(Genode::Env &env)
{
/* XXX execute constructors of global statics */
env.exec_static_constructors();
static Scout::Main main(env);
}

View File

@ -311,4 +311,10 @@ class Liquid_fb::Main : public Scout::Event_handler
};
void Component::construct(Genode::Env &env) { static Liquid_fb::Main main(env); }
void Component::construct(Genode::Env &env)
{
/* XXX execute constructors of global statics */
env.exec_static_constructors();
static Liquid_fb::Main main(env);
}

View File

@ -496,4 +496,10 @@ struct Nitlog::Main
};
void Component::construct(Genode::Env &env) { static Nitlog::Main main(env); }
void Component::construct(Genode::Env &env)
{
/* XXX execute constructors of global statics */
env.exec_static_constructors();
static Nitlog::Main main(env);
}

View File

@ -194,4 +194,10 @@ struct Cpu_sampler::Main : Thread_list_change_handler
** Component **
***************/
void Component::construct(Genode::Env &env) { static Cpu_sampler::Main inst(env); }
void Component::construct(Genode::Env &env)
{
/* XXX execute constructors of global statics (uses shared objects) */
env.exec_static_constructors();
static Cpu_sampler::Main inst(env);
}

View File

@ -690,6 +690,9 @@ extern char _binary_terminus_16_tff_start;
void Component::construct(Genode::Env &env)
{
/* XXX execute constructors of global statics */
env.exec_static_constructors();
using namespace Genode;
Attached_rom_dataspace config(env, "config");

View File

@ -304,6 +304,13 @@ ACPI_STATUS AcpiOsInstallInterruptHandler(UINT32 irq, ACPI_OSD_HANDLER handler,
/* used by normal (no-printf-debug) target */
void Component::construct(Genode::Env &env) { static Acpica::Main main(env); }
void Component::construct(Genode::Env &env)
{
/* XXX execute constructors of global statics */
env.exec_static_constructors();
static Acpica::Main main(env);
}
/* used by debug target (using printf of libc) */
void Libc::Component::construct(Libc::Env &env) { static Acpica::Main main(env); }

View File

@ -54,6 +54,8 @@ struct Qt_launchpad_namespace::Local_env : Genode::Env
{ return genode_env.upgrade(id, args); }
void close(Parent::Client::Id id) { return genode_env.close(id); }
void exec_static_constructors() override { }
};

View File

@ -243,4 +243,10 @@ struct Framebuffer::Main
};
void Component::construct(Genode::Env &env) { static Framebuffer::Main inst(env); }
void Component::construct(Genode::Env &env)
{
/* XXX execute constructors of global statics */
env.exec_static_constructors();
static Framebuffer::Main inst(env);
}

View File

@ -159,6 +159,9 @@ class Libc::Env_implementation : public Libc::Env
void close(Parent::Client::Id id) override {
return _env.close(id); }
/* already done by the libc */
void exec_static_constructors() override { }
};
@ -864,16 +867,30 @@ void Component::construct(Genode::Env &env)
Libc::init_dl(env);
Libc::sysctl_init(env);
kernel = unmanaged_singleton<Libc::Kernel>(env);
Libc::libc_config_init(kernel->libc_env().libc_config());
/*
* XXX The following two steps leave us with the dilemma that we don't know
* which linked library may depend on the successfull initialization of a
* plugin. For example, some high-level library may try to open a network
* connection in its constructor before the network-stack library is
* initialized. But, we can't initialize plugins before calling static
* constructors as those are needed to know about the libc plugin. The only
* solution is to remove all libc plugins beside the VFS implementation,
* which is our final goal anyway.
*/
/* finish static construction of component and libraries */
Libc::with_libc([&] () { env.exec_static_constructors(); });
/* initialize plugins that require Genode::Env */
auto init_plugin = [&] (Libc::Plugin &plugin) {
plugin.init(env);
};
Libc::plugin_registry()->for_each_plugin(init_plugin);
kernel = unmanaged_singleton<Libc::Kernel>(env);
Libc::libc_config_init(kernel->libc_env().libc_config());
/* construct libc component on kernel stack */
Libc::Component::construct(kernel->libc_env());
}

View File

@ -404,4 +404,10 @@ struct Transform::Main {
};
void Component::construct(Genode::Env &env) { static Transform::Main main(env); }
void Component::construct(Genode::Env &env)
{
/* XXX execute constructors of global statics */
env.exec_static_constructors();
static Transform::Main main(env);
}

View File

@ -26,23 +26,20 @@
using namespace Genode;
/*****************************************
** Implementation of the input service **
*****************************************/
/*
* Input event buffer that is shared with the client
*/
enum { MAX_EVENTS = 1000 };
static Dataspace_capability ev_ds_cap;
namespace Input {
class Session_component : public Genode::Rpc_object<Session>
{
private:
Dataspace_capability _ev_ds_cap;
public:
Dataspace_capability dataspace() override { return ev_ds_cap; }
Session_component(Dataspace_capability ev_ds_cap)
: _ev_ds_cap(ev_ds_cap) { }
Dataspace_capability dataspace() override { return _ev_ds_cap; }
bool pending() const override { return 0; }
@ -60,36 +57,36 @@ namespace Input {
{
protected:
Dataspace_capability _ev_ds_cap;
Session_component *_create_session(const char *args) {
return new (md_alloc()) Session_component(); }
return new (md_alloc()) Session_component(_ev_ds_cap); }
public:
Root(Genode::Entrypoint &ep, Allocator &md_alloc)
: Root_component<Session_component>(ep, md_alloc) { }
Root(Genode::Entrypoint &ep, Allocator &md_alloc,
Dataspace_capability ev_ds_cap)
:
Root_component<Session_component>(ep, md_alloc),
_ev_ds_cap(ev_ds_cap)
{ }
};
}
struct Main
{
Genode::Sliced_heap heap;
Genode::Env &env;
Input::Root root;
Sliced_heap heap { env.ram(), env.rm() };
Main(Genode::Env &env)
:
heap(env.ram(), env.rm()),
root(env.ep(), heap)
Dataspace_capability ev_ds_cap {
env.ram().alloc(1000*sizeof(Input::Event)) };
Input::Root root { env.ep(), heap, ev_ds_cap };
Main(Genode::Env &env) : env(env)
{
/* create dataspace for event buffer that is shared with the client */
try { ev_ds_cap = env.ram().alloc(MAX_EVENTS*sizeof(Input::Event)); }
catch (Ram_session::Alloc_failed) {
Genode::error("could not allocate dataspace for event buffer");
throw Genode::Exception();
}
/* tell parent about the service */
env.parent().announce(env.ep().manage(root));
}
};

View File

@ -143,4 +143,10 @@ struct Platform::Main
};
void Component::construct(Genode::Env &env) { static Platform::Main main(env); }
void Component::construct(Genode::Env &env)
{
/* XXX execute constructors of global statics */
env.exec_static_constructors();
static Platform::Main main(env);
}

View File

@ -82,4 +82,10 @@ struct Main
};
void Component::construct(Genode::Env &env) { static Main server(env); }
void Component::construct(Genode::Env &env)
{
/* XXX execute constructors of global statics */
env.exec_static_constructors();
static Main server(env);
}

View File

@ -43,8 +43,6 @@ class Iso::Sector {
BLOCK_SIZE = 2048,
};
static Lock _lock;
private:
Block::Session::Tx::Source &_source;
@ -56,8 +54,6 @@ class Iso::Sector {
unsigned long blk_nr, unsigned long count)
: _source(*block.tx())
{
// Lock::Guard lock_guard(_lock);
try {
_p = Block::Packet_descriptor(
block.dma_alloc_packet(blk_size() * count),
@ -81,7 +77,6 @@ class Iso::Sector {
}
~Sector() {
// Lock::Guard lock_guard(_lock);
_source.release_packet(_p);
}
@ -98,12 +93,6 @@ class Iso::Sector {
};
/*
* Static members of Sector
*/
Lock Iso::Sector::_lock;
/**
* Rock ridge extension (see IEEE P1282)
*/

View File

@ -65,4 +65,10 @@ struct Main
};
void Component::construct(Genode::Env &env) { static Main nic_bridge(env); }
void Component::construct(Genode::Env &env)
{
/* XXX execute constructors of global statics */
env.exec_static_constructors();
static Main nic_bridge(env);
}

View File

@ -55,4 +55,10 @@ Main::Main(Env &env)
}
void Component::construct(Env &env) { static Main main(env); }
void Component::construct(Env &env)
{
/* XXX execute constructors of global statics */
env.exec_static_constructors();
static Main main(env);
}

View File

@ -59,4 +59,10 @@ Main::Main(Env &env)
}
void Component::construct(Env &env) { static Main main(env); }
void Component::construct(Env &env)
{
/* XXX execute constructors of global statics */
env.exec_static_constructors();
static Main main(env);
}

View File

@ -1394,4 +1394,10 @@ void Nitpicker::Main::handle_fb_mode()
}
void Component::construct(Genode::Env &env) { static Nitpicker::Main nitpicker(env); }
void Component::construct(Genode::Env &env)
{
/* XXX execute constructors of global statics */
env.exec_static_constructors();
static Nitpicker::Main nitpicker(env);
}

View File

@ -385,6 +385,9 @@ void perform(Genode::Env &env, Genode::Heap &heap, unsigned timeo_ms = 0)
void Component::construct(Genode::Env &env)
{
/* XXX execute constructors of global statics */
env.exec_static_constructors();
using namespace Genode;
try {

View File

@ -13,7 +13,6 @@
/* Genode includes */
#include <cap_session/connection.h>
#include <dataspace/client.h>
#include <base/lock.h>
@ -39,19 +38,19 @@ void (*libc_select_notify)();
Socket_io_channel_backend *name = \
dynamic_cast<Socket_io_channel_backend*>(backend)
static Genode::Lock _select_notify_lock;
/**
* This callback function is called from lwip via the libc_select_notify
* function pointer if an event occurs.
*/
static void select_notify()
{
static Genode::Lock mutex;
/*
* The function could be called multiple times while actually
* still running.
*/
Genode::Lock::Guard guard(_select_notify_lock);
Genode::Lock::Guard guard(mutex);
for (Io_receptor *r = io_receptor_registry()->first();
r != 0; r = r->next()) {

View File

@ -185,4 +185,10 @@ struct Main
};
void Component::construct(Genode::Env &env) { static Main main(env); }
void Component::construct(Genode::Env &env)
{
/* XXX execute constructors of global statics */
env.exec_static_constructors();
static Main main(env);
}