Author: Mugabi Siro

Category: GNU/Linux Toolchain

Summary:

This entry presents a case study on using a crosstool-NG built toolchain for an ARM target. A QEMU emulated ARM machine is considered here as the embedded target. Ubuntu (amd64) is used as the cross-build and QEMU VM host machine. These instructions were originally performed on Ubuntu 12.04 LTS. They should still apply for the current Ubuntu LTS, 14.04.

Tags: gnu/linux toolchain gnu linux qemu cross-compiling arm

Overview

This guide describes the process of building and using a crosstool-NG based cross-toolchain for an ARM target. The QEMU emulated Vexpress platform running Linaro OpenEmbedded is used here as the embedded target.

The general flow of the guide is as follows:

  • Generate the Linaro OpenEmbedded root filesystem from its binary components.

  • Build a cross-toolchain with crosstool-NG to match the architecture level, ABI and C library version of the Linaro OE distribution.

  • Build a Linux ARM kernel for the QEMU target with the crosstool-NG based toolchain.

  • Build 3rd party software with the crosstool-NG based toolchain and test it on the QEMU/OE target.

Obtaining a Linaro OpenEmbedded Release

Linaro maintains and releases a number of popular Linux based distributions including OpenEmbedded, Ubuntu and Android OS for select ARM platforms. A Linaro GNU/Linux ARM distribution comprises of two parts:

  • A board independent root filesystem.

  • A SoC/Board hardware package (or simply, hwpack) that provides board specific bits such as the kernel, bootloader and Device Tree Blob (DTB) (if applicable). A list of supported boards and their corresponding hwpacks can be viewed here

This separation allows a degree of flexibility: it is common practice to replace a given component of a distribution package with a different kernel or root filesystem artifact.

Install linaro-media-create

  • Via Ubuntu

    On Ubuntu 14.04, simply run:

    sudo apt-get update
    sudo apt-get install linaro-image-tools
    

    For earlier releases such as 12.04, it may be necessary to first configure APT to include the Linaro PPA i.e.

    sudo add-apt-repository ppa:linaro-maintainers/tools
    sudo apt-get update
    sudo apt-get install linaro-image-tools
    
  • Direct Download from the Linaro release archives

    $ wget http://releases.linaro.org/13.09/components/platform/linaro-image-tools/linaro-image-tools-2013.09.tar.gz
    

Generate an OpenEmbedded disk image

The following set of commands generate and populate an OpenEmbedded disk image.

$ WORKDIR=$PWD
$ wget -c http://releases.linaro.org/13.11/openembedded/vexpress-lsk/hwpack_linaro-lsk-vexpress_20131125-536_armhf_supported.tar.gz
$ wget -c http://releases.linaro.org/13.11/openembedded/vexpress-lsk/linaro-image-alip-genericarmv7a-20131126-163.rootfs.tar.gz
$ sudo linaro-media-create --image-file oe-alip_vexpress.img --dev vexpress --hwpack hwpack_linaro-lsk-vexpress_20131125-536_armhf_supported.tar.gz --binary linaro-image-alip-genericarmv7a-20131126-163.rootfs.tar.gz

NOTES:

  • Since the image being built is for a QEMU target, the --image-file oe-alip-vexpress.img option is used. If installing on an SD card, use --mmc /dev/sdX instead.

  • This image generation process does not require/involve a cross-toolchain.

The following disk image mounts will be referred to throughout the guide:

$ sudo losetup /dev/loop0 oe-alip_vexpress.img 
$ sudo fdisk -u -l /dev/loop0
Disk /dev/loop0: 3221 MB, 3221225472 bytes
255 heads, 63 sectors/track, 391 cylinders, total 6291456 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000

    Device Boot      Start         End      Blocks   Id  System
/dev/loop0p1   *          63      106494       53216    e  W95 FAT16 (LBA)
/dev/loop0p2          106496     6291455     3092480   83  Linux

$ sudo losetup -o $((63*512)) /dev/loop1 /dev/loop0
$ sudo losetup -o $((106496*512)) /dev/loop2 /dev/loop0
$ mkdir {boot,rfs}
$ sudo mount /dev/loop1 boot
$ sudo mount /dev/loop2 rfs
$ ls boot/
boot.ini  u-boot.bin             uImage            v2p-ca9.dtb
boot.scr  uefi_v2p-ca15-tc1.bin  v2p-ca15-tc1.dtb
boot.txt  uefi_v2p-ca15-tc2.bin  v2p-ca15-tc2.dtb
rtsm      uefi_v2p-ca5s.bin      v2p-ca5s.dtb

$ ls rfs/
bin   dev  home  lost+found  mnt   run   sys  usr
boot  etc  lib   media       proc  sbin  tmp  var

Build an ARM Cross-Toolchain with crosstool-NG

Toolchain Considerations for the QEMU/OE Target

