Cross compiling Golang apps for RISC-V with cgo
On my adventures in playing with RISC-V (see Emulating RISC-V, and IPFS on RISC-V), I hit on a few issues.
I was struggling to find a guide online on how to cross compile Go code on RISC-V, when cgo is involved. Here is a quick guide and trouble shooting tips to help others.
Important: the riscv64
architecture was not added until Go version 1.15.x
, you will need to make sure you are running a recent release.
Here is the Github issue that tracked the implementation https://github.com/golang/go/issues/36641
Setting up and compiling
# Install standard GCC for your system
sudo apt install build-essential
# Add the RISC-V GCC package
sudo apt install g++-riscv64-linux-gnu gcc-riscv64-linux-gnu
# Tell golang what the compilation target is
# Importantly, set the CC (Cross Compiler) to use the riscv64 version of GCC, instead of our system's default
export GOOS=linux
export GOARCH=riscv64
export CGO_ENABLED=1
export CC=riscv64-linux-gnu-gcc
Now files will compile when cross compiling for RISC-V
# Code sample from issue https://github.com/golang/go/issues/36641#issuecomment-630204733
# Create a sample app
cat > cgo.go << EOF
package main
import "runtime"
/*
#include <stdio.h>
void hello(char *goos, char *goarch) {
printf("Hi from cgo on %s/%s!\n", goos, goarch);
}
*/
import "C"
func main() {
goos := C.CString(runtime.GOOS)
goarch := C.CString(runtime.GOARCH)
C.hello(goos, goarch)
}
EOF
# build it
go build ./cgo.go
Troubleshooting
Verify environment settings
When you run go env
you should see this similar output.
$ go env
GOOS="linux"
GOARCH="riscv64"
CGO_ENABLED="1"
CC="riscv64-linux-gnu-gcc"
...
Force env settings for a single go build
You can preface the go command with the environment settings you want, if you cannot export
env GOOS=linux GOARCH=riscv64 CGO_ENABLED=1 CC=riscv64-linux-gnu-gcc go build
Older version of Go
If you see something similar to this, then you have an older version of golang.
Solution: Install a version later than 1.15.x
$ go build ./cgo.go
/usr/bin/ld: $WORK/b009/_x008.o: in function `x_cgo_thread_start':
/usr/lib/go-1.15/src/runtime/cgo/gcc_util.c:23: undefined reference to `_cgo_sys_thread_start'
collect2: error: ld returned 1 exit status
Cross compiler not set
This is what got me stuck for quite a while. This happens when CC
is still set to your system default gcc.
Solution: set export CC=riscv64-linux-gnu-gcc
$ go build ./cgo.go
gcc_riscv64.S: Assembler messages:
gcc_riscv64.S:15: Error: no such instruction: `sd x1,-200(sp)'
gcc_riscv64.S:16: Error: no such instruction: `addi sp,sp,-200'
gcc_riscv64.S:17: Error: no such instruction: `sd x8,8(sp)'
gcc_riscv64.S:18: Error: no such instruction: `sd x9,16(sp)'
gcc_riscv64.S:19: Error: no such instruction: `sd x18,24(sp)'
...
gcc_riscv64.S:29: Error: no such instruction: `fsd f8,104(sp)'
gcc_riscv64.S:30: Error: no such instruction: `fsd f9,112(sp)'
gcc_riscv64.S:31: Error: no such instruction: `fsd f18,120(sp)'
...
gcc_riscv64.S:43: Error: no such instruction: `mv s1,a0'
gcc_riscv64.S:44: Error: no such instruction: `mv s0,a1'
gcc_riscv64.S:45: Error: no such instruction: `mv a0,a2'
gcc_riscv64.S:46: Error: no such instruction: `jalr ra,s0'
gcc_riscv64.S:47: Error: no such instruction: `jalr ra,s1'
gcc_riscv64.S:49: Error: no such instruction: `ld x1,0(sp)'
gcc_riscv64.S:50: Error: no such instruction: `ld x8,8(sp)'
...
gcc_riscv64.S:62: Error: too many memory references for `fld'
gcc_riscv64.S:63: Error: too many memory references for `fld'
gcc_riscv64.S:64: Error: too many memory references for `fld'
...
gcc_riscv64.S:74: Error: no such instruction: `addi sp,sp,200'
gcc_riscv64.S:76: Error: no such instruction: `jr ra'
libc6 not installed
/lib/ld-linux-riscv64-lp64d.so.1: No such file or directory
Solution:
Unfortunately I could not figure out a clean way to do this. Apparently you can set export CGO_LDFLAGS="-L/path/to/the/lib
but that did not work for me. So in the short term, copying them into the /lib
folder works after installing. http://www.gridengine.eu/index.php/other-stories/232-avoiding-the-ldlibrarypath-with-shared-libs-in-go-cgo-applications-2015-12-21
- Install the libc6 libraries
apt install libc6-dbg-riscv64-cross
- Give CGO access to the libraries
Copy them into your lib foldercp /usr/riscv64-linux-gnu/lib/*.* /lib