Learn how easy it is to sync an existing GitHub or Google Code repo to a SourceForge project! See Demo

Close

#357 Destructor ordering fix

64bit_readiness
closed
nobody
None
2013-02-13
2008-03-07
Joseph Myers
No

GCC trunk uses atexit to call C++ static object destructors on MinGW:
each constructor (registered in .ctors) will call atexit to register
the corresponding destructor, so that they are called in reverse order
of construction (and interleaved with any atexit functions registered
by the user's constructor code) as required by the C++ standard.

If GCC trunk is configured with --disable-sjlj-exceptions, so that
DWARF-2 exception handling is used, a function calling
__register_frame_info will be registered in .ctors and one calling
__deregister_frame_info will be registered in .dtors. After
__deregister_frame_info is called, exception handling will no longer
work. Since exceptions may be raised and caught from within static
object destructors, this means that __deregister_frame_info must be
called after functions registered with atexit from within
constructors. Thus __do_global_ctors needs to call atexit to register
__do_global_dtors before, rather than after, calling the constructors.

This patch to gccmain.c fixes the libstdc++ test failures

FAIL: 27_io/objects/char/6.cc execution test
FAIL: 27_io/objects/wchar_t/6.cc execution test

for GCC trunk configured with --disable-sjlj-exceptions, with no
testsuite regressions.

Discussion

  • Joseph Myers
    Joseph Myers
    2008-03-07

     
    Attachments
  • Danny Smith
    Danny Smith
    2008-03-07

    Logged In: YES
    user_id=11494
    Originator: NO

    The code in mingw's gccmain.c was copied (many years ago) from gcc/libgcc2.c #ifdef L__main block.

    The use of ctors/dtor in cygming_crtstuff.c to handle __[de]|register_frame_info was used to allow cygwin amd mingw to share the same code (cygwin's __do_global_[c|d}tors is cygwin1.dll and change that would cause cygwin-version compat issues)

    I think the right patch for mingw (ignoring cygin) would be to would be be to provide a mingw-specific __do_global_[c|d}tors so that we could explicitily control EH init/fini ordering regardless of ctor priorities

    like the one _now_ in libgcc2.c."L__main" block

    void
    __do_global_dtors (void)
    {
    #ifdef DO_GLOBAL_DTORS_BODY
    DO_GLOBAL_DTORS_BODY;
    #else
    static func_ptr *p = __DTOR_LIST__ + 1;
    while (*p)
    {
    p++;
    (*(p-1)) ();
    }
    #endif
    #if defined (EH_FRAME_SECTION_NAME)
    {
    static int completed = 0;
    if (! completed)
    {
    completed = 1;
    __deregister_frame_info (__EH_FRAME_BEGIN__);
    }
    }
    #endif
    }

    /* Run all the global constructors on entry to the program. */

    void
    __do_global_ctors (void)
    {
    #ifdef EH_FRAME_SECTION_NAME
    {
    static struct object object;
    __register_frame_info (__EH_FRAME_BEGIN__, &object);
    }
    #endif
    DO_GLOBAL_CTORS_BODY;
    atexit (__do_global_dtors);
    }

     
  • Joseph Myers
    Joseph Myers
    2008-03-07

    Logged In: YES
    user_id=2022679
    Originator: YES

    Note that libgcc's copy of these functions is not used (despite the comment in gccmain.c suggesting it should be) because of the position of -lgcc in LIBGCC_SPEC. If you put -lgcc at the start of the spec, then you find the libgcc copies of these functions (with the same atexit ordering as the gccmain.c copies) have duplicate calls to __register_frame_info and __deregister_frame_info, so you need to do something like you suggest to stop them being duplicates.

     
  • Danny Smith
    Danny Smith
    2008-03-13

    Logged In: YES
    user_id=11494
    Originator: NO

    This is the main concern I have about your patch

    Consider

    // dtors.C
    #include <stdio.h>
    class X
    {
    private:
    int m_i;
    public:
    X(int i): m_i(i){}
    ~X(){printf ("in ~X()\n");} // compiler registers this directly
    // with atexit during construction
    };

    X x(1);

    static void
    my_dtor () __attribute__((destructor)); // registered with atexit
    // via __do_global_[c|d]tors

    static void
    my_dtor ()
    { printf ("in my_dtor\n");}

    int main(){}

    Currently, functions that are registered with atexit directly by a ctor
    are run _after_ all functions in .dtor sections (which, as you explained,
    is why the libstdc++ tests fail), and we get

    in my_dtor
    in ~X()

    With your patch, the order is changed so that we get

    in ~X()
    in my_dtor

    which fixes the specific use of .dtors to handle EH frame cleanup and
    AFAICT is the only way to fix the bug for gcc 4.3.0

    Is this generic change of ordering justified by that?
    Or should we just change cygming-crtend to use direct atexit registration
    of frame cleanup from the frame registration ctor, and leave the fix till 4.3.1.

    Danny

     
  • Danny Smith
    Danny Smith
    2008-04-25

    Logged In: YES
    user_id=11494
    Originator: NO

    This is fixed on gcc trunk by:
    http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=134656
    I will backport to 4.3 branch in 10 days if no problems on trunk.

    Danny

     
  • Earnie Boyd
    Earnie Boyd
    2013-02-13

    • status: open --> closed
    • milestone: --> 64bit_readiness