Cross Compile Madness.

So I pushed that pax-utils.

  • Improved Makefile handling. (vapier)
  • QA_TEXTREL/QA_EXECSTACK/QA_WX_LOAD exempt filtering flags. (kevquinn)
  • Handle versioned symbols correctly on unstripped ELF files when using the -s flag.
  • Do not assume it is ok to read from stdin if the -l -p flags were given. (solar)

Now it’s time to get back to some cross compiling goodness. Well one thing I know about myself is anything I do I tend todo in excess. So I figure why build 1 cross compiler when you can build them all. Why merge into 1 place when you can reasonable test cross compiling for all arches.. Well that’s just what I did and am doing.

Little script I’m using for just this task..

#!/bin/sh
# Copyright 2006 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header: <solar@gentoo.org>

CFLAGS="-Os -pipe"
CBUILD=$(portageq envvar CHOST)
CXXFLAGS="${CFLAGS}"

export PYTHON_DONTCOMPILE=1

USE_SAVE="${USE}"
if [ ! -x /usr/bin/qlist ]; then
        echo "please emerge portage-utils"
        exit 1
fi

gcc_executables=$(qlist -oeCI gcc| sort -u | grep ^cross- | sed s/cross-//g | tr / -)

opts="$@"
for x in "$@"; do
        if [[ $x == "--libc" ]]; then
                opts="${opts/${x}/}"
                DO_LIBC=1
                do_libc=1
        fi
        if [[ "${lastopt}" == "--target" ]]; then
                ctarget_only="${x}"
                opts="${opts/${x}/}"
                opts="${opts/${lastopt}/}"
        fi
        lastopt=${x}
done

function cross_env_update() {
        mkdir -p ${ROOT}/etc
        for lpath in /usr/local/lib /usr/${CTARGET}/lib /usr/lib/gcc/${CTARGET}/$(${CTARGET}-gcc -dumpversion) /usr/lib/libstdc++-v3/; do
                echo "${lpath}" >> ${ROOT}/etc/ld.so.conf
        done
}

function cmerge() {
        CHOST=${1}
        shift

        export CTARGET=${CHOST}
        ARCH=$(echo ${CTARGET} | cut -d '-' -f 1)
        CC=${CTARGET}-gcc
        CXX=${CTARGET}-g++
        PKGDIR=${HOME}/packages/${CHOST}
        ROOT="${HOME}/ROOT/${CHOST}"
        LDFLAGS="-L${ROOT}/lib -L${ROOT}/usr/lib"

        do_libc=${DO_LIBC}
        unset ELIBC
        case ${CTARGET} in
                *-linux-gnu)    ELIBC=glibc;;
                *-linux-uclibc) ELIBC=uclibc;;
                avr) ELIBC=avr-libc;;
        esac
        export ELIBC
        [[ $ELIBC == "" ]] && do_libc=0

        USE="${ARCH} ${USE_SAVE}"
        case ${ARCH} in
                armeb) ARCH=arm;;
                s390x) ARCH=s390;;
                mips*) ARCH=mips;;
                powerpc64) ARCH=ppc64;;
                sparc*) ARCH=sparc;;
                powerpc) ARCH=ppc;;
                hppa*) ARCH=hppa;;
                sh4*|sheb) ARCH=sh;;
                x86_64) ARCH=amd64;;
                avr|ee|iop|cris) ARCH="x86";;
                i?86) ARCH=x86;;
        esac
        ACCEPT_KEYWORDS="${ARCH}"
        [[ "${unstable}" != "" ]] && ACCEPT_KEYWORDS="${ACCEPT_KEYWORDS}  ~${ARCH}"

        USE="-* ${ARCH} ${USE} elibc_${ELIBC} multicall make-symlinks"

        export ARCH ACCEPT_KEYWORDS CFLAGS CXXFLAGS LDFLAGS ROOT CBUILD CHOST CTARGET CC CXX PKGDIR USE

        mkdir -p ${ROOT}/etc
        if [[ $ELIBC != "" ]]; then
                if [ -e ${ROOT}/etc/ld.so.conf ]; then
                        if [ "$(md5sum ${ROOT}/etc/ld.so.conf|awk '{print $1}')" == "b18efa8c9f95b6aecc0974c0f54d8bb9" ]; then
                                touch ${ROOT}/etc/ld.so.conf
                                cross_env_update
                        fi
                else
                        cross_env_update
                fi
        fi

        if [[ "$do_libc" == 1 ]]; then
                emerge -b cross-${CHOST}/${ELIBC} ${opts}
        else
                emerge -b ${opts}
        fi

        unset ARCH ACCEPT_KEYWORDS LDFLAGS ROOT CHOST CTARGET CC CXX PKGDIR USE ELIBC
}

