GHC Cross Compiler for Raspberry Pi

Calvin Beck edited this page Dec 17, 2015 · 13 revisions

Host setup

  • The following cross compilation was done on Linux Mint 17 64bit, although the instructions should work on any Debian/Ubuntu based distribution.

  • Install llvm:

    sudo apt-get install llvm-3.5 llvm-3.5-tools
  • Install ghc from from distribution repository:

    sudo apt-get install haskell-platform
  • Because ghci uses it (and even though the ghci you build will be kind of useless), you'll need to install ncurses:

    sudo apt-get install ncurses-dev
  • Some notes:

    • Each version of GHC requires a specific version of llvm. For ghc-7.8 or ghc-7.10 you need to use llvm-3.5. llvm-3.5 has a bug regarding the ghc calling convention that was fixed in 3.5.2. Make sure you have installed llvm 3.5.2 or higher. In current Debian, this is in unstable.

    • There are settings for llvm versions in /usr/lib/ghc/settings. You may need to set

      ("LLVM llc command", "llc-3.5"),
      ("LLVM opt command", "opt-3.5")
      

      there.

    • ghc-7.6 and ghc-7.8 have bugs preventing building a cross-compiling ghc-7.10. Best results seem to be obtained by using 7.10 for both the host ghc and the compiler source.

    • (Gentoo specific) The Raspberry Pi tools that we will use are dynamically linked to 32-bit zlib, so if your host is 64-bit, you need

      echo "sys-libs/zlib abi_x86_32" >> /etc/portage/package.use/ghc-crossdev-rpi

Get Raspberry Pi Tools

  • Create directory for cross compiler build and change into it:

    mkdir ~/raspberrypi
    cd ~/raspberrypi
  • Install Raspberry Pi compilation tools into the working directory:

    git clone https://github.com/raspberrypi/tools.git
  • Add the cross compiler directory to your shell path:

    export PATH=$HOME/raspberrypi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin:$PATH

Get GHC Source

  • Download GHC tarball (ghc-7.10.2-src.tar.xz) into working directory and unpack:

    tar xJf ghc-7.10.2-src.tar.xz
  • Configure the GHC build using the following commands: (This will configure the build such that it uses the installed Raspberry Pi cross tools, and installs the GHC cross tools in /usr/local/haskell/ghc-7.10.2-arm).

    cd ~/raspberrypi/ghc-7.10.2
    ./configure --target=arm-linux-gnueabihf --with-gcc=arm-linux-gnueabihf-gcc \
      --prefix=/usr/local/haskell/ghc-7.10.2-arm --with-llc=llc-3.5 --with-opt=opt-3.5
    cp mk/build.mk.sample mk/build.mk
  • Edit mk/build.mk and uncomment the line with BuildFlavour = perf-cross or BuildFlavour = quick-cross, as preferred.

