如何让GNU__attribute__((构造函数))在库中工作
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);
}
配置.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编译成存储在liblibrary.a中的myfunc.o,并将其与其他对象文件链接到"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
添加到config.ac中,但这并没有改变结果。我将尝试您在C++中提到的"带有显式构造函数的静态数据"技巧,但我仍然有兴趣了解如何使我的示例与__attribute__((constructor))
一起工作。
编辑于2015-02-23我尝试了"带有显式构造函数的静态数据"技巧,但得到了与以前相同的结果:如果所有对象文件都显式链接到一个可执行文件中,则它有效,但如果我想要自动构建的东西通过库链接到可执行文件,则无效。
添加hello_LDFLAGS = -Wl,--whole-archive
(David Grayson建议)会导致许多"多重定义"错误。Automake将这些标志放在链接命令的开头附近,这样它就不仅仅适用于库。Automake建议不要在指定了要链接的库的hello_LDADD
中直接包含链接器标志。可以用显式Make规则覆盖Automake规则(我可以将链接器标志放在我想要的位置),但这样我可能会面临其他标准Make规则(由Automake提供)行为不端的风险。
我会看看是否可以使用dlopen
使其工作。
我想您有一个Linux系统。然后确保该库构建为共享库(请参见此处),而不是静态库。
当加载共享库时,将调用具有__attribute__(constructor)
的函数,例如在ld.so
时间,或者如果库是已加载的插件,则在dlopen
时间。
CCD_ 12在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链接选项,并为我实际需要的驱动程序包含了来自存档的驱动程序init代码。这似乎是合理的,因为这也是一种很好的方式来构建所有内容,然后控制最终程序中的内容。我非常喜欢这种方式,因为我不再需要用包含的内容来监督编译步骤。我可以编译和归档所有内容。
所以当你链接:
gcc -Wl,-u,myconstructor1,-u,myconstructor2 -o prog ... -llib1 -llib2
可以根据应用程序的选定功能自动生成所需的构造函数列表。尽管我还没有弄清楚如何使用自动工具实现自动化。
- 优先级队列构造函数的工作
- 这个C++编译器优化(在自身的实例上调用对象自己的构造函数)的名称是什么,它是如何工作的?
- 为什么我的类工作正常,即使在返回垃圾值作为赋值运算符和空复制构造函数的返回之后
- 成员的初始值(如"content(ht * wd, c)"C++ Primer 的构造函数中如何工作?
- 隐式生成的移动构造函数在 c++ 中如何工作?
- "= 删除"如何工作?有人可以解释这个构造函数吗?
- 如何让 call_once() 在构造函数中正常工作?
- C++参数化构造函数使代码在传递大输入时停止工作
- 我是否应该在构造函数或成员方法中进行大量初始化工作
- 在 GCC 中工作的外行构造函数模板在 Clang 中失败
- 为什么带有 const 关键字的构造函数可以工作,而没有它就不能工作?
- 共享_ptr复制构造函数如何工作
- VS为我提供了对构造函数的另一个解决方案,但我想知道为什么我的工作不起作用
- 当默认构造函数充当默认参数时,它是如何工作的
- 构造函数和析构函数的工作原理
- 如何让我的构造函数和函数工作,以便我的 main() 能够同时显示字符串和 int 数据
- 隐式删除的默认构造函数在 C++14 中工作
- 构造函数未按预期工作
- 以 std::p air 作为参数的构造函数:T a({1,2}) 有效,T a = {1,2} 不工作
- 为什么我的序列构造函数无法正常工作