if [[ "$1" == "" ]]; then
        echo "$0: <opts> <emerge opts>"
        echo "   --libc"
        echo "   --target <chost>"
        echo "   --libc  --target i386-gentoo-linux-uclibc busybox -pv"
        exit 1
fi

for gnugcc in ${gcc_executables}; do
        [ -e /usr/bin/${gnugcc} ] || { echo bummer $gnugcc ; continue; }
        # include=$($gnugcc -print-file-name=)
        [[ $ctarget_only != "" ]] && [[ ${gnugcc/-gcc/} != $ctarget_only ]] && continue
        cmerge "${gnugcc/-gcc/}" "$@"
done

A blind run at building busybox and libc for just about every chost combo yields the following results. Anything marked N is something that failed. R means it installed of course.

tinderbox ~ # ./cmerge -qpv busybox
[ebuild  N    ] sys-apps/busybox-1.1.0 to /root/ROOT/alpha-gentoo-linux-uclibc/ 
[ebuild   R   ] sys-apps/busybox-1.1.0 to /root/ROOT/alpha-unknown-linux-gnu/ 
[ebuild   R   ] sys-apps/busybox-1.1.0 to /root/ROOT/arm-gentoo-linux-uclibc/ 
[ebuild   R   ] sys-apps/busybox-1.1.0 to /root/ROOT/arm-softfloat-linux-gnu/ 
[ebuild   R   ] sys-apps/busybox-1.1.0 to /root/ROOT/arm-softfloat-linux-uclibc/ 
[ebuild   R   ] sys-apps/busybox-1.1.0 to /root/ROOT/arm-unknown-linux-gnu/ 
[ebuild   R   ] sys-apps/busybox-1.1.0 to /root/ROOT/armeb-gentoo-linux-uclibc/ 
[ebuild   R   ] sys-apps/busybox-1.1.0 to /root/ROOT/armeb-softfloat-linux-gnu/ 
[ebuild   R   ] sys-apps/busybox-1.1.0 to /root/ROOT/armeb-softfloat-linux-uclibc/ 
[ebuild   R   ] sys-apps/busybox-1.1.0 to /root/ROOT/armeb-unknown-linux-gnu/ 
[ebuild  N    ] sys-apps/busybox-1.1.0 to /root/ROOT/avr/ 
[ebuild  N    ] sys-apps/busybox-1.1.0 to /root/ROOT/cris-gentoo-linux-uclibc/ 
[ebuild  N    ] sys-apps/busybox-1.1.0 to /root/ROOT/ee/ 
[ebuild  N    ] sys-apps/busybox-1.1.0 to /root/ROOT/hppa-unknown-linux-gnu/ 
[ebuild  N    ] sys-apps/busybox-1.1.0 to /root/ROOT/hppa1.1-unknown-linux-gnu/ 
[ebuild  N    ] sys-apps/busybox-1.1.0 to /root/ROOT/hppa2.0-unknown-linux-gnu/ 
[ebuild  N    ] sys-apps/busybox-1.1.0 to /root/ROOT/hppa64-unknown-linux-gnu/ 
[ebuild   R   ] sys-apps/busybox-1.1.0 to /root/ROOT/i386-gentoo-linux-uclibc/ 
[ebuild   R   ] sys-apps/busybox-1.1.0 to /root/ROOT/i386-pc-linux-gnu/ 
[ebuild   R   ] sys-apps/busybox-1.1.0 to /root/ROOT/i686-gentoo-linux-uclibc/ 
[ebuild  N    ] sys-apps/busybox-1.1.0 to /root/ROOT/ia64-unknown-linux-gnu/ 
[ebuild  N    ] sys-apps/busybox-1.1.0 to /root/ROOT/iop/ 
[ebuild  N    ] sys-apps/busybox-1.1.0 to /root/ROOT/m68k-gentoo-linux-uclibc/ 
[ebuild  N    ] sys-apps/busybox-1.1.0 to /root/ROOT/m68k-unknown-linux-gnu/ 
[ebuild   R   ] sys-apps/busybox-1.1.0 to /root/ROOT/mips-gentoo-linux-uclibc/ 
[ebuild   R   ] sys-apps/busybox-1.1.0 to /root/ROOT/mips-unknown-linux-gnu/ 
[ebuild  N    ] sys-apps/busybox-1.1.0 to /root/ROOT/mips64-unknown-linux-gnu/ 
[ebuild  N    ] sys-apps/busybox-1.1.0 to /root/ROOT/mips64el-unknown-linux-gnu/ 
[ebuild   R   ] sys-apps/busybox-1.1.0 to /root/ROOT/mipsel-gentoo-linux-uclibc/ 
[ebuild   R   ] sys-apps/busybox-1.1.0 to /root/ROOT/mipsel-unknown-linux-gnu/ 
[ebuild   R   ] sys-apps/busybox-1.1.0 to /root/ROOT/powerpc-gentoo-linux-uclibc/ 
[ebuild  N    ] sys-apps/busybox-1.1.0 to /root/ROOT/powerpc-softfloat-linux-gnu/ 
[ebuild   R   ] sys-apps/busybox-1.1.0 to /root/ROOT/powerpc-softfloat-linux-uclibc/ 
[ebuild   R   ] sys-apps/busybox-1.1.0 to /root/ROOT/powerpc-unknown-linux-gnu/ 
[ebuild  N    ] sys-apps/busybox-1.1.0 to /root/ROOT/powerpc64-unknown-linux-gnu/ 
[ebuild   R   ] sys-apps/busybox-1.1.0 to /root/ROOT/s390-ibm-linux-gnu/ 
[ebuild   R   ] sys-apps/busybox-1.1.0 to /root/ROOT/s390x-ibm-linux-gnu/ 
[ebuild  N    ] sys-apps/busybox-1.1.0 to /root/ROOT/sh-gentoo-linux-uclibc/ 
[ebuild  N    ] sys-apps/busybox-1.1.0 to /root/ROOT/sh-unknown-linux-gnu/ 
[ebuild   R   ] sys-apps/busybox-1.1.0 to /root/ROOT/sh4-gentoo-linux-uclibc/ 
[ebuild   R   ] sys-apps/busybox-1.1.0 to /root/ROOT/sh4-unknown-linux-gnu/ 
[ebuild  N    ] sys-apps/busybox-1.1.0 to /root/ROOT/sh4eb-gentoo-linux-uclibc/ 
[ebuild  N    ] sys-apps/busybox-1.1.0 to /root/ROOT/sh4eb-unknown-linux-gnu/ 
[ebuild  N    ] sys-apps/busybox-1.1.0 to /root/ROOT/sheb-gentoo-linux-uclibc/ 
[ebuild  N    ] sys-apps/busybox-1.1.0 to /root/ROOT/sheb-unknown-linux-gnu/ 
[ebuild  N    ] sys-apps/busybox-1.1.0 to /root/ROOT/sparc-gentoo-linux-uclibc/ 
[ebuild   R   ] sys-apps/busybox-1.1.0 to /root/ROOT/sparc-unknown-linux-gnu/ 
[ebuild   R   ] sys-apps/busybox-1.1.0 to /root/ROOT/sparc64-unknown-linux-gnu/ 
[ebuild  N    ] sys-apps/busybox-1.1.0 to /root/ROOT/x86_64-gentoo-linux-uclibc/ 
[ebuild   R   ] sys-apps/busybox-1.1.0 to /root/ROOT/x86_64-pc-linux-gnu/ 

