以前曾经问过这个问题,但是深入研究各种开发工具的文档似乎 可能,只是不明显。
动机:
创建一个供其他iOS开发人员使用的静态库。如果导出库中的某些符号会导致问题,所以我希望将它们作为仅内部符号。使用动态库这很简单,只需使用-exported_symbols_list
libtool
(ld
)参数并列出您想要公开的参数。 libtool
文档不允许这个参数用于静态库。
Library有几个ObjectiveC .m文件,它们使用彼此的代码。只需要将组中的一个类公开给最终.a静态库文件的用户。
尝试libtool -exported_symbols_list publicsymbols.exp
但libtool
的参数不支持-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”使用)
那里似乎有些不对劲......
任何人都知道一种聪明的方法来完成这项工作吗?感谢。
答案 0 :(得分:15)
这真的不可能,我很遗憾地说。它与静态库的工作方式有关。静态库只是捆绑在一起的一堆对象*.o
文件,但动态库是可加载的二进制映像,就像可执行文件一样。
假设您有四个文件,
common
,即“私有”fn1
,调用common
。fn2
,调用common
。other
。 在动态库中,链接器将所有内容捆绑到一大块代码中。该库导出other
,fn1
和fn2
。您必须加载整个库或不加载它,但是两个程序都可以加载它而无需在内存中放置多个副本。符号表中缺少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
无法使用静态库,但可以对目标文件生效。