为什么可执行文件这么大? (为什么死代码没有被删除?)
编译并链接此文件会生成 1-KiB 可执行文件:
#pragma comment(linker, "/Entry:mainCRTStartup") // No CRT code (reduce size)
#pragma comment(linker, "/Subsystem:Console") // Needed if avoiding CRT
#define STRINGIFIER(x) func##x
#define STRINGIFY(x) STRINGIFIER(x)
#define G int STRINGIFY(__COUNTER__)(void) { return __COUNTER__; }
int mainCRTStartup(void) { return 0; } // Does nothing
#if 0
// Every `G' generates a new, unused function
G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G
G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G
#endif
当您将 #if 0
更改为 #if 1
时,输出大小 <翻倍至 2 KiB。
尽管我的命令行选项包含我能想到的所有优化,但迄今为止所有版本的 Visual C++ 似乎都是这样做的:
/Ox /MD /link /fixed /OPT:ICF /OPT:REF
而且,具体来说,我没有包含任何调试信息。
有谁知道为什么 /OPT:REF
不会导致链接器删除未使用的函数?
Compilng and linking this file results in a 1-KiB executable:
#pragma comment(linker, "/Entry:mainCRTStartup") // No CRT code (reduce size)
#pragma comment(linker, "/Subsystem:Console") // Needed if avoiding CRT
#define STRINGIFIER(x) func##x
#define STRINGIFY(x) STRINGIFIER(x)
#define G int STRINGIFY(__COUNTER__)(void) { return __COUNTER__; }
int mainCRTStartup(void) { return 0; } // Does nothing
#if 0
// Every `G' generates a new, unused function
G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G
G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G G
#endif
When you change #if 0
to #if 1
), the output size doubles to 2 KiB.
It seems to do this with all versions of Visual C++ to date, even though my command-line options contain all optimizations I could think of:
/Ox /MD /link /fixed /OPT:ICF /OPT:REF
and, specifically, I did not include any debugging information.
Does anyone know why /OPT:REF
is not causing the linker to remove the unused functions?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
从广义上讲......编译器在“对象记录”中生成代码,其中包含一堆汇编代码和支持信息。链接器将这些对象记录链接在一起以创建可执行文件。
通常,编译器会为整个源文件创建一个单个对象记录。在这种情况下,链接器只能决定是否链接整个对象记录。由于对象记录中至少有一个函数被使用,因此它必须链接所有函数。
在某些编译器上,您可以告诉它为每个函数生成单独的对象记录(一个对象文件可以有多个对象记录)。在这种情况下,链接器可以决定省略一些从未被调用过的对象记录。
来自 /OPT 的 Microsoft 文档:
/Gy
编译器选项启用函数级链接。作为参考,这个功能在 gcc 中也存在:
以及 ld 中的伴随选项:
In broad terms... the compiler generates code in "object records" that contains a bunch of assembly code and supporting information. The linker links these object records together to create an executable.
Often a compiler will create a single object record for an entire source file. In this case, the linker can only decide to link in the entire object record, or not. Since there is at least one function in the object record that is used, it must link in all of it.
On some compilers, you can tell it to generate a separate object record for each function (an object file can have multiple object records). In this case, the linker can make the decision to omit some of the object records if they're never called.
From the Microsoft documentation for /OPT:
The
/Gy
compiler option enables function-level linking.For reference, this feature also exists in gcc:
And the companion option in ld: