6

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.

Community
  • 1
  • 1
Joe
  • 3,090
  • 6
  • 37
  • 55
  • 1
    Do you load shared library dynamically, or link to them? – Slava Oct 24 '13 at 21:31
  • "provide exported functions (`lib_init()` and `lib_deinit()`)" -- from what I've seen, this is the common idiom. – Brian Cain Oct 24 '13 at 21:36
  • @Slava: It's linked dynamically. – Joe Oct 24 '13 at 22:07
  • After really searching and trying out different approaches, I found this [question](http://stackoverflow.com/questions/11106875/attribute-constructor-call-order-confusion/11161557#11161557) asked by [queen3](http://stackoverflow.com/users/119224/queen3) from about 1 year ago and it looks that my question is a duplicate of that. The only solution I found there, was to experiment with linker scripts (what I'm aware of). – Joe Oct 24 '13 at 22:44
  • Another way is to use a proxy object for accessing the shared library: https://stackoverflow.com/a/23405008/412080 – Maxim Egorushkin Mar 05 '20 at 12:20

2 Answers2

1

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.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • Wow, what a tricky approach. But it also looks a little hacky. I don't want to have that kind of impact to all of my translation units. At least, not all of the makefiles are under my control because of usage of third party code. – Joe Oct 24 '13 at 22:58
  • This approach relies on the special object actually being constructed. As far as I can tell, the standard only requires this before any function in the TU is called. It'll probably work in practice, but it's a bit tricky to *prove* that it'll work. – Kerrek SB Oct 25 '13 at 07:51
  • @Kerrek This should work wit gcc and gnu ld, I'm not claiming it's portable. It is not as the standard says nothing about dynamic loading of code. – n. m. could be an AI Oct 25 '13 at 08:47
  • @n.m.: Could you give a link for standard's specification about dynamic loading of code? – Joe Oct 25 '13 at 09:10
  • @Joe: There's nothing in the standard about dynamic loading. – n. m. could be an AI Oct 25 '13 at 09:20
0

@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).

nymgo
  • 1