Building Haskell for Omni OS

OpenSolaris is a fascinating operating system, developed by sun just before it was bought by Oracle. It didn’t last very long, the codebase was close sourced by Oracle soon after, but it’s short life has left an impact. Features that it was famous for were

  • Zones: Lightweight containers as a first class citizen (They can even run linux!)
  • SMF: Service management that doesn’t suck
  • Crossbow: Software defined networks for zones
  • DTrace: Needs no introduction

The Illumos Project has forked the OpenSolaris kernel and a few distributions have popped up using it, one of which is OmniOS, a server oriented flavor.

Download Haskell

For those who don’t want to DIY, you can download:

Building it yourself

Instead of blowing the dust of a really old solaris build of GHC and building newer and newer versions, the easiest way to bring haskell to a new platform is to cross compile. The way this works is that we build a haskell cross compiler (using our C cross compiler) and then cross compile haskell itself. This is a simple, but long journey. We then have to bootstrap cabal on our OmniOS system.

Sysroot

The easiest way to build a cross compiler is to use a sysroot. This is a subset of the root file system of your target OS that contains all of the libraries needed to link against. A nice speed up is that the illumos project provides a bare bones one for us! So lets grab that:

NOTE
In all the code examples, we expect $SYSROOT to be set to a path leading to the sysroot.

URL=https://github.com/illumos/sysroot/releases/download/20181213-de6af22ae73b-v1/illumos-sysroot-i386-20181213-de6af22ae73b-v1.tar.gz
rm -fr $SYSROOT
mkdir -p $SYSROOT
wget $URL -O $SYSROOT/sysroot.tar.gz
pushd $SYSROOT
tar xvf sysroot.tar.gz
ln -s lib lib64
popd
export PATH=$PATH:$SYSROOT/bin:$SYSROOT/usr/bin

Binutils

Binutils provides us with a linker and some other utilities that our compilers will use to build executables for OmniOS

BINUTILS_NAME=binutils-2.39
BINUTILS_SOURCE_ARCHIVE=$BINUTILS_NAME.tar.bz2
wget http://ftpmirror.gnu.org/binutils/$BINUTILS_SOURCE_ARCHIVE
tar xjfv $BINUTILS_SOURCE_ARCHIVE

BINUTILS_BUILD_DIR=binutils-build
mkdir $BINUTILS_BUILD_DIR

pushd $BINUTILS_BUILD_DIR
../$BINUTILS_NAME/configure      \
  --with-sysroot=$SYSROOT        \
  --target=x86_64-pc-solaris2.11 \
  --prefix=$SYSROOT              \
  --disable-nls                  \
  --disable-multilib             \
  --with-gnu-as                  \
  --with-gnu-ld                  \
  --disable-libssp
make -j4 all
make install
popd

GCC

We can now build gcc, which is part of the GNU Compiler Collection or… GCC

GCC_NAME=gcc-11.2.0
GCC_SOURCE_ARCHIVE=$GCC_NAME.tar.xz
wget https://ftp.gnu.org/gnu/gcc/gcc-11.2.0/$GCC_SOURCE_ARCHIVE

tar xfv $GCC_SOURCE_ARCHIVE
GCC_BUILD_DIR=gcc-build
mkdir $GCC_BUILD_DIR

pushd $GCC_BUILD_DIR
../$GCC_NAME/configure           \
  --target=x86_64-pc-solaris2.11 \
  --prefix=$SYSROOT              \
  --enable-languages=c           \
  --disable-bootstrap            \
  --disable-libssp               \
  --disable-libgomp              \
  --disable-libmudflap           \
  --disable-multilib             \
  --disable-decimal-float        \
  --disable-libffi               \
  --disable-libmudflap           \
  --disable-libquadmath          \
  --disable-libssp               \
  --disable-nls                  \
  --with-gnu-as                  \
  --with-gnu-ld                  \
  --with-sysroot=$SYSROOT
make -j4
make install
popd

At this point we should have in $SYSROOT/bin all of the compilers and utilities we need to build haskell. They will be named something like x86_64-pc-solaris2.11-gcc for example.

GHC

This is where it starts to get tricky as we have to patch the haskell runtime to support illumos. You can download my patches here, which are assumed to be applied with quilt.

git clone https://github.com/ghc/ghc

pushd ghc
git checkout ghc-8.10.7-release
git submodule init
git submodule update

./boot
./configure                      \
  --target=x86_64-pc-solaris2.11 \
  --prefix=$SYSROOT              \
  --disable-ld-override          \
  --with-intree-gmp

