Cross-compiling Go for Raspberry Pi

chrischdi
4 min readNov 25, 2018

--

This post is about how to cross-compile Go applications for other architectures. Cross-compiling Go applications could get quiet challenging when we have dependencies to modules which depend on cgo and on C-libraries.

Update: I’ve added a section at the end.

Introduction

I’m currently writing a small Go application which has a gui implemented by using github.com/gotk3/gotk3 as library for gtk bindings. Compiling on a Raspberry Pi is quiet slow compared to my laptop, which is already running for some years.

Why don’t I just cross-compile the application on my laptop

Yes cross-compiling could finally speed up my development workflow and is quiet easy for Go when there are no dependencies to cgo or even libraries used by cgo.

I’d like to share the different ways how to cross-compile Go for the Raspberry Pi.

Compiling without cgo

Cross-compiling Go-applications for other architectures like aarch64 is quiet easy. Consider the following simple hello-world Go application:

hello-world Go application

You can easily cross-compile this for a Raspberry Pi and run it there:

$ env GOARCH=arm64 GOOS=linux go build -o hello-world main.go
$ scp hello-world rpi:~/hello-world
$ ssh rpi ./hello-world
hello world

As long as we are writing applications which do not need cgo this works very well.

Compiling with cgo

Let’s try a small example including cgo::

Let’s try to build it for the Raspberry Pi the same way we did without cgo:

$ env GOARCH=arm64 GOOS=linux CGO_ENABLED=1 go build -o cgo main.go
# runtime/cgo
gcc: error: unrecognized command line option ‘-marm’; did you mean ‘-mabm’?

This fails because go tries to use the default gcc compiler of our operating system. But in this case we need to use a compiler which builds for arm. On archlinux we can install aarch64-linux-gnu-gcc and also pass the name of the compiler to the go build-command:

$ export CC=aarch64-linux-gnu-gcc
$ GOARCH=arm64 GOOS=linux CGO_ENABLED=1 go build -o cgo main.go
$ scp hello-world rpi:~/hello-world
$ ssh rpi ./cgo
hello world cgo

Adding libraries for cgo

As I noted at the beginning, I want to use github.com/gotk3/gotk3.

So let’s implement a small user interface and cross-compile it for the Raspberry Pi:

$ export CC=aarch64-linux-gnu-gcc
$ env GOARCH=arm64 GOOS=linux CGO_ENABLED=1 go build -o cgo main.go
# github.com/gotk3/gotk3/glib
/usr/lib/gcc/aarch64-linux-gnu/8.2.0/../../../../aarch64-linux-gnu/bin/ld: cannot find -lgio-2.0
/usr/lib/gcc/aarch64-linux-gnu/8.2.0/../../../../aarch64-linux-gnu/bin/ld: cannot find -lgobject-2.0
/usr/lib/gcc/aarch64-linux-gnu/8.2.0/../../../../aarch64-linux-gnu/bin/ld: cannot find -lglib-2.0
collect2: error: ld returned 1 exit status

So what went wrong here? Let’s assume the necessary libraries are installed on the x86 system we’re working on. The problem is that these are not for the cross-compile toolchain.

We also have a Raspberry Pi running the aarch64 architecture and having the libraries installed. Let’s mount the file system of the sdcard which is normally used by the Raspberry Pi and pass the path to the go compiler:

$ mount /dev/sdc2 /mnt
$ export CC=aarch64-linux-gnu-gcc
$ export CGO_CFLAGS="--sysroot=/mnt"
$ export CGO_LDFLAGS="--sysroot=/mnt"
$ env GOARCH=arm64 GOOS=linux CGO_ENABLED=1 go build -o cgo main.go

That’s it.

Of course we could copy the content of the Raspberry Pi’s file system somewhere on our disk so we don’t have to mount the sdcard all the time and keep the Raspberry Pi running while we are cross-compiling.

cgo & pkg-build

Note: this section was added later on

After writing this blog post and compiling for aarch64 I recognized that the GPIO support on the Raspberry Pi at aarch64 does not work well. Afterwards I tried to cross-compile my application for armv7 .

Long story short: I had to learn that Go is using its own pkg-config (see docs) to gather build dependency information.

To cross-compile my application for armv7 I have used the precompiled toolchain from archlinuxARM and added the environment variables PKG_CONFIG_DIR, PKG_CONFIG_LIBDIR and PKG_CONFIG_SYSROOT_DIR.

Now I’m compiling successfully for armv7 using the following command:

$ mount /dev/sdc2 /mnt
$ export CC=aarch64-linux-gnu-gcc
$ export CGO_CFLAGS="--sysroot=/mnt"
$ export CGO_LDFLAGS="--sysroot=/mnt"
$ export CC="path/to/toolchain/arm-unknown-linux-gnueabihf/bin/arm-unknown-linux-gnueabihf-gcc
$ env \
PKG_CONFIG_DIR="" \
PKG_CONFIG_LIBDIR="/mnt/usr/lib/pkgconfig:/mnt/usr/share/pkgconfig"\
PKG_CONFIG_SYSROOT_DIR="/mnt" \
GOARCH=arm \
GOARM=7 \
GOOS=linux \
CGO_ENABLED=1 \
go build -o app ./

Addition

Archlinux directly offers the aarch64-linux-gnu-gcc as a package but this is not the case for the armv7 or other architectures and I don’t know whether or not the compilers are easily available on other distributions. crosstool-ng is able to create cross-compiling toolchains for different architectures. There are prebuilt cross-compilers available at the Cross-Compiling documentation of the Archlinux ARM website and they also explain how to build your own cross-compiler using crosstool-ng.

--

--

chrischdi

Developing, building and maintaining #kubernetes clusters since k8s v1.6! Working for @DaimlerTSS