Book Image

Instant Optimizing Embedded Systems Using BusyBox

Book Image

Instant Optimizing Embedded Systems Using BusyBox

Overview of this book

As hundreds of millions of people have started using Android smartphones, embedded Linux systems are becoming more and more popular. To get more market share, not only for hardware and function piling up, smartphone manufacturers gradually realized the importance of user experience. To improve user experience, the back-end Linux system must be optimized in many ways. Instant Optimizing Embedded System Using BusyBox is a practical, hands-on guide that provides you with a number of clear, step-by-step exercises to help you take advantage of the real power behind Busybox, and give you a good grounding for using it to optimize your embedded (Android Linux) systems. Moving on from the basics, this book will teach you how to configure and compile it from source code, including cross-compiling it with static linking and dynamic linking. You will also learn how to install and use Busybox on the Android emulator. You will learn to replace the simple Android mksh console with Busybox ash console and start a telnet and HTTP service provided by Busybox. You will also build embedded Linux file system from scratch and start it on Android emulator. We will take a look at how to add functionality to Busybox based system, including adding external applets to Busybox, as well as building development environments (like Bash and C) for it manually or with the automatic Buildroot system. If want to learn how to take advantage of using Busybox applets to optimize your embedded system, then this is the book for you for it will also show you how to use the powerful applets to optimize multiple aspects of an embedded (Android Linux) system.This book will teach you how to build an embedded (Android Linux) system with Busybox, enhance its functionality to meet diverse system requirements, and optimize it to provide a better user experience for embedded products.
Table of Contents (8 chapters)

Adding new applets to a BusyBox-based embedded system (Intermediate)


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.

Getting ready

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

How to do it...

To add the sstrip program into BusyBox, follow these steps:

  1. 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 a misc utility so we add it to miscutils/.

    $ cd ~/tools/busybox/
    $ cp ../sstrip/src/sstrip.c miscutils/sstrip.c
    
  2. Provide an applet-specific main entry, sstrip_main():

    The entry of a new applet should be named applet_main(). For sstrip, it should be sstrip_main().

    $ sed -i -e "s/int main/int sstrip_main/g" miscutils/sstrip.c
    
  3. 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.

  4. 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.

  5. 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 the CONFIG_SSTRIP configure option is enabled.


  6. 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 with make install, and also exports the macros about the main entry, usage information, and links.

  7. Use common header files:

    Later, replace all of its header files except <elf.h> with libbb.h.

  8. Configure and compile it:

    To build sstrip in the BusyBox binary, enable it with make 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.

How it works...

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.

There's more...

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 BusyBox ash. 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 Android ttyS device.

    $ adb shell cat /proc/devices | grep ttyS
    253 ttyS
    

    The default tty console is not ttyS0, but ttyS2. So, we need to fix them and repackage ramdisk.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, and C_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.