开发者

Linux shared library global constructors interdependency

Operating system Centos 5.6 i686 2.6.18-53.1.4.el5vm.

gcc version 4.1.2 20080704 (Red Hat 4.1.2-48)

ld version 2.17.50.0.6-6.el5 20061020

I compile in this way:

gcc -c -fnon-call-exceptions -fexceptions -Wall -DUNICODE -D_UNICODE -D_REENTRANT -I.

and link this way:

gcc -lstdc++ -pthread -ldl -lrt --no-relocate -Wl,-rpath,$SO_DIR -L$SO_DIR $LIBRARIES

I have 3 libraries and an executable: A.so, B.so, C.so, ElfExec

B.so depends on A.so. C.so depends on B.so.

In code A.so has a header through which it exposes functionality A.h, B.so in code has a B.h header which includes A.h and B functionality. C.so in code includes B.h.

A.h has defined a static variable K of a type that can be used if and only if a static memory manager from A.so is initialized. Variable K is directly defined in A.h, in the header, because of this its initialization is propagated in the global constructors of all objects composing B.so and C.so.

I link everything like this:

gcc "ALL B MODULES" -lstdc++ -pthread -ldl -lrt --no-relocate -Wl,-rpath,$SO_DIR -L$SO_DIR A.so

gcc "ALL C MODULES" -lstdc++ -pthread -ldl -lrt --no-relocate -Wl,-rpath,$SO_DIR -L$SO_DIR B.so

gcc "ALL ElfExec MODULES" -lstdc++ -pthread -ldl -lrt --no-relocate -Wl,-rpath,$SO_DIR -L$SO_DIR C.so

I also tried:

gcc "ALL ElfExec MODULES" -lstdc++ -pthread -ldl -lrt --no-relocate -Wl,-rpath,$SO_DIR -L$SO_DIR A.so B.so C.so

When run ElfExec gets a SIGSEGV because it tries to initialize the variable K before the static memory manager from A.so is initialized.

This is because the global constructors from C.so are called before the ones from A.so.

If I make an application ElfExec2 that only need B.so

gcc "ALL ElfExec1 MODULES" -lstdc++ -pthread -ldl -lrt --no-relocate -Wl,-rpath,$SO_DIR -L$SO_DIR B.so

this works correctly.

In the case of ElfExec1 the linker sees that global constructors from A.so are needed to be called first before the ones from B.s开发者_Python百科o.

This does not happed in the case of ElfExec.

My solution is to link C.so like this: gcc "ALL C MODULES" -lstdc++ -pthread -ldl -lrt --no-relocate -Wl,-rpath,$SO_DIR -L$SO_DIR A.so B.so

This puts a direct dependency in the C.so to A.so.

Is there another way to tell the linker the order of global constructors calling?


As you found, don't trust the linker to know better than you. If the order is important, you need to programmatically specify the order. Don't just try to trick the linker.

That's what you'd do if these weren't libraries, right? Contructors/initialization functions that called each other in the right order?

My first choice would be to design the library to not have or use globals.

If you can't do that, my second choice is for each library that needs to initialize globals to have an init method. Consumers of the library need to call that init method before they can do anything, and the library must try to prevent use/construction until init has been properly done. Perhaps making them static to the init method and then setting global pointers to them (K* k) might help in that implementation. This should be sufficient to make the initialization chain together in the right order.

Finally, if there is an obstacle to having the user of any library (meaning B for A, or C for B, application for C) call the init method, you can use language extensions such as this for gcc:

extern "C" __attribute__ ((constructor)) void A_lib_ctor()
{
    // ....
}
extern "C" __attribute__ ((destructor)) void A_lib_dtor()
{
    // ....
}

to do what you need automatically on library load. This sacrifices some portability. Possibly sacrificing more, newer versions of gcc support a constructor(priority) syntax.

My last choice would be the complicated steps to manually load libraries with dlopen.

Design is better with the foremost choice that works for you and worse for the later ones.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