link: remove -z,now (BIND_NOW) from CC toolchain extldflags#4578
Merged
fmeum merged 8 commits intobazel-contrib:masterfrom Mar 31, 2026
Merged
link: remove -z,now (BIND_NOW) from CC toolchain extldflags#4578fmeum merged 8 commits intobazel-contrib:masterfrom
fmeum merged 8 commits intobazel-contrib:masterfrom
Conversation
Contributor
Author
|
@fmeum @tyler-french PTAL |
fmeum
approved these changes
Mar 24, 2026
fmeum
reviewed
Mar 30, 2026
The CC toolchain (rules_cc) typically passes -Wl,-z,relro,-z,now to the linker for security hardening. While -z,relro (partial RELRO) is safe, -z,now forces all dynamic symbols to be resolved at load time. This breaks Go libraries that use dlopen/dlsym to load symbols at runtime (e.g., NVIDIA's go-nvml), because the dynamic linker tries to resolve undefined symbols before dlopen has a chance to make them available. The standard Go toolchain (go build) does not pass -z,now. This is consistent with existing filtering of other CC toolchain flags that are incompatible with Go binaries (-Wl,--gc-sections, -pie). Fixes bazel-contrib#4377. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Go linker itself sets BIND_NOW for -buildmode=pie, so the test should only verify non-PIE cgo binaries. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of a custom _remove_bind_now function in link.bzl, add the flags to the existing _LINKER_OPTIONS_DENYLIST in context.bzl. This is consistent with how -Wl,--gc-sections and -pie are already handled. The Go toolchain (go build) does not pass -z,relro or -z,now either, so filtering the compound flag entirely aligns with go build behavior. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace magic numbers with debug/elf constants (elf.DT_FLAGS, elf.DF_BIND_NOW, elf.DT_FLAGS_1, elf.DF_1_NOW) and add missing defer e.Close() to match project conventions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
cb08aa0 to
a8d33b5
Compare
Contributor
Author
|
I have replaced the previous go_test-based bind_now_test.go (which hardcoded "BIND_NOW should not be set") with a go_bazel_test that builds the same cgo source with both bazel build and native go build, then asserts their BIND_NOW flags match. This covers both auto and pie link modes via table-driven subtests, making the test future-proof — if Go changes its default BIND_NOW behavior, the test adapts automatically. |
Member
|
I like the idea, but CI is red 🙃 |
Contributor
Author
|
fixed, only test on linux |
fmeum
reviewed
Mar 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #4377.
The CC toolchain (
rules_cc) typically passes-Wl,-z,relro,-z,nowto the linker for security hardening. However,-z,nowforces all dynamic symbols to be resolved at load time (BIND_NOW), which breaks Go libraries that usedlopen/dlsymto load symbols at runtime.For example, NVIDIA's go-nvml declares NVML C functions in its header file, which CGO compiles into undefined dynamic symbols. At runtime, go-nvml loads these symbols via
dlopen("libnvidia-ml.so.1", RTLD_LAZY | RTLD_GLOBAL)insidenvml.Init(). WithBIND_NOW, the dynamic linker fails immediately at program start:The same code works with
go buildbecause the standard Go toolchain does not pass-z,relroor-z,nowto the linker.Root Cause
The resulting binary has
FLAGS: BIND_NOWin its ELF dynamic section, whilego buildproduces a binary without it.Fix
Add
-Wl,-z,relro,-z,nowand-Wl,-z,nowto_LINKER_OPTIONS_DENYLISTincontext.bzl, consistent with how-Wl,--gc-sectionsand-pieare already filtered for the same reason (CC toolchain defaults incompatible with Go binaries).Since
go builddoes not pass-z,relroeither, filtering the compound flag entirely aligns withgo buildbehavior.Test plan
bind_now_test.gothat inspects the ELF dynamic section of a non-PIE cgo binary to verifyDF_BIND_NOW/DF_1_NOWflags are not set (PIE excluded because the Go linker itself sets BIND_NOW for-buildmode=pie)gc_linkopts = ["-extldflags", "-Wl,-z,lazy"])