GHC + musl = easier static linking

TL;DR: I made some GHC binaries for linux that rely on musl instead of glibc for easier static linking. Scroll down for the link. Dear *, As most of you know, musl is a linux only C standard library that is optimized for static linking. Statically linked binaries can simplify distribution and deployment. If you have tried to statically link a program with glibc, you will probably know that this does not always work as expected: for example, in many cases the resulting binary will require to be executed in a system that has the exact same version of glibc as the one that was used to compiled it. In essence, static binaries linked with glibc are oftentimes *less* portable than dynamic ones. I decided to see if I could use GHC with musl. The problem was that while I could find some references online, I could not find a precompiled version of GHC that uses musl. So I decided to try and a build one myself. I was able to successfully bootstrap GHC under musl and I am posting it today in case someone else finds it usefull: https://drive.google.com/folderview?id=0B0zktggRQYRkbGJkbmU0UFFSYUE#list I posting this on google drive, I have no better place to host this, hopefully google will not disable my account :-) This is a fully bootstrapped (i.e. stage 2) GHC and *not* a cross compiler. So this will *not* work in a typical glibc based linux distribution. You need a complete musl based environment to use it (see bellow for that). Also, the binaries produced by this GHC will all depend on musl and not work on most glibc based distros. On the other hand, statically compiled binaries should be very portable and will not depend on any particular C standard library. I have done some minimal testing and it seems to work rather well; everything I tried to compile from hackage just worked. Additionally it can compile GHC itself which is always a good test. The size of the resulting static binaries is acceptable. In my 64 bit system, a simple hello world has the following sizes (stripped): 800K glibc dynanic, 1648K glibc static, and 1012K musl static. Why not use a cross-compiler? I have found that in practice it is easier to have a separete environment for producing static binaries. A cross-compiler requires that you also cross-compile all the C libraries that you use. Additionally, cabal has still problems (e.g. [1]) with cross-compiling and will not work out of the box. Note that musl is not 100% compatible with glibc, so some things might work slightly differently. If you do not like using a GHC precompiled by someone else, you can use my binaries to compile GHC under musl yourself; it would be easier than cross-compiling GHC from scratch. So how do you use this? You need a complete musl based environment. There a few musl based distros [2], albeit most of them are geared towards embedded applications. I personally use the experimental musl based gentoo image [3] which feels more like a regular distro. You can use it with chroot, lxc, or systemd-nspawn. For the sake of completeness, I have included at the end of this message some quick and dirty instructions on how to do this. [1] https://github.com/haskell/cabal/issues/1493 [2] http://wiki.musl-libc.org/wiki/Projects_using_musl [3] http://distfiles.gentoo.org/experimental/amd64/musl/ 8<------------------------------------- # gentoo stage3 image instructions CHROOT_PATH=/var/tmp/gentoo-musl cd /var/tmp wget http://distfiles.gentoo.org/experimental/amd64/musl/stage3-amd64-musl-vanill... mkdir "$CHROOT_PATH" tar xjf stage3-amd64-musl-vanilla-20150405.tar.bz2 -C "$CHROOT_PATH" for x in dev proc sys; do mount --bind /$x "$CHROOT_PATH/$x"; done cp -L /etc/resolv.conf "$CHROOT_PATH/etc" chroot "$CHROOT_PATH" /bin/bash -l PS1="(chroot) $PS1" # fetch the gentoo package db for the first time (slow): emerge-webrsync # enable static libraries (*.a), which are disabled by default: sed -ie 's/USE="/USE="static-libs /' /etc/portage/make.conf # reinstall/recompile gmp so that we have its static libs: emerge --oneshot dev-libs/gmp # install ghc: tar xJf /path/to/ghc-7.10.1-x86_64-unknown-linux-musl.tar.xz -C /tmp cd /tmp/ghc-7.10.1; ./configure --prefix=/opt/ghc-7.10.1; make install # compile a hello world: cd /tmp echo 'main = putStrLn "Hello world"' > test.hs /opt/ghc-7.10.1/bin/ghc --make -O -optl-static test.hs ./test file ./test # Of course, using chroot this way is insecure, don't use to install packages from hackage.

On Wed, May 27, 2015 at 1:40 PM, Marios Titas
I posting this on google drive, I have no better place to host this, hopefully google will not disable my account :-)
github? -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

I wouldn't recommend churning a 70mb binary over and over, but a one-off is
no problem if they have to download it regardless.
I've had many-gigabyte git repos that were perfectly usable.
On Thu, May 28, 2015 at 2:23 PM, Marios Titas
On Thu, May 28, 2015 at 7:16 PM, Brandon Allbery
wrote: On Wed, May 27, 2015 at 1:40 PM, Marios Titas
wrote: I posting this on google drive, I have no better place to host this, hopefully google will not disable my account :-)
github?
Does github allow large (70 MiB) binaries? _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

