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

Ben*_*tto 46 obfuscation xcode gcc symbols

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

__attribute__((visibility("hidden")))根据这个文档在一些C++类上使用了该属性.我还将小辅助C函数定义为文件本地(静态)等.

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

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

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

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

谢谢.

ble*_*ter 50

隐藏内部名称需要一些简单的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"选项来提供一个文件,其中包含我想从全局符号表中删除的符号列表.

  • 我建议在上面的列表中添加5.:将"SymbolsHiddenByDefult"设置为YES.(它在代码生成部分).它相当于-fvisibility = hidden.步骤1..4帮助我删除字符串,但步骤5.隐藏了非公共方法,静态变量和缓冲区. (10认同)
  • "使用单独的条带"不再是一种选择 - 苹果公司不赞成声称剥离总是单独进行.尽管如此,通过做其余的事情,我仍然从这篇文章中获得了一些里程 - 似乎有效. (3认同)

小智 16

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

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

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

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

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

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

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

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


yps*_*psu 7

我有点不清楚如何根据之前的答案从 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();
}
Run Code Online (Sandbox Code Playgroud)

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

__attribute__((visibility("default")))
void print_greeting(void)
{
  puts(get_english_greeting());
}
Run Code Online (Sandbox Code Playgroud)

您想要将这两个文件转换为导出这两个文件的静态库get_greetingprint_greetingget_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
Run Code Online (Sandbox Code Playgroud)

现在这有效:

// gcc -L. main.c -lf
void get_greeting(void);
void print_greeting(void);
int main(void)
{
  get_greeting();
  print_greeting();
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

而这并没有:

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

对于后者,您会收到此错误:

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

这就是我们想要的。

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