Well that’s cool and all. But how to test stuff? Thats where qemu comes to the rescue.. Few mins of hacking and poof we come up with a little something something like so.

#!/bin/sh

pkg_genext2fs() {

        if ! type -p genext2fs > /dev/null; then
                echo "no genext2fs"
                return 1
        fi

        cd $1 || return 1

rom=/rom

        mkdir -p {usr/,}{s,}bin etc/init.d proc/self dev/shm root tmp ./${rom}

        chmod 700 root
        chmod 1777 tmp

################
cat <<EOF > etc/init.d/rcS 
#!/bin/sh
rom=$rom
mount -a
mount tmpfs \$rom -t tmpfs
mkdir -p \$rom/proc \$rom/dev \$rom/ro \$rom/tmp
mount -o bind /dev \$rom/dev
mount -o bind /proc \$rom/proc
busybox cp -a bin etc lib root sbin usr \$rom/
chmod 1777 \$rom/tmp
pivot_root \$rom/ \$rom/ro
EOF
################

        chmod +x etc/init.d/rcS

        if [ ! -L proc/self/exe ]; then
                cd proc/self
                ln -s /bin/busybox exe

                if [ -e /proc/self/fd ] ; then
                        ln -fs /proc/self/fd fd 2> /dev/null
                        ln -fs fd/0 stdin
                        ln -fs fd/1 stdout
                        ln -fs fd/2 stderr
                fi
                cd ../../
        fi
        cd etc
        ln -sf ../proc/mounts mtab
        grep -v ^# /etc/protocols > protocols

        cd ..

        cd dev
        ln -sf /proc/kcore core

        # ttys
        for i in `seq 0 9`; do
                [ ! -e tty$i ] && mknod tty$i c 4 $i
        done
        [ ! -e tty ] && (mknod -m 666 tty c 5 0 2> /dev/null || echo cant mknod for tty)

        [ ! -e null ] &&  (mknod -m 666 null c 1 3 2> /dev/null || echo cant mknod for null)
        [ ! -e zero ] && (mknod -m 666 zero c 1 5 2> /dev/null || echo cant mknod for zero)
        [ ! -e urandom ] && (mknod -m 644 urandom c 1 9 2> /dev/null || echo cant mknod for urandom)
        [ ! -e random ] && mknod random c 1 8
        [ ! -e console ] && mknod console c 5 1
        [ ! -e full ] && mknod full c 1 7
        [ ! -e kmem ] && mknod kmem c 1 2
        [ ! -e mem ] && mknod mem c 1 1
        [ ! -e port ] && mknod port c 1 4

        # IDE devs
                [ ! -e hda ] && mknod hda b 3 0
        [ ! -e hdb ] && mknod hdb b 3 64
        [ ! -e hdc ] && mknod hdc b 22 0
        [ ! -e hdd ] && mknod hdd b 22 64

        # loop devs
        for i in `seq 0 7`; do [ ! -e loop$i ] && mknod loop$i b 7 $i; done

        # ram devs
        for i in `seq 0 9`; do [ ! -e  ram$i ] && mknod ram$i b 1 $i ; done
        [ ! -L ram ] && ln -s ram1 ram

        # virtual console screen devs
        for i in `seq 0 9`; do [ ! -e vcs$i ] && mknod vcs$i b 7 $i; done

        chown root:tty tty 2> /dev/null
        chown root:sys null zero 2> /dev/null
        cd -

################
## /etc/fstab ##
################
cat <<EOF > etc/fstab
#/dev/root      /               ro,defaults  defaults 0 0
proc            /proc           proc    defaults 0 0
#none           /dev            devfs   defaults 0 0
devpts          /dev/pts        devpts  defaults,gid=5,mode=620  0 0
tmpfs           /tmp            tmpfs   defaults 0 0
tmpfs           $rom            tmpfs   defaults 0 0
tmpfs           /dev/shm        tmpfs   defaults 0 0
EOF
################

        cd ${TOPDIR} || return 1
        cd $1 || return 1

        scanelf -qnBR . | grep libgcc
        REALSIZE=$(LANG=C du ./ -s -c -k | grep total | awk '{print $1}')
        ADDTOROOTSIZE=$([ $REALSIZE -ge 20000 ] && echo 16384 || echo 16)
        SIZE=$(( $REALSIZE + $ADDTOROOTSIZE ))
        INODES=$(($(find ${DESTDIR} | wc -l) + 400))
        genext2fs -i ${INODES} -b ${SIZE} -q -d . $2
        cd $TOPDIR
}

