crossdev and GNU Hurd
Tl;DR: crossdev
is a tool to generate a cross-compiler for you in
gentoo
and with some hacks (see below) you can even cross-compile to
hurd
!
FOSDEM
2018 conference happened recently and a lot of cool talks
tool place there. The full list counts 689
events!
Hurd’s PCI
arbiter
was a nice one. I never actually tried hurd
before and thought to
give it a try in a VM.
Debian
already provides full hurd
installer (installation
manual) and I picked
it. hurd
works surprisingly well for a such an understaffed project!
Installation process is very simple: it’s a typical debian
CD
which
asks you for a few details about final system (same as for linux
)
and you get your OS
booted.
hurd
has a ton of debian
software already built and working
(like 80% of the whole repo). Even GHC
is ported there. While at it
I grabbed all the tiny GHC
patches related to hurd
from Debian
and pushed them upstream:
Now plain ./configure && make && make test
just works.
Hurd supports only 32-bit x86
CPUs and does not support SMP
(only one
CPU is available). That makes building heavyweight stuff (like GHC
)
in a virtual machine a slow process.
To speed things up a bit I decided to build a cross-compiler from
gentoo linux
to hurd
with the help of crossdev
. What does it
take to support bootstrap like that? Let’s see!
To get the idea of how to cross-compile to another OS
let’s check how
typical linux
to linux
case looks like.
Normally aiming gcc
at another linux-glibc
target takes the
following steps:
- install cross-binutils
- install system headers (kernel headers and glibc headers):
- install minimal gcc without glibc support (not able to link final executables yet)
- install complete glibc (gcc will need crt.o files)
- install full gcc (able to link final binaries for C and C++)
In gentoo
crossdev
does all the above automatically by running
emerge
a few times for you. I wrote a more up-to-date crossdev
README to
describe a few details of what is happening when you run crossdev -t <target>
.
hurd-glibc
is not fundamentally different from linux-glibc
case.
Only a few packages need to change their names, namely:
- install cross-binutils
- install gnumach-headers (kernel headers part 1)
- [NEW] install cross-mig tool (Mach Interface Generator, a flavour of IDL compiler)
- install hurd-headers and glibc-headers (kernel headers part 2 and libc headers)
- install minimal gcc without libc support (not able to link final executables yet)
- install complete libc (gcc will need crt.o files)
- install full gcc (able to link final binaries for C and C++)
The only change from linux
is the cross-mig
tool. I’ve
collected ebuild
s needed in gentoo-hurd
overlay.
Here is how one gets hurd
cross-compiler
with today’s
crossdev-99999999
:
git clone https://github.com/trofi/gentoo-hurd.git
HURD_OVERLAY=$(pwd)/gentoo-hurd
CROSS_OVERLAY=$(portageq get_repo_path / crossdev)
TARGET_TUPLE=i586-pc-gnu
# this will fail around glibc, it's ok we'll take on manually from there
crossdev --l 9999 -t crossdev -t ${TARGET_TUPLE}
ln -s "${HURD_OVERLAY}"/sys-kernel/gnumach ${CROSS_OVERLAY}/cross-${TARGET_TUPLE}/
ln -s "${HURD_OVERLAY}"/dev-util/mig ${CROSS_OVERLAY}/cross-${TARGET_TUPLE}/
ln -s "${HURD_OVERLAY}"/sys-kernel/hurd ${CROSS_OVERLAY}/cross-${TARGET_TUPLE}/
emerge -C cross-${TARGET_TUPLE}/linux-headers
ACCEPT_KEYWORDS='**' USE=headers-only emerge -v1 cross-${TARGET_TUPLE}/gnumach
ACCEPT_KEYWORDS='**' USE=headers-only emerge -v1 cross-${TARGET_TUPLE}/mig
ACCEPT_KEYWORDS='**' USE=headers-only emerge -v1 cross-${TARGET_TUPLE}/hurd
ACCEPT_KEYWORDS='**' USE=headers-only emerge -v1 cross-${TARGET_TUPLE}/glibc
USE="-*" emerge -v1 cross-${TARGET_TUPLE}/gcc
ACCEPT_KEYWORDS='**' USE=-headers-only emerge -v1 cross-${TARGET_TUPLE}/glibc
USE="-sanitize" emerge -v1 cross-${TARGET_TUPLE}/gcc
Done!
A few things to note here:
gentoo-hurd
overlay is used for newgnumach
,mig
andhurd
packages- symlinks to new packages are created manually (
crossdev
fix pending, wait on packages to get into::gentoo
) - uninstall
linux-headers
as our target is not linux (crossdev
fix pending) - use
ACCEPT_KEYWORDS='**'
for many packages (need to decide on final keywords, maybex86-hurd
) - all
crossdev
phases are ran manually - only
glibc
git
version is working as changes were merged upstream very recently. USE="sanitize"
is disabled in finalgcc
because it’s broken forhurd
Now you can go to /usr/${TARGET_TUPLE}/etc/portage/
and tweak the
defaults for ELIBC
, KERNEL
and other things.
Basic sanity check for a toolchain:
$ i586-pc-gnu-gcc main.c -o main
$ file main
main: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld.so, for GNU/Hurd 0.0.0, with debug_info, not stripped
Copying to the target hurd
VM and running there also works as
expected.
I use crossdev -t x86_64-HEAD-linux-gnu
to have GHC
built
against HEAD
in parallel to system’s GHC
. Let’s use that for
more heavyweight test to build a GHC
cross-compiler to hurd
:
$ EXTRA_ECONF=--with-ghc=x86_64-HEAD-linux-gnu-ghc emerge -v1 cross-i586-pc-gnu/ghc --quiet-build=n
This fails as:
rts/posix/Signals.c:398:28: error:
note: each undeclared identifier is reported only once for each function it appears in
|
398 | action.sa_flags |= SA_SIGINFO;
| ^
Which hints at lack of SA_SIGINFO
support in upstream glibc.git
.
Debian
has an out-of-tree tg-hurdsig-SA_SIGINFO.diff
patch
to provide these defines (as least it’s not our local toolchain
breakage). The outcome is positive: we have got very far into
cross-compiling and hit real portability issues. Woohoo!
Final words
As long as underlying toolchains are not too complicated building
cross-compilers in gentoo
is trivial. Next tiny step is to
cross-build hurd
kernel itself and run it in qemu
. ebuild
s in
gentoo-hurd
are not yet ready for it but tweaking them should be
easy.
Have fun!