Before embarking on configuring and building the toolchain, certain toolchain-related aspects of the QEMU Vexpress/OpenEmbedded target need to be considered:

  • ARM Arch Level, CPU and Endianism of the QEMU Vexpress Platform:

    ARMv7-A, Cortex-A9, 32-bit, little-endian

  • ABI and Floating Point Support:

    $ cd $WORKDIR
    $ readelf -A rfs/lib/libc-2.18-2013.10.so | grep FP
        Tag_FP_arch: VFPv3
        Tag_ABI_FP_rounding: Needed
        Tag_ABI_FP_denormal: Needed
        Tag_ABI_FP_exceptions: Needed
        Tag_ABI_FP_number_model: IEEE 754
        Tag_ABI_HardFP_use: SP and DP
        Tag_ABI_VFP_args: VFP registers
    

    In other words, its root filesystem artifact is armhf.

  • C Library and Toolchain Component Versions:

    The OpenEmbedded target C library is eglibc and so the toolchain of choice will have to be eglibc-based.

    Also note that a (significant) mismatch between the cross-toolchain components (especially the C library) and the target's native components may result in improper or even non-functioning binaries1. Version info on the OpenEmbedded target's C library can be obtained via:

    $ qemu-arm -L rfs rfs/lib/libc-2.18-2013.10.so 
    (...)
    Compiled by GNU CC version 4.8.3 20131111 (prerelease).
    Compiled on a Linux 3.10.0 system on 2013-11-22.
    (...)
    
    $ file rfs/lib/libc-2.18-2013.10.so
    (...)/rfs/lib/libc-2.18-2013.10.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.16, stripped
    

    These commands yeild information such as the Linux version the OpenEmbedded target's eglibc was built against (v3.10.0) and the minimum kernel version it supports (v2.6.16)2.

Cross-Toolchain Build

Support in upstream crosstool-NG for the linaro toolchain was added shortly after the 1.10 release. See Using crosstool-NG for general background coverage on using the crosstool-NG utility.

Linaro Toolchain Working Group now directly support a fork of crosstool-NG for building its eglibc-based toolchain. The release of the Linaro crosstool-NG sources used in this guide was downloaded from here. Below are build steps that were perfomed against the package:

$ cd $WORKDIR
$ mkdir {ctng-linaro-temp,ctng-linaro-2013.10-tarballs}
$ CTNG_LINARO_TEMP="${PWD}/ctng-linaro-temp"
$ tar xjf crosstool-ng-linaro-1.13.1-4.8-2013.10.tar.bz2 
$ cd crosstool-ng-linaro-1.13.1-4.8-2013.10/
$ ./configure --prefix=${CTNG_LINARO_TEMP}
$ make && make install
$ cd ${CTNG_LINARO_TEMP}
$ bin/ct-ng menuconfig

The following are the options that were explicitly set/enabled/disabled. The rest of the options were left at their default settings:

  • Under Paths and misc options:

    • Enable Try features marked as EXPERIMENTAL (CT_EXPERIMENTAL)
    • Set Local tarballs directory (CT_LOCAL_TARBALLS_DIR) to ${PWD}/../ctng-linaro-2013.10-tarballs
    • Select Save new tarballs (CT_SAVE_TARBALLS)
    • Set Prefix directory (CT_PREFIX_DIR) to ${PWD}/../x-tools/${CT_TARGET}

      Note that this will be the installation directory of the cross-toolchain binaries. If some other value is chosen, it should, preferably, be a non-system dirctory e.g. /usr.

      The value of ${CT_TARGET} is computed by crosstool-NG and translates to the toolchain tuple. See Tuple's vendor string under Toolchain options below.

    • If Remove the prefix directory prior to building (CT_RM_RF_PREFIX_DIR) is selected/enabled, and if performing a re-build, then the toolchain installation directory specified against CT_PREFIX_DIR above will be first deleted (if it already exists) then created for the new toolchain installation.

    • Set Number of parallel jobs (CT_PARALLEL_JOBS) to an appropriate value. The default value, zero, instructs crosstool-NG to set the number of make jobs based on the number of processors the build host has.
  • Under Target Options:

    • Set Target Architecture to arm
    • Select Use MMU (CT_ARCH_USE_MMU)
    • Set Endianess to Little endian
    • Set Bitness to 32-bit
    • Set Architecture level (CT_ARCH_ARCH) to armv7-a

      This option corresponds to the gcc configuration flag --with-arch= and runtime flag -march=. Values can be seen in ARM Options in gcc(1) under -march=name

    • Emit assembly for CPU (CT_ARCH_CPU) may be left unset/blank

      This option corresponds to the gcc configuration flag --with-cpu and runtime flag -mcpu=. This option causes gcc to restrict the instruction set to the CPU specified. See gcc(1) for a more detailed explanation.

    • Set Tune for CPU (CT_ARCH_TUNE) to cortex-a9

      This option corresponds to the gcc configuration flag --with-tune= and the runtime flag -mtune=. Values can be seen in ARM Options in gcc(1) under -mcpu=name.

    • Set Use specific FPU (CT_ARCH_FPU) to a value such as vfpv3-d16 or neon. vfpv3-d16 was used in the case-study.

    • Select hardware (FPU) for Floating point:.

      Selecting this value will result in a toolchain that generates an EABI for armhf.

    • Set Default instruction set mode to thumb (CT_ARCH_ARM_MODE_THUMB)

    • Enable Use EABI (CT_ARCH_ARM_EABI)

  • Under Toolchain options

    • (Optional) Set Tuple's vendor string (CT_TARGET_VENDOR) to linaro.

      Setting this value will result in the toolchain tuple arm-linaro-linux-gnueabihf-. Otherwise, by default, the tuple will be set to arm-unknown-linux-gnueabihf-.

  • Under Operating System:

    • Set Target OS to Linux (CT_KERNEL_LINUX)
    • You may leave the Linux kernel version value to the default i.e. 3.1.1. This is the kernel version that the C Library will be built against.
    • Enable Build shared libraries (CT_SHARED_LIBS)
  • Under C compiler (gcc):

    • Enable Show Linaro versions (EXPERIMENTAL) (CT_CC_GCC_SHOW_LINARO)
    • Set gcc version to linaro-4.7-2013.10 (EXPERMINTAL)

    • Select other compilers e.g. C++, fortran and Java. C++ support (CT_CC_LANG_CXX) is required for a case study in the next section.

      On the Ubuntu 12.04 x86_64 build host, selecting Objective-C and Objective-C++ resulted in the following warning messages during toolchain build:

      [INFO ]  Installing final compiler
      [WARN ]    Building Objective-C language is not yet supported. Will try...
      [WARN ]    Building Objective-C++ language is not yet supported. Will try...
      
    • Enable GRAPHITE loop optimisations (CT_CC_GCC_USE_GRAPHITE) was disabled (for now) to avoid selecting PPL which may lead to build complications. There exist workarounds but this won't be covered here.

  • Under C-library:

    • Set eglibc version to Linaro 2.18-2013.10.

      This is done for the sake of matching C library versions of the toolchain and the Linaro QEMU/OpenEmbedded target.

    • Minimum supported kernel version was left to the default value, Same as kernel headers (CT_LIBC_GBLIC_KERNEL_VERSION_AS_HEADERS) i.e. the Linux kernel version value 3.1.1 under the Operating System top-level option.

      Note that this has some implications. Userspace applications compiled against this toolchain will not run on kernel's with versions less than v3.1.1 and, instead, may produce the following error message:

      FATAL: kernel too old
      

      See Section GNU/Linux Toolchain intro for a general discussion on this issue. Otherwise, set this value to, say, 2.6.16 via Specify kernel version (CT_LIBC_GLIBC_KERNEL_VERSION_CHOSEN) in order to support older kernels.

