Porting the Hare language to DragonFly
The Hare programming language is a practical systems programming language with a focus on
-
fitting on one floppy disk (that’s 1.44 MiB),
-
being around for the next 100 years,
-
being “hackable” and simple enough to be understood by a single person without holding a PhD in formal systems or type theory,
-
while at the same time being elegant and readable.
At least that’s my basic understanding of Hare’s goals. And now, tada, it’s also available for DragonFly, the underdog among the BSD operating systems.
Porting Hare to DragonFly was straightforward, especially compared to how much effort and time it took me to port Rust to DragonFly.
Further details about the porting process can be found in the following sections.
Porting the Bootstrap Compiler - harec
The first step was to port harec. This is the Hare bootstrap compiler written
in ANSI C. You can obtain it from this location:
https://git.sr.ht/~sircmpwn/harec
The commit that adds support for DragonFly is the following:
https://git.sr.ht/~sircmpwn/harec/commit/8d848f258a7edbbb48e5890cc24507d40b243417
As the compiler is written in portable ANSI C, only the Makefile’s had to be
slightly adapted. A bit more effort was to port the minimal runtime called
“rt”. This library provides the interface to the DragonFly kernel via system
calls, so basically it is a replacement for libc. It is a mix of Hare and
assembly language and is kept to a minimum, just enough to be able to compile
the “real” Hare compiler, which is written in Hare itself.
The “rt” code for DragonFly is mostly a copy of the code for FreeBSD. The only
exception, apart from a few syscalls having different numbers, is that
DragonFly’s mmap system call takes 7 arguments, while it is just 6 arguments
for all the other operating systems. And also the pipe2 system call is
slightly different in that the DragonFly kernel does not touch the filedes[2]
array, instead it returns the two file descriptors in registers eax and edx.
The fact that mmap takes 7 arguments on DragonFly made it a bit more tricky.
The x86-64 System V calling convention only allows passing 6 arguments in
registers, so the additional 7th argument has to be passed via the stack. For
this, I first had to obtain a basic understanding of the System V system call
and function call conventions. Basically, you only need to know which argument
is passed in which register and which register is caller saved and which is
callee saved. Then you are good to go. Doing the register assignment or
“shuffling” in your head can be tricky, so I recommend writing things down on a
piece of paper. After all, it was quite easy and such a welcome change to get
my hands dirty with assembly code again.
At a much younger age, some 25 years ago, I even spoke machine language, which
I taught myself while writing a disassembler, if my mind doesn’t trick me. So
at that time, I really could type in the hex codes for 32-bit Intel ISA
directly into an editor written in assembly, which would then execute the
machine code directly from memory. Hell, that was fun and it really made me
appreciate assembly language! 0xcd 0x90 anyone?
Once I got harec working, it was time for me to start porting the “real” Hare
compiler, hare.
Porting “The Real Thing” - hare
(If you are old enough and from Europe you might remember “The Real Thing” which “makes your body sweat” from 2 Unlimited).
This is the Hare compiler written in Hare itself and also includes a full blown
standard library and some other tools such as haredoc, the documentation
generator.
At this point, I really want to take the chance and express my thankfulness to the Hare community, which maintains both the C and the Hare version of the compiler in parallel and keeps both versions up to date. This makes bootstrapping so much easier.
The hare compiler and it’s standard library are available here:
https://git.sr.ht/~sircmpwn/hare
Porting hare to DragonFly was more effort, mainly because the runtime library
“rt” and the other parts of the standard library provide you with a complete
“libc” alternative out of the box without depending on libc. This, by the way,
is the same approach other languages like Go or Zig take. Luckily, it wasn’t
the first time I have done things like that - for instance I originally ported
Rust and it’s libc to DragonFly and also did the port of the Crystal
language.
The porting was an iterative process, heavily supported by the test-suite and
based on the existing code for FreeBSD. I picked the code for FreeBSD as
starting point mainly because DragonFly has it’s roots in FreeBSD, and even 20
years later, both operating systems still share most of it’s system calls and
data structures. Same same, but different. Most porting work was done by hand,
carefully reviewed by my eyes and supported by some small Ruby scripts to parse
#define‘s and translate them into Hare code.
The commit that brings in support for DragonFly is the following one:
https://git.sr.ht/~sircmpwn/hare/commit/e69fd862122c95486a87d05bba66e5856b2693d8
Once I submitted the patch to the hare-dev mailing list, the first question I’ve been asked was “did you use an LLM to create this patch” to which I responded with “nope, or should I?”. Thankfully, this was replied with “no please not :D”.
No further comment needed.
Future Work
The commit message above mentions a few open tasks:
-
DragonFly does not yet ship with a
/usr/share/zoneinfo/leap-seconds.listfile which is required fortime::chronosupport. It can be obtained from NetBSD in the meanwhile -
DragonFly also does not ship with
/etc/mime.typesby default. But you canpkg install mime-supportwhich gives you/usr/local/etc/mime.types -
There is also no CI support for DragonFly on sourcehut yet
So the next big step is to add support for DragonFly to the CI system of
sourcehut. This would give us immediate feedback on every git pull. This not
just helps anybody who wants to use Hare on DragonFly, but also is of great
help to the other Hare developers, who are kind enough to care not to
accidentially break DragonFly support while adding new functionality to
Hare.
Summary
It was a pleasant experience to port Hare to DragonFly. Hare and it’s implementation stayed true to its goals and my expectations of being easy to work with and “hackable”. In that regard, I really love the language and it’s approach.
Let me close this article with the unspectacular Hello world example written in Hare:
use fmt;
export fn main() void = {
fmt::printfln("Hello {}", "world")!;
};
You can run the example either directly via hare run hw.ha or by first
compiling it with hare build hw.ha and then by running the executable hw. Without
going too much into detail here, but if you look closely at the above example you might
notice three characteristics:
- It uses some kind of module system, similar to Go.
-
It does not silently hide errors. Did you notice the exclamation mark
!? -
It consistently terminates every statement with a semicolon
;. This likely simplifies the parser.
The way Hare handles errors is really worth an extra article. Stay tuned! Over and out.