求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
  
 
 
     
   
分享到
Android跨平台移植经验之谈
 

作者:xwebsite,发布于2013-2-19,来源:CSDN

 

Part 1

元旦休息,闲来无事,又暂无睡意,写点东西捣鼓捣鼓吧!学的东西多了,就怕忘记以前的知识,所以还是记下来比较好。正所谓,好记性不如烂笔头!目前做的一个项目是移植android4.2,所以刚好可以把移植的经验跟大家分享分享,共同进步。尽管界面还没启动起来,但相信到那一天应该不远了。

标题自称为跨平台移植,那么究竟怎样跨平台了呢?出于公司利益的考虑,这里只透露一点点吧!我们现在所用的cpu(面向嵌入式)是公司自主研发的,市场上暂时还没有,其指令架构不同于市场上任何的cpu架构,例如arm、mips等。由于google谷歌官方不支持我们的cpu架构,而我们又想跑android系统,所以就需要把它移植到我们的平台上。架构不同,必然要使用不同的编译器,公司也自己开发了一个编译器。

android使用的kernel也是linux,所以在移植android之前,必须先把linux kernel移植好,这部份工作由kernel团队的成员完成,我很少参与,这里大概说个情况吧。首先就是要选择kernel的版本,在google官方,JellyBean(android 4.x版本的代号)搭配的kernel是3.0.x版本的,所以android4.2至少应该选择一个3.0或以上的kernel。我们选择的是3.4,为何要选择如此高版本的kernel?因为android与kernel的版本更新太快了,我们不想google官方出一个新版本,我们就要重新移植一次kernel,所以就选择版本比较高的kernel,用来兼容未来的android版本。版本定下来后,还要考虑用哪里的linux源代码,因为目前支持android的kernel有多种多样,这些都是被各大芯片厂商或者手机厂商修改过的,所以要选择最合适自己架构的kernel。kernel源码下载下来后,把里面架构相关的代码修改成自己平台的代码,其实主要是汇编级的代码。修改完毕后,做一个根文件系统,让kernel能启动到shell。最后就是开发各种驱动,不断地支持android的移植工作。

对于android这边的工作,首先下载4.2的源码,用git管理,源码里面有多个分支,我们一般不会跟着master分支走,因为master分支每天都有更新,很难维护。一般checkout到某个分支上,例如android-4.2_r1。android4.2里面支持3种架构:arm、mips、x86,它们的代码都混杂在一起,编译系统是如何区分它们的?是通过android自己的Makefile区分的,在一些目录下面的Android.mk文件中,可以看到里面有语句判断是哪个架构的;还有,在一些头文件(xx.h)里面也能看到#ifdef或者#ifndef等,这些架构信息都是在编译时通过读取环境变量得到的,编译系统会根据不同的环境变量来编译需要的代码。所以,我们需要修改android的编译脚本,把自己的架构添加进去,具体修改的地方有build/和device/目录下的编译脚本等。修改后的效果是,在编译前,执行source build/envsetup.sh,然后lunch,能看到自己的平台,这是最基本的一步。跟着,我们要了解有哪些模块涉及到架构相关的代码。经过一番摸索,终于知道了有哪些模块跟架构相关,这里给各位看官列出来:

  1. bionic:仿生库,其实这个不用说大家也知道,bionic提供libc、libstdc++等函数库以及一些系统调用,里面很多头文件都由kernel导过来,所以必然有架构相关代码。要想android能够跑起来,bionic一定要移植成功。其实bionic的移植工作非常困难,公司是由一个有多年工具链开发经验的同事做的。
  2. llvm:一个编译器的后端,前端是clang。涉及编译器相关知识,不太了解,clang是apple公司开发的一款编译器,貌似性能要比gcc好。
  3. v8:JavaScript引擎,在浏览器中用到,分为解析执行和编译执行,解析执行时会产生汇编代码,所以要修改汇编代码生成器。
  4. libbcc:RenderScript需要依赖于libbcc和llvm。
  5. RenderScript:由于2和4的原因,所以RenderScript也涉及架构相关的代码。RenderScript是一个3D的画图库,功能类似于openGL,虽然没有openGL强大,但是其开发流程比较简单,而且拥有高性能的3D渲染效果。主要用在动态壁纸的实现等。
  6. dalvik:android的java虚拟机,这家伙也是一大块啊,涉及很多编译原理。

可能还有些跟架构相关的模块没列出来,本人目前只记得这些了,欢迎补充!android的启动不需要依赖于上面所有的模块,所以我们的计划是先把必须的模块移植好,先让系统起来。在上面的模块中,我们只需要移植好bionic和dalvik就能把系统运行起来。