For now, leave all options under Debug facilities disabled. Save and exit the menuconfig interface. Build.

$ bin/ct-ng build
[INFO ]  Performing some trivial sanity checks
[INFO ]  Build started 20140108.092813
[INFO ]  Building environment variables
[INFO ]  =================================================================
[INFO ]  Retrieving needed toolchain components' tarballs
[INFO ]  Retrieving needed toolchain components' tarballs: done in 0.15s (at 00:02)
[INFO ]  =================================================================
[INFO ]  Extracting and patching toolchain components
[INFO ]  Extracting and patching toolchain components: done in 173.47s (at 02:55)
[INFO ]  =================================================================
[INFO ]  Installing GMP
[INFO ]  Installing GMP: done in 79.07s (at 04:14)
[INFO ]  =================================================================
[INFO ]  Installing MPFR
[INFO ]  Installing MPFR: done in 40.01s (at 04:54)
[INFO ]  =================================================================
[INFO ]  Installing MPC
[INFO ]  Installing MPC: done in 14.12s (at 05:09)
[INFO ]  =================================================================
[INFO ]  Installing zlib
[INFO ]  Installing zlib: done in 4.43s (at 05:13)
[INFO ]  =================================================================
[INFO ]  Installing binutils
[INFO ]  Installing binutils: done in 139.76s (at 07:33)
[INFO ]  =================================================================
[INFO ]  Installing static core C compiler
[INFO ]  Installing static core C compiler: done in 1095.16s (at 25:48)
[INFO ]  =================================================================
[INFO ]  Installing kernel headers
[INFO ]  Installing kernel headers: done in 11.45s (at 25:59)
[INFO ]  =================================================================
[INFO ]  Installing C library headers & start files
[INFO ]  Installing C library headers & start files: done in 34.48s (at 26:34)
[INFO ]  =================================================================
[INFO ]  Installing shared core C compiler
[INFO ]  Installing shared core C compiler: done in 1529.28s (at 52:03)
[INFO ]  =================================================================
[INFO ]  Installing C library
[INFO ]  Installing C library: done in 848.56s (at 66:12)
[INFO ]  =================================================================
[INFO ]  Installing final compiler
[INFO ]  Installing final compiler: done in 2818.72s (at 113:11)
[INFO ]  =================================================================
[INFO ]  Cleaning-up the toolchain's directory
[INFO ]    Stripping all toolchain executables
[INFO ]  Cleaning-up the toolchain's directory: done in 3.20s (at 113:14)
[INFO ]  Build completed at 20140108.112126
[INFO ]  (elapsed: 113:13.35)
[INFO ]  Finishing installation (may take a few seconds)...

