Skip to content

Latest commit

 

History

History
416 lines (321 loc) · 14.8 KB

0006-busybox-android.asc

File metadata and controls

416 lines (321 loc) · 14.8 KB

编译Android版busybox

📎
反馈与建议,请移步: #7

文章更新历史:

  • 2015/07/23 文章发布


本文介绍如何为Android编译busybox,包括静态链接版和动态链接版。

如果仅是为了开发者自己在手机上使用,应该编译静态链接版本,这样兼容性好,但编译出的可执行性文件较大; 如果是需要在应用中内置一个busybox,这时应该编译动态链接版本,并且仅保留需要的功能,这样可执行文件非常小,但兼容性会差一些。

1. 熟悉busybox官方源码

在为Android编译busybox之前,先熟悉下busybox官方代码和相应的编译方法。

1.1. 下载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'

1.2. 编译Ubuntu本地版练手

先看看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左右。

2. 编译busybox的Android静态链接版

如果要编译busybox的Android静态链接版,可以用busybox官方代码,通过交叉编译工具链即可编译生成。

2.1. 安装交叉编译工具链

首先,查看可供安装的交叉编译工具(仅以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
...

2.2. 编译busybox

生成默认配置的配置文件:

$ 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-"
...

3. 编译:

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