Ios 在Objective-C中运行时检测并使用可选的外部C库
我正在构建一个SDK,iPhone开发者可以将其包含在他们的项目中。它是以编译的“.a”形式交付的,没有源代码。让我们称我的SDK为“AAA” 客户在他的项目(我们称之为“BBB”)中,除了使用AAA之外,还可能使用一个名为“CCC”的第三方库,该库也是预编译的、封闭源代码的。我不卖CCC,它是另一家公司 我的SDK AAA可以选择使用CCC来改进产品,使用这些第三方功能。例如,假设CCC是一个用于加密某些内容的安全SDK。AAA不需要CCC,但如果客户选择将CCC也包括在其项目中,则会更加安全 现在这里有一个特别棘手的部分-CCC库,是纯C代码,由C结构和C函数组成-它没有面向对象的功能 这些问题是:Ios 在Objective-C中运行时检测并使用可选的外部C库,ios,objective-c,c,static-libraries,late-binding,Ios,Objective C,C,Static Libraries,Late Binding,我正在构建一个SDK,iPhone开发者可以将其包含在他们的项目中。它是以编译的“.a”形式交付的,没有源代码。让我们称我的SDK为“AAA” 客户在他的项目(我们称之为“BBB”)中,除了使用AAA之外,还可能使用一个名为“CCC”的第三方库,该库也是预编译的、封闭源代码的。我不卖CCC,它是另一家公司 我的SDK AAA可以选择使用CCC来改进产品,使用这些第三方功能。例如,假设CCC是一个用于加密某些内容的安全SDK。AAA不需要CCC,但如果客户选择将CCC也包括在其项目中,则会更加安全
- 我如何编译AAA SDK以使用CCC中的函数/结构,而不在我的项目中包含CCC(法律上不允许,也不希望更新版本)
- 如何检测客户的项目中是否有CCC,仅在可用的情况下使用这些额外功能
- 您可以使用弱函数来完成。
在静态库中,声明要使用的ccc的所有函数,如下所示:
int cccfunction(void) __attribute__((weak));
不要在你的库中包含ccc。
由于函数被声明为弱函数,编译器不会抱怨它们的缺失,但是您可以在代码中引用它。
然后,当您将库分发给用户时,给他们一个.c文件,其中包含空的ccc函数,返回0/null。
当ccc库不可用时,这是必需的。如果导入CCC库,用户必须删除此文件 执行IOSLibraries并查看日志。 在第一次执行时,您将在日志中看到
CCC not found <--- this line is printed by libstatic (your library)
Executing a function of CCC <--- this line is printed by libccc
CCC has been found and the function has been executed <--- this line is printed by libstatic (your library)
CCC未找到因此,以下是您问题的要点
您自己的进程无法交换静态库。。。那是在我链接libfoo.1.a的链接时,现在在运行时,这个进程无法可靠地交换libfoo.2.a的符号
所以你需要绕过这个限制
最简单的方法是使用动态库和动态链接器。。。但是你使用的是iOS,所以你没有访问权限
如果你能运行一个助手,你可能会在第一个进程中改变实际的对象,但是你在iOS上,这是行不通的
所以树叶试图让一个对象修改它自己的内容。。。哪种代码签名不允许您执行
因此,只需在程序中构建溢出并尝试使其执行:)
事实上,这比那要简单得多
缓冲
用代码片段填充它
设置堆栈帧(需要一点asm)
为您计划调用的函数设置参数
对方法运行buffer+offset
利润
作为旁注,我写了一个演示运行时动态绑定的小东西。。。但是你需要一个编译器等等。。。这种策略在iOS上不起作用
编辑事实上,我重新阅读了你的问题,我认为这是一个更简单的问题,你正在试图解决
您可以使用其他头定义的任何内容来执行条件编译。。。
如果需要将这些结构中的一个包含到对象中,只需键入定义该结构,然后只使用指向该结构的指针,只要库具有构造和销毁函数。用于按函数名获取C函数指针。如果它能找到他们,他们就在那里。否则他们就不是了。只需使用RTLD\u DEFAULT
作为第一个参数
编辑:回顾了一个iOS示例,请参阅Mike Ash的,特别是关于“失败”的部分。您将看到他检查是否存在objc_loadWeakRetained
(与弱引用相关的运行时调用)。在5+以下,它是,他的版本直接调用真实版本。在4岁以下,他的版本并不是这样做的,而是做了一些其他的事情
EDIT2:示例代码:
样本1:
#import <Foundation/Foundation.h>
#include <dlfcn.h>
int main(int argc, char *argv[])
{
@autoreleasepool
{
NSLog(@"%p", dlsym(RTLD_DEFAULT, "someFunc"));
}
}
输出除0x0以外的地址
样本3:
#import <Foundation/Foundation.h>
#include <dlfcn.h>
void someFunc()
{
NSLog(@"Hi!");
}
int main(int argc, char *argv[])
{
@autoreleasepool
{
void (* func)();
func = dlsym(RTLD_DEFAULT, "someFunc");
func();
}
}
#导入
#包括
void someFunc()
{
NSLog(@“嗨!”);
}
int main(int argc,char*argv[])
{
@自动释放池
{
无效(*func)();
func=dlsym(RTLD_默认为“someFunc”);
func();
}
}
输出<代码>嗨代码>
结构在.a或运行时的其他地方不存在,它们只是编译器如何格式化数据的指令。因此,您需要在代码中包含实际的结构或它们的兼容重述。这很棘手,但易于管理。如果您只需要CCC中的Objective-C类,这会更容易,但您明确表示需要访问结构/函数
围绕所有CCC功能构建一个代理类。所有CCC功能都必须封装到代理的实例方法中。所有CCC类型必须适应您自己的类型。CCC的任何部分都不能包含在代理类的实现文件之外的任何内容中。我将调用这个类MyCCCProxy
切勿直接引用MyCCCProxy
类对象。稍后将对此进行详细介绍
在不链接MyCCCProxy.m的情况下构建库
仅使用MyCCCProxy构建第二个静态库
拥有CCC的客户需要链接AAA、CCC和CCCProxy。没有CCC的客户将只链接AAA
棘手的一步是第二步
大多数情况下,在创建类的实例时,您会使用:
MyCCCProxy *aCCCProxy = [[MyCCCProxy alloc] init];
这将直接引用MyCCCProxy的类对象,如果不包括MyCCCProxy,将导致用户链接问题
相反,如果你写:
MyCCCProxy *aCCCProxy = [[NSClassFromString(@"MyCCCProxy") alloc] init];
这不会直接引用类对象,而是动态加载类对象。如果MyCCCProxy
MyCCCProxy *aCCCProxy = [[NSClassFromString(@"MyCCCProxy") alloc] init];
MyCCCProxy *aCCCProxy = [[NSClassFromString(@"MyCCCProxy") alloc] init];
if (aCCCProxy != nil) {
// I have access to CCC through MyCCCProxy.
}
int CCC_wrap_isfake(void) {
#if HAVE_CCC
return 0;
#else
return 1;
#endif
}
int CCC_wrap_isfake(void) { return 1;}
int CCC_wrap_isfake(void) { return 0;}
LDFLGAS=-lCCC_wrap_fake
LDFLGAS=-lCCC_wrap -lCCC