📎
|
反馈与建议,请移步: #7 |
文章更新历史:
-
2015/07/23 文章发布
本文介绍如何为Android编译busybox,包括静态链接版和动态链接版。
如果仅是为了开发者自己在手机上使用,应该编译静态链接版本,这样兼容性好,但编译出的可执行性文件较大; 如果是需要在应用中内置一个busybox,这时应该编译动态链接版本,并且仅保留需要的功能,这样可执行文件非常小,但兼容性会差一些。
在为Android编译busybox之前,先熟悉下busybox官方代码和相应的编译方法。
下载busybox源码:
$ git clone git://busybox.net/busybox.git
下载完成后,可选择切换到最新的稳定分支上:
$ git branch -r ... origin/1_21_stable origin/1_22_stable origin/1_23_stable origin/HEAD -> origin/master origin/master [PWD: ~/work/opensrc/busybox/busybox] (master) $ git checkout origin/1_23_stable -b android 分支 android 设置为跟踪来自 origin 的远程分支 1_23_stable。 切换到一个新分支 'android'
先看看makefile的帮助信息:
[PWD: ~/work/opensrc/busybox/busybox] (android) $ make help ... Configuration: allnoconfig - disable all symbols in .config allyesconfig - enable all symbols in .config (see defconfig) config - text based configurator (of last resort) defconfig - set .config to largest generic configuration menuconfig - interactive curses-based configurator ...
编译一个默认配置版本:
[PWD: ~/work/opensrc/busybox/busybox] (android) $ make defconfig HOSTCC scripts/basic/docproc HOSTCC scripts/kconfig/zconf.tab.o HOSTLD scripts/kconfig/conf scripts/kconfig/conf -d Config.in ... $ make clean busybox -j4 ... $ ll busybox -rwxrwxr-x 1 yongce yongce 861752 7月 21 13:50 busybox $ ldd busybox linux-vdso.so.1 => (0x00007ffc82bb6000) libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fa56aba6000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa56a7dc000) /lib64/ld-linux-x86-64.so.2 (0x00007fa56aeae000) $ ndk-depends busybox WARNING: Could not find library: libm.so.6 WARNING: Could not find library: libc.so.6 busybox libm.so.6 libc.so.6 $ ./busybox BusyBox v1.23.2 (2015-07-21 13:50:45 CST) multi-call binary. ... Currently defined functions: [, [[, acpid, add-shell, addgroup, adduser, adjtimex, arp, arping, ash, awk, base64, basename, beep, blkid, blockdev, bootchartd, brctl, bunzip2, ... $ ./busybox uname -a Linux yongce-XPS-8700 3.19.0-21-generic #21-Ubuntu SMP Sun Jun 14 18:31:11 UTC 2015 x86_64 GNU/Linux
简单解释前面执行的动作:首先,使用命令“make defconfig”创建了一个包含默认配置的配置文件“.config”;然后,基于这个配置文件“.config”,编译生成了一个本地可执行的busybox,这是一个动态链接版本,大小大概800多KB。
接下来,让我们编译一个最小的版本(不包含任何命令):
$ make allnoconfig ... $ make clean busybox -j4 ... $ ll busybox -rwxrwxr-x 1 yongce yongce 6112 7月 21 14:19 busybox* $ ./busybox BusyBox v1.23.2 (2015-07-21 14:19:19 CST) multi-call binary. ... Currently defined functions:
可以看到,不包含任何命令的最小busybox,其动态链接版本大小为6KB左右。
如果要编译busybox的Android静态链接版,可以用busybox官方代码,通过交叉编译工具链即可编译生成。
首先,查看可供安装的交叉编译工具(仅以arm为例):
$ uname -a Linux yongce-XPS-8700 3.19.0-21-generic #21-Ubuntu SMP Sun Jun 14 18:31:11 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux $ apt-cache search "^gcc-arm" gcc-arm-linux-gnueabihf - The GNU C compiler for armhf architecture gcc-arm-linux-androideabi - cross toolchain and binutils for Android/Bionic on ARM gcc-arm-none-eabi - GCC cross compiler for ARM Cortex-A/R/M processors gcc-arm-linux-gnueabi - The GNU C compiler for armel architecture
这里,选择安装“gcc-arm-linux-gnueabi”:
$ sudo apt-get install gcc-arm-linux-gnueabi
安装完成后,所有gcc命令带有前缀“arm-linux-gnueabi-”:。
$ ls -w 1 /usr/bin/arm-linux-gnueabi-* /usr/bin/arm-linux-gnueabi-gcc /usr/bin/arm-linux-gnueabi-gcc-4.7 ...
生成默认配置的配置文件:
$ make defconfig
修改配置文件“.config”,改成静态链接,并使用交叉编译工具链“gcc-arm-linux-gnueabi”:(可通过make menuconfig来配置,也可以直接修改.config文件)
$ make menuconfig ... $ git df diff --git a/.config b/.config ... -# CONFIG_STATIC is not set +CONFIG_STATIC=y ... -CONFIG_CROSS_COMPILER_PREFIX="" +CONFIG_CROSS_COMPILER_PREFIX="arm-linux-gnueabi-" ...
$ make clean busybox -j4 …
$ ll busybox -rwxrwxr-x 1 yongce yongce 2037416 7月 21 14:35 busybox*
$ ndk-depends busybox busybox
$ /home/pub/tools/android-ndk/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-readelf -d busybox
There is no dynamic section in this file.
$ ./busybox bash: ./busybox: cannot execute binary file: 可执行文件格式错误
可以看到,默认配置编译出来的busybox静态版本,大小为2MB左右。当然,在本地机器上是无法执行的。 NOTE: 使用这种方法编译的busybox可在github上下载: https://github.com/yongce/DevTools/blob/7487ff04e190960e1db18c3b64072e8b1759b6a3/app/src/main/assets/busybox_static 。 === 在手机上测试运行 先测试一个Android 2.3的手机:
$ adb shell getprop | grep fingerprint
$ adb push busybox /data/local/tmp/busybox 1307 KB/s (2037416 bytes in 1.521s)
$ adb shell /data/local/tmp/busybox BusyBox v1.23.2 (2015-07-21 14:29:45 CST) multi-call binary. …
$ adb shell /data/local/tmp/busybox uname -a Linux localhost 2.6.38.8-SavagedZen-4N1-BFS+ #20110701 PREEMPT Fri Jul 1 23:57:52 SGT 2011 armv7l GNU/Linux
让我们再测试一个Android 5.0的机器:
$ adb shell getprop | grep fingerprint
$ adb push busybox /data/local/tmp/busybox 4403 KB/s (2037416 bytes in 0.451s)
$ adb shell /data/local/tmp/busybox BusyBox v1.23.2 (2015-07-21 14:29:45 CST) multi-call binary. …
$ adb shell /data/local/tmp/busybox uname -a Linux localhost 3.4.0-perf-g60eefcd #1 SMP PREEMPT Fri Oct 10 18:28:38 UTC 2014 armv7l GNU/Linux
=== 编译一个最小的busybox
$ make allnoconfig …
$ make menuconfig …
$ git df diff --git a/.config b/.config … -# CONFIG_STATIC is not set +CONFIG_STATIC=y … -CONFIG_CROSS_COMPILER_PREFIX="" +CONFIG_CROSS_COMPILER_PREFIX="arm-linux-gnueabi-" …
$ make clean busybox -j4 …
$ ll busybox -rwxrwxr-x 1 yongce yongce 509028 7月 21 15:01 busybox*
$ ./busybox bash: ./busybox: cannot execute binary file: 可执行文件格式错误
可以看到,不包含任何命令的最小静态链接版本也有500KB左右。 == 编译busybox的Android动态链接版 === PIE介绍 Android 4.1引入了PIE(position-independent executables), 在此模式下,DL(Dynamic Linker)在加载动态链接库时,不再加载到一个固定地址上, 从而提高系统的安全性。 Android 5.0强制启用了PIE,要求其上运行的可执行文件必须以PIE模式加载动态链接库。 因此,在Android 5.0上运行的可执行文件,如果是动态链接的,则必须以PIE模式编译。 例如,Android 5.0+的手机上,运行未启用PIE且动态链接的可执行文件会遇到如下错误提示:
$ adb shell /data/local/tmp/busybox error: only position independent executables (PIE) are supported.
而在Android 4.1之前的手机上,运行启用了PIE的可执行文件则会遇到段错误:
$ adb shell /data/local/tmp/busybox [1] Segmentation fault /data/local/tmp/…
因此,如果要编译动态链接的可执行文件,则至少需要编译两个版本, 分别针对Android 4.1之前的系统和Android 4.1及其后的系统(以Android 5.0为条件也可以)。 === 使用Android NDK编译官方源码 由于Android NDK是官方提供的Android native程序编译工具,支持arm, x86, mips等架构。 因此,Android NDK算是编译动态链接版本程序的理想工具。 但由于busybox是按照Linux编程接口开发的,而Android NDK仅支持部分编程接口。 因此,在官方busybox源码基础上编译出来完整功能的busybox是很困难的(很多功能的代码都需要打补丁才能编译)。 回到初衷,我们之所以需要编译动态链接版本的busybox,是因为我们需要一个剪裁版的busybox,并且需要可执行文件尽可能的小。 因此,编译剪裁版busybox,使用Android NDK值得尝试的方案。 在busybox官方源码中,已经有了支持Android NDK的编译配置(文件configs/android_ndk_defconfig)。 这个Android版配置与默认配置的主要差别在一些编译选项上有所不同,如下面列出的5个选项: * CONFIG_CROSS_COMPILER_PREFIX和CONFIG_SYSROOT:分别指定交叉编译工具链前缀和sysroot目录。 * CONFIG_EXTRA_CFLAGS:定义一些宏和指定一些编译选项。 * CONFIG_EXTRA_LDFLAGS:指定一些连接选项 * CONFIG_EXTRA_LDLIBS:指定需要动态链接的库 例如,我使用android-ndk-r10e编译busybox,相应的配置如下:
CONFIG_CROSS_COMPILER_PREFIX="/home/pub/tools/android-ndk/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-" CONFIG_SYSROOT="/home/pub/tools/android-ndk/platforms/android-9/arch-arm" CONFIG_EXTRA_CFLAGS="-DANDROID -DANDROID -DSK_RELEASE -nostdlib -march=armv7-a -msoft-float -mfloat-abi=softfp -mfpu=neon -mthumb -mthumb-interwork -fpic -fno-short-enums -fgcse-after-reload -frename-registers" CONFIG_EXTRA_LDFLAGS="-fuse-ld=bfd -Xlinker -z -Xlinker muldefs -nostdlib -Bdynamic -Xlinker -dynamic-linker -Xlinker /system/bin/linker -Xlinker -z -Xlinker nocopyreloc -Xlinker --no-undefined ${SYSROOT}/usr/lib/crtbegin_dynamic.o ${SYSROOT}/usr/lib/crtend_android.o" CONFIG_EXTRA_LDLIBS="c gcc"
NOTE: 在上面的示例中,需要关注CONFIG_EXTRA_CFLAGS中的-march=armv7-a,这个选项仅支持armv7; 在CONFIG_EXTRA_LDFLAGS选项中,我添加了“-fuse-ld=bfd”,原因是android-ndk-r10e中ld有bug,参见: https://code.google.com/p/android/issues/detail?id=177690 。 例如,我仅启用了部分功能的测试情况:
$ make clean busybox …
$ ll busybox -rwxrwxr-x 1 yongce yongce 70872 7月 23 17:40 busybox*
$ /home/pub/tools/android-ndk/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-readelf -h busybox | grep "Type:" Type: EXEC (Executable file)
$ /home/pub/tools/android-ndk/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-readelf -d busybox | grep "Shared library:" 0x00000001 (NEEDED) Shared library: [libc.so]
$ ndk-depends busybox busybox libc.so
$ adb push busybox /data/local/tmp/busybox 974 KB/s (70872 bytes in 0.070s)
$ adb shell /data/local/tmp/busybox BusyBox v1.23.2 (2015-07-23 17:40:17 CST) multi-call binary. BusyBox is copyrighted by many authors between 1998-2012. Licensed under GPLv2. See source distribution for detailed copyright notices.
Usage: busybox [function [arguments]…] or: busybox --list or: function [arguments]…
BusyBox is a multi-call binary that combines many common Unix utilities into a single executable. Most people will create a link to busybox for each function they wish to use and BusyBox will act like whatever it was invoked as.
Currently defined functions: basename, cat, chgrp, chmod, chown, cp, cut, echo, egrep, env, fgrep, grep, id, ln, ls, mkdir, mv, pwd, readlink, rm, touch, uname, whoami
NOTE: “touch”命令不能启用“-h”选项,否则编译会失败。 在选择功能/编译的过程中,如果遇到编译错误,要么禁用无法编译的命令, 要么修改busybox代码来适应Android。网上也有非常多的相关命令的patch,可以参考。 NOTE: 这里使用的完整配置文件可到github上查看: https://github.com/ycdev-fork/busybox/blob/023db9a8b299f60fd0803d9d7c20e1ea963a446d/configs/android_ndk_ycdev 。 ==== 启用PIE编译 启用PIE比较简单,在CONFIG_EXTRA_CFLAGS中添加选项“-fPIE”, 在CONFIG_EXTRA_LDFLAGS中添加选项“-fPIE -pie”即可:
$ git df diff --git a/.config b/.config … -CONFIG_EXTRA_CFLAGS="-DANDROID -DANDROID -DSK_RELEASE -nostdlib -march=armv7-a -msoft-float -mfloat-abi=softfp -mfpu=neon -mthumb -mthumb-interwork -fpic -fno-short-enums -fgcse-after-reload -frename-registers" -CONFIG_EXTRA_LDFLAGS="-fuse-ld=bfd -Xlinker -z -Xlinker muldefs -nostdlib -Bdynamic -Xlinker -dynamic-linker -Xlinker /system/bin/linker -Xlinker -z -Xlinker nocopyreloc -Xlinker --no-undefined ${SYSROOT}/usr/lib/crtbegin_dynamic.o ${SYSROOT}/usr/lib/crtend_android.o" +CONFIG_EXTRA_CFLAGS="-fPIE -DANDROID -DANDROID -DSK_RELEASE -nostdlib -march=armv7-a -msoft-float -mfloat-abi=softfp -mfpu=neon -mthumb -mthumb-interwork -fpic -fno-short-enums -fgcse-after-reload -frename-registers" +CONFIG_EXTRA_LDFLAGS="-fPIE -pie -fuse-ld=bfd -Xlinker -z -Xlinker muldefs -nostdlib -Bdynamic -Xlinker -dynamic-linker -Xlinker /system/bin/linker -Xlinker -z -Xlinker nocopyreloc -Xlinker --no-undefined ${SYSROOT}/usr/lib/crtbegin_dynamic.o ${SYSROOT}/usr/lib/crtend_android.o" …
编译成功后,可以通过readelf命令查看是否启用了PIE:
$ /home/pub/tools/android-ndk/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-readelf -h busybox | grep "Type:" Type: DYN (Shared object file)
NOTE: 这里使用的完整配置文件可到github上查看: https://github.com/ycdev-fork/busybox/blob/023db9a8b299f60fd0803d9d7c20e1ea963a446d/configs/android_ndk_ycdev_pie 。 === 使用Android源码环境编译CyanogenMod版busybox CyanogenMod移植了busybox,以使其可在Android源码环境中编译。 代码地址如下:
使用CyanogenMod版本的busybox,编译busybox的动态/静态版本都比较容易。但有两个主要缺点: * 相对官方代码有一定的版本滞后性,版本更新不够及时 * 在对busybox裁剪时,没有官方的版本方便