c++constructorg++function-attributes

gcc linker extension __attribute__((constructor)) causes crash in main()


First I've a singleton.cpp file to build a singleton object, and declare instance using attribute((constructor))

#include<iostream>
using namespace std;
class singleton{
public:
    singleton(){cout<<"singleton ctor\n";}
};
__attribute__((constructor)) static void beforeFunction()
{
    printf("beforeFunction\n");
    singleton obj;
}

And a simple main.cpp

#include<iostream>
using namespace std;
int main(){
    return 0;
}

I build main.cpp singleton.cpp together:

g++ singleton.cpp main.cpp -o main

./main
beforeFunction
Segmentation fault

So why my program crashes, what happened? How to fix it? I'm using gcc on ubuntu. Thanks a lot.


Solution

  • I reproduced this with g++ (Debian 7.3.0-5), and also g++ (GCC) 9.0.0 20180902 (experimental).

    It's interesting that this fails:

    $ g++ singleton.cpp main.cpp && ./a.out
    beforeFunction
    Segmentation fault
    

    but this works as expected:

    $ g++ main.cpp singleton.cpp && ./a.out
    beforeFunction
    singleton ctor
    

    As Acorn correctly stated, the iostream / std::cout machinery has not been properly initialized by the time you called singleton constructor. This is happening because there is special code emitted into main.o (and only main.o) which calls std::ios_base::Init::Init(). And only because main.cpp has extraneous #include <iostream>.

    How to fix it?

    The best fix is to not use __attribute__((constructor)) at all. In your case, there is no reason to do what you are doing. Do this instead:

    // singleton2.cpp
    #include<iostream>
    using namespace std;
    
    class singleton{
      public:
        singleton(){cout<<"singleton ctor\n";}
    };
    
    static singleton obj;
    

    With above code, either order of linking works:

    $ g++ main.cpp singleton2.cpp && ./a.out
    singleton ctor
    
    $ g++ singleton2.cpp main.cpp && ./a.out
    singleton ctor
    

    If you insist on using __attribute__((constructor)), then make sure main.o is on your link line before any other object that could use iostreams.