gcc 使用Xcode构建的静态库中的符号隐藏

nkcskrwz  于 2023-10-19  发布在  其他
关注(0)|答案(3)|浏览(205)

我试图弄清楚我是否可以构建一个静态库,隐藏其所有内部对象和函数等,除了我想要导出的接口。我正在尝试使用Xcode(gcc 4.2)。
我在一些C++类上使用了__attribute__((visibility("hidden")))属性,每个this documentation。我还定义了一些小的辅助C函数作为文件本地的(静态的),等等。
然而,当我在产生的.a库文件上运行strings时,即使在Release配置中编译,我仍然会看到表面上隐藏的类的名称,以及它们的方法名称,甚至文件本地函数的名称也散落在那里。
我已经将-fvisibility=hidden甚至-fno-rtti添加到gcc标志中。虽然这减少了一些字符串,但类名、方法名和静态函数名仍然以普通或变形但可读的形式存在。
有没有一种可靠的方法可以让编译器构建这些东西,而不需要将所有内部内容的字符串名称发送到二进制文件中?它不应该是必要的,有任何外部客户端链接。
(To澄清:我问的是内部命名的混淆,而不是字面导出绑定的需要。我感到不安的是,所有的内部工作都可以通过strings命令看到,不管这些符号是否正式导出。
谢谢.

sycxhyv7

sycxhyv71#

隐藏内部名称需要一些简单的Xcode构建设置,并且通常不需要修改源代码或更改构建产品的类型。
1.通过执行单对象预链接消除模块之间所需的任何内部符号。将Xcode构建设置“Perform Single-Object Prelink”设置为Yes(GENERATE_MASTER_OBJECT_FILE=YES)。这会导致ld使用-r标志运行。
1.确保设置“Strip Style”设置为“Non-global symbols”(STRIP_STYLE=non-global),这将-x传递给ld。
1.如果启用了后处理(这不是默认设置),剥离实际上只在静态库上执行。将Xcode构建设置“Deployment Postprocessing”设置为yes。(DEPLOYMENT_POSTPROCESSING=YES)。同时确保“使用单独的条带”设置为“是”(不总是默认值)(SEPARATE_STRIP=YES)。
1.如果除了本地符号之外,如果您需要删除一些全局符号,您可以使用Xcode构建设置“Single-Object Prelink Flags”(即-unexported_symbols_list file命令或-unexported_symbol symbol命令)将-unexported_symbols_list file命令提供给单对象预链接步骤。PRELINK_FLAGS)。(或者,这也可以通过使用strip命令的附加选项来完成,在Xcode构建设置“附加strip标志”下,例如:-R file选项,与ld的unexported_symbols_list选项具有相同的效果)。

ryhaxcpt

ryhaxcpt2#

在静态库中隐藏符号的主要技巧是生成一个可重定位对象文件(与静态库归档文件相反,静态库归档文件仅由单个.o文件的集合组成)。要构建一个可重定位的对象文件,你需要在XCode中选择你的目标作为Bundle(而不是“可可Touch Static Library”)。Bundle目标出现在OS X模板下,如果您正在为iOS构建,则可以在Build设置中将其目标设置为iOS。
正确设置目标后,以下步骤将有助于正确隐藏符号:
1.在构建设置中将“默认情况下隐藏的符号”选项设置为“是”。这可以确保文件中编译的所有符号都标记为私有。
1.由于这是一个库,您确实需要将一些符号保持为公共。你应该把你想要公开可见的函数的代码放在单独的文件中,并使用-fvisibility=default标志编译这些文件(你可以在Xcode中为单个文件设置这个标志“Build Phases > Compile Sources > -- Compiler Flags”)。或者,您可以使用__attribute__((visibility("default")))指令在您希望可见的函数/类的名称前面加上前缀。
1.在X-code项目中的链接设置下,将Mach-O类型设置为“Relocatable Object File”。这意味着所有.o文件将被重新链接以生成单个目标文件。当.o文件链接到一个文件中时,此步骤有助于将所有符号标记为私有。如果你构建一个静态库(即一个.a文件)这个重新链接步骤不会发生,所以符号永远不会被隐藏。因此,选择一个可重定位的对象文件作为目标是至关重要的。
1.即使将符号标记为私有,它们仍然会显示在.o文件中。您需要启用剥离以删除私有符号。这可以通过在构建设置中将“剥离的链接产品”设置设置为“是”来完成。设置此选项将在目标文件上运行strip -x命令,该命令将从目标文件中删除专用符号。
1.通过在构建过程生成的最终可重定位目标文件上运行nm命令,再次检查所有内部符号是否消失。
上述步骤将帮助您从nm命令中删除符号名称。如果在对象文件上运行strings命令,您仍然会看到一些函数名和文件名(由于某些字符串和对象名是通过异常编译的)。我的一个colleagues有一个脚本,它通过查看二进制部分并重命名这些字符串来重命名其中的一些符号。我把它贴在这里供你用途:https://gist.github.com/varungulshan/6198167。您可以将此脚本作为Xcode中的额外构建步骤添加。

ev7lccsx

ev7lccsx3#

根据前面的答案,我不太清楚如何在Linux命令行环境中隐藏静态库中的符号,所以我只是在这里发布我的解决方案供后人参考(考虑到这是google上针对该问题的最佳结果之一)。
假设你有两个.c文件:

// f1.c
const char *get_english_greeting(void)
{
  return "hello";
}

__attribute__((visibility("default")))
const char *get_greeting(void)
{
  return get_english_greeting();
}

// f2.c
#include <stdio.h>
const char *get_english_greeting(void);

__attribute__((visibility("default")))
void print_greeting(void)
{
  puts(get_english_greeting());
}

您希望将这两个文件转换为一个静态库,同时导出get_greetingprint_greeting,但不导出get_english_greeting,因为您希望在整个库中使用它,所以不希望将get_english_greeting设置为静态。
以下是实现这一目标的步骤:

gcc -fvisibility=hidden -c f1.c f2.c
ld -r f1.o f2.o -o libf.o
objcopy --localize-hidden libf.o
ar rcs libf.a libf.o

现在这个工作:

// gcc -L. main.c -lf
void get_greeting(void);
void print_greeting(void);
int main(void)
{
  get_greeting();
  print_greeting();
  return 0;
}

而这并不:

// gcc -L. main.c -lf
const char *get_english_greeting(void);
int main(void)
{
  get_english_greeting();
  return 0;
}

对于后者,你会得到这个错误:

/tmp/ccmfg54F.o: In function `main':
main.c:(.text+0x8): undefined reference to `get_english_greeting'
collect2: error: ld returned 1 exit status

这正是我们想要的
请注意,隐藏的符号名称在静态库中仍然可见,但链接器将拒绝在静态库之外与它们链接。要完全删除符号名称,您需要剥离和混淆。

相关问题