[英]How do I get GNU __attribute__((constructor)) to work in a library?
如果我将所有目标文件链接在一个链接中,我可以使GNU __attribute__((constructor))
工作(对于C ++程序),但如果我将包含构造函数的对象文件存储在库中,它就不再起作用了然后链接库而不是目标文件。 我究竟做错了什么?
Makefile.am:
SUBDIRS = src
SRC / Makefile.am:
bin_PROGRAMS = hello
hello_SOURCES = hello.cc register.cc register.hh myfunc.cc
SRC / hello.cc:
#include <iostream> // for cout
#include <map>
#include "register.hh"
int main(int argc, char* argv[])
{
std::cout << "Hello, World!" << std::endl;
std::cout << "Have " << functions.size() << " functions registered."
<< std::endl;
for (Function_map::iterator it = functions.begin(); it != functions.end(); ++it) {
std::cout << "Registered " << (*it).first << std::endl;
(*it).second();
}
return 0;
}
SRC / register.cc:
#include <map>
#include <string>
#include "register.hh"
Function_map functions;
void register_function(const std::string& name, Function f)
{
functions[name] = f;
}
SRC / register.hh:
#ifndef REGISTER_H_
#define REGISTER_H_
#include <map>
#include <string>
typedef void (*Function)();
typedef std::map<const std::string, Function> Function_map;
extern Function_map functions;
void register_function(const std::string& name, Function f);
#endif
SRC / myfunc.cc:
#include "register.hh"
#include <iostream>
void myfunc()
{
std::cout << "This is myfunc!" << std::endl;
}
__attribute__((constructor))
void register_myfunc()
{
register_function("MYFUNC", myfunc);
}
configure.ac:
AC_PREREQ([2.69])
AC_INIT([hello], [1.4], [bugs@my.domain])
AC_CONFIG_SRCDIR([src/hello.cc])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_AUX_DIR([auxiliary])
AM_INIT_AUTOMAKE([-Wall -Werror])
AC_PROG_CXX
AM_PROG_AR
AC_CONFIG_FILES([Makefile
src/Makefile])
AC_OUTPUT
所以所有C ++文件都被编译成目标文件,这些目标文件被链接到'hello'可执行文件中。
生成的'hello'程序的输出是:
Hello, World!
Have 1 functions registered.
Registered MYFUNC
This is myfunc!
如果我将src / Makefile.am更改为
bin_PROGRAMS = hello
hello_SOURCES = hello.cc register.cc register.hh
hello_LDADD = liblibrary.a
noinst_LIBRARIES = liblibrary.a
liblibrary_a_SOURCES = myfunc.cc
(即,myfunc.cc编译成myfunc.o,它存储在liblibrary.a中,它与其他目标文件链接成'hello'),然后'hello'的输出是
Hello, World!
Have 0 functions registered.
所以现在'register_myfunc'函数没有被执行。 为什么不?
编辑2015-02-22 (回应Basile Starynkevitch的回答):我正在使用GNU / Linux(Fedora 20)系统。 我尝试使用libtools构建共享库,但没有成功。 我调整了src / Makefile.am,如下所示:
bin_PROGRAMS = hello
hello_SOURCES = hello.cc register.cc register.hh
hello_LDADD = liblibrary.la
noinst_LTLIBRARIES = liblibrary.la
liblibrary_la_SOURCES = myfunc.cc
liblibrary_la_LDFLAGS = -shared -fPIC
(首先使用-shared
,稍后使用-fPIC
)并将LT_INIT
添加到configure.ac,但这并没有改变结果。 我将尝试使用C ++提到的“带有显式构造函数的静态数据”技巧,但我仍然有兴趣知道如何使用__attribute__((constructor))
来运行我的示例。
编辑2015-02-23我尝试了“带有显式构造函数的静态数据”技巧但获得了与之前相同的结果:如果所有对象文件显式链接到可执行文件中,它可以工作,但如果我想要自动构建的东西是通过库链接到可执行文件。
添加hello_LDFLAGS = -Wl,--whole-archive
(由David Grayson建议)会导致许多“多重定义”错误。 Automake将这些标志放在link命令的开头附近,因此它不仅适用于库。 Automake建议不要直接在hello_LDADD
中包含链接器标志,其中指定了要链接的库。 可以使用显式的Make规则覆盖Automake规则(我可以将链接器标记准确地放在我想要的位置),但是我可能会冒其他标准Make规则(由Automake提供)行为不端的风险。
我会看看我是否可以使用dlopen
来使用它。
我想你有一个Linux系统。 然后确保将库构建为共享库(请参阅此处 ),而不是静态库。
加载该共享库时将调用带有__attribute__(constructor)
函数,例如在ld.so
时,或者如果库是加载的插件,则在dlopen
时调用。
BTW __attribute__(constructor)
在C中比在C ++中更有用。 在C ++中,您并不真正需要它,因为您可以在其class
使用static
数据和一些显式定义的构造函数来实现相同的结果。
有关详细信息,请阅读Drepper的论文: 如何编写共享库 。
默认情况下,如果程序实际引用了某些符号,GCC的链接器将仅链接到静态库( liblibrary.a
)中。
因此,使您的库链接的一种方法是使用它的符号。 例如,您可以将其添加到main.cc
:
void myfunc();
...
std::cout << (void *)&myfunc << std::endl;
或者你可以手动调用库中的一些初始化函数。 在你这样做的时候,可能没有理由再使用__attr__((constructor))
了。
或者,你可以尝试使用-Wl,--whole-archive
所描述的选项链接在这里 。 为此,您可以将此行添加到src / Makefile.am:
hello_LDFLAGS = -Wl,--whole-archive
但是,这导致我的GCC版本为libgcc.a
各种符号输出大量的多个定义错误,所以我不知道这是否是一个真正的解决方案。
我最终使用了-u link选项,并为我实际需要的驱动程序包含了存档的驱动程序初始化代码。 这似乎是合理的,因为它也是一个很好的方法来构建所有内容,然后控制最终程序中的确切内容。 我非常喜欢这种方式,因为我不再需要用包含的内容来监督编译步骤。 我可以编译和存档一切。
所以当你链接:
gcc -Wl,-u,myconstructor1,-u,myconstructor2 -o prog ... -llib1 -llib2
根据应用程序的选定功能,可以自动生成所需的构造函数列表。 虽然我还没有弄清楚如何使用autotools自动化它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.