Libtool linkage - global convenient library initialization system

I have a setting that does not work, and I have no idea what I'm doing wrong here - I am trying to convert a project from handfileed Makefiles to autotools, and I think that most of it was configured for me correctly, since the application and everything its convenience libraries are built and linked correctly, but there are some problems with global state initializers convenient libraries.

Some of the libraries follow a pattern similar to this in the code:

// in global scope of somemodule.cpp namespace { bool registered = ModuleShare::registerModule<SomeModule>("SomeModule"); } 

this code along with the actual source of the module is compiled into a convenience library using libtool

 // libsomething Makefile.am noinst_LTLIBRARIES = libsomething.la libsomething_la_SOURCES = \ [ ... ] moduleshare.cpp moduleshare.h \ somemodule.cpp somemodule.h \ [ ... ] 

and this library is built and referenced in the Makefile.am application as follows:

 // someapp Makefile.am bin_PROGRAMS = someapp someapp_SOURCES = someapp.c someapp.h someapp_CPPFLAGS = -I ${top_srcdir}/something someapp_LDADD = ${top_srcdir}/something/libsomething.la 

I modified ModuleShare :: registerModule to make sure it is not being called:

 template<typename T> static bool registerModule(const std::string &module){ printf("%s\n", module.c_str()); [ ... ] return true; } 

What could be the reason for this?

EDIT:

At this point, I found out that this problem is related to the linker, which is allowed to delete unused characters during communication. If I contact manually using --whole-archive , everything will work as expected.

Based on background C, I also tried

 static void __attribute__((constructor)) register (void) { ModuleShare::registerModule<SomeModule>("SomeModule"); } 

but this also does not lead to the behavior that I expected, which is stranger, given that I rely a lot on this construct in my private C projects.

At this moment I am open to suggestions in any direction. I know that libtool does not provide flags for each library in LDADD, and I cannot and just do not want to compile everything with -whole-archive to get rid of these symptoms. I have limited control over the codebase, but I think I really need to ask what is a good and reliable way to initialize the state of a program in a convenience library using autotools?

EDIT2:

I think I'm one step closer - it seems that there are no calls to the convenience library in the application code, and therefore the linker omits it. An application is called to the library only with the help of the template function defined in the SomeModule header file, which relies on static initializers called in convenience libraries. This dependency replication bolts the entire assembly.

However, I'm not sure how to solve this: /

Thanks Andy

+6
source share
3 answers

This answer may require too many code changes for you, but I will talk about it. As I mentioned earlier, the convenience library model is a kind of static link, where the libraries are mapped to each other until the final link to the encapsulating library. Only in this case is the "library" executable. Therefore, I would suggest that material in an unnamed namespace (essentially static variables), especially unwritten variables, will be deleted. With the code above, it worked as I expected.

I managed to get registerModule to print this message without special intermediary tricks in a convenient library, for example:

somemodule.cpp

 // in global scope namespace registration { bool somemodule = ModuleShare::registerModule<SomeModule>("SomeModule"); } 

someapp.cpp

 // somewhere namespace registration { extern bool somemodule; ... // and all the other registration bools } // in some code that does initialization, possibly if (!(registration::somemodule && ...)) { // consequences of initialization failure } 
+4
source

Since you use autotools, you may be in a situation where exclusive use of gcc is possible. In this case, you can use the `used 'attribute to tell gcc to force the linker to include the character:

 namespace { __attribute__ ((used)) bool registered = ModuleShare::registerModule<SomeModule>("SomeModule"); } 
+5
source

Instead of using - whole-archive, did you try just adding -u registered ?

As ld specified manually:

 -u symbol --undefined=symbol Force symbol to be entered in the output file as an undefined symbol. Doing this may, for example, trigger linking of additional modules from standard libraries. -u may be repeated with different option arguments to enter additional undefined symbols. This option is equivalent to the "EXTERN" linker script command. 

Edit: I think what you are trying to achieve is very similar to managing a Qt plugin. This article describes a bit. And this is the official documentation 4.8.

Given that convenience libraries are built statically, it might be sufficient to create a dummy instance inside this convenience library (or a dummy use registered ) and declare it extern where you use it (this is what Q_EXPORT_PLUGIN2 and Q_IMPORT_PLUGIN do).

+5
source

Source: https://habr.com/ru/post/958581/


All Articles