and viewing the fresh installation...

$ cd $WORKDIR
$ tree -L 3 x-tools/
x-tools/
└── arm-linaro-linux-gnueabihf
    ├── arm-linaro-linux-gnueabihf
    │   ├── bin
    │   ├── debug-root
    │   ├── include
    │   ├── lib
    │   ├── lib32 -> lib
    │   ├── lib64 -> lib
    │   └── sysroot
    ├── bin
    │   ├── arm-linaro-linux-gnueabihf-addr2line
    (...)
    │   ├── arm-linaro-linux-gnueabihf-g++
    │   ├── arm-linaro-linux-gnueabihf-gcc
    │   ├── arm-linaro-linux-gnueabihf-gcc-4.7.4
    │   ├── arm-linaro-linux-gnueabihf-gcc-ar
    │   ├── arm-linaro-linux-gnueabihf-gcc-nm
    (...)
    │   ├── arm-linaro-linux-gnueabihf-size
    │   ├── arm-linaro-linux-gnueabihf-strings
    │   └── arm-linaro-linux-gnueabihf-strip
    ├── build.log.bz2
    ├── include
    ├── lib
    │   ├── gcc
    │   ├── gcj-4.7.4-13
    │   ├── libiberty.a
    │   ├── logging.properties
    │   ├── pkgconfig
    │   └── security
    ├── libexec
    │   └── gcc
    └── share
        ├── gcc-4.7.4
        └── java

At this point, $CTNG_LINARO_TEMP (about 5GB) may be deleted to save disk space:

$ rm -rf $CTNG_LINARO_TEMP

Building a Linux ARM kernel using the crosstool-NG based toolchain

Recall that the boot partition of the generated OpenEmbedded disk image already contains a functional kernel:

$ cd $WORKDIR
$ ls boot/
boot.ini  u-boot.bin             uImage            v2p-ca9.dtb
boot.scr  uefi_v2p-ca15-tc1.bin  v2p-ca15-tc1.dtb
boot.txt  uefi_v2p-ca15-tc2.bin  v2p-ca15-tc2.dtb
rtsm      uefi_v2p-ca5s.bin      v2p-ca5s.dtb

Nevertheless, kernel build from source with the crosstool-NG toolchain is presented here as a case study. The Linaro maintained Linux sources are used.

  • Download

    $ cd $WORKDIR
    $ git clone git://git.linaro.org/kernel/linux-linaro-tracking.git
    $ cd linux-linaro-tracking
    $ git checkout ll_20131122.0
    Checking out files: 100% (9613/9613), done.
    Branch ll_20131122.0 set up to track remote branch ll_20131122.0 from origin.
    Switched to a new branch ’ll_20131122.0’
    
  • Configure

    $ ARCH=arm scripts/kconfig/merge_config.sh linaro/configs/linaro-base.conf linaro/configs/distribution.conf linaro/configs/big-LITTLE-IKS.conf linaro/configs/vexpress.conf
    
  • Build

    $ CT_PREFIX_DIR=${WORKDIR}/x-tools/arm-linaro-linux-gnueabihf
    $ make ARCH=arm CROSS_COMPILE=${CT_PREFIX_DIR}/bin/arm-linaro-linux-gnueabihf- LOADADDR=0x60008000 uImage
    (...)
    Image Type: ARM Linux Kernel Image (uncompressed)
    Data Size: 4016552 Bytes = 3922.41 kB = 3.83 MB
    Load Address: 60008000
    Entry Point: 60008000
    Image arch/arm/boot/uImage is ready
    
  • Optional: Build the Device Tree Blob

    The Vexpress QEMU target does not require use of a DTB. But in the interests of completeness:

    $ make ARCH=arm CROSS_COMPILE=${CT_PREFIX_DIR}/bin/arm-linaro-linux-gnueabihf- dtbs 
    DTC arch/arm/boot/dts/vexpress-v2p-ca5s.dtb
    DTC arch/arm/boot/dts/vexpress-v2p-ca9.dtb
    DTC arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dtb
    DTC arch/arm/boot/dts/vexpress-v2p-ca15_a7.dtb
    DTC arch/arm/boot/dts/rtsm_ve-cortex_a9x2.dtb
    DTC arch/arm/boot/dts/rtsm_ve-cortex_a9x4.dtb
    DTC arch/arm/boot/dts/rtsm_ve-cortex_a15x1.dtb
    DTC arch/arm/boot/dts/rtsm_ve-cortex_a15x2.dtb
    DTC arch/arm/boot/dts/rtsm_ve-cortex_a15x4.dtb
    DTC arch/arm/boot/dts/rtsm_ve-v2p-ca15x1-ca7x1.dtb
    DTC arch/arm/boot/dts/rtsm_ve-v2p-ca15x4-ca7x4.dtb
    

    The DTB will be located under arch/arm/boot/dts.

  • Boot Test

    The qemu-system-arm version to be used could be anything later than (and including) v1.5.0. For Ubuntu 12.04, use the Linaro Tools PPA to upgrade from the default v1.0.50. See QEMU SYSTEM ARM, howto for details on obtaining qemu-system-arm.

    $ cp arch/arm/boot/uImage ${WORKDIR}/uImage
    $ cd $WORKDIR
    $ sync && sudo umount boot/ && sudo umount rfs/
    $ sudo qemu-system-arm -M vexpress-a9 -cpu cortex-a9 -kernel uImage -drive file=oe-alip_vexpress.img,if=sd,cache=writeback -append "root=/dev/mmcblk0p2 vga=normal rw"
    

    This should result in a QEMU SDL window instance that resembles:

    QEMU OE boot test

