检测C ++二进制文件是否已优化

时间:2009-07-13 17:54:38

标签: c++ optimization

是否有一个标志或其他可靠的方法来检测编译的C ++二进制文件是否使用优化进行编译?

我对编译器特定的解决方案没问题。


编辑:这适用于构建部署系统,可能会意外部署未正确构建的二进制文件。不太可能采用防水解决方案,但如果可以在某些时候检测到这种情况,它会节省一些痛苦(和金钱)。

编译器通常是gcc,有时是sun,如果有MSVC解决方案,我不想为了社区的利益而排除它。

10 个答案:

答案 0 :(得分:11)

GCC的最新版本有way to report which flags were used to compile a binary(第三个要点)。

有一个相关的命令行开关(--fverbose-asm)“只记录汇编器输出文件中的信息作为注释,因此信息永远不会到达目标文件。” --frecord-gcc-switches开关“导致用于调用编译器的命令行被记录到正在创建的目标文件中。”

答案 1 :(得分:5)

可能的启发式解决方案

如果我接受这项任务,那将证明这是一项合理的任务。我会对可执行的反汇编中看到的模式进行“频率分析”。作为程序员,我能够乍看之下区分优化和未优化(调试)代码。我会尝试将决策过程正式化,使用Visual Studio和x86平台,在未经优化的exe中看到的典型特征将是:

  • 具有完整的序言/结语(基于ebp的堆栈帧)的功能
  • 很多来自/进入内存的mov-s(所有变量都放在内存中)

这绝对不是100%,但是如果使用更长的exe,我会期望结果非常可靠。

我认为对于包括GCC在内的其他x86平台,规则将类似,如果不相同,并且对于其他平台,可以找到类似的规则。

检测运行时库等其他启发式方法也可以正常工作,具体取决于编译器设置。

任务听起来很傻

那就是说,我认为这样的任务“闻起来”,在大多数情况下完全避免它是明智的。如果您将提供此类任务背后的真正原因,很可能会找到一些合理的解决方案。

答案 2 :(得分:5)

我们使用的技术只是创建一个只有在构建调试时才出现在每个库中的符号:

// included_by_everything.h
#ifdef (_DEBUG)
extern "C" __declspec( dllexport ) void WasBuiltInDebug() {}
#else
// nothing!
#endif

然后在运行时(或在Perforce触发器中)加载每个模块时,我们可以通过简单地查找该符号来询问二进制文件是否进行调试:

bool IsDebugDLL( HMODULE DllHandle )
{
  // this system call returns a nonzero address if it can 
  // find the symbol, and NULL otherwise:
  return GetProcAddress( DllHandle , "WasBuiltInDebug" );
}

答案 3 :(得分:4)

在Windows上,您可以检查是否从二进制文件中删除了调试信息。如果检查COFF标头的二进制映像,则在COFF标头的字节偏移18处有一个名为Characteristics的2字节标志块。如果是旗帜

 IMAGE_FILE_DEBUG_STRIPPED = 0x0200
设置

然后从图像文件中删除调试信息。

这可以通过从文件偏移量0x52读取一个简单的2字节并检查标志是否已设置来完成。

可以按如下方式检查标志

 short flag_block;

 FILE * p_image_file = fopen (...); 

 fseek(p_image_file,0x52,SEEK_SET);

 fread(&flag_block,2,1,p_image_file);

 if(flag_block & 0x0200 > 0)
    then debug info is stripped

您可以从Microsoft找到Windows PE format document,它会更详细地描述它,以便您计算要读取的字节偏移等...

答案 4 :(得分:3)

您没有提及特定平台,因此答案可能是肯定的。但对于大多数常见平台,答案是否定的。


答案 5 :(得分:3)

对于Microsoft C ++,如果可执行文件引用了运行时DLL的调试版本,则可以假设已关闭优化。但这不是保证。

答案 6 :(得分:2)

根据我的经验,只有可靠的方法是检查反汇编本身如果你不了解编译器。首先尝试使用您喜欢的工具转储所有字符串,并在链接的库名称和导出的符号中查找线索。如果做不到这一点,寻找常见的指令序列,这些指令序列在堆栈帧序言和有序且易于理解的寄存器使用等优化之后大多被抛弃。如果像ollydbg这样的工具对函数的开始和结束位置感到困惑,那么很有可能使用优化编译二进制文件。

检查反编译器的输出也可能有所帮助。上次我检查时,他们并不比反汇编更好,但事情可能会有所改善。

答案 7 :(得分:2)

如果你还没有这样做,我会感到惊讶,但是:

如果您“信任”构建系统,请将用于构建的选项放入可以从正在运行的程序中提取的字符串中。 (即,从--version或关于对话框)

由于构建系统损坏,这不会检测到非优化对象文件进入构建的问题,但它可以让您轻松区分开发人员构建与发布版本。

然后,您的发布过程可以包括检查版本以确保它不是调试/非优化的。

答案 8 :(得分:1)

我很确定没有办法。除非您具有内部函数的特定知识,否则您可以检查代码序列是否最佳。

答案 9 :(得分:0)

也许您可以使用不同的指标来确定您是否正在部署正确的二进制文件。 e.g。

  • 调试信息:也许您的发布二进制文件的不同之处在于它们没有调试信息?您可以使用elfdump或gdb进行检查。
  • 剥离:如果您的发布二进制文件被剥离,您可以使用“file”来检查此