Skip to content
Merged
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
61 changes: 61 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ jobs:

- run: cargo +nightly fmt --all -- --check

- name: Install libbpf-rs prerequisites
run: |
set -euxo pipefail

sudo apt update
sudo apt -y install autopoint

- run: ./clippy.sh

# On the `aya-rs/aya` repository, regenerate the public API on a schedule.
Expand Down Expand Up @@ -102,7 +109,19 @@ jobs:
with:
target: ${{ matrix.arch }}

- name: Install libbpf-rs prerequisites
run: |
set -euxo pipefail

sudo apt update
sudo apt -y install autopoint

- name: Build
env:
# Fix for 32-bit ARM: Ubuntu 24.04 GCC defines _TIME_BITS=64 as a built-in,
# which requires _FILE_OFFSET_BITS=64 (glibc requirement). We must unset
# _TIME_BITS first because the built-in is seen before -D flags by libbpf-sys.
CFLAGS_armv7_unknown_linux_gnueabi: "-U_TIME_BITS -D_FILE_OFFSET_BITS=64"
run: |
set -euxo pipefail
cargo hack build --all-targets --feature-powerset \
Expand Down Expand Up @@ -262,7 +281,12 @@ jobs:
sudo udevadm trigger --name-match=kvm || true # kvm is not available on arm64.

sudo apt update
# linux-libc-dev provides kernel UAPI headers for libbpf-sys cross-compilation.
#
# See https://github.com/libbpf/libbpf-sys/issues/137.
sudo apt -y install \
autopoint \
linux-libc-dev \
liblzma-dev \
lynx \
musl-tools \
Expand Down Expand Up @@ -370,10 +394,47 @@ jobs:
if: runner.os == 'Linux' && matrix.skip-local != true
run: cargo xtask integration-test local

# macOS cross-compilation fixes for libbpf-sys vendored dependencies.
#
# See https://github.com/libbpf/libbpf-sys/issues/137.
- name: Set cross-compilation env for macOS
if: runner.os == 'macOS'
run: |
# Add make wrapper that overrides AR/RANLIB for zlib build.
echo "${{ github.workspace }}/ci/bin" >> $GITHUB_PATH
# Export env vars, stripping comments and quotes (shell quoting breaks GITHUB_ENV).
grep -v '^#' ci/libbpf-sys.env ci/macos-toolchain.env | grep -v '^$' | sed 's/"//g' >> $GITHUB_ENV

# libbpf-sys cross-compilation workarounds:
# - linux-libc-dev provides kernel UAPI headers (installed above)
# - LIBBPF_SYS_EXTRA_CFLAGS adds -idirafter for cross-compile header paths
# - ac_cv_search_* variables from ci/libbpf-sys.env skip autoconf checks
#
# See https://github.com/libbpf/libbpf-sys/issues/137.
- name: Run virtualized integration tests
if: matrix.download-kernel-images != 'local'
run: |
set -euxo pipefail

# Source cross-compilation env vars for libbpf-sys vendored dependencies.
set -a
. ci/libbpf-sys.env
if [ "$(uname -s)" = "Darwin" ]; then
. ci/macos-toolchain.env
fi
set +a

arch=$(uname -m)
libbpf_sys_extra_cflags=(-idirafter "/usr/include/${arch}-linux-gnu" -idirafter /usr/include)
if [ "${arch}" = aarch64 ]; then
libbpf_sys_extra_cflags+=(-mno-outline-atomics)
fi
export LIBBPF_SYS_EXTRA_CFLAGS="${libbpf_sys_extra_cflags[@]}"

# libbpf-sys forwards LIBBPF_SYS_EXTRA_CFLAGS to libbpf, but the
# vendored elfutils build ignores it and only honors CFLAGS/CPPFLAGS.
export CFLAGS='-I${{ github.workspace }}/ci/headers'

find test/.tmp -name '*.deb' -print0 | sort -Vz | xargs -t -0 \
cargo xtask integration-test vm --cache-dir test/.tmp \
--github-api-token ${{ secrets.GITHUB_TOKEN }}
Expand Down
5 changes: 5 additions & 0 deletions Brewfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ brew "lynx"
brew "pkg-config"
brew "qemu"

# Required by libbpf-sys vendored dependencies.
brew "autoconf"
brew "automake"
brew "gawk"