I'd recommend using the release feature on Github. We just started using it
for minghc
On Thu, May 28, 2015, 10:27 PM Christopher Allen
I wouldn't recommend churning a 70mb binary over and over, but a one-off is no problem if they have to download it regardless.
I've had many-gigabyte git repos that were perfectly usable.
On Thu, May 28, 2015 at 2:23 PM, Marios Titas
wrote: On Thu, May 28, 2015 at 7:16 PM, Brandon Allbery
wrote: On Wed, May 27, 2015 at 1:40 PM, Marios Titas
wrote: I posting this on google drive, I have no better place to host this, hopefully google will not disable my account :-)
github?
Does github allow large (70 MiB) binaries? _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

I posted some notes about GHC on musl here:
https://github.com/redneb/ghc-alt-libc. I also posted there some notes
and a download link of a (mostly working) port of GHC to uClibc.
On Wed, May 27, 2015 at 6:40 PM, Marios Titas
TL;DR: I made some GHC binaries for linux that rely on musl instead of glibc for easier static linking. Scroll down for the link.
Dear *,
As most of you know, musl is a linux only C standard library that is optimized for static linking. Statically linked binaries can simplify distribution and deployment. If you have tried to statically link a program with glibc, you will probably know that this does not always work as expected: for example, in many cases the resulting binary will require to be executed in a system that has the exact same version of glibc as the one that was used to compiled it. In essence, static binaries linked with glibc are oftentimes *less* portable than dynamic ones.
I decided to see if I could use GHC with musl. The problem was that while I could find some references online, I could not find a precompiled version of GHC that uses musl. So I decided to try and a build one myself. I was able to successfully bootstrap GHC under musl and I am posting it today in case someone else finds it usefull:
https://drive.google.com/folderview?id=0B0zktggRQYRkbGJkbmU0UFFSYUE#list
I posting this on google drive, I have no better place to host this, hopefully google will not disable my account :-) This is a fully bootstrapped (i.e. stage 2) GHC and *not* a cross compiler. So this will *not* work in a typical glibc based linux distribution. You need a complete musl based environment to use it (see bellow for that). Also, the binaries produced by this GHC will all depend on musl and not work on most glibc based distros. On the other hand, statically compiled binaries should be very portable and will not depend on any particular C standard library. I have done some minimal testing and it seems to work rather well; everything I tried to compile from hackage just worked. Additionally it can compile GHC itself which is always a good test. The size of the resulting static binaries is acceptable. In my 64 bit system, a simple hello world has the following sizes (stripped): 800K glibc dynanic, 1648K glibc static, and 1012K musl static.
Why not use a cross-compiler? I have found that in practice it is easier to have a separete environment for producing static binaries. A cross-compiler requires that you also cross-compile all the C libraries that you use. Additionally, cabal has still problems (e.g. [1]) with cross-compiling and will not work out of the box.
Note that musl is not 100% compatible with glibc, so some things might work slightly differently.
If you do not like using a GHC precompiled by someone else, you can use my binaries to compile GHC under musl yourself; it would be easier than cross-compiling GHC from scratch.
So how do you use this? You need a complete musl based environment. There a few musl based distros [2], albeit most of them are geared towards embedded applications. I personally use the experimental musl based gentoo image [3] which feels more like a regular distro. You can use it with chroot, lxc, or systemd-nspawn. For the sake of completeness, I have included at the end of this message some quick and dirty instructions on how to do this.
[1] https://github.com/haskell/cabal/issues/1493 [2] http://wiki.musl-libc.org/wiki/Projects_using_musl [3] http://distfiles.gentoo.org/experimental/amd64/musl/
8<-------------------------------------
# gentoo stage3 image instructions
CHROOT_PATH=/var/tmp/gentoo-musl cd /var/tmp wget http://distfiles.gentoo.org/experimental/amd64/musl/stage3-amd64-musl-vanill... mkdir "$CHROOT_PATH" tar xjf stage3-amd64-musl-vanilla-20150405.tar.bz2 -C "$CHROOT_PATH" for x in dev proc sys; do mount --bind /$x "$CHROOT_PATH/$x"; done cp -L /etc/resolv.conf "$CHROOT_PATH/etc" chroot "$CHROOT_PATH" /bin/bash -l PS1="(chroot) $PS1" # fetch the gentoo package db for the first time (slow): emerge-webrsync # enable static libraries (*.a), which are disabled by default: sed -ie 's/USE="/USE="static-libs /' /etc/portage/make.conf # reinstall/recompile gmp so that we have its static libs: emerge --oneshot dev-libs/gmp # install ghc: tar xJf /path/to/ghc-7.10.1-x86_64-unknown-linux-musl.tar.xz -C /tmp cd /tmp/ghc-7.10.1; ./configure --prefix=/opt/ghc-7.10.1; make install # compile a hello world: cd /tmp echo 'main = putStrLn "Hello world"' > test.hs /opt/ghc-7.10.1/bin/ghc --make -O -optl-static test.hs ./test file ./test
# Of course, using chroot this way is insecure, don't use to install packages from hackage.
participants (4)
-
Brandon Allbery
-
Christopher Allen
-
Marios Titas
-
Michael Snoyman