背景
前段时间在北汽项目中,遇到了一个奇怪现象,困扰了大家较长时间。最终在和同事的不懈努力下,从根因上解决了该问题,过程中也学习到了很多。在此,记录并分享,希望能够帮助大家。
问题描述
作为OTA服务的提供方,我们提供方式一般为将自己的代码编译成动态库(libsysi4dpc.so),提供给设备厂家,让他们进行集成,并调试。
熟悉aotodomain代码框架的同事应该知道,模块之间的通信方式为MQTT,并且libupc.a 提供了MQTT 的接口实现。也就是说,libsysi4dpc.so 库中其实是包含libupc.a 静态库的所有代码段和数据段,并不再依赖其他MQTT 动态库。如下图所示:
图一:libsysi4dpc.so 依赖动态库
但是当客户集成libsysi4dpc.so 库,生成可执行程序hal-process。当出现crash时,通过gdb进行堆栈跟踪。会发现,虽然出错的线程是我们的adm_ups_run 接口(进行MQTT 重连的线程),但是其栈进入到了libmqtt-paho.so库中(这应该也是支持MQTT协议的库)。这就让我们很奇怪,因为我们代码中的MQTT协议接口应该是在libupc.a 中,为什么会走到libmqtt-paho.so 中呢?
通过查询hal-process 动态库依赖,如下:
图2: hal-process 依赖动态库
由上可知,libmqtt-paho.so 是hal-process进行链接的。(细心的同事可能会觉得奇怪,为什么没有看到libsysi4dpc.so,那是因为hal-process并不直接依赖我们的库,而是通过libqzm_dpc.so 间接依赖)。
到此,问题应该就明确了,crash 的原因:是因为adm_ups_run 接口,并没有走到我们自己的mqtt库中,而是运行到了客户提供的mqtt库中,由于mqtt版本的不同,其中兼容问题,导致了crash。
解决方式
上小节中的问题,在编译原理中有一个专业的名词:符号抢占。并且该业务情景应该是非常常见的,但为什么以前都不会出现呢?经过思考,我觉得原因可能如下:
符号抢占可能一直存在,但是存在并不一定会出错。因为我们依赖的一些底层库,即使出现的符号抢占,但是客户依赖的库,其版本可能和我们是一样的,或者是相互兼容的,也就不会出现所谓的crash。只有当版本差距较大,或不兼容时,才会出现问题。
最终问题的解决方式,在编译libsysi4dpc.so 时,加上编译选项-Wl,-Bsymbolic,该选项表示,强制采用本地的全局变量定义。
思考
如果我们的mqtt库不再是静态库libupc.a,而是动态库libupc.so,上述的方法是否还可以呢?