From ca38751f959cde85cc79fd0429ce8918df75997b Mon Sep 17 00:00:00 2001 From: Simon Friis Vindum Date: Fri, 28 Aug 2026 15:48:12 +0200 Subject: [PATCH 1/2] Rust: Enable LTO for the extractor --- misc/bazel/rust.bzl | 26 +++++++++++++++++++++++++- rust/extractor/BUILD.bazel | 1 + 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/misc/bazel/rust.bzl b/misc/bazel/rust.bzl index 97afee4ba1c1..9142776318cc 100644 --- a/misc/bazel/rust.bzl +++ b/misc/bazel/rust.bzl @@ -1,17 +1,41 @@ load("@rules_rust//rust:defs.bzl", "rust_binary") load("@semmle_code//buildutils-internal:glibc_symbols_check.bzl", "glibc_symbols_check") load("@semmle_code//buildutils-internal:lipo.bzl", "universal_binary") +load("@semmle_code//buildutils-internal:transitions.bzl", "forward_binary_from_transition", "get_transition_attrs") + +def _full_lto_transition_impl(_settings, _attr): + return {"@rules_rust//rust/settings:lto": "fat"} + +_full_lto_transition = transition( + implementation = _full_lto_transition_impl, + inputs = [], + outputs = ["@rules_rust//rust/settings:lto"], +) + +_full_lto_binary = rule( + implementation = forward_binary_from_transition, + attrs = get_transition_attrs(_full_lto_transition), +) def codeql_rust_binary( name, + full_lto = False, target_compatible_with = None, visibility = None, symbols_test = True, **kwargs): rust_label_name = "single_arch/" + name + binary_dep = ":" + rust_label_name + if full_lto: + lto_label_name = "full_lto/" + name + _full_lto_binary( + name = lto_label_name, + dep = binary_dep, + ) + binary_dep = ":" + lto_label_name universal_binary( name = name, - dep = ":" + rust_label_name, + dep = binary_dep, target_compatible_with = target_compatible_with, visibility = visibility, ) diff --git a/rust/extractor/BUILD.bazel b/rust/extractor/BUILD.bazel index 52b551f6335d..d3ae10fe39e8 100644 --- a/rust/extractor/BUILD.bazel +++ b/rust/extractor/BUILD.bazel @@ -11,6 +11,7 @@ codeql_rust_binary( "src/qltest_cargo.mustache", "src/nightly-toolchain/rust-toolchain.toml", ], + full_lto = True, proc_macro_deps = all_crate_deps( proc_macro = True, ) + [ From 693c0f3bfe76104684dd31d9a72f16b9ab2a3cab Mon Sep 17 00:00:00 2001 From: Simon Friis Vindum Date: Sat, 29 Aug 2026 09:10:55 +0200 Subject: [PATCH 2/2] Fix full LTO in standalone and Linux builds Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a52f212e-fee3-4245-a5fc-e5be4cdf2cdf --- misc/bazel/rust.bzl | 5 +++- misc/bazel/transitions.bzl | 47 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 misc/bazel/transitions.bzl diff --git a/misc/bazel/rust.bzl b/misc/bazel/rust.bzl index 9142776318cc..7f83f09bc085 100644 --- a/misc/bazel/rust.bzl +++ b/misc/bazel/rust.bzl @@ -1,7 +1,7 @@ load("@rules_rust//rust:defs.bzl", "rust_binary") load("@semmle_code//buildutils-internal:glibc_symbols_check.bzl", "glibc_symbols_check") load("@semmle_code//buildutils-internal:lipo.bzl", "universal_binary") -load("@semmle_code//buildutils-internal:transitions.bzl", "forward_binary_from_transition", "get_transition_attrs") +load("//misc/bazel:transitions.bzl", "forward_binary_from_transition", "get_transition_attrs") def _full_lto_transition_impl(_settings, _attr): return {"@rules_rust//rust/settings:lto": "fat"} @@ -27,6 +27,9 @@ def codeql_rust_binary( rust_label_name = "single_arch/" + name binary_dep = ":" + rust_label_name if full_lto: + # rustc must consume the LLVM bitcode because the C++ linker may use an + # incompatible LLVM version. + kwargs["experimental_use_cc_common_link"] = 0 lto_label_name = "full_lto/" + name _full_lto_binary( name = lto_label_name, diff --git a/misc/bazel/transitions.bzl b/misc/bazel/transitions.bzl new file mode 100644 index 000000000000..bc94ccdf4a03 --- /dev/null +++ b/misc/bazel/transitions.bzl @@ -0,0 +1,47 @@ +load("@bazel_skylib//lib:paths.bzl", "paths") + +def forward_binary_from_transition(ctx): + binary = ctx.attr.dep[0] + default_info = binary[DefaultInfo] + original_executable = default_info.files_to_run.executable + if not original_executable: + fail("Cannot transition a target that is not executable") + + (_, extension) = paths.split_extension(original_executable.basename) + new_executable = ctx.actions.declare_file(ctx.label.name + extension) + inputs = [original_executable] + command = "cp %s %s" % (original_executable.path, new_executable.path) + + providers = [] + if OutputGroupInfo in binary: + pdb_file = getattr(binary[OutputGroupInfo], "pdb_file", None) + if pdb_file: + (pdb_file,) = pdb_file.to_list() + linked_pdb_file = ctx.actions.declare_file(ctx.label.name + ".pdb") + ctx.actions.symlink(target_file = pdb_file, output = linked_pdb_file) + inputs.append(linked_pdb_file) + providers.append(binary[OutputGroupInfo]) + + ctx.actions.run_shell( + inputs = inputs, + outputs = [new_executable], + command = command, + ) + files = depset(direct = [new_executable]) + runfiles = default_info.default_runfiles.merge(ctx.runfiles([new_executable])) + providers.append( + DefaultInfo( + files = files, + runfiles = runfiles, + executable = new_executable, + ), + ) + return providers + +def get_transition_attrs(transition_rule): + return { + "_allowlist_function_transition": attr.label( + default = "@bazel_tools//tools/allowlists/function_transition_allowlist", + ), + "dep": attr.label(mandatory = True, cfg = transition_rule), + }