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

时间:2010-07-18 17:15:39

标签: xcode gcc obfuscation symbols

我正在试图弄清楚是否可以构建一个隐藏其所有内部对象和函数等的静态库,除了我要导出的接口。我正在尝试使用Xcode(gcc 4.2)。

我在每个this documentation的某些C ++类中使用了__attribute__((visibility("hidden")))属性。我还将小辅助C函数定义为文件本地(静态)等。

但是,当我在生成的.a库文件上运行strings时,即使在Release配置中编译,我仍然会看到表面上隐藏的类的名称,它们的方法名称,甚至名称文件本地函数也在那里散布。

我已将-fvisibility=hidden甚至-fno-rtti添加到gcc标记中。虽然这减少了一些字符串,但类名,方法名和静态函数名仍然以简单或错误但可读的形式存在。

有没有一种可靠的方法让编译器构建这些东西而不必将所有内部内容的字符串名称发送到二进制文件中?没有必要为任何外部客户端链接。

(澄清一下:我问的是内部命名的混淆,以及文字导出绑定的需求。我很遗憾通过strings命令可以看到所有内部工作,无论这些符号是否正式是否出口。)

感谢。

3 个答案:

答案 0 :(得分:48)

隐藏内部名称需要一些简单的Xcode构建设置,通常不需要修改源代码或更改构建产品的类型。

  1. 通过执行单对象预链接消除模块之间所需的任何内部符号。将名为“Perform Single-Object Prelink”的Xcode构建设置设置为Yes(GENERATE_MASTER_OBJECT_FILE = YES)。这会导致ld以“-r”标志运行。
  2. 确保设置“条带样式”设置为“非全局符号”(STRIP_STYLE =非全局符号),这会将“-x”传递给ld。
  3. 如果启用了后处理,则实际上只对静态库执行剥离(这不是默认值)。将Xcode构建设置“Deployment Postprocessing”设置为yes。 (DEPLOYMENT_POSTPROCESSING = YES)。还要确保“使用单独的条带”设置为“是”(并非始终为默认值)(SEPARATE_STRIP = YES)。
  4. 如果除了本地符号之外,如果需要删除某些全局符号,则可以在Xcode构建设置“附加条带标记”下为strip命令提供其他选项。例如。我通常使用条带“-R somefile”选项来提供一个文件,其中包含我想从全局符号表中删除的符号列表。

答案 1 :(得分:15)

在静态库中隐藏符号的主要技巧是生成可重定位目标文件(而不是静态库归档,它只包含单个.o文件的集合)。要构建可重定位目标文件,需要在XCode中选择目标作为Bundle(而不是“Cocoa Touch Static Library”)。 Bundle目标显示在OS X模板下,如果您要为iOS构建,则可以在Build设置中将其目标设置为iOS。

正确设置目标后,以下步骤将有助于使符号隐藏正确:

  1. 在构建设置中将“默认情况下隐藏的符号”选项设置为“是”。这可以确保文件中编译的所有符号都标记为私有。

  2. 由于这是一个库,您需要公开一些符号。您应该将要保留的函数的代码放在单独的文件中公开显示,并使用-fvisibility=default标志编译这些文件(您可以为各个文件设置此标志“Build Phases> Compile Sources> - Compiler标志“在Xcode中”。或者,您可以使用__attribute__((visibility("default")))指令为希望显示的函数/类的名称添加前缀。

  3. 在X代码项目的链接设置下,将Mach-O类型设置为“可重定位目标文件”。这意味着将重新链接所有.o文件以生成单个目标文件。当.o文件链接到一个文件中时,这一步有助于将所有符号标记为私有。如果您构建静态库(即.a文件),则不会发生此重新链接步骤,因此符号永远不会被隐藏。因此,选择可重定位目标文件作为目标非常重要。

  4. 即使将符号标记为私有,它们仍会显示在.o文件中。您需要启用剥离以摆脱私有符号。这可以通过在构建设置中将“剥离的链接产品”设置设置为“是”来完成。设置此选项会在目标文件上运行strip -x命令,该命令会从目标文件中删除私有符号。

  5. 通过在构建过程生成的最终可重定位目标文件上运行nm命令,仔细检查所有内部符号是否已消失。

  6. 以上步骤将帮助您摆脱nm命令中的符号名称。如果在目标文件上运行strings命令,则仍会看到一些函数名和文件名(由于某些字符串和对象名称是通过异常编译的)。我的一个colleagues有一个脚本,通过查看二进制部分并重命名这些字符串来重命名其中一些符号。我已在此处发布,供您使用:https://gist.github.com/varungulshan/6198167。您可以将此脚本添加为Xcode中的额外构建步骤。

答案 2 :(得分:0)

我有点不清楚如何根据之前的答案从linux命令行环境中隐藏静态库中的符号所以我只是在这里发布我的解决方案以供后代使用(假设这是最好的结果之一)谷歌为这个问题)。

假设您有这两个.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,您不希望使用静态库,因为您希望使用它整个图书馆。

以下是实现这一目标的步骤:

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

这就是我们想要的。

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