http://www.cnblogs.com/sdphome/archive/2011/09/14/2176612.html
移植Android3.0 SDK到freescale imx51_bbg总结
因为以前没有接触过Android系统,所以刚开始的时候对Android系统做了些了解。
Android平台由应用、应用框架、Android运行时、库以及Linux内核五部分组成。
Android的代码结构如下:
|--Makefile 全局的Makefile
|--bionic Bionic含义为仿生,这里面是一些基础的库的源文件
|--bootloader 引导加载器
|--build build目录中的内容不是目标所用的代码,而是编译和配置所需要的脚本和工具
|--dalvil java虚拟机
|--development 程序开发所需要的模板和工具
|--external 目标机器使用的一些库
|--frameworks 应用程序的框架层
|--hardware 与硬件相关的库
|--kernel_imx Linux2.6的源代码
|--packages Android的各种应用程序
|--prebuilt Android的各种平台下编译的预制脚本
|--system Android的底层的一些库
拿到代码后需要先对代码打补丁,打了r10的补丁。对照r10的文档对源码进行编译,依次编译出uboot、uImage然后还有Android系统的三个镜像ramdisk.img、system.img和userdata.img,下面的三个镜像构成了根文件系统。ramdisk中init程序和init.rc、init.xxxx.rc两个启动脚本,完成Android的初始化。
对代码进行编译的时候出现了错误
You are attempting to build on a 32-bit system.
Only 64-bit build environments are supported beyond froyo/2.2
在网上找到这是Android Froyo2.2版本基于32 bit系统的一个问题,默认需要64位机去编译,如果需要32位机去编译的话需要更改一些代码,更改情况如下:
(-表示删除行,+表示添加行)
1、修改build/core目录下的main.mk文件,修改策略为:
ifeq ($(BUILD_OS),linux)
build_arch := $(shell uname -m)
-ifneq (64,$(findstring 64,$(build_arch)))
+ifneq (i686,$(findstring i686,$(build_arch)))
$(warning ******************************************)
$(warning You are attempting to build on a 32-bit system.)
$(warning Only 64-bit build environments are supported beyond froyo/2.2.)
2、修改下列文件:
/external/clearsilver/cgi/Android.mk
/external/clearsilver/cs/Android.mk
/external/clearsilver/java-jni/Android.mk
/external/clearsilver/util/Android.mk
4个文件的修改策略相同,为:
# This forces a 64-bit build for Java6
-LOCAL_CFLAGS += -m64
-LOCAL_LDFLAGS += -m64
+LOCAL_CFLAGS += -m32
+LOCAL_LDFLAGS += -m32
有时候还会出现错误 ERROR:Could not load 'clearsilver-jni',解决方法如下
$make clean
$make update-api
$make
编译之前还需要设定ARCH和CROSS_COMPILE
在对源码进行编译的时候不能直接对源码进行make,需要设定product信息,这里有两种方法进行设置:
方法一:在源码根目录下
$ . build/envsetup.sh
$ lunch #这个时候将会出现源码中能够检测到的产品信息
选择一个编号,然后就可以make了
$ make
上面第二步和第三步也可以合并成一步,假如对产品信息已经知道了的话
$ lunch imx51_bbg-eng #编译imx51_bbg的时候
$ lunch sdk-eng #编译sdk的时候
方法二:直接在make的时候设定产品信息
$ make PRODUCT-imx51_bbg-eng #编译imx51_bbg的时候
编译出SDK后,可以在emulator模拟器上模拟编译出来的Android系统。
这里有一些命令,使用下面的命令时需要对系统的环境变量进行设置, 修改/etc/environment将Android的tools文件夹加入到PATH中。
1.android list target 查看有几个有效的target
2.android create avd -n name -t NO. 创建AVD
3.android list avd 查看是否创建成功
4.emulator @ name 启动模拟器
或者可以使用命令 emulator -avd name
emulator -avd name -skin QVGA 选择皮肤
对于新编译出来的sdk,使用android list target时找不到会出现任何target的现象,解决方法是进入sdk目录,cp -a platform-tools platforms/android-2.3.1/tools。
对于生成的镜像到底里面是什么呢,这个可以从编译输出来的log得到结果,可以看到systrm.img是对同一目录下的system文件夹进行打包的,ramdisk.img是对同一目录下的randisk进行打包的。ramdisk里面只有一些初始化用到的可执行文件和脚本。通过执行init文件来执行对系统的初始化,init的执行即创建了init进程,init进程是由内核启动的一个用户级进程,内核自行启动之后就通过启动一个用户级程序init的方式,来完成引导进程,init始终是第一个进程。
init程序的代码是system/core/init/init.c,在这个进程中首先会解析初始化脚本init.rc [init_parse_config_file("/init.rc");],然后通过get_hardware_name(hardware, &revision); 读取硬件信息,这个函数的定义在system/core/init/util.c中,可以发现它是通过读取"/proc/cpuinfo"中的硬件信息来判断所用平台的。接着解析"/init.%s.rc",这个脚本是解析与硬件平台相关的,模拟器是用的init.goldfish.rc脚本。
在init.rc中会启动一系列service,它是通过将service放入service_list中实现的。对分区的挂载也是在init.rc中完成的,这里需要注意挂载分区的格式。
下载镜像有多种方式,可以下到nandflash中,也可以下载sd卡中,让系统从sd卡进行执行,这里我使用的是下载到sd卡方式。需要对镜像进行重定位处理,将地址信息加入到镜像中。同时还需要对sd卡进行正确的分区,并进行格式化,格式化成预定的格式。要使系统能够正常工作,还有一个地方需要注意的,需要在uboot命令行中查看环境变量的设置,设置正确的启动参数。
下面就开始移植了,首先将系统所有镜像下载到板子里面,确保能够正常启动,然后替换成HoneyComb system.img,会发现启动不了,同时出现了错误
EXT4-fs (mmcblk0p2): VFS: Can't find ext4 filesystem
EXT4-fs (mmcblk0p5): recovery complete
EXT4-fs (mmcblk0p5): mounted filesystem with ordered data mode. Opts: (null)
EXT4-fs (mmcblk0p6): recovery complete
EXT4-fs (mmcblk0p6): mounted filesystem with ordered data mode. Opts: (null)
init: cannot find '/system/bin/servicemanager', disabling 'servicemanager'
init: cannot find '/system/bin/vold', disabling 'vold'
init: cannot find '/system/bin/netd', disabling 'netd'
init: cannot find '/system/bin/dispd', disabling 'dispd'
init: cannot find '/system/bin/debuggerd', disabling 'debuggerd'
init: cannot find '/system/bin/app_process', disabling 'zygote'
init: cannot find '/system/bin/mediaserver', disabling 'media'
init: cannot find '/system/bin/dbus-daemon', disabling 'dbus'
init: cannot find '/system/bin/installd', disabling 'installd'
init: cannot find '/system/etc/install-recovery.sh', disabling 'flash_recovery'
init: cannot find '/system/bin/keystore', disabling 'keystore'
init: cannot find '/system/bin/logwrapper', disabling 'wpa_supplicant'
init: cannot find '/system/bin/rild', disabling 'ril-daemon'
init: cannot find '/system/bin/sh', disabling 'console'
出现这样的原因是system并没有挂载上,查看了下init.rc启动脚本,发现是格式问题,sdk生成的镜像是yaffs格式的,而imx51_bbg使用的是ext4格式的镜像,在init.rc中是按照ext4的格式挂载的,挂载脚本如下:
on fs
mount ext4 /dev/block/mmcblk0p2 /system
mount ext4 /dev/block/mmcblk0p2 /system ro remount
mount ext4 /dev/block/mmcblk0p5 /data nosuid nodev
mount ext4 /dev/block/mmcblk0p6 /cache nosuid nodev
sdk中的挂载脚本如下:
on fs
mount yaffs2 mtd@system /system
mount yaffs2 mtd@system /system ro remount
mount yaffs2 mtd@userdata /data nosuid nodev
mount yaffs2 mtd@cache /cache nosuid nodev
对于system.img的打包需要使用make_ext4fs工具,这个工具在out/host目录下生成了,将它拷贝到/usr/bin/目录下以后就可以直接使用了。
$ make_ext4fs -l 128M -a system system.img 目录
yaffs格式镜像解压方法为unyaffs system.img,unyaffs工具需要下载一个代码进行编译生成可执行文件,然后将它拷贝到/usr/bin/下面。
对于ramdisk.img解包和打包的方法有很多种,其中一种比较快捷的方法是
解包 $mkdir ramdisk && cd ramdisk && { zcat ../ramdisk.img |cpio -iv; cd -; }
打包 $cd ramdisk && { find . |cpio -ov -H newc |gzip > ../ramdisk.img; cd -; }
直接转换成uramdisk.img
$cd ramdisk && { find . |cpio -ov -H newc |gzip > ../ramdisk.img; cd -; } && mkimage -A arm -O linux -T ramdisk -C none -a 0x90308000 -n "Android Root Filesystem" -d ./ramdisk.img ./uramdisk.img
将文件格式改变后发现还是不行,一直出现进程挂掉,
untracked pid ** exited
尝试将system下面的bin、xbin换掉后提示libc.so链接不上,而且还是进不了命令行,然后再将lib文件夹替换掉以后发现可以进入命令行了,但是还是不能进入界面。
然后就开始怀疑是交叉编译链的不同而导致的原因了,因为两种平台的服务器并不相同,模拟器用的是goldfish的虚拟处理器,是基于ARMv5架构的,而imx51_bbg是ARMv7架构的。这就要到Android的编译系统去查找原因了。
Android的编译系统由build 目录下的代码和其他目录下的Android.mk一起组成,Android Makefile 的引用关系是这样的Makefile -> build/core/main.mk -> build/core/config.mk -> build/core/envsetup.mk -> build/core/product_config.mk,在build/core/product_config.mk 中编译系统首先调用 build/core/product.mk中定义的函数get-all-product-makefiles,来遍历所有的AndroidProducts.mk,不同子目录下的AndroidProducts.mk中定义了不同的 PRODUCT_NAME, PRODUCT_DEVICE 等信息,接着build/core/product_config.mk 会调用resolve-short-product-name 将TARGET_PRODUCT匹配的AndroidProducts.mk 中定义的 PRODUCT_DEVICE 赋值给TARGET_DEVICE。
有了这个TARGET_DEVICE,再回到build/core/config.mk中,有
include $(TARGET_DEVICE)/BoardConfig.mk
board_config_mk:=
$(strip $widcard $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)BoardConfig.mk
Vendor/*/$(TARGET_DEVICE)/BoardConfig.mk))
include $(board_config_mk)
BoardConfig.mk文件决定了目标系统编译属性,而且上面的TARGET_DEVICE还决定了TARGET_DEVICE_DIR
TARGET_DEVICE_DIR:= $(patsubst %/,%,$(dir $board_config_mk)))
Android的目标输出目录也是由由TARGET_DEVICE决定的,在build/core/
envsetup.mk中,设定了各种文件的存放目录。再回到main.mk中,编译系统接着会做的一件事情就是会遍历所有子目录下的Android.mk文件,并且把它们都include进来。
在config.mk中,有如下设置
combo_target := HOST_
include $(BUILD_SYSTEM)/combo/select.mk
combo_target := TARGET_
include $(BUILD_SYSTEM)/combo/select.mk
分别对两个平台进行设置,build/core/combo/sselect.mk中有
ifeq ($(TARGET_KERNEL_2G),true)
$(combo_target)PRELINKER_MAP
:= $(BUILD_SYSTEM)/prelink-$(combo_os_arch)-2g.map --------2G/2G方式
else
$(combo_target)PRELINKER_MAP
:= $(BUILD_SYSTEM)/prelink-$(combo_os_arch).map --------3G/1G方式
endif
这边可以看出在这个代码中有两种内存分配方式,一种是2G/2G方式,一种是3G/1G方式,是按照(user/kernel)设置的,这边是对TARGET_KERNEL_2G变量进行判断来分配的,默认是使用3G/1G的方式的。
在device/fsl/imx5x/BoardConfigCommon.mk的结尾处有
TARGET_KERNEL_2G := true 设置了BBG用的是2G/2G的内存模型,而SDK中并没有这个设置。
在map文件中分别将各种内存地址分配好了,将需要预链接的动态库也映射好了。
如在prelink-linux-arm.map中
0xC0000000 - 0xFFFFFFFF Kernel 内核空间1G
0xB0100000 - 0xBFFFFFFF Thread 0 static
0xB0000000 - 0xB00FFFFF Linker
0xA0000000 - 0xBFFFFFFF Prelinked System Libraries
0x90000000 - 0x9FFFFFFF Prelinked App Libraries
0x80000000 - 0x8FFFFFFF Non-prelinked Libraries
0x40000000 - 0x7FFFFFFF mmap’s stuff
0x10000000 - 0x3FFFFFFF Thread Stacks
0x00000000 - 0x0FFFFFFF .text / .data / heap
下面还有需要预链接的每个lib库的加载基地址
........................................
所有的动态库默认是需要预链接的,如果一个动态库不需要预链接,需要对其进行设置 LOCAL_PRELINK_MODULE := false,如果新添一个动态库,需要进行预链接,而map文件中没有这个文件的映射关系的话,编译的时候会出错,解决的方法是将其加入到map文件中,而且还要注意内存对齐,分配的内存不能与其它的重叠。
在编译系统总还有一个与TARGET_KERNEL_2G有关系的地方在bionic\linker\
Android.mk
ifeq ($(TARGET_ARCH),sh) ------这个不成立,进入else
LINKER_TEXT_BASE := 0x70000100
else
ifeq ($(TARGET_KERNEL_2G),true)
LINKER_TEXT_BASE := 0x70001000
else
LINKER_TEXT_BASE := 0xB0001000
endif
endif
下面一些linker的FLAGS也就相应的不同了
ifeq ($(TARGET_KERNEL_2G),true)
LOCAL_CFLAGS += -DKERNEL_IS_2G
endif
LINKER_AREA_SIZE := 0x01000000
LOCAL_LDFLAGS := -Wl,-Ttext,$(LINKER_TEXT_BASE)
LOCAL_CFLAGS += -DPRELINK
LOCAL_CFLAGS += -DLINKER_TEXT_BASE=$(LINKER_TEXT_BASE)
LOCAL_CFLAGS += -DLINKER_AREA_SIZE=$(LINKER_AREA_SIZE)
将原本2G/2G模型换成3G/1G模型后,编译,下载生成的Android 系统镜像,发现不能进入命令行,查看kernel的配置文件,发现kernel中也有关于虚拟内存的配置选项
# CONFIG_VMSPLIT_3G is not set
CONFIG_VMSPLIT_2G =y
# CONFIG_VMSPLIT_1G is not set
原本是2G的,VMSPLIT是虚拟内存分配的意思,这个配置选项有点不够明确,不知道是用户空间还是内核空间分配的内存,通过查看huashe的配置文件发现,huashe的编译系统中设置的是用户空间3G,内核空间1G,kernel的配置中配置的是CONFIG_VMSPLIT_3G=y,由此可见这个是按照用户空间的虚拟内存大小来分配的。
重新编译内核后,下载后,系统恢复正常。
在整个移植过程中最常出现的问题就是netd和mediaservice两个服务不能起来。这个可能是基于不同的服务器架构而引起的。通过替换lib文件夹能够使系统进入命令行。