美文网首页
静态库中全局c方法被覆盖导致bug的原因和分析

静态库中全局c方法被覆盖导致bug的原因和分析

作者: yohunl | 来源:发表于2019-07-22 16:57 被阅读0次

    背景

    给客户提供了一个sdk,客户那边安装了sdk后,发现另一个sdk的就会异常.....最后分析来分析去,发现只要添加我们的sdk就会有这样的问题,于是,问题就到了我这了.

    结论

    先说结论吧,结论就是,我们的sdk中定义的一个全局c方法,覆盖了另一个sdk中的全局c方法,然后,我们两个c方法的作用又不一样,从而导致出了不同的结果,导致问题.

    分析

    通常这种诡异的问题,调试分析是最头痛的,因为完全不知道问题在哪,直接把对方的sdk和我们sdk的源码加到一个测试工程里来,调试,发现,确实是这样,由于我们这个sdk中的文件数量不多,于是,一个个的删除,定位到是由于一个包含全局c方法的文件中的c方法被对方sdk调用了...
    也就是说,我们的c方法覆盖了对方sdk中的同名方法了.

    对于同样都是静态库来说(静态framework,.a文件),在编译的时候,是会融合到宿主app的二进制中去的,那么按理说,编译阶段,同名c方法,会被xcode提示出来啊,为什么这里会没有提示呢?

    我们随便新建一个sdk,再建立一个宿主app,使用它.


    image.png

    在demo app中使用这个sdk


    image.png
    有几个比较有意思的发现:

    宿主工程不加-ObjC的时候
    主工程和库工程中有重名的c方法的时候,xcode不会提示重复符号,且能够编译运行,至于哪个c方法会被调用,有如下的情况:
    如果使用了c方法所在的文件中的类,那么调用的就是静态库中定义的c方法

    - (void)viewDidLoad {
        [super viewDidLoad];
        convertSomething(@"");
        CustomAction *act = [CustomAction new];
       [act test1];
        // Do any additional setup after loading the view.
    }
    
    image.png

    如果没有使用,那么调用的就是宿主中定义的c方法.


    image.png

    宿主添加-ObjC的时候,xcode就会报错了,提示符号重复


    image.png

    这个也是我们通常认为的结果!!

    可是,使用方明明是加了 -ObjC的,按照我们的理解,有两个重名的c方法,不是应该报错,提示符号重复么,为什么没有?

    然后,让对方自己新建一个demo,添加-ObjC,看看,是不是会报,结果对方告诉我,不会....
    心里立马万马奔腾啊,在群里和小伙伴们讨论,群里有人提示可能是弱符号.于是试了下弱符号 attribute((weak))

    image.png
    再编译一下,不报错了

    这里插播一下弱符号是啥,有什么作用

    弱符号

    我们经常在编程中碰到一种情况叫符号重复定义。多个目标文件中含有相同名字全局符号的定义,那么这些目标文件链接的时候将会出现符号重复定义的错误。比如我们在目标文件A和目标文件B都定义了一个全局函数/变量,并将它们都初始化,那么链接器将A和B进行链接时会报错.这种在全局中不能有重名的符号,可以称之为 strong symbol(强符号).
    对于C/C++语言来说,编译器默认函数和初始化了的全局变量为强符号,未初始化的全局变量为弱符号。
    就如同我们上面的情况
    为了解决这样的问题,就引入了弱符号(weak symbol)的概念:用attribute((weak))修饰的全局变量/函数就是 弱符号
    针对强弱符号的概念,链接器就会按如下规则处理与选择被多次定义的全局符号:
    规则1:不允许强符号被多次定义(即不同的目标文件中不能有同名的强符号);如果有多个强符号定义,则链接器报符号重复定义错误。
    规则2:如果一个符号在某个目标文件中是强符号,在其他文件中都是弱符号,那么选择强符号。
    规则3:如果一个符号在所有目标文件中都是弱符号,那么选择其中占用空间最大的一个。比如目标文件A定义全局变量global为int型,占4个字节;目标文件B定义global为double型,占8个字节,那么目标文件A和B链接后,符号global占8个字节(尽量不要使用多个不同类型的弱符号,否则容易导致很难发现的程序错误)。
    更详细的介绍可以看GCC的强符号和弱符号

    继续分析

    以为发现了问题所在,兴冲冲的让对方去查下,是不是那个c方法用attribute((weak))修饰了,并且从内心已经认定是这样的了,准备收工,可现实往往那么的出其不意..对方告诉我,全局搜索了下,没有使用到 attribute((weak))....
    我去,这,...还会有什么问题导致呢?

    好吧,只能继续分析了

    对方也发来了他们的测试demo,说确实会出现可以在宿主中定义同他们的静态库中的c方法名字一样的方法.

    真的很奇怪,为啥我建立的demo,加了-ObjC,没用attribute((weak))会报错,他们建立的demo就不会呢?

    一度怀疑,难道是我用的xcode和对方的有什么地方默认的不一致?

    再查看对方的demo

    .....

    对方的domo中,c方法所在的文件,只有c方法,没有任何oc的类的定义.难道是这个导致的?

    于是乎,试了下:

    在demo中的sdk1中添加个只包含c方法的文件CustomeAction2
    内容是

    //  CustomeAction2.h
    #import <Foundation/Foundation.h>
    NSString *convertSomething2(NSString * oriStr);
    //  CustomeAction2.m
    #import "CustomeAction2.h"
    NSString *convertSomething2(NSString * oriStr){
        NSLog(@"convertSomething2 SDK1");
        return @"convertSomething2 SDK1 ";
    }
    

    然后在宿主中定义一个同名的

    NSString *convertSomething2(NSString * oriStr){
        NSLog(@"convertSomething2 app");
        return @"convertSomething2 app ";
    }
    

    编译,运行,果然不报错,
    看来就是这个原因导致的了

    再添加 -all_load,不出意外,报错了


    image.png

    问题原因解读

    再度回忆 -ObjC的作用
    -all_load Loads all members of static archive libraries.
    -ObjC Loads all members of static archive libraries that implement an Objective-C class or category.

    因为定义的c方法所在的文件并没有定义Objective-C的class或者category,所以 -ObjC并不会在符号表中导入他们,也即是这个-ObjC失效了,所以在宿主app中,就可以定义同名的方法了

    当然了解决方式很简单
    1 要求所有的宿主app,也就是接入方,在other linker flags中添加-all_load,这个来加载静态库中所有的方法,当然了,这个解决方式不太好.
    更好的解决方式是
    2 在仅仅包含c方法的文件中添加一个类的定义

    后记

    最后,我还对比了下,在一个方法中添加不添加attribute((weak))最后的可执行文件有什么不同

    当把一个方法定义为弱符号后
    attribute((weak)) NSData * pasa_cipherOperation(NSData *contentData, NSData *keyData, CCOperation operation)
    在最后的 静态库中出现的不同是:

    在符号表 Symbol Table中对应的方法


    111.png

    拿一个app的可执行文件来试试
    在other linker flags中 添加 -ObjC 和不添加,对于最后的二进制的区别
    当然,由于xcode编译两次后,codesign部分会不一致,可以用
    codesign --remove-signature 可执行文件名 来移除签名
    然后用 beyond compare来进行对比二进制


    E3C9173D-5F09-4D3E-BC0A-69048627BDBC.png

    再在machoview中查看对应的地址

    DC24243A-D1CE-47D2-8A52-B03556765A8A.png

    看来,这两个,改变的都是最后的mach-o文件中符号表Symbol Table所在的内容.

    相关文章

      网友评论

          本文标题:静态库中全局c方法被覆盖导致bug的原因和分析

          本文链接:https://www.haomeiwen.com/subject/rcxwlctx.html