mirror of
https://github.com/mmueller41/genode.git
synced 2026-01-21 20:42:56 +01:00
base: setup thread object for main thread in CRT0
For a main thread a thread object is created by the CRT0 before _main gets called so that _main can already run in a generic environment that, e.g., catches stack overflows as a page-fault instead of corrupting the BSS. Additionally dynamic programs have only one CRT0 - the one of the LDSO - which does the initialization for both LDSO and program. ref #989
This commit is contained in:
committed by
Norman Feske
parent
ba8e61653f
commit
0b64328944
@@ -73,20 +73,6 @@ namespace Genode {
|
||||
* Heap backed by the ram_session of the environment.
|
||||
*/
|
||||
virtual Allocator *heap() = 0;
|
||||
|
||||
/**
|
||||
* Reload parent capability and reinitialize environment resources
|
||||
*
|
||||
* This function is solely used for implementing fork semantics.
|
||||
* After forking a process, the new child process is executed
|
||||
* within a copy of the address space of the forking process.
|
||||
* Thereby, the new process inherits the original 'env' object of
|
||||
* the forking process, which is meaningless in the context of the
|
||||
* new process. By calling this function, the new process is able
|
||||
* to reinitialize its 'env' with meaningful capabilities obtained
|
||||
* via its updated parent capability.
|
||||
*/
|
||||
virtual void reload_parent_cap(Capability<Parent>::Dst, long) = 0;
|
||||
};
|
||||
|
||||
extern Env *env();
|
||||
|
||||
@@ -185,11 +185,13 @@ namespace Genode {
|
||||
/**
|
||||
* Allocate thread context for specified thread
|
||||
*
|
||||
* \param thread thread for which to allocate the new context
|
||||
* \return virtual address of new thread context, or
|
||||
* 0 if the allocation failed
|
||||
* \param thread thread for which to allocate the new context
|
||||
* \param main_thread wether to alloc for the main thread
|
||||
*
|
||||
* \return virtual address of new thread context, or
|
||||
* 0 if the allocation failed
|
||||
*/
|
||||
Context *alloc(Thread_base *thread);
|
||||
Context *alloc(Thread_base *thread, bool main_thread);
|
||||
|
||||
/**
|
||||
* Release thread context
|
||||
@@ -224,8 +226,11 @@ namespace Genode {
|
||||
|
||||
/**
|
||||
* Allocate and locally attach a new thread context
|
||||
*
|
||||
* \param stack_size size of this threads stack
|
||||
* \param main_thread wether this is the main thread
|
||||
*/
|
||||
Context *_alloc_context(size_t stack_size);
|
||||
Context *_alloc_context(size_t stack_size, bool main_thread);
|
||||
|
||||
/**
|
||||
* Detach and release thread context of the thread
|
||||
@@ -246,11 +251,6 @@ namespace Genode {
|
||||
*/
|
||||
static void _thread_start();
|
||||
|
||||
/**
|
||||
* Hook for platform-specific constructor supplements
|
||||
*/
|
||||
void _init_platform_thread();
|
||||
|
||||
/**
|
||||
* Hook for platform-specific destructor supplements
|
||||
*/
|
||||
@@ -285,6 +285,14 @@ namespace Genode {
|
||||
*/
|
||||
Genode::Lock _join_lock;
|
||||
|
||||
/**
|
||||
* Thread type
|
||||
*
|
||||
* Some threads need special treatment at construction. This enum
|
||||
* is solely used to distinguish them at construction.
|
||||
*/
|
||||
enum Type { NORMAL, MAIN, REINITIALIZED_MAIN };
|
||||
|
||||
private:
|
||||
|
||||
Trace::Logger _trace_logger;
|
||||
@@ -296,6 +304,13 @@ namespace Genode {
|
||||
*/
|
||||
static Trace::Logger *_logger();
|
||||
|
||||
/**
|
||||
* Hook for platform-specific constructor supplements
|
||||
*
|
||||
* \param main_thread wether this is the main thread
|
||||
*/
|
||||
void _init_platform_thread(Type type);
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
@@ -303,6 +318,7 @@ namespace Genode {
|
||||
*
|
||||
* \param name thread name for debugging
|
||||
* \param stack_size stack size
|
||||
* \param type enables selection of special construction
|
||||
*
|
||||
* \throw Stack_too_large
|
||||
* \throw Stack_alloc_failed
|
||||
@@ -313,8 +329,14 @@ namespace Genode {
|
||||
* stack size is internally used by the framework for storing
|
||||
* thread-context information such as the thread's name (see
|
||||
* 'struct Context').
|
||||
*
|
||||
* FIXME: With type = Forked_main_thread the whole
|
||||
* Context::_alloc_context call gets skipped but we should
|
||||
* at least set Context::ds_cap in a way that it references
|
||||
* the dataspace of the already attached stack.
|
||||
*/
|
||||
Thread_base(const char *name, size_t stack_size);
|
||||
Thread_base(const char *name, size_t stack_size,
|
||||
Type type = NORMAL);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
@@ -448,9 +470,10 @@ namespace Genode {
|
||||
* Constructor
|
||||
*
|
||||
* \param name thread name (for debugging)
|
||||
* \param type enables selection of special construction
|
||||
*/
|
||||
explicit Thread(const char *name)
|
||||
: Thread_base(name, STACK_SIZE) { }
|
||||
explicit Thread(const char *name, Type type = NORMAL)
|
||||
: Thread_base(name, STACK_SIZE, type) { }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
SRC_S += crt0.s
|
||||
SRC_CC += _main.cc
|
||||
SRC_CC += init_main_thread.cc
|
||||
|
||||
REP_INC_DIR += src/platform
|
||||
|
||||
LIBS += syscall
|
||||
|
||||
vpath _main.cc $(BASE_DIR)/src/platform
|
||||
vpath init_main_thread.cc $(BASE_DIR)/src/platform
|
||||
|
||||
@@ -84,6 +84,15 @@ LIBS += $(BASE_LIBS)
|
||||
else
|
||||
LIBS := $(filter-out $(BASE_LIBS),$(LIBS))
|
||||
LIBS += $(DYNAMIC_LINKER)
|
||||
|
||||
#
|
||||
# Ensure that startup_dyn is build for the dynamic programs that depend on a
|
||||
# shared library. They add it to their dependencies as replacement for the
|
||||
# static-case startup as soon as they recognize that they are dynamic.
|
||||
# The current library in contrast filters-out startup_dyn from its
|
||||
# dependencies before they get merged.
|
||||
#
|
||||
LIBS += startup_dyn
|
||||
endif
|
||||
|
||||
|
||||
|
||||
@@ -125,9 +125,15 @@ $(LIB_A): $(OBJECTS)
|
||||
$(VERBOSE)$(AR) -rc $@ $(OBJECTS)
|
||||
|
||||
#
|
||||
# Prevent linkage of startup code against shared libraries except for ld.lib.so
|
||||
# Prevent linkage of startup_dyn as we added it only in order that it gets
|
||||
# build for the dynamic programs.
|
||||
#
|
||||
ifdef SHARED_LIB
|
||||
override DEPS := $(filter-out startup_dyn.lib,$(DEPS))
|
||||
|
||||
#
|
||||
# Prevent linkage of startup code against shared libraries except for ld.lib.so
|
||||
#
|
||||
ifneq ($(LIB),ld)
|
||||
override DEPS := $(filter-out startup.lib,$(DEPS))
|
||||
endif
|
||||
|
||||
@@ -120,9 +120,13 @@ LD_CMD += -Wl,--dynamic-linker=$(DYNAMIC_LINKER).lib.so \
|
||||
-Wl,--eh-frame-hdr
|
||||
|
||||
#
|
||||
# Filter out the base libraries since they will be provided by the ldso.library
|
||||
# Filter out the base libraries since they will be provided by the LDSO
|
||||
# library and the startup library as the CRT0 part of program startup is
|
||||
# done by LDSO already. As replacement for the startup library startup_dyn
|
||||
# is used. The startup_dyn build is triggered by any shared library without
|
||||
# merging it to the library.
|
||||
#
|
||||
FILTER_DEPS := $(filter-out $(BASE_LIBS),$(DEPS:.lib=))
|
||||
FILTER_DEPS := $(filter-out $(BASE_LIBS) startup,$(DEPS:.lib=)) startup_dyn
|
||||
SHARED_LIBS += $(LIB_CACHE_DIR)/$(DYNAMIC_LINKER)/$(DYNAMIC_LINKER).lib.so
|
||||
|
||||
#
|
||||
|
||||
30
base/src/base/env/platform_env.h
vendored
30
base/src/base/env/platform_env.h
vendored
@@ -23,7 +23,7 @@
|
||||
/* Genode includes */
|
||||
#include <base/printf.h>
|
||||
#include <base/env.h>
|
||||
#include <base/heap.h>
|
||||
#include <base/heap.h>
|
||||
|
||||
/* local includes */
|
||||
#include <platform_env_common.h>
|
||||
@@ -36,7 +36,9 @@ namespace Genode {
|
||||
}
|
||||
|
||||
|
||||
struct Genode::Expanding_rm_session_client : Upgradeable_client<Genode::Rm_session_client>
|
||||
struct Genode::Expanding_rm_session_client
|
||||
:
|
||||
Upgradeable_client<Genode::Rm_session_client>
|
||||
{
|
||||
Expanding_rm_session_client(Rm_session_capability cap)
|
||||
: Upgradeable_client<Genode::Rm_session_client>(cap) { }
|
||||
@@ -139,7 +141,29 @@ class Genode::Platform_env : public Genode::Env, public Emergency_ram_reserve
|
||||
_emergency_ram_ds(_resources.ram.alloc(_emergency_ram_size()))
|
||||
{ }
|
||||
|
||||
void reload_parent_cap(Native_capability::Dst, long);
|
||||
/**
|
||||
* Reload parent capability and reinitialize environment resources
|
||||
*
|
||||
* This function is solely used for implementing fork semantics.
|
||||
* After forking a process, the new child process is executed
|
||||
* within a copy of the address space of the forking process.
|
||||
* Thereby, the new process inherits the original 'env' object of
|
||||
* the forking process, which is meaningless in the context of the
|
||||
* new process. By calling this function, the new process is able
|
||||
* to reinitialize its 'env' with meaningful capabilities obtained
|
||||
* via its updated parent capability.
|
||||
*/
|
||||
void reinit(Native_capability::Dst, long);
|
||||
|
||||
/**
|
||||
* Reinitialize main-thread object
|
||||
*
|
||||
* \param context_area_rm new RM session of the context area
|
||||
*
|
||||
* This function is solely used for implementing fork semantics
|
||||
* as provided by the Noux environment.
|
||||
*/
|
||||
void reinit_main_thread(Rm_session_capability &);
|
||||
|
||||
|
||||
/*************************************
|
||||
|
||||
55
base/src/base/env/reinitialize.cc
vendored
55
base/src/base/env/reinitialize.cc
vendored
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* \brief Environment reinitialization
|
||||
* \author Norman Feske
|
||||
* \author Martin Stein
|
||||
* \date 2012-02-16
|
||||
*/
|
||||
|
||||
@@ -11,12 +12,28 @@
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* env includes */
|
||||
#include <platform_env.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/construct_at.h>
|
||||
#include <base/crt0.h>
|
||||
#include <rm_session/connection.h>
|
||||
|
||||
void prepare_reinit_main_thread();
|
||||
|
||||
void reinit_main_thread();
|
||||
|
||||
namespace Genode
|
||||
{
|
||||
extern bool inhibit_tracing;
|
||||
|
||||
Rm_session * env_context_area_rm_session();
|
||||
}
|
||||
|
||||
|
||||
void Genode::Platform_env::reload_parent_cap(Native_capability::Dst dst,
|
||||
long local_name)
|
||||
void Genode::Platform_env::reinit(Native_capability::Dst dst,
|
||||
long local_name)
|
||||
{
|
||||
/*
|
||||
* This function is unused during the normal operation of Genode. It is
|
||||
@@ -32,21 +49,26 @@ void Genode::Platform_env::reload_parent_cap(Native_capability::Dst dst,
|
||||
* provided by the actual parent.
|
||||
*/
|
||||
|
||||
/* avoid RPCs by the tracing framework as long as we have no valid env */
|
||||
inhibit_tracing = true;
|
||||
|
||||
/* do platform specific preparation */
|
||||
prepare_reinit_main_thread();
|
||||
|
||||
/*
|
||||
* Patch new parent capability into the original location as specified by
|
||||
* the linker script.
|
||||
*/
|
||||
Native_capability::Raw *raw = (Native_capability::Raw *)(&_parent_cap);
|
||||
|
||||
raw->dst = dst;
|
||||
raw->local_name = local_name;
|
||||
raw->dst = dst;
|
||||
raw->local_name = local_name;
|
||||
|
||||
/*
|
||||
* Re-initialize 'Platform_env' members
|
||||
*/
|
||||
static_cast<Parent_client &>(_parent_client) = Parent_client(Genode::parent_cap());
|
||||
|
||||
_resources = Resources(_parent_client);
|
||||
Expanding_parent_client * const p = &_parent_client;
|
||||
construct_at<Expanding_parent_client>(p, parent_cap(), *this);
|
||||
construct_at<Resources>(&_resources, _parent_client);
|
||||
|
||||
/*
|
||||
* Keep information about dynamically allocated memory but use the new
|
||||
@@ -57,3 +79,20 @@ void Genode::Platform_env::reload_parent_cap(Native_capability::Dst dst,
|
||||
*/
|
||||
_heap.reassign_resources(&_resources.ram, &_resources.rm);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Genode::Platform_env::
|
||||
reinit_main_thread(Rm_session_capability & context_area_rm)
|
||||
{
|
||||
/* reinitialize context area RM session */
|
||||
Rm_session * const rms = env_context_area_rm_session();
|
||||
Rm_session_client * const rmc = dynamic_cast<Rm_session_client *>(rms);
|
||||
construct_at<Rm_session_client>(rmc, context_area_rm);
|
||||
|
||||
/* re-enable tracing */
|
||||
inhibit_tracing = false;
|
||||
|
||||
/* reinitialize main-thread object */
|
||||
::reinit_main_thread();
|
||||
}
|
||||
|
||||
@@ -51,24 +51,34 @@ addr_t Thread_base::Context_allocator::addr_to_base(void *addr)
|
||||
|
||||
size_t Thread_base::Context_allocator::base_to_idx(addr_t base)
|
||||
{
|
||||
return (base - Native_config::context_area_virtual_base()) /
|
||||
Native_config::context_virtual_size();
|
||||
/* the first context isn't managed through the indices */
|
||||
return ((base - Native_config::context_area_virtual_base()) /
|
||||
Native_config::context_virtual_size()) - 1;
|
||||
}
|
||||
|
||||
|
||||
addr_t Thread_base::Context_allocator::idx_to_base(size_t idx)
|
||||
{
|
||||
/* the first context isn't managed through the indices */
|
||||
return Native_config::context_area_virtual_base() +
|
||||
idx * Native_config::context_virtual_size();
|
||||
(idx + 1) * Native_config::context_virtual_size();
|
||||
}
|
||||
|
||||
|
||||
Thread_base::Context *Thread_base::Context_allocator::alloc(Thread_base *thread_base)
|
||||
Thread_base::Context *
|
||||
Thread_base::Context_allocator::alloc(Thread_base *thread_base, bool main_thread)
|
||||
{
|
||||
Lock::Guard _lock_guard(_threads_lock);
|
||||
|
||||
try {
|
||||
return base_to_context(idx_to_base(_alloc.alloc()));
|
||||
addr_t base;
|
||||
if (main_thread) {
|
||||
/* the main-thread context isn't managed by '_alloc' */
|
||||
base = Native_config::context_area_virtual_base();
|
||||
} else {
|
||||
/* contexts besides main-thread context are managed by '_alloc' */
|
||||
base = idx_to_base(_alloc.alloc());
|
||||
}
|
||||
return base_to_context(base);
|
||||
} catch(Bit_allocator<MAX_THREADS>::Out_of_indices) {
|
||||
return 0;
|
||||
}
|
||||
@@ -78,8 +88,13 @@ Thread_base::Context *Thread_base::Context_allocator::alloc(Thread_base *thread_
|
||||
void Thread_base::Context_allocator::free(Context *context)
|
||||
{
|
||||
Lock::Guard _lock_guard(_threads_lock);
|
||||
addr_t const base = addr_to_base(context);
|
||||
|
||||
_alloc.free(base_to_idx(addr_to_base(context)));
|
||||
/* the main-thread context isn't managed by '_alloc' */
|
||||
if (base == Native_config::context_area_virtual_base()) { return; }
|
||||
|
||||
/* contexts besides main-thread context are managed by '_alloc' */
|
||||
_alloc.free(base_to_idx(base));
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +109,8 @@ Thread_base::Context_allocator *Thread_base::_context_allocator()
|
||||
}
|
||||
|
||||
|
||||
Thread_base::Context *Thread_base::_alloc_context(size_t stack_size)
|
||||
Thread_base::Context *
|
||||
Thread_base::_alloc_context(size_t stack_size, bool main_thread)
|
||||
{
|
||||
/*
|
||||
* Synchronize context list when creating new threads from multiple threads
|
||||
@@ -105,7 +121,7 @@ Thread_base::Context *Thread_base::_alloc_context(size_t stack_size)
|
||||
Lock::Guard _lock_guard(alloc_lock);
|
||||
|
||||
/* allocate thread context */
|
||||
Context *context = _context_allocator()->alloc(this);
|
||||
Context *context = _context_allocator()->alloc(this, main_thread);
|
||||
if (!context)
|
||||
throw Context_alloc_failed();
|
||||
|
||||
@@ -213,7 +229,7 @@ void Thread_base::join()
|
||||
|
||||
void* Thread_base::alloc_secondary_stack(char const *name, size_t stack_size)
|
||||
{
|
||||
Context *context = _alloc_context(stack_size);
|
||||
Context *context = _alloc_context(stack_size, false);
|
||||
strncpy(context->name, name, sizeof(context->name));
|
||||
return (void *)context->stack_top();
|
||||
}
|
||||
@@ -226,13 +242,14 @@ void Thread_base::free_secondary_stack(void* stack_addr)
|
||||
}
|
||||
|
||||
|
||||
Thread_base::Thread_base(const char *name, size_t stack_size)
|
||||
Thread_base::Thread_base(const char *name, size_t stack_size, Type type)
|
||||
:
|
||||
_context(_alloc_context(stack_size)),
|
||||
_context(type == REINITIALIZED_MAIN ?
|
||||
_context : _alloc_context(stack_size, type == MAIN)),
|
||||
_join_lock(Lock::LOCKED)
|
||||
{
|
||||
strncpy(_context->name, name, sizeof(_context->name));
|
||||
_init_platform_thread();
|
||||
_init_platform_thread(type);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -36,9 +36,6 @@ void Thread_base::_thread_start()
|
||||
** Thread base **
|
||||
*****************/
|
||||
|
||||
void Thread_base::_init_platform_thread() { }
|
||||
|
||||
|
||||
void Thread_base::_deinit_platform_thread()
|
||||
{
|
||||
env()->cpu_session()->kill_thread(_thread_cap);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <base/env.h>
|
||||
#include <base/heap.h>
|
||||
#include <ram_session/client.h>
|
||||
#include <rm_session/capability.h>
|
||||
|
||||
/* core includes */
|
||||
#include <platform.h>
|
||||
@@ -174,7 +175,9 @@ namespace Genode {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void reload_parent_cap(Capability<Parent>::Dst, long) { }
|
||||
void reinit(Capability<Parent>::Dst, long) { }
|
||||
|
||||
void reinit_main_thread(Rm_session_capability &) { }
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -31,13 +31,8 @@
|
||||
using namespace Genode;
|
||||
|
||||
extern int main(int argc, char **argv, char **envp);
|
||||
extern void init_exception_handling(); /* implemented in base/cxx */
|
||||
|
||||
namespace Genode {
|
||||
Rm_session *env_context_area_rm_session();
|
||||
void platform_main_bootstrap();
|
||||
}
|
||||
|
||||
namespace Genode { Rm_session *env_context_area_rm_session(); }
|
||||
|
||||
enum { ATEXIT_SIZE = 256 };
|
||||
|
||||
@@ -46,6 +41,8 @@ enum { ATEXIT_SIZE = 256 };
|
||||
** C++ stuff **
|
||||
***************/
|
||||
|
||||
void * __dso_handle = 0;
|
||||
|
||||
enum Atexit_fn_type { ATEXIT_FN_EMPTY, ATEXIT_FN_STD, ATEXIT_FN_CXA };
|
||||
|
||||
struct atexit_fn
|
||||
@@ -229,11 +226,6 @@ namespace Genode { extern bool inhibit_tracing; }
|
||||
*/
|
||||
extern "C" int _main()
|
||||
{
|
||||
platform_main_bootstrap();
|
||||
|
||||
/* call env() explicitly to setup the environment */
|
||||
(void*)env();
|
||||
|
||||
/*
|
||||
* Allow exit handlers to be registered.
|
||||
*
|
||||
@@ -244,34 +236,6 @@ extern "C" int _main()
|
||||
*/
|
||||
atexit_enable();
|
||||
|
||||
/* initialize exception handling */
|
||||
init_exception_handling();
|
||||
|
||||
/*
|
||||
* We create the thread-context area as early as possible to prevent other
|
||||
* mappings from occupying the predefined virtual-memory region.
|
||||
*/
|
||||
env_context_area_rm_session();
|
||||
|
||||
/*
|
||||
* 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)());
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/**
|
||||
* \brief Startup code for Genode applications on ARM
|
||||
* \author Norman Feske
|
||||
* \author Martin Stein
|
||||
* \date 2007-04-28
|
||||
*/
|
||||
|
||||
@@ -11,34 +12,58 @@
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/*--- .text (program code) -------------------------*/
|
||||
|
||||
/**************************
|
||||
** .text (program code) **
|
||||
**************************/
|
||||
|
||||
.section ".text.crt0"
|
||||
|
||||
.globl _start
|
||||
_start:
|
||||
/* program entry-point */
|
||||
.global _start
|
||||
_start:
|
||||
|
||||
ldr r4, .initial_sp
|
||||
/* make initial value of some registers available to higher-level code */
|
||||
ldr r4, =__initial_sp
|
||||
str sp, [r4]
|
||||
|
||||
ldr sp, .stack_high
|
||||
b _main
|
||||
/*
|
||||
* Install initial temporary environment that is replaced later by the
|
||||
* environment that init_main_thread creates.
|
||||
*/
|
||||
ldr sp, =_stack_high
|
||||
|
||||
.initial_sp: .word __initial_sp
|
||||
.stack_high: .word _stack_high
|
||||
/* create proper environment for main thread */
|
||||
bl init_main_thread
|
||||
|
||||
.globl __dso_handle
|
||||
__dso_handle: .long 0
|
||||
/* apply environment that was created by init_main_thread */
|
||||
ldr sp, =init_main_thread_result
|
||||
ldr sp, [sp]
|
||||
|
||||
/* jump into init C code instead of calling it as it should never return */
|
||||
b _main
|
||||
|
||||
|
||||
/*********************************
|
||||
** .bss (non-initialized data) **
|
||||
*********************************/
|
||||
|
||||
/*--- .bss (non-initialized data) ------------------*/
|
||||
.section ".bss"
|
||||
|
||||
/* stack of the temporary initial environment */
|
||||
.p2align 4
|
||||
.globl _stack_low
|
||||
_stack_low:
|
||||
.space 128*1024
|
||||
.globl _stack_high
|
||||
_stack_high:
|
||||
.global _stack_low
|
||||
_stack_low:
|
||||
.space 128 * 1024
|
||||
.global _stack_high
|
||||
_stack_high:
|
||||
|
||||
/* initial value of the SP register */
|
||||
.globl __initial_sp
|
||||
__initial_sp: .space 4
|
||||
.global __initial_sp
|
||||
__initial_sp:
|
||||
.space 4
|
||||
|
||||
/* return value of init_main_thread */
|
||||
.global init_main_thread_result
|
||||
init_main_thread_result:
|
||||
.space 4
|
||||
|
||||
130
base/src/platform/init_main_thread.cc
Normal file
130
base/src/platform/init_main_thread.cc
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* \brief Setup the thread environment of a programs first thread
|
||||
* \author Christian Helmuth
|
||||
* \author Christian Prochaska
|
||||
* \author Martin Stein
|
||||
* \date 2013-12-04
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 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 <util/construct_at.h>
|
||||
#include <base/env.h>
|
||||
#include <base/printf.h>
|
||||
#include <base/thread.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
extern addr_t init_main_thread_result;
|
||||
|
||||
extern void init_exception_handling();
|
||||
|
||||
namespace Genode { Rm_session * env_context_area_rm_session(); }
|
||||
|
||||
void prepare_init_main_thread();
|
||||
|
||||
enum { MAIN_THREAD_STACK_SIZE = 16UL * 1024 * sizeof(Genode::addr_t) };
|
||||
|
||||
/**
|
||||
* The first thread in a program
|
||||
*/
|
||||
class Main_thread : public Thread<MAIN_THREAD_STACK_SIZE>
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param reinit wether this is called for reinitialization
|
||||
*/
|
||||
Main_thread(bool reinit)
|
||||
:
|
||||
Thread("main", reinit ? REINITIALIZED_MAIN : MAIN)
|
||||
{ }
|
||||
|
||||
/**********************
|
||||
** Thread interface **
|
||||
**********************/
|
||||
|
||||
void entry() { }
|
||||
};
|
||||
|
||||
|
||||
Main_thread * main_thread()
|
||||
{
|
||||
static Main_thread s(false);
|
||||
return &s;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a thread object for the main thread
|
||||
*
|
||||
* \return stack pointer of the new environment via init_main_thread_result
|
||||
*
|
||||
* This function must be called only once per program and before the _main
|
||||
* function. It can be called as soon as a temporary environment provides
|
||||
* some stack space and inter-process communication. At this stage, global
|
||||
* static objects are not registered for implicit destruction at program exit.
|
||||
*/
|
||||
extern "C" void init_main_thread()
|
||||
{
|
||||
/* do platform specific preparation */
|
||||
prepare_init_main_thread();
|
||||
|
||||
/*
|
||||
* Explicitly setup program environment at this point to ensure that its
|
||||
* destructor won't be registered for the atexit routine.
|
||||
*/
|
||||
(void*)env();
|
||||
|
||||
/* initialize exception handling */
|
||||
init_exception_handling();
|
||||
|
||||
/*
|
||||
* We create the thread-context area as early as possible to prevent other
|
||||
* mappings from occupying the predefined virtual-memory region.
|
||||
*/
|
||||
env_context_area_rm_session();
|
||||
|
||||
/*
|
||||
* 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 (...) { }
|
||||
|
||||
/* create a thread object for the main thread */
|
||||
main_thread();
|
||||
|
||||
/**
|
||||
* The new stack pointer enables the caller to switch from its current
|
||||
* environment to the those that the thread object provides.
|
||||
*/
|
||||
addr_t sp = reinterpret_cast<addr_t>(main_thread()->stack_top());
|
||||
init_main_thread_result = sp;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reinitialize main-thread object according to a reinitialized environment
|
||||
*/
|
||||
void reinit_main_thread() { construct_at<Main_thread>(main_thread(), true); }
|
||||
@@ -1,6 +1,7 @@
|
||||
/**
|
||||
* \brief Startup code for Genode applications
|
||||
* \author Christian Helmuth
|
||||
* \author Martin Stein
|
||||
* \date 2009-08-12
|
||||
*/
|
||||
|
||||
@@ -11,53 +12,79 @@
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/*--- .text (program code) -------------------------*/
|
||||
.text
|
||||
.global _start
|
||||
|
||||
_start:
|
||||
/**************************
|
||||
** .text (program code) **
|
||||
**************************/
|
||||
|
||||
.text
|
||||
|
||||
/* program entry-point */
|
||||
.global _start
|
||||
_start:
|
||||
|
||||
/* make initial value of some registers available to higher-level code */
|
||||
mov %esp, __initial_sp
|
||||
mov %eax, __initial_ax
|
||||
mov %edi, __initial_di
|
||||
|
||||
/* XXX Switch to our own stack. */
|
||||
/*
|
||||
* Install initial temporary environment that is replaced later by the
|
||||
* environment that init_main_thread creates.
|
||||
*/
|
||||
leal _stack_high, %esp
|
||||
|
||||
/* Clear the base pointer so that stack backtraces will work. */
|
||||
/* create proper environment for the main thread */
|
||||
call init_main_thread
|
||||
|
||||
/* apply environment that was created by init_main_thread */
|
||||
movl init_main_thread_result, %esp
|
||||
|
||||
/* clear the base pointer in order that stack backtraces will work */
|
||||
xor %ebp,%ebp
|
||||
|
||||
/* Jump into init C code */
|
||||
call _main
|
||||
/* jump into init C code instead of calling it as it should never return */
|
||||
jmp _main
|
||||
|
||||
/* We should never get here since _main does not return */
|
||||
1: int $3
|
||||
jmp 2f
|
||||
.ascii "_main() returned."
|
||||
2: jmp 1b
|
||||
|
||||
.globl __dso_handle
|
||||
__dso_handle: .long 0
|
||||
/**********************************
|
||||
** .eh_frame (exception frames) **
|
||||
**********************************/
|
||||
|
||||
/*--- .eh_frame (exception frames) -----------------*/
|
||||
/*
|
||||
.section .eh_frame,"aw"
|
||||
.section .eh_frame,"aw"
|
||||
|
||||
.global __EH_FRAME_BEGIN__
|
||||
__EH_FRAME_BEGIN__:
|
||||
__EH_FRAME_BEGIN__:
|
||||
*/
|
||||
|
||||
/*--- .bss (non-initialized data) ------------------*/
|
||||
.bss
|
||||
|
||||
/*********************************
|
||||
** .bss (non-initialized data) **
|
||||
*********************************/
|
||||
|
||||
.bss
|
||||
|
||||
/* stack of the temporary initial environment */
|
||||
.p2align 4
|
||||
.global _stack_low
|
||||
_stack_low:
|
||||
.space 64*1024
|
||||
_stack_low:
|
||||
.space 64 * 1024
|
||||
.global _stack_high
|
||||
_stack_high:
|
||||
_stack_high:
|
||||
|
||||
/* initial value of the ESP, EAX and EDI register */
|
||||
.globl __initial_sp
|
||||
.globl __initial_ax
|
||||
.globl __initial_di
|
||||
__initial_sp: .space 4
|
||||
__initial_ax: .space 4
|
||||
__initial_di: .space 4
|
||||
.global __initial_sp
|
||||
__initial_sp:
|
||||
.space 4
|
||||
.global __initial_ax
|
||||
__initial_ax:
|
||||
.space 4
|
||||
.global __initial_di
|
||||
__initial_di:
|
||||
.space 4
|
||||
|
||||
/* return value of init_main_thread */
|
||||
.global init_main_thread_result
|
||||
init_main_thread_result:
|
||||
.space 4
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/**
|
||||
* \brief Startup code for Genode 64Bit applications
|
||||
* \author Sebastian Sumpf
|
||||
* \author Martin Stein
|
||||
* \date 2011-05-11
|
||||
*/
|
||||
|
||||
@@ -11,60 +12,90 @@
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/*--- .text (program code) -------------------------*/
|
||||
.text
|
||||
|
||||
/**************************
|
||||
** .text (program code) **
|
||||
**************************/
|
||||
|
||||
.text
|
||||
|
||||
/* program entry-point */
|
||||
.global _start
|
||||
_start:
|
||||
|
||||
_start:
|
||||
|
||||
/* make initial value of some registers available to higher-level code */
|
||||
movq __initial_ax@GOTPCREL(%rip), %rbx
|
||||
movq %rax, (%rbx)
|
||||
|
||||
movq __initial_di@GOTPCREL(%rip), %rbx
|
||||
movq %rdi, (%rbx)
|
||||
|
||||
movq __initial_sp@GOTPCREL(%rip), %rax
|
||||
movq %rsp, (%rax)
|
||||
|
||||
/* XXX Switch to our own stack. */
|
||||
/*
|
||||
* Install initial temporary environment that is replaced later by the
|
||||
* environment that init_main_thread creates.
|
||||
*/
|
||||
leaq _stack_high@GOTPCREL(%rip),%rax
|
||||
movq (%rax), %rsp
|
||||
|
||||
/* Clear the base pointer so that stack backtraces will work. */
|
||||
xorq %rbp,%rbp
|
||||
/* create proper environment for the main thread */
|
||||
call init_main_thread
|
||||
|
||||
/* Jump into init C code */
|
||||
call _main
|
||||
/* apply environment that was created by init_main_thread */
|
||||
movq init_main_thread_result@GOTPCREL(%rip), %rax
|
||||
movq (%rax), %rsp
|
||||
|
||||
/* We should never get here since _main does not return */
|
||||
1: int $3
|
||||
jmp 2f
|
||||
.ascii "_main() returned."
|
||||
2: jmp 1b
|
||||
/* clear the base pointer in order that stack backtraces will work */
|
||||
xorq %rbp, %rbp
|
||||
|
||||
.globl __dso_handle
|
||||
__dso_handle: .quad 0
|
||||
/*
|
||||
* We jump into initial C code instead of calling it as it should never
|
||||
* return on the one hand and because the alignment of the stack pointer
|
||||
* that init_main_thread returned expects a jump at the other hand. The
|
||||
* latter matters because GCC expects the initial stack pointer to be
|
||||
* aligned to 16 byte for at least the handling of floating points.
|
||||
*/
|
||||
jmp _main
|
||||
|
||||
|
||||
/**********************************
|
||||
** .eh_frame (exception frames) **
|
||||
**********************************/
|
||||
|
||||
/*--- .eh_frame (exception frames) -----------------*/
|
||||
/*
|
||||
.section .eh_frame,"aw"
|
||||
.section .eh_frame,"aw"
|
||||
|
||||
.global __EH_FRAME_BEGIN__
|
||||
__EH_FRAME_BEGIN__:
|
||||
__EH_FRAME_BEGIN__:
|
||||
*/
|
||||
|
||||
/*--- .bss (non-initialized data) ------------------*/
|
||||
.bss
|
||||
|
||||
/*********************************
|
||||
** .bss (non-initialized data) **
|
||||
*********************************/
|
||||
|
||||
.bss
|
||||
|
||||
/* stack of the temporary initial environment */
|
||||
.p2align 8
|
||||
.global _stack_low
|
||||
_stack_low:
|
||||
.space 64*1024
|
||||
_stack_low:
|
||||
.space 64 * 1024
|
||||
.global _stack_high
|
||||
_stack_high:
|
||||
_stack_high:
|
||||
|
||||
/* initial value of the RSP, RAX and RDI register */
|
||||
.globl __initial_sp
|
||||
__initial_sp:
|
||||
.space 8
|
||||
.globl __initial_ax
|
||||
__initial_ax:
|
||||
.space 8
|
||||
.globl __initial_di
|
||||
__initial_sp: .space 8
|
||||
__initial_ax: .space 8
|
||||
__initial_di: .space 8
|
||||
__initial_di:
|
||||
.space 8
|
||||
|
||||
/* return value of init_main_thread */
|
||||
.globl init_main_thread_result
|
||||
init_main_thread_result:
|
||||
.space 8
|
||||
|
||||
Reference in New Issue
Block a user