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

发布于 2024-11-27 06:35:08 字数 1402 浏览 0 评论 0原文

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

动机: 制作静态库供其他 iOS 开发人员使用。库中的某些符号如果导出会导致问题,因此我希望将它们设为仅供内部使用的符号。对于动态库,这很容易,只需使用 -exported_symbols_list libtool (ld) 参数并列出您想要公开的。 libtool 文档不允许静态库使用此参数。

库有几个相互使用代码的 ObjectiveC .m 文件。该组中只有一个类需要向最终 .a 静态库文件的用户公开。

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

无法使用属性将符号设为私有(如果可以的话),因为组中的其他 .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”)

所以那里似乎有问题......

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

This question has been asked before, but digging into the documentation for the various development tools it seems like this is possible, just not obvious.

Motivation:
Making a static library for use by other iOS developers. Some symbols in the library will cause problems if exported so I wish to make them internal-only symbols. With a dynamic library this is easy, just use -exported_symbols_list libtool (ld) argument and list the ones you want public. libtool documentation will not allow this argument for static libraries.

Library has several ObjectiveC .m files that use code from each other. Only one class in the group needs to be made public to users of the final .a static library file.

Tried libtool -exported_symbols_list publicsymbols.exp but that argument to libtool is not supported with -static for static libraries.

Can't make the symbols private with attributes (if that'd even work) because they are needed by the other .m files in the group.

looks like ld can take several .o files and link them together into a new .o file (via the -r argument) and it doesn't have the "dynamic only" disclaimer for the -exported_symbols_list argument (which could just be unclear documentation...).

just as a test I build my project with Xcode so I have all the .o files made, and then try to call ld on the command line, like so:

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

where the {path} type things have long paths to the appropriate places in there.

but I get errors like the following:

/usr/bin/ld_classic: /Users/Dad/ABCsdk/iphone-ABClib/build/ABCLib.build/Distribution-iphoneos/ABCLib-device.build/Objects-normal/armv6/ABCmain.o incompatible, file contains unsupported type of section 3 (_TEXT,_picsymbolstub4) in load command 0 (must specify "-dynamic" to be used)

so something seems wrong there...

Anyone know a clever way to make this work? Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

記柔刀 2024-12-04 06:35:08

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

假设您有四个文件,

  • common.c 定义 common,它是“私有”的
  • fn1.c 定义 fn1,它调用 common
  • fn2.c 定义了fn2,它调用common
  • other.c 定义other

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

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

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

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

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

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

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

This is really not possible, I'm sorry to say. It has to do with the way static libraries work. A static library is little more than a bunch of object *.o files bundled together, but a dynamic library is a loadable binary image, just like an executable.

Suppose you have four files,

  • common.c defines common, which is "private"
  • fn1.c defines fn1, which calls common.
  • fn2.c defines fn2, which calls common.
  • other.c defines other.

In a dynamic library, the linker bundles everything up into one big chunk of code. The library exports other, fn1, and fn2. You have to load the entire library or none of it, but two programs can both load it without putting multiple copies in memory. The entry point to common is simply missing from the symbol table — you can't call it from outside the library because the linker can't find it.

Note that an application and a shared library have essentially the same format: an application is basically a shared library that only exports one symbol, main. (This is not exactly true, but close.)

In a static library, the linker never runs. The files all get compiled into *.o files and put into a *.a library archive. Internal references will be unresolved.

Suppose your application calls fn1. The linker sees an unresolved call to fn1, and then looks through the libraries. It finds a definition for fn1 in fn1.o. Then the linker notices an unresolved call to common, so it looks it up in common.o. This program won't get the code from fn2.c or other.c, because it doesn't use the definitions from those files.

Static libraries are very old, and they do not have any of the features of dynamic libraries. You can think of a static library as basically a zip file full of compiled source code, unlike a dynamic library which is linked together. Nobody ever bothered to extend the archive format to add symbol visibility. When you link with a static library, you get the same result as if you had added the library's source code to your program.

The short version: A dynamic library has one symbol table of all of the exported symbols, but none of the private symbols. In the same way, an object file has a list of all of its extern symbols but none of the static ones. But a static library has no symbol table, it is just an archive. So there is no mechanism to make code private to a static library (other than defining objects static, but that doesn't work for Objective-C classes).

If we knew why you were trying to do this, perhaps we could give you a suggestion. (Is it for security? Name clashes? All of these questions have solutions.)

吹泡泡o 2024-12-04 06:35:08

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

这显然被称为“单个对象预链接”,并且在treert提到的XCode中可以选择执行此操作。但您只需使用标准命令行工具即可完成此操作(示例存储库此处):

查看一下(这是在 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 一起使用,则不会删除非全局符号名称,而是替换为唯一的虚拟名称,该名称在链接到最终链接图像时将自动删除。这允许死代码剥离(使用符号来分解代码和数据)正常工作,并提供删除源符号名称的安全性。

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

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

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

This is possible! As Dietrich said, all exported symbols in .o files in a static library are public, and if a file needs to refer to a symbol in another .o file it needs to be exported from that file (and therefore public). But there's an easy workaround - pre-link all of your .o files into a single one. Then you only need to export the public symbols.

This is apparently called "Single Object Prelinking", and there's an option to do it in XCode that treert mentioned. But you can do it with just standard command line tools (example repo here):

Check it out (this is on Mac).

First lets create some test files

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

int public_function() {
    return internal_private_function();
}

Compile them

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

Add them to a static library (it's basically a zip file, but in a decades old format).

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

Check what symbols are in it.

$ 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

Ok as you can see both functions are visible, and both symbols are g which means global.

Now lets prelink into a single file and then put that on its own in a static library.

$ 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

Similar result - they're in one file but both still present and global. Finally let's filter them out (this is Mac specific). We need a list of symbols to export:

$ cat exported_symbols_osx.lds
_public_function

Then use the -exported_symbols_list option.

$ 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

Tada! _internal_private_function is now a local symbol. You can add the -x option (or alternatively run strip -x) to change the name to a random meaningless value (here 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

Here's what Apple's linker has to say about -x:

Do not put non-global symbols in the output file's symbol table. Non-global symbols are useful when debugging and getting symbol names in back traces, but are not used at runtime. If -x is used with -r non-global symbol names are not removed, but instead replaced with a unique, dummy name that will be automatically removed when linked into a final linked image. This allows dead code stripping, which uses symbols to break up code and data, to work properly and provides the security of having source symbol names removed.

All of this is the same on Linux except -exported_symbols_list. On Linux I think you have to use --version-script with a file like this:

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

But I haven't tested this yet. Both this file and the exported_symbols_list files support wildcards.

丶情人眼里出诗心の 2024-12-04 06:35:08

XCode BuildSetting 可以做到这一点!
1. 将执行单对象预链接设置为YES
2. 将Exported Symbols File设置为path_for_symbols_file

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

XCode BuildSetting can do this!
1. Set Perform Single-Object Prelink to YES
2. Set Exported Symbols File to path_for_symbols_file

maybe you should remove -static, -exported_symbols_list can not work static lib, but can take effect on object file.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文