Segmentation Fault before Main

There is a great article about the learning curve of C++ stating that there are two types of people who claim to know C++: The first type has knowledge of C and superficial knowledge of C++'s classes and considers themselves an expert in C++ because there is nothing more to it. The second type has gone through the valley of frustrations and knows most of the quirks of the language.

I looked at the points on the curve and checked whether I knew what they are about. I got stuck at the "static object initialization segfaults". So far I have tried very hard to not use global variables in my code, so I did not experience something like this first hand. Since I wanted to know how to do this, I have just tried it out.

Let's start with this innocent program:

#include <iostream>

int main(int argc, char **argv) {
    std::cout << "Hello, World!" << std::endl;
}

You compile it, run it, it just works:

$ g++ -Wall -Wpedantic --std=c++11 main.cpp -o main
$ ./main 
Hello, World!

I have some other code in other.cpp that I do not use from the main.cpp. The main function does not reference anything else. So I should be able to compile that as well, right? It will not do any damage, right? So let's try that out:

$ g++ -Wall -Wpedantic --std=c++11 main.cpp other.cpp -o main
$ ./main
Segmentation fault (core dumped)

So what just happened? We have added more stuff to our program but we do not use it. This should not make any difference whatsoever. If you link to a library that you do not use, nothing should change. Still the program crashes and that even before the main function. What is going on here?

That is probably exactly the point that is meant on the curve. You just link another file into the program and now it crashes before the main function.

Here is the code that I have in this other file:

#include <iostream>

struct Foo {
    Foo() {
#ifdef PRINT
        std::cout << "Foo::Foo()" << std::endl;
#endif
        int *p = nullptr;
        *p = 1;
    }

    ~Foo() {
#ifdef PRINT
        std::cout << "Foo::~Foo()" << std::endl;
#endif
    }
};

Foo foo;

It creates a static (file global) variable of the class type Foo. When the program is starting up, the constructor of that class is run. And there I just create a segmentation fault by writing something into the null pointer. This will then let the whole program crash before reaching main.

Defining this PRINT constant will show you that the constructor is indeed called before main:

$ g++ -Wall -Wpedantic --std=c++11 -DPRINT main.cpp other.cpp -o main
$ ./main
Foo::Foo()
Segmentation fault (core dumped)

That's how you get "static object initialization segfaults". At least this one is rather easy to find with GDB:

$ g++ -Wall -Wpedantic --std=c++11 -g -DPRINT main.cpp other.cpp -o main
$ gdb ./main
GNU gdb (GDB) Fedora 7.12.1-48.fc25
Copyright (C) 2017 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./main...done.
(gdb) r
Starting program: /home/mu/TODO/C++/segfault/main 
Foo::Foo()

Program received signal SIGSEGV, Segmentation fault.
0x00000000004009e0 in Foo::Foo (this=0x601172 <foo>) at other.cpp:9
9               *p = 1;
(gdb) bt
#0  0x00000000004009e0 in Foo::Foo (this=0x601172 <foo>) at other.cpp:9
#1  0x0000000000400980 in __static_initialization_and_destruction_0 (__initialize_p=1, __priority=65535)
    at other.cpp:19
#2  0x00000000004009aa in _GLOBAL__sub_I_foo () at other.cpp:19
#3  0x0000000000400a6d in __libc_csu_init ()
#4  0x00007ffff7189390 in __libc_start_main (main=0x4008b6 <main(int, char**)>, argc=1, argv=0x7fffffffe4d8, 
    init=0x400a20 <__libc_csu_init>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe4c8)
    at ../csu/libc-start.c:245
#5  0x00000000004007ea in _start ()

Still I'd rather not have these thing in my program.