必要的架构相关的模块移植好后,下一步要做的是编译一个android的最小系统,并把镜像做出来。编译的话,只能编到哪里出错就解决哪里的问题了。如果人手足够的话,可以把最小系统的模块分工好,让其他人员一个模块一个模块地扫过去,把不能编译的都修复好。修复的内容主要是,缺少架构的定义,缺少头文件,缺少某些变量和宏的定义等。这些一般都是修改一些xx.mk文件添加架构支持,或者在bionic中导入所需要的头文件,定义变量、宏等。这样分工,能加快进展速度。

最小系统编译完成后,就要做image,烧到SD卡或者nanflash中启动,什么样的根文件系统格式配什么样的镜像,大家要弄清楚。

启动的第一步当然是启动init进程,接着是把必要的服务运行起来,直到shell能用。这时,就能调试servicemanager和surfaceflinger等服务了,幸运的话,一般起来都不会有太大的问题。surfaceflinger可以用bootanimation验证它的功能,如果看到android的文字logo,就证明surfaceflinger能工作。如果shell都不能启动的话,可以跑一下在android源码中system/extra/tests下面的测试程序,主要是测试kernel是否为启动android作了足够的准备。在jni层的服务都能启动后,接下来就是调试zygote了,zygote里面开始进入java世界,调试zygote是最难的一关,总会遇到各种segmentfault,很恐怖的说。。。。。

目前的进展只到这里,等到系统的界面出来后再跟大家接着分享吧!!

Part 2

经过一翻折腾后,终于看到界面了,尽管很难看!呵呵!导致这一问题的原因是RenderScript没移植好,另外,还有部分的服务没启动起来。接下来是把其它服务和模块逐一地移植上去,启动界面的过程中遇到挺多问题的,有空再跟大家分享!先上图!左边紫色那块的数字是时间9:xx。

问题:

启动界面遇到一个最重要的错误是产生了不对齐的地址访问,这个不对齐的地址访问会导致segment fault(段错误),打印的log如下:

page_fault() #2: sending SIGSEGV to ActivityManager for invalid read access from
 31f90000 (epc == 2fdbceb4, ra == 2b9993b4)
 F/libc    (  272): Failed while talking to debuggerd: Bad file number
 I/DEBUG   (   43): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
 I/DEBUG   (   43): Build fingerprint: 'Android/mini_duck/duck:4.2/JOP40C/eng.zengdaquan.20130104.113607:
eng/test-keys'
 I/DEBUG   (   43): Revision: '0'
 I/DEBUG   (   43): pid: 272, tid: 287, name: ActivityManager  >>> system_server <<<
 I/DEBUG   (   43): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 31f90000
 I/DEBUG   (   43):  zr 00000000  at 00000007  v0 00060000  v1 555b9c80
 I/DEBUG   (   43):  a0 555ba070  a1 b8500001  a2 321071cd  a3 00000001
 I/DEBUG   (   43):  t0 31e83ed0  t1 2e69b308  t2 2d634a70  t3 2e68cf52
 I/DEBUG   (   43):  t4 00000000  t5 ffffffec  t6 00000001  t7 00000001
 I/DEBUG   (   43):  s0 00000000  s1 555b9c18  s2 00000008  s3 00000018
 I/DEBUG   (   43):  s4 00000000  s5 00000000  s6 31e83e90  s7 00000000
 I/DEBUG   (   43):  t8 ffffffff  t9 2fdbceb0  k0 00000000  k1 00000000
 I/DEBUG   (   43):  gp 2baa17d0  sp 31f9fb50  s8 0000004c  ra 2b9993b4
 I/DEBUG   (   43):  hi 00554909  lo 00000000 bva 2ab9d3c0 epc 2fdbceb4
 I/DEBUG   (   43):
 I/DEBUG   (   43): backtrace:
 I/DEBUG   (   43):     #00  pc 0003ceb4  /system/lib/libjavacore.so
 I/DEBUG   (   43):     #01  pc 000293b0  /system/lib/libdvm.so (dvmPlatformInvoke+340)
 I/DEBUG   (   43):
 I/DEBUG   (   43): stack:
 I/DEBUG   (   43):          31f9fb10  003fffff
 I/DEBUG   (   43):          31f9fb14  0000b97a
 I/DEBUG   (   43):          31f9fb18  0000c680
 I/DEBUG   (   43):          31f9fb1c  2bef11c0  /dev/ashmem/dalvik-heap (deleted)
 I/DEBUG   (   43):          31f9fb20  00001204
 I/DEBUG   (   43):          31f9fb24  00000000
 I/DEBUG   (   43):          31f9fb28  00000000
 I/DEBUG   (   43):          31f9fb2c  00000000
 I/DEBUG   (   43):          31f9fb30  00000000
 I/DEBUG   (   43):          31f9fb34  00000000
 I/DEBUG   (   43):          31f9fb38  00000000
 I/DEBUG   (   43):          31f9fb3c  00000000
 I/DEBUG   (   43):          31f9fb40  00000001
 I/DEBUG   (   43):          31f9fb44  2ba9c1c8  /system/lib/libdvm.so (gDvm+896)
 I/DEBUG   (   43):          31f9fb48  00000001
 I/DEBUG   (   43):          31f9fb4c  00000000
 I/DEBUG   (   43):     #00  31f9fb50  2b9e4948  /system/lib/libdvm.so (dvmCallJNIMethod(unsigned int const*, 