Building 3rd Party Software for the QEMU/OE target with the crosstool-NG based toolchain

This section presents a case study on building 3rd party software for the embedded target i.e. software that is not included in the prestine Linaro OpenEmbedded distro. The DirectFB framework is considered here. The DirectFB build instructions in the Embedded Linux Training guide by free-electrons.com merit special acknowlegment.

DirectFB Build Pre-requisites

Cross-Toolchain Requirements

In addition to the C cross-compiler driver, a C++ cross-compiler driver is also required for the compilation of the DirectFB core. Otherwise configure will fall back to the build machine's native C++ compiler driver, for instance:

$ ... ./configure ...
    ...
    arm-linaro-linux-gnueabihf-gcc yes
    arm-linaro-linux-gnueabihf-g++ no
    ...
    CXX    g++   ## instead of "CXX arm-linaro-linux-gnueabi-g++"
    ...

and the build will fail.

  • Video Settings:

    The target is configured with X Windows on the default (and only) virtual terminal:

    root@genericarmv7a:~# cat /etc/inittab
    ...
    1:2345:respawn:/sbin/getty 38400 tty1
    

    Naturally, these settings can be adjusted but the defaults will be used in this demo.

    The target also includes support for the Linux Frame Buffer:

    root@genericarmv7a:~# ls -l /dev/fb*
    lrwxrwxrwx    1 root     root             3 Jan 29 08:40 /dev/fb -> fb0
    crw-rw----    1 root     video      29,   0 Jan 28 11:10 /dev/fb0
    
    root@genericarmv7a:~# cat /proc/fb
    0 CLCD FB
    
    root@genericarmv7a:~# cat /sys/class/vtconsole/vtcon1/name
    (M) frame buffer device
    root@genericarmv7a:~# cat /sys/class/vtconsole/vtcon1/bind
    1
    
    root@genericarmv7a:~# fbset
    
    mode "1024x768-60"
        # D: 63.500 MHz, H: 47.816 kHz, V: 59.920 Hz
        geometry 1024 768 1024 768 16
        timings 15748 152 48 23 3 104 4
        rgba 5/11,6/5,5/0,0/0
    endmode
    
  • Existing Runtime Library Support for the DirectFB platform:

    • FreeType

      root@genericarmv7a:~# ls /usr/lib/libfreetype*
      /usr/lib/libfreetype.so.6  /usr/lib/libfreetype.so.6.10.2
      
    • PNG

      root@genericarmv7a:~# ls /usr/lib/libpng*
      /usr/lib/libpng16.so.16  /usr/lib/libpng16.so.16.6.0
      
    • zlib

      root@genericarmv7a:~# ls /lib/libz*
      /lib/libz.so.1  /lib/libz.so.1.2.8
      
    • JPEG

      root@genericarmv7a:~# ls /usr/lib/libjpeg*
      /usr/lib/libjpeg.so.8  /usr/lib/libjpeg.so.8.0.2
      

    In other words, to minimize the likelihood of surprises during runtime, the DirectFB framework should be built against the corresponding shared library development versions.

  • Miscellaneous Machine Info:

    root@genericarmv7a:~# uname -a
    Linux genericarmv7a 3.10.20.0-1-linaro-lsk-vexpress #1ubuntu1~ci+131122213412-Ubuntu SMP Sat Nov 23 07:33:54 UTC 201 armv7l GNU/Linux
    

DirectFB Infrastructure and Application Build

Prepare working directories

$ mkdir {archives,build,staging,target}
$ WORKDIR=$PWD
$ ARCHIVES=${PWD}/archives
$ BUILD=${PWD}/build
$ STAGING=${PWD}/staging
$ TARGET=${PWD}/target
$ mkdir ${TARGET}/lib
$ mkdir -p ${TARGET}/usr/lib

Build zlib

  • Download Sources:

    Download zlib v1.2.8, e.g. from here into $ARCHIVES then extract:

    $ cd $ARCHIVES
    $ tar xJf zlib-1.2.8.tar.xz -C ${BUILD}
    
  • Build and Install into Staging Directory:

    $ cd ${BUILD}/zlib-1.2.8
    

    Note that the ./configure script in this package is a non-autotools generated configure script:

    $ cat configure
    ...
    # To impose specific compiler or flags or install directory, use for example:
    #    prefix=$HOME CC=cc CFLAGS="-O4" ./configure
    # or for csh/tcsh users:
    #    (setenv prefix $HOME; setenv CC cc; setenv CFLAGS "-O4"; ./configure)
    ...
    

    So, cross-building with those instructions:

    $ CC=arm-linaro-linux-gnueabihf-gcc ./configure --prefix=/usr
    $ make
    $ make DESTDIR=${STAGING} install
    
  • Prepare OE-target runtime libraries:

    $ cd $WORKDIR
    $ cp -a ${STAGING}/usr/lib/libz.so.1* ${TARGET}/usr/lib
    $ ls ${TARGET}/usr/lib
    libz.so.1  libz.so.1.2.8
    $ arm-linaro-linux-gnueabihf-strip ${TARGET}/usr/lib/libz.so.1.2.8
    

