xref: /aosp_15_r20/external/libcap/contrib/bug216610/README.md (revision 2810ac1b38eead2603277920c78344c84ddf3aff)
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