3

Can someone help understanding the following seg fautl, and also why it does work on changing the link order:

common.h file:

#include <set>
struct T {
    explicit T(const char*) {
        Instances.insert(this);
    }
    static std::set<T*> Instances;
};

d.cc file:

#include "common.h"
T d(__FILE__);

main.cc file:

#include "common.h"
#include <set>
#include <iostream>

/*static*/ std::set<T*> T::Instances;
int main() {
   std::cout << "T::Instances.size() = " << T::Instances.size() << std::endl;
   while(true);
}

Building and running with following commands:

g++ -c -g -Wall -Wextra d.cc -o d.o
g++ -c -g -Wall -Wextra main.cc -o main.o
g++  -g  d.o main.o -o app

./app

Running the commands I get a seg fault, with the following backtrace:

$ ./app
Segmentation fault (core dumped)
$ gdb ./app
(gdb) run
Starting program: /home/meodou/zdev/poc_at_7405/testi/app 
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7afebca in std::local_Rb_tree_decrement (__x=0x6031e8 <T::Instances+8>) at ../../../../../libstdc++-v3/src/c++98/tree.cc:98
98          && __x->_M_parent->_M_parent == __x)
(gdb) bt
#0  0x00007ffff7afebca in std::local_Rb_tree_decrement (__x=0x6031e8 <T::Instances+8>) at ../../../../../libstdc++-v3/src/c++98/tree.cc:98
#1  0x0000000000401365 in std::_Rb_tree_iterator<T*>::operator-- (this=0x7fffffffdc50) at /usr/include/c++/5.3.1/bits/stl_tree.h:220
#2  0x000000000040102f in std::_Rb_tree<T*, T*, std::_Identity<T*>, std::less<T*>, std::allocator<T*> >::_M_get_insert_unique_pos (
this=0x6031e0 <T::Instances>, __k=@0x7fffffffde08: 0x6031d1 <d>) at /usr/include/c++/5.3.1/bits/stl_tree.h:1819
#3  0x0000000000400dfe in std::_Rb_tree<T*, T*, std::_Identity<T*>, std::less<T*>, std::allocator<T*> >::_M_insert_unique (this=0x6031e0 <T::Instances>, 
__v=@0x7fffffffde08: 0x6031d1 <d>) at /usr/include/c++/5.3.1/bits/stl_tree.h:1863
#4  0x0000000000400d79 in std::set<T*, std::less<T*>, std::allocator<T*> >::insert (this=0x6031e0 <T::Instances>, __x=@0x7fffffffde08: 0x6031d1 <d>)
at /usr/include/c++/5.3.1/bits/stl_set.h:485
#5  0x0000000000400d47 in T::T (this=0x6031d1 <d>) at common.hh:6
#6  0x0000000000400ce0 in __static_initialization_and_destruction_0 (__initialize_p=1, __priority=65535) at d.cc:2
#7  0x0000000000400d0a in _GLOBAL__sub_I_d () at d.cc:2
#8  0x0000000000401b2d in __libc_csu_init ()
#9  0x00007ffff71a050f in __libc_start_main (main=0x40173b <main()>, argc=1, argv=0x7fffffffdf68, init=0x401ae0 <__libc_csu_init>, fini=<optimized out>, 
rtld_fini=<optimized out>, stack_end=0x7fffffffdf58) at libc-start.c:245
#10 0x0000000000400bc9 in _start ()

So that something I m trying to understand. The second thing is that if I invert the linking order:

From  g++  -g  d.o main.o -o app
To  g++  -g  main.o d.o -o app

The program runs without seg fault.

$ g++ -c -g -Wall -Wextra d.cc -o d.o   
$ g++ -c -g -Wall -Wextra main.cc -o main.o
$ g++  -g  main.o d.o -o app
$ ./app
T::Instances.size() = 1

Any explanation what this is working? The problem seems to be related to global variables initialization but I still cannot see what is happening.

Using g++ (GCC) 5.3.1

BR,

...........................

In answer to Quentin comment: Yes I know that is should be related to static initialization but still I dont see really how this is happening. (I am not in the case of https://isocpp.org/wiki/faq/ctors#static-init-order where a compile unit is calling an object not initialized as it s found in another compile unit, at least not directly).

I can also add a T object the main.cc file:

#include <iostream>
/*static*/ std::set<T*> T::Instances;
+T main_obj(__FILE__);
int main() {
std::cout << "T::Instances.size() = " << T::Instances.size() << std::endl;

I ll still get a crash, here my understanding is that we are calling a static function on an object that we already have in current compile unit (should be correctly initialized). So we are not depending at least directly on the object found in d.cc file. Again I strongly believe that is related to static initialization but I dont clearly see the explanation of it. (The object that we are calling its static member should be already initialized)

meodou
  • 49
  • 4
  • 3
    You've been bitten by the [Static Initialization Order Fiasco](https://isocpp.org/wiki/faq/ctors#static-init-order). – Quentin May 02 '16 at 11:27

1 Answers1

0

You should define static std::set<T*> Instances; in d.cc:

#include "common.h"
#include <set>
std::set<T*> T::Instances;
T d(__FILE__);

And remove it from main.cc.

Hatted Rooster
  • 35,759
  • 6
  • 62
  • 122
  • It looks like OP wants to instantiate a `T` in each of several `cc` files -- your solution will only work for one. – Quentin May 02 '16 at 11:33
  • @quentin That's true, I only took into account the current scenario. – Hatted Rooster May 02 '16 at 11:33
  • Yes... I want to have it in several files. The change you proposed makes the problem to disappear but I am lacking understanding what is happening here, could you please give more info? – meodou May 02 '16 at 13:31
  • @Meodou It makes sure that there's a definition of `Instances` before `main()` uses it. – Hatted Rooster May 02 '16 at 13:39
  • That is really the reason I tried putting it in main.cc, I am missing something here. – meodou May 02 '16 at 13:56