Build Libpng

  • Download Sources:

    Download libpng v1.6.8, e.g. from here into $ARCHIVES then extract:

    $ cd $ARCHIVES
    $ tar xJf libpng-1.6.8.tar.xz -C ${BUILD}
    
  • Build and Install into Staging Directory:

    Note: libpng depends on zlib

    $ cd ${BUILD}/libpng-1.6.8
    $ LDFLAGS=-L${STAGING}/usr/lib CPPFLAGS=-I${STAGING}/usr/include CC=arm-linaro-linux-gnueabihf-gcc ./configure --prefix=/usr --host=arm-linaro-linux-gnueabihf --build=`./config.guess` 
    $ make
    $ make DESTDIR=${STAGING} install
    
  • Prepare OE-target runtime libraries:

    $ cd ${WORKDIR}
    $ cp -a ${STAGING}/usr/lib/libpng16.so.* ${TARGET}/usr/lib
    $ ls ${TARGET}/usr/lib
    libpng16.so.16  libpng16.so.16.8.0  libz.so.1  libz.so.1.2.8
    $ arm-linaro-linux-gnueabihf-strip ${TARGET}/usr/lib/libpng16.so.16.8.0
    

Build libjpeg

  • Download Sources:

    Download libjpeg v8, e.g. from here into $ARCHIVES then extract:

    $ cd $ARCHIVES
    $ tar xzf jpegsrc.v8.tar.gz -C ${BUILD}
    
  • Build and Install into Staging Directory:

    $ cd ${BUILD}/jpeg-8
    $ CC=arm-linaro-linux-gnueabihf-gcc ./configure --prefix=/usr --host=arm-linux-gnueabihf --build=`./config.guess`
    $ make
    $ make DESTDIR=${STAGING} install
    
  • Prepare OE-target runtime libraries:

    $ cd $WORKDIR
    $ cp -a ${STAGING}/usr/lib/libjpeg.so.8* ${TARGET}/usr/lib/
    $ ls ${TARGET}/usr/lib
    libjpeg.so.8  libjpeg.so.8.0.0  libpng16.so.16  libpng16.so.16.8.0 
    libz.so.1  libz.so.1.2.8
    $ arm-linaro-linux-gnueabihf-strip ${TARGET}/usr/lib/libjpeg.so.8.0.0
    

Build FreeType

  • Download Sources:

    Download FreeType v2.5.0, e.g. from here into $ARCHIVES, then extract:

    $ cd $ARCHIVES
    $ tar xjf freetype-2.5.0.tar.bz2 -C ${BUILD}
    
  • Build and Install into Staging Directory:

    Note: FreeType depends on libpng

    $ cd ${BUILD}/freetype-2.5.0
    $ LDFLAGS=-L${STAGING}/usr/lib CPPFLAGS=-I${STAGING}/usr/include LIBPNG_CFLAGS="`${STAGING}/usr/bin/libpng-config --cflags`"   LIBPNG_LDFLAGS="`${STAGING}/usr/bin/libpng-config --ldflags`"  CC=arm-linaro-linux-gnueabihf-gcc ./configure --prefix=/usr --host=arm-linaro-linux-gnueabihf
    

    The LIBPNG_*FLAGS settings are required to ensure:

    ...
    checking for libpng... -L/usr/lib -lpng16
    ...
    

    otherwise, configure will use the build machine's binaries e.g.:

    ....
    checking for libpng... -L/usr/lib/x86_64-linux-gnu -lpng12
    ...
    

    and an attempt to build will fail when arm-linaro-linux-gnueabi-ld attempts to link the x86_64 libpng binary with the ARM binaries.

    Also note that although the --build= option to configure was omitted (due to the abscence of config.guess in the package), configure correctly set this to

    ...
    checking build system type... x86_64-unknown-linux-gnu
    ...
    

    Some distributions such as Debian/Ubuntu provide the config.guess(1) (and config.sub(1)) utilities. See Cross-toolchain Usage for more details.

    Now, build:

    $ make
    $ make DESTDIR=${STAGING} install
    
  • Prepare OE-target runtime libraries:

    $ cd $WORKDIR
    $ cp -a ${STAGING}/usr/lib/libfreetype.so.6* ${TARGET}/usr/lib
    $ arm-linaro-linux-gnueabihf-strip ${TARGET}/usr/lib/libfreetype.so.6.10.2
    $ ls ${TARGET}/usr/lib
    libfreetype.so.6  libfreetype.so.6.10.2  libjpeg.so.8  libjpeg.so.8.0.0 
    libpng16.so.16  libpng16.so.16.8.0  libz.so.1  libz.so.1.2.8
    

