Skip to content
Draft
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
52 changes: 31 additions & 21 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,14 @@ jobs:
fail-fast: false
matrix:
include:
- rust: 1.86.0
llvm-version: 19
llvm-from: apt
exclude-features: default,llvm-20,llvm-21,rust-llvm-20,rust-llvm-21
- rust: 1.89.0
- rust: 1.90.0
llvm-version: 20
llvm-from: apt
exclude-features: default,llvm-19,llvm-21,rust-llvm-19,rust-llvm-21
- rust: 1.91.0
llvm-version: 21
llvm-from: apt
exclude-features: default,llvm-19,llvm-20,rust-llvm-19,rust-llvm-20
- rust: beta
llvm-version: 21
llvm-from: apt
Expand All @@ -75,7 +75,8 @@ jobs:

env:
RUST_BACKTRACE: full
LLVM_FEATURES: llvm-${{ matrix.llvm-version }},llvm-sys-${{ matrix.llvm-version }}/force-dynamic
LLVM_FEATURES_DYNAMIC: llvm-${{ matrix.llvm-version }},llvm-sys-${{ matrix.llvm-version }}/force-dynamic
LLVM_FEATURES_STATIC: llvm-${{ matrix.llvm-version }},llvm-sys-${{ matrix.llvm-version }}/force-static

steps:
- uses: actions/checkout@v5
Expand All @@ -96,28 +97,28 @@ jobs:
run: cargo install btfdump

- name: Install prerequisites
# ubuntu-22.04 comes with clang 13-15[0]; support for signed and 64bit
# enum values was added in clang 15[1] which isn't in `$PATH`.
#
# gcc-multilib provides at least <asm/types.h> which is referenced by libbpf.
#
# [0] https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2204-Readme.md
#
# [1] https://github.com/llvm/llvm-project/commit/dc1c43d
run: |
set -euxo pipefail
sudo apt update
sudo apt -y install gcc-multilib
echo /usr/lib/llvm-15/bin >> $GITHUB_PATH

- name: Install LLVM
if: matrix.llvm-from == 'apt'
- name: Install clang
# We use clang in compiletests to test linking of bitcode produced by
# both C and Rust. The major version of clang must match the version of
# LLVM that bpf-linker is using.
run: |
set -euxo pipefail
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc
echo -e deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-${{ matrix.llvm-version }} main | sudo tee /etc/apt/sources.list.d/llvm.list

sudo apt update
sudo apt -y install clang-${{ matrix.llvm-version }}

- name: Install LLVM libraries and headers
if: matrix.llvm-from == 'apt'
run: |
set -euxo pipefail
# TODO(vadorovsky): Remove the requirement of libpolly.
#
# Packages from apt.llvm.org are being built all at once, with one
Expand Down Expand Up @@ -165,13 +166,13 @@ jobs:
run: |
cargo hack check --feature-powerset \
--exclude-features ${{ matrix.exclude-features }} \
--features ${{ env.LLVM_FEATURES }}
--features ${{ env.LLVM_FEATURES_DYNAMIC }}

- name: Build
run: |
cargo hack build --feature-powerset \
--exclude-features ${{ matrix.exclude-features }} \
--features ${{ env.LLVM_FEATURES }}
--features ${{ env.LLVM_FEATURES_DYNAMIC }}

# Toolchains provided by rustup include standard library artifacts
# only for Tier 1 targets, which do not include BPF targets.
Expand All @@ -185,7 +186,7 @@ jobs:
run: |
RUSTC_BOOTSTRAP=1 cargo hack test --feature-powerset \
--exclude-features ${{ matrix.exclude-features }} \
--features ${{ env.LLVM_FEATURES }}
--features ${{ env.LLVM_FEATURES_DYNAMIC }}

# To make things easier for package maintainers, the step of building a
# custom sysroot can be skipped by setting the `BPFEL_SYSROOT_DIR`
Expand All @@ -209,7 +210,7 @@ jobs:

BPFEL_SYSROOT_DIR="$BPFEL_SYSROOT_DIR" cargo hack test --feature-powerset \
--exclude-features ${{ matrix.exclude-features }} \
--features ${{ env.LLVM_FEATURES }}
--features ${{ env.LLVM_FEATURES_DYNAMIC }}

- uses: actions/checkout@v5
if: matrix.rust == 'nightly'
Expand All @@ -220,7 +221,16 @@ jobs:

