aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew G. Morgan <morgan@kernel.org>2022-10-23 15:15:30 -0700
committerAndrew G. Morgan <morgan@kernel.org>2022-10-23 15:28:58 -0700
commit08d48b659aa59d2a5acd9cd13f640f6497718796 (patch)
tree3f80bf6bf6b929daf7d6aa1752738d4df08d782d
parent70998415a87587f31063a26a1e52c6f7806b7834 (diff)
downloadlibcap-08d48b659aa59d2a5acd9cd13f640f6497718796.tar.gz
Add an example of combining Go, C code and "psx" without cgo.
This example was developed while investigating the issues discussed in: https://bugzilla.kernel.org/show_bug.cgi?id=216610 At this time, it is not possible to build CGO_ENABLED=1 and include the "psx" package without using its "cgo"-tagged build variant. This example provides a worked example of doing the opposite: link a CGO_ENABLED=0 binary with "psx", including some compiled C code. Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
-rw-r--r--contrib/bug216610/Makefile23
-rw-r--r--contrib/bug216610/README.md98
-rw-r--r--contrib/bug216610/c/fib.c20
-rw-r--r--contrib/bug216610/go/.gitignore3
-rw-r--r--contrib/bug216610/go/go.mod3
-rw-r--r--contrib/bug216610/go/main.go28
-rw-r--r--contrib/bug216610/go/vendor/fibber/fib.go26
-rw-r--r--contrib/bug216610/go/vendor/fibber/fibs_linux_amd64.s57
8 files changed, 258 insertions, 0 deletions
diff --git a/contrib/bug216610/Makefile b/contrib/bug216610/Makefile
new file mode 100644
index 0000000..c83284c
--- /dev/null
+++ b/contrib/bug216610/Makefile
@@ -0,0 +1,23 @@
+topdir=$(shell pwd)/../..
+include ../../Make.Rules
+
+GOTARGET=$(shell eval $$(go env) ; echo $${GOHOSTOS}_$${GOARCH})
+
+all: go/fib
+
+go/fib: go/main.go go/vendor/fibber/fib.syso go/vendor/fibber/fib.go go/vendor/fibber/fibs_linux_amd64.s go/vendor/kernel.org/pub/linux/libs/security/libcap/psx
+ cd go && CGO_ENABLED=0 go build -o fib main.go
+
+go/vendor/kernel.org/pub/linux/libs/security/libcap/psx:
+ mkdir -p go/vendor/kernel.org/pub/linux/libs/security/libcap/
+ ln -s $(topdir)/psx $@
+
+go/vendor/fibber/fib.syso: c/fib.c
+ gcc -c -o go/vendor/fibber/fib_$(GOTARGET).syso c/fib.c
+
+clean:
+ rm -f *~
+ rm -f c/*.o c/*~
+ rm -f go/fib go/*~
+ rm -f go/vendor/fibber/*.syso go/vendor/fibber/*~
+ rm -rf go/vendor/kernel.org
diff --git a/contrib/bug216610/README.md b/contrib/bug216610/README.md
new file mode 100644
index 0000000..ae59a0d
--- /dev/null
+++ b/contrib/bug216610/README.md
@@ -0,0 +1,98 @@
+# Linking psx and C code without cgo
+
+## Overview
+
+In some embedded situations, there is a desire to compile Go binaries
+to include some C code, but not `libc` etc. For a long time, I had
+assumed this was not possible, since using `cgo` *requires* `libc` and
+`libpthread` linkage.
+
+This embedded compilation need was referenced in a [bug
+filed](https://bugzilla.kernel.org/show_bug.cgi?id=216610) against the
+[`"psx"`](https://pkg.go.dev/kernel.org/pub/linux/libs/security/libcap/psx)
+package. The bug-filer was seeking an alternative to `CGO_ENABLED=1`
+compilation needing the `cgo` variant of `psx` build. However, the go
+`"runtime"` package will
+[`panic()`](https://cs.opensource.google/go/go/+/refs/tags/go1.19.2:src/runtime/os_linux.go;l=717-720)
+if you try this.
+
+However, in researching that bug, I have learned there is a trick to
+combining a non-CGO built binary with compiled C code. I learned about
+it from a brief reference in the [Go Programming Language
+Wiki](https://zchee.github.io/golang-wiki/GcToolchainTricks/).
+
+This present directory evolved from my attempt to understand and
+hopefully resolve what was going on as reported in that bug into an
+example of this _trick_.
+
+*Caveat Emptor*: this example is potentially very fragile. The Go team
+only supports `cgo` linking against C.
+
+## Content
+
+In this example we have:
+- Some C code, `fib_init()` and `fib_next()` that combine to implement
+a _compute engine_ to determine [Fibonacci
+Numbers](https://en.wikipedia.org/wiki/Fibonacci_number). The source
+for this is in the sub directory `.../c/fib.c`.
+- Some Go code, in the directory `.../go/vendor/fibber` that uses this
+C compiled compute kernel.
+- A top level `Makefile` to build it all.
+
+This build uses vendored Go packages so I could experiment with
+modifications of the `"psx"` package to explore potential changes (of
+which there have been none).
+
+## Building and running the built binary
+
+Set things up with:
+```
+$ git clone git://git.kernel.org/pub/scm/libs/libcap/libcap.git
+$ cd libcap
+$ make all
+$ cd contrib/bug216610
+$ make clean all
+```
+When you run `.../go/fib` it should generate the following output:
+```
+$ ./go/fib
+psx syscall result: PID=<nnnnn>
+fib: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...
+$
+```
+Where `<nnnnn>` is the PID of the program at runtime and will be
+different each time the program is invoked.
+
+## Discussion
+
+The Fibonacci detail of what is going on is mostly uninteresting. The
+reason for developing this example was to explore the build issues in
+the reported [Bug
+216610](https://bugzilla.kernel.org/show_bug.cgi?id=216610). Ultimately,
+this example offers an alternative path to build a `nocgo` that links
+to compute engine style C code.
+
+## Future thoughts
+
+At present, this example only works on Linux with `x86_64` (in
+go-speak that is `linux_amd64`). This is because I have only provided
+some bridging assembly for Go to C calling conventions on that
+architecture target (`.../go/vendor/fibber/fibs_linux_amd64.s`).
+
+Perhaps a later version will have bridging code for all the Go
+supported Linux architectures, but it will also have to provide some
+mechanism to build the `.../c/fib.c` code to make
+`fib_linux_<arch>.syso` files. The [cited
+bug](https://bugzilla.kernel.org/show_bug.cgi?id=216610) includes some
+pointers for how to use Docker to support this.
+
+The compilation optimization level for `.../c/fib.c` seems to be
+important for this example. Depending on which version of the compiler
+is being used, the optimization process can make more or less use of
+link-time optimizations, which don't seem to work in this example. For
+this reason, we don't include `-O<n>` gcc options when compiling that
+C file.
+
+Please report issues or offer improvements to this example via the
+[Fully Capable `libcap`](https://sites.google.com/site/fullycapable/)
+website.
diff --git a/contrib/bug216610/c/fib.c b/contrib/bug216610/c/fib.c
new file mode 100644
index 0000000..bd665c7
--- /dev/null
+++ b/contrib/bug216610/c/fib.c
@@ -0,0 +1,20 @@
+#include <inttypes.h>
+
+struct state {
+ uint32_t b, a;
+};
+
+void fib_init(struct state *s);
+void fib_init(struct state *s)
+{
+ s->a = 0;
+ s->b = 1;
+}
+
+void fib_next(struct state *s);
+void fib_next(struct state *s)
+{
+ uint32_t next = s->a + s->b;
+ s->a = s->b;
+ s->b = next;
+}
diff --git a/contrib/bug216610/go/.gitignore b/contrib/bug216610/go/.gitignore
new file mode 100644
index 0000000..68b7ed0
--- /dev/null
+++ b/contrib/bug216610/go/.gitignore
@@ -0,0 +1,3 @@
+fib
+*.syso
+vendor/kernel.org
diff --git a/contrib/bug216610/go/go.mod b/contrib/bug216610/go/go.mod
new file mode 100644
index 0000000..819081e
--- /dev/null
+++ b/contrib/bug216610/go/go.mod
@@ -0,0 +1,3 @@
+module fib
+
+go 1.18
diff --git a/contrib/bug216610/go/main.go b/contrib/bug216610/go/main.go
new file mode 100644
index 0000000..bb5a346
--- /dev/null
+++ b/contrib/bug216610/go/main.go
@@ -0,0 +1,28 @@
+// Program fib uses the psx package once, and then prints the first
+// ten Fibonacci numbers.
+package main
+
+import (
+ "fibber"
+ "fmt"
+ "log"
+ "syscall"
+
+ "kernel.org/pub/linux/libs/security/libcap/psx"
+)
+
+func main() {
+ pid, _, err := psx.Syscall3(syscall.SYS_GETPID, 0, 0, 0)
+ if err != 0 {
+ log.Fatalf("failed to get PID via psx: %v", err)
+ }
+ fmt.Print("psx syscall result: PID=")
+ fmt.Println(pid)
+ s := fibber.NewState()
+ fmt.Print("fib: ", s.A, ", ", s.B)
+ for i:=0; i<8; i++ {
+ s.Next()
+ fmt.Print(", ", s.B)
+ }
+ fmt.Println(", ...")
+}
diff --git a/contrib/bug216610/go/vendor/fibber/fib.go b/contrib/bug216610/go/vendor/fibber/fib.go
new file mode 100644
index 0000000..e69a309
--- /dev/null
+++ b/contrib/bug216610/go/vendor/fibber/fib.go
@@ -0,0 +1,26 @@
+package fibber
+
+import (
+ "unsafe"
+)
+
+type State struct {
+ B, A uint32
+}
+
+func fibInit(ptr unsafe.Pointer)
+func fibNext(ptr unsafe.Pointer)
+
+// NewState initializes a Fibonacci Number sequence generator. Upon
+// return s.A=0 and s.B=1 are the first two numbers in the sequence.
+func NewState() (*State) {
+ s := &State{}
+ fibInit(unsafe.Pointer(&s.B))
+ return s
+}
+
+// Next advances the state to the next number in the sequence. Upon
+// return, s.B is the most recently calculated value.
+func (s *State) Next() {
+ fibNext(unsafe.Pointer(&s.B))
+}
diff --git a/contrib/bug216610/go/vendor/fibber/fibs_linux_amd64.s b/contrib/bug216610/go/vendor/fibber/fibs_linux_amd64.s
new file mode 100644
index 0000000..4e0d800
--- /dev/null
+++ b/contrib/bug216610/go/vendor/fibber/fibs_linux_amd64.s
@@ -0,0 +1,57 @@
+// To transition from a Go call to a C function call, we are skating
+// on really thin ice... Ceveat Emptor!
+//
+// Ref:
+// https://gitlab.com/x86-psABIs/x86-64-ABI/-/wikis/home
+//
+// This is not strictly needed, but it makes gdb debugging less
+// confusing because spacer ends up being an alias for the TEXT
+// section start.
+TEXT ·spacer(SB),$0
+ RET
+
+#define RINDEX(n) (8*n)
+
+// Push all of the registers the C callee isn't expected to preserve.
+#define PUSHALL() \
+ ADJSP $(RINDEX(9)) \
+ MOVQ AX, RINDEX(0)(SP) \
+ MOVQ CX, RINDEX(1)(SP) \
+ MOVQ DX, RINDEX(2)(SP) \
+ MOVQ SI, RINDEX(3)(SP) \
+ MOVQ DI, RINDEX(4)(SP) \
+ MOVQ R8, RINDEX(5)(SP) \
+ MOVQ R9, RINDEX(6)(SP) \
+ MOVQ R10, RINDEX(7)(SP) \
+ MOVQ R11, RINDEX(8)(SP)
+
+// Pop all of the registers the C callee isn't expected to preserve.
+#define POPALL() \
+ MOVQ RINDEX(0)(SP), AX \
+ MOVQ RINDEX(1)(SP), CX \
+ MOVQ RINDEX(2)(SP), DX \
+ MOVQ RINDEX(3)(SP), SI \
+ MOVQ RINDEX(4)(SP), DI \
+ MOVQ RINDEX(5)(SP), R8 \
+ MOVQ RINDEX(6)(SP), R9 \
+ MOVQ RINDEX(7)(SP), R10 \
+ MOVQ RINDEX(8)(SP), R11 \
+ ADJSP $-(RINDEX(9))
+
+// Header to this function wrapper is the last time we can voluntarily
+// yield to some other goroutine.
+TEXT ·fibInit(SB),$0-8
+ PUSHALL()
+ MOVQ ptr+RINDEX(0)(FP), DI
+ CALL fib_init(SB)
+ POPALL()
+ RET
+
+// Header to this function wrapper is the last time we can voluntarily
+// yield to some other goroutine.
+TEXT ·fibNext(SB),$0-8
+ PUSHALL()
+ MOVQ ptr+RINDEX(0)(FP), DI
+ CALL fib_next(SB)
+ POPALL()
+ RET