A unikernel with dune
How to build an unikernel with dune
This next step requires you to be familiar with dune, one of OCaml's build
systems. You can find out more on its website or in its
documentation. The official OCaml website also documents
how to use it.
For more experienced OCaml users, dune isn't strictly necessary for compiling
a unikernel. As we've seen previously, ocamlfind may suffice, and there are
workarounds involving ocamlbuild. However, dune is currently the build
system most widely used by the OCaml community, and our unikernels are built
using it. In short, in this section we will learn how to use dune to build
our unikernels.
Required files
As with any project intended to be built using dune, we need to create a
dune-project file:
(lang dune 3.0)
Next, to enable cross-compilation and compile our unikernel using our
ocaml-solo5 toolchain, we also need to define the dune-workspace file:
(lang dune 3.0)
(context (default))
(context (default
(name solo5)
(host default)
(toolchain solo5)
(disable_dynamically_linked_foreign_archives true)))
In this file, we define a "context" which is added to the default context. This means that our unikernel will be compiled in both the default context and the Solo5 context. We can now define our dune file:
(executable
(name main)
(modules main)Don't forget to add the flag that selects our Solo5 ABI.
(link_flags :standard -cclib "-z solo5-abi=hvt") (libraries fmt)
(foreign_stubs
(language c)Finally, we must always include our manifest.o and our startup.o, as seen earlier.
(names startup manifest)))
(rule
(targets manifest.c)
(enabled_if
(= %{context_name} "default"))
(action
(write-file manifest.c "")))We can use dune to generate the manifest.c file on the fly so that we
only keep the manifest.json file.
(rule
(targets manifest.c)
(deps manifest.json)
(enabled_if
(= %{context_name} "solo5"))
(action
(run solo5-elftool gen-manifest manifest.json manifest.c)))Our unikernel with dune
So all we need to do is write our main.ml file and our manifest.json
file (an example can be found here). And then build our unikernel
using dune build:
let () = Fmt.pr "Hello World!\n%!"
$ dune build
$ solo5-hvt _build/solo5/main.exe
| ___|
__| _ \ | _ \ __ \
\__ \ ( | | ( | ) |
____/\___/ _|\___/____/
Solo5: Bindings version v0.12.0
Solo5: Memory map: 512 MB addressable:
Solo5: reserved @ (0x0 - 0xfffff)
Solo5: text @ (0x100000 - 0x1b9fff)
Solo5: rodata @ (0x1ba000 - 0x1e8fff)
Solo5: data @ (0x1e9000 - 0x441fff)
Solo5: heap >= 0x442000 < stack < 0x20000000
Hello World!
Solo5: solo5_exit(0) called
There you go! Your first unikernel in OCaml using dune. As you can see, the
artefact is located in the _build/solo5 folder, which contains all the
targets compiled in the ocaml-solo5 context. Later on, we'll look at how we
can both build our unikernel and build executables in the default context, and
use them in the construction of our unikernel.