$ 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裁剪时,没有官方的版本方便