Compiling non-OCaml programs using MirageOS possible?


#1

Hello everyone,

I’m just getting started with Unikernels and systems engineering in general. I found it to be a very helpful and friendly community and I’m really exited because of the potential and impact Unikernels how we think about cloud infrastructure.
I really like the things I read about MirageOS, especially the Jitsu paper was particularly interesting.

Now to my question: Have there been experiments on making MirageOS able to compile non-OCaml code into a unikernel? I know there is the Rumprun kernel which allows applications to be written in C, C++, Erlang, Go, Javascript, … but I’m curious whether there were discussions on making MirageOS usable with applications written in other languages?


#2

Absolutely – we actually use MirageOS libraries in this way inside Docker for Mac and Windows (as an embedded piece of a bigger non-OCaml application).

The specifics are somewhat at the vagaries of the build system of our individual libraries however, and this is something that’s being actively improved. Some notes in no particular order:

  • The Ctypes makes it easier to separate the description of the foreign C code to run from the linking mechanism. This is key to retaining flexibility in how we link code, without having to rewrite all the foreign function bindings. So if you do build a foreign interface, we highly recommend the use of Ctypes to start new code with.
  • The Solo5 project has given us two alternative hypervisor backends in Mirage (Xen and KVM). This means that the way we build C libraries is changing to be more general and easier to integrate. Dan Williams and Martin Lucina are working on this and should have an update soon.

Ctypes is so powerful as a linking library because it not only lets you link to C libraries, but it also lets OCaml code expose a C interface that can be called from C code. This is known as “inverted stubs”, and forms the basis for how MirageOS can act as a kernel of sorts by exposing a C ABI for other code to run.

Now, the question is: what should these C APIs that Mirage exposes look like? Should they be boring old POSIX, which Mirage can only do an imperfect job at emulating (quirks, bugs and all), or should be the cleanest C interfaces that we can come up. One experiment we’re working on is inverted tls, where we expose the OpenBSD libtls API that is a significantly saner version of the TLS APIs than OpenSSL’s.

What sort of non-OCaml programs did you have in mind when you raised the question?


#3

Thanks for the very insightful reply,
I was curious whether I could compile a Go program into a Mirage Unikernel, for example consider this very simple webserver below. I see how this approach works for linking to C libraries but I’m not sure how to go forward when using Go. Would I need a Ctypes equivalent (foreign function interface) written for go in this case?

server.go

package main

import (
    "fmt"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

On that note and for future reference, this article also talks about ocaml-ctypes in MirageOS:
https://mirage.io/blog/modular-foreign-function-bindings


#4

Go is unfortunately slightly problematic, since it doesn’t use the C ABI to interact with the system – it instead directly generates syscalls using the OS ABI. Ctypes works at the C level, and so requires going through a level of indirection via Cgo to work.

I have heard rumours of a C ABI syscall backend for Go that would make this easier, but haven’t investigated. Also worth investigating is how the Rumprun port that powers DeferPanic works, to see if we can reuse some of their library infrastructure…


#5

Yeh, @avsm is correct - this is what makes Go such an oddball in the unikernel world. It’s honestly a great language design except for when you want to port to a different system.

We’ve ported rumprun but our changes aren’t substantial - most of our code deals with orchestration and tooling.

The actual fork is Go - if you want to compile your own programs locally https://github.com/deferpanic/gorump contains the actual Go fork.

There are lots of great unikernel/Go fits but there are also definite contradictions such as virtual memory.

You mentioned simple but we got NATS running for a friend the other weekend simply by adding a missing GOOS file - https://www.youtube.com/watch?v=WALE6IgXDHk .

I think it’d be rather easy to support a new OS target - while there’s a lot that looks like it was changed the vast majority are copies to ensure compilation cause of the way the build tags in Go work.


#6

How feasible would it be to integrate the Node.js JavaScript runtime / V8 Engine into Mirage? I’ve read a blog post about JavaScript as a backend for MirageOS but how does relate to the ability to run JavaScript in a unikernel running on Xen? Is

I’m imagining a PoC where I have a MirageOS unikernel where I can pass in a js program as a string via xen boot parameters and it is eval()'ed and the unikernel shuts down.