Upgrade to Pro — share decks privately, control downloads, hide ads and more …

CGO journey: from using to understanding it

CGO journey: from using to understanding it

Avatar for José Carlos Chávez

José Carlos Chávez

January 12, 2022
Tweet

More Decks by José Carlos Chávez

Other Decks in Programming

Transcript

  1. CGO journey: from using to understanding it José Carlos Chávez

    @jcchavezs January Golang Meetup - GDG Berlin Golang
  2. 2 About me José Carlos Chávez - Software Engineer at

    Traceable.ai - Open source contributor in Observability projects - Maths student - Loving father @jcchavezs
  3. What is CGO? Cgo lets Go packages call C code.

    Given a Go source file written with some special features, cgo outputs Go and C files that can be combined into a single Go package. Andrew Gerrand https://go.dev/blog/cgo 3 @jcchavezs Foto de Anne Nygård en Unsplash
  4. There are many libraries written in C or C++. If

    we are willing to use them there are only a couple of options to go for: 1. Rewrite them in Go 2. Use them directly with CGO Why is CGO useful? 4 @jcchavezs
  5. // source: main.go package main //int sum(int a, int b)

    { return a+b; } import "C" func main() { println(C.sum(1, 1)) } 5 @jcchavezs How does it look like?
  6. // source: main.go package main // Linker Options: #cgo darwin

    LDFLAGS: -framework Cocoa -framework OpenGL -framework IOKit -framework CoreVideo // Linux Build Tags #cgo linux,!wayland CFLAGS: -D_GLFW_X11 -D_GLFW_GLX #cgo linux,wayland CFLAGS: -D_GLFW_WAYLAND -D_GLFW_EGL ... 6 @jcchavezs How does it really look like?
  7. 8 @jcchavezs What happens inside? $ go tool cgo main.go

    $ ls _obj | awk '{print $NF}' >>_cgo_.o >>_cgo_export.c >>_cgo_export.h >>_cgo_flags >>_cgo_gotypes.go >>_cgo_main.c >>main.cgo1.go >>main.cgo2.c
  8. //main.cgo1.go package main //int sum(int a, int b) { return

    a+b; } import _ "unsafe" func main() { println(( /*line :11:13*/_Cfunc_sum /*line :11:17*/)(1, 1)) } 9 @jcchavezs What happens inside?
  9. Some people, when confronted with a problem, think “I know,

    I’ll use cgo.” Now they have two problems. Dave Cheney - cgo is not Go 11 @jcchavezs
  10. - Programming gotchas - Build gotchas - Runtime gotchas -

    Tooling gotchas Gotchas 12 @jcchavezs
  11. Programming gotchas 13 Manual memory management - Go is a

    garbage-collected runtime, but C is not. - Copying data aren’t always avoidable and eats up CPU. - Slices can be cumbersome due to its lifecycle. Data conversion - Most of the types are 1-1 mapping but some special cases use unsafe.pointer (e.g. string). - Special types lead to manual memory management. - C type used in one Go package is different from the same C type used in another. @jcchavezs
  12. Programming gotchas 14 Passing a function pointer to C Callbacks’

    overhead: Go code may pass a Go pointer to C provided the Go memory to which it points does not contain any Go pointers. See: https://golang.org/cmd/cgo/#hdr-Passing_pointers Cgoroutines != goroutines A blocking cgo call occupies a system thread. Go runtime can’t schedule them like it can a goroutine: Kb vs Mb in allocations. More: http://bit.ly/cgo-bench-test @jcchavezs
  13. Build gotchas 15 Complicated builds - The build process is

    not anymore self contained: e.g. go get, go build and go test now require preconditions (e.g. ldflags, library location, etc) - Build caching mechanics will impose a structure for C/library code. - Loading libraries is cumbersome. The location of the library can be a pain depending on the nature of the source code: - Go applications - Go libraries Slower build times - The cgo tool is invoked to generate the C to Go and Go to C thunks and stubs. - Your system C compiler has to be invoked for every C file in the package. - The individual compilation units are combined together into a single .o file. - The resulting .o file take a trip through the system linker for fix-ups against shared objects they reference. @jcchavezs
  14. Build gotchas 16 Static builds - The premise “single, static

    binary” isn’t easily achievable. - Unless static libraries you can’t guarantee 100% consistency between test - build - run. (Lack of) Cross compilation - By default CGO is disabled when cross compiling. You can still enable it passing a C cross-compiler for each GOOS+GOARCH - One extra degree of compatibility is added by the C library you are loading. @jcchavezs
  15. Runtime gotchas 17 Dynamic linking - Usage of different libraries

    for different architectures (e.g. amd64, arm64). Not even talking about distros. - If a library isn’t installed in the host we need to link the library in runtime using LD_LIBRARY_PATH or a CWD relative location (predefined in the code). - It is possible to link the library after a lookup but that requires a manual loading (see example) Call overhead - CGO calls are expensive compared to a call within Go, although the overhead is negligible (see this benchmark) @jcchavezs
  16. Tooling gotchas 18 Debugging - The portions residing in C

    aren’t as readily accessed through Go’s tooling. - Race detector, pprof for profiling code, coverage, fuzz testing, and source code analysis tools don’t work across the board @jcchavezs
  17. 19 @jcchavezs Summary - CGO is not a union, is

    an intersection of C and Go worlds. - Using libraries introduce another layer of complexity, they don’t come for free. - CGO was a foundational piece in Go evolution.
  18. 20 References • https://blog.marlin.org/cgo-referencing-c-library-in-go • https://pkg.go.dev/cmd/cgo#hdr-Passing_pointers • https://www.programmerall.com/article/2072595424/ • https://medium.com/mysterium-network/golang-c-interopera

    bility-caf0ba9f7bf3 • https://www.cockroachlabs.com/blog/the-cost-and-complexi ty-of-cgo/ • https://honnef.co/posts/2015/06/statically_compiled_go_p rograms__always__even_with_cgo__using_musl/ @jcchavezs