RISC-V Development on GNU Guix

Thanks to the hard work of the GNU Guix community we can cross-compile RISC-V software, run it in emulation and even push binary blobs to RISC-V hardware. In this BLOG we will:

Install GNU Guix

Install GNU Guix on any distro using the binary download. On Debian it is a simple `apt-get install guix`.

After running

mkdir -p ~/opt
guix pull -p ~/opt/guix-pull
source ~/opt/guix-pull/etc/profile

we should be on the latest guix. This takes a while because guix packages get compiled. Note it is possible to install an earlier version of GNU Guix by passing in the git hash of the guix repository. Make sure guix-daemon has a large enough tmpdir (10Gb I would recommend).

Compiling and running a package for RISC-V

After installing GNU Guix and running a `guix pull` we can install the latest package and cross-compile to RISC-V with a disconcertingly simple

guix build --target=riscv64-linux-gnu hello

This fetches the full build chain and installs

/gnu/store/fnsg59lb4c1gaqzbn553rdjcnam355gs-hello-2.10/bin/hello

Trying to run it will give `cannot execute binary file`. We'll need to use qemu to run a RISC-V binary. Let's install qemu in ~/opt/amd64-dev

guix package -i qemu -p ~/opt/amd64-dev

The `-p` switch creates of profile of symlinks so we can reach the tools easily. What is interesting is that qemu does not require a VM to run(!). We can now simply run:

 ~/opt/amd64-dev/bin/qemu-riscv64 /gnu/store/fnsg59lb4c1gaqzbn553rdjcnam355gs-hello-2.10/bin/hello
Hello, world!

Explore the 'hello' binary

To view the shared libraries included in a binary we can use the `ldd` tool, part of the gcc-toolchain.

guix build --target=riscv64-linux-gnu gcc-toolchain --keep-going

this may take a while if not all binary substitutes are available (to get a list use the `--dry-run` switch. RISC-V is not much used yet, so this is likely the case. With more users we will get better coverage. The good news is that builds are in parallel (see -c and -M switches) and only happen once. And because it is cross-compilation, it will build relatively fast.

While trying above I found that gcc-toolchain is currently not building. WIP.

Another way to test for dependencies is to invoke

guix gc --references /gnu/store/mxiglsphimzg27ibyfgjfvy6gwrx6grd-hello-2.12
/gnu/store/1wijzqplqypcvlgmsnczj530ciq8ny8r-gcc-cross-riscv64-linux-gnu-10.3.0-lib
/gnu/store/bzk4a5ip4mmprbw3f2af8l52zshc9dwl-glibc-cross-riscv64-linux-gnu-2.33
/gnu/store/mxiglsphimzg27ibyfgjfvy6gwrx6grd-hello-2.12

The AMD64 version shows that the binary is linked against the following shared libs:

ldd /gnu/store/khaaib6s836bk5kbik239hlk6n6ianc4-hello-2.11/bin/hello
        linux-vdso.so.1 (0x00007ffdff9b6000)
        libgcc_s.so.1 => /gnu/store/094bbaq6glba86h1d4cj16xhdi6fk2jl-gcc-10.3.0-lib/lib/libgcc_s.so.1 (0x00007f2571d1b000)
        libc.so.6 => /gnu/store/5h2w4qi9hk1qzzgi1w83220ydslinr4s-glibc-2.33/lib/libc.so.6 (0x00007f2571b59000)
        /gnu/store/5h2w4qi9hk1qzzgi1w83220ydslinr4s-glibc-2.33/lib/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007f2571d37000)

To build a static binary we need to modify the build script. I lifted the definition from guix packages and made the following guix.scm file:

(use-modules
 (guix download)
 (guix packages)
 (guix build-system gnu)
 (guix licenses))

(define-public hello
  (package
    (name "hello")
    (version "2.11")
    (source (origin
              (method url-fetch)
              (uri (string-append "mirror://gnu/hello/hello-" version
                                  ".tar.gz"))
              (sha256
               (base32
                "1g84a3hqs4pgx3yzs99cysv3iq440ncdw77bf03fsi1w5mby174c"))))
    (build-system gnu-build-system)
    (arguments
     `(#:make-flags '("CFLAGS=-static")))
    (synopsis "Hello, GNU world: An example GNU package")
    (description
     "GNU Hello prints the message \"Hello, world!\" and then exits.  It
serves as an example of standard GNU coding practices.  As such, it supports
command-line arguments, multiple languages, and so on.")
    (home-page "https://www.gnu.org/software/hello/")
    (license gpl3+)))

hello

The only addition is

    (arguments
     `(#:make-flags '("CFLAGS=-static")))

This can be compiled with

guix build -f guix.scm

resulting in

/gnu/store/0nfr261c948mdfdj6pm7yffgws98kvi3-hello-2.11/bin/hello
Hello, world!
ldd /gnu/store/0nfr261c948mdfdj6pm7yffgws98kvi3-hello-2.11/bin/hello
        not a dynamic executable

Now this works, lets do the risc64 version:

guix build --target=riscv64-linux-gnu -f ../guix.scm
/gnu/store/hk6l0hxq5fga9kcj2z65pxd93l841ws7-hello-2.11
 ldd /gnu/store/hk6l0hxq5fga9kcj2z65pxd93l841ws7-hello-2.11/bin/hello
        not a dynamic executable
 ~/opt/amd64-dev/bin/qemu-riscv64 /gnu/store/hk6l0hxq5fga9kcj2z65pxd93l841ws7-hello-2.11/bin/hello
Hello, world!

Success!

Let's try running hello on its own by copying the static binary and

deleting the installed package from the store

cp /gnu/store/hk6l0hxq5fga9kcj2z65pxd93l841ws7-hello-2.11/bin/hello hello-riscv64
guix gc -D /gnu/store/hk6l0hxq5fga9kcj2z65pxd93l841ws7-hello-2.11

and run standalone

~/opt/amd64-dev/bin/qemu-riscv64 hello-riscv64
Hello, world!

Discussion

At this point we can take a GNU Guix package and cross compile that directly to RISC-V 64-bits with a standard toolchain. We can turn that into a statically linked binary that can be deployed anywhere.

In the near future we hope to show a native development environment running in a RISC-V container or virtual machine.

We thank the excellent free software GNU Mes and GNU Guix projects that make this work possible. We thank UTHSC and the NSF for funding RISC-V development for pangenomics and the NLNet foundation that is funding the GNU Mes bootstrapping effort for RISC-V. Efforts are underway to migrate all important GNU Guix packages to RISC-V, including for Rust development.