2

I am working on a project which includes startup code prior to the call to main. I am however unaware of the std library initializations.

I know that the following code will throw a segmentation fault.

#include <iostream>

void foo(void) __attribute__((constructor));
void foo() {
  std::cout << "foo" << std::endl;
}

int main() {
  std::cout << "Simple program to throw a segmentation fault" << std::endl;
}

The above code can be made to work by force initializing the ostream buffer (not sure if it's exactly ostream) by using std::ios_base::Init mInitializer;. This would mean that the std library was not completely initialized at this point (that is my inference of the above example).

So when can I use std functions without actually breaking the code? Is there a way to force initialize complete std library?

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Rohit
  • 175
  • 11
  • 3
    As a rule of thumb, as soon as `main()` is on the call stack. – user207421 Jun 05 '18 at 10:36
  • 1
    If you want to be portable you can't. Order of initialization between [translation units](https://en.wikipedia.org/wiki/Translation_unit_(programming)) is undefined. The only sure way to know that everything in the standard library works is to be in the `main` function. – Some programmer dude Jun 05 '18 at 10:36
  • @EJP: I think it's earlier. static-initialization code can call standard library code IIANM, specifically it can `std::cout << "stuff"`; see the static block in the answer to [this question](https://stackoverflow.com/questions/19227664/whats-the-c-idiom-equivalent-to-the-java-static-block). – einpoklum Jun 05 '18 at 10:48
  • I think `__attribute__((constructor))` comes in pretty early, which might explain your problem. – Paul Sanders Jun 05 '18 at 10:54
  • @Someprogrammerdude That should be irrelevant for though, due to the static [`std::ios_base_Init`](http://en.cppreference.com/w/cpp/io/ios_base/Init)-object. Looks like there's a bug... – Deduplicator Jun 05 '18 at 11:29
  • A dynamic initialization like `constexpr int dummy_call = (foo(), 0);` after `#include ` would be safe since `` has its own dynamic initializer. But apparently as a compiler-specific thing, `__attribute__((constructor))` causes something to happen even earlier? I'm not aware of any standard libraries that require initialization other than `std::ios_base::Init()`... – aschepler Jun 05 '18 at 11:56
  • @PaulSanders I just took it as an example to show that the std library was not completely initialized by then. I'm unaware as to when it gets completely initialized. – Rohit Jun 05 '18 at 15:27
  • @Rohit OK, well, I think that by using `__attribute__((constructor))` you undermined your test. Try something similar without it. My best guess is that it will then work. – Paul Sanders Jun 05 '18 at 16:46
  • 1
    You've got a funky attribute that, apparently, messes with initialization order. You need to read the documentation for your compiler to know what that thing does. This is not standard C++. In standard C++ the runtime library has been initialized before your code gets control. – Pete Becker Jun 05 '18 at 17:03
  • @PeteBecker that is what I wish to know. At what point is it completely initialized? Is there a function that does the initialization part? – Rohit Jun 05 '18 at 21:25
  • 1
    @Rohit -- again: in standard C++ the runtime library has been initialized **before** your code gets control. This isn't something you have to worry about unless you're doing something funky with compiler-specific stuff that you should read about and understand before using. – Pete Becker Jun 05 '18 at 22:10

1 Answers1

2

The documentation for the constructor attribute says this:

However, at present, the order in which constructors for C++ objects with static storage duration and functions decorated with attribute constructor are invoked is unspecified.

This means that ELF constructors and static objects (as the one used for initialization of std::cout) do not mix well.

On the other hand, std::cout is a historical exception because it does not rely on a constructor in the C++ standard library. On ELF systems, ELF constructors run in topological order. This means that the dynamic linker looks at the library dependencies (the DT_NEEDED entries) and delays initialization of libraries before their dependencies are initialized.

As a result, C++ code can assume that the C++ run-time library is fully initialized when ELF constructors run and global objects defined by the application are constructed. The only exception is a C++ library which preempts routines used by the standard C++ standard library itself (via ELF symbol interposition), and if there are cyclic dependencies among the libraries, so that there is no correct initialization order. Both cases are not common, and unless you write a custom malloc or something like that, can be avoid with proper application design.

Florian Weimer
  • 32,022
  • 3
  • 48
  • 92