- name: Install
if: matrix.rust == 'nightly'
run: cargo install --path . --no-default-features --features ${{ env.LLVM_FEATURES }}
# `LD_LIBRARY_PATH` set to a custom LLVM build messes up with clang,
# that is linked to system-wide LLVM with larger configuration than our
# custom build, causing errors like:
# /usr/bin/clang-21: symbol lookup error: /usr/lib/llvm-21/bin/../lib/libclang-cpp.so.21.1:
# undefined symbol: _ZTIN4llvm5MachO13RecordVisitorE, version
# Unset the variable and link the custom LLVM statically.
run: |
set -euxo pipefail
echo "LD_LIBRARY_PATH=" >> $GITHUB_ENV
cargo install --path . --no-default-features --features ${{ env.LLVM_FEATURES_STATIC }}

- name: Run aya integration tests
if: matrix.rust == 'nightly'
Expand Down
7 changes: 7 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ thiserror = { version = "2.0.12" }
tracing = "0.1"

[dev-dependencies]
assert_matches = "1.5.0"
compiletest_rs = { version = "0.11.0" }
regex = { version = "1.11.1", default-features = false }
rustc-build-sysroot = { workspace = true }
Expand Down
63 changes: 58 additions & 5 deletions src/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,29 @@ use llvm_sys::{
use thiserror::Error;
use tracing::{debug, error, info, warn};

use crate::llvm::{self, LLVMContext, LLVMModule, LLVMTargetMachine, MemoryBuffer};
use crate::llvm::{
self, LLVMContext, LLVMModule, LLVMTargetMachine, LlvmVersionDetectionError, MemoryBuffer,
};

#[cfg(feature = "llvm-19")]
const SUPPORTED_LLVM_MAJOR: u32 = 19;
#[cfg(feature = "llvm-20")]
const SUPPORTED_LLVM_MAJOR: u32 = 20;
#[cfg(feature = "llvm-21")]
const SUPPORTED_LLVM_MAJOR: u32 = 21;

#[cfg(any(
all(feature = "llvm-19", not(feature = "rust-llvm-19")),
all(feature = "llvm-20", not(feature = "rust-llvm-20")),
all(feature = "llvm-21", not(feature = "rust-llvm-21")),
))]
const SUGGESTED_FEATURE_PREFIX: &str = "llvm-";
#[cfg(any(
feature = "rust-llvm-19",
feature = "rust-llvm-20",
feature = "rust-llvm-21",
))]
const SUGGESTED_FEATURE_PREFIX: &str = "rust-llvm-";

/// Linker error
#[derive(Debug, Error)]
Expand Down Expand Up @@ -74,6 +96,27 @@ pub enum LinkerError {
/// LLVM cannot create a module for linking.
#[error("failed to create module")]
CreateModuleError,

/// The LLVM version embedded in the input bitcode is not supported.
#[error(
"bitcode {path} was built with LLVM {bitcode_version}, but this bpf-linker
supports LLVM {linker_version}; please re-install bpf-linker with
`cargo install --force bpf-linker --no-default-features --features
{SUGGESTED_FEATURE_PREFIX}{bitcode_version}`"
)]
LlvmVersionMismatch {
path: PathBuf,
bitcode_version: String,
linker_version: u32,
},

/// Failed to determine the LLVM version for a bitcode input.
#[error("failed to determine LLVM version for `{path}`: {kind}")]
LlvmVersionDetectionError {
path: PathBuf,
#[source]
kind: LlvmVersionDetectionError,
},
}

/// BPF Cpu type
Expand Down Expand Up @@ -602,11 +645,21 @@ fn link_reader<'ctx>(
InputType::Archive => panic!("nested archives not supported duh"),
};

if !llvm::link_bitcode_buffer(context, module, &bitcode) {
return Err(LinkerError::LinkModuleError(path.to_owned()));
match llvm::link_bitcode_buffer(context, module, &bitcode, Some(SUPPORTED_LLVM_MAJOR)) {
Ok(true) => Ok(()),
Ok(false) => Err(LinkerError::LinkModuleError(path.to_owned())),
Err(LlvmVersionDetectionError::VersionMismatch {
bitcode_version, ..
}) => Err(LinkerError::LlvmVersionMismatch {
path: path.to_owned(),
bitcode_version,
linker_version: SUPPORTED_LLVM_MAJOR,
}),
Err(kind) => Err(LinkerError::LlvmVersionDetectionError {
path: path.to_owned(),
kind,
}),
}

Ok(())
}

fn create_target_machine(
Expand Down
Loading
Loading