7

How to create an instance of a particular class earlier than main(), earlier than any other instance (including static and global variables, including in the static and dynamic libraries (including libraries provided by 3rd parties))?


I'm dealing with a project with a number of classes that during construction can cause an error, e.g. access a NULL pointer. Any such error causes a signal to be sent to the app. I have a signal handler that catches the signals, shows the stack trace of the offending thread, and invokes the default signal handler which results in the core dump generated, etc.
However some of such error-causing instances are created as the global variables and static variables of the classes. I.e. they are constructed and cause a signal earlier than main() is entered.

To catch such signals I need to register my signal handler earlier than in main(), i.e. I need to create an instance (that will register the signal handler) also as a global or class-static variable, and I need to guarantee that such an instance is created/constructed earlier than any other instance.

How to get that done?


To register a signal handler I use sigaction().
To show the stack trace I use backtrace(), backtrace_symbols(), abi::__cxa_demangle().

Pablo
  • 13,271
  • 4
  • 39
  • 59
Robin Kuzmin
  • 742
  • 4
  • 12

6 Answers6

3

Standard C++ doesn't provide a way to order initializers between translation units, but gcc does. Example from https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Attributes.html:

Some_Class A __attribute__((init_priority (2000)));

The lowest value (highest priority) is 101.

Todd Fleming
  • 216
  • 2
  • 4
  • I haven't worked it with myself, but perhaps sections in Win32 PE format could also be a solution. See https://learn.microsoft.com/en-us/cpp/preprocessor/init-seg – gast128 Feb 08 '18 at 09:05
  • @Todd Fleming, I used `__attribute__ ((init_priority (101)))` and looks like it works for the executable only, the globals/statics in the dynamic/shared library are still initialized earlier than `__attribute__ ((init_priority (101)))`. So your solution partially solves the issue. – Robin Kuzmin Feb 13 '18 at 17:26
  • @RobinKuzmin that's unfortunate. I guess gcc's assumption is that executables depend on libraries but not the other way around. – Todd Fleming Feb 14 '18 at 17:57
2

Order of calling static constructors depends on their order in a source file, order of linking and order of loading shared libraries. The usual way of doing what you need is to create a separate file with your registration function, and to make sure that the corresponding object appears as the very first one at the linker command line. It should not be a part of any shared or static library.

i.e. first.c

static int first_function() {
    // register your signals
    ...
    return 0;
}
static int status = first_function();


gcc -c first.c -o first.o
gcc -o myexec first.o ... all other files
Serge
  • 11,616
  • 3
  • 18
  • 28
1

You can create a small project or executable outside of your main application that is compiled & built separately within its own solution. However this small application would have a required command line argument that is needed for it to work with your main application that you'd like to test against. The command line argument would be the path to where your main project's folder path or directory is located that will contain a header file that works with this external app. Then depending on which IDE you are using you would have to do some configurations of your IDE to set up the correct paths.

For example if you are working in MS Visual Studio. You can right click on your startup project and select properties, then on the properties page on the left side there is a section named Build Events then if you expand this section it gives you three choices: Pre-Build Event, Pre-Link Event & Post-Build Event all three of these have the same fields on the right hand side for their settings: Command Line where you would have to set your paths, Description anything you want to use to describe the build event command, and Use In Build which is basically a Boolean toggle of Y/N.

Now other IDEs will be different in how they are configured and the options available but something of this nature might fit your needs. Forgive me for the example above that uses Microsoft Visual Studio as I'm not familiar with Linux. However I do believe that something similar can be done; it's just a matter of knowing your compiler, linker & debugger.

Francis Cugler
  • 7,788
  • 2
  • 28
  • 59
1

There may be a solition by using static fuction variables.

class A;

A & GetA()
{
    static A internal_a;
    return internal_a;
}

...

class B
{
    public:
    B()
    {
        auto &a = GetA();
        a.DoSomething();
    }
}

...

B global_b;

...

Whenever the constructor of B is called, By calling 'GetA' the static function variable 'internal_a' is initialised before it's further used.

See also What is the lifetime of a static variable in a C++ function?

This technique is also described in the book "Effective C++" from Scott Meyers.

Tunichtgut
  • 301
  • 1
  • 6
1

(Providing a temporary answer to my own question)
Based on

the final answer seems to be

  • to create a global instance in a .CPP file,
  • to add __attribute__ ((init_priority (101))) to that instance,
  • to list that file first in the linker list for a dynamic/shared library,
  • build a dynamic/shared library,
  • load that library first of all other dynamic/shared libraries.

This solution has not been checked in full (hopefully some time later I will check).


If you decide to vote for this answer then instead (or at least in addition to) better vote for the other answers I mentioned above.

Robin Kuzmin
  • 742
  • 4
  • 12
0

It's not possible to do this unfortunately, unless you have access to the source (which is ruled out since you have 3rd party libraries).

If you do something like this:

// Foo.h
inline fooGlobal() { static Foo f; return f; }
static auto& foo = fooGlobal();

Then you can guarantee that any file which includes Foo.h, will see foo initialized before any globals in that file. If your third party libraries only define globals in header files (using a trick similar to the one above to prevent getting multiple globals), you can ensure that in all TU's, you include Foo.h before the third party header. But if the third party library has globals defined in the .cpp file, there is absolutely nothing you can do, in terms of pure C++, shy of hacking that code.

Now, there is one thing you can do, if you are really desperate: you can define your global in a shared library, and use the LD_PRELOAD trick to ensure that your shared library is loaded before anything else: What is the LD_PRELOAD trick?. This is pretty hacky but if it's a binary you have total control of (i.e. you aren't distributing it to clients; perhaps just a server you run yourself) then this may be good enough.

Nir Friedman
  • 17,108
  • 2
  • 44
  • 72