wiki:TBR/UserManual/Using_C_Plus_Plus

Version 10 (modified by Rsg, on 06/26/07 at 21:23:49) (diff)

/* C++ .init Linker Magic */

Using C Plus Plus

Using C++

RTEMS provides support for C++ applications. Applications should be compiled with an RTEMS specific tool sets that provides RTEMS thread support. RTEMS tool sets are build with the <cpu>-rtems target. The thread model of the compiler builds into the GCC libraries support for interacting with RTEMS to insure the thread safe operation of your application.

Size and Performance

It is common to see comments made about the size and speed of C++ applications. Using C++ does not effect the performance of RTEMS or the performance of accessing RTEMS services from C++. A C++ appication may result in a larger executable size as linking to the standard C++ library can pull in parts of the library, or using RTTI can result in extra data being including in the executable.

The GNU compiler for RTEMS is built using the RTEMS thread model. This means it is best you use an RTEMS built tool chain when using C++. The thread model in GCC lets applications operate in a thread safe manner. The compiler is built with low level calls to RTEMS mutex type resources it uses when performing thread safe operations.

More to come here.

Exceptions

A great description of the C++ Exception design in GCC including section and startup information is at -

  http://gcc.gnu.org/ml/gcc/2002-07/msg00352.html

Linking

C++ programs need to link to the standard C++ library (<tt>libstd++.c</tt>) and the C++ front-end (g++) is required to perform the linking task. Do not add -lstd++ to the command line of gcc, ld or a specs file.

The rationale is quite simple: Linking c++ is more than "just adding a library". G++ reflects this thought and treats libstdc++ as an internal implementation detail users do not need to know about or touch.

On some targets/with some compilers "linking c++" is effectively "just adding -lstdc++" but on most others it is more. What confuses some users is "gcc ... -lstdc++" once having been worked with old gcc (IIRC, gcc < 4.0) on i386 targets.

If you use the RTEMS Makefile Template ($TARGET/make/Templates/Makefile.leaf), this is the easy way to link properly (example from the Template):

# The following links using C++ rules to get the C++ libraries.
# Be sure you BSP has a make-cxx-exe rule if you use this.
${ARCH}/xxx-your-program-here: ${OBJS} ${LINK_FILES}
	$(make-cxx-exe)

Global Object Construction and Destruction

The C++ language allows the declaration of objects at a global level. A global object like every other type of object needs to be constructed how-ever it is not your code that defines when and how, as it does with heap or locally allocated objects. Global objects are constructed by the runtime supporting your application, RTEMS. Global objects that are not constructed may or may not work depending on the object. An object that uses virtual functions will not work and can result in bus type errors as the object's vtable pointer will not have been initialised.

Global object construction requires the compiler and RTEMS work together. This is a constant source of maintenance issues as changes in one can effect the other. The compiler generates special code to construct and destruct each global object. The constructor and destructor pointer's to this code is placed in a special section, usually called .ctor and .dtor. The linker will pull all the pointers into a single section giving you a table of pointers to constructors and destructors. The order or placement of a specific pointer in these tables cannot be easily set and any application that relies on a specific order should be considered broken and needs to be fixed.

RTEMS calls the constructors after the kernel is running, but before first Init task runs. This means global objects can allocate memory from the heap, and create objects such as threads and semaphores. These constructors run in the context of the Init task before its entry point is invoked. See _Thread_Handler() for details.

Different processors can require different initialisation sequences. An example is the PowerPC and its support for the SYSV/EABI compliant environment.

PowerPC Initialisation

BSP should call (gcc provided) _eabi() very early to set up a SYSV/EABI compliant environment (load r2/r13, stack align etc.). If you don't do this, e.g., SYSV/EABI short data areas won't work (see gcc -msdata -meabi options).

RTEMS calls init() which among other things works through the C++ static constructor list -- provided that your linker script and bsp_specs files are correct. See C++ .init Linker Magic on how it works.

  • However, there is a problem here, in that eabi() ends up calling init() (actually, init(), we renamed to fix the problem described here) when it is still too early to initialize the C++ environment.

Note both details are of the 'seems to work' type. You won't notice anything if you don't call eabi() until you try to use an essential SYSV/EABI feature. Likewise, calling init() too early might not cause problems in many cases until one of your constructors uses an yet unavailable feature provided by RTEMS such as malloc().

For the PowerPC a typical chicken and egg problem exists -

#we want to call _eabi() early (libbsp/powerpc/shared/start/start.S) #we want to prevent _init() from being called too early (by eabi()) #we want to call init() at the apropriate time. #we dont want to hack gcc.

Here's the solution using the .init magic as described in the appendix -

  • an additional startup file rtems_crti.S terminates _init()
  • so it becomes a no-op and introduces a new _init() entry point
  • to be used by RTEMS (ThreadHandler?).

Hence here's what you need :

  • BSP's 'start.S' file must call '_eabi()'
  • BSP's bsp_specs :

startfile: must contain (order is crucial) :

ecrti%O%s /* prologue of init() */ rtems_crti%0%s /* epilogue of init(), prologue of _init() */

/* now _init() does everything init() usually

  • does */

crtbegin.o%s /* crucial stuff, e.g. C++ exceptions, dtors */

endfile: must contain (order is essential) -

crtend.o%s /* crucial stuff, e.g., C++ exceptions, ctors */ ecrtn.o%s /* _init() epilogue() */

Here's what happens (properly linked executable) :

#BSP start.s calls _eabi(); SYSVI/EABI environment setup #eabi() calls _init() #init() returns immediately #BSP initializes #RTEMS starts up; initializes newlibc #ThreadHandler? calls init() (points to what _init() was intended to do) #_init() walks through code snippets provided by various .init sections #init() encounters doglobalctors_aux() (provided by crtend.o) #doglobalctorsaux() initializes C++ environment (exceptions, ctors) #...

C++ .init Linker Magic

#InitMagic?

  • Note, _init() is not an ordinary function but 'compiler/linker magic' which uses the special .init section. An object file's .init* section must be composed of small snippets of code like

do_something (); do_something_else ();

that should eventually go into the _init() routine. Any object may contain such code. The linker finally gathers all these snippets (in the order the objects are linked together) and that's where the ecrti.o/ecrtn.o files come into play. These two files bracket the .init snippets with a proper function prologue (from ecrti.o) and epilogue (ecrtn.o), i.e., if you link (ecrti/ecrtn implicitely provided by gcc specs) :

ecrti.o my_object.o ecrtn.o

you end up with something like ('translated into C'):

_init () { /* from ecrti.o */

do_something(); do_something_else(); /* from my_object.o */

} /* from ecrtn.o */

Hence, the rtems_crti.S file does the following :

/* from ecrti.o: */ init() { /* from rtems_crti.o */ }

_init() { /* '.init' sections from other objects */

/* from crtend.o: */ do_global_ctors_aux();

/* from ecrtn.o: */ }

GCC uses this feature to call C++ static constructors by sticking a call to :

do_global_ctors_aux()

into the .init section of crtend.o hence if you don't link against crtbegin/crtend (bsp_specs) your constructors are not called.

<big><strong>Your gcc configuration, linkcmds and bsp_specs must harmonize</strong></big>