在构建时更改子例程名称以避免 Xcode 中的冲突
Change Subroutine Names at Build Time to Avoid Collisions in Xcode
背景
我正在构建一个 iOS 应用程序(我将在这里称之为 MyApp
),它将依赖于由几个独立的静态库(我将其称为 Lib1
、Lib2
、Lib3
、...)。每个库都构建在它自己的项目中,然后导入到单个工作区(因此工作区将包含 MyApp
、Lib1
、Lib2
、...)。有关如何设置的更多详细信息 here。这些库由独立于 MyApp
的其他产品使用,因此我想尽量减少库中的任何更改。这些库也是用(普通)C
编写的,因此没有头文件。
某些函数名称被多个库使用(因此 Lib1
和 Lib2
可能每个都有一个 DoStuff
方法)。具有相同名称的函数通常做同样的事情,但有一些关于如何做到这一点的细节在库之间可能不同,因此 Lib1
上的 DoStuff
中的实际代码可能与Lib2
上 DoStuff
中的代码。编写一个在每个库中都完全相同的通用 DoStuff
是非常困难的。
问题
虽然应用程序是 运行,但它没有从正确的库中调用正确的 DoStuff
。我发现这一点是因为在调试会话期间调用了错误的函数(由于 DoStuff
函数中的细微差别,最终导致应用程序崩溃)。
我在找什么
每个库只有一个来自 MyApp
的入口点,并且每个入口点都有唯一的名称。如果从 Lib1
的入口点方法(或 Lib1
上的任何其他方法)调用 DoStuff
,那么我希望它调用 DoStuff
方法Lib1
。实现这一目标的最佳方式是什么?
有什么办法(也许通过 XCode 中某处的设置)让每个库都有自己的命名空间?那将是我解决问题的首选方法。我想我可以重命名重复的函数,以便它们都是唯一的(因此 Lib1
上的 DoStuff
方法可以重命名为 Lib1DoStuff
,或类似的东西),但是有数百个可能具有重名的函数,我们将向项目添加数百个库,因此必须手动重命名所有函数并修复对它们的所有调用将花费大量时间,我的老板不认为这是一个可行的选择。
更新
在查看了 Josh Caswell 的评论和他提供的一些链接之后,似乎可以在编译库时自动重命名所有函数,这将是尝试的最佳方式修复上面的THE ISSUE
。据我所见,iOS 不支持评论中几个链接中提到的 objcopy
。我最终遇到了 this blog entry, which talks about creating custom build rules for Xcode targets, and this 博客,其中讨论了自定义构建设置和构建阶段。
我是否可以假设我可以在构建过程中的某个时刻使用脚本自动附加到我的每个库中的所有函数的名称,而不是像我那样手动执行在上面 WHAT I'M LOOKING FOR
部分的最后一段中描述? 如果是这样,那么构建过程中进行这些更改的正确部分是什么?最后,执行类似操作的语法是什么样的? 在构建过程的不同部分中使用的 'scripts' 当然看起来不像 Obj-C。我以前从未使用过这些 'scripts',所以我完全不知道如何使用它们,这就是我正在寻求帮助的原因。
我尽量表达清楚,但如果对我的问题有任何疑问,请告诉我。
为什么 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 和 link 时。 xcode links 但不加载所有对象文件。假设 objective 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 对象)。
如果您阅读了 link 和 flags/options,例如 -all_load
、-force_load
和 -objC
,您将会有更好的理解。
如果我们添加-all_load
linker选项,它会强制linker加载库的所有对象,所以我们会在xcode中得到以下错误
ld: 2 duplicate symbols for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
失败的原因是 linker 检测到 doStuff()
被多次重新定义。
解决此问题的唯一方法是更改 linker 输入,即这 3 个库中存在的符号。 Josh 在评论中已经提到了这一点。我会把我的 0.02 美元加进去。
可能的解决方案
解决方案一:
最好的解决方案(不言自明)是更改源代码(如果您可以访问源代码)。
方案二:
按照此 How to deal with symbol collisions between statically linked libraries?
的答案中提供的,使用 objcopy 为函数重命名或添加前缀
现在您对如何找到 objcopy 存疑了。
选项 1:
您可以使用这个项目 https://github.com/RodAtDISA/llvm-objcopy. This will be tricky to compile as it builds along with llvm. You will have to follow instructions at http://llvm.org/docs/GettingStarted.html and http://llvm.org/docs/CMake.html。
如果您重写 https://github.com/RodAtDISA/llvm-objcopy/blob/master/llvm-objcopy.cpp,您可能可以在不依赖 llvm 的情况下重用解析和对象重写逻辑。
选项 2:
编译并重用 binutils objcopy
方案三:
你可以按照Richard在这个linkRewriting symbols in static iOS libraries. This is more of a hack but using a hex editor you can rewrite the symbols if their length is kept the same. If you have more symbols and its a big library, you can consider using https://sourceforge.net/projects/bbe-/中提供的答案和nm写一个脚本。
所有这些都是相当大的努力,但显然没有捷径可走。
背景
我正在构建一个 iOS 应用程序(我将在这里称之为 MyApp
),它将依赖于由几个独立的静态库(我将其称为 Lib1
、Lib2
、Lib3
、...)。每个库都构建在它自己的项目中,然后导入到单个工作区(因此工作区将包含 MyApp
、Lib1
、Lib2
、...)。有关如何设置的更多详细信息 here。这些库由独立于 MyApp
的其他产品使用,因此我想尽量减少库中的任何更改。这些库也是用(普通)C
编写的,因此没有头文件。
某些函数名称被多个库使用(因此 Lib1
和 Lib2
可能每个都有一个 DoStuff
方法)。具有相同名称的函数通常做同样的事情,但有一些关于如何做到这一点的细节在库之间可能不同,因此 Lib1
上的 DoStuff
中的实际代码可能与Lib2
上 DoStuff
中的代码。编写一个在每个库中都完全相同的通用 DoStuff
是非常困难的。
问题
虽然应用程序是 运行,但它没有从正确的库中调用正确的 DoStuff
。我发现这一点是因为在调试会话期间调用了错误的函数(由于 DoStuff
函数中的细微差别,最终导致应用程序崩溃)。
我在找什么
每个库只有一个来自 MyApp
的入口点,并且每个入口点都有唯一的名称。如果从 Lib1
的入口点方法(或 Lib1
上的任何其他方法)调用 DoStuff
,那么我希望它调用 DoStuff
方法Lib1
。实现这一目标的最佳方式是什么?
有什么办法(也许通过 XCode 中某处的设置)让每个库都有自己的命名空间?那将是我解决问题的首选方法。我想我可以重命名重复的函数,以便它们都是唯一的(因此 Lib1
上的 DoStuff
方法可以重命名为 Lib1DoStuff
,或类似的东西),但是有数百个可能具有重名的函数,我们将向项目添加数百个库,因此必须手动重命名所有函数并修复对它们的所有调用将花费大量时间,我的老板不认为这是一个可行的选择。
更新
在查看了 Josh Caswell 的评论和他提供的一些链接之后,似乎可以在编译库时自动重命名所有函数,这将是尝试的最佳方式修复上面的THE ISSUE
。据我所见,iOS 不支持评论中几个链接中提到的 objcopy
。我最终遇到了 this blog entry, which talks about creating custom build rules for Xcode targets, and this 博客,其中讨论了自定义构建设置和构建阶段。
我是否可以假设我可以在构建过程中的某个时刻使用脚本自动附加到我的每个库中的所有函数的名称,而不是像我那样手动执行在上面 WHAT I'M LOOKING FOR
部分的最后一段中描述? 如果是这样,那么构建过程中进行这些更改的正确部分是什么?最后,执行类似操作的语法是什么样的? 在构建过程的不同部分中使用的 'scripts' 当然看起来不像 Obj-C。我以前从未使用过这些 'scripts',所以我完全不知道如何使用它们,这就是我正在寻求帮助的原因。
我尽量表达清楚,但如果对我的问题有任何疑问,请告诉我。
为什么 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 和 link 时。 xcode links 但不加载所有对象文件。假设 objective 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 对象)。
如果您阅读了 link 和 flags/options,例如 -all_load
、-force_load
和 -objC
,您将会有更好的理解。
如果我们添加-all_load
linker选项,它会强制linker加载库的所有对象,所以我们会在xcode中得到以下错误
ld: 2 duplicate symbols for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
失败的原因是 linker 检测到 doStuff()
被多次重新定义。
解决此问题的唯一方法是更改 linker 输入,即这 3 个库中存在的符号。 Josh 在评论中已经提到了这一点。我会把我的 0.02 美元加进去。
可能的解决方案
解决方案一:
最好的解决方案(不言自明)是更改源代码(如果您可以访问源代码)。
方案二:
按照此 How to deal with symbol collisions between statically linked libraries?
的答案中提供的,使用 objcopy 为函数重命名或添加前缀现在您对如何找到 objcopy 存疑了。
选项 1: 您可以使用这个项目 https://github.com/RodAtDISA/llvm-objcopy. This will be tricky to compile as it builds along with llvm. You will have to follow instructions at http://llvm.org/docs/GettingStarted.html and http://llvm.org/docs/CMake.html。
如果您重写 https://github.com/RodAtDISA/llvm-objcopy/blob/master/llvm-objcopy.cpp,您可能可以在不依赖 llvm 的情况下重用解析和对象重写逻辑。
选项 2:
编译并重用 binutils objcopy方案三:
你可以按照Richard在这个linkRewriting symbols in static iOS libraries. This is more of a hack but using a hex editor you can rewrite the symbols if their length is kept the same. If you have more symbols and its a big library, you can consider using https://sourceforge.net/projects/bbe-/中提供的答案和nm写一个脚本。
所有这些都是相当大的努力,但显然没有捷径可走。