You are here: Home / Software / embedded linux notes / bootstrapping cross compiler

bootstrapping cross compilerΒΆ

Compiling on an embedded system (sheevaplug, raspberry, etc) is slow. Using a Cross Compiler on your Desktop is a lot faster. You can download ready made cross compiler toolchains. For example plugcomputer.org (Downloads, plug computer basics) or codesourcery.cpm (products, source g++, editions, lit)

Or we can strap our own.

Requires Diskspace and CPU-Time. The steps are

  • binutils
  • gcc, static version, no threads
  • kernelheader
  • eglibc, header so you can compile
  • gcc, only C
  • eglibc
  • gcc, C and C++

All Details, download URLs can be seen in the following shell script.

#!/bin/bash
set -e
set -u

# building my custom cross compiler

# This directory is used as root for all operations
BASE=$(dirname "$0")
BASE=$(cd "$BASE" && pwd)

# target machine tuple. This is suitable for the marvell sheevaplug
TARGET=armv5tel-softfloat-linux-gnueabi
BUILD=$(gcc -dumpmachine)        # x86_64-unknown-linux-gnu, i686-linux-gnu, ...
ARCH=arm

SYSROOT="$BASE/sysroot"     # files for the target system 
TOOLCHAIN="$BASE/toolchain" # the cross compilers
SRCDIR="$BASE/src"          # saving tarballs
WORKDIR="$BASE/work"        # unpacked tarballs
BUILDDIR="$BASE/build"      # running compile 

PARALLEL="-j2"              # dualcore

# Software Versions to use
BINUTILS=binutils-2.20.1
KERNEL=linux-2.6.34
GCC=gcc-4.5.0
EGLIBC=eglibc-2_11          # abi compatible to glibc, better support for arm