cp ../patches.tar.xz .
tar xf patches.tar.xz
quilt push -a

cp mk/build.mk.sample mk/build.mk
vim mk/build.mk

We want to uncomment the line #BuildFlavour = quick and add #HADDOCK_DOCS = NO underneath then save with :wq

make -j4
make install
make binary-dist

We now have a binary distribution of GHC!

Cabal

We want to be on our OmniOS machine for this part. We install GHC using our bindist from the last stage. We can install it by unpacking it, and in the unpacked folder, running:

./configure --prefix=/usr --disable-ld-override
pfexec make install

We now want to grab cabal and build it!

git clone https://github.com/haskell/cabal
cd cabal
git checkout Cabal-v3.6.2.0
./bootstrap/bootstrap.py -d bootstrap/linux-8.10.7.json -w /usr/bin/ghc

Uh oh! It failed! And sadly in a dependency that it downloaded!

Now run the bootstrap script again, but this time as soon as it says Configuring Network quickly press CTRL-z, to suspend the task in the terminal.

We then have to modify the network package before it starts building to fix it.

Configuring network-3.1.2.2...
^Z
[1]+  Stopped                 ./bootstrap/bootstrap.py -d bootstrap/linux-8.10.7.json -w /usr/bin/ghc
james@omnios:~/gits/cabal$ █

The first file we are going to change is one that marshells the posix c struct for a socket message to haskell

vim _build/unpacked/network-3.1.2.2/Network/Socket/Posix/MsgHdr.hsc

We are going to change the Storable instance in the file to:

instance Storable (MsgHdr sa) where
  sizeOf    _ = (#const sizeof(struct msghdr))
  alignment _ = alignment (0 :: CInt)

  peek p = do
    name       <- (#peek struct msghdr, msg_name)       p
    nameLen    <- (#peek struct msghdr, msg_namelen)    p
    iov        <- (#peek struct msghdr, msg_iov)        p
    iovLen     <- (#peek struct msghdr, msg_iovlen)     p
    return $ MsgHdr name nameLen iov iovLen nullPtr 0 0

  poke p mh = do
    -- We need to zero the msg_control, msg_controllen, and msg_flags
    -- fields, but they only exist on some platforms (e.g. not on
    -- Solaris).  Instead of using CPP, we zero the entire struct.
    zeroMemory p (#const sizeof(struct msghdr))
    (#poke struct msghdr, msg_name)       p (msgName       mh)
    (#poke struct msghdr, msg_namelen)    p (msgNameLen    mh)
    (#poke struct msghdr, msg_iov)        p (msgIov        mh)
    (#poke struct msghdr, msg_iovlen)     p (msgIovLen     mh)

Next, we are going to change a c file that seems to do something about message authentification… I don’t really know.

vim _build/unpacked/network-3.1.2.2/cbits/cmsg.c

Look down at lines 78-69. We want to change all of there returns to default values instead of function calls (null and 0).

struct cmsghdr *cmsg_firsthdr(struct msghdr *mhdr) {
  return NULL;
}

struct cmsghdr *cmsg_nxthdr(struct msghdr *mhdr, struct cmsghdr *cmsg) {
  return NULL;
}

unsigned char *cmsg_data(struct cmsghdr *cmsg) {
  return NULL;
}

int cmsg_space(int l) {
  return 0;
}

int cmsg_len(int l) {
  return 0;
}

I’m quite happy with the haskell code change, but I would be lying if I said I knew what I had done with the c change. If anyone has any idea, please get in contact so we can get feature parity and support for illumos distributions in the network package!

next we run the fg command, and our code should all built just fine! We have built cabal!

james@omnios:~/gits/cabal$ fg
./bootstrap/bootstrap.py -d bootstrap/linux-8.10.7.json -w /usr/local/bin/ghc
configure: WARNING: unrecognized options: --with-compiler, --with-hc-pkg
checking build system type... x86_64-pc-solaris2.11
checking host system type... x86_64-pc-solaris2.11
checking for gcc... /usr/bin/gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
...

Work to do

  • Fix the Network package. Already fixed in newer version
  • Get my RTS patch looked at by someone who understands the illumos threading library or haskell RTS.
  • Submit the RTS patch upstream
  • Add GHC and cabal to an IPS repository

Another thing to try is to look at passing -D_XOPEN_SOURCE=1 -D_XOPEN_SOURCE_EXTENDED=1 to gcc during the build of ghc and patching GHC to set it as a default compiler flag. This might get rid of our networking issues. If you want to have a try, check out this blog post where we did exactly this to build GHC with fPic by default on linux.