This section documents CS3's model for target initialization
prior to invoking the main
function of
your program, and aspects of program termination that are
left unspecified in the C and C++ standards. It explains how
you can customize or override the default behavior for your
application.
CS3 divides the startup sequence into three phases:
The hard reset phase
(__cs3_reset
)
includes actions such as initializing the memory controller and
setting up the memory map.
The assembly initialization phase
(__cs3_start_asm
)
prepares the stack to run C code,
and jumps to the C initialization function.
The C initialization phase
(__cs3_start_c
)
is responsible for initializing the data areas,
running constructors for statically-allocated objects,
and calling main
.
The hard reset and assembly initialization phases are necessarily written in assembly language; at reset, there may not yet be stack to hold compiler temporaries, or perhaps even any RAM accessible to hold the stack. These phases do the minimum necessary to prepare the environment for running simple C code. Then, the code for the final phase may be written in C; CS3 leaves as much as possible to be done at this point.
The CodeSourcery board support library provides default code for all three phases. The hard reset phase is implemented by board- and profile-specific code. The assembly initialization phase is implemented by profile-specific code. The C initialization phase is implemented by generic code.
This phase, which begins at __cs3_reset
,
is responsible for initializing board-specific
registers, such as memory base registers and DRAM controllers,
or scanning memory to check the available size. It is written
in assembler and ends with a jump to
__cs3_start_asm
, which is where the assembly
initialization phase begins.
The hard reset code is in a section named
.cs3.reset
. CS3 linker scripts define
__cs3_reset
as an alias for a board- and
profile-specific entry point. You may override the
CS3-provided reset code by defining your own
__cs3_reset
entry point in the
.cs3.reset
section.
Program execution always begins at __cs3_reset
,
whether the program is started from the reset vector, the debugger, or
a boot monitor.
However, the __cs3_reset
code linked into the
application is typically non-empty only for ROM-based profiles.
For example, in a RAM-based profile, resetting
the memory controllers would overwrite the code being executed.
This phase is responsible for initializing the stack pointer
and creating an initial stack frame.
The symbol __cs3_start_asm
marks the entry point of the
assembly initialization code.
The assembly initialization phase ends with a call or jump to
__cs3_start_c
.
The assembly initialization phase is profile-specific. For example, while bare-board applications typically must initialize the stack themselves, CS3 also supports boot-monitor profiles where the stack is initialized by the boot monitor before it launches the application. Likewise, some simulators automatically initialize the stack pointer and initial stack frame on startup, while others require a supervisory operation on startup to determine the amount of available memory. Each of these scenarios requires different assembly initialization behavior.
Note that on bare-board targets setting the stack pointer explicitly in the assembly initialization phase is required even if the processor itself initializes the stack pointer automatically on reset. This is to support running programs from the debugger as well as from processor reset.
For backwards compatibility with previous versions of CS3,
on RAM and ROM profiles the symbol
__cs3_start_asm
is actually an alias
for a symbol named _start
.
However, referencing or defining _start
directly is now deprecated.
The value of the symbol __cs3_stack
provides the initial value of the stack pointer for
profiles that must set it explicitly.
The CodeSourcery linker scripts provide a default value for this
symbol, which you may override by defining
__cs3_stack
yourself.
See Section 5.3.3, “Heap and Stack Placement” for an example of a custom
stack.
The initial stack frame is created for the use of ordinary C and C++ calling conventions. The stack should be initialized so that backtraces stop cleanly at this point; this might entail zeroing a dynamic link pointer, or providing hand-written DWARF call frame information.
The last action of the assembly initialization phase is to
call the C function
__cs3_start_c
. This function never
returns, and __cs3_start_asm
need not be prepared to handle a return from it.
As with the hard reset code, the CodeSourcery board
support library provides reasonable default assembly
initialization code. However, you may provide
your own code by providing a definition
for __cs3_start_asm
, either in an object
file or a library.
Finally, C code can be executed. The C startup function is declared as follows:
void __cs3_start_c (void) __attribute__ ((noreturn));
This function performs the following steps:
Initialize all .data
-like sections by
copying their contents. For example, ROM-profile linker
scripts use this mechanism to initialize writable data in RAM
from the read-only data program image.
Clear all .bss
-like sections.
Run constructors for statically-allocated objects, recorded using whatever conventions are usual for C++ on the target architecture.
CS3 reserves priorities from 0 to 100 for use by initialization code. You can handle tasks like enabling interrupts, initializing coprocessors, pointing control registers at interrupt vectors, and so on by defining constructors with appropriate priorities.
Call main
as appropriate.
Call exit
, if it is available.
As with the hard reset and assembly initialization code, the
CodeSourcery board support library provides a reasonable
definition for the __cs3_start_c
function. You may override this by providing
a definition for __cs3_start_c
,
either in an object file or in a library.
The CodeSourcery-provided definition of
__cs3_start_c
can pass command-line arguments
to main
using the normal C
argc
and argv
mechanism
if the board support package provides corresponding definitions for
__cs3_argc
and __cs3_argv
.
For example:
int __cs3_argc; char **__cs3_argv;
These variables should be initialized using a constructor function,
which is run by __cs3_start_c
after it
initializes the data segment. Use the constructor
attribute on the function definition:
__attribute__((constructor)) void __cs3_init_args (void) { __cs3_argc = ...; __cs3_argv = ...; }
The constructor function must be named
__cs3_init_args
, since some
profiles provide a default definition of this function.
If definitions of __cs3_argc
and
__cs3_argv
are not provided, then the default
__cs3_start_c
function invokes
main
with zero as the argc
argument and a null pointer as argv
.
A program running on an embedded system is usually designed
never to exit — it runs until the system is powered down.
The C and C++ standards leave it unspecified
as to whether exit
is called at program
termination. If the program never exits, then there is no
reason to include exit
, facilities to run
functions registered with atexit
, or global
destructors. This code would never be run and would therefore
just waste space in the application.
The CS3 startup code, by itself, does not cause
exit
to be present in the application. It
dynamically checks whether exit
is present,
and only calls it if it is. If you require
exit
to be present, either refer to it
within your application, or add -Wl,-u,exit
to
the linking command line.
Similarly, code to register global destructors is only invoked
when atexit
is already in the executable;
CS3, by itself, does not cause atexit
to be
present. If you require atexit
, either
refer to it within your application, or add
-Wl,-u,atexit
to the linking command line.