How does CGO_ENABLED affect dynamic vs static linking?
Asked Answered
K

1

10

We are compiling our Go code to be run on docker and we were investigating why our binary was not executing. We found that it was missing some dynamic libraries (although we want statically linked binaries).

This is how it was compiled.

env GOOS=linux CGO_ENABLED=1 GO111MODULE=on GOPRIVATE=github.com/ourrepo GOPROXY=https://proxy.golang.org go build --installsuffix cgo --ldflags='-extldflags=-static' -o program main.go

Using the same build command with CGO_ENABLED=0 ended up fixing the issue, and the output binary was statically linked.

Now the weird part is we have another program that is using the same build command, this time with CGO_ENABLED=1 and... it is statically linked!

So I'm very confused why in some cases CGO_ENABLED=1 produces dynamic linking, and sometimes static linking. Happy to provide more details.

Kiloton answered 9/7, 2020 at 14:23 Comment(8)
The binary is only dynamically linked if it needs to be. If you're not using cgo, then CGO_ENABLED=1 makes no difference.Toneme
I'm not sure what the question is here. If the binary from CGO_ENABLED=0 is what you want, what are you trying to accomplish with the original go build command? (that original command looks like a combination of random things just pasted together)Toneme
@Toneme I'm trying to understand why in one case (cgo_enabled=1) the binary is dynamically linked and in the other it is not. to your question "is CGO_ENABLED=0 what we want?" not really. We want static. CGO_ENABLED=0 only magically makes this happen and I don't get why. Does it make more sense? Do you suggest to remove --installsuffix cgo and GO111MODULE and CGO altogether?Kiloton
Because one is using cgo and one is not. The command itself doesn't make sense, you're passing -static to an external linker that you're not calling (there's no -linkmode external), you don't seem to be cross-compiling, but you have GOOS defined, though no GOARCH; and you have GO111MODULE=on but you're not building a package. It looks like you added a bunch of options without knowing what they do, and it's causing confusion.Toneme
CGO_ENABLED=0 doesn't "magically" make it happen, it prevents the use of cgo, which will result in a static binary, which sounds like what you want.Toneme
@Toneme I tried env GOOS=linux GOARCH=amd64 GOPRIVATE=github.com/ourrepo GOPROXY=https://proxy.golang.org go build -o program main.go and it is dynamically linked. Adding CGO_ENABLED=0 worked; I'm still not sure how things ended up statically linked in one case of CGO_ENABLED=1Kiloton
I can't say for sure since you have not supplied any code, but if you don't require cgo, then use CGO_ENABLED=0 as we've already covered.Toneme
and for the comment edit: you will have a static binary with CGO_ENABLED=1 (which is usually the default) if you don't use cgo at all, because the default result of the go toolchain is a static binary.Toneme
W
10

Some Go packages use CGO under the hood, leveraging (very) common C libraries for broader compatibility across environments and edge cases encountered at runtime.

These common libraries are found on most major OS distributions - but obviously are not included in the Scratch image (which is by nature completely empty).

CGO_ENABLED is set to 1 by default, which means that it must be explicitly disabled with CGO_ENABLED=0 to avoid it, even when using the -static flag.

Wedekind answered 9/7, 2020 at 18:27 Comment(4)
Notably, the net package uses some libc functions by default.Wedekind
"to reduce compiled binary size." not really. Under Cgo binaries contain both resolvers. Pure-Go one is the default and always used unless you explicitly tell it otherwise or OS configuration has something that pure-Go one doesn't support. Cgo resolver is much harder on resources because every Cgo call consumes entire thread instead of goroutine. It's there probably for compatibility (pure-Go tries to emulate common implementations) and because on some systems you can't send DNS requests yourself (macOS). Go doesn't need libc.Silica
Interesting point! I read up on it a bit more (github.com/golang/go/issues/25670) and you're absolutely correct. I knew about the overhead cost of calling Cgo, but always assumed the benefit of defaulting to include the C hooks was for size optimization. Thanks for sharing. I'll update the answer to reflectWedekind
I don't think that in any way changes the substance of the answer though... @Flimzy's problem is the attempted use of those underlying libraries in the Scratch container, which is solved by disabling CGO.Wedekind

© 2022 - 2024 — McMap. All rights reserved.