Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cgo: -lstdc++ being stripped out of linkopts #4174

Open
adam-azarchs opened this issue Nov 16, 2024 · 0 comments
Open

cgo: -lstdc++ being stripped out of linkopts #4174

adam-azarchs opened this issue Nov 16, 2024 · 0 comments

Comments

@adam-azarchs
Copy link
Contributor

adam-azarchs commented Nov 16, 2024

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

    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<char, std::char_traits<char> >):(.text+0x4b1): error: undefined reference to 'std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, unsigned long, std::allocator<char> 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

# Exclude -lstdc++ from link options. We don't want to link against it
# unless we actually have some C++ code. _cgo_codegen will include it
# in archives via CGO_LDFLAGS if it's needed.
extldflags = [f for f in extldflags_from_cc_toolchain(go) if f not in ("-lstdc++", "-lc++", "-static")]

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

// #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

// TODO(jayconrod): this doesn't write CGO_LDFLAGS into the archive. We
// might miss dependencies like -lstdc++ if they aren't referenced in
// some other way.

I'm not 100% sure that

# NOTE(#2545): avoid unnecessary dynamic link
if "-static-libstdc++" in clinkopts:
clinkopts = [
option
for option in clinkopts
if option not in ("-lstdc++", "-lc++")
]

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant