【发布时间】:2015-08-25 18:56:36
【问题描述】:
几年前我问a question 如何减小可执行文件的大小。使用 MinGW 编译器,剥离符号(-s 选项)有助于减少 50% 以上的大小。
为什么剥离不是默认的 - 那么在某些情况下是否有充分的理由不剥离符号呢?我想更深入地理解它:今天,我只是隐约知道链接库中涉及符号。它们是否需要在可执行文件中,它们会影响执行速度吗?
【问题讨论】:
标签: c++ c compilation mingw
几年前我问a question 如何减小可执行文件的大小。使用 MinGW 编译器,剥离符号(-s 选项)有助于减少 50% 以上的大小。
为什么剥离不是默认的 - 那么在某些情况下是否有充分的理由不剥离符号呢?我想更深入地理解它:今天,我只是隐约知道链接库中涉及符号。它们是否需要在可执行文件中,它们会影响执行速度吗?
【问题讨论】:
标签: c++ c compilation mingw
我无法想象它们会以任何明显的方式影响执行速度,但理论上,进程映像中可能会有微小数量的缓存未命中。
您希望在调试文件时将符号保留在文件中,以便查看您所在的函数、检查变量的值等等。
但符号会使文件变大:可能会变大一个很多。因此,您不希望将二进制文件中的符号放在小型嵌入式设备上。 (我说的是真正的嵌入式设备,而不是 21 世纪拥有 9,000GB 存储空间的 Raspberry Pi!)
我通常会剥离所有发布版本,而不剥离任何调试版本。这会降低来自客户的核心转储的用处,但您始终可以保留一份未剥离的发布版本的副本并针对它调试您的核心。
我确实听说过一家公司的政策是即使在发布版本中也不会剥离符号,因此他们可以将核心直接加载到调试器中。这对我来说似乎有点抽象泄漏,但无论如何。他们的呼唤。
当然,如果你真的想要,你可以不用它们自己分析程序集。但这太疯狂了……
【讨论】:
MinGW 是“Minimalist GNU for Windows”的缩写;因此,编译器套件是 GCC ... GNU Compiler Collection。自然,此编译器套件将倾向于遵守 GNU 编码标准,该标准要求 每个 构建都应是“调试构建”......即它应包括调试符号和链接所需的符号. (这是合乎逻辑的,因为“调试构建”对应用程序开发人员来说比所谓的“发布构建”更有用几个数量级)。此外,区分“调试构建”和“发布构建”模式的构建系统比仅关注“调试构建”的构建系统更复杂——可能要复杂得多。
在 GNU 构建模型中,无论如何都不需要“发布构建”。 “发布”不是在构建时创建的;它是在安装时创建的——通常作为“分阶段”安装,从中创建一个发布包。 GNU 工具链包括一个strip 命令和一个install 命令,当创建分阶段安装以打包为发行版时,它可以动态剥离“调试构建”,(或者在适当的位置,如果你希望),所以真的没有必要用“发布构建”细节来混淆构建系统;只需预先创建一个“调试版本”,然后在事件发生后将其剥离,以便在需要时将其转换为有效的“发布版本”。
由于您的 MinGW 工具链本质上是一个 GNU 工具链,它完全支持这个 GNU 构建模型。
【讨论】:
gcc -g ...,在发布版本中可以运行gcc -s -O2。
gcc -s ... 比使用gcc -g ... 构建并随后剥离有什么优势?此外,如果您在构建系统中使用区分调试和发布构建的逻辑,那么您将不必要地让它变得比它需要的更复杂(因此更容易出错);显然,这只是......愚蠢。
gcc -g ... 与 gcc -s -O2。
-g 和 -O2 是不互斥的。无论如何,默认的 GNU 构建使用 gcc -g -O2 ...。这对于大量调试可能不是最有用的,但它确实保留了在发生崩溃时生成回溯的能力;使用gcc -s ... 时,您将失去这一点,无论您是否指定了任何优化级别。
没有充分的理由去除符号。例如,通过剥离符号,您将消除收集进程堆栈的可能性——这是一个非常有用的功能。
编辑。这里的几个人似乎对调试符号和“通用”符号之间的区别感到困惑。第一个仅在为编译器提供 -g(或类似)选项时生成,并且通常仅用于调试构建(尽管也可以与优化构建一起使用)。常规符号是诸如函数名称之类的东西,由编译器生成,以便可以链接目标文件或加载 .so 文件。有几个非常有用的非侵入性工具可用于非调试运行的可执行文件,这些可执行文件依赖于未剥离的符号。
【讨论】: