在构建时更改子例程名称以避免Xcode中的冲突 [英] Change Subroutine Names at Build Time to Avoid Collisions in Xcode

查看:57
本文介绍了在构建时更改子例程名称以避免Xcode中的冲突的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景

我正在构建一个iOS应用程序(我将在这里将其称为MyApp),它将依赖于几个单独的静态库(我将称为Lib1Lib2Lib3、...)所完成的计算。每个库都构建在它自己的项目中,然后导入到单个工作区(因此工作区将包含MyAppLib1Lib2、...)。有关如何设置的更多详细信息here。这些库被独立于MyApp的其他产品使用,所以我希望最小化这些库中的任何更改。这些库也是用(纯)C编写的,所以没有头文件。

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

问题

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

我要找的

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

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


更新

在查看了Josh Caswell的注释和他提供的一些链接后,似乎有可能在编译库时自动重命名所有函数,这将是尝试修复上述THE ISSUE的最佳方法。据我所知,评论中的几个链接中提到的objcopy在iOS上不支持。我最终看到了this博客条目,其中讨论了为Xcode目标创建自定义构建规则,this博客讨论了定制构建设置和构建阶段。

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

我尽量说清楚,但如果对我所问的问题有任何疑问,请让我知道。

推荐答案

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

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

库1-test1lib.a,代码:

#include <stdio.h>

void doStuff()
{
  printf("
Doing stuff for lib1
");
}

void uniqueEntryPoint1()
{
  printf("
Unique entry point for lib1
");
  doStuff();
}

库2-test2lib.a,代码:

#include <stdio.h>

void doStuff()
{
  printf("
Doing stuff for lib2
");
}

void uniqueEntryPoint2()
{
  printf("
Unique entry point for lib2
");
  doStuff();
}

库3-est3lib.a,代码:

#include <stdio.h>

void doStuff()
{
  printf("
Doing stuff for lib3
");
}

void uniqueEntryPoint3()
{
  printf("
Unique entry point for lib3
");
  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个库中存在的符号。乔希在评论中已经提到了这一点。我会加上我的0.02美元。

可能的解决方案

解决方案1:

最佳解决方案(不言而喻)是更改源代码(如果您有权访问源代码)。

解决方案2:

使用objCopy重命名此How to deal with symbol collisions between statically linked libraries?答案中提供的函数或为其添加前缀

现在您对如何找到对象副本有疑问。

选项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对象副本

解决方案3:

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

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

这篇关于在构建时更改子例程名称以避免Xcode中的冲突的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