在构建时更改子例程名称以避免Xcode中的冲突

Gen*_*ike 5 xcode objective-c static-libraries collision ios

背景

我建立一个iOS应用程序(这我就打电话MyApp从这里开始),这将依赖于由几个独立的静态库进行计算(我称之为Lib1,Lib2,Lib3,...).每个库建在它自己的项目,然后导入到一个工作区(这样的工作区将包含MyApp,Lib1,Lib2,...).有关如何设置此处的更多详细信息.这些库由其他独立的产品使用MyApp,因此我希望尽量减少库中的任何更改.这些库也是用(plain)编写的C,因此没有头文件.

某些功能名称由多个存储库中使用(因此两者Lib1和Lib2可能各自具有DoStuff方法).具有相同名称的功能大致做同样的事情,但对于如何是做到这一点,可以是磁带库之间不同的一些具体的,所以在实际的代码DoStuff就Lib1可能比代码完全不同DoStuff的Lib2.编写一个DoStuff在每个库中完全相同的通用将是非常困难的.

问题

当应用程序正在运行时,它没有DoStuff从正确的库中调用正确的.我发现了这一点,因为在调试会话期间调用了错误的函数(由于DoStuff函数的细微差别,最终导致应用程序崩溃).

我在寻找什么

每个库只有一个入口点MyApp,每个入口点都有唯一的名称.如果DoStuff从入口点方法Lib1(或任何其他方法Lib1)调用,那么我希望它调用该DoStuff方法Lib1.实现这一目标的最佳方法是什么?

有没有办法(可能通过XCode中的某个设置)我可以这样做,以便每个库都是它自己的命名空间?这将是我解决问题的首选方法.我想我可以通过并重命名重复的函数,以便它们都是唯一的(因此DoStuff方法Lib1可以重命名为Lib1DoStuff或类似的东西),但有数百个函数可能有重复的名称,我们将是将数百个库添加到项目中,因此必须手动重命名所有函数并修复对它们的所有调用将花费大量时间,而我的老板并不认为这是一个可行的选择.


UPDATE

在查看了Josh Caswell的评论以及他提供的一些链接后,看起来可能会在编译库时自动重命名所有函数,这将是尝试修复THE ISSUE上述内容的最佳方法.从我所看到的,objcopy在评论中的几个链接中提到的内容不支持iOS.我终于碰上了这个博客条目,其中谈到创建自定义生成Xcode的目标规则,这个博客,谈到了自定义生成设置和构建阶段.

我是否正确地假设我可以在构建过程中的某个时刻使用脚本自动附加到我的每个库中的所有函数的名称,而不是像我在上一WHAT I'M LOOKING FOR节的最后一段中所描述的那样手动执行它? 如果是这样,那么构建过程的正确部分是进行这些更改的?最后,做这样的事情的语法是什么样的? 在构建过程的不同部分中使用的"脚本"当然看起来不像Obj-C.我之前从未使用过这些"剧本",所以我完全不知道如何使用它们,这就是我正在寻求帮助的地方.

我试着尽可能地清楚,但如果对我要问的问题有任何疑问,请告诉我.

man*_*shg 2

为什么 xcode 没有调用正确的库函数?

假设我有你提到的 3 个 C 库。假设它有以下代码。

库 1 - test1lib.a 及其代码:

#include <stdio.h>

void doStuff()
{
  printf("\nDoing stuff for lib1\n");
}

void uniqueEntryPoint1()
{
  printf("\nUnique entry point for lib1\n");
  doStuff();
}
Run Code Online (Sandbox Code Playgroud)

库 2 - test2lib.a 及其代码:

#include <stdio.h>

void doStuff()
{
  printf("\nDoing stuff for lib2\n");
}

void uniqueEntryPoint2()
{
  printf("\nUnique entry point for lib2\n");
  doStuff();
}
Run Code Online (Sandbox Code Playgroud)

库 3 - test3lib.a 及其代码:

#include <stdio.h>

void doStuff()
{
  printf("\nDoing stuff for lib3\n");
}

void uniqueEntryPoint3()
{
  printf("\nUnique entry point for lib3\n");
  doStuff();
}
Run Code Online (Sandbox Code Playgroud)

这里每个库都有一个独特的功能和一个通用的功能doStuff()

当我们将这3个库添加到xcode并链接它们时。xcode 链接但不加载所有对象文件。假设目标 C 代码是这样的:

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    uniqueEntryPoint1();

}
Run Code Online (Sandbox Code Playgroud)

输出是

Unique entry point for lib1

Doing stuff for lib1
Run Code Online (Sandbox Code Playgroud)

在这种情况下,xcode 将仅加载在这种情况下引用的符号(库 1 对象)。

如果您阅读过有关链接器标志/选项(例如-all_load、-force_load 和 )的信息-objC,您将会有更好的理解。

如果我们添加-all_load链接器选项,它将强制链接器加载库的所有对象,因此我们将在 xcode 中收到以下错误

ld: 2 duplicate symbols for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Run Code Online (Sandbox Code Playgroud)

失败的原因是链接器检测到它doStuff()被重新定义了多次。

解决此问题的唯一方法是更改​​链接器输入,即这 3 个库中存在的符号。乔什在评论中已经提到了这一点。我将添加 0.02 美元。

可能的解决方案

解决方案一:

最好的解决方案(不言自明)是更改源代码(如果您有权访问源代码)。

解决方案2:

使用 objcopy 重命名或为函数添加前缀,如此How to deal with symbol colons Between statically linkedlibraries?的答案中提供的那样。

现在您对如何找到 objcopy 存有疑问。

选项 1: 您可以使用此项目https://github.com/RodAtDISA/llvm-objcopy。由于它与 llvm 一起构建,因此编译起来会很棘手。您必须遵循http://llvm.org/docs/GettingStarted.html和http://llvm.org/docs/CMake.html中的说明。

如果您重写https://github.com/RodAtDISA/llvm-objcopy/blob/master/llvm-objcopy.cpp,您可能可以重用解析和对象重写逻辑,而无需依赖 llvm。

选项2:

编译并重用https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=blob;f=binutils/objcopy.c;h=2636ab4bcb34cf1e1e54db9933018a805b366727;hb=HEAD中的 binutils objcopy

解决方案3:

您可以按照 Richard 在此链接中提供的答案重写静态 iOS 库中的符号。这更像是一种黑客攻击,但使用十六进制编辑器,如果符号的长度保持相同,您可以重写符号。如果您有更多符号并且库很大,您可以考虑使用https://sourceforge.net/projects/bbe-/和 nm 编写脚本。

所有这一切都需要付出相当大的努力,但显然没有捷径。