如何在不创建所有符号的情况下为iOS创建静态库

时间:2011-08-01 04:27:11

标签: objective-c ios cocoa static-libraries

以前曾经问过这个问题,但是深入研究各种开发工具的文档似乎 可能,只是不明显。

动机:   创建一个供其他iOS开发人员使用的静态库。如果导出库中的某些符号会导致问题,所以我希望将它们作为仅内部符号。使用动态库这很简单,只需使用-exported_symbols_list libtoolld)参数并列出您想要公开的参数。 libtool文档不允许这个参数用于静态库。

Library有几个ObjectiveC .m文件,它们使用彼此的代码。只需要将组中的一个类公开给最终.a静态库文件的用户。

尝试libtool -exported_symbols_list publicsymbols.explibtool的参数不支持-static静态库。

不能使符号与属性一起使用(如果它甚至可以工作),因为组中的其他.m文件需要这些符号。

看起来像ld可以带几个.o文件并将它们链接到一个新的.o文件中(通过-r参数)并且它没有“动态唯一”免责声明-exported_symbols_list论证(可能只是不清楚文档......)。

作为测试,我使用Xcode构建我的项目,因此我创建了所有.o文件,然后尝试在命令行上调用ld,如下所示:

ld -r -x -all_load -static -arch armv6 -syslibroot {path} 
   -filelist /Users/Dad/ABCsdk/iphone-ABClib/build/ABCLib.build/Distribution-iphoneos/ABCLib-device.build/Objects-normal/armv6/ABCsdk.LinkFileList 
   -exported_symbols_list {exp file path} -o outputfile.o

{path}类型的东西有很长的路径到那里的适当位置。

但是我收到如下错误:

  

/ usr / bin / ld_classic:/Users/Dad/ABCsdk/iphone-ABClib/build/ABCLib.build/Distribution-iphoneos/ABCLib-device.build/Objects-normal/armv6/ABCmain.o不兼容,文件包含加载命令0中不支持的第3部分类型(_ TEXT, _picsymbolstub4)(必须指定“-dynamic”使用)

那里似乎有些不对劲......

任何人都知道一种聪明的方法来完成这项工作吗?感谢。

3 个答案:

答案 0 :(得分:15)

这真的不可能,我很遗憾地说。它与静态库的工作方式有关。静态库只是捆绑在一起的一堆对象*.o文件,但动态库是可加载的二进制映像,就像可执行文件一样。

假设您有四个文件,

  • common.c定义common,即“私有”
  • fn1.c定义fn1,调用common
  • fn2.c定义fn2,调用common
  • other.c定义other

在动态库中,链接器将所有内容捆绑到一大块代码中。该库导出otherfn1fn2。您必须加载整个库或不加载它,但是两个程序都可以加载它而无需在内存中放置多个副本。符号表中缺少common的入口点 - 您无法从库外部调用它,因为链接器无法找到它。

请注意,应用程序和共享库的格式基本相同:应用程序基本上是一个只导出一个符号main的共享库。 (这不完全正确,但接近。)

在静态库中,链接器永远不会运行。这些文件都被编译成* .o文件并放入* .a库归档文件中。内部参考将无法解决。

假设您的应用程序调用{​​{1}}。链接器会看到对fn1的未解析调用,然后查看库。它在fn1.o中找到fn1的定义。然后链接器注意到对fn1的未解析的调用,因此它在common.o中查找它。该程序不会从fn2.c或other.c获取代码,因为它不使用这些文件中的定义。

静态库非常陈旧,它们没有动态库的任何功能。您可以将静态库视为一个充满已编译源代码的zip文件,而不像链接在一起的动态库。没有人愿意扩展存档格式以添加符号可见性。当您与静态库链接时,您获得的结果与将库的源代码添加到程序中的结果相同。

简短版本:动态库有一个包含所有导出符号的符号表,但没有一个私有符号。同样,目标文件包含所有common个符号的列表,但没有extern个符号。但是静态库没有符号表,它只是一个存档。因此,没有任何机制可以将代码专用于静态库(除了定义对象static之外,但这对于Objective-C类不起作用)。

如果我们知道你为什么要这样做,也许我们可以给你一个建议。 (这是为了安全吗?姓名冲突?所有这些问题都有解决方案。)

答案 1 :(得分:1)

可能的!正如迪特里希所说,静态库中 .o 文件中的所有导出符号都是公开的,如果一个文件需要引用另一个 .o 文件中的符号,则需要从该文件中导出(因此民众)。但有一个简单的解决方法 - 将所有 .o 文件预先链接到一个文件中。那么你只需要导出公共符号即可。

这显然被称为“单对象预链接”,并且有一个选项可以在 treert 提到的 XCode 中执行此操作。但是您可以仅使用标准命令行工具(例如 repo here)来完成:

