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

时间:2016-11-01 18:34:24

标签: ios objective-c xcode static-libraries collision

背景

我正在构建一个iOS应用程序(我将从这里调用MyApp),它将依赖于几个单独的静态库(我称之为Lib1,{{ 1}},Lib2,...)。每个库都在其自己的项目中构建,然后导入到单个工作区(因此工作区将包含Lib3MyAppLib1,...)。有关如何设置here的详细信息。这些库由独立于Lib2的其他产品使用,因此我希望尽可能减少库中的任何更改。这些库也是用(普通)MyApp编写的,因此没有头文件。

多个库使用某些函数名称(因此CLib1都可能都有Lib2方法。具有相同名称的函数通常做同样的事情,但有一些细节关于如何在库之间做出不同的事情,因此DoStuff DoStuff上的实际代码可能与Lib1DoStuff的代码。编写一个在每个库中完全相同的通用Lib2是非常困难的。

问题

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

我正在寻找什么

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

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

更新

在查看Josh Caswell的评论和他提供的一些链接之后,看起来可能会在编译库时自动重命名所有函数,这将是尝试修复<的最佳方法强> THE ISSUE 以上。从我所看到的,在评论中的几个链接中提到的objcopy不支持iOS。我最终遇到了this博客文章,其中讨论了为Xcode目标创建自定义构建规则,以及this博客,其中讨论了自定义构建设置和构建阶段。

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

我试图尽可能清楚,但如果对我要问的内容有任何疑问,请告诉我。

1 个答案:

答案 0 :(得分: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();
}

图书馆2 - test2lib.a,代码为:

#include <stdio.h>

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

void uniqueEntryPoint2()
{
  printf("\nUnique entry point for lib2\n");
  doStuff();
}

图书馆3 - test3lib.a,代码为:

#include <stdio.h>

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

void uniqueEntryPoint3()
{
  printf("\nUnique entry point for lib3\n");
  doStuff();
}

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

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

@implementation ViewController

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

}

输出

Unique entry point for lib1

Doing stuff for lib1

在这种情况下,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)

由于链接器检测到多次重新定义doStuff(),因此失败的原因。

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

可能的解决方案

解决方案1:

最佳解决方案(自解释)是在您有权访问源代码时更改源代码。

解决方案2:

使用objcopy重命名或添加函数前缀,如How to deal with symbol collisions between statically linked libraries?

的答案所示

现在你怀疑如何找到objcopy。

选项1: 您可以使用此项目https://github.com/RodAtDISA/llvm-objcopy。随着它与llvm一起构建,这将很难编译。您必须按照http://llvm.org/docs/GettingStarted.htmlhttp://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:

您可以在此链接Rewriting symbols in static iOS libraries中按照理查德提供的答案进行操作。这更像是一个黑客,但使用十六进制编辑器,如果它们的长度保持不变,你可以重写符号。如果您有更多符号及其大型库,则可以考虑使用https://sourceforge.net/projects/bbe-/和nm来编写脚本。

所有这些都是相当大的努力,但显然没有捷径。