1# Linking psx and C code without cgo 2 3## Overview 4 5In some embedded situations, there is a desire to compile Go binaries 6to include some C code, but not `libc` etc. For a long time, I had 7assumed this was not possible, since using `cgo` *requires* `libc` and 8`libpthread` linkage. 9 10This _embedded compilation_ need was referenced in a [bug 11filed](https://bugzilla.kernel.org/show_bug.cgi?id=216610) against the 12[`"psx"`](https://pkg.go.dev/kernel.org/pub/linux/libs/security/libcap/psx) 13package. The bug-filer was seeking an alternative to `CGO_ENABLED=1` 14compilation _requiring_ the `cgo` variant of `psx` build. However, the 15go `"runtime"` package will always 16[`panic()`](https://cs.opensource.google/go/go/+/refs/tags/go1.19.2:src/runtime/os_linux.go;l=717-720) 17if you try this because it needs `libpthread` and `[g]libc` to work. 18 19In researching that bug report, however, I have learned there is a 20trick to combining a non-CGO built binary with compiled C code. I 21learned about it from a brief reference in the [Go Programming 22Language 23Wiki](https://zchee.github.io/golang-wiki/GcToolchainTricks/). 24 25This present directory evolved from my attempt to understand and 26hopefully resolve what was going on as reported in that bug into an 27example of this _trick_. I was unable to resolve the problem as 28reported because of the aformentioned `panic()` in the Go 29runtime. However, I was able to demonstrate embedding C code in a Go 30binary _without_ use of cgo. In such a binary, the Go-native version 31of `"psx"` is thus achievable. This is what the example in this 32present directory demonstrates. 33 34*Caveat Emptor*: this example is very fragile. The Go team only 35supports `cgo` linking against C. That being said, I'd certainly like 36to receive bug fixes, etc for this directory if you find you need to 37evolve it to make it work for your use case. 38 39## Content 40 41In this example we have: 42 43- Some C code for the functions `fib_init()` and `fib_next()` that 44combine to implement a _compute engine_ to determine [Fibonacci 45Numbers](https://en.wikipedia.org/wiki/Fibonacci_number). The source 46for this is in the sub directory `c/fib.c`. 47 48- Some Go code, in the directory `go/fibber` that uses this C compiled 49compute kernel. 50 51- `c/gcc.sh` which is a wrapper for `gcc` that adjusts the compilation 52to be digestible by Go's (internal) linker (the one that gets invoked 53when compiling `CGO_ENABLED=0`. Using `gcc` directly instead of this 54wrapper generates an incomplete binary - which miscomputes the 55expected answers. See the discussion below for what seems to be going 56on. 57 58- A top level `Makefile` to build it all. 59 60## Building and running the built binary 61 62Set things up with: 63``` 64$ git clone git://git.kernel.org/pub/scm/libs/libcap/libcap.git 65$ cd libcap 66$ make all 67$ cd contrib/bug216610 68$ make clean all 69``` 70When you run `./go/fib` it should generate the following output: 71``` 72$ ./go/fib 73psx syscall result: PID=<nnnnn> 74fib: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ... 75$ 76``` 77Where `<nnnnn>` is the PID of the program at runtime and will be 78different each time the program is invoked. 79 80## Discussion 81 82The Fibonacci detail of what is going on is mostly uninteresting. The 83reason for developing this example was to explore the build issues in 84the reported [Bug 85216610](https://bugzilla.kernel.org/show_bug.cgi?id=216610). Ultimately, 86this example offers an alternative path to building a `nocgo` program 87that links to compute kernel of C code. 88 89The reason we have added the `c/gcc.sh` wrapper for `gcc` is that 90we've found the Go linker has a hard time digesting the 91cross-sectional `%rip` based data addressing that various optimization 92modes of gcc like to use. Specifically, in the x86_64/amd64 93architecture, if a `R_X86_64_PC32` relocation entry made in a `.text` 94section refers to an `.rodata.cst8` section in a generated `.syso` 95file, the Go linker seems to [replace this reference with a `0` offset 96to 97`(%rip)`](https://github.com/golang/go/issues/24321#issuecomment-1296084103). What 98our wrapper script does is rewrite the generated assembly to store 99these data references to the `.text` section. The Go linker has no 100problem with this _same section_ relative addressing and is able to 101link the resulting objects without problems. 102 103If you want to cross compile, we have support for 32-bit arm 104compilation: what is needed for the Raspberry PI. To get this support, 105try: 106``` 107$ make clean all arms 108$ cd go 109$ GOARCH=arm CGO_ENABLED=0 go build 110``` 111The generated `fib` binary runs on a 32-bit Raspberry Pi. 112 113## Future thoughts 114 115At present, this example only works on Linux with `x86_64` and `arm` 116build architectures. (In go-speak that is `linux_amd64` and 117`linux_arm`). This is because I have only provided some bridging 118assembly for Go to C calling conventions for those architecture 119targets: `./go/fibber/fibs_linux_amd64.s` and 120`./go/fibber/fibs_linux_arm.s`. The non-native, `make arms`, cross 121compilation requires the `docker` command to be available. 122 123I intend to implement an `arm64` build, when I have a system on which 124to test it. 125 126**Note** The Fedora system on which I've been developing this has some 127 SELINUX impediment to naively using the `docker -v ...` bind mount 128 option. I need the `:z` suffix for bind mounting. I don't know how 129 common an issue this is. On Fedora, building the arm variants of the 130 .syso file can be performed as follows: 131``` 132$ docker run --rm -v $PWD/c:/shared:z -h debian -u $(id -u) -it expt shared/build.sh 133``` 134 135## Reporting bugs 136 137Please report issues or offer improvements to this example via the 138[Fully Capable `libcap`](https://sites.google.com/site/fullycapable/) 139website. 140