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

Generate .ifso libraries with llvm-ifs #21194

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ common --check_direct_dependencies=error
# Add mirrors for certain download URLs
common --experimental_downloader_config=bazel_downloader.cfg

common --noincompatible_sandbox_hermetic_tmp

# Enable modern C++ features
build:linux --cxxopt=-std=c++17
build:linux --host_cxxopt=-std=c++17
Expand Down
2 changes: 1 addition & 1 deletion MODULE.bazel.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions site/en/docs/user-manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,15 @@ Modes:
[mostly static](/reference/be/c-cpp#cc_binary.linkstatic) mode.
If `-static` is set in linkopts, targets will change to fully static.

#### `--interface_shared_objects` {:#interface-shared-objects}

If enabled, Bazel will link against interface libraries instead of dynamic libraries (if
[dynamic linking](#dynamic-mode) is enabled). This avoids relinking when changes only affect the
implementation of the library, but not its interface. Interface libraries are always available on
Windows. The default Linux C++ toolchain automatically generates interface libraries if the
[`llvm-ifs`](https://llvm.org/docs/CommandGuide/llvm-ifs.html) tool is provided via
the `BAZEL_LLVM_IFS` environment variable or can be found in `PATH`.

#### `--fission (yes|no|[dbg][,opt][,fastbuild])` {:#fission}

Enables [Fission](https://gcc.gnu.org/wiki/DebugFission){: .external},
Expand Down
123 changes: 123 additions & 0 deletions src/test/shell/bazel/cc_integration_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1912,4 +1912,127 @@ EOF
fi
}

function test_ifso_generation() {
type -P llvm-ifs-14 || return 0

mkdir pkg
cat > pkg/BUILD <<'EOF'
cc_library(
name = "lib",
srcs = ["lib.c"],
hdrs = ["lib.h"],
)
cc_test(
name = "test",
srcs = ["test.c"],
deps = [":lib"],
)
EOF
cat > pkg/lib.c <<'EOF'
#include "lib.h"
int func() { return 42; }
EOF
cat > pkg/lib.h <<'EOF'
int func();
EOF
cat > pkg/test.c <<'EOF'
#include "lib.h"
#include "stdio.h"
int main() {
printf("func(): %d\n", func());
}
EOF

bazel test //pkg:test -s --test_output=all \
--repo_env=BAZEL_LLVM_IFS=llvm-ifs-14 \
&> "$TEST_log" || fail "Build failed"
expect_log "action 'Compiling pkg/lib.c'"
expect_log "action 'Linking pkg/liblib.so'"
expect_log "action 'Linking pkg/test'"
expect_log "func(): 42"
[[ -f ${PRODUCT_NAME}-bin/pkg/liblib.ifso ]] || fail "liblib.ifso not found"

# Only modify the implementation of func(). This should not trigger a relink
# as the .ifso file isn't modified.
cat > pkg/lib.c <<'EOF'
#include "lib.h"
int func() { return 43; }
EOF
bazel test //pkg:test -s --test_output=all \
--repo_env=BAZEL_LLVM_IFS=llvm-ifs-14 \
&> "$TEST_log" || fail "Build failed"
expect_log "action 'Compiling pkg/lib.c'"
expect_log "action 'Linking pkg/liblib.so'"
expect_not_log "action 'Linking pkg/test'"
expect_log "func(): 43"
}

function test_ifso_symbol_versioning() {
type -P llvm-ifs-14 || return 0

mkdir pkg
cat > pkg/BUILD <<'EOF'
cc_library(
name = "lib",
srcs = ["lib.c"],
hdrs = ["lib.h"],
additional_linker_inputs = ["version_script.lds"],
linkopts = ["-Wl,--version-script,$(execpath version_script.lds)"],
)
cc_test(
name = "test",
srcs = ["test.c"],
deps = [":lib"],
)
EOF
cat > pkg/version_script.lds <<'EOF'
v1 {};
v2 {};
EOF
cat > pkg/lib.c <<'EOF'
#include "lib.h"
__asm__(".symver old_func, func@@v1, remove");
int old_func() { return 42; }
__asm__(".symver new_func, func@v2, remove");
int new_func() { return 43; }
EOF
cat > pkg/lib.h <<'EOF'
int func();
EOF
cat > pkg/test.c <<'EOF'
#include "lib.h"
#include "stdio.h"
int main() {
printf("func(): %d\n", func());
}
EOF

bazel test //pkg:test -s --test_output=all \
--repo_env=BAZEL_LLVM_IFS=llvm-ifs-14 \
&> "$TEST_log" || fail "Build failed"
expect_log "action 'Compiling pkg/lib.c'"
expect_log "action 'Linking pkg/liblib.so'"
expect_log "action 'Linking pkg/test'"
expect_log "func(): 42"
[[ -f ${PRODUCT_NAME}-bin/pkg/liblib.ifso ]] || fail "liblib.ifso not found"

# Only modify the default symbol version of func(). This results in a
# different implementation being at runtime after a relink. Without the
# relink, the runtime behavior will be incorrect.
cat > pkg/lib.c <<'EOF'
#include "lib.h"
__asm__(".symver old_func, func@v1, remove");
int old_func() { return 42; }
__asm__(".symver new_func, func@@v2, remove");
int new_func() { return 43; }
EOF
bazel test //pkg:test -s --test_output=all \
--repo_env=BAZEL_LLVM_IFS=llvm-ifs-14 \
&> "$TEST_log" || fail "Build failed"
expect_log "action 'Compiling pkg/lib.c'"
expect_log "action 'Linking pkg/liblib.so'"
expect_log "action 'Linking pkg/test'"
expect_log "func(): 43"
}

run_suite "cc_integration_test"
2 changes: 1 addition & 1 deletion src/test/tools/bzlmod/MODULE.bazel.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 14 additions & 3 deletions tools/cpp/build_interface_so
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
#!/bin/bash
#!/bin/sh

if [[ $# != 2 ]]; then
set -eu

if [ $# != 2 ]; then
echo "Usage: $0 <so> <interface so>" 1>&2
exit 1
fi

exec cp $1 $2
has_versioned_symbol() {
"$NM" --dynamic --format=just-symbols --no-sort "$1" \
| grep --quiet --max-count=1 --fixed-strings @
}

if has_versioned_symbol "$1"; then
exec cp "$1" "$2"
else
"$LLVM_IFS" --input-format=ELF "$1" --output-elf "$2"
fi
16 changes: 14 additions & 2 deletions tools/cpp/unix_cc_configure.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ def _get_tool_paths(repository_ctx, overriden_tools):
"ld",
"llvm-cov",
"llvm-profdata",
"llvm-ifs",
"cpp",
"gcc",
"dwp",
Expand Down Expand Up @@ -373,6 +374,14 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overriden_tools):
warn = True,
silent = True,
)
overriden_tools["llvm-ifs"] = _find_generic(
repository_ctx,
"llvm-ifs",
"BAZEL_LLVM_IFS",
overriden_tools,
warn = True,
silent = True,
)
overriden_tools["ar"] = _find_generic(
repository_ctx,
"ar",
Expand Down Expand Up @@ -545,7 +554,10 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overriden_tools):
"%{cc_toolchain_identifier}": cc_toolchain_identifier,
"%{name}": cpu_value,
"%{modulemap}": ("\":module.modulemap\"" if generate_modulemap else "None"),
"%{cc_compiler_deps}": get_starlark_list([":builtin_include_directory_paths"] + (
"%{cc_compiler_deps}": get_starlark_list([
":builtin_include_directory_paths",
"@bazel_tools//tools/cpp:link_dynamic_library",
] + (
[":cc_wrapper"] if darwin else []
)),
"%{compiler}": escape_string(get_env_var(
Expand Down Expand Up @@ -591,7 +603,7 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overriden_tools):
False,
)),
"%{tool_paths}": ",\n ".join(
['"%s": "%s"' % (k, v) for k, v in tool_paths.items()],
['"%s": "%s"' % (k, v) for k, v in tool_paths.items() if v != None],
),
"%{cxx_builtin_include_directories}": get_starlark_list(builtin_include_directories),
"%{compile_flags}": get_starlark_list(
Expand Down
Loading
Loading