# macOS provides only dynamic zlib. Install the static one.
brew "zlib"

Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ futures = { version = "0.3.28", default-features = false }
glob = { version = "0.3.0", default-features = false }
hashbrown = { version = "0.16.0", default-features = false }
indoc = { version = "2.0", default-features = false }
libbpf-rs = { version = "0.25", default-features = false }
libc = { version = "0.2.105", default-features = false }
log = { version = "0.4", default-features = false }
network-types = { version = "0.1.0", default-features = false }
Expand Down
4 changes: 0 additions & 4 deletions aya-obj/src/btf/btf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,6 @@ pub enum BtfError {
/// unable to get symbol name
#[error("Unable to get symbol name")]
InvalidSymbolName,

/// BTF map wrapper's layout is unexpected
#[error("BTF map wrapper's layout is unexpected: {0:?}")]
UnexpectedBtfMapWrapperLayout(Struct),
}

/// Available BTF features
Expand Down
43 changes: 0 additions & 43 deletions aya-obj/src/obj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1267,49 +1267,6 @@ fn parse_btf_map_def(btf: &Btf, info: &DataSecEntry) -> Result<(String, BtfMapDe
}
};

// In aya-ebpf, the BTF map definition types are not used directly in the
// map variables. Instead, they are wrapped in two nested types:
//
// * A struct representing the map type (e.g., `Array`, `HashMap`) that
// provides methods for interacting with the map type (e.g.
// `HashMap::get`, `RingBuf::reserve`).
// * It has a single field with name `__0`.
// * An `UnsafeCell`, which informs the Rust compiler that the type is
// thread-safe and can be safely mutated even as a global variable. The
// kernel guarantees map operation safety.
// * It has a single field with name `value`.
//
// Therefore, the traversal to the actual map definition looks like:
//
// HashMap -> __0 -> value
let mut s = s;
for (index, expected_field_name) in ["__0", "value"].into_iter().enumerate() {
match s.members.as_slice() {
[m] => {
let field_name = btf.string_at(m.name_offset)?;
if field_name.as_ref() != expected_field_name {
return Err(BtfError::UnexpectedBtfMapWrapperLayout(s.clone()));
}
s = match btf.type_by_id(m.btf_type)? {
BtfType::Struct(s) => s,
_ => {
return Err(BtfError::UnexpectedBtfType {
type_id: m.btf_type,
});
}
};
}
// If the first wrapper level is missing, use the original struct.
_ => {
if index == 0 {
break;
} else {
return Err(BtfError::UnexpectedBtfMapWrapperLayout(s.clone()));
}
}
}
}

for m in &s.members {
match btf.string_at(m.name_offset)?.as_ref() {
"type" => {
Expand Down
8 changes: 8 additions & 0 deletions ci/bin/make
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env sh

set -eux

# Wrapper to override AR/RANLIB for cross-compilation from macOS to Linux musl.
# zlib's configure sets AR=libtool on Darwin (Mach-O format, not ELF).
# See https://github.com/madler/zlib/issues/331
exec /usr/bin/make AR=x86_64-linux-musl-ar ARFLAGS=rcs RANLIB=x86_64-linux-musl-ranlib "$@"
42 changes: 42 additions & 0 deletions ci/headers/argp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#ifndef AYA_CI_STUB_ARGP_H
Comment thread
tamird marked this conversation as resolved.
#define AYA_CI_STUB_ARGP_H

#include <stdio.h>

typedef int error_t;

struct argp_option {
const char *name;
int key;
const char *arg;
int flags;
const char *doc;
int group;
};

struct argp_state;

typedef error_t (*argp_parser_t)(int key, char *arg, struct argp_state *state);

struct argp {
const struct argp_option *options;
argp_parser_t parser;
const char *args_doc;
const char *doc;
const void *children;
void *help_filter;
const char *argp_domain;
};

struct argp_state {
const char *name;
};

#define OPTION_ARG_OPTIONAL 0x1
#define ARGP_HELP_SEE 0x40
#define ARGP_ERR_UNKNOWN 1

int argp_help(const struct argp *argp, FILE *stream, unsigned int flags,
char *name);

#endif /* AYA_CI_STUB_ARGP_H */
6 changes: 6 additions & 0 deletions ci/libbpf-sys.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Autoconf cache variables to skip configure checks for libraries unavailable
# during cross-compilation.
ac_cv_search_argp_parse="none required"
ac_cv_search__obstack_free="none required"
ac_cv_search_gzdirect="none required"
ac_cv_search_fts_close="none required"
4 changes: 4 additions & 0 deletions ci/macos-toolchain.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Target-specific toolchain for x86_64 musl cross-compilation on macOS.
# cc-rs checks AR_{target} before AR, so these only apply for x86_64-unknown-linux-musl.
AR_x86_64_unknown_linux_musl=x86_64-linux-musl-ar
RANLIB_x86_64_unknown_linux_musl=x86_64-linux-musl-ranlib
16 changes: 16 additions & 0 deletions clippy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@

set -eux

# macOS cross-compilation fixes for libbpf-sys vendored dependencies.
#
# PATH and CFLAGS are set here rather than in libbpf-sys.env because they
# require dynamic absolute paths (the env file only contains static values).
#
# See https://github.com/libbpf/libbpf-sys/issues/137.
if [ "$(uname -s)" = "Darwin" ]; then
script_dir=$(cd "$(dirname "$0")" && pwd)
export PATH="$script_dir/ci/bin:$PATH"
export CFLAGS="-I$script_dir/ci/headers"
set -a
. "$script_dir/ci/libbpf-sys.env"
. "$script_dir/ci/macos-toolchain.env"
set +a
fi

# `-C panic=abort` because "unwinding panics are not supported without std"; integration-ebpf
# contains `#[no_std]` binaries.
#
Expand Down
41 changes: 18 additions & 23 deletions ebpf/aya-ebpf/src/btf_maps/array.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,29 @@
use core::{borrow::Borrow, cell::UnsafeCell, ptr::NonNull};
use core::{borrow::Borrow, ptr::NonNull};

use crate::{bindings::bpf_map_type::BPF_MAP_TYPE_ARRAY, btf_map_def, cty::c_long, insert, lookup};
use crate::{btf_maps::btf_map_def, cty::c_long, insert, lookup};

btf_map_def!(ArrayDef, BPF_MAP_TYPE_ARRAY);

#[repr(transparent)]
pub struct Array<T, const M: usize, const F: usize = 0>(UnsafeCell<ArrayDef<u32, T, M, F>>);

unsafe impl<T: Sync, const M: usize, const F: usize> Sync for Array<T, M, F> {}

impl<T, const M: usize, const F: usize> Array<T, M, F> {
/// Creates a new [`Array`] instance with elements of type `T`, maximum
/// capacity of `M` and additional flags `F`.
btf_map_def!(
/// A BTF-compatible BPF array map.
///
/// This map type stores elements of type `T` indexed by `u32` keys.
///
/// # Example
///
/// ```rust
/// use aya_ebpf::{btf_maps::Array, macros::btf_map};
///
/// #[btf_map]
/// static ARRAY: Array<u32, 10 /* max_elements */, 0> = Array::new();
/// static ARRAY: Array<u32, 10, 0> = Array::new();
/// ```
#[expect(
clippy::new_without_default,
reason = "BPF maps are always used as static variables, therefore this method has to be `const`. `Default::default` is not `const`."
)]
pub const fn new() -> Self {
Self(UnsafeCell::new(ArrayDef::new()))
}

pub struct Array<T; const MAX_ENTRIES: usize, const FLAGS: usize = 0>,
map_type: BPF_MAP_TYPE_ARRAY,
max_entries: MAX_ENTRIES,
map_flags: FLAGS,
key_type: u32,
value_type: T,
);

impl<T, const MAX_ENTRIES: usize, const FLAGS: usize> Array<T, MAX_ENTRIES, FLAGS> {
#[inline(always)]
pub fn get(&self, index: u32) -> Option<&T> {
unsafe { self.lookup(index).map(|p| p.as_ref()) }
Expand All @@ -46,12 +41,12 @@ impl<T, const M: usize, const F: usize> Array<T, M, F> {

#[inline(always)]
unsafe fn lookup(&self, index: u32) -> Option<NonNull<T>> {
lookup(self.0.get().cast(), &index)
lookup(self.as_ptr(), &index)
}

/// Sets the value of the element at the given index.
#[inline(always)]
pub fn set(&self, index: u32, value: impl Borrow<T>, flags: u64) -> Result<(), c_long> {
insert(self.0.get().cast(), &index, value.borrow(), flags)
insert(self.as_ptr(), &index, value.borrow(), flags)
}
}
Loading