Fast Builds of Old Software for Armel on Linux x64 (amd64)

craig arm-based netbook running debian

Why Use Cross-Compilation vs QEMU User Mode to Emulate Armel?

  • Speed. It’s much faster, especially if you have multiple cores and use parallel builds.
  • QEMU user mode doesn’t support using more than one core (which means not only are you doing emulation (which is slow), but you are stuck with single-threaded builds.
  • QEMU user mode is still useful for things like build a rootfs because it allows you to use standard package management tools for your distribution rather than requiring a real machine.
  • Likewise QEMU user mode is useful for doing automated testing for alternate architectures (like armel) on an x86 host.


  • We’re going to use a cross-compilation toolchain, but we’re going to use do it on Debian Jessie. We do this because we’re trying to build older software which often has incompatibilities with newer system, even doing cross-compilation with older toolchains.
  • The instructions are written assuming we are running on a Debian system, however the host environment should not be relevant (it could even be Windows provided you have Docker for Windows).
  • You need Docker on your system (apt-get install deboostrap
  • This article assumes you have Docker configured and running
  • We use Docker because it’s provides more protection from shooting yourself in the foot than a chroot (which requires root access to initiate the chroot). It also provides better (but not complete) protection against exploits. Best (short of a virtual machine or dedicated host would be to run in an unprivileged container.
  • For this exercise we are building for Linux kernel as this is part of the series on getting Debian working on the Craig CLP281

Build the Development Environment Container

These instruction only produce an environment for compiling C and Assembler programs, not C++ or any other language.

NB Normally you wouldn’t want to build your tools in the same container as you use for your environment, but in this case we are creating a development environment, so having the development tools in the container can be handy.

  1. Create a directory (e.g. mkdir workdir) and change to it (e.g. cd workdir).
  2. Create a file called Dockerfile such as:
FROM debian:jessie

ARG uid=1000
ARG gid=1000
ARG user=builder
ARG group=${user}
ARG cores=1

RUN apt-get -y update && \
  apt-get install -y \
  bash-completion \
  binutils-dev \
  build-essential \
  bzip2 \
  cmake \
  curl \
  debhelper \
  dh-systemd \
  dpkg-dev \
  diffutils \
  fakeroot \
  fakechroot \
  gawk \
  git \
  libgmp-dev \
  gnupg2 \
  libbfb0-dev \
  libc6-dev \
  libcurl4-gnutls-dev \
  libdw-dev \
  libelf-dev \
  libisl-dev \
  libtool \
  ncurses-dev \
  libmpc-dev \
  libmpfr-dev \
  pkg-config \
  python-minimal \
  quilt \
  texinfo \
  u-boot-tools \
  wget \
  xmlto \
  xz-utils \
  zlib1g-dev && \
  apt-get clean && rm -rf /var/lib/apt/lists/*

ENV PATH "/opt/arm-linux-2.6/bin:$PATH"

RUN addgroup --gid ${gid} ${group} && adduser --uid ${uid} --gid ${gid} --disabled-password ${user} --gecos "" && \
        mkdir -p /home/${user}/Build && \
        chown ${user}:${group} /home/${user}/Build && \
        mkdir -p /opt/build && \
        chown -R ${user}:${group} /opt

USER ${user}:${group}

# Stage 1: Build binutils & initial gcc

RUN cd /opt/build && \
        curl -O && \
        curl -O && \
        tar -xjf binutils-2.25.tar.bz2 && \
        tar -xjf gcc-4.9.4.tar.bz2

RUN cd /opt/build && mkdir binutils-build && \
        cd binutils-build && \
        ../binutils-2.25/configure --quiet --prefix=/opt/arm-linux-2.6 --target=arm-linux-gnueabi --with-sysroot --disable-nls --disable-werror --disable-multilib && \
        make -j${cores} && \
        make install && \
        mkdir ../gcc-build && \
        cd ../gcc-build && \
        ../gcc-4.9.4/configure --quiet --prefix=/opt/arm-linux-2.6 --target=arm-linux-gnueabi --enable-languages=c --without-headers --disable-nls --enable-serial-configure --disable-multilib && \
        make -j${cores} all-gcc && \
        make install-gcc && \
        cd .. && \
        rm -f *.bz2

# Stage 2: Build glibc standard C library header files and startup files
# Stage 3: Go Back and Forth between GCC and Glibc as Required to Build Both

RUN cd /opt/build && \
        curl -O && \
        curl -O && \
        tar -xjf glibc-2.19.tar.bz2 && \
        tar -xjf linux-

RUN cd /opt/build/linux- && \
        make ARCH=arm INSTALL_HDR_PATH=/opt/arm-linux-2.6/arm-linux-gnueabi headers_install && \
        cd .. && \
        mkdir -p glibc-build && \
        cd glibc-build && \
        ../glibc-2.19/configure --quiet --prefix=/opt/arm-linux-2.6/arm-linux-gnueabi --build=$MACHTYPE --host=arm-linux-gnueabi --target=arm-linux-gnueabi --with-headers=/opt/arm-linux-2.6/arm-linux-gnueabi/include --disable-multilib libc_cv_forced_unwind=yes && \
        make install-bootstrap-headers=yes install-headers && \
        make -j${cores} csu/subdir_lib && \
        install csu/crt1.o csu/crti.o csu/crtn.o /opt/arm-linux-2.6/arm-linux-gnueabi/lib && \
        arm-linux-gnueabi-gcc -nostdlib -nostartfiles -shared -x c /dev/null -o /opt/arm-linux-2.6/arm-linux-gnueabi/lib/ && \
        touch /opt/arm-linux-2.6/arm-linux-gnueabi/include/gnu/stubs.h

RUN cd /opt/build/gcc-build && \
        make -j${cores} all-target-libgcc && \
        make install-target-libgcc

RUN cd /opt/build/glibc-build && \
        make -j${cores} && \
        make install

RUN cd /opt/build/gcc-build && \
        make -j${cores} && \
        make install && \
        cd /opt && rm -rf build

VOLUME ["/home/${user}/Build"]

WORKDIR /home/${user}/Build

CMD ["bash"]

  1. In the directory with the Docker file, execute docker build -t jessie-cross-armel:latest .

NB: If you want a different user id and group id than the default, a different user and/or group name, and/or to set more than default of one core (faster builds!) for compilation you can use a command line such as:

docker build -t jessie-cross-armel:latest --build-arg uid=1001 --build-arg gid=1001 --build-arg user=john --build-arg group=john --build-arg cores=8 .

Using the container

  • If you need additional packages or software you can use another Dockerfile with FROM jessie-cross-armel:latest and the appropriate Dockerfile commands for what you want to add.
  • When you want an interactive shell that uses the software in the container, you can do: docker run -it -v /your/build/directory:/home/[your-user]/Build jessie-cross-armel:latest /bin/bash

Further Reading

By Daniel F. Dickinson

Daniel Dickinson has been using and exploring computer and electronic technologies for over three decades. He is proficient with various flavours of Linux (including varieties of desktop, server, and embedded systems) as well as modern Windows. Daniel still enjoys software and firmware development, but prefers integration and extending existing open source solutions to quickly achieve desired goals. Fo more details see

Leave a comment

Your email address will not be published. Required fields are marked *