【问题标题】:Limit visibility of symbols in static library (MSVC/Visual C++)限制静态库中符号的可见性 (MSVC/Visual C++)
【发布时间】:2020-10-02 03:41:06
【问题描述】:

gcc 有一个 -fvisibility 选项,您可以在其中定义哪些函数对链接到您的库的任何人“可见”。虽然这主要用于共享库,但它似乎也可以用于静态库(参见How to apply -fvisibility option to symbols in static libraries?)。

是否可以阻止将我的库与 MSVC 一起使用的人使用某些功能?我不希望我的“内部”函数都在单个翻译单元内,因此“静态”或使用匿名命名空间将不起作用(AFAIK)。

我怎样才能做到这一点?请注意,我特别指的是使用 MSVC 构建的静态 .lib 库。

谢谢

编辑:我与其他人共享我的库,因此这就是我想隐藏某些符号的原因。

【问题讨论】:

  • IIRC MSVC 现在可以只检查库的内容,然后直接转到原始 OBJ 文件,无需解压库内容。 Use Library Dependency Inputs
  • 另外,“我的内部功能都在一个翻译单元内”? MSVC 的翻译单元是一个 OBJ 文件。将 OBJ 文件聚合到 LIB 文件中不会合并翻译单元。因此它也不会合并各自的匿名命名空间。
  • 如果您确实需要阻止对某些函数的访问,您可以使用两个单独的标题构建库:一个用于“公共”内容,一个用于内部构建。您甚至可以混淆您的“私有”函数,给它们起类似(比如)“FF03A370”的名称,然后在内部标题中有一个#define MyFunction1 FF03A370 行(对于每个) - 这样,它会很棘手(至少可以说)供公共用户捕获您的私人功能。
  • @MSalters 我不明白你的第一条评论与我的问题有什么关系,你能解释一下吗?我需要能够在其他用户无法访问我的内部功能的情况下共享我的 .lib 文件。关于您的第二条评论 - 这正是问题所在。匿名命名空间将不起作用,因为我需要能够(内部)调用不同翻译单元中的函数,而无需为 .lib 文件用户提供外部链接。
  • @AdrianMole 代码混淆绝对是一种选择,但最终我想首先阻止对这些功能的访问。如果我做不到这一点,我很可能会尝试一下。谢谢

标签: c++ c visual-c++


【解决方案1】:

不,这是不可能的。 Windows“LIB”文件只是 OBJ 文件(翻译单元,TU)的集合。您可以使用LIB.EXE /EXTRACT:member.obj Library.LIB 查看这些部分。您的客户会将他们的 TU 和您的 TU 链接在一起。这还包括您的 TU 相互链接的部分彼此

根据您的计划,将您的 TU 相互关联变得不可能。您的 TU 的符号要么可见,要么不可见。

您可能想查看 MSVC 的 Unity/Jumbo 构建。

【讨论】:

    【解决方案2】:

    虽然我不是 gcc 和 MSVC 链接器选项方面的专家,但我想我可以给你一个明智的答案:

    共享链接和静态链接是非常不同的概念。共享链接是在运行或加载时针对特殊准备好的符号(即函数)完成的。共享链接不是标准化的,大多数编译器都要求程序员在运行/加载时显式标记稍后将链接到的函数/符号。 静态链接是在编译或链接时链接。 (编译

    共享库更像是一个可执行文件,通过将符号设为公共或非隐藏或导出,您说“此符号将被此共享库的用户使用”,因此编译器准备它可以被调用容易地。此共享函数可能使用来自目标文件或静态库/档案的其他符号。共享符号不传递使用的符号将被省略,即不会复制到共享库(.so .dll)中

    据我了解,gcc 为静态库/符号提供的隐藏功能似乎只是为了防止 gcc 将共享符号使用的内部/静态非标记符号标记为共享可链接。

    但是,如果使用了该共享库,您仍会在该共享库的主体中找到这些内部符号。然而,它们通常不能像“官方”导出的符号那样简单地使用。通常,您使用的代码将在使用它的程序中。您可以尝试混淆函数或符号的名称,通常由于您不会将标头分配给此内部符号,因此人们将很难正确猜测 ABI 或 API。但是使用简单的反汇编程序作为逆向工程工具仍然是可能的。

    但是我推荐另一种策略:C++ 知道“内联”的概念,通过说编译器不生成额外的目标文件,而只是将函数的主体复制给曾经使用它的人,你会变得非常困难来识别这个函数,如果不修改它就不能从外部调用(因为它缺少被调用者的机器代码)

    如果冒昧地给你一个例子,它也演示了 gcc 隐藏标志如何不适用于静态符号:

    #include <cstdio>
    
    #if __GNUC__ >= 4
    #define hidden __attribute__ ((visibility ("hidden")))
    #else
    #define hidden
    #endif
    
    
    hidden int hidden_func(int x, int y) {
        return x * y;
    }
    
    inline int hidden_func2(int x, int y) {
        return x * y;
    }
    
    int normal_func(int x, int y) {
        return hidden_func(x,y) * hidden_func2(x,y);
    }
    
    int main()
    {
        int x{}, y{};
        std:: scanf("%d %d", &x, &y);
        std::printf("%d\n", normal_func(x,y));
        std::printf("%d\n", hidden_func(x,y));
    }
    

    在生成的机器代码中,在这里发布https://godbolt.org/z/tFpdpE,您会看到只有hidden_func2 在 gcc 和 clang 二进制文件中作为符号不可见,但是,由于 inline 标志只是提示编译器,它仍然可以在 MSVC 二进制文件中找到。

    【讨论】:

      猜你喜欢
      • 2017-02-20
      • 2010-09-28
      • 2013-05-22
      • 2022-01-22
      • 2015-10-18
      • 2023-03-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多