TOPDIR=$PWD

tarballs="$@"
[[ $1 == "" ]] && tarballs="$(ls *.tar.bz2)"

for x in ${tarballs} ; do
        d=$(basename $x .tar.bz2)
        cd $TOPDIR
        pkg_genext2fs ${d} $TOPDIR/${d}.ext2
        [ $? != 0 ] && echo WTF $?
done

End result..
arm-gentoo-linux-uclibc.ext2 armeb-softfloat-linux-uclibc.ext2 mips-gentoo-linux-uclibc.ext2 powerpc-softfloat-linux-uclibc.ext2
arm-softfloat-linux-uclibc.ext2 i386-gentoo-linux-uclibc.ext2 mipsel-gentoo-linux-uclibc.ext2 sh4-gentoo-linux-uclibc.ext2
armeb-gentoo-linux-uclibc.ext2 i686-gentoo-linux-uclibc.ext2 powerpc-gentoo-linux-uclibc.ext2

As we can see uClibc is clearly better at everything. In this case being cross compiled and producing all the parts needed in order to be booted..

wget http://tinderbox.x86.dev.gentoo.org/portage/local/misc/i386-gentoo-linux-uclibc.ext2

qemu -kernel /boot/bzImage -append “root=/dev/hda init=/sbin/init rootfstype=ext2 ro” i386-gentoo-linux-uclibc.ext2

Sweet worked like a charm..
Next task is to mass x-compile some kernels for other arches and see what qemu can do for us..