【问题标题】:Garbage from other linking units来自其他链接单元的垃圾
【发布时间】:2011-06-16 10:56:51
【问题描述】:

在讨论this话题时,我问了自己以下问题。

在某些情况下,翻译单元中的一些未使用代码会链接到 GCC 和 VC++ 等流行编译器的最终可执行代码(当然是在发布模式下)?

例如假设我们有 2 个编译单元:

//A.hpp
//Here are declarations of some classes, functions, extern variables etc.

还有源文件

//A.cpp
//defination of A.hpp declarations

最后是主要的

//main.cpp
//including A.hpp library
#include "A.hpp"
//here we will use some stuff from A.hpp library, but not everything

我的问题是。如果在 main.cpp 中没有使用 A.hpp 中的所有内容怎么办?链接器是否会删除所有未使用的代码,或者在某些情况下,某些未使用的代码可以链接到可执行文件?

编辑:我对 G++ 和 VC++ 链接器感兴趣。

编辑:当然我的意思是在发布模式下。

编辑:我开始赏金这个问题,以获得好的和完整的答案。我期待答案,它将解释在哪些情况下 g++ 和 VC++ 链接器正在链接垃圾以及它们能够从可执行文件中删除什么样的代码(不需要的函数、不需要的全局变量、不需要的类定义等)和为什么他们不能删除某种不需要的东西。

【问题讨论】:

  • 请指定编译器。
  • 对于 GCC,在 SO 中搜索 -gc-sections。我以前回答过这个问题。我认为它被称为 VC++ 的函数级链接。
  • @leppie 我在互联网上搜索了函数级链接。好的,这是部分答案,但是类、外部变量等呢?他们是否也会从可执行文件中删除? gcc 也有类似的东西吗?

标签: c++ optimization linker


【解决方案1】:

正如其他发布者所指出的,链接器通常不会在构建最终可执行文件之前删除死代码。但是,您通常可以使用优化设置来强制链接器更加努力地执行此操作。

对于 GCC,这分两个阶段完成:

  1. 首先编译数据,但告诉编译器将代码分成翻译单元内的单独部分。这将通过使用以下两个编译器标志对函数、类和外部变量完成:

    -fdata-sections -ffunction-sections

  2. 使用链接器优化标志将翻译单元链接在一起(这会导致链接器丢弃未引用的部分):

    -Wl,--gc-sections

因此,如果您有一个名为 test.cpp 的文件,其中声明了两个函数,但其​​中一个未使用,则可以使用以下 gcc(g++) 命令省略未使用的函数:

gcc -Os -fdata-sections -ffunction-sections test.cpp -o test.o -Wl,--gc-sections

(注意 -Os 是一个额外的链接器标志,告诉 GCC 优化大小)

我也在某处读到链接静态库是不同的。在这种情况下,GCC 会自动省略未使用的符号。也许另一张海报可以证实/反驳这一点。

对于 MSVC,正如其他人所提到的,功能级链接完成了同样的事情。 我相信为此的编译器标志是(将事物分类为部分):

/Gy

然后是链接器标志(丢弃未使用的部分):

/OPT:REF

编辑:经过进一步研究,我认为关于 GCC 自动为静态库执行此操作的说法是错误的。

【讨论】:

    【解决方案2】:

    链接器不会删除代码。

    您仍然可以在代码中通过dlsym 动态访问它。

    【讨论】:

    • 当然你可以告诉链接器丢弃未使用的代码部分。
    • @Benoit Thiery 我正在尝试弄清楚 dlsym 是如何工作的。我编写了休闲程序pastebin.com/YpfFTkkn,然后尝试从另一个pastebin.com/en8NjsQF 调用它的方法。但是我遇到了分段错误。那我做错了什么?
    • dlopen 允许加载动态库(.so 文件),但不能加载另一个可执行文件。请在使用句柄调用 dlsym 之前检查 dlopen 的错误。
    • @Benoit Thiery 我在问 EXECUTABLE 文件中是否存在不需要的代码。如果 dlopen 无法加载可执行文件,则说明您的答案不正确。
    • @Ashot 不要混淆 dlopen 和 dlsym。 dlsym 可以查看可执行文件中已经存在的符号。
    【解决方案3】:

    一般来说,链接器倾向于包含在命令行中显式传递的目标文件中的所有内容,但只从静态库中拉入这些目标文件,其中包含解析已链接目标文件的外部引用所需的符号。

    但是,链接器可能决定丢弃从未调用过的函数或从未引用过的数据。具体细节取决于编译器和链接器开关。

    在 C++ 代码中,如果源文件被显式编译并链接到您的应用程序,那么我希望包含具有构造函数和/或析构函数的静态存储持续时间的对象,并且它们的构造函数/析构函数运行在适当的时间。因此,从这些构造函数或析构函数调用的任何代码都必须在最终的可执行文件中。但是,如果没有从任何地方调用代码,那么如果不使用 dlsym 之类的东西,您就无法编写程序来判断是否包含代码,因此链接器可能会忽略将其包含在最终的可执行文件中。

    我还希望任何具有全局可见性的符号都可以通过dlsym 找到(与仅在可执行文件可见的“隐藏”符号相反)会出现在最终的可执行文件中。但是,这是一种预期,而不是我通过测试或阅读文档确认的内容。

    【讨论】:

      【解决方案4】:

      如果你想确保代码在你的可执行文件中,即使它没有被它内部调用,你可以将它作为一个静态感知动态链接库加载(静态感知库是一个自动加载到内存中的库)程序已加载,而不是您可以将字符串传递给加载库的函数然后手动搜索挂钩的功能)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-07-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多