bazel-contrib / bazel-contrib/rules_go

cgo: `-lstdc++` being stripped out of linkopts

Open
#4,174 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
1.5k
Forks
760
Avg merge
1d 11h
Merged PRs (30d)
12

Description

### What version of rules_go are you using?

0.48.1

### What version of gazelle are you using?

n/a

### What version of Bazel are you using?

7.4.1

### Does this issue reproduce with the latest releases of all the above?

Yes

### What operating system and processor architecture are you using?

Linux/amd64

### Any other potentially useful information about your toolchain?

Custom c++ toolchain configuration including

```starlark
link_flags = [
"-static-libstdc++",
"-fuse-ld=gold",
"-Wl,-z,relro,-z,now",
"-no-canonical-prefixes",
"-Wl,--warn-execstack",
],
link_libs = [
"-Wl,--push-state,-as-needed,-Bstatic",
"-lstdc++",
"-Bdynamic",
"-lm",
"-Wl,--pop-state",
],
```

### What did you do?

Try to compile a cgo executable with a transitive dependency on c++ `cc_library`.

### What did you expect to see?

Successful linking with the toolchain-provided link flags.

### What did you see instead?

The `-lstdc++` is stripped out from the link line, so we get
```
bazel-out/k8-opt-exec-ST-9082f79b1d9e/bin/external/go_sdk/builder_reset/builder link ... -extldflags '-static-libstdc++ -fuse-ld=gold ... -Wl,--push-state,-as-needed,-Bstatic -Wl,-Bdynamic -lm -Wl,--pop-state -Wl,--push-state,-as-needed,-Bstatic -Wl,-Bdynamic -lm -Wl,--pop-state'
...
external/go_sdk/pkg/tool/linux_amd64/link: running /gcc failed: exit status 1
gcc -m64 -s ... bazel-out/k8-fastbuild/bin/external/com_googlesource_code_re2/libre2.a ... -static-libstdc++ -fuse-ld=gold -Wl,--push-state,-as-needed,-Bstatic -Wl,-Bdynamic -lm -Wl,--pop-state -Wl,--push-state,-as-needed,-Bstatic -Wl,-Bdynamic -lm -Wl,--pop-state ...
bazel-out/k8-fastbuild/bin/external/com_googlesource_code_re2/libre2.a(re2.pic.o):re2.cc:function re2::trunc(std::basic_string_view >):(.text+0x4b1): error: undefined reference to 'std::__cxx11::basic_string, std::allocator >::basic_string(char const*, unsigned long, std::allocator const&)'
```

Up until recently we'd been using a wrapper script around the linker which just injected `-lc++` no matter how broken the build system was being, but now I need to support compilation against either `libc++` or `libstdc++`, so that's no longer an option.

This seems to be caused by I think
https://github.com/bazel-contrib/rules_go/blob/5200a61dbf7f566586b027b7c5cc36a4bced3bfc/go/private/actions/link.bzl#L55-L58
which strips it out assuming that it'll be in `CGO_LDFLAGS` if needed except, leaving aside that `_cgo_codegen` doesn't seem to be a thing any more, I'm not sure how it's supposed to do that, because the go library doesn't have a direct dependency on the c++ library, and the go library doesn't use `#cgo LDFLAGS` directives because it wouldn't work anyway because the library it needs to link to doesn't exist outside of bazel. Adding
```golang
// #cgo LDFLAGS: -lstdc++
```
in the code is out of the question because this has to build with either `libstdc++` or `libc++` (including on linux).
The comment also seems somewhat at odds with
https://github.com/bazel-contrib/rules_go/blob/5200a61dbf7f566586b027b7c5cc36a4bced3bfc/go/tools/builders/cgo2.go#L50-L52

I'm not 100% sure that
https://github.com/bazel-contrib/rules_go/blob/5200a61dbf7f566586b027b7c5cc36a4bced3bfc/go/private/rules/cgo.bzl#L63-L69
isn't involved as well.

It may be possible to set it explicitly in the linkopts for the `go_library`, by configuring complicated `select()`s to infer which c++ standard library should be in use, but frankly I went to the trouble of configuring my c++ toolchain correctly so that I wouldn't have to do that.

Now that I've tracked down the cause of this issue, I do have a workaround, which is to change my toolchain configuration's linkopts to use `-Wl,-Bstatic,-lstdc++` to defeat the string matching, but it does seem like a case of the go rules trying to outsmart the c++ toolchain configuration and failing.

Contributor guide

Open the contributing guide

Research direction

Start with the linkopts handling in go/private/actions/link.bzl at the referenced lines, then compare the cgo paths in go/tools/builders/cgo2.go and go/private/rules/cgo.bzl. Reproduce the cgo executable link with the custom C++ toolchain and trace why -lstdc++ is removed; done means the toolchain-provided library remains available and linking succeeds without a hard-coded libc++ or libstdc++ directive.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, go
Domain
build-system
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.