GHC as a cross-compiler update
TL;DR:
Gentoo’s dev-lang/ghc-8.2.1_rc1 supports both cross-building and
cross-compiling modes! It’s useful for cross-compiling haskell software
and initial porting of GHC itself on a new gentoo target.
Building a GHC cross-ompiler on Gentoo
Getting ${CTARGET}-ghc (cross-compiler) on gentoo:
# # convenience variables:
CTARGET=powerpc64-unknown-linux-gnu
#
# # Installing a target toolchain: gcc, glibc, binutils
crossdev ${CTARGET}
# # Installing ghc dependencies:
emerge-${CTARGET} -1 libffi ncurses gmp
#
# # adding 'ghc' symlink to cross-overlay:
ln -s path/to/haskell/overlay/dev-lang/ghc part/to/cross/overlay/cross-${CTARGET}/ghc
#
# # Building ghc crosscompiler:
emerge -1 cross-${CTARGET}/ghc
#
powerpc64-unknown-linux-gnu-ghc --info | grep Target
# ,("Target platform","powerpc64-unknown-linux")Cross-building GHC on Gentoo
Cross-building ghc on ${CTARGET}:
# # convenience variables:
CTARGET=powerpc64-unknown-linux-gnu
#
# # Installing a target toolchain: gcc, glibc, binutils
crossdev ${CTARGET}
# # Installing ghc dependencies:
emerge-${CTARGET} -1 libffi ncurses gmp
#
# # Cross-building ghc crosscompiler:
emerge-${CTARGET} --buildpkg -1 dev-lang/ghc
#
# # Now built packages can be used on a target to install
# # built ghc as: emerge --usepkg -1 dev-lang/ghcBuilding a GHC cross-compiler (generic)
That’s how you get a powerpc64 cross-compiler in a fresh git
checkout:
$ ./configure --target=powerpc64-unknown-linux-gnu
$ cat mk/build.mk
HADDOCK_DOCS=NO
BUILD_SPHINX_HTML=NO
BUILD_SPHINX_PDF=NO
# to speed things up
BUILD_PROF_LIBS=NO
$ make -j$(nproc)
$ inplace/bin/ghc-stage1 --info | grep Target
,("Target platform","powerpc64-unknown-linux")
Simple!
Below are details that have only historical (or backporting) value.
How did we get there?
Cross-compiling support in GHC is not a new thing. GHC wiki has
a detailed
section
on how to build a cross-compiler. That works quite good. You can even
target ghc at m68k: porting
example.
What did not work so well is the attempt to install the result! In some
places GHC build system tried to run ghc-pkg built for
${CBUILD}, in some places for ${CHOST}.
I never really tried to install a cross-compiler before. I think mostly
because I was usually happy to make cross-compiler build at all: making
GHC build for a rare target usually required a patch or two.
But one day I’ve decided to give full install a run. Original
motivation was a bit unusual: I wanted to free space on my hard drive.
The build tree for GHC usually takes about 6-8GB. I had about 15
GHC source trees lying around. All in all it took about 10% of all
space on my hard drive. Fixing make install would allow me to
install only final result and get rid of all intermediate files.
I’ve decided to test make install code on gentoo
dev-lang/ghc package as a proper package.
As a result a bunch of minor cleanups happened:
- fixed
NCG/llvmpresence for target - marked
aarch64asNCG/llvmtarget - fixed
${CTARGET}-prefixing forhp2ps - fixed
${CTARGET}-prefixing for non-canonical targets - dropped
${CTARGET}-prefixing forstage2installs (cross-builds) - added
${CTARGET}-prefixing forghci - fixed
stage2install to run only${CBUILD}tools - fixed all
stage2binaries to run on${CHOST}, not${CBUILD}
What works?
It allowed me to test various targets. Namely:
| Target | Bits | Endianness | Codegen |
|---|---|---|---|
| cross-aarch64-unknown-linux-gnu/ghc | 64 | LE | LLVM |
| cross-alpha-unknown-linux-gnu/ghc | 64 | LE | UNREG |
| cross-armv7a-unknown-linux-gnueabi/ghc | 32 | LE | LLVM |
| cross-hppa-unknown-linux-gnu/ghc | 32 | BE | UNREG |
| cross-m68k-unknown-linux-gnu/ghc | 32 | BE | UNREG |
| cross-mips64-unknown-linux-gnu/ghc | 32/64 | BE | UNREG |
| cross-powerpc64-unknown-linux-gnu/ghc | 64 | BE | NCG |
| cross-powerpc64le-unknown-linux-gnu/ghc | 64 | LE | NCG |
| cross-s390x-unknown-linux-gnu/ghc | 64 | BE | UNREG |
| cross-sparc-unknown-linux-gnu/ghc | 32 | BE | UNREG |
| cross-sparc64-unknown-linux-gnu/ghc | 64 | BE | UNREG |
I am running all of this on x86_64 (64-bit LE platform).
Quite a list! With help of qemu we can even test whether cross-compiler
produces something that works:
$ cat hi.hs
main = print "hello!"
$ powerpc64le-unknown-linux-gnu-ghc hi.hs -o hi.ppc64le
[1 of 1] Compiling Main ( hi.hs, hi.o )
Linking hi.ppc64le ...
$ file hi.ppc64le
hi.ppc64le: ELF 64-bit LSB executable, 64-bit PowerPC or cisco 7500, version 1 (SYSV), dynamically linked, interpreter /lib64/ld64.so.2, for GNU/Linux 3.2.0, not stripped
$ qemu-ppc64le -L /usr/powerpc64le-unknown-linux-gnu/ ./hi.ppc64le
"hello!"
Many qemu targets are slightly buggy and usually are very easy to fix!
A few recent examples:
epollsyscall is not wired properly onqemu-alpha: patch- CPU initialization code on
qemu-s390x - thread creation fails on
qemu-sparc32plusdue to simplemmap()emulation bug tcgonqemu-sparc64crashes at runtime instatic_code_gen_buffer()
Tweaking qemu is fun :)