Get Raspberry Pi Libraries

  • The Raspberry Pi was setup with Raspbian version September 2014.

  • On the Raspberry Pi install ncurses-dev:

    sudo apt-get install ncurses-dev
  • Copy the RootFS from the Raspberry Pi to the build machine for required libraries (where $PI_ADDR in the rsync command is the IP address of your Pi):

    cd ~/raspberrypi
    mkdir rootfs
    rsync -rl --delete-after --safe-links pi@$PI_ADDR:/{lib,usr} $HOME/raspberrypi/rootfs
  • There are quite a few symlinks in the Pi file system’s /usr/lib/arm-linux-gnueabihf directory that point to /lib/arm-linux-gnueabihf that will not be copied properly by the above rsync. Therefore, change to the root file system copy’s /usr/lib/arm-linux-gnueabihf and create the following links:

    cd ~/raspberrypi/rootfs/usr/lib/arm-linux-gnueabihf  
    ln -s ../../../lib/arm-linux-gnueabihf/libanl.so.1 libanl.so  
    ln -s ../../../lib/arm-linux-gnueabihf/libBrokenLocale.so.1 libBrokenLocale.so  
    ln -s ../../../lib/arm-linux-gnueabihf/libcidn.so.1 libcidn.so  
    ln -s ../../../lib/arm-linux-gnueabihf/libcrypt.so.1 libcrypt.so  
    ln -s ../../../lib/arm-linux-gnueabihf/libdl.so.2 libdl.so  
    ln -s ../../../lib/arm-linux-gnueabihf/libm.so.6 libm.so  
    ln -s ../../../lib/arm-linux-gnueabihf/libnsl.so.1 libnsl.so  
    ln -s ../../../lib/arm-linux-gnueabihf/libnss_compat.so.2 libnss_compat.so  
    ln -s ../../../lib/arm-linux-gnueabihf/libnss_dns.so.2 libnss_dns.so  
    ln -s ../../../lib/arm-linux-gnueabihf/libnss_files.so.2 libnss_files.so  
    ln -s ../../../lib/arm-linux-gnueabihf/libnss_hesiod.so.2 libnss_hesiod.so  
    ln -s ../../../lib/arm-linux-gnueabihf/libnss_nisplus.so.2 libnss_nisplus.so  
    ln -s ../../../lib/arm-linux-gnueabihf/libnss_nis.so.2 libnss_nis.so  
    ln -s ../../../lib/arm-linux-gnueabihf/libresolv.so.2 libresolv.so  
    ln -s ../../../lib/arm-linux-gnueabihf/librt.so.1 librt.so  
    ln -s ../../../lib/arm-linux-gnueabihf/libthread_db.so.1 libthread_db.so  
    ln -s ../../../lib/arm-linux-gnueabihf/libutil.so.1 libutil.so    

Symlink the Pi root filesystem into the tools

  • Find the current ARM compiler root filesystem using the following command:

    arm-linux-gnueabihf-gcc -print-sysroot
  • On the build system I was using this was:

    ~/raspberrypi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/../arm-linux-gnueabihf/libc
  • Rename the original and link in the rootfs copied from the Pi:

    cd ~/raspberrypi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/../arm-linux-gnueabihf
    mv libc libc.orig
    ln -s ~/raspberrypi/rootfs libc

Perform the cross compile

  • Start the build with:

    cd ~/raspberrypi/ghc-7.10.2
    make
  • When the build finishes, the cross compiler will be available at:

    ~/raspberrypi/ghc-7.10.2/inplace/bin/ghc-stage1
  • Install the build cross compiler:

    sudo make install
  • The install binary directory, /usr/local/haskell/ghc-7.10.2-arm/bin should be added to your shell path so that the compiler and tools are available.

Package cross-install

  • The following sequence of commands is an example of setting up the cabal sandbox and issuing the proper cabal command to build and install the package random with the cross compiler. This should work for any pure-Haskell package with build-type: Simple.

    cabal sandbox init
    cabal --with-ghc=arm-unknown-linux-gnueabihf-ghc --with-ghc-pkg=arm-unknown-linux-gnueabihf-ghc-pkg --with-ld=arm-linux-gnueabihf-ld --with-strip=arm-linux-gnueabihf-strip install random
  • Packages that use hsc2hs and/or configure scripts (such as directory) require some additional flags. You may want to use the below as cabal.config file in the directory containing your sandbox:

    jobs: 1
    
    compiler: ghc
    with-compiler: arm-unknown-linux-gnueabihf-ghc
    with-hc-pkg:   arm-unknown-linux-gnueabihf-ghc-pkg
    configure-option: --host=arm-linux-gnueabihf
    
    program-locations
      gcc-location:   arm-linux-gnueabihf-gcc
      ar-location:    arm-linux-gnueabihf-ar
      ld-location:    arm-linux-gnueabihf-ld
      strip-location: arm-linux-gnueabihf-strip
    
    program-default-options
      hsc2hs-options: -x
    

    This should be equivalent to:

    cabal --with-ghc=arm-unknown-linux-gnueabihf-ghc \
          --with-ghc-pkg=arm-unknown-linux-gnueabihf-ghc-pkg \
          --with-gcc=arm-linux-gnueabihf-gcc \
          --with-ar=arm-linux-gnueabihf-ar \
          --with-ld=arm-linux-gnueabihf-ld \
          --with-strip=arm-linux-gnueabihf-strip \
          --configure-option=--host=arm-linux-gnueabihf \
          --hsc2hs-option=-x \
          install $package_name