JValue*, Method const*, Thread*)+488)
 I/DEBUG   (   43):          ........  ........
 I/DEBUG   (   43):     #01  31f9fb50  2b9e4948  /system/lib/libdvm.so (dvmCallJNIMethod(unsigned int const*,
 JValue*, Method const*, Thread*)+488)
 I/DEBUG   (   43):          31f9fb54  555b9c18  [heap]
 I/DEBUG   (   43):          31f9fb58  2fdbceb0  /system/lib/libjavacore.so
 I/DEBUG   (   43):          31f9fb5c  2d547f00  /dev/ashmem/dalvik-LinearAlloc (deleted)
 I/DEBUG   (   43):          31f9fb60  555b9c18  [heap]
 I/DEBUG   (   43):          31f9fb64  555b9c08  [heap]
 I/DEBUG   (   43):          31f9fb68  00000000
 ...

从中可以看到”epc 2fdbceb4“这条信息,表示发生错误的pc指向2fdbceb4,这是谁的空间呢,再看

 I/DEBUG   (   43): backtrace:
 I/DEBUG   (   43):     #00  pc 0003ceb4  /system/lib/libjavacore.so
 I/DEBUG   (   43):     #01  pc 000293b0  /system/lib/libdvm.so (dvmPlatformInvoke+340) 

这3行信息,pc是从#01跳到#00的,而#00的pc偏移量刚好是epc的最后几位,所以libjavacore.so就是发生错误的动态库。用objdump反汇编libjavacore.so,查看偏移量指向的指令是

3ceb4: 60660000 ld.w $3,0($6)

这条指令的意思是读取6号寄存器偏移量为0的内容,放到3号寄存器,我们可以从上面打印的log中看到6号寄存器的内容是321071cd,16进制的,最后一位d表示13,是基地址,我现在用的cpu不支持不对齐的访问,所以就产生了segment fault。

如何处理这个错误!?

首先是调查哪句代码产生了不对齐的访问,用命令“addr2line -e libjavacore.so 3ceb4”可以跟踪到具体哪行代码(addr2line的使用请参考其它资料,这里不再说明)。查看到的代码是

libcore/luni/src/main/native/libcore_io_Memory.cpp:284
 
283 static jint Memory_peekInt(JNIEnv*, jclass, jint srcAddress, jboolean swap) {
 284     jint result = *cast<const jint*>(srcAddress);
 285     if (swap) {
 286         result = bswap_32(result);
 287     }
 288     return result;
 289 } 

284行是jint result = *cast<const jint*>(srcAddress),上面说到读取6号寄存器时产生了错误,6号寄存器是a2,也就是函数的第3个参数,这里srcAddress刚好是第3个参数,所以符合产生错误的逻辑。既然直接读取srcAddress的值会产生不对齐的访问,那么我们就换另外一种方式去读取。有那些方式可以避免不对齐的访问呢?可以用memcpy,它读取内容的方式是一个字节一个字节的读取的,不理会地址是否对齐。所以用它来间接读取srcAddress就能解决这一问题,代码修改后如下:

static jint Memory_peekInt(JNIEnv*, jclass, jint srcAddress, jboolean swap) {
      jint result;
      memcpy((void*)&result, (void*)(srcAddress),sizeof(jint));
      if (swap) {
          result = bswap_32(result);
      }
      return result;
   } 

其实,这只是一个暂时的解决办法,不对齐的操作应该还是在kernel里面处理的,现在只能等到kernel团队的人有空的时候再做修改。

Part 3

修了一个bug之后,能显示成这样子了,没有显示背景主要是因为wallpaper service没启动好。Go ahead~

Part 4

架构相关的模块移植好之后,剩下的都是一些很零散的问题,例如android跟kernel的磨合,驱动的debug,android自身的各种服务和它们之间的机制问题等!只能一个一个地去修bug了!目前android系统已基本上运行起来!

到此,本文也应该到了收笔之处。感谢大家一直以来的关注和支持!


 
分享到
 
 


android人机界面指南
Android手机开发(一)
Android手机开发(二)
Android手机开发(三)
Android手机开发(四)
iPhone消息推送机制实现探讨
手机软件测试用例设计实践
手机客户端UI测试分析
手机软件自动化测试研究报告
更多...   


Android高级移动应用程序
Android应用开发
Android系统开发
手机软件测试
嵌入式软件测试
Android软、硬、云整合


领先IT公司 android开发平台最佳实践
北京 Android开发技术进阶
某新能源领域企业 Android开发技术
某航天公司 Android、IOS应用软件开发
阿尔卡特 Linux内核驱动
艾默生 嵌入式软件架构设计
西门子 嵌入式架构设计
更多...