# download functions
get_url() {
	# pass a full url as parameter
	url=$1
	file=${url##*/}
	if [ ! -f "$SRCDIR/$file" ]; then
		echo "downloading $file from $url"
		wget "$url" -O "$BASE/src/$file"
	fi
}

unpack() {
	# pass a filename as parameter
	file=$1
	ext=${file##*.}
	folder=${file%%.tar.*}
	if [ !  -d "$WORKDIR/$folder"  ]; then
		echo "unpacking $file"
	else
		return 0
	fi
	cd $WORKDIR
	if [ $ext == "bz2" ]; then
		tar jxf "$SRCDIR/$file"
	else
		tar zxf "$SRCDIR/$file"
	fi
}

check_done() {
	OBJ=$1
	if [ -f $BUILDDIR/$OBJ.done ]; then
		echo "already done"
		return 0
	fi
	return 1
}

do_msg() {
	OBJ=$1
	ACTION=$2
	echo "========================================================="
	echo "$OBJ - $ACTION"
	echo "========================================================="
}


binutils() {
	# compile ar, as, ld, objcopy, ...
	OBJ=$BINUTILS
	do_msg $OBJ "start"
	check_done $OBJ && return 0
	mkdir -p $BUILDDIR/$OBJ
	pushd $BUILDDIR/$OBJ
	if [ ! -f Makefile ]; then
		# run configure
		$WORKDIR/$OBJ/configure \
			--target=$TARGET \
			--prefix=$TOOLCHAIN \
			--with-sysroot=$SYSROOT \
			--disable-nls 
	fi
	do_msg $OBJ "compile"
	make $PARALLEL
	do_msg $OBJ "install"
	make install 
	do_msg $OBJ "done"
	touch $BUILDDIR/$OBJ.done
	popd
}


gccstatic() {
	# static gcc, only C, able to compile the libc
	# would be enough if we only compile kernels
	OBJ=$GCC-static
	do_msg $OBJ "start"
	check_done $OBJ && return 0
	mkdir -p $BUILDDIR/$OBJ
	pushd $BUILDDIR/$OBJ
	if [ ! -f Makefile ]; then
		$WORKDIR/$GCC/configure \
			--target=$TARGET \
			--prefix=$TOOLCHAIN \
			--without-headers \
			--with-newlib \
			--disable-shared \
			--disable-threads \
			--disable-libssp \
			--disable-libgomp \
			--disable-libmudflap \
			--disable-nls \
			--enable-languages=c 
	# --without-headers and --with-newlib make gcc believe that there
	# is no c library available (yet)
	fi
	do_msg $OBJ "compile"
	PATH=$TOOLCHAIN/bin:$PATH \
	make $PARALLEL 
	do_msg $OBJ "install"
	PATH=$TOOLCHAIN/bin:$PATH \
	make install
	do_msg $OBJ "done"
	touch $BUILDDIR/$OBJ.done
	popd
}

gccminimal() {
	OBJ=$GCC-min
	do_msg $OBJ "start"
	check_done $OBJ && return 0
	mkdir -p $BUILDDIR/$OBJ
	pushd $BUILDDIR/$OBJ
	if [ ! -f Makefile ]; then
		$WORKDIR/$GCC/configure \
			--target=$TARGET \
			--prefix=$TOOLCHAIN \
			--with-sysroot=$SYSROOT \
			--disable-libssp \
			--disable-libgomp \
			--disable-libmudflap \
			--disable-nls \
			--enable-languages=c 
	# now with shared libs and threads
	fi
	do_msg $OBJ "compile"
	PATH=$TOOLCHAIN/bin:$PATH \
	make $PARALLEL 
	do_msg $OBJ "install"
	PATH=$TOOLCHAIN/bin:$PATH \
	make install
	do_msg $OBJ "done"
	touch $BUILDDIR/$OBJ.done
	popd
}
gccfull() {
	OBJ=$GCC
	do_msg $OBJ "start"
	check_done $OBJ && return 0
	mkdir -p $BUILDDIR/$OBJ
	pushd $BUILDDIR/$OBJ
	if [ ! -f Makefile ]; then
		$WORKDIR/$GCC/configure \
			--target=$TARGET \
			--prefix=$TOOLCHAIN \
			--with-sysroot=$SYSROOT \
			--enable-__cxy_atexit \
			--disable-libssp \
			--disable-libgomp \
			--disable-libmudflap \
			--enable-languages=c,c++ \
			--disable-nls 
	# now with c++
	# the cxy_atexit is special for eglibc
	fi
	do_msg $OBJ "compile"
	PATH=$TOOLCHAIN/bin:$PATH \
	make $PARALLEL 
	do_msg $OBJ "install"
	PATH=$TOOLCHAIN/bin:$PATH \
	make install
	do_msg $OBJ "done"
	touch $BUILDDIR/$OBJ.done
	popd
}

kernelheader() {
	# compiling headers that work on the target system
	# this is done in-tree 
	OBJ=$KERNEL
	do_msg $OBJ "start"
	check_done $OBJ && return 0
	pushd $WORKDIR/$OBJ
	do_msg $OBJ "cleaning"
	make mrproper
	do_msg $OBJ "installing"
	PATH=$TOOLCHAIN/bin:$PATH \
	make \
		ARCH=$ARCH \
		INSTALL_HDR_PATH=$SYSROOT/usr \
		CROSS_COMPILE=$TARGET- \
		headers_install 
	do_msg $OBJ "done"
	touch $BUILDDIR/$OBJ.done
	popd
}

eglibcheader() {
	OBJ=$EGLIBC-header
	do_msg $OBJ "start"
	check_done $OBJ && return 0
	mkdir -p $BUILDDIR/$OBJ
	pushd $BUILDDIR/$OBJ
	if [ ! -f Makefile ]; then
		BUILD_CC=gcc \
		CC=$TOOLCHAIN/bin/$TARGET-gcc \
		CXX=$TOOLCHAIN/bin/$TARGET-g++ \
		AR=$TOOLCHAIN/bin/$TARGET-ar \
		LD=$TOOLCHAIN/bin/$TARGET-ld \
		RANLIB=$TOOLCHAIN/bin/$TARGET-ranlib \
		$WORKDIR/$EGLIBC/configure \
			--prefix=/usr \
			--with-headers=$SYSROOT/usr/include \
			--build=$BUILD \
			--host=$TARGET \
			--disable-nls \
			--disable-profile \
			--without-gd \
			--without-cvs \
			--enable-add-ons

	fi
	do_msg $OBJ "install"
	make \
		install-headers \
		install_root=$SYSROOT \
		install-bootstrap-headers=yes
	do_msg $OBJ "crtX and fake libc"
	make csu/subdir_lib
	mkdir -p $SYSROOT/usr/lib
	cp csu/crt1.o csu/crti.o csu/crtn.o $SYSROOT/usr/lib
	# build a dummy libc
	$TOOLCHAIN/bin/$TARGET-gcc \
		-nostdlib \
		-nostartfiles \
		-shared \
		-x c /dev/null \
		-o $SYSROOT/usr/lib/libc.so
	do_msg $OBJ "done"
	touch $BUILDDIR/$OBJ.done
	popd
}
eglibc() {
	OBJ=$EGLIBC
	do_msg $OBJ "start"
	check_done $OBJ && return 0
	mkdir -p $BUILDDIR/$OBJ
	pushd  $BUILDDIR/$OBJ
	if [ ! -f Makefile ]; then
		BUILD_CC=gcc \
		CC=$TOOLCHAIN/bin/$TARGET-gcc \
		CXX=$TOOLCHAIN/bin/$TARGET-g++ \
		AR=$TOOLCHAIN/bin/$TARGET-ar \
		RANLIB=$TOOLCHAIN/bin/$TARGET-ranlib \
		$WORKDIR/$EGLIBC/configure \
			--prefix=/usr \
			--with-headers=$SYSROOT/usr/include \
			--build=$BUILD \
			--host=$TARGET \
			--disable-nls \
			--disable-profile \
			--without-gd \
			--without-cvs \
			--enable-add-ons
	fi
	do_msg $OBJ "compile"
	PATH=$TOOLCHAIN/bin:$PATH \
	make $PARALLEL
	do_msg $OBJ "install"
	PATH=$TOOLCHAIN/bin:$PATH \
	make install install_root=$SYSROOT
	do_msg $OBJ "done"
	touch $BUILDDIR/$OBJ.done
	popd
}



# ==============================================
# MAIN
# ==============================================


do_msg "INIT" "creating directories"
mkdir -p $BUILDDIR
mkdir -p $WORKDIR
mkdir -p $SRCDIR
mkdir -p $SYSROOT
mkdir -p $TOOLCHAIN

do_msg "INIT" "download"
pushd $SRCDIR
get_url "http://ftp.gnu.org/gnu/binutils/$BINUTILS.tar.bz2"
get_url "ftp://ftp.gnu.org/gnu/gcc/$GCC/$GCC.tar.bz2"
get_url "ftp://ftp.kernel.org/pub/linux/kernel/v2.6/$KERNEL.tar.bz2"
# our very special friend eglibc has no release tarball
if [ ! -d $SRCDIR/$EGLIBC ]; then
	svn export svn://svn.eglibc.org/branches/$EGLIBC/libc $SRCDIR/$EGLIBC
	# arm is only supported in ports
	svn export svn://svn.eglibc.org/branches/$EGLIBC/ports $SRCDIR/$EGLIBC/ports
fi
	
do_msg "INIT" "unpacking"
unpack "$BINUTILS.tar.bz2"
unpack "$GCC.tar.bz2"
unpack "$KERNEL.tar.bz2"
if [ ! -h $WORKDIR/$EGLIBC ]; then
	ln -s $SRCDIR/$EGLIBC $WORKDIR/$EGLIBC
fi

binutils
gccstatic
kernelheader
eglibcheader
gccminimal
eglibc
gccfull

echo "     _                   "
echo "  __| | ___  _ __   ___  "
echo " / _  |/ _ \| '_ \ / _ \ "
echo "| (_| | (_) | | | |  __/ "
echo " \__,_|\___/|_| |_|\___| "
echo "                         "
echo "                         "