DirectFB Infrastructure Build

  • Download Sources:

    Download DirectFB-1.7.0.tar.gz, e.g. from here into $ARCHIVES, then extract:

    $ cd $ARCHIVES
    $ tar xzf DirectFB-1.7.0.tar.gz -C $BUILD
    
  • Build and Install into Staging Directory:

    Before attempting to build the DirectFB infrastructure, a few fixes must be performed:

    • Edit ${STAGING}/usr/lib/libfreetype.la such that:

      $ cat ${STAGING}/usr/lib/libfreetype.la
      ...
      19 # Libraries that this one depends upon.
      20 #dependency_libs=' -L${STAGING}/usr/lib -L/usr/lib /usr/lib/libpng16.la -lz -lm'
      22 dependency_libs=' -L${STAGING}/usr/lib ${STAGING}/usr/lib/libpng16.la -lz -lm'
      ...
      

      NOTE: Replace ${STAGING} here with the explicit path value

      This edit fixes the following build error:

      $ make
      ...
      make[3]: Entering directory `${BUILD}/DirectFB-1.7.0/interfaces/IDirectFBFont'
      CC     idirectfbfont_ft2.lo
      CCLD   libidirectfbfont_ft2.la
      libtool: link: warning: library `${STAGING}/usr/lib/libfreetype.la' was moved.
      /bin/sed: can't read /usr/lib/libpng16.la: No such file or directory
      libtool: link: `/usr/lib/libpng16.la' is not a valid libtool archive
      ...
      
    • Set CT_PREFIX_DIR:

      $ CT_PREFIX_DIR=${WORKDIR}/x-tools/arm-linaro-linux-gnueabihf
      

      This value is used in the LDFLAGS option in the configure commandline below. This LDFLAGS setting (along with -lstdc++) is necessary since libstdc++.so.6 does not reside in the toolchain's sysroot.

      These linker flags fix the following build error:

      collect2: error: ld returned 1 exit status
      make[2]: *** [dfbdump] Error 1
      make[2]: *** Waiting for unfinished jobs....
      (...)arm-linaro-linux-gnueabihf/bin/ld: warning: libstdc++.so.6, needed by ../src/.libs/libdirectfb.so, not found (try using -rpath or -rpath-link)
      

    Now run configure:

    $ cd ${BUILD}/DirectFB-1.7.0
    $ LDFLAGS="-L${STAGING}/usr/lib -L${CT_PREFIX_DIR}/arm-linaro-linux-gnueabihf/lib -lstdc++" CPPFLAGS=-I${STAGING}/usr/include PKG_CONFIG_PATH=${STAGING}/usr/lib/pkgconfig PKG_CONFIG_SYSROOT_DIR=${STAGING} CC=arm-linaro-linux-gnueabihf-gcc ./configure --prefix=/usr --host=arm-linaro-linux-gnueabihf --build=`./config.guess` --disable-mesa --disable-drmkms --disable-x11 --with-gfxdrivers=none --with-inputdrivers=keyboard,linuxinput
    

    Note: The PKG_CONFIG_* settings were required to give:

    Building Image Provider Modules:
    PNG               yes  -I${STAGING}/usr/include/libpng16   -lpng16
    
    Building Font Modules:
    FreeType2         yes  -I${STAGING}/usr/include/freetype2   -lfreetype
    

    instead of:

    Building Image Provider Modules:
    PNG               yes  -I/usr/include/libpng12  -lpng12
    
    Building Font Modules:
    FreeType2         yes  -I/usr/include/freetype2 -lfreetype
    

    Finally, build:

    $ make
    $ make DESTDIR=${STAGING} install
    
  • Prepare Target Runtime Libraries:

    $ cd $WORKDIR
    $ cp -a ${STAGING}/usr/lib/libdirect-1.7.so.0* target/usr/lib/
    $ cp -a ${STAGING}/usr/lib/libdirectfb-1.7.so.0* target/usr/lib/
    $ cp -a ${STAGING}/usr/lib/libfusion-1.7.so.0* target/usr/lib/
    $ arm-linaro-linux-gnueabihf-strip ${TARGET}/usr/lib/libdirect-1.7.so.0.0.0 
    $ arm-linaro-linux-gnueabihf-strip ${TARGET}/usr/lib/libdirectfb-1.7.so.0.0.0 
    $ arm-linaro-linux-gnueabihf-strip ${TARGET}/usr/lib/libfusion-1.7.so.0.0.0
    

    Copy the DirectFB Plugins and remove unnecessary files:

    $ cp -a ${STAGING}/usr/lib/directfb-1.7-0/ target/usr/lib/
    $ find ${TARGET}/usr/lib/directfb-1.7-0/ -name '*.la' -exec rm {} \;
    $ find ${TARGET}/usr/lib/directfb-1.7-0/ -name '*.so' -exec arm-linaro-linux-gnueabihf-strip {} \;
    

Build DirectFB Applications

  • Download Sources:

    Download DirectFB-examples-1.7.0.tar.gz, e.g. from here into $ARCHIVES, then extract:

    $ cd $ARCHIVES
    $ tar xzf DirectFB-examples-1.7.0.tar.gz -C ${BUILD}
    
  • Build and Install Directly into $TARGET

    $ cd ${BUILD}/DirectFB-examples-1.7.0
    $ CT_PREFIX_DIR=${WORKDIR}/x-tools/arm-linaro-linux-gnueabihf
    $ LDFLAGS="-L${STAGING}/usr/lib -L${CT_PREFIX_DIR}/arm-linaro-linux-gnueabihf/lib -lstdc++" CPPFLAGS=-I${STAGING}/usr/include PKG_CONFIG_PATH=${STAGING}/usr/lib/pkgconfig PKG_CONFIG_SYSROOT_DIR=${STAGING} CC=arm-linaro-linux-gnueabihf-gcc ./configure --prefix=/usr --host=arm-linaro-linux-gnueabihf --build=`./config.guess` 
    $ make
    $ make STRIP=arm-linaro-linux-gnueabihf-strip DESTDIR=${TARGET} install-strip
    

The final state of $TAGET should now resemble:

$ cd $WORKDIR
$ tree -L 3 target/
target/
├── lib
└── usr
    ├── bin
    │   ├── df_andi
    │   ├── df_andi3d
    (...)
    │   ├── df_video
    │   ├── df_video_particle
    │   ├── df_window
    │   ├── pss
    │   └── spacedream
    ├── lib
    │   ├── directfb-1.7-0
    │   ├── libdirect-1.7.so.0 -> libdirect-1.7.so.0.0.0
    │   ├── libdirect-1.7.so.0.0.0
    (...)
    │   ├── libz.so.1 -> libz.so.1.2.8
    │   └── libz.so.1.2.8
    └── share
        └── directfb-examples

Transfer of the DirectFB Framework to the OE Target

There are several ways of transfering files into the OpenEmbedded target. Here, simple copy operations are performed against the root filesystem artifact mount.

Note:

  • It might be prudent to use a copy or a derived image of the original oe-alip_vexpress.img file. See Manipulating QEMU Disk Images.

  • Only the DirectFB files and missing library dependencies are transferred.

Perform the root filesystem component mount as described in Section link, then:

    $ cd $WORKDIR
    $ sudo cp -a ${TARGET}/usr/bin/* rfs/usr/bin/
    $ sudo cp -a ${TARGET}/usr/lib/directfb-1.7-0 rfs/usr/lib/
    $ sudo cp -a ${TARGET}/usr/lib/libdirect* rfs/usr/lib/
    $ sudo cp -a ${TARGET}/usr/lib/libfusion* rfs/usr/lib/
    $ sudo cp -a ${TARGET}/usr/share/directfb-examples rfs/usr/share/

Testing the DirectFB Framework

Section link discusses qemu-system-arm version requirements. The -net nic -net tap,ifname=tap0,... options in the command line below are optional. The subject of QEMU networking will not be covered here. See, for example, A QEMU TAP Networking Setup or refer to other online material such as this and that.

umount any mounted partitions of the OpenEmbedded disk iamge before executing the following (or similar) QEMU commandline:

$ sudo qemu-system-arm -M vexpress-a9 -cpu cortex-a9 -kernel uImage -drive file=oe-alip_vexpress.img,if=sd,cache=writeback -append "root=/dev/mmcblk0p2 vga=normal rw" -net nic -net tap,ifname=tap0,script=qemu_br0_ifup.sh,downscript=qemu_br0_ifdown

Then, from a (remote) terminal on the embedded target, executing

root@genericarmv7a:~# df_window [--dfb:system=fbdev] [&]

where:

  • --dfb:system=fbdev is required if executing the command from an xterm on the QEMU SDL window interface. This setting causes DirectFB to bypass the OE target's X Windows system and directly make use of the LinuxFB platform instead. Recall that X11 support was not enabled during the DirectFB infrastructure build.

  • & will place in the process background (and return the terminal prompt).

Whichever the case, the command should result in an output similar to:

directfb on qemu-openembedded

Click on the QEMU SDL window to give it control of the keyboard/mouse and strike ESC to quit. Alternatively, sending a TERM signal to the df_window process e.g:

root@genericarmv7a:~# ps ax | grep df_window
 10666 root       0:02 df_window
 ...
root@genericarmv7a:~# kill -15 10666

should also result in graceful termination and a return to the OpenEmbedded X Windows interface:

openembedded x-windows

Resources

  • http://crosstool-ng.org/
  • http://releases.linaro.org/
  • https://wiki.linaro.org/WorkingGroups/ToolChain
  • https://wiki.linaro.org/WorkingGroups/ToolChain/Using/CrosstoolNg
  • http://free-electrons.com/doc/training/embedded-linux/labs.pdf

Footnotes

1. Several factors including incompatible ABI, incompatible library versioned symbols, and minimum supported kernel version contribute to execution time failure as a result of shared library mismatch in the build machine (cross-)toolchain and run-time host environment. [go back]

2. file(1) reads the .note.ABI-tag section of an ELF object file. See src/readelf.c of its source tree. [go back]