【问题标题】:gcc functions with constructor attribute are not being linked具有构造函数属性的 gcc 函数没有被链接
【发布时间】:2011-07-05 22:56:35
【问题描述】:

我有一堆静态库,它们是相互依赖的。 由于依赖关系,我在为我的目标链接这些库时遇到了问题。 作为一种解决方法,我从所有库中创建了一个存档文件。

其中一个静态库具有构造函数和析构函数,组合存档也是如此(使用 nmobjdump 检查存档) 但是当我为我的目标使用组合存档时,最终的二进制文件不包含构造函数和析构函数。

我也尝试了 --whole-archive 但这个选项似乎对我不起作用(二进制大小没有增加)。

任何可能出错的想法。 谢谢

【问题讨论】:

  • 你的链接器是什么?它的版本是什么? (如果你使用 gcc,请说出 gcc 的版本,ld 和 binutils 的版本)

标签: gcc attributes constructor static-linking


【解决方案1】:

链接器只加载它必须加载的模块(即你实际引用的模块隐式显式),除非你强制它。

两者都是好事,也是不幸的。它使链接过程更快并防止代码膨胀。它通常也不会引起问题,因为通常您会以某种方式引用所有需要的模块,而不需要您不引用的模块。正常。
但这也是导致这种静默失败的原因:链接器从不加载包含您的构造函数/析构函数的模块,并且甚至从不查看它。那是因为你从来没有真正调用过这些函数。

您可以通过显式链接与包含构造函数/析构函数的源对应的目标文件来强制链接器包含模块。

换句话说,如果构造函数/析构函数在foo.c 中,请将-l/path/to/foo.o 添加到链接器的选项中,或者只是将命令行上的foo.o 传递给链接器(作为额外参数)。

在任何一种情况下,通过这样做,您明确告诉链接器考虑这个模块,这样做会使其找到构造函数并正确调用它们。

另一种选择(不需要您使用命令行)可能是将变量或函数(不一定做任何事情)放入与构造函数/析构函数相同的源文件中,然后从任何主程序中的源文件。
这也将强制链接器加载包含模块(它将在其中找到构造函数)。

更新:
我测试了那个替代方案,它工作正常:

/* libcode.c */
void enable_constructors() { /* do nothing */ }
void __attribute__ ((constructor)) con() { __builtin_puts("construct"); }
void __attribute__ ((destructor))  des() { __builtin_puts("destruct"); }


/* main.c */
extern void enable_constructors();

int main()
{
    enable_constructors();
    __builtin_puts("main");
    return 0;
}

输出是:

construct
main
destruct

它也适用于您从主程序的源文件中触摸的全局变量:

/* libcode.c */
int enable_constructors; /* not used for anything */

void __attribute__ ((constructor)) cons() { __builtin_puts("construct"); }
void __attribute__ ((destructor)) des() { __builtin_puts("destruct"); }


/* main.c */
extern int enable_constructors;

int main()
{
    ++enable_constructors; /* touch the other module */
    __builtin_puts("main");
    return 0;
}

【讨论】:

  • 我还没有尝试这种方法,但我注意到我没有正确使用 --Whole-archive。现在有了这个选项,我可以使用构造函数,但如果我可以在不做任何特殊操作的情况下将构造函数链接起来,我会很高兴。
  • 调用一个什么都不做的函数或增加一个外部变量无疑是一个有点丑陋的hack,但它归结为一个多余的INC指令或一个单一的CALL(或者如果你什么都不做'很幸运)在你程序的任何时候(例如在main的开头),我认为这仍然是可以接受的。请注意,编译器优化不会死条的任何代码都可以,它不必在main 中或在特定时间发生。只要 something 在另一个模块中引用 something,链接器就会偶然发现构造函数属性。
  • 使用构造函数的地址从主模块初始化变量可能就足够了(尽管我没有尝试过!)。仅此一项就应该让链接器也接触到模块。
  • @kanna 现在我遇到了和你一样的问题。我尝试将“-Wl,--whole-archive”标志添加到 g++,但它没有用。你能举例说明它是如何工作的吗?
  • @Lion 抱歉,我现在没有确切的命令,已经很久了。您能说出您使用的是哪个版本的工具链吗?
【解决方案2】:

从另一个答案:

您可以通过显式链接与包含构造函数/析构函数的源对应的目标文件来强制链接器包含模块。

使用现代 CMake,这就像库中的 INTERFACE 源一样简单。只需为构造函数使用专用的源文件即可。

cmake_minimum_required(VERSION 3.13)

add_library(mylib STATIC libsource.c)
target_sources(mylib INTERFACE lib_ctor.c)
# CMake prior 3.13 requires an absolute path
# target_sources(mylib INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib_ctor.c)

add_executable(prog app.c)
target_link_libraries(prog mylib)

这里,lib_ctor.c 将包含构造函数。 INTERFACE 源被添加到与mylib 链接的每个目标的源文件列表中。因此它们被显式链接,并且构造函数被链接器读取。

我在 --whole-archive 和其他 hack 上浪费了一两个小时,然后才意识到我的构建系统可以以更简洁的方式处理这个问题。由于 CMake 现在非常流行,并且这是“gcc force constructor”和“gcc constructor not linked”等术语的热门搜索结果,我想分享一下。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-24
    • 1970-01-01
    • 1970-01-01
    • 2014-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多