背景

我们APP的引擎包engine.so。包括了A、B、C三个project。但每次都是源代码形式编译,导致svn上存在多份同样代码拷贝。

很不科学。

。。核心的Bproject由我维护。整个SO编译project由多个人维护。

于是乎偶进行了一次升级:将B源代码从soproject中解耦:将B打成一个静态库,然后编译So的时候链接静态库。


開始行动

基本思路:将B的源代码包到一个guide_b外壳project中。ndk-build生成guide_b.so 的同一时候诱导生成libB.a静态库,然后这个libB.a能够公布。
android的编译文件夹实在蛋疼。eclipse下设置路径难用的非常。还是习惯命令行下的ndk-build。可是ndk-build的前提是:当前路径下必须有一个jni文件夹,且它里面有一个Android.mk文件,以及srcxx子文件夹。里面放了源码

为了遵循android ndk编译这个蛋疼的规定,并且又不破坏Bproject的项目结构(旧的支持xcode和vs编译),在build文件夹下添加一个android子文件夹。创建Android.mk文件,然后通过python脚本将所需源代码文件复制到android/jni下,全部这些操作通过一个run.bat批处理脚本串联,build完毕以后删掉拷贝的源代码和编译中间结果。

将python引入编译很灵活。

踩雷了

拿到编译出的libB.a,放入engine.so编译project中,改动mk文件,头部增加静态库预编译段,

include $(CLEAR_VARS)

LOCAL_MODULE := BModule

LOCAL_SRC_FILES := libB.a

include $(PREBUILT_STATIC_LIBRARY)

在so编译部分载入BModule模块:LOCAL_STATIC_LIBRARIES := BModule

编译so很顺利。可是拿到APPproject中。傻眼了build以后的包仅仅有地图文字,没有底图了。

泪奔了,libB.a和engine.so的编译过程都自我感觉很之良好啊。。。尼玛。仅仅能不断自我打击。暗示一定什么环节出问题了。。

没有crash,文字标注还有,可是底图一直渲染不成功。

。。

排雷的过程

1)将B的源代码放入SO编译project,终于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。最后复制到androidproject中。build APK。

source =>	libB.a	=>	engine.so	=>	apk.

整个蛋疼的定位过程得益于windows的批处理脚本。能够实现半自己主动化。

不断反复这个过程,不断调整log精度。终于定位究竟图瓦片绘制失败的问题:坐标转化函数GetGeoRect的结果错误。导致绘制时候取不到数据

定位问题的解决办法

尼玛,疑问重重:源代码编译没问题。build成静态库,然后再链接就有问题。代码没有修改,为何单单这个核心函数出问题呢?
进一步验证:
GetGeoRect是一个类静态函数。写一个main.cpp,測试libB.a中的该函数是否正确,显式传入制定的參数,build成可运行程序,然后推到手机上运行,详细參见http://blog.csdn.net/ryfdizuo/article/details/28891649 结果函数运行结果正确。

。说明libB.a内这些函数本身没有问题。

问题出在so包的链接阶段

跟组内一经验丰富的哥们讨论,那天恰好周五。下班前还是没结果。。

。晚上回去后回一直在回忆编译的整个过程。想起他无心的一句话:“是不是可能有反复的定义啥的“。最终想到了一个问题,Aproject里面Bproject的两个头文件。当时为了解耦其它人将两个头文件反复拷贝了一份,(明显触犯了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结构体:

旧的:新的:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcnlmZGl6dW8=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

第二天周六,按耐不住奔到公司,更新A模块中的vv.h头文件,build出的so包最终正确了。

总算是找到问题所在了:

A和Bproject中的vv.h和yy.h文件反复。B中vv.h文件近期被更新过。

1)当A、Bproject均採用源代码编译时,终于SO中的GetGeoRect函数内部使用了最新的render_config_t结构体布局(编译器可能依据文件的改动时间等等作为比对条件吧?),因此底图绘制正确。

2)可是当Bprojectbuild成静态库libB.a时。此时build成SO时,GetGeoRect函数定义採用了Aproject中源代码(编译器可能更加信赖源代码吧)。因此render_config_t也採用了旧的内存布局。因此当调用SO执行时。传入GetGeoRect函数的render_config_t的对象採用最新的内存布局,可是内部实际上是按旧的结构体解析和执行,当然结果就全然错了。

一句话总结问题

文件反复拷贝 =》 导致两个地方文件更新不同步 =》导致同一个结构体有两份定义。两种内存布局 =》 导致SO中的全局函数中,libB.a中没有被反复定义的函数採用了新的结构体布局。被反复的函数则採用了旧的内存布局 =》终于结果:传入GetGeoRect函数之前的结构体是新布局,函数内部按旧布局解析,全部參数错乱。

附录


静态库,仅仅是.o文件的集合打包。
动态库文件:没有作用域区分,全部函数都以唯一的全局函数形式存在,C++的函数会被name-mangling处理,假设我们希望直接通过函数名获取so中的函数地址,则使用extern C包裹防止函数名被改动。通过objdump工具能够验证。



