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