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

背景

给客户提供了一个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所在的内容.

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,089评论 1 32
  • 前言 Swift 贡献给社区 作者 关于中文翻译 条件语句 尤达表达式 nil 和 BOOL 检查 黄金大道 复杂...
    一条鱼的星辰大海阅读 359评论 0 1
  • objc_getAssociatedObject返回与给定键的特定对象关联的值。ID objc_getAssoci...
    有一种再见叫青春阅读 1,564评论 0 7
  • 有些东西在记忆深处,抹不去,忘不掉,组成生命中的温暖。 有些话语就在嘴边,说不出,道不明,沉淀在心灵的深处。 波涛...
    高慧_阅读 173评论 1 4
  • 多少父母望子成龙望女成凤,何故?何求?一颗爱心无他求,不为攀龙附凤只为儿女有一个幸福通达无悔的人生吗?恐不尽然! ...
    跨马涉远道阅读 139评论 0 2