From 613aaf8d7ad42e8d5bffbf0ee8e84b99a43052fe Mon Sep 17 00:00:00 2001 From: Michal Rostecki Date: Wed, 13 Mar 2024 20:22:18 +0100 Subject: [PATCH 1/3] integration-test: Set `rust-lld` as a linker only on macOS The recommendation (coming from rust-lang/rust#130062) for Linux hosts is using C compiler driver as a linker, which is able to find system-wide libraries. Using linker binaries directly in `-C linker` (e.g. `-C linker=rust-lld`) often results in errors like: ``` cargo:warning=error: linking with `rust-lld` failed: exit status: 1ger, ppv-lite86, libc... cargo:warning= | cargo:warning= = note: LC_ALL="C" PATH="/home/vadorovsky/.rustup/toolchains/stable-x86_64-un cargo:warning= = note: rust-lld: error: unable to find library -lgcc_s cargo:warning= rust-lld: error: unable to find library -lc cargo:warning= cargo:warning= cargo:warning= cargo:warning=error: aborting due to 1 previous error ``` Not touching the linker settings is usually the best approach for Linux systems. Native builds pick up the default C toolchain. Cross builds default to GCC cross wrapper, but that's easy to supress with clang and lld using RUSTFLAGS. However, `-C linker=rust-lld` still works the best on macOS, where Rust toolchains come with libc and runtime library and there is no need to link any system libraries. Keep setting it only for macOS. Fixes #907 --- xtask/src/run.rs | 54 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/xtask/src/run.rs b/xtask/src/run.rs index c92d08d59..b7e835781 100644 --- a/xtask/src/run.rs +++ b/xtask/src/run.rs @@ -54,12 +54,60 @@ pub fn build(target: Option<&str>, f: F) -> Result> where F: FnOnce(&mut Command) -> &mut Command, { - // Always use rust-lld and -Zbuild-std in case we're cross-compiling. let mut cmd = Command::new("cargo"); cmd.args(["build", "--message-format=json"]); if let Some(target) = target { - let config = format!("target.{target}.linker = \"rust-lld\""); - cmd.args(["--target", target, "--config", &config]); + cmd.args(["--target", target]); + // During a cross build, it's important to pick the correct linker. + // + // On Linux, C compiler driver should be always used as the linker. + // The C compiler eventually ends up calling the linker binary (e.g. + // ld, lld), but before doing that, it figures out the appropiate + // linker flags, which include the system library paths. Calling linker + // binaries directly results in them not being able to find system + // libraries (like libc or runtime library), which can manifest in + // errors like `unable to find library -lgcc_s`. + // + // The issue was discussed with the Rust maintainers[0] and the + // consensus is to always use `-C linker` to specify the C compiler + /// (e.g. `-C linker=gcc`, `-C linker=clang`). Choice of a specific + // linker (like ldd or mold) can be done with `-C link-arg=-fuse-ld=`. + // + // However, the same doesn't hold true for macOS. Cross toolchains for + // Linux targets on macOS hosts, provided by rustup, are self-contained, + // come with libc, runtime library and don't depend on any system + // libraries. Therefore, direct usage of rust-lld through + // `-C linker=rust-lld` works fine, because rust-lld is able to find + // libc and runtime in rustup's toolchain. + // + // Using system-wide compiler (clang) on macOS would take the opposite + // effect than on Linux. The system compiler would be the one not being + // able to find the Linux-compatible libc and runtime + // + // To sum it up, this is the way of determining the linker we follow: + // + // - On Linux, use a C compiler for the cross target. + // - On macOS, use rust-lld directly. + // + // The first point is already covered by the configuration in + // `.cargo/config.toml`, which uses cross GCC compilers as linkers for + // popular non-x86_64 targets (e.g. aarch64-linux-musl-gcc). People + // who want to use a different compiler (e.g. clang), can overwrite + // RUSTFLAGS. Set of flags like `-C linker=clang + // -C link-arg=--target=aarch64-unknown-linux-musl + // -C link-arg=-fuse-ld=lld` should result in the build which uses only + // LLVM and has no dependency on GCC. mold can be used with + // `-C link-arg=-fuse-ld=mold` + // + // To cover the macOS case, we explicitly set the linker to rust-lld, + // ignoring the default configuration from `.cargo/config.toml`. + // + // [0] https://github.com/rust-lang/rust/issues/130062 + #[cfg(target_os = "macos")] + { + let config = format!("target.{target}.linker = \"rust-lld\""); + cmd.args(["--config", &config]); + } } f(&mut cmd); From a77399393b04634cc289b676a63711c5816e46e9 Mon Sep 17 00:00:00 2001 From: Michal Rostecki Date: Wed, 27 Nov 2024 11:50:48 +0100 Subject: [PATCH 2/3] test(aya): iter: Disable the init system assertion in containers It's hard to predict what's the PID of the first process in a container. Use this assertion only on non-containerized systems. --- test/integration-test/src/tests/iter.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/test/integration-test/src/tests/iter.rs b/test/integration-test/src/tests/iter.rs index 3325ad2e4..388656a10 100644 --- a/test/integration-test/src/tests/iter.rs +++ b/test/integration-test/src/tests/iter.rs @@ -1,4 +1,4 @@ -use std::io::BufRead; +use std::{env, io::BufRead, path::Path}; use aya::{programs::Iter, Btf, Ebpf}; use test_log::test; @@ -20,11 +20,15 @@ fn iter_task() { let line_init = lines.next().unwrap().unwrap(); assert_eq!(line_title, "tgid pid name"); - let expected_values = ["1 1 init", "1 1 systemd"]; - assert!( - expected_values.contains(&line_init.as_str()), - "Unexpected line_init value: '{}', expected one of: {:?}", - line_init, - expected_values - ); + // It's hard to predict what's the PID of the first process in a container. + // Use this assertion only on non-containerized systems. + if !Path::new("/.dockerenv").exists() && env::var_os("container").is_none() { + let expected_values = ["1 1 init", "1 1 systemd"]; + assert!( + expected_values.contains(&line_init.as_str()), + "Unexpected line_init value: '{}', expected one of: {:?}", + line_init, + expected_values + ); + } } From 9e1d8b4cf97a9acd10b1dcd1769f370358fc62db Mon Sep 17 00:00:00 2001 From: Michal Rostecki Date: Thu, 10 Oct 2024 09:47:47 +0200 Subject: [PATCH 3/3] ci: Run integration tests on Alpine This way we are making sure that the integration tests infra doesn't regress on musl environments. --- .github/workflows/ci.yml | 78 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 05778c2c2..6e354f508 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -202,19 +202,69 @@ jobs: - target: x86_64-unknown-linux-gnu # We don't use ubuntu-latest because we care about the apt packages available. os: ubuntu-22.04 + - target: x86_64-unknown-linux-musl + os: ubuntu-22.04 + container: + image: docker.io/alpine:3.20 + options: --privileged -v /sys/fs/bpf:/sys/fs/bpf -v /sys/kernel:/sys/kernel runs-on: ${{ matrix.os }} + container: ${{ matrix.container }} steps: - - uses: actions/checkout@v4 - with: - submodules: recursive + - name: Install prerequisites + if: runner.os == 'Linux' && contains(matrix.container.image, 'alpine') + # bash is used in the `rust-toolchain` action. + # + # clang and make are needed for building the C eBPF programs, which are + # part of the integration tests. + # + # curl, file and jq are used in the steps below. + # + # dpkg is used to unpack the kernel images from .deb packages, which we + # use for virtualized tests. lynx is used to download them. + # + # gcc is needed as a linker, it also provides the runtime library, both + # needed by Rust. + # + # git is needed for the `checkout` action. + # + # libstdc++ is a dependency of llvm-sys, which is a dependency of + # bpf-linker. + # + # musl-dev provides CRT objects, which are linked by Rust. + # + # QEMU is used to run virtualized integration tests. + # + # sudo is needed by integration tests. + run: | + set -euxo pipefail + apk add \ + bash \ + clang \ + curl \ + dpkg \ + file \ + gcc \ + git \ + jq \ + libstdc++-dev \ + lynx \ + make \ + musl-dev \ + qemu-system-arm \ + qemu-system-x86_64 \ + sudo - name: Install prerequisites - if: runner.os == 'Linux' + if: runner.os == 'Linux' && matrix.container == '' # 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 which is referenced by libbpf. # + # lynx is used to download kernel images. + # + # QEMU is used to run virtualized integration tests. + # # [0] https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2204-Readme.md # # [1] https://github.com/llvm/llvm-project/commit/dc1c43d @@ -224,6 +274,26 @@ jobs: sudo apt -y install gcc-multilib lynx qemu-system-{arm,x86} echo /usr/lib/llvm-15/bin >> $GITHUB_PATH + - uses: actions/checkout@v4 + with: + submodules: recursive + + # When running in a container as root, git throws the following error: + # + # fatal: detected dubious ownership in repository at '/__w/aya/aya' + # + # Which makes a lot of sense, not running regular git commands as root is + # a good thing. However, using a container image with a regular user + # results in permission errors thrown by the runner binary.[0] It's most + # likely because of a host volume mount which is owned by root. It would + # be great to make it work, but it's not going to be trivial and might + # require changes in the runner code itself. + # + # [0] https://github.com/aya-rs/aya/actions/runs/12034434029/job/33550963904 + - name: Mark the directory as safe for git + if: matrix.container != '' + run: git config --global --add safe.directory /__w/aya/aya + - name: Install prerequisites if: runner.os == 'macOS' # The xargs shipped on macOS always exits 0 with -P0, so we need GNU findutils.