#357 Destructor ordering fix

64bit_readiness
closed
nobody
None
2013-02-13
2008-03-07
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
     
  • 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

     
  • Earnie Boyd

    Earnie Boyd - 2013-02-13
    • status: open --> closed
    • milestone: --> 64bit_readiness
     

Get latest updates about Open Source Projects, Conferences and News.

Sign up for the SourceForge newsletter:





No, thanks