检查一下(这是在 Mac 上)。

首先让我们创建一些测试文件

$ cat private.c
int internal_private_function() {
    return 5;
}
$ cat public.c
extern int internal_private_function();

int public_function() {
    return internal_private_function();
}

编译它们

$ clang -c private.c -o private.o
$ clang -c public.c -o public.o

将它们添加到静态库中(它基本上是一个 zip 文件,但采用了几十年前的格式)。

$ ar -r libeverything_public.a public.o private.o

检查里面有什么符号。

$ objdump -t libeverything_public.a

libeverything_public.a(private.o):  file format Mach-O 64-bit x86-64

SYMBOL TABLE:
0000000000000000 g     F __TEXT,__text _internal_private_function

libeverything_public.a(public.o):   file format Mach-O 64-bit x86-64

SYMBOL TABLE:
0000000000000000 g     F __TEXT,__text _public_function
0000000000000000         *UND* _internal_private_function

好的,您可以看到两个函数都是可见的,并且两个符号都是 g,这意味着全局。

现在让我们预链接到单个文件中,然后将其单独放入静态库中。

$ ld -r -o prelinked.o private.o public.o
$ ar -r libeverything_public_prelinked.a prelinked.o
$ objdump -t libeverything_public_prelinked.a

libeverything_public_prelinked.a(prelinked.o):  file format Mach-O 64-bit x86-64

SYMBOL TABLE:
0000000000000020 l     O __TEXT,__eh_frame EH_Frame1
0000000000000038 l     O __TEXT,__eh_frame func.eh
0000000000000060 l     O __TEXT,__eh_frame EH_Frame1
0000000000000078 l     O __TEXT,__eh_frame func.eh
0000000000000000 g     F __TEXT,__text _internal_private_function
0000000000000010 g     F __TEXT,__text _public_function

类似的结果 - 它们在一个文件中,但仍然存在并且是全局的。最后让我们过滤掉它们(这是 Mac 特有的)。我们需要一个要导出的符号列表:

$ cat exported_symbols_osx.lds
_public_function

然后使用 -exported_symbols_list 选项。

$ ld -r -exported_symbols_list exported_symbols_osx.lds -o prelinked_filtered.o private.o public.o
$ ar -r libfiltered_prelinked.a prelinked_filtered.o
ar: creating archive libfiltered_prelinked.a
$ objdump -t libfiltered_prelinked.a

libfiltered_prelinked.a(prelinked_filtered.o):  file format Mach-O 64-bit x86-64

SYMBOL TABLE:
0000000000000000 l     F __TEXT,__text _internal_private_function
0000000000000020 l     O __TEXT,__eh_frame EH_Frame1
0000000000000038 l     O __TEXT,__eh_frame func.eh
0000000000000060 l     O __TEXT,__eh_frame EH_Frame1
0000000000000078 l     O __TEXT,__eh_frame func.eh
0000000000000010 g     F __TEXT,__text _public_function

多田! _internal_private_function 现在是本地符号。您可以添加 -x 选项(或运行 strip -x)以将名称更改为随机的无意义值(此处为 l001)。

$ ld -r -x -exported_symbols_list exported_symbols_osx.lds -o prelinked_filtered.o private.o public.o
$ objdump -t prelinked_filtered.o

prelinked_filtered.o:   file format Mach-O 64-bit x86-64

SYMBOL TABLE:
0000000000000000 l     F __TEXT,__text l001
0000000000000020 l     O __TEXT,__eh_frame EH_Frame1
0000000000000038 l     O __TEXT,__eh_frame func.eh
0000000000000060 l     O __TEXT,__eh_frame EH_Frame1
0000000000000078 l     O __TEXT,__eh_frame func.eh
0000000000000010 g     F __TEXT,__text _public_function

以下是 Apple 的链接器对 -x 的说明:

<块引用>

不要将非全局符号放入输出文件的符号表中。非全局符号在调试和获取回溯中的符号名称时很有用,但在运行时不使用。如果 -x 与 -r 一起使用,则不会删除非全局符号名称,而是替换为唯一的虚拟名称,该名称在链接到最终链接图像时将自动删除。这允许使用符号分解代码和数据的死代码剥离正常工作,并提供删除源符号名称的安全性。

所有这些在 Linux 上都是一样的,除了 -exported_symbols_list。在 Linux 上,我认为您必须将 --version-script 与这样的文件一起使用:

V0 {
  global:
    _public_function;
  local:
    *;
};

但我还没有测试过。此文件和 exported_symbols_list 文件都支持通配符。

答案 2 :(得分:0)

XCode BuildSetting可以做到这一点! 1.将Perform Single-Object Prelink设为YES 2.将Exported Symbols File设为path_for_symbols_file

也许您应该删除-static-exported_symbols_list无法使用静态库,但可以对目标文件生效。