符号冲突
什么是符号冲突,就是库与库之间有相同的符号,使用者不知道用哪个;例如:A SDK有个符号a,B SDK也有个符号a,最终app调用a时,可能用的是A SDK的a,也可能是B SDK的a;这样的话,就会产生歧义,假如app想调用A SDK的a,但可能实际调用的却是B SDK的a,这样就会造成app行为异常,或是崩溃。
静态库之间符号冲突
静态库冲突经常会遇到下面几个问题:
- 为什么有些重复符号在链接时会报错,有些不会。
首先静态库包含的是.o文件;.o文件就是对应的每个cpp/c文件编译后的产物。当链接时,链接器会按app使用到的函数逐个扫描静态库里的.o,如果发现要链接的.o里存在着已链接过的符号就会报错。不同编译器的链接算法不同,结果也不同。下面以vs 2015,xcode clang,ndk21来分析。
- xcode clang: app使用到的函数是按字符串排列的,链接器会按这个顺序逐个扫描静态库,看下静态库里的.o是否存在在app使用到的函数,如果有就将.o所有符号放进全局符号表里,如果发现全局符号表里有相同的符号就报错
def symsGlobal
def symsAppCall
for (sym in symsAppCall) {
if (symsGlobal dont found sym) {
for (symsObj in symsObjs) {
if(symsObj found sym) {
if(symsGlobal dont contain symsObj) {
symsGlobal.addAll(symsObj)
}else {
print sym conflict
abort
}
}
}
}
}
- vs2015 vc,ndk21 clang:链接器会按静态库链接顺序扫描静态库,看.o是否存在着app使用的函数,如果有就将.o所有符号放进全局符号表里,如果发现全局符号表里有相同的符号就报错
def symsGlobal
def symsAppCall
for (symsObj in symsObjs) {
for (sym in symsAppCall) {
if (symsGlobal dont found sym) {
if(symsObj found sym) {
if(symsGlobal dont contain symsObj) {
symsGlobal.addAll(symsObj)
}else {
print sym conflict
abort
}
}
}
}
}
备注:上面的算法并不一定完全准确,因为这些链接器的代码并不开源,只是通过例子推测出来,有问题欢迎指正
- 下面,我们结合例子分析下
情形 1 :
上面情况,无论在xcode或是vs2015/ndk,app先链接谁就用谁的d函数,而且不会链接报错。
情形 2:
上面的情况:
- 在xcode下,因为链接器会先链接a函数,他会遍历当前的静态库,发现在a.o里,然后将a.o里的所有符号都放进全局符号里;当链接d函数时,因为d已经在全局符号,因此不需要将b.o放进全局符号,所以无论链接顺序是怎样,app始终用的是liba.a的d;
- 在vs2015/ndk下,当liba.a先链接时,链接器会发现a.o里存在着app需要的a,d函数,因此将a.o里的所有符号放进全局符号,因为app需要的函数都链接完了,所以不需要将b.o放进全局符号。当libb.a先链接时,链接器会发现b.o存在着app需要的d函数,因此将b.o所有符号放进全局符号。当链接到liba.a时,发现a.o里存在着app需要的a函数,当将a.o所有符号放进全局符号里时,发现已存在了d函数,因此就报符号冲突错误。
情形 3:
上面情况:无论在xcode或是vs2015/ndk都会报链接出错,因为无论怎么链接,都需要将a.o和b.o里的符号放进全局符号里。
- 链接顺序可以确保app使用的是哪个库的符号吗。
不同编译器结果不同;对于xcode不能保证,对于vs,ndk,只要不报错,app会用先链接的库的符号。
作者:howardpangx
链接:https://www.jianshu.com/p/fb5a5550f858
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。