If BusyBox built-in applets are not enough for our embedded requirement, new tools should be added.
This recipe shows three methods to integrate new utilities to a BusyBox-based embedded system.
In order to extend a fork of BusyBox by adding functionality from an existing utility, just compile and install the new utility as a separate binary, which will be explained later. But to distribute one binary exactly to our embedded system and get a smaller-sized system, integrating new applets into BusyBox is a direct method.
To do so, use the small sstrip
program as an example. sstrip
is a small utility that removes the contents at the end of an ELF file that are not part of the program's memory image. ELF (http://en.wikipedia.org/wiki/Executable_and_Linkable_Format) is the common standard file format for Linux executables.
Download it with SVN or download it from this link: https://dev.openwrt.org/browser/trunk/tools/sstrip/src/sstrip.c. In our example, use svn
to check it out.
$ cd ~/tools/ $ svn co svn://dev.openwrt.org/openwrt/trunk/tools/sstrip
To add the sstrip
program into BusyBox, follow these steps:
Add the original tool to the BusyBox source code:
The new applet should be added to a suitable directory of BusyBox. In our example,
sstrip
is amisc
utility so we add it tomiscutils/
.$ cd ~/tools/busybox/ $ cp ../sstrip/src/sstrip.c miscutils/sstrip.c
Provide an applet-specific main entry,
sstrip_main()
:The entry of a new applet should be named
applet_main()
. Forsstrip
, it should besstrip_main()
.$ sed -i -e "s/int main/int sstrip_main/g" miscutils/sstrip.c
Add usage information:
Add the trivial and full usage macros into
miscutils/sstrip.c
.//usage:# define sstrip_trivial_usage //usage:# "sstrip FILE..." //usage:# define sstrip_full_usage "\n\n" //usage:# "sstrip discards all nonessential bytes from an executable.\n\n" //usage:# "Version 2.0-X Copyright (C) 2000,2001 Brian Raiter.\n" //usage:# "Cross-devel hacks Copyright (C) 2004 Manuel Novoa III.\n" //usage:# "This program is free software, licensed under the GNU\n" //usage:# "General Public License. There is absolutely no warranty.\n"
By default, the trivial usage is added to minimize the size of the compiled binary, but if you want verbose output with full usage information, the
CONFIG_FEATURE_VERBOSE_USAGE
option should be enabled.Add configuration support:
We add the configuration description at the end of
miscutils/Config.src
.config SSTRIP bool "sstrip" default n help sstrip is a small utility that removes the contents at the end of an ELF file that are not part of the program's memory image.
This allows users to enable or disable the
sstrip
applet via the configuration utility.Add compiling support:
Add compiling support at the end of
miscutils/Kbuild.src
.lib-$(CONFIG_SSTRIP) += sstrip.o
This builds the
sstrip
binary in the last BusyBox binary when theCONFIG_SSTRIP
configure option is enabled.Add macros declaration about main entry, usage information, links, and the installation directory:
Add the following line to
include/applets.src.h
to add the necessary macros.IF_SSTRIP(APPLET(sstrip, BB_DIR_USR_SBIN, BB_SUID_DROP))
It lets BusyBox install
sstrip
to/usr/bin
at the compiling stage withmake install
, and also exports the macros about the main entry, usage information, and links.Use common header files:
Later, replace all of its header files except
<elf.h>
withlibbb.h
.Configure and compile it:
To build
sstrip
in the BusyBox binary, enable it withmake menuconfig
:Miscellaneous Utilities ---> [*] sstrip
The
docs/new-applet-HOWTO.txt
file provides more information about how to add a new applet, but part of it is out of date. Real examples may be better references; for example,miscutils/chrt.c
.
BusyBox is modular and highly configurable. As seen previously, a new applet can be easily integrated, except renaming the main entry. The configure, build, and header files are added or modified for configuration, compiling, and installation respectively.
Since the main()
entry is used by the BusyBox binary itself, the other applets' entries should be renamed with their own names.
The change in miscutils/Config.src
adds a configure symbol, CONFIG_SSTRIP
, with respective help information.
The modification in miscutils/Kbuild.src
allows sstrip
to be compiled while the CONFIG_SSTRIP
symbol is enabled.
The addition in include/applets.src.h
declares the prototypes of the new applet, sstrip
, and the respective usage, main entry, and links.
Lightweight tools like sstrip
can be simply integrated into the BusyBox package. But for getting more functional utilities for our embedded systems, such methods don't work. However, we do have two other methods.
Build standalone tools:
One method is to compile the existing tools independently and then add them into a BusyBox-based embedded system.
As an example, we add a more powerful shell interpreter, Bash 4.2, into a BusyBox-based embedded system, and then download and compile it for ARM.
$ sudo apt-get install bison autoconf $ cd ~/tools $ git clone git://git.savannah.gnu.org/bash.git $ cd bash $ git checkout -b bash-4.2 f281b8f4f $ ./configure –host=arm-linux-gnueabi --enable-static-link \ --without-bash-malloc $ make
Now the compiled Bash can be put into
ramdisk.img
and used instead of BusyBoxash
. Note that Bash is really powerful for writing shell scripts, but does cost more storage. It should not be used for a size-critical embedded system but rather for testing.Automated building systems:
Another method is to use automated building system; such systems include Buildroot. Buildroot can automate the procedure we performed in the Building BusyBox-based embedded system (Intermediate) recipe. It facilitates the generation of a complete embedded Linux system. It not only includes a root filesystem and a kernel image, but also a cross-compilation toolchain and a bootloader image. The root filesystem integrates BusyBox and other powerful utilities, and even programming environments.
To build
ramdisk.img
automatically with Buildroot, create a configuration file,android_emulator_ramdisk_defconfig
, first:# architecture BR2_arm=y BR2_cortex_a8=y BR2_EXTRA_GCC_CONFIG_OPTIONS="--with-fpu=vfpv3 --with-float=hard" # BR2_SOFT_FLOAT is not set # system BR2_TARGET_GENERIC_HOSTNAME="android-emulator" # development files, required by develop tools BR2_DEPRECATED=y BR2_HAVE_DEVFILES=y # gcc BR2_PACKAGE_GCC_TARGET=y # package with cpio+gzip for init ramfs BR2_TARGET_ROOTFS_CPIO=y BR2_TARGET_ROOTFS_CPIO_GZIP=y # BR2_TARGET_ROOTFS_TAR is not set # bash shell BR2_PACKAGE_BUSYBOX_SHOW_OTHERS=y BR2_PACKAGE_BASH=y
This configuration file allows us to build a BusyBox-based embedded system in the
gzipped cpio
archive with GCC and Bash support for the ARM target with Cortex A8 CPU.Now download, configure, and compile it as follows:
$ cd ~/tools $ wget http://www.buildroot.org/downloads/buildroot-2013.05.tar.bz2 $ tar jxf buildroot-2013.05.tar.bz2 $ cp android_emulator_ramdisk_defconfig .config $ make oldconfig $ make
The default major number of
ttyS
device is not the same as the AndroidttyS
device.$ adb shell cat /proc/devices | grep ttyS 253 ttyS
The default
tty
console is notttyS0
, butttyS2
. So, we need to fix them and repackageramdisk.img
.$ sed -i -e '/ttyS\t/s#4\t#253\t#g' system/device_table_dev.txt $ sed -i -e "/ttyS0/ittyS2::respawn:/sbin/getty -L ttyS2 115200 vt100" output/target/etc/inittab $ make
After compiling, a target ramdisk image,
output/images/rootfs.cpio.gz
, should be ready for the Android emulator.$ cp output/images/rootfs.cpio.gz ramdisk.img $ emulator @busybox-avd -ramdisk ./ramdisk.img \ -show-kernel - shell -no-window ... Welcome to Buildroot android-emulator login:
After booting, log in as a
root
user. We can use the native GCC compiler directly without any extra configuration. The-no-window
option disables window output for this simple root filesystem, and it doesn't include window support.If you want to avoid some of the pitfalls of cross-compilation and want to build C applications directly on the Android device as an exercise, a native C programming environment can be built for Android system by creating another configuration file,
android_emulator_defconfig
.# architecture BR2_arm=y BR2_cortex_a8=y BR2_EXTRA_GCC_CONFIG_OPTIONS="--with-fpu=vfpv3 --with-float=hard" # BR2_SOFT_FLOAT is not set # system BR2_TARGET_GENERIC_HOSTNAME="android-emulator" # development files, required by develop tools BR2_DEPRECATED=y BR2_HAVE_DEVFILES=y # gcc BR2_PACKAGE_GCC_TARGET=y # don't compile busybox in BR2_INIT_NONE=y # BR2_PACKAGE_BUSYBOX is not set # don't tar of the rootfs # BR2_TARGET_ROOTFS_TAR is not set
This configuration file tells us to compile the native compiler, GCC, for our ARM target device without BusyBox support and without packaging the target root filesystem.
Now, configure and compile it as follows:
$ cp android_emulator_defconfig .config $ make oldconfig $ make
Then prepare a configuration,
native-gcc.rc
, for the setting of environment variables as follows:# Compile and Using uClibc library based native gcc compiler in Android system export GCC_VER=4.7.3 export GCC_HOME=/data/gcc/ export GCC_NAME=arm-buildroot-linux-uclibcgnueabi export GCC_LIB=$GCC_HOME/usr/lib/gcc/$GCC_NAME/$GCC_VER/ export GCC_LIBEXEC=$GCC_HOME/usr/libexec/gcc/$GCC_NAME/$GCC_VER/ export PATH=$GCC_HOME/bin:$GCC_HOME/sbin:$GCC_HOME/usr/bin:$GCC_HOME/usr/sbin:$PATH export PATH=$GCC_HOME/usr/lib/ldscripts/:$GCC_LIB:$GCC_LIBEXEC:$PATH export LD_LIBRARY_PATH=$GCC_HOME/usr/lib:$GCC_HOME/lib:$GCC_LIB:$GCC_LIBEXEC:$LD_LIBRARY_PATH export LIBRARY_PATH=$LD_LIBRARY_PATH export C_INCLUDE_PATH=$GCC_HOME/usr/include:$GCC_LIBEXEC/include/:$GCC_LIBEXEC/include-fixed [ ! -d /lib ] && mkdir /lib ln -sf $GCC_HOME/lib/ld-uClibc.so.0 /lib/ld-uClibc.so.0
This file mainly configures the
PATH, LD_LIBRARY_PATH
,LIBRARY_PATH
, andC_LINCLUDE_PATH
environment variables to let GCC works as is.At last, let's push the target directory and configuration to the Android system, and use the native GCC to compile a simple program as follows:
$ adb push output/target /data/gcc $ adb push native-gcc.rc /data/ $ adb shell root@android:/ # source /data/native-gcc.rc root@android:/ # echo -e '#include <stdio.h>\nmain(void){ printf("Hello, Android\\n"); }' | gcc -xc -o t - && ./t Hello, Android
In reality, with Bash (the popular shell-scripting environment) and with C (the popular native programming environment), we're able to re-use lots of existing shell scripts, C software, or write new ones for our BusyBox-based embedded system to enhance its functionality to a very flexible extent.