为什么可执行文件如此之大? (为什么不删除死代码?)

时间:2012-02-23 23:50:49

标签: c++ c visual-c++

编译并链接此文件会产生 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没有导致链接器删除未使用的函数?

1 个答案:

答案 0 :(得分:20)

从广义上讲......编译器在“对象记录”中生成代码,其中包含一堆汇编代码和支持信息。链接器将这些对象记录链接在一起以创建可执行文件。

编译器通常会为整个源文件创建单个对象记录。在这种情况下,链接器只能决定是否链接整个对象记录。由于在对象记录中至少有一个函数被使用,它必须链接所有函数。

在某些编译器上,您可以告诉它为每个函数生成单独的对象记录(对象文件可以有多个对象记录)。在这种情况下,如果从未调用它们,链接器可以决定省略一些对象记录。

来自/OPT的Microsoft文档:

  

/ OPT:REF

     
    

LINK默认删除未引用的打包函数。如果对象包含已使用/ Gy选项编译的打包函数(COMDAT),则该对象包含该函数。此优化称为传递COMDAT消除。要覆盖此默认值并在程序中保留未引用的COMDAT,请指定/ OPT:NOREF。您可以使用/ INCLUDE选项覆盖特定符号的删除。

  

/Gy编译器选项启用功能级链接。

作为参考,此功能也存在于gcc:

  

-ffunction截面
  -fdata-sections

     
    

如果目标支持任意部分,则将每个函数或数据项放入输出文件中的自己的部分。函数名称或数据项名称确定输出文件中节的名称。

         

在链接器可以执行优化的系统上使用这些选项,以改善指令空间中的引用位置。大多数使用ELF对象格式的系统和运行Solaris 2的SPARC处理器都具有这种优化的链接器。 AIX可能会在将来进行这些优化。

         

只有在获得重大好处时才使用这些选项。指定这些选项时,汇编器和链接器将创建更大的对象和可执行文件,并且速度也会更慢。如果指定此选项,则无法在所有系统上使用“gprof”,如果同时指定-g,则可能在调试时遇到问题。

  

ld中的伴侣选项:

  

- gc-sections

     
    

启用未使用输入节的垃圾回收。在不支持此选项的目标上会被忽略。此选项与-r或--emit-relocs不兼容。可以通过在命令行上指定--no-gc-sections来恢复默认行为(不执行此垃圾回收)。