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:
Martin Stein
2014-01-28 14:30:36 +01:00
committed by Norman Feske
parent ba8e61653f
commit 0b64328944
80 changed files with 1299 additions and 823 deletions

View File

@@ -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();

View File

@@ -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) { }
};
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
#

View File

@@ -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 &);
/*************************************

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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 &) { }
};

View File

@@ -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)());

View File

@@ -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

View 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); }

View File

@@ -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

View File

@@ -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