Joe
Joe

Reputation: 3160

Linux shared library init & deinit when also using c++ static initializer

I want to have automated calls to initialize and deinitialize my shared library.

In my shared library, I need some static initialization of C++ objects, among others because of use of third party code (e.g. UnitTest++). When my init function is executed, I need to be guaranted, that all static initialization of C++ objects (of all linked translation units) is done (and vice versa for deinit); so just the same conditions as the execution of main() can expect in a C++ program.

I've seen much informations about linux shared library init/deinit e.g. like:

But the provided solutions won't fit my needs. In both approaches (__attribute__((constructor)) and even -Wl,-init,<function name>) the init function seems to be called before static initialization of C++ objects is completely done.

I also played around with __attribute__ ((init_priority(…))) like:

class InitAndDeinit {
public:
    InitAndDeinit() {
        // Do some initialization
    }
    ~InitAndDeinit() {
        // Do some cleanup
    }
} initAndDeinit __attribute__((init_priority(65535)));

But that also won't place the calls to the desired point; even with __attribute__((constructor(65535))).

I'd tested with gcc 4.6.4, 4.7.3 and 4.8.1 (4.6.4 shows a slightly different behaviour regarding the sorting of __attribute__((constructor))).

Any suggestions?

My current workaround is to provide exported functions (lib_init() and lib_deinit()) which have to called manually by the application.

Upvotes: 6

Views: 5743

Answers (2)

nymgo
nymgo

Reputation: 11

@Joe static object initialization and the order of initialization is determined by loader or linker handling the .ctors and .dtors section or the .init_array and fini_array section. When the loader calls dlopen() before it returns, global constructors and static object initialization takes place. It may already be happening unless the shared libraries you are using are compiled with `-nostartfiles'' or ``-nostdlib'' The Runtime ABI specification does not mention how it needs to be implemented, it's upto linker/loader. However the standard states that static data and global constructor initialization must take place before any function of the shared library can be called.

please see below.

5.2. Library constructor and destructor functions Libraries should export initialization and cleanup routines using the gcc attribute((constructor)) and attribute((destructor)) function attributes. See the gcc info pages for information on these. Constructor routines are executed before dlopen returns (or before main() is started if the library is loaded at load time). Destructor routines are executed before dlclose returns (or after exit() or completion of main() if the library is loaded at load time). The C prototypes for these functions are: void attribute ((constructor)) my_init(void); void attribute ((destructor)) my_fini(void);

Shared libraries must not be compiled with the gcc arguments -nostartfiles'' or-nostdlib''. If those arguments are used, the constructor/destructor routines will not be executed (unless special measures are taken).

Upvotes: 1

n. m. could be an AI
n. m. could be an AI

Reputation: 119877

Here's one possible solution.

Static objects in a TU are initialized in their order of definition. Append a definition of a static object of a special type T to the end of each source file. The constructor of T should increment a static zero-initialized member. As soon as the counter reaches the number of source files in the module (determined by a build script), call your lib_init().

lib_deinit() is called after the counter is decremented back to zero.

Each library should have its own T.

You should be able to modify your makefile such that you don't have to physically alter source files. For instance, instead of g++ -c foo.C use g++ -c myspecialstaticinitcode.C -o foo.C -include foo.C or something like that.

Upvotes: 1

Related Questions