Cross-Compiler targeting Alpine

From Alpine Linux
Jump to: navigation, search

This page explains how I set up a cross-compiler on ArchLinux (both x86 and x86_64) targeting Alpine. There are only a few situations in which you'd want to do this. I had to do it because I wanted to port GHC to Alpine, and to do that, you need to start with some alreay-compiled GHC binary. So that meant cross-compiling from some system where the binaries were already available.

I used Buildroot to setup my cross-compiler. These instructions target Buildroot 2013.05 and Alpine 2.6.0, both released in May 2013. The specific versions installed by this Buildroot (as I've configured it) are:

  • kernel headers v3.9.2
  • uClibc v0.9.33.2
  • binutils v2.23.2
  • gcc v4.7.3
  • gmp v5.1.1
  • mpc v1.0.1
  • mpfr v3.1.2 (Alpine currently using mpfr3-3.1.1)
  • m4 v1.4.16
  • autoconf v2.68 (Alpine currently using autoconf-2.69)
  • automake v1.11.6 (Alpine currently using automake-1.13.1)
  • libiconv v1.14 (Alpine currently using 1.12)
  • libtool v2.2.10 (Alpine currently using libtool-2.4.2)
  • ncurses v5.9
  • libffi v3.0.13

Buildroot also installs BusyBox v1.12.0, but I ignore this.

The instructions below also describe how to install a cross-compiling LLVM and Clang v3.2 on top of the Buildroot tools. This might be useful for some purposes.

If you want to use these instructions as a basis for doing the same thing with more recent releases of Buildroot or targeting newer releases of Alpine, be sure to check for patches added to the Alpine ports tree, especially to gcc or uClibc, after 2.6.0.

  1. On the Arch system, install the following packages:

    pacman -S less man-db man-pages licenses procps-ng psmisc sysfsutils \ base-devel openssh cpio elfutils gperf rsync unzip vim wget zip

  2. On the Arch system, do the following:

    mkdir ~/python2-path ln -s /usr/bin/python2 ~/python2-path/python export PATH=$HOME/python2-path:$PATH

  3. On the Arch system, prepare the sources and patches to build the cross-compiler.

    mkdir -p $HOME/sources/patches && cd $HOME/sources get_from_aports() ( local APORTS_URL=http://git.alpinelinux.org/cgit/aports/plain mkdir -p $1 cd $1 || return 1 if [ -n "$3" ]; then wget -N -O $(printf '%02d-%s' $3 ${2##*/}) $APORTS_URL/main/$2 else wget -N $APORTS_URL/main/$2 fi ) get_from_gist() ( local GIST_URL=https://gist.github.com/dubiousjim/5603159/raw mkdir -p $1 cd $1 || return 1 if [ -n "$3" ]; then wget -N -O $(printf '%02d-%s' $3 ${2##*/}) $GIST_URL/$2 else wget -N $GIST_URL/$2 fi ) get_from_aports patches/binutils/2.23.2 binutils/binutils-ld-fix-static-linking.patch get_from_aports patches/gcc/4.7.3 gcc/pt_gnu_eh_frame.patch 1 get_from_aports patches/gcc/4.7.3 gcc/uclibc-getipinfo.patch 2 get_from_aports patches/gcc/4.7.3 gcc/gcc-4.7-dynamic-linker.patch 3 get_from_aports patches/gcc/4.7.3 gcc/gcc-4.6-pr32219.patch 4 get_from_aports patches/gcc/4.7.3 gcc/gcc-pure64.patch 5 sed -i -e 's/\$(ESP_NOPIE_CFLAGS) //' patches/gcc/4.7.3/03-gcc-4.7-dynamic-linker.patch get_from_aports patches/gmp/5.1.1 gmp5/gmp-4.1.4-noexecstack.patch # work around rpath issue get_from_gist patches/libiconv/1.14 libiconv-configure.patch get_from_aports patches/uClibc/0.9.33.2 libc0.9.32/APKBUILD source=$( cd patches/uClibc/0.9.33.2 && . APKBUILD && echo $source ) j=1 for i in $source; do case $i in *.patch) get_from_aports patches/uClibc/0.9.33.2 libc0.9.32/$i $((j++)) esac done # finally, set abi version and remove unsupported warnings c flag get_from_gist patches/uClibc/0.9.33.2 uclibc-Rules.mak.patch $j case `uname -m` in x86_64) ARCH=x86_64; H=$ARCH T=$ARCH;; i?86) ARCH=x86 H=i686 T=i486;; *) echo Unknown architecture;; esac get_from_aports . libc0.9.32/uclibcconfig.$ARCH echo '# USE_OLD_VFPRINTF is not set' >> uclibcconfig.$ARCH BUILDROOTVER=2013.05-rc2 wget -N http://buildroot.uclibc.org/downloads/buildroot-$BUILDROOTVER.tar.bz2 get_from_gist . config.$ARCH get_from_gist . buildroot-makes.patch


  4. Now, compile buildroot:

    cd && tar -xjf sources/buildroot-$BUILDROOTVER.tar.bz2 cd buildroot-$BUILDROOTVER && patch -p1 -i ../sources/buildroot-makes.patch BUILDROOT=$HOME/buildroot-$ARCH mkdir $BUILDROOT && cd $BUILDROOT && cp ../sources/config.$ARCH .config make O=$PWD -C ../buildroot-$BUILDROOTVER 2>&1 | tee build.log

  5. If the compilation was successful, then:

    export PATH=$PATH:$BUILDROOT/host/usr/bin

  6. (Optional) Download and compile LLVM and clang. (This needs the patch at #1915, which hasn't yet made it into the aports tree.)

    cd $HOME/sources wget -N http://llvm.org/releases/3.2/llvm-3.2.src.tar.gz wget -N http://llvm.org/releases/3.2/clang-3.2.src.tar.gz get_from_aports . llvm/llvm-3.2-alpine-linux.patch cd && tar -xzf sources/llvm-3.2.src.tar.gz && tar -xzf sources/clang-3.2.src.tar.gz && cd llvm-3.2.src rm -rf tools/clang && mv ../clang-3.2.src tools/clang patch -p1 < ../sources/llvm-3.2-alpine-linux.patch cd && mkdir llvm-build.$ARCH && cd llvm-build.$ARCH ../llvm-3.2.src/configure --prefix=$BUILDROOT/host/usr \ --build=$H-pc-linux-gnu --host=$H-pc-linux-gnu --target=$T-buildroot-linux-uclibc \ --with-gcc-toolchain=$BUILDROOT/host/usr --with-default-sysroot=$BUILDROOT/staging \ --enable-jit --enable-pic --enable-assertions --enable-optimized --enable-shared --disable-docs \ --enable-targets=x86,x86_64 | tee build.log { make && make install; } 2>&1 | tee -a build.log

Now on the Arch system you can do this:

echo 'int main(void) {return 0;}' > test.c i486-buildroot-linux-uclibc-gcc -o test test.c

Or you could use i486-buildroot-linux-uclibc-clang instead.

The resulting binary won't run on the Arch system (or from a shell launched from inside the Arch chroot, if the Arch system is on a chroot inside your Alpine system, as mine is). But it will run on the Alpine system (or from a shell launched from outside the Arch chroot).