mon
mon

Reputation: 22368

When using CGO_ENABLED is must and what happens

Question

Please help understand when CGO_ENABLED is must and unavoidable, and what happens with the go compilation process.

When CGO_ENABLED is must

After having read the articles in references, it looks as far as go has cross-compile native support for the target platform, CGO_ENABLED will never be a must. Is this correct?

The situation when cgo and CGO_ENABLED is absolute-must is when go compiler cannot produce the binary code for the target platform. For instance, imagine I am writing a Space X control board embedded program in Go and I have to use C library and the compiler for the board. There is no other library or compiler available for the board.

In such case, I have to do CGO_ENABLED=1 and tell cgo the compiler and the linker to use and where the C library binaries are copied in my laptop, and setup the compilers CFLAGS etc etc. Perhaps I would have to use C for all the coding instead of using Go in this case, but somehow I was in the situation that I had to use Go. This is when CGO_ENABLED=1 would be must. Is this correct?

Otherwise, if especially go has the support for the target platform cross-compilation, using CGO_ENABLED=1 could be a short-cut to just re-use the existing C library for the target platform. Is this correct or is there any other reason why CGO_ENABLED is required?

What happens

I suppose when CGO_ENABLED=1 is used, basically there will be a composition of the binary from Go part, and the binary from the C library in the executable created by go build command. And at runtime, the execution goes back and forth between the two binary world of go and C. Go binary side would not know C side, so the go tools like debugger would not be available.

I suppose whether the executable hard-links all the libraries or uses dynamic linking depends on the target platform compiler.

Are these correct understandings?

The reason CGO_ENABLED=0 by default for cross-compilation is because we should use the go built-in support of the target platform cross-compilation and there should be no reason not to do so.

The reason CGO_ENABLED=1 by default for the native local platform is because the cgo compiler knows (or the author of the compiler knows) the local machine architecture and the OS libraries (or well known 3rd party's) available, hence best-optimization can be expected? But I am not sure this is true because the go compiler itself can be best-optimized for the local OS and architecture, then why need to use cgo?

Kindly explain why CGO_ENABLED=1 by default for the native local platform.

References

Upvotes: 70

Views: 95149

Answers (2)

kurt
kurt

Reputation: 653

If you're running on an Alpine image, it is impossible to compile and run Go programs in Alpine images right away. You must disable CGO by setting the environment variable CGO_ENABLED to false (the default value is true).

You can do this either by:

  • Adding go env -w CGO_ENABLED=0 like Robert mentions in his comment or,
  • Setting the env value prefixing it in the go command, e.g.: CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build -ldflags '-s -w' -tags lambda.norpc -o bin/<YOUR_FUNC>/bootstrap <YOUR_PATH>/main.go

See: https://megamorf.gitlab.io/2019/09/08/alpine-go-builds-with-cgo-enabled/

Upvotes: 5

Martin Tournoij
Martin Tournoij

Reputation: 27852

Many things are only available as C libraries, and re-implementing that all in Go would be costly. cgo has its downsides, but it can be a good trade-off. Even the standard library uses it (net for DNS lookups, os/user for user lookups) because it doesn't re-implement 100% of the behaviour in Go.

Cross-compiling C code is still rather hard; you'll need the target architecture's C compiler and toolchain (e.g. CC=aarch64-linux-musl-gccgo build to build an arm64 binary). None of that is installed by default so for most people cgo simply won't work when cross-compiling; they need to take manual steps to set it up first.

cgo often isn't strictly required (like in the net and os/user packages), so disabling it by default seems the most user-friendly option.

But there are no such constraints on the native platform, and it's expected to work by default without any user setup; so why not enable it by default?

Upvotes: 17

Related Questions