[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [rtems-users] C++ Virtual Functions



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