genode/base/src/platform/_main.cc
Christian Prochaska ea3adfef9a Create the context area RM session at startup
With this patch, the thread context area RM session gets created at
program startup to have the thread context area's virtual address range
reserved right from the beginning.

Fixes #734.

This patch implies that all Genode processes try to create an RM
session. So a route to the RM service must be present even for processes
that have only a single thread. Hence, the patch contains an update of
affected components.
2013-05-07 21:56:07 +02:00

269 lines
6.2 KiB
C++

/*
* \brief Startup code
* \author Christian Helmuth
* \author Christian Prochaska
* \date 2006-04-12
*
* The startup code calls constructors for static objects before calling
* main(). Furthermore, this file contains the support of exit handlers
* and destructors.
*
* Some code within this file is based on 'atexit.c' of FreeBSD's libc.
*/
/*
* Copyright (C) 2006-2013 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.
*/
/* Genode includes */
#include <base/crt0.h>
#include <base/env.h>
#include <base/sleep.h>
#include <base/printf.h>
/* platform-specific local helper functions */
#include <_main_helper.h>
#include <_main_parent_cap.h>
using namespace Genode;
extern int main(int argc, char **argv, char **envp);
extern void init_exception_handling(); /* implemented in base/cxx */
namespace Genode {
extern Rm_session *env_context_area_rm_session();
}
enum { ATEXIT_SIZE = 256 };
/***************
** C++ stuff **
***************/
enum Atexit_fn_type { ATEXIT_FN_EMPTY, ATEXIT_FN_STD, ATEXIT_FN_CXA };
struct atexit_fn
{
Atexit_fn_type fn_type;
union
{
void (*std_func)(void);
void (*cxa_func)(void *);
} fn_ptr; /* function pointer */
void *fn_arg; /* argument for CXA callback */
void *fn_dso; /* shared module handle */
};
static struct atexit
{
int index;
struct atexit_fn fns[ATEXIT_SIZE];
} _atexit;
static Lock *atexit_lock()
{
static Lock _atexit_lock;
return &_atexit_lock;
}
static int atexit_register(struct atexit_fn *fn)
{
atexit_lock()->lock();
if (_atexit.index >= ATEXIT_SIZE) {
PERR("Cannot register exit handler - ATEXIT_SIZE reached");
atexit_lock()->unlock();
return -1;
}
_atexit.fns[_atexit.index++] = *fn;
atexit_lock()->unlock();
return 0;
}
/**
* Register a function to be performed at exit
*/
int genode_atexit(void (*func)(void))
{
struct atexit_fn fn;
int error;
fn.fn_type = ATEXIT_FN_STD;
fn.fn_ptr.std_func = func;;
fn.fn_arg = 0;
fn.fn_dso = 0;
error = atexit_register(&fn);
return (error);
}
/**
* Register a function to be performed at exit or when an shared object
* with given dso handle is unloaded dynamically.
*
* This function is called directly by compiler generated code, so
* it needs to be declared as extern "C" and cannot be local to
* the cxx lib.
*/
int genode___cxa_atexit(void (*func)(void*), void *arg, void *dso)
{
struct atexit_fn fn;
int error;
fn.fn_type = ATEXIT_FN_CXA;
fn.fn_ptr.cxa_func = func;;
fn.fn_arg = arg;
fn.fn_dso = dso;
error = atexit_register(&fn);
return (error);
}
/*
* Call all handlers registered with __cxa_atexit for the shared
* object owning 'dso'. Note: if 'dso' is NULL, then all remaining
* handlers are called.
*/
void genode___cxa_finalize(void *dso)
{
struct atexit_fn fn;
int n = 0;
atexit_lock()->lock();
for (n = _atexit.index; --n >= 0;) {
if (_atexit.fns[n].fn_type == ATEXIT_FN_EMPTY)
continue; /* already been called */
if (dso != 0 && dso != _atexit.fns[n].fn_dso)
continue; /* wrong DSO */
fn = _atexit.fns[n];
/*
* Mark entry to indicate that this particular handler
* has already been called.
*/
_atexit.fns[n].fn_type = ATEXIT_FN_EMPTY;
atexit_lock()->unlock();
/* call the function of correct type */
if (fn.fn_type == ATEXIT_FN_CXA)
fn.fn_ptr.cxa_func(fn.fn_arg);
else if (fn.fn_type == ATEXIT_FN_STD)
fn.fn_ptr.std_func();
atexit_lock()->lock();
}
atexit_lock()->unlock();
}
/**
* Terminate the process.
*/
void genode_exit(int status)
{
/* inform parent about the exit status */
env()->parent()->exit(status);
/*
* Call destructors for static objects.
*
* It happened that a function from the dtors list (namely
* __clean_env_destructor() from the libc) called another function, which
* depended on the Genode environment. Since the Genode environment gets
* destroyed by genode___cxa_finalize(), the functions from the dtors list
* are called before genode___cxa_finalize().
*
*/
void (**func)();
for (func = &_dtors_start; func != &_dtors_end; (*func++)());
/* call all handlers registered with atexit() or __cxa_atexit() */
genode___cxa_finalize(0);
/*
* Wait for destruction by the parent who was supposed to be notified by
* the destructor of the static Genode::Env instance.
*/
sleep_forever();
}
/**
* Dummy default arguments for main function
*/
static char argv0[] = { '_', 'm', 'a', 'i', 'n', 0};
static char *argv[1] = { argv0 };
/**
* Arguments for main function
*
* These global variables may be initialized by a constructor provided by an
* external library.
*/
char **genode_argv = argv;
int genode_argc = 1;
char **genode_envp = 0;
/**
* C entry function called by the crt0 startup code
*/
extern "C" int _main()
{
main_thread_bootstrap();
/* initialize exception handling */
init_exception_handling();
/*
* Trigger first exception. This step has two purposes.
* First, it enables us to detect problems related to exception handling as
* early as possible. If there are problems with the C++ support library,
* it is much easier to debug them at this early stage. Otherwise problems
* with half-working exception handling cause subtle failures that are hard
* to interpret.
*
* Second, the C++ support library allocates data structures lazily on the
* first occurrence of an exception. This allocation traverses into
* Genode's heap and, in some corner cases, consumes several KB of stack.
* This is usually not a problem when the first exception is triggered from
* the main thread but it becomes an issue when the first exception is
* thrown from the context of a thread with a specially tailored (and
* otherwise sufficient) stack size. By throwing an exception here, we
* mitigate this issue by eagerly performing those allocations.
*/
try { throw 1; } catch (...) { }
/* call constructors for static objects */
void (**func)();
for (func = &_ctors_end; func != &_ctors_start; (*--func)());
/* now, it is save to call printf */
/* create the thread context area RM session */
env_context_area_rm_session();
/* call real main function */
int ret = main(genode_argc, genode_argv, genode_envp);
genode_exit(ret);
/* not reached */
return ret;
}