首页 > 代码库 > 一个令人蛋疼的链接错误
一个令人蛋疼的链接错误
背景
开始行动
踩雷了
拿到编译出的libB.a,放入engine.so编译工程中,修改mk文件,头部加入静态库预编译段,include $(CLEAR_VARS) LOCAL_MODULE := BModule LOCAL_SRC_FILES := libB.a include $(PREBUILT_STATIC_LIBRARY)在so编译部分加载BModule模块:LOCAL_STATIC_LIBRARIES := BModule
泪奔了,libB.a和engine.so的编译过程都自我感觉非常之良好啊。。。尼玛。只能不断自我打击,暗示一定什么环节出问题了。。没有crash,文字标注还有,但是底图一直渲染不成功。。。
排雷的过程
1)将B的源码放入SO编译工程,最终so包没问题。只能怀疑自己的libB.a编译有问题或者链接有问题咯,于是进行第一个尝试:
不直接进行源码编译,而是通过ndk自带的arm-linux-androideabi-ar.exe工具,将源码编译时产生的一系列.o文件,手工编译成.a,然后链接这个.a,发现build的so包还是有问题。
source => *.o => engine.so *.o => libB.a => engine.so ar上述两种路径:第一条表示源码编译,ok;第二条是源码编译的中间结果.o文件,手工通过ar打包成 libB.a,然后链接libB.a,就有问题。真是见鬼了。!。
2)躲不过了,只能source中增加log,第一次build成libB.a,然后第二次build成engine.so,最后拷贝到android工程中,build APK。
source => libB.a => engine.so => apk.
整个蛋疼的定位过程得益于windows的批处理脚本,可以实现半自动化。
不断重复这个过程,不断调整log精度。最终定位到底图瓦片绘制失败的问题:坐标转化函数GetGeoRect的结果错误,导致绘制时候取不到数据。
定位问题的原因
跟组内一经验丰富的哥们讨论,那天恰好周五,下班前还是没结果。。。晚上回去后回一直在回想编译的整个过程,想起他无心的一句话:“是不是可能有重复的定义啥的“。终于想到了一个问题,A工程里面B工程的两个头文件,当时为了解耦其他人将两个头文件重复拷贝了一份,(明显触犯了DRY原则)如下, yy.h中包含了静态函数的GetGeoRect定义,vv.h中包含了render_config_t结构体定义,而GetGeoRect中使用了render_config_t结构体。
我最近一次B模块升级,更新了vv.h中的render_config_t结构体,内部增加了一个256的char数组。。
附图新旧vv.h头文件中的render_config_t结构体:
旧的:新的:
第二天周六,按耐不住奔到公司,更新A模块中的vv.h头文件,build出的so包终于正确了。总算是找到问题所在了:
A和B工程中的vv.h和yy.h文件重复,B中vv.h文件最近被更新过。
1)当A、B工程均采用源码编译时,最终SO中的GetGeoRect函数内部使用了最新的render_config_t结构体布局(编译器可能根据文件的修改时间等等作为比对条件吧?),因此底图绘制正确。
2)但是当B工程build成静态库libB.a时,此时build成SO时,GetGeoRect函数定义采用了A工程中源码(编译器可能更加信赖源码吧),因此render_config_t也采用了旧的内存布局。因此当调用SO运行时,传入GetGeoRect函数的render_config_t的对象采用最新的内存布局,但是内部实际上是按旧的结构体解析和执行,当然结果就完全错了。