虽然我不是 gcc 和 MSVC 链接器选项方面的专家,但我想我可以给你一个明智的答案:
共享链接和静态链接是非常不同的概念。共享链接是在运行或加载时针对特殊准备好的符号(即函数)完成的。共享链接不是标准化的,大多数编译器都要求程序员在运行/加载时显式标记稍后链接的函数/符号。静态链接是在编译或链接时链接。(编译 < 链接 < 加载 < 运行时间。“<” == 稍后)。静态库只是一种组织代码的方式,通常是所谓的翻译单元概念中的 C++ 东西,以使编译器的工作包更小,因此每个翻译单元都会被编译成一个目标文件。当翻译一个普通的可执行文件时,它只会编译一些对象文件,然后只将使用过的东西/符号从对象文件中复制到可执行文件(.exe)中。通常不会在可执行文件中找到未使用的符号。静态库只是说明编译器和链接器将所有符号(无论使用情况如何)放入像可执行文件这样的大文件中的一种方式。因为无论是否使用它们都包含所有内容,因此它们被称为档案。
共享库更像是一个可执行文件,通过将符号设为公开或非隐藏或导出,您说“此符号将被此共享库的用户使用”,因此编译器做好了可以轻松调用的准备。此共享函数可能使用来自目标文件或静态库/档案的其他符号。共享符号未传递使用的符号将被省略,即不会复制到共享库 (.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,您会看到 onlyhidden_func2
在 gcc 和 clang 二进制文件中不作为符号可见,但是,因为该inline
标志只是编译器的提示,它仍然可以在 MSVC 二进制文件中找到。