Bootstrap

android busybox 编译,AndroidDevNotes/0006-busybox-android.asc at master · yongce/AndroidDevNotes · Git...

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

;