一个令人蛋疼的NDK链接错误的更多相关文章

  1. 一个令人蛋疼的 Microsoft.AspNet.FriendlyUrls

    我一个项目都基本上做完了,结果部署到我服务器的时候结果一直报404 找不到 一看global.asax有个路由注册的代码 public static void RegisterRoutes(Route ...

  2. 使用NDK r10构建Cocosd-x v3.2时编译和链接错误的解决办法

    如果你使用NDK r10构建Cocos2d-x v3.2,将会遇到所有测试用例编译错误以及Lua测试用例链接错误. 1. 编译错误 错误信息是: 1 2 3 4 5 6 7 8 /Users/ming ...

  3. 模板函数(template function)出现编译链接错误(link error)之解析

    总的结论:    将template function 或者 template class的完整定义直接放在.h文件中,然后加到要使用这些template function的.cpp文件中. 1. 现 ...

  4. C++常见gcc编译链接错误解决方法

    除非明确说明,本文内容仅针对x86/x86_64的Linux开发环境,有朋友说baidu不到,开个贴记录一下(加粗字体是关键词): 用“-Wl,-Bstatic”指定链接静态库,使用“-Wl,-Bdy ...

  5. 关于ios 程序加载百度地图lib,出现链接错误:找不到符号 (null): _OBJC_CLASS_$_BMKMapManager的解决办法

    报告的错误信息 ld: warning: ignoring file /Users/5012/Documents/sphuang/IOS_project/baidu_map/ShareLocation ...

  6. Treat wchar_t as built-in type不一致导致的链接错误

    今天用VS2013新建了一个工程,生成时出现很多怪异的链接错误,比如: error LNK2019: unresolved external symbol "__declspec(dllim ...

  7. VS编程常见的编译和链接错误

    常见错误1: Error 2 error LNK1120: 1 unresolved externals Error 1 error LNK2019: unresolved external symb ...

  8. Duplicate Symbol链接错误的原因总结和解决方法[转]

    from:http://www.cocoachina.com/bbs/read.php?tid=177492 duplicate symbol是一种常见的链接错误,不像编译错误那样可以直接定位到问题的 ...

  9. [问题记录]libpomelo工程调整编译链接错误

    1. 描述: 如下图所示,出现链接错误.那么链接问题一般也就两块设置: (1)包含路径Additional Library Directories (2)lib库的包含Additional Depen ...

随机推荐

  1. 帆软FineBI试用

    FineBI是帆软软件有限公司推出的一款商业智能(Business Intelligence)产品,FineBI的本质是通过分析企业已有的信息化数据,帮助企业发现并解决存在的问题,预测模拟企业将来的发 ...

  2. zookeeper_相关命令 以及 API

    (区分大小写) 启动ZooKeeper服务        进入主目录下的 /bin 文件夹. zkServer.sh start.  需要每个节点运行启动命令 客户端启动          zkCli ...

  3. cocos2d-x 3.0正式版 vs2013配置

    cocos2d-x 3.0正式版与之前的版本号差异较大,曾经的教程非常多都不使用了. 1.从cocos2d-x官网http://www.cocos2d-x.org下载3.0版的压缩包,随便解压一个位置 ...

  4. 面试基础_03实现strcpy、strcat、strcmp、strlen

    实现代码例如以下: /************************************************************************* > File Name: ...

  5. PHP写文件到指定位置

    <?php $fp = fopen("output.json", "r+"); $flag = fseek($fp, -3, SEEK_END); if( ...

  6. 休假结束,Linus重回内核开发岗位

    在休假反省一个多月之后,Linus Torvalds 又回来了.10 月 22 日爱丁堡举行的欧洲开源峰会上,Linus Torvalds 将与内核维护者们碰头,这是他重新接管Linux内核开发的第一 ...

  7. vim7.4官方源码在vs2013的编译方法及问题总结

    vim7.4发布也有一段时候了,也该是把之前编译的7.3重新编译一下了,于是考虑着到最新的visual studio 2013编译一下,也顺便看看有没有其它问题. 1.安装vs2013,这个应该不用说 ...

  8. Java基础String的方法

    Java基础String的方法 字符串类型写法格式如下: 格式一: String 变量名称; 变量名称=赋值(自定义或传入的变量值); 格式二: String 变量名称=赋值(自定义或传入的变量值); ...

  9. Android学习笔记(23):列表项的容器—AdapterView的子类们

    AdapterView的子类的子类ListView.GridView.Spinner.Gallery.AdapterViewFlipper和StackView都是作为容器使用,Adapter负责提供各 ...

  10. SQL事务+异常

    BEGIN TRAN BEGIN TRY --这里写代码 --这里写代码 END TRY BEGIN CATCH ROLLBACK TRAN END CATCH COMMIT TRAN