【问题标题】:Is there any reason why not to strip symbols from executable?有什么理由不从可执行文件中删除符号吗?
【发布时间】:2015-08-25 18:56:36
【问题描述】:

几年前我问a question 如何减小可执行文件的大小。使用 MinGW 编译器,剥离符号(-s 选项)有助于减少 50% 以上的大小。

为什么剥离不是默认的 - 那么在某些情况下是否有充分的理由不剥离符号呢?我想更深入地理解它:今天,我只是隐约知道链接库中涉及符号。它们是否需要在可执行文件中,它们会影响执行速度吗?

【问题讨论】:

    标签: c++ c compilation mingw


    【解决方案1】:

    我无法想象它们会以任何明显的方式影响执行速度,但理论上,进程映像中可能会有微小数量的缓存未命中。

    您希望在调试文件时将符号保留在文件中,以便查看您所在的函数、检查变量的值等等。

    但符号会使文件变大:可能会变大一个很多。因此,您不希望将二进制文件中的符号放在小型嵌入式设备上。 (我说的是真正的嵌入式设备,而不是 21 世纪拥有 9,000GB 存储空间的 Raspberry Pi!)

    我通常会剥离所有发布版本,而不剥离任何调试版本。这会降低来自客户的核心转储的用处,但您始终可以保留一份未剥离的发布版本的副本并针对它调试您的核心。

    我确实听说过一家公司的政策是即使在发布版本中也不会剥离符号,因此他们可以将核心直接加载到调试器中。这对我来说似乎有点抽象泄漏,但无论如何。他们的呼唤。

    当然,如果你真的想要,你可以不用它们自己分析程序集。但这太疯狂了……

    【讨论】:

    • 显然,您不必在运行可执行文件时运行 pstack。
    • 正是我所说的。如果你不得不这样做,你就不会脱衣服。
    • 而且你似乎不知道调试符号和符号之间的区别。
    • @Olaf,那里有非常不同的环境。例如,在某些情况下,开发人员会访问他们的客户环境并在那里进行故障排除。 ;)
    • 对于非平凡的系统,尤其是分布式/网络化的系统,在 alpha 测试期间未发现的客户配置/环境中几乎不可避免地会出现错误。发生这种情况时,您将需要从调试器和记录器中获得的所有帮助。我经常保留一个调试版本(所有符号、范围检查等都打开,优化关闭等),方便换入。
    【解决方案2】:

    MinGW 是“Minimalist GNU for Windows”的缩写;因此,编译器套件是 GCC ... GNU Compiler Collection。自然,此编译器套件将倾向于遵守 GNU 编码标准,该标准要求 每个 构建都应是“调试构建”......即它应包括调试符号和链接所需的符号. (这是合乎逻辑的,因为“调试构建”对应用程序开发人员来说比所谓的“发布构建”更有用几个数量级)。此外,区分“调试构建”和“发布构建”模式的构建系统比仅关注“调试构建”的构建系统更复杂——可能要复杂得多。

    在 GNU 构建模型中,无论如何都不需要“发布构建”。 “发布”不是在构建时创建的;它是在安装时创建的——通常作为“分阶段”安装,从中创建一个发布。 GNU 工具链包括一个strip 命令和一个install 命令,当创建分阶段安装以打包为发行版时,它可以动态剥离“调试构建”,(或者在适当的位置,如果你希望),所以真的没有必要用“发布构建”细节来混淆构建系统;只需预先创建一个“调试版本”,然后在事件发生后将其剥离,以便在需要时将其转换为有效的“发布版本”。

    由于您的 MinGW 工具链本质上是一个 GNU 工具链,它完全支持这个 GNU 构建模型。

    【讨论】:

    • 通常可以使用 GCC 以及 GCC+MinGW 进行单独的调试和发布版本。例如,在调试版本中可以运行gcc -g ...,在发布版本中可以运行gcc -s -O2
    • @pts:你确实可以做到这一点,但这完全不符合 GNU 构建模型的典型特征。务实地说,gcc -s ... 比使用gcc -g ... 构建并随后剥离有什么优势?此外,如果您在构建系统中使用区分调试和发布构建的逻辑,那么您将不必要地让它变得比它需要的更复杂(因此更容易出错);显然,这只是......愚蠢。
    • 它们是等价的,没有实际优势。但我不是提议这两个。我提议 gcc -g ...gcc -s -O2
    • @pts: -g-O2互斥的。无论如何,默认的 GNU 构建使用 gcc -g -O2 ...。这对于大量调试可能不是最有用的,但它确实保留了在发生崩溃时生成回溯的能力;使用gcc -s ... 时,您将失去这一点,无论您是否指定了任何优化级别。
    【解决方案3】:

    没有充分的理由去除符号。例如,通过剥离符号,您将消除收集进程堆栈的可能性——这是一个非常有用的功能。

    编辑。这里的几个人似乎对调试符号和“通用”符号之间的区别感到困惑。第一个仅在为编译器提供 -g(或类似)选项时生成,并且通常仅用于调试构建(尽管也可以与优化构建一起使用)。常规符号是诸如函数名称之类的东西,由编译器生成,以便可以链接目标文件或加载 .so 文件。有几个非常有用的非侵入性工具可用于非调试运行的可执行文件,这些可执行文件依赖于未剥离的符号。

    【讨论】:

    • 当然有。它使可执行文件更小。您可能无法在目标平台上安装带有调试符号的可执行文件。
    • 调试符号和符号是有区别的。不要混淆他们。
    • 这是一个没有论据的不平衡答案,只是一个激进的断言。
    • 有一些命令可以获取正在运行的进程的堆栈跟踪。 Solaris/Linux 上的 pstack,AIX 上的 procstack。我相信,任何现代 Unix 都有它们的味道。当您需要解决运行过程中的问题时,它们非常方便,连同 truss/proctruss/d-truss/... 没有符号它们就无法工作。
    • 一个很好的理由可能是阻碍逆向工程,由于公司的政策,这在商业产品中可能尤其需要。
    猜你喜欢
    • 2018-07-12
    • 2011-09-03
    • 1970-01-01
    • 2017-01-26
    • 2012-03-14
    • 2021-12-21
    • 2018-08-26
    • 1970-01-01
    • 2012-12-19
    相关资源
    最近更新 更多