views:

602

answers:

5

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.

+2  A: 

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.

eduffy
Is it guaranteed that non-object static variables will always be initialized before static objects?
Tyler McHenry
In Linux, POD types will be initialized by placing them in the data section - these will be loaded before any user code is executed.
bdonlan
But you can't break on the data section, so the question is if a POD type is initialized by means of a call to a function (as with break1 and break2 in eduffy's example), is that guaranteed to happen before constructors of objects get called?
Tyler McHenry
@Tyler, no, they're initialized just as constructor calls are. In my limited testing they seem to be initialized in order of declaration, but this may not be guarenteed.
bdonlan
@bdonlan: Within a compilation unit. This order is guranteed.
Martin York
But that is of course the whole problem - I have many compilation units.
Tyler McHenry
+8  A: 

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.

bdonlan
On *some* platforms this answer is correct. On other platforms it is wrong. You probably shouldn't assume that "the whole world is made of ELF binaries".
Employed Russian
@Russian, updated with an "on Linux" disclaimer :)
bdonlan
+3  A: 

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.

Josh Kelley
Very clever idea. I'll try this later today, and probably accept this answer if it works.
Tyler McHenry
While this didn't exactly answer my question per se, it did solve the problem that inspired the question. Using this method I was able to see that the problem occurred before any static initialization in my app, and by modifying the libraries it used to do the same thing, I was able to determine which library had the problem. It was then much easier to find the static initialization bug since that library did much less static initialization than my app.
Tyler McHenry
A: 

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

Martin York
A: 

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 ()
{
}
Richard Corden