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

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

Virtual functions now work. I did exactly what Joel said to do using the psim example and Till's write-up explains the theory behind it.
(please see a few comments/questions below)

Till Straumann wrote, On 2/3/2005 3:33 PM:

Here's my recollection of things [powerpc] -- sorry for the lengthy

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()'

bl __eabi
/shared/start/start.S does this right before zeroing .bss so I will too in my startup assembly file.

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

I manually copied rtems_crti.o from where I built it in powerpc/mybsp/start/ to be with the other "installed" files like crtbegin.o under /opt/rtems-4.6. If not copied, the examples fail to link during rtems build since bsp_specs calls out this file. Is there a right way to handle this. (Possibly BSP(s) that use powerpc/shared/start/ have this figured out.)

              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]
    - ...


-- 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 you need a separate section called .init or, as most BSPs seem to do, put them in the .text section? (I made a separate section.)

Also, do you need to define the _init sysbol in linkcmds, e.g.,
PROVIDE (_init = .);
(I did not define this symbol.)

Do you need a .fini section in linkcmds and define a _fini symbol?
(I included this section and defined the symbol per psim example.)

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_else();  /* from my_object.o */

      } /* from ecrtn.o */

    Hence, the rtems_crti.S file does the following:

      /* from ecrti.o: */
      /* from rtems_crti.o */

      /* '.init' sections from other objects */

      /* from crtend.o: */

      /* from ecrtn.o:  */

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


    into the '.init' section of 'crtend.o' -- hence if you don't link
    against 'crtbegin/crtend' [bsp_specs] your constructors are not

==> your gcc configuration, linkcmds and bsp_specs must harmonize!

What is the "correct" way to make your .ctors and .dtors sections in linkcmds. There seems to be several schools of thought in the various BSPs. I.e, do you need a length value in the section and a terminating zero and/or just a symbol defining the .[cd]tors start, i.e. __CTOR_LIST__ ?
(I did not did not provide length and zero termination, again like psim example.)

Finally, what about dtors? Should they work too? I have not tried them since shutting down is not a big priority right now.

Thanks to everyone for the excellent help!