Bug 81337 - g++.dg/gcov/pr16855.C etc. FAIL
Summary: g++.dg/gcov/pr16855.C etc. FAIL
Status: RESOLVED DUPLICATE of bug 52477
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 8.0
: P3 normal
Target Milestone: ---
Assignee: Martin Liška
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2017-07-06 08:31 UTC by Rainer Orth
Modified: 2017-08-10 19:12 UTC (History)
5 users (show)

See Also:
Host:
Target: *-*-solaris2.12, *-*-darwin*, *-*-freebsd*
Build:
Known to work:
Known to fail:
Last reconfirmed: 2017-08-10 00:00:00


Attachments
reduced testcase (177 bytes, text/plain)
2017-07-06 08:31 UTC, Rainer Orth
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Rainer Orth 2017-07-06 08:31:42 UTC
Created attachment 41693 [details]
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
Comment 1 Martin Liška 2017-08-10 09:13:35 UTC
Thanks Rainer for the report, I've composed email to GCC and glibc folks who should hopefully help us.
Comment 2 Jason Merrill 2017-08-10 18:57:28 UTC
Bug 52477 deals with the parallel issue for construction.  As I mentioned there, I think the right solution is to handle attribute constructor/destructor using the C++ __static_initialization_and_destruction mechanism rather than .{init,fini}_array.
Comment 3 Martin Liška 2017-08-10 19:12:49 UTC
Thanks Jason, then marking as duplicate.

*** This bug has been marked as a duplicate of bug 52477 ***