【问题标题】:How to hide a global variable, which is visible across multiple files?如何隐藏在多个文件中可见的全局变量?
【发布时间】:2013-03-04 07:39:20
【问题描述】:

我正在编写一个 C(共享)库。它最初是一个单独的翻译单元,我可以在其中定义几个 static 全局变量,对外部模块隐藏。

现在库已经增长,我想将模块分成几个较小的源文件。问题是现在我对提到的全局变量有两个选择:

  1. 在每个源文件中都有私有副本,并通过函数调用以某种方式同步它们的值 - 这会很快变得非常丑陋。

  2. 删除static 定义,因此使用extern 在所有翻译单元之间共享变量 - 但是现在链接到库的应用程序代码可以访问这些全局变量,如果在那里进行了所需的声明。

那么,有没有一种巧妙的方法让私有全局变量在多个特定翻译单元之间共享?

【问题讨论】:

    标签: c linux global-variables shared-libraries private


    【解决方案1】:

    您想要 GCC 的 visibility attribute 扩展。

    实际上,类似:

     #define MODULE_VISIBILITY  __attribute__ ((visibility ("hidden")))
     #define PUBLIC_VISIBILITY  __attribute__ ((visibility ("default")))
    

    (您可能想要#ifdef 上述宏,使用一些配置技巧à la autoconf 和其他自动工具;在其他系统上,您只会有空定义,例如@987654328 @等...)

    然后,声明一个变量:

    int module_var  MODULE_VISIBILITY;
    

    或函数

    void module_function (int) MODULE_VISIBILITY;
    

    然后您可以在共享库内部使用module_var 或调用module_function,但不能在外部使用。

    另请参阅 GCC 的 -fvisibility 代码生成选项。

    顺便说一句,您也可以使用-Dsomeglobal=alongname3419a6 编译整个库并照常使用someglobal;要真正找到它,您的用户需要将相同的预处理器定义传递给编译器,并且您可以使名称 alongname3419a6 随机且不太可能,以使冲突不太可能发生。


    PS。这种可见性特定于 GCC(并且可能特定于 ELF 共享库,例如 Linux 上的那些)。如果没有 GCC 或在共享库之外,它就无法工作......所以它是非常特定于 Linux 的(即使其他一些系统,也许是带有 GCC 的 Solaris,也有它)。可能其他一些编译器(LLVM 中的clang)也可能支持 在 Linux 上用于 共享库(不是静态库)。实际上,真正的隐藏(对单个共享库的几个编译单元)主要由链接器完成(因为ELF 共享库允许这样做)。

    【讨论】:

    • 是什么让您认为 OP 正在使用 GCC 并且不需要代码可移植性?
    • 我确实告诉过它是 GCC 扩展。我添加了一个重复的 PS。
    • 是的,你做到了。我评论是因为我不认为 Stack Overflow 应该是一个 GCC 论坛,现在每个人似乎都隐含地假设 GCC。
    • 它实际上变得足够有用,可以被其他编译器支持。它实际上主要由链接器实现:编译器只将属性传递给链接器,链接器完成真正的隐藏工作。
    • 谢谢。我不知道这些属性。我确实使用 GCC,顺便说一句。不过,我不确定我是否应该在这个库方面坚持使用 Linux 私有技巧。
    【解决方案2】:

    最简单(“老派”)的解决方案是简单地不在预期的公共标头中声明变量。

    将您的库头文件拆分为“header.h”和“header-internal.h”,并在后者中声明内部内容。

    当然,您还应该注意保护库全局变量的名称,以免它与用户代码冲突;大概你已经有一个前缀,用于此目的的函数。

    您还可以将变量包装在 struct 中,以使其更清晰,因为此时只有一个实际符号是全局可见的。

    【讨论】:

    • “你可以保护代码免受墨菲的攻击,但永远不能保护马基雅维利。”
    • 谢谢。当然,您不应该将它放在公共头文件中,但是,正如我在问题中提到的,您仍然可以在应用程序中使用正确的声明访问该变量。我尽量保持变量名称简洁明了,因此用户定义相似名称的概率,尤其是。在使用该库的代码的上下文中,略高于零。这就是为什么我要保护它。不过,将它们放在struct 中是我可以用来进一步降低这种可能性的一个想法。
    • @ysap 如果您将整个库链接到“某物”,这不是原始源代码,所有此类命名空间问题都会消失,您的库的用户将只需要担心公共标头.
    • 好吧,用户应用程序 IS 源代码(至少,这是一般情况。我不能假设用户只是链接预编译的对象)。
    【解决方案3】:

    如果你真的想尽可能地隐藏信息,你可以用伪装的结构来混淆。例如在头文件中,

    struct data_s {
       void *v;
    };
    

    在你的源代码中的某个地方:

    struct data_s data;
    struct gbs {
       // declare all your globals here
    } gbss;
    

    然后:

    data.v = &gbss;

    然后您可以通过以下方式访问所有全局变量:((struct gbs *)data.v)->

    【讨论】:

    • 谢谢。基本上是@unwind 建议的。混淆是直接的解决方案,但会使我的代码更难看。
    • @ysap 您可以使用宏使它看起来更整洁。这当然会让事情变得更加困难,但要找到全局变量要困难得多。
    【解决方案4】:

    我知道这不是您真正想要的,但是您可以将全局变量保留为静态并将它们分成多个源文件。

    在同样声明为静态的源文件中复制写入相应静态变量的函数。

    声明读取静态变量的函数,以便同一模块的外部源文件可以读取它的值。

    在某种程度上使其不那么全球化。如果可能,将大文件拆分成小文件的最佳逻辑是根据数据做出决定。

    如果无法做到这一点,那么您可以将所有全局变量作为静态变量放入一个源文件中,并通过函数从模块的其他源文件中访问它们,使其成为官方的,因此如果有人正在操纵您的全局变量至少你知道怎么做。 但是,使用@unwind 的方法可能会更好。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-14
      • 1970-01-01
      相关资源
      最近更新 更多