https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81337
Bug ID: 81337 Summary: g++.dg/gcov/pr16855.C etc. FAIL Product: gcc Version: 8.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: ro at gcc dot gnu.org CC: jason at gcc dot gnu.org, marxin at gcc dot gnu.org, nathan at gcc dot gnu.org Target Milestone: --- Target: *-*-solaris2.12, *-*-darwin*, *-*-freebsd* Created attachment 41693 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=41693&action=edit reduced testcase g++.dg/gcov/pr16855.C and it's companion g++.dg/gcov/pr16855-priority.C for targets with constructor priority support have been FAILing on Solaris 12 only (it does work on Solaris 10 and 11) since they were first added: FAIL: g++.dg/gcov/pr16855-priority.C -std=gnu++11 gcov: 1 failures in line counts, 0 in branch percentages, 0 in return percentages, 0 in intermediate format FAIL: g++.dg/gcov/pr16855-priority.C -std=gnu++11 line 22: is #####:should be 1 FAIL: g++.dg/gcov/pr16855-priority.C -std=gnu++14 gcov: 1 failures in line counts, 0 in branch percentages, 0 in return percentages, 0 in intermediate format FAIL: g++.dg/gcov/pr16855-priority.C -std=gnu++14 line 22: is #####:should be 1 FAIL: g++.dg/gcov/pr16855-priority.C -std=gnu++98 gcov: 1 failures in line counts, 0 in branch percentages, 0 in return percentages, 0 in intermediate format FAIL: g++.dg/gcov/pr16855-priority.C -std=gnu++98 line 22: is #####:should be 1 FAIL: g++.dg/gcov/pr16855.C -std=gnu++11 gcov: 1 failures in line counts, 0 in branch percentages, 0 in return percentages, 0 in intermediate format FAIL: g++.dg/gcov/pr16855.C -std=gnu++11 line 21: is #####:should be 1 FAIL: g++.dg/gcov/pr16855.C -std=gnu++14 gcov: 1 failures in line counts, 0 in branch percentages, 0 in return percentages, 0 in intermediate format FAIL: g++.dg/gcov/pr16855.C -std=gnu++14 line 21: is #####:should be 1 FAIL: g++.dg/gcov/pr16855.C -std=gnu++98 gcov: 1 failures in line counts, 0 in branch percentages, 0 in return percentages, 0 in intermediate format FAIL: g++.dg/gcov/pr16855.C -std=gnu++98 line 21: is #####:should be 1 I've finally managed to investigate what's going on and the result may point to an issue with destructor handling beyond just this testcase. I've managed to produce a reduced testcase that shows the underlying issue: $ g++ -c prio2.C $ gcc -o prio2 prio2.o -lsupc++ (I'm linking with gcc and libsupc++ to avoid having to deal with the libstdc++ destructors during investigation.) On Solaris 12, the testcase produces In dtor In Test::~Test while on Linux (and on Solaris 11 which lacks __cxa_atexit or Solaris 12 with -fno-use-cxa-atexit) you get In Test::~Test In dtor On Solaris 12 with -fuse-cxa-atexit (the default): * Test::~Test() is registered via .init_array -> _GLOBAL__sub_I_T1 -> __static_initialization_and_destruction_0(1, 65535) -> __cxa_atexit(Test::~Test(), &T1, &__dso_handle); * dtor() is called from .fini_array Init/Fini Array Section: .init_array index value derived-address name [0] 0x80552e0 0x80552e0 frame_dummy [1] 0x8055348 0x8055348 _GLOBAL__sub_I_T1 [2] 0x8056b60 0x8056b60 _GLOBAL__sub_I_eh_alloc.cc Init/Fini Array Section: .fini_array index value derived-address name [0] 0x80552b0 0x80552b0 __do_global_dtors_aux [1] 0x80552f7 0x80552f7 _ZL4dtorv [dtor()] During execution, here are the relevant atexit and __cxa_atexit calls: __cxa_atexit(_ZN4TestD2Ev) #0 0xfe1bd348 in __cxa_atexit () from /lib/libc.so.1 #1 0x08055342 in __static_initialization_and_destruction_0(int, int) () #2 0x0805535d in _GLOBAL__sub_I_T1 () #3 0xfe6b046b in call_array () from /usr/lib/ld.so.1 #4 0xfe6b0636 in call_init () from /usr/lib/ld.so.1 atexit(atexit_fini) #0 0xfe1bd327 in atexit () from /lib/libc.so.1 #1 0x080551a3 in __start_crt () #2 0x0805514c in _start () Here's the order and call stack of both destructors: dtor() called: #0 0x080552fd in dtor() () #1 0xfe6b046b in call_array () from /usr/lib/ld.so.1 #2 0xfe6b082e in call_fini () from /usr/lib/ld.so.1 #3 0xfe6b0a32 in atexit_fini () from /usr/lib/ld.so.1 #4 0xfe1bd4e5 in _exithandle () from /lib/libc.so.1 #5 0xfe1aefa2 in exit () from /lib/libc.so.1 #6 0x08055171 in _start () Test::~Test() called: #0 0x08055368 in Test::~Test() () #1 0xfe1bd4dd in _exithandle () from /lib/libc.so.1 #2 0xfe1aefa2 in exit () from /lib/libc.so.1 #3 0x08055171 in _start () So we have this registration order: __cxa_atexit(Test::~Test()) atexit(atexit_fini) ... and as expected the reverse call order: atexit_fini dtor() Test::~Test() * On Solaris 11 or Solaris 12 with -fno-use-cxa-atexit: ** Test::~Test() is called like this: .fini_array -> _GLOBAL__sub_D_T1 -> __static_initialization_and_destruction_0(0, 65535) -> Test::~Test(&T1) ** dtor() is called from .fini_array Init/Fini Array Section: .fini_array index value derived-address name [0] 0x8055290 0x8055290 __do_global_dtors_aux 3. [1] 0x80552d7 0x80552d7 _ZL4dtorv [dtor()] 2. [2] 0x8055338 0x8055338 _GLOBAL__sub_D_T1 1. So both destructors are called via the same mechanism and their relative order is as expected by the testcase. * On Linux with -fuse-cxa-atexit (the default): Code is practically identical to Solaris Init/Fini Array Section: .init_array index value derived-address name [0] 0x8048dd0 0x8048dd0 frame_dummy [1] 0x8048e32 0x8048e32 _GLOBAL__sub_I_T1 [2] 0x8048c70 0x8048c70 _GLOBAL__sub_I_eh_alloc.cc Init/Fini Array Section: .fini_array index value derived-address name [0] 0x8048da0 0x8048da0 __do_global_dtors_aux [1] 0x8048de0 0x8048de0 _ZL4dtorv During execution, I find those __cxa_atexit calls: __cxa_atexit(_dl_fini) #0 0xf7deacf0 in __cxa_atexit () from /lib/libc.so.6 #1 0xf7dd111e in __libc_start_main () from /lib/libc.so.6 #2 0x08048d05 in _start () __cxa_atexit(_ZN4TestD2Ev) #0 0xf7deacf0 in __cxa_atexit () from /lib/libc.so.6 #1 0x08048e2c in __static_initialization_and_destruction_0(int, int) () #2 0x08048e47 in _GLOBAL__sub_I_T1 () #3 0x08054c7b in __libc_csu_init () #4 0xf7dd1156 in __libc_start_main () from /lib/libc.so.6 #5 0x08048d05 in _start () _dl_fini from glibc elf/dl-fini.c resulting in the execution order expected by the testcase: Test::~Test() called: #0 0x0000000000401074 in Test::~Test() () #1 0x00007ffff7834410 in __run_exit_handlers () from /lib64/libc.so.6 #2 0x00007ffff783446a in exit () from /lib64/libc.so.6 #3 0x00007ffff781a408 in __libc_start_main () from /lib64/libc.so.6 #4 0x0000000000400f4a in _start () dtor() called: #0 0x0000000000401006 in dtor() () #1 0x00007ffff7de818a in _dl_fini () from /lib64/ld-linux-x86-64.so.2 #2 0x00007ffff7834410 in __run_exit_handlers () from /lib64/libc.so.6 #3 0x00007ffff783446a in exit () from /lib64/libc.so.6 #4 0x00007ffff781a408 in __libc_start_main () from /lib64/libc.so.6 #5 0x0000000000400f4a in _start () Initially, I thought the Solaris 12 implementation of __cxa_atexit had gotten this wrong, but on second thought that's not so clear anymore. I saw that the original testcases FAIL not only on Solaris 12, but also on Darwin and FreeBSD, also both targets using __cxa_atexit. I could verify that my reduced testcase executes on Darwin the same way as on Solaris 12, but don't have access to a FreeBSD system to check what's going on there. Now I believe there are three possible causes here: 1. The testcases make unwarranted assumptions about the relative execution order of exit handlers from C++ destructors vs. destructors with __attribute__((destructor)), so just the testcases need to be fixed. 2. 3 out of 4 __cxa_atexit implementations either missed the requirement for relative ordering of exit handlers registered with __cxa_atexit vs. .fini_array entires or got it wrong, with only glibc doing the right thing. 3. The relative ordering in both the original and reduced testcases *is* required, but g++ makes unwarranted assumptions about the two different mechanisms it uses to have them called. In this case, g++ would have to be changed to e.g. use .fini_array entries for both destructors and __attribute__((destructor)) functions. Rainer