Skip to content

Fix static native library trailing link arg#3993

Open
wi-adam wants to merge 2 commits intobazelbuild:mainfrom
wi-adam:fix-static-native-link-arg
Open

Fix static native library trailing link arg#3993
wi-adam wants to merge 2 commits intobazelbuild:mainfrom
wi-adam:fix-static-native-link-arg

Conversation

@wi-adam
Copy link
Copy Markdown

@wi-adam wi-adam commented Apr 27, 2026

Motivation

I ran into this while adding Rust FFI for Tink through native static libraries with alwayslink = False while using zig cc as the C/C++ toolchain. In that setup, rules_rust currently emits two references for the same static native library: an early -lstatic=<name> and a trailing -Clink-arg=-l<name> used by the native/Rust/native link-order sandwich.

On Linux, rustc restores dynamic linker mode before appending -Clink-arg values. The early -lstatic=<name> selects the static archive, but the trailing -l<name> is handed to the linker after that mode change, so it is no longer tied to the archive artifact that rules_rust already selected. With zig cc, this can fail even when Bazel has a concrete .a input available: Zig treats the trailing -l<name> as a dynamic library lookup and searches for lib<name>.so.

Minimal Repro

This reproduces with a cc_import(static_library = ...) dependency and no shared library artifact.

MODULE.bazel:

module(name = "rules_rust_zig_static_repro")

bazel_dep(name = "rules_rust", version = "0.70.0")
bazel_dep(name = "rules_cc", version = "0.2.17")

local_path_override(
    module_name = "rules_rust",
    path = "../rules_rust",
)

rust = use_extension("@rules_rust//rust:extensions.bzl", "rust")
rust.toolchain(edition = "2021")
use_repo(rust, "rust_toolchains")

register_toolchains("@rust_toolchains//:all")

BUILD.bazel:

load("@rules_cc//cc:defs.bzl", "cc_import")
load("@rules_rust//rust:defs.bzl", "rust_binary")

genrule(
    name = "make_native_archive",
    srcs = ["native.c"],
    outs = ["libnative.a"],
    cmd = "set -eu; gcc -c $(location native.c) -o $(@D)/native.o; ar rcs $@ $(@D)/native.o",
)

cc_import(
    name = "native",
    static_library = ":libnative.a",
)

rust_binary(
    name = "demo",
    srcs = ["main.rs"],
    edition = "2021",
    deps = [":native"],
)

native.c:

int native_value(void) {
    return 42;
}

main.rs:

unsafe extern "C" {
    fn native_value() -> i32;
}

fn main() {
    assert_eq!(unsafe { native_value() }, 42);
}

Use a zig cc wrapper as the configured C compiler. The cache exports avoid Zig failing inside Bazel's sanitized/sandboxed Rust actions:

#!/bin/sh
export ZIG_GLOBAL_CACHE_DIR="${ZIG_GLOBAL_CACHE_DIR:-/tmp/zig-global-cache}"
export ZIG_LOCAL_CACHE_DIR="${ZIG_LOCAL_CACHE_DIR:-/tmp/zig-local-cache}"
exec /path/to/zig cc -target x86_64-linux-gnu "$@"

Then run:

bazel run \
  --repo_env=CC=/path/to/zigcc \
  --action_env=CC=/path/to/zigcc \
  //:demo \
  --verbose_failures

Before this change, the Rust action contains:

-Lnative=bazel-out/k8-fastbuild/bin
-lstatic=native
-Clink-arg=-lnative

The link fails under zig cc because the trailing -lnative is processed after rustc has restored dynamic mode, so Zig reports that it is unable to find dynamic system library native and searches for libnative.so.

With this change, the trailing sandwich arg is the selected archive path instead:

-Clink-arg=bazel-out/k8-fastbuild/bin/libnative.a

and the repro builds and runs.

Summary

  • pass the selected static archive path for the trailing native-library sandwich link arg on Unix/Darwin
  • keep Windows GNU/MSVC link flag behavior unchanged
  • update native dependency, proc-macro leakage, and linker-input ordering analysis tests for the archive-path contract

Verification

  • Linux Docker + Zig 0.13.0: the repro above fails before this change and passes with this change
  • bazel test //test/unit/proc_macro:proc_macro_does_not_leak_deps_test //test/unit/native_deps:native_deps_test_suite //test/unit/linker_inputs_propagation:linker_inputs_propagation_test_suite //test/unit/windows_lib_name:windows_lib_name_test_suite
  • Linux Docker: bazel test //test/unit/proc_macro:proc_macro_does_not_leak_deps_test --test_output=errors
  • Linux Docker: bazel test //test/unit/native_deps:native_deps_test_suite //test/unit/linker_inputs_propagation:linker_inputs_propagation_test_suite

Notes

I reviewed CONTRIBUTING.md: this is a focused bug fix with tests and no user-facing API/doc change, so I did not add docs or release notes.

@google-cla
Copy link
Copy Markdown

google-cla Bot commented Apr 27, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@wi-adam wi-adam force-pushed the fix-static-native-link-arg branch from ff3b783 to 915e999 Compare April 27, 2026 14:38
@wi-adam wi-adam marked this pull request as ready for review April 27, 2026 17:33
@wi-adam wi-adam force-pushed the fix-static-native-link-arg branch from 915e999 to 05d26c0 Compare April 27, 2026 22:43
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

Successfully merging this pull request may close these issues.

1 participant