Ios 在Objective-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也包括在其项目中,则会更加安全

我正在构建一个SDK,iPhone开发者可以将其包含在他们的项目中。它是以编译的“.a”形式交付的,没有源代码。让我们称我的SDK为“AAA”

客户在他的项目(我们称之为“BBB”)中,除了使用AAA之外,还可能使用一个名为“CCC”的第三方库,该库也是预编译的、封闭源代码的。我不卖CCC,它是另一家公司

我的SDK AAA可以选择使用CCC来改进产品,使用这些第三方功能。例如,假设CCC是一个用于加密某些内容的安全SDK。AAA不需要CCC,但如果客户选择将CCC也包括在其项目中,则会更加安全

现在这里有一个特别棘手的部分-CCC库,是纯C代码,由C结构和C函数组成-它没有面向对象的功能

这些问题是:

  • 我如何编译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