[rtems-users] C++ Virtual Functions
Till Straumann
strauman at slac.stanford.edu
Thu Feb 3 20:33:50 UTC 2005
Here's my recollection of things [powerpc] -- sorry for the lengthy
explanation...
1) 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) !!
2) RTEMS calls '_init()' which among other things works through the
C++ static constructor list -- provided that your linker stript
and bsp_specs files are correct. (See Appendix 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 that 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 [such as e.g., 'malloc()'].
==> a typical chicken + egg. problem
1) we want to call _eabi() early [libbsp/powerpc/shared/start/start.S]
2) we want to prevent __init() from being called too early (by _eabi())
3) we want to call __init() at the apropriate time.
4) we dont want to hack gcc.
Here's the solution [uses '.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:
o BSP's 'start.S' file must call '_eabi()'
o 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 _do_global_ctors_aux() [provided by crtend.o]
- _do_global_ctors_aux() initializes C++ environment [exceptions, ctors]
- ...
HTH
-- Till
APPENDIX on '.init' linker magic
--------------------------------
Note that '_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.
==> your gcc configuration, linkcmds and bsp_specs must harmonize!
Phil Torre wrote:
> On Thu, Feb 03, 2005 at 09:07:44AM -0500, Smith, Gene wrote:
>
>>I went back a read everything I could find on this list regarding this
>>and several people have reported this problem. The only one who reported
>>a resolution did like you and wrote their own initializer (Phil Torre).
>>However there was a lot of discussion about "eabi" that got into the
>>ctor init and pointed out that it occurs in a call to _init() in
>>_Thread_Handler when __USE_INIT_FINI__ is defined. The _init() call
>>occurs on my system as described which in turn calls etext and
>>eventually __do_global_ctors_aux__ but I never see any constructors
>>actually called before _init() returns. I assume it is supposed to call
>>the constructors for any globally or statically defined c++ objects
>>somewhere inside the _init() call? Also, are these c++ objects supposed
>>to be in the .bss section?
>>-gene
>
>
> Someone subsequently pointed out to me that this was the wrong thing
> to do, so these days I have added crtbegin.o and crtend.o to our
> bsp_specs file, like so:
>
> *startfile:
> %{!qrtems: %(old_startfile)} %{!nostdlib: %{qrtems: ecrti%O%s crtbegin.o%s \
> %{!qrtems_debug: start.o%s} \
> %{qrtems_debug: start_g.o%s}}}
>
> *endfile:
> %{!qrtems: %(old_endfile)} %{qrtems: crtend.o%s ecrtn%O%s}
>
> (I'm guity of cargo-cult programming here, as I don't actually understand
> why this works. It does the trick, though, and without requiring me to
> explicitly call my own initializer function.)
>
> -Phil
More information about the users
mailing list