【问题标题】:SCCS "what" strings not optimised away by the compilerSCCS “what” 字符串没有被编译器优化掉
【发布时间】:2009-01-27 17:34:05
【问题描述】:

我们尝试在二进制对象中嵌入 what 字符串,以便我们可以看到已部署的可执行文件或共享库的版本号。通常我们将标准 CVS Id 信息嵌入到这个 what 字符串中。例如,我们可能会嵌入:

const char cvsid[] = "@(#)OUR_TEAM_staging_remap_$Revision: 1.30 $ $Name:  $";

在 C 代码中。

从人(1)什么:

what 实用程序在每个文件名中搜索出现的 SCCS get 命令的模式@(#)(参见 sccs-get(1)) 替换 @(#) ID 关键字,并打印以下内容 最多一个 "、>、NEWLINE、\ 或 NULL 字符。

该变量只有一个实例,并且从未被引用。有人建议这可能会被编译器优化掉。

多年来,我一直在 C 和 C++ 以及各种编译器中使用这种技术,但我还没有看到什么字符串被优化掉了。

有人知道为什么它们没有被优化掉吗?

【问题讨论】:

    标签: c compiler-optimization


    【解决方案1】:

    直到最近(我在 2005 年年中发现了这个问题),才有可能使用:

    static const char sccs[] = "@(#)%W% %E%";
    

    或源代码中的类似内容,GCC 和大多数其他编译器不会对其进行优化。大约从那个时候开始发布 GCC(可能是 GCC 4.0.x,起源于 2005 年 4 月),这些常量字符串被排除在二进制文件之外。因此,我不得不四处修改我的源代码以使变量在外部可见。编译器不可能单独查看目标文件并得出未使用该字符串的结论,因为可以想象文件之外的某些内容可能会引用它。所以,我的文件现在包含:

    #ifndef lint
    extern const char jlss_id_filename_c[];
    const char jlss_id_filename_c[] = "@(#)$Id$";
    #endif /* lint */
    

    好的 - 这是一个混合体;我确实使用 RCS 来存储源代码,但我仍然更喜欢 what 而不是 ident 来识别文件 - 另外我有自己的黑客 what 可以同时执行 whatident 以及我的一些调整自己的。但我在一些文件中都有声明 - 不是全部 - 以及所有文件中的定义。 (在一些现在不记得的警告标志下,当变量在声明之前定义时,我收到了警告。可能是 GCC 中的一个更改解决了这个问题;我不确定了。)

    当我创建一个新文件时,我的模板生成器将“filename_c”替换为正在生成的文件的适当名称。标题也是如此——尽管标识字符串只嵌入一个文件中以避免多个定义。

    我更喜欢带有静态常量的旧系统 - 但这已经为我工作了 3 年多。

    【讨论】:

    • 如果我们不走运,你将可以再使用这个技巧 3 年,直到 google gcc 的人在他们的 [WHOPR 努力[1] 取得进展,这将使你的技巧过时;- ) [1]:gcc.gnu.org/projects/lto/whopr.pdf
    • 我想到那时,我必须找到一种方法来打印出任意名称列表,或者以其他方式使用。在可执行文件中必须有其他人想要这样的元数据; GCC 应该提供一种机制来标记“这未使用但仍必须出现在二进制文件中”。
    【解决方案2】:

    这通常不会发生,因为无关字符串的成本很低,并且在这种情况下很有用(例如,存储一堆字符串资源,只在代码中实际引用第一个)。

    【讨论】:

    • +1 编译器可能不知道您没有以其他方式引用它。此外,将其添加到数据部分没有任何害处。
    【解决方案3】:

    它们可能不会被优化掉,因为你的编译器知道这样的字符串可以用于这些目的。

    当然,编译器完全可以优化掉它,只要程序的行为,更确切地说是可观察的行为,没有改变。这意味着对 volatile 的写入和读取顺序,并且对库函数的调用不会改变。

    通过在您的应用程序中优化这样的字符串,我想这种行为不会改变。但是编译器想要可用并且尽量不以用户的方式进行攻击。这就是为什么它们也包含有用的扩展。如果您想确保它不会偶尔被优化,也许可以查看编译器扩展。 GCC 有一个unused 属性,这使得它不会对未使用的对象发出警告。也许那个或类似的东西可以帮助你变量没有被优化掉。

    从语言的角度来看,虽然有一个实用程序可以强制编译器保留它。

    编辑:有一篇关于该主题的 usenet 帖子here,提供了有用的答案。

    【讨论】:

      【解决方案4】:

      (是的,我知道这个问题很久以前就有人问过了。但是这种新的回答方式是可用的,所以....)

      gcc(至少在 3.3 及以下版本)中,现在有编译器指令__attribute__((unused)) 将变量标记为“已知它可能不被引用”以抑制警告,和__attribute__((used)) 将其标记为已使用(因此不是优化的候选对象),即使没有其他代码实际引用它。

      所以这可能适合你:

      static const char what_ident[] __attribute__((used)) = "@(#) $Id$";
      

      如果linker 仍然对其进行优化,那么您可能需要将其放在标记为 keep 的部分中,而不管链接时间引用如何。

      static const char what_ident[] __attribute__((section("what"), used)) = "@(#) $Id$";
      

      并添加 gcc 选项 -Wl,-bkeepfile:file.o,这样链接器就不会抑制 file.c 输出中未引用的部分。

      【讨论】:

        【解决方案5】:

        Microsoft 的 Visual C++ 2005 有一个链接器选项,它应该控制它对未使用数据的处理方式:/OPT:UNREF 强制链接器保留未使用的数据,/OPT:REF 允许它删除它。

        但是,在我的简单测试中,该选项对语句没有影响

        static char VersionString[] = "HELLO_WORLD 2.0";
        

        无论标志如何,该字符串都出现在发布和调试二进制文件中。

        【讨论】:

        • 这可能取决于您使用的优化级别。
        【解决方案6】:

        没有“static”关键字,变量不能被优化掉,因为另一个模块可能会声明一个对它的引用(使用extern)。由于 C/C++ 通常一次编译一个文件,因此编译器无法知道是否存在外部引用。

        通过添加 static 关键字,您可以告诉编译器该名称仅在编译中可见,使用和优化它成为可能。

        如果对象格式允许,我认为链接器可以检测到未使用的全局变量并将其优化掉,但我不确定是否有人这样做。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-05-16
          • 2013-06-07
          • 1970-01-01
          • 2012-05-23
          • 2010-11-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多