Question

In C++, I know that the compiler can choose to initialize static objects in any order that it chooses (subject to a few constraints), and that in general you cannot choose or determine the static initialization order.

However, once a program has been compiled, the compiler has to have made a decision about what order to initialize these objects in. Is there any way to determine, from a compiled program with debugging symbols, in what order static constructors will be called?

The context is this: I have a sizeable program that is suddenly segfaulting before main() when it is built under a new toolchain. Either this is a static initialization order problem, or it is something wrong with one of the libraries that it is loading. However, when I debug with gdb, the crash location is simply reported as a raw address without any symbolic information or backtrace. I would like to decide which of these two problems it is by placing a breakpoint at the constructor of the very first statically-initialized object, but I don't know how to tell which object that is.

Was it helpful?

Solution

Matthew Wilson provides a way to answer this question in this section (Safari Books Online subscription required) of Imperfect C++. (Good book, by the way.) To summarize, he creates a CUTrace.h header that creates a static instance of a class that prints the filename of the including source file (using the nonstandard preprocessor macro __BASE_FILE__) when created, then he includes CUTrace.h in every source file.

This requires a recompilation, but the #include "CUTrace.h" can easily be added and removed via a script, so it shouldn't be too hard to set up.

OTHER TIPS

In G++ on Linux, static constructor and destructor ordering is determined by function pointers in the .ctors and .dtors sections. Note that with sufficient debugging available, you can actually get a backtrace:

(gdb) bt
#0  0xb7fe3402 in __kernel_vsyscall ()
#1  0xb7d59680 in *__GI_raise (sig=6)
    at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
#2  0xb7d5cd68 in *__GI_abort () at abort.c:88
#3  0x08048477 in foo::foo() ()
#4  0x0804844e in __static_initialization_and_destruction_0(int, int) ()
#5  0x0804846a in global constructors keyed to foo_inst ()
#6  0x0804850d in __do_global_ctors_aux ()
#7  0x08048318 in _init ()
#8  0x080484a9 in __libc_csu_init ()
#9  0xb7d4470c in __libc_start_main (main=0x8048414 <main>, argc=1,
    ubp_av=0xbfffcbc4, init=0x8048490 <__libc_csu_init>,
    fini=0x8048480 <__libc_csu_fini>, rtld_fini=0xb7ff2820 <_dl_fini>,
    stack_end=0xbfffcbbc) at libc-start.c:181
#10 0x08048381 in _start () at ../sysdeps/i386/elf/start.S:119

This is with debugging symbols for libc and libstdc++ installed. As you can see, the crash here occured in the foo::foo() constructor for the static object foo_inst.

If you want to break into the initialization process, you could then set a breakpoint on __do_global_ctors_aux and step through its disassembly, I suppose. Or just wait for it to crash to get the backtrace like the above.

Could you initialize dummy variables in the static space, and put break points on those function calls?

extern "C" int breakOnMe () { return 0 };

int break1 = breakOnMe ();
float pi = 3.1415;
int break2 = breakOnMe ();
myClass x = myClass (1, 2, 3);

Then in gdb run break breakOnMe before you execute the program. That should make gdb pause before each on the static initializations.

I think that should work .. I'm a little rusty on gdbbing.

You can find the order the TUs are being initialized using templates as highlighted by this question. It requires a small bit of code change to each of the TUs that you're interested in:

// order.h
//

#ifndef INCLUDED_ORDER
#define INCLUDED_ORDER

#include <iostream>

inline int showCountAndFile (const char * file)
{
  static int cnt = 0;
  std::cout << file << ": " << cnt << std::endl;
  ++cnt;
  return cnt;
}

template <int & i>
class A {
  static int j;
};

template <int & i>
int A<i>::j = showCountAndFile (SRC_FILE);

namespace
{
  int dummyGlobal;
}
template class A<dummyGlobal>;

#endif

The basic idea is that each TU will have a different unique address for dummyGlobal and so the template will have a different instantiation in each TU. The initialization of the static member results in the call to "showCountAndFile" which then prints out SRC_FILE (set in the TU) and the current value of cnt which will therefore show the order.

You'd use it as follows:

static const char * SRC_FILE=__FILE__;
#include "order.h"

int main ()
{
}

g++ provides some help with this.
Its not portable but I am sure at this point that is not your main problem.

http://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Attributes.html#C_002b_002b-Attributes

Actually, through the use of Singletons you can control the order of initialisation of global/static objects quite effectively in C++.

For example, say you have:

class Abc
{
public:
    void foo();
};

and a corresponding object defined at global scope:

Abc abc;

Then you have a class:

class Def
{
public:
    Def()
    {
        abc.foo();
    }
};

which also has an object defined at global scope:

Def def;

In this situation, you don't have control of the initialisation order and if def is initialized first, then it's likely your program will crash because it is calling the foo() method on an Abc which has not yet been initialised.

The solution is to have a function at global scope do something like this:

Abc& abc()
{
    static Abc a;
    return a;
}

and then Def would look something like:

class Def
{
public:
    Def()
    {
        abc().foo();
    }
};

This way, abc is always guaranteed to be initialised before it is used because this will happen during the first call of the abc() function. Likewise, you should do the same with Def global object to make it wont have any unexpected initialisation dependencies either.

Def& def()
{
    static Def d;
    return d;
}

If you need to strictly control the order of initialisation in addition to simply making sure everything is initialised before it is used, put all global objects in a Global singleton as follows.

struct Global
{
    Abc abc;
    Def def;
};

Global& global()
{
    static Global g;
    return g;
}

And make references to these items as follows:

//..some code
global().abc.foo();
//..more code here
global().def.bar();

Regardless of which one gets calls first, C++ member initialisation rules will guarantee that the abc and def objects are initialised in the order they are defined in the Global class.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top