Porting the Hare language to DragonFly

The Hare programming language is a practical systems programming language with a focus on

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:

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:

The way Hare handles errors is really worth an extra article. Stay tuned! Over and out.

Porting harec - The bootstrap Compiler

--------------------------------------
blah blah blah

Porting harec - The bootstrap Compiler

--------------------------------------
blah blah blah