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/ghc
Building 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
/llvm
presence for target - marked
aarch64
asNCG
/llvm
target - fixed
${CTARGET}
-prefixing forhp2ps
- fixed
${CTARGET}
-prefixing for non-canonical targets - dropped
${CTARGET}
-prefixing forstage2
installs (cross-builds) - added
${CTARGET}
-prefixing forghci
- fixed
stage2
install to run only${CBUILD}
tools - fixed all
stage2
binaries 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:
epoll
syscall is not wired properly onqemu-alpha
: patch- CPU initialization code on
qemu-s390x
- thread creation fails on
qemu-sparc32plus
due to simplemmap()
emulation bug tcg
onqemu-sparc64
crashes at runtime instatic_code_gen_buffer()
Tweaking qemu
is fun :)