通过剥离未使用的代码来解析未定义的引用

Resolve undefined reference by stripping unused code

提问人:Post Self 提问时间:1/30/2023 最后编辑:John BollingerPost Self 更新时间:1/30/2023 访问量:119

问:

假设我们有以下 C 代码:

void undefined_reference(void);

void bad(void) {
    undefined_reference();
}

int main(void) {}

在功能中,我们陷入了 链接器错误 ,正如预期的那样。但是,此函数实际上并未在代码中的任何位置使用,因此,对于程序的执行,此未定义的引用无关紧要。badundefined reference to 'undefined_reference'

是否有可能成功编译此代码,以便简单地将其删除,因为它从未被调用(类似于 JavaScript 中的摇树)?bad

c 未定义引用 死代码

评论

2赞 Eric Postpischil 1/30/2023
有一些链接器可以剥离死代码,并结合编译器支持,因此可以使用正确的工具成功编译和链接此代码。但是您没有说明您正在使用或感兴趣的工具。从理论上讲,是的,这是可能的。至于仅使用 C 标准指定的功能可以完成什么,不,不可能保证成功的编译和链接。对于它们之间的具体答案,您需要确定要使用的工具。
0赞 Eugene Sh. 1/30/2023
您还可以执行其他奇怪的技巧,例如从链接器脚本导出符号。但是为什么?只需删除死代码即可。undefined_reference
0赞 John Bollinger 1/30/2023
删除 [tree-shaking] 标签,因为它专门与 ECMAscript 相关联,问题是关于 C 的。
0赞 Ian Abbott 1/30/2023
您可以将对象文件放入库中,并将程序与库链接,以便包含的目标文件仅在需要时才被链接。badbad
0赞 Eugene Sh. 1/30/2023
@IanAbbott 你是说吗?我猜是“固定的”undefined_reference()bad()

答:

2赞 0___________ 1/30/2023 #1

许多编译器(例如 gcc)会正确编译和链接它,如果你

  1. 启用优化
  2. make 函数。否则,它将具有外部链接。badstatic

https://godbolt.org/z/KrvfrYYdn

另一种方法是添加此函数的 stump 版本(以及显示警告的编译指示)

评论

0赞 Eugene Sh. 1/30/2023
不过,我不会在生产代码中依赖它......
0赞 0___________ 1/30/2023
@EugeneSh。我也是。。。。
3赞 John Bollinger 1/30/2023
但是,如果还不是静态的,那么这需要修改源代码,如果您愿意并且能够这样做,那么您不妨完全删除该函数或通过条件编译指令取消其定义。bad()
4赞 n. m. could be an AI 1/30/2023 #2

这个函数实际上并没有在代码中的任何位置使用!

你知道,我知道,但编译器不知道。它一次处理一个翻译单元。它不能说没有其他翻译单位。

但是 main 不调用任何东西,所以不能有其他翻译单元!

可以有在之前和之后运行的代码(以实现定义的方式)。main

好的,链接器呢?它可以看到整个程序!

没有。代码可以在运行时动态加载(也可以通过链接器看不到的代码加载)。

因此,默认情况下,编译器和链接器甚至都不会尝试查找未使用的函数。

在某些系统上,可以指示编译器和链接器尝试对未使用的代码进行垃圾回收(并在这样做时假定整个程序视图),但这通常不是默认的操作模式。

使用 gcc 和 gnu ld,您可以使用以下选项:

gcc -ffunction-sections -Wl,--gc-sections main.c -o main

其他系统可能有不同的方法来执行此操作。

评论

0赞 Eugene Sh. 1/30/2023
在大多数情况下,链接器能够消除未使用的部分。我不确定这里的默认值是什么,但它也能够将每个函数放在一个单独的部分中(实际上,编译器可以)。因此,有了这两个能力,它可以(并且将)消除未使用的功能。
1赞 n. m. could be an AI 1/30/2023
@EugeneSh。添加了启用此行为的选项。