Merge branch 'aya-rs:main' into main

reviewable/pr728/r3
Mohammad Javad Pooladkhay 2 years ago committed by GitHub
commit e99e800390
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -30,6 +30,9 @@ jobs:
with:
tool: cargo-hack,taplo-cli
- name: Check C formatting
run: git ls-files -- '*.c' '*.h' | xargs clang-format --dry-run --Werror
- run: taplo fmt --check
- name: Check formatting
@ -139,8 +142,14 @@ jobs:
--target ${{ matrix.target }} \
-Z build-std=core
build-integration-test:
runs-on: ubuntu-22.04
run-integration-test:
strategy:
fail-fast: false
matrix:
runner:
- macos-12
- ubuntu-22.04
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v3
with:
@ -150,13 +159,12 @@ jobs:
with:
toolchain: nightly
components: rust-src
targets: aarch64-unknown-linux-musl,x86_64-unknown-linux-musl
- uses: Swatinem/rust-cache@v2
- name: bpf-linker
run: cargo install bpf-linker --git https://github.com/aya-rs/bpf-linker.git
- name: Install dependencies
- name: Install prerequisites
if: runner.os == 'Linux'
# ubuntu-22.04 comes with clang 14[0] which doesn't include support for signed and 64bit
# enum values which was added in clang 15[1].
#
@ -171,63 +179,82 @@ jobs:
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 deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy main | sudo tee /etc/apt/sources.list.d/llvm.list
sudo apt-get update
sudo apt-get -y install clang gcc-multilib llvm
sudo apt update
sudo apt -y install clang gcc-multilib llvm locate qemu-system-{arm,x86}
- name: Build
- name: bpf-linker
if: runner.os == 'Linux'
run: cargo install bpf-linker --git https://github.com/aya-rs/bpf-linker.git
- name: Install prerequisites
if: runner.os == 'macOS'
# The xargs shipped on macOS always exits 0 with -P0, so we need GNU findutils.
#
# The tar shipped on macOS doesn't support --wildcards, so we need GNU tar.
#
# The clang shipped on macOS doesn't support BPF, so we need LLVM from brew.
#
# We also need LLVM for bpf-linker, see comment below.
run: |
set -euxo pipefail
mkdir -p integration-test-binaries
# See https://doc.rust-lang.org/cargo/reference/profiles.html for the
# names of the builtin profiles. Note that dev builds "debug" targets.
cargo xtask build-integration-test --cargo-arg=--profile=dev | xargs -I % cp % integration-test-binaries/dev
cargo xtask build-integration-test --cargo-arg=--profile=release | xargs -I % cp % integration-test-binaries/release
- uses: actions/upload-artifact@v3
with:
name: integration-test-binaries
path: integration-test-binaries
brew update
brew install dpkg findutils gnu-tar llvm pkg-config
# Workaround for https://github.com/Homebrew/homebrew-core/pull/139492.
brew reinstall qemu
echo /usr/local/opt/findutils/libexec/gnubin >> $GITHUB_PATH
echo /usr/local/opt/gnu-tar/libexec/gnubin >> $GITHUB_PATH
echo /usr/local/opt/llvm/bin >> $GITHUB_PATH
run-integration-test:
runs-on: macos-latest
needs: ["build-integration-test"]
steps:
- uses: actions/checkout@v3
with:
sparse-checkout: |
test/run.sh
test/cloud-localds
- name: bpf-linker
if: runner.os == 'macOS'
# NB: rustc doesn't ship libLLVM.so on macOS, so disable proxying (default feature).
run: cargo install bpf-linker --git https://github.com/aya-rs/bpf-linker.git --no-default-features
- name: Install Pre-requisites
- name: Download debian kernels
if: runner.arch == 'ARM64'
run: |
brew install qemu gnu-getopt coreutils cdrtools
- name: Cache tmp files
uses: actions/cache@v3
with:
path: |
.tmp/*.qcow2
.tmp/test_rsa
.tmp/test_rsa.pub
key: tmp-files-${{ hashFiles('test/run.sh') }}
- uses: actions/download-artifact@v3
with:
name: integration-test-binaries
path: integration-test-binaries
- name: Run integration tests
set -euxo pipefail
mkdir -p test/.tmp/debian-kernels/arm64
# NB: a 4.19 kernel image for arm64 was not available.
# TODO: enable tests on kernels before 6.0.
# linux-image-5.10.0-23-cloud-arm64-unsigned_5.10.179-3_arm64.deb \
printf '%s\0' \
linux-image-6.1.0-10-cloud-arm64-unsigned_6.1.38-2_arm64.deb \
linux-image-6.4.0-2-cloud-arm64-unsigned_6.4.4-3_arm64.deb \
| xargs -0 -t -P0 -I {} wget -nd -nv -P test/.tmp/debian-kernels/arm64 ftp://ftp.us.debian.org/debian/pool/main/l/linux/{}
- name: Download debian kernels
if: runner.arch == 'X64'
run: |
set -euxo pipefail
find integration-test-binaries -type f -exec chmod +x {} \;
test/run.sh integration-test-binaries
mkdir -p test/.tmp/debian-kernels/amd64
# TODO: enable tests on kernels before 6.0.
# linux-image-4.19.0-21-cloud-amd64-unsigned_4.19.249-2_amd64.deb \
# linux-image-5.10.0-23-cloud-amd64-unsigned_5.10.179-3_amd64.deb \
printf '%s\0' \
linux-image-6.1.0-10-cloud-amd64-unsigned_6.1.38-2_amd64.deb \
linux-image-6.4.0-2-cloud-amd64-unsigned_6.4.4-3_amd64.deb \
| xargs -0 -t -P0 -I {} wget -nd -nv -P test/.tmp/debian-kernels/amd64 ftp://ftp.us.debian.org/debian/pool/main/l/linux/{}
- name: Extract debian kernels
run: |
set -euxo pipefail
find test/.tmp -name '*.deb' -print0 | xargs -t -0 -I {} \
sh -c "dpkg --fsys-tarfile {} | tar -C test/.tmp --wildcards --extract '*vmlinuz*' --file -"
- name: Run integration tests
run: find test/.tmp -name 'vmlinuz-*' | xargs -t cargo xtask integration-test vm
# Provides a single status check for the entire build workflow.
# This is used for merge automation, like Mergify, since GH actions
# has no concept of "when all status checks pass".
# https://docs.mergify.com/conditions/#validating-all-status-checks
build-workflow-complete:
needs: ["lint", "build-test-aya", "build-test-aya-bpf", "run-integration-test"]
needs:
- lint
- build-test-aya
- build-test-aya-bpf
- run-integration-test
runs-on: ubuntu-latest
steps:
- name: Build Complete

@ -6,6 +6,7 @@ members = [
"aya-log-parser",
"aya-obj",
"aya-tool",
"init",
"test/integration-test",
"xtask",
@ -29,6 +30,7 @@ default-members = [
"aya-log-parser",
"aya-obj",
"aya-tool",
"init",
# test/integration-test is omitted; including it in this list causes `cargo test` to run its
# tests, and that doesn't work unless they've been built with `cargo xtask`.
"xtask",
@ -72,8 +74,9 @@ lazy_static = { version = "1", default-features = false }
libc = { version = "0.2.105", default-features = false }
log = { version = "0.4", default-features = false }
netns-rs = { version = "0.1", default-features = false }
num_enum = { version = "0.6", default-features = false }
object = { version = "0.31", default-features = false }
nix = { version = "0.26.2", default-features = false }
num_enum = { version = "0.7", default-features = false }
object = { version = "0.32", default-features = false }
parking_lot = { version = "0.12.0", default-features = false }
proc-macro-error = { version = "1.0", default-features = false }
proc-macro2 = { version = "1", default-features = false }

@ -1,6 +1,6 @@
#![no_std]
use core::{mem, num, ptr};
use core::num::{NonZeroUsize, TryFromIntError};
use num_enum::IntoPrimitive;
@ -86,7 +86,7 @@ pub trait UpperMacFormatter {}
impl UpperMacFormatter for [u8; 6] {}
#[repr(u8)]
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug, IntoPrimitive)]
pub enum RecordField {
Target = 1,
Level,
@ -99,7 +99,7 @@ pub enum RecordField {
/// Types which are supported by aya-log and can be safely sent from eBPF
/// programs to userspace.
#[repr(u8)]
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug, IntoPrimitive)]
pub enum Argument {
DisplayHint,
@ -147,65 +147,42 @@ pub enum DisplayHint {
UpperMac,
}
struct TagLenValue<T, V> {
pub tag: T,
pub value: V,
}
impl<T, V> TagLenValue<T, V>
where
V: IntoIterator<Item = u8>,
<V as IntoIterator>::IntoIter: ExactSizeIterator,
{
pub(crate) fn write(self, mut buf: &mut [u8]) -> Result<usize, ()> {
// Break the abstraction to please the verifier.
if buf.len() > LOG_BUF_CAPACITY {
buf = &mut buf[..LOG_BUF_CAPACITY];
}
let Self { tag, value } = self;
let value = value.into_iter();
let len = value.len();
let wire_len: LogValueLength = value
.len()
.try_into()
.map_err(|num::TryFromIntError { .. }| ())?;
let size = mem::size_of_val(&tag) + mem::size_of_val(&wire_len) + len;
if size > buf.len() {
return Err(());
}
let tag_size = mem::size_of_val(&tag);
unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, tag) };
buf = &mut buf[tag_size..];
unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, wire_len) };
buf = &mut buf[mem::size_of_val(&wire_len)..];
buf.iter_mut().zip(value).for_each(|(dst, src)| {
*dst = src;
});
Ok(size)
}
}
impl<T, V> TagLenValue<T, V> {
#[inline(always)]
pub(crate) fn new(tag: T, value: V) -> TagLenValue<T, V> {
TagLenValue { tag, value }
// Must be inlined, else the BPF backend emits:
//
// llvm: <unknown>:0:0: in function _ZN14aya_log_common5write17hc9ed05433e23a663E { i64, i64 } (i8, ptr, i64, ptr, i64): only integer returns supported
#[inline(always)]
pub(crate) fn write(tag: u8, value: &[u8], buf: &mut [u8]) -> Option<NonZeroUsize> {
let wire_len: LogValueLength = match value.len().try_into() {
Ok(wire_len) => Some(wire_len),
Err(TryFromIntError { .. }) => None,
}?;
let mut size = 0;
macro_rules! copy_from_slice {
($value:expr) => {{
let buf = buf.get_mut(size..)?;
let buf = buf.get_mut(..$value.len())?;
buf.copy_from_slice($value);
size += $value.len();
}};
}
copy_from_slice!(&[tag]);
copy_from_slice!(&wire_len.to_ne_bytes());
copy_from_slice!(value);
NonZeroUsize::new(size)
}
pub trait WriteToBuf {
#[allow(clippy::result_unit_err)]
fn write(self, buf: &mut [u8]) -> Result<usize, ()>;
fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize>;
}
macro_rules! impl_write_to_buf {
($type:ident, $arg_type:expr) => {
impl WriteToBuf for $type {
fn write(self, buf: &mut [u8]) -> Result<usize, ()> {
TagLenValue::new($arg_type, self.to_ne_bytes()).write(buf)
// This need not be inlined because the return value is Option<N> where N is
// mem::size_of<$type>, which is a compile-time constant.
#[inline(never)]
fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
write($arg_type.into(), &self.to_ne_bytes(), buf)
}
}
};
@ -227,46 +204,65 @@ impl_write_to_buf!(f32, Argument::F32);
impl_write_to_buf!(f64, Argument::F64);
impl WriteToBuf for [u8; 16] {
fn write(self, buf: &mut [u8]) -> Result<usize, ()> {
TagLenValue::new(Argument::ArrU8Len16, self).write(buf)
// This need not be inlined because the return value is Option<N> where N is 16, which is a
// compile-time constant.
#[inline(never)]
fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
write(Argument::ArrU8Len16.into(), &self, buf)
}
}
impl WriteToBuf for [u16; 8] {
fn write(self, buf: &mut [u8]) -> Result<usize, ()> {
// This need not be inlined because the return value is Option<N> where N is 16, which is a
// compile-time constant.
#[inline(never)]
fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
let bytes = unsafe { core::mem::transmute::<_, [u8; 16]>(self) };
TagLenValue::new(Argument::ArrU16Len8, bytes).write(buf)
write(Argument::ArrU16Len8.into(), &bytes, buf)
}
}
impl WriteToBuf for [u8; 6] {
fn write(self, buf: &mut [u8]) -> Result<usize, ()> {
TagLenValue::new(Argument::ArrU8Len6, self).write(buf)
// This need not be inlined because the return value is Option<N> where N is 6, which is a
// compile-time constant.
#[inline(never)]
fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
write(Argument::ArrU8Len6.into(), &self, buf)
}
}
impl WriteToBuf for &[u8] {
fn write(self, buf: &mut [u8]) -> Result<usize, ()> {
TagLenValue::new(Argument::Bytes, self.iter().copied()).write(buf)
// Must be inlined, else the BPF backend emits:
//
// llvm: <unknown>:0:0: in function _ZN63_$LT$$RF$$u5b$u8$u5d$$u20$as$u20$aya_log_common..WriteToBuf$GT$5write17h08f30a45f7b9f09dE { i64, i64 } (ptr, i64, ptr, i64): only integer returns supported
#[inline(always)]
fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
write(Argument::Bytes.into(), self, buf)
}
}
impl WriteToBuf for &str {
fn write(self, buf: &mut [u8]) -> Result<usize, ()> {
TagLenValue::new(Argument::Str, self.as_bytes().iter().copied()).write(buf)
// Must be inlined, else the BPF backend emits:
//
// llvm: <unknown>:0:0: in function _ZN54_$LT$$RF$str$u20$as$u20$aya_log_common..WriteToBuf$GT$5write17h7e2d1ccaa758e2b5E { i64, i64 } (ptr, i64, ptr, i64): only integer returns supported
#[inline(always)]
fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
write(Argument::Str.into(), self.as_bytes(), buf)
}
}
impl WriteToBuf for DisplayHint {
fn write(self, buf: &mut [u8]) -> Result<usize, ()> {
// This need not be inlined because the return value is Option<N> where N is 1, which is a
// compile-time constant.
#[inline(never)]
fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
let v: u8 = self.into();
TagLenValue::new(Argument::DisplayHint, v.to_ne_bytes()).write(buf)
write(Argument::DisplayHint.into(), &v.to_ne_bytes(), buf)
}
}
#[allow(clippy::result_unit_err)]
#[doc(hidden)]
#[inline(always)]
#[inline(always)] // This function takes too many arguments to not be inlined.
pub fn write_record_header(
buf: &mut [u8],
target: &str,
@ -275,20 +271,23 @@ pub fn write_record_header(
file: &str,
line: u32,
num_args: usize,
) -> Result<usize, ()> {
) -> Option<NonZeroUsize> {
let level: u8 = level.into();
let mut size = 0;
size += TagLenValue::new(RecordField::Target, target.as_bytes().iter().copied())
.write(&mut buf[size..])?;
size += TagLenValue::new(RecordField::Level, level.to_ne_bytes()).write(&mut buf[size..])?;
size += TagLenValue::new(RecordField::Module, module.as_bytes().iter().copied())
.write(&mut buf[size..])?;
size += TagLenValue::new(RecordField::File, file.as_bytes().iter().copied())
.write(&mut buf[size..])?;
size += TagLenValue::new(RecordField::Line, line.to_ne_bytes()).write(&mut buf[size..])?;
size +=
TagLenValue::new(RecordField::NumArgs, num_args.to_ne_bytes()).write(&mut buf[size..])?;
Ok(size)
macro_rules! write {
($tag:expr, $value:expr) => {{
let buf = buf.get_mut(size..)?;
let len = write($tag.into(), $value, buf)?;
size += len.get();
}};
}
write!(RecordField::Target, target.as_bytes());
write!(RecordField::Level, &level.to_ne_bytes());
write!(RecordField::Module, module.as_bytes());
write!(RecordField::File, file.as_bytes());
write!(RecordField::Line, &line.to_ne_bytes());
write!(RecordField::NumArgs, &num_args.to_ne_bytes());
NonZeroUsize::new(size)
}
#[cfg(test)]

@ -147,8 +147,8 @@ pub(crate) fn log(args: LogArgs, level: Option<TokenStream>) -> Result<TokenStre
match unsafe { &mut ::aya_log_ebpf::AYA_LOG_BUF }.get_ptr_mut(0).and_then(|ptr| unsafe { ptr.as_mut() }) {
None => {},
Some(::aya_log_ebpf::LogBuf { buf }) => {
let _: Result<(), ()> = (|| {
let mut len = ::aya_log_ebpf::write_record_header(
let _: Option<()> = (|| {
let size = ::aya_log_ebpf::write_record_header(
buf,
#target,
#lvl,
@ -157,13 +157,15 @@ pub(crate) fn log(args: LogArgs, level: Option<TokenStream>) -> Result<TokenStre
line!(),
#num_args,
)?;
let mut size = size.get();
#(
let slice = buf.get_mut(len..).ok_or(())?;
len += ::aya_log_ebpf::WriteToBuf::write(#values_iter, slice)?;
let slice = buf.get_mut(size..)?;
let len = ::aya_log_ebpf::WriteToBuf::write(#values_iter, slice)?;
size += len.get();
)*
let record = buf.get(..len).ok_or(())?;
let record = buf.get(..size)?;
unsafe { &mut ::aya_log_ebpf::AYA_LOGS }.output(#ctx, record, 0);
Ok(())
Some(())
})();
}
}

@ -98,7 +98,7 @@ impl BpfLogger {
/// Starts reading log records created with `aya-log-ebpf` and logs them
/// with the default logger. See [log::logger].
pub fn init(bpf: &mut Bpf) -> Result<BpfLogger, Error> {
BpfLogger::init_with_logger(bpf, DefaultLogger {})
BpfLogger::init_with_logger(bpf, log::logger())
}
/// Starts reading log records created with `aya-log-ebpf` and logs them
@ -356,23 +356,6 @@ macro_rules! impl_format_float {
impl_format_float!(f32);
impl_format_float!(f64);
#[derive(Copy, Clone, Debug)]
struct DefaultLogger;
impl Log for DefaultLogger {
fn enabled(&self, metadata: &log::Metadata) -> bool {
log::logger().enabled(metadata)
}
fn log(&self, record: &Record) {
log::logger().log(record)
}
fn flush(&self) {
log::logger().flush()
}
}
#[derive(Error, Debug)]
pub enum Error {
#[error("log event array {} doesn't exist", MAP_NAME)]
@ -584,7 +567,7 @@ mod test {
use aya_log_common::{write_record_header, WriteToBuf};
use log::{logger, Level};
fn new_log(args: usize) -> Result<(usize, Vec<u8>), ()> {
fn new_log(args: usize) -> Option<(usize, Vec<u8>)> {
let mut buf = vec![0; 8192];
let len = write_record_header(
&mut buf,
@ -595,7 +578,7 @@ mod test {
123,
args,
)?;
Ok((len, buf))
Some((len.get(), buf))
}
#[test]
@ -603,7 +586,7 @@ mod test {
testing_logger::setup();
let (mut len, mut input) = new_log(1).unwrap();
len += "test".write(&mut input[len..]).unwrap();
len += "test".write(&mut input[len..]).unwrap().get();
_ = len;
@ -621,8 +604,8 @@ mod test {
testing_logger::setup();
let (mut len, mut input) = new_log(2).unwrap();
len += "hello ".write(&mut input[len..]).unwrap();
len += "test".write(&mut input[len..]).unwrap();
len += "hello ".write(&mut input[len..]).unwrap().get();
len += "test".write(&mut input[len..]).unwrap().get();
_ = len;
@ -640,8 +623,11 @@ mod test {
testing_logger::setup();
let (mut len, mut input) = new_log(2).unwrap();
len += DisplayHint::LowerHex.write(&mut input[len..]).unwrap();
len += [0xde, 0xad].write(&mut input[len..]).unwrap();
len += DisplayHint::LowerHex
.write(&mut input[len..])
.unwrap()
.get();
len += [0xde, 0xad].write(&mut input[len..]).unwrap().get();
_ = len;
@ -659,13 +645,19 @@ mod test {
testing_logger::setup();
let (mut len, mut input) = new_log(5).unwrap();
len += DisplayHint::LowerHex.write(&mut input[len..]).unwrap();
len += [0xde, 0xad].write(&mut input[len..]).unwrap();
len += DisplayHint::LowerHex
.write(&mut input[len..])
.unwrap()
.get();
len += [0xde, 0xad].write(&mut input[len..]).unwrap().get();
len += " ".write(&mut input[len..]).unwrap();
len += " ".write(&mut input[len..]).unwrap().get();
len += DisplayHint::UpperHex.write(&mut input[len..]).unwrap();
len += [0xbe, 0xef].write(&mut input[len..]).unwrap();
len += DisplayHint::UpperHex
.write(&mut input[len..])
.unwrap()
.get();
len += [0xbe, 0xef].write(&mut input[len..]).unwrap().get();
_ = len;
@ -683,9 +675,9 @@ mod test {
testing_logger::setup();
let (mut len, mut input) = new_log(3).unwrap();
len += "default hint: ".write(&mut input[len..]).unwrap();
len += DisplayHint::Default.write(&mut input[len..]).unwrap();
len += 14.write(&mut input[len..]).unwrap();
len += "default hint: ".write(&mut input[len..]).unwrap().get();
len += DisplayHint::Default.write(&mut input[len..]).unwrap().get();
len += 14.write(&mut input[len..]).unwrap().get();
_ = len;
@ -703,9 +695,12 @@ mod test {
testing_logger::setup();
let (mut len, mut input) = new_log(3).unwrap();
len += "lower hex: ".write(&mut input[len..]).unwrap();
len += DisplayHint::LowerHex.write(&mut input[len..]).unwrap();
len += 200.write(&mut input[len..]).unwrap();
len += "lower hex: ".write(&mut input[len..]).unwrap().get();
len += DisplayHint::LowerHex
.write(&mut input[len..])
.unwrap()
.get();
len += 200.write(&mut input[len..]).unwrap().get();
_ = len;
@ -723,9 +718,12 @@ mod test {
testing_logger::setup();
let (mut len, mut input) = new_log(3).unwrap();
len += "upper hex: ".write(&mut input[len..]).unwrap();
len += DisplayHint::UpperHex.write(&mut input[len..]).unwrap();
len += 200.write(&mut input[len..]).unwrap();
len += "upper hex: ".write(&mut input[len..]).unwrap().get();
len += DisplayHint::UpperHex
.write(&mut input[len..])
.unwrap()
.get();
len += 200.write(&mut input[len..]).unwrap().get();
_ = len;
@ -743,10 +741,10 @@ mod test {
testing_logger::setup();
let (mut len, mut input) = new_log(3).unwrap();
len += "ipv4: ".write(&mut input[len..]).unwrap();
len += DisplayHint::Ip.write(&mut input[len..]).unwrap();
len += "ipv4: ".write(&mut input[len..]).unwrap().get();
len += DisplayHint::Ip.write(&mut input[len..]).unwrap().get();
// 10.0.0.1 as u32
len += 167772161u32.write(&mut input[len..]).unwrap();
len += 167772161u32.write(&mut input[len..]).unwrap().get();
_ = len;
@ -764,14 +762,14 @@ mod test {
testing_logger::setup();
let (mut len, mut input) = new_log(3).unwrap();
len += "ipv6: ".write(&mut input[len..]).unwrap();
len += DisplayHint::Ip.write(&mut input[len..]).unwrap();
len += "ipv6: ".write(&mut input[len..]).unwrap().get();
len += DisplayHint::Ip.write(&mut input[len..]).unwrap().get();
// 2001:db8::1:1 as byte array
let ipv6_arr: [u8; 16] = [
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x01,
];
len += ipv6_arr.write(&mut input[len..]).unwrap();
len += ipv6_arr.write(&mut input[len..]).unwrap().get();
_ = len;
@ -789,13 +787,13 @@ mod test {
testing_logger::setup();
let (mut len, mut input) = new_log(3).unwrap();
len += "ipv6: ".write(&mut input[len..]).unwrap();
len += DisplayHint::Ip.write(&mut input[len..]).unwrap();
len += "ipv6: ".write(&mut input[len..]).unwrap().get();
len += DisplayHint::Ip.write(&mut input[len..]).unwrap().get();
// 2001:db8::1:1 as u16 array
let ipv6_arr: [u16; 8] = [
0x2001, 0x0db8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001,
];
len += ipv6_arr.write(&mut input[len..]).unwrap();
len += ipv6_arr.write(&mut input[len..]).unwrap().get();
_ = len;
@ -813,11 +811,14 @@ mod test {
testing_logger::setup();
let (mut len, mut input) = new_log(3).unwrap();
len += "mac: ".write(&mut input[len..]).unwrap();
len += DisplayHint::LowerMac.write(&mut input[len..]).unwrap();
len += "mac: ".write(&mut input[len..]).unwrap().get();
len += DisplayHint::LowerMac
.write(&mut input[len..])
.unwrap()
.get();
// 00:00:5e:00:53:af as byte array
let mac_arr: [u8; 6] = [0x00, 0x00, 0x5e, 0x00, 0x53, 0xaf];
len += mac_arr.write(&mut input[len..]).unwrap();
len += mac_arr.write(&mut input[len..]).unwrap().get();
_ = len;
@ -835,11 +836,14 @@ mod test {
testing_logger::setup();
let (mut len, mut input) = new_log(3).unwrap();
len += "mac: ".write(&mut input[len..]).unwrap();
len += DisplayHint::UpperMac.write(&mut input[len..]).unwrap();
len += "mac: ".write(&mut input[len..]).unwrap().get();
len += DisplayHint::UpperMac
.write(&mut input[len..])
.unwrap()
.get();
// 00:00:5E:00:53:AF as byte array
let mac_arr: [u8; 6] = [0x00, 0x00, 0x5e, 0x00, 0x53, 0xaf];
len += mac_arr.write(&mut input[len..]).unwrap();
len += mac_arr.write(&mut input[len..]).unwrap().get();
_ = len;

@ -1,11 +1,11 @@
#include <asm-generic/socket.h>
#include <linux/bpf.h>
#include <linux/btf.h>
#include <linux/perf_event.h>
#include <linux/if_link.h>
#include <linux/rtnetlink.h>
#include <asm-generic/socket.h>
#include <linux/pkt_sched.h>
#include <linux/perf_event.h>
#include <linux/pkt_cls.h>
#include <linux/pkt_sched.h>
#include <linux/rtnetlink.h>
/* workaround the fact that bindgen can't parse the IOC macros */
int AYA_PERF_EVENT_IOC_ENABLE = PERF_EVENT_IOC_ENABLE;

@ -17,7 +17,7 @@ use crate::{
info::{FuncSecInfo, LineSecInfo},
relocation::Relocation,
Array, BtfEnum, BtfKind, BtfMember, BtfType, Const, Enum, FuncInfo, FuncLinkage, Int,
IntEncoding, LineInfo, Struct, Typedef, VarLinkage,
IntEncoding, LineInfo, Struct, Typedef, Union, VarLinkage,
},
generated::{btf_ext_header, btf_header},
util::{bytes_of, HashMap},
@ -171,6 +171,7 @@ pub struct BtfFeatures {
btf_float: bool,
btf_decl_tag: bool,
btf_type_tag: bool,
btf_enum64: bool,
}
impl BtfFeatures {
@ -182,6 +183,7 @@ impl BtfFeatures {
btf_float: bool,
btf_decl_tag: bool,
btf_type_tag: bool,
btf_enum64: bool,
) -> Self {
BtfFeatures {
btf_func,
@ -190,6 +192,7 @@ impl BtfFeatures {
btf_float,
btf_decl_tag,
btf_type_tag,
btf_enum64,
}
}
@ -227,6 +230,11 @@ impl BtfFeatures {
pub fn btf_kind_func_proto(&self) -> bool {
self.btf_func && self.btf_decl_tag
}
/// Returns true if the BTF_KIND_ENUM64 is supported.
pub fn btf_enum64(&self) -> bool {
self.btf_enum64
}
}
/// Bpf Type Format metadata.
@ -467,83 +475,106 @@ impl Btf {
buf
}
// This follows the same logic as libbpf's bpf_object__sanitize_btf() function.
// https://github.com/libbpf/libbpf/blob/05f94ddbb837f5f4b3161e341eed21be307eaa04/src/libbpf.c#L2701
//
// Fixup: The loader needs to adjust values in the BTF before it's loaded into the kernel.
// Sanitize: Replace an unsupported BTF type with a placeholder type.
//
// In addition to the libbpf logic, it performs some fixups to the BTF generated by bpf-linker
// for Aya programs. These fixups are gradually moving into bpf-linker itself.
pub(crate) fn fixup_and_sanitize(
&mut self,
section_infos: &HashMap<String, (SectionIndex, u64)>,
symbol_offsets: &HashMap<String, u64>,
features: &BtfFeatures,
) -> Result<(), BtfError> {
// ENUM64 placeholder type needs to be added before we take ownership of
// self.types to ensure that the offsets in the BtfHeader are correct.
let placeholder_name = self.add_string("enum64_placeholder");
let enum64_placeholder_id = (!features.btf_enum64
&& self.types().any(|t| t.kind() == BtfKind::Enum64))
.then(|| {
self.add_type(BtfType::Int(Int::new(
placeholder_name,
1,
IntEncoding::None,
0,
)))
});
let mut types = mem::take(&mut self.types);
for i in 0..types.types.len() {
let t = &types.types[i];
let t = &mut types.types[i];
let kind = t.kind();
match t {
// Fixup PTR for Rust
// LLVM emits names for Rust pointer types, which the kernel doesn't like
// Fixup PTR for Rust.
//
// LLVM emits names for Rust pointer types, which the kernel doesn't like.
// While I figure out if this needs fixing in the Kernel or LLVM, we'll
// do a fixup here
// do a fixup here.
BtfType::Ptr(ptr) => {
let mut fixed_ty = ptr.clone();
fixed_ty.name_offset = 0;
types.types[i] = BtfType::Ptr(fixed_ty)
ptr.name_offset = 0;
}
// Sanitize VAR if they are not supported
// Sanitize VAR if they are not supported.
BtfType::Var(v) if !features.btf_datasec => {
types.types[i] = BtfType::Int(Int::new(v.name_offset, 1, IntEncoding::None, 0));
*t = BtfType::Int(Int::new(v.name_offset, 1, IntEncoding::None, 0));
}
// Sanitize DATASEC if they are not supported
// Sanitize DATASEC if they are not supported.
BtfType::DataSec(d) if !features.btf_datasec => {
debug!("{}: not supported. replacing with STRUCT", kind);
// STRUCT aren't allowed to have "." in their name, fixup this if needed.
let mut name_offset = t.name_offset();
let mut name_offset = d.name_offset;
let name = self.string_at(name_offset)?;
// Handle any "." characters in struct names
// Handle any "." characters in struct names.
// Example: ".maps"
let fixed_name = name.replace('.', "_");
if fixed_name != name {
name_offset = self.add_string(&fixed_name);
}
let mut members = vec![];
for member in d.entries.iter() {
let mt = types.type_by_id(member.btf_type).unwrap();
members.push(BtfMember {
name_offset: mt.name_offset(),
btf_type: member.btf_type,
offset: member.offset * 8,
let entries = std::mem::take(&mut d.entries);
let members = entries
.iter()
.map(|e| {
let mt = types.type_by_id(e.btf_type).unwrap();
BtfMember {
name_offset: mt.name_offset(),
btf_type: e.btf_type,
offset: e.offset * 8,
}
})
}
.collect();
types.types[i] =
BtfType::Struct(Struct::new(name_offset, members, d.entries.len() as u32));
// Must reborrow here because we borrow `types` immutably above.
let t = &mut types.types[i];
*t = BtfType::Struct(Struct::new(name_offset, members, entries.len() as u32));
}
// Fixup DATASEC
// DATASEC sizes aren't always set by LLVM
// we need to fix them here before loading the btf to the kernel
// Fixup DATASEC.
//
// DATASEC sizes aren't always set by LLVM so we need to fix them
// here before loading the btf to the kernel.
BtfType::DataSec(d) if features.btf_datasec => {
// Start DataSec Fixups
let name = self.string_at(d.name_offset)?;
let name = name.into_owned();
let mut fixed_ty = d.clone();
// Handle any "/" characters in section names
// Handle any "/" characters in section names.
// Example: "maps/hashmap"
let fixed_name = name.replace('/', ".");
if fixed_name != name {
fixed_ty.name_offset = self.add_string(&fixed_name);
d.name_offset = self.add_string(&fixed_name);
}
// There are some cases when the compiler does indeed populate the
// size
if t.size().unwrap() > 0 {
// There are some cases when the compiler does indeed populate the size.
if d.size > 0 {
debug!("{} {}: size fixup not required", kind, name);
} else {
// We need to get the size of the section from the ELF file
// We need to get the size of the section from the ELF file.
// Fortunately, we cached these when parsing it initially
// and we can this up by name in section_infos
// and we can this up by name in section_infos.
let size = match section_infos.get(&name) {
Some((_, size)) => size,
None => {
@ -551,22 +582,23 @@ impl Btf {
}
};
debug!("{} {}: fixup size to {}", kind, name, size);
fixed_ty.size = *size as u32;
d.size = *size as u32;
// The Vec<btf_var_secinfo> contains BTF_KIND_VAR sections
// that need to have their offsets adjusted. To do this,
// we need to get the offset from the ELF file.
// This was also cached during initial parsing and
// we can query by name in symbol_offsets
for d in &mut fixed_ty.entries.iter_mut() {
let var_type = types.type_by_id(d.btf_type)?;
let var_kind = var_type.kind();
if let BtfType::Var(var) = var_type {
// we can query by name in symbol_offsets.
let mut entries = mem::take(&mut d.entries);
let mut fixed_section = d.clone();
for e in entries.iter_mut() {
if let BtfType::Var(var) = types.type_by_id(e.btf_type)? {
let var_name = self.string_at(var.name_offset)?;
if var.linkage == VarLinkage::Static {
debug!(
"{} {}: {} {}: fixup not required",
kind, name, var_kind, var_name
"{} {}: VAR {}: fixup not required",
kind, name, var_name
);
continue;
}
@ -579,29 +611,31 @@ impl Btf {
});
}
};
d.offset = *offset as u32;
e.offset = *offset as u32;
debug!(
"{} {}: {} {}: fixup offset {}",
kind, name, var_kind, var_name, offset
"{} {}: VAR {}: fixup offset {}",
kind, name, var_name, offset
);
} else {
return Err(BtfError::InvalidDatasec);
}
}
fixed_section.entries = entries;
// Must reborrow here because we borrow `types` immutably above.
let t = &mut types.types[i];
*t = BtfType::DataSec(fixed_section);
}
types.types[i] = BtfType::DataSec(fixed_ty);
}
// Fixup FUNC_PROTO
// Fixup FUNC_PROTO.
BtfType::FuncProto(ty) if features.btf_func => {
let mut ty = ty.clone();
for (i, param) in ty.params.iter_mut().enumerate() {
if param.name_offset == 0 && param.btf_type != 0 {
param.name_offset = self.add_string(&format!("param{i}"));
}
}
types.types[i] = BtfType::FuncProto(ty);
}
// Sanitize FUNC_PROTO
// Sanitize FUNC_PROTO.
BtfType::FuncProto(ty) if !features.btf_func => {
debug!("{}: not supported. replacing with ENUM", kind);
let members: Vec<BtfEnum> = ty
@ -612,18 +646,16 @@ impl Btf {
value: p.btf_type,
})
.collect();
let enum_type = BtfType::Enum(Enum::new(ty.name_offset, members));
types.types[i] = enum_type;
let enum_type = BtfType::Enum(Enum::new(ty.name_offset, false, members));
*t = enum_type;
}
// Sanitize FUNC
// Sanitize FUNC.
BtfType::Func(ty) => {
let name = self.string_at(ty.name_offset)?;
// Sanitize FUNC
// Sanitize FUNC.
if !features.btf_func {
debug!("{}: not supported. replacing with TYPEDEF", kind);
let typedef_type =
BtfType::Typedef(Typedef::new(ty.name_offset, ty.btf_type));
types.types[i] = typedef_type;
*t = BtfType::Typedef(Typedef::new(ty.name_offset, ty.btf_type));
} else if !features.btf_func_global
|| name == "memset"
|| name == "memcpy"
@ -635,7 +667,6 @@ impl Btf {
// and verified separately from their callers, while instead we
// want tracking info (eg bound checks) to be propagated to the
// memory builtins.
let mut fixed_ty = ty.clone();
if ty.linkage() == FuncLinkage::Global {
if !features.btf_func_global {
debug!(
@ -645,30 +676,47 @@ impl Btf {
} else {
debug!("changing FUNC {name} linkage to BTF_FUNC_STATIC");
}
fixed_ty.set_linkage(FuncLinkage::Static);
ty.set_linkage(FuncLinkage::Static);
}
types.types[i] = BtfType::Func(fixed_ty);
}
}
// Sanitize FLOAT
// Sanitize FLOAT.
BtfType::Float(ty) if !features.btf_float => {
debug!("{}: not supported. replacing with STRUCT", kind);
let struct_ty = BtfType::Struct(Struct::new(0, vec![], ty.size));
types.types[i] = struct_ty;
*t = BtfType::Struct(Struct::new(0, vec![], ty.size));
}
// Sanitize DECL_TAG
// Sanitize DECL_TAG.
BtfType::DeclTag(ty) if !features.btf_decl_tag => {
debug!("{}: not supported. replacing with INT", kind);
let int_type = BtfType::Int(Int::new(ty.name_offset, 1, IntEncoding::None, 0));
types.types[i] = int_type;
*t = BtfType::Int(Int::new(ty.name_offset, 1, IntEncoding::None, 0));
}
// Sanitize TYPE_TAG
// Sanitize TYPE_TAG.
BtfType::TypeTag(ty) if !features.btf_type_tag => {
debug!("{}: not supported. replacing with CONST", kind);
let const_type = BtfType::Const(Const::new(ty.btf_type));
types.types[i] = const_type;
*t = BtfType::Const(Const::new(ty.btf_type));
}
// Sanitize Signed ENUMs.
BtfType::Enum(ty) if !features.btf_enum64 && ty.is_signed() => {
debug!("{}: signed ENUMs not supported. Marking as unsigned", kind);
ty.set_signed(false);
}
// The type does not need fixing up or sanitization
// Sanitize ENUM64.
BtfType::Enum64(ty) if !features.btf_enum64 => {
debug!("{}: not supported. replacing with UNION", kind);
let placeholder_id =
enum64_placeholder_id.expect("enum64_placeholder_id must be set");
let members: Vec<BtfMember> = ty
.variants
.iter()
.map(|v| BtfMember {
name_offset: v.name_offset,
btf_type: placeholder_id,
offset: 0,
})
.collect();
*t = BtfType::Union(Union::new(ty.name_offset, members.len() as u32, members));
}
// The type does not need fixing up or sanitization.
_ => {}
}
}
@ -1056,7 +1104,8 @@ pub(crate) struct SecInfo<'a> {
mod tests {
use super::*;
use crate::btf::{
BtfParam, DataSec, DataSecEntry, DeclTag, Float, Func, FuncProto, Ptr, TypeTag, Var,
BtfEnum64, BtfParam, DataSec, DataSecEntry, DeclTag, Enum64, Float, Func, FuncProto, Ptr,
TypeTag, Var,
};
use assert_matches::assert_matches;
@ -1244,9 +1293,9 @@ mod tests {
0,
)));
let name_offset = btf.add_string("foo");
let var_name_offset = btf.add_string("foo");
let var_type_id = btf.add_type(BtfType::Var(Var::new(
name_offset,
var_name_offset,
int_type_id,
VarLinkage::Static,
)));
@ -1271,11 +1320,14 @@ mod tests {
assert_eq!(fixed.name_offset , name_offset);
assert_matches!(*fixed.members, [
BtfMember {
name_offset: _,
name_offset: name_offset1,
btf_type,
offset: 0,
},
] => assert_eq!(btf_type, var_type_id));
] => {
assert_eq!(name_offset1, var_name_offset);
assert_eq!(btf_type, var_type_id);
})
});
// Ensure we can convert to bytes and back again
let raw = btf.to_bytes();
@ -1675,4 +1727,96 @@ mod tests {
let u32_ty = btf.type_by_id(u32_base).unwrap();
assert_eq!(u32_ty.kind(), BtfKind::Int);
}
#[test]
fn test_sanitize_signed_enum() {
let mut btf = Btf::new();
let name_offset = btf.add_string("signed_enum");
let name_a = btf.add_string("A");
let name_b = btf.add_string("B");
let name_c = btf.add_string("C");
let enum64_type = Enum::new(
name_offset,
true,
vec![
BtfEnum::new(name_a, -1i32 as u32),
BtfEnum::new(name_b, -2i32 as u32),
BtfEnum::new(name_c, -3i32 as u32),
],
);
let enum_type_id = btf.add_type(BtfType::Enum(enum64_type));
let features = BtfFeatures {
btf_enum64: false,
..Default::default()
};
btf.fixup_and_sanitize(&HashMap::new(), &HashMap::new(), &features)
.unwrap();
assert_matches!(btf.type_by_id(enum_type_id).unwrap(), BtfType::Enum(fixed) => {
assert!(!fixed.is_signed());
assert_matches!(fixed.variants[..], [
BtfEnum { name_offset: name1, value: 0xFFFF_FFFF },
BtfEnum { name_offset: name2, value: 0xFFFF_FFFE },
BtfEnum { name_offset: name3, value: 0xFFFF_FFFD },
] => {
assert_eq!(name1, name_a);
assert_eq!(name2, name_b);
assert_eq!(name3, name_c);
});
});
// Ensure we can convert to bytes and back again.
let raw = btf.to_bytes();
Btf::parse(&raw, Endianness::default()).unwrap();
}
#[test]
fn test_sanitize_enum64() {
let mut btf = Btf::new();
let name_offset = btf.add_string("enum64");
let name_a = btf.add_string("A");
let name_b = btf.add_string("B");
let name_c = btf.add_string("C");
let enum64_type = Enum64::new(
name_offset,
false,
vec![
BtfEnum64::new(name_a, 1),
BtfEnum64::new(name_b, 2),
BtfEnum64::new(name_c, 3),
],
);
let enum_type_id = btf.add_type(BtfType::Enum64(enum64_type));
let features = BtfFeatures {
btf_enum64: false,
..Default::default()
};
btf.fixup_and_sanitize(&HashMap::new(), &HashMap::new(), &features)
.unwrap();
assert_matches!(btf.type_by_id(enum_type_id).unwrap(), BtfType::Union(fixed) => {
let placeholder = btf.id_by_type_name_kind("enum64_placeholder", BtfKind::Int)
.expect("enum64_placeholder type not found");
assert_matches!(fixed.members[..], [
BtfMember { name_offset: name_offset1, btf_type: btf_type1, offset: 0 },
BtfMember { name_offset: name_offset2, btf_type: btf_type2, offset: 0 },
BtfMember { name_offset: name_offset3, btf_type: btf_type3, offset: 0 },
] => {
assert_eq!(name_offset1, name_a);
assert_eq!(btf_type1, placeholder);
assert_eq!(name_offset2, name_b);
assert_eq!(btf_type2, placeholder);
assert_eq!(name_offset3, name_c);
assert_eq!(btf_type3, placeholder);
});
});
// Ensure we can convert to bytes and back again.
let raw = btf.to_bytes();
Btf::parse(&raw, Endianness::default()).unwrap();
}
}

@ -400,6 +400,12 @@ pub struct BtfEnum {
pub value: u32,
}
impl BtfEnum {
pub fn new(name_offset: u32, value: u32) -> Self {
Self { name_offset, value }
}
}
#[repr(C)]
#[derive(Clone, Debug)]
pub struct Enum {
@ -439,9 +445,12 @@ impl Enum {
mem::size_of::<Fwd>() + mem::size_of::<BtfEnum>() * self.variants.len()
}
pub fn new(name_offset: u32, variants: Vec<BtfEnum>) -> Self {
pub fn new(name_offset: u32, signed: bool, variants: Vec<BtfEnum>) -> Self {
let mut info = (BtfKind::Enum as u32) << 24;
info |= (variants.len() as u32) & 0xFFFF;
if signed {
info |= 1 << 31;
}
Self {
name_offset,
info,
@ -453,6 +462,14 @@ impl Enum {
pub(crate) fn is_signed(&self) -> bool {
self.info >> 31 == 1
}
pub(crate) fn set_signed(&mut self, signed: bool) {
if signed {
self.info |= 1 << 31;
} else {
self.info &= !(1 << 31);
}
}
}
#[repr(C)]
@ -463,6 +480,16 @@ pub struct BtfEnum64 {
pub(crate) value_high: u32,
}
impl BtfEnum64 {
pub fn new(name_offset: u32, value: u64) -> Self {
Self {
name_offset,
value_low: value as u32,
value_high: (value >> 32) as u32,
}
}
}
#[repr(C)]
#[derive(Clone, Debug)]
pub struct Enum64 {
@ -515,6 +542,26 @@ impl Enum64 {
pub(crate) fn is_signed(&self) -> bool {
self.info >> 31 == 1
}
pub fn new(name_offset: u32, signed: bool, variants: Vec<BtfEnum64>) -> Self {
let mut info = (BtfKind::Enum64 as u32) << 24;
if signed {
info |= 1 << 31
};
info |= (variants.len() as u32) & 0xFFFF;
Enum64 {
name_offset,
info,
// According to the documentation:
//
// https://www.kernel.org/doc/html/next/bpf/btf.html
//
// The size may be 1/2/4/8. Since BtfEnum64::new() takes a u64, we
// can assume that the size is 8.
size: 8,
variants,
}
}
}
#[repr(C)]
@ -614,6 +661,16 @@ pub struct Union {
}
impl Union {
pub(crate) fn new(name_offset: u32, size: u32, members: Vec<BtfMember>) -> Self {
let info = (BtfKind::Union as u32) << 24;
Self {
name_offset,
info,
size,
members,
}
}
pub(crate) fn to_bytes(&self) -> Vec<u8> {
let Self {
name_offset,
@ -1794,4 +1851,21 @@ mod tests {
assert!(types_are_compatible(&btf, u32t, &btf, u64t).unwrap());
assert!(types_are_compatible(&btf, array_type, &btf, array_type).unwrap());
}
#[test]
pub fn test_read_btf_type_enum64() {
let endianness = Endianness::default();
let data: &[u8] = &[
0x00, 0x00, 0x00, 0x00, // name offset
0x01, 0x00, 0x00, 0x13, // info: vlen, type_kind
0x08, 0x00, 0x00, 0x00, // size
0xd7, 0x06, 0x00, 0x00, // enum variant name offset
0xbb, 0xbb, 0xbb, 0xbb, // enum variant low
0xaa, 0xaa, 0xaa, 0xaa, // enum variant high
];
assert_matches!(unsafe { BtfType::read(data, endianness) }.unwrap(), BtfType::Enum64(got) => {
assert_eq!(got.to_bytes(), data);
});
}
}

@ -73,15 +73,6 @@ pub enum RelocationError {
address: u64,
},
/// Referenced map not created yet
#[error("the map `{name}` at section `{section_index}` has not been created")]
MapNotCreated {
/// The section index
section_index: usize,
/// The map name
name: String,
},
/// Invalid relocation offset
#[error("invalid offset `{offset}` applying relocation #{relocation_number}")]
InvalidRelocationOffset {
@ -114,7 +105,7 @@ pub(crate) struct Symbol {
impl Object {
/// Relocates the map references
pub fn relocate_maps<'a, I: Iterator<Item = (&'a str, Option<i32>, &'a Map)>>(
pub fn relocate_maps<'a, I: Iterator<Item = (&'a str, i32, &'a Map)>>(
&mut self,
maps: I,
text_sections: &HashSet<usize>,
@ -187,8 +178,8 @@ impl Object {
fn relocate_maps<'a, I: Iterator<Item = &'a Relocation>>(
fun: &mut Function,
relocations: I,
maps_by_section: &HashMap<usize, (&str, Option<i32>, &Map)>,
maps_by_symbol: &HashMap<usize, (&str, Option<i32>, &Map)>,
maps_by_section: &HashMap<usize, (&str, i32, &Map)>,
maps_by_symbol: &HashMap<usize, (&str, i32, &Map)>,
symbol_table: &HashMap<usize, Symbol>,
text_sections: &HashSet<usize>,
) -> Result<(), RelocationError> {
@ -230,7 +221,7 @@ fn relocate_maps<'a, I: Iterator<Item = &'a Relocation>>(
continue;
}
let (name, fd, map) = if let Some(m) = maps_by_symbol.get(&rel.symbol_index) {
let (_name, fd, map) = if let Some(m) = maps_by_symbol.get(&rel.symbol_index) {
let map = &m.2;
debug!(
"relocating map by symbol index {:?}, kind {:?} at insn {ins_index} in section {}",
@ -266,18 +257,13 @@ fn relocate_maps<'a, I: Iterator<Item = &'a Relocation>>(
};
debug_assert_eq!(map.section_index(), section_index);
let map_fd = fd.ok_or_else(|| RelocationError::MapNotCreated {
name: (*name).into(),
section_index,
})?;
if !map.data().is_empty() {
instructions[ins_index].set_src_reg(BPF_PSEUDO_MAP_VALUE as u8);
instructions[ins_index + 1].imm = instructions[ins_index].imm + sym.address as i32;
} else {
instructions[ins_index].set_src_reg(BPF_PSEUDO_MAP_FD as u8);
}
instructions[ins_index].imm = map_fd;
instructions[ins_index].imm = *fd;
}
Ok(())
@ -588,7 +574,7 @@ mod test {
let maps_by_section = HashMap::new();
let map = fake_legacy_map(1);
let maps_by_symbol = HashMap::from([(1, ("test_map", Some(1), &map))]);
let maps_by_symbol = HashMap::from([(1, ("test_map", 1, &map))]);
relocate_maps(
&mut fun,
@ -642,8 +628,8 @@ mod test {
let map_1 = fake_legacy_map(1);
let map_2 = fake_legacy_map(2);
let maps_by_symbol = HashMap::from([
(1, ("test_map_1", Some(1), &map_1)),
(2, ("test_map_2", Some(2), &map_2)),
(1, ("test_map_1", 1, &map_1)),
(2, ("test_map_2", 2, &map_2)),
]);
relocate_maps(
@ -683,7 +669,7 @@ mod test {
let maps_by_section = HashMap::new();
let map = fake_btf_map(1);
let maps_by_symbol = HashMap::from([(1, ("test_map", Some(1), &map))]);
let maps_by_symbol = HashMap::from([(1, ("test_map", 1, &map))]);
relocate_maps(
&mut fun,
@ -737,8 +723,8 @@ mod test {
let map_1 = fake_btf_map(1);
let map_2 = fake_btf_map(2);
let maps_by_symbol = HashMap::from([
(1, ("test_map_1", Some(1), &map_1)),
(2, ("test_map_2", Some(2), &map_2)),
(1, ("test_map_1", 1, &map_1)),
(2, ("test_map_2", 2, &map_2)),
]);
relocate_maps(

@ -4,7 +4,7 @@ use std::{
ffi::CString,
fs, io,
os::{
fd::{OwnedFd, RawFd},
fd::{AsFd as _, OwnedFd},
raw::c_int,
},
path::{Path, PathBuf},
@ -39,8 +39,8 @@ use crate::{
sys::{
bpf_load_btf, bpf_map_freeze, bpf_map_update_elem_ptr, is_bpf_cookie_supported,
is_bpf_global_data_supported, is_btf_datasec_supported, is_btf_decl_tag_supported,
is_btf_float_supported, is_btf_func_global_supported, is_btf_func_supported,
is_btf_supported, is_btf_type_tag_supported, is_perf_link_supported,
is_btf_enum64_supported, is_btf_float_supported, is_btf_func_global_supported,
is_btf_func_supported, is_btf_supported, is_btf_type_tag_supported, is_perf_link_supported,
is_probe_read_kernel_supported, is_prog_name_supported, retry_with_verifier_logs,
SyscallError,
},
@ -84,6 +84,7 @@ fn detect_features() -> Features {
is_btf_float_supported(),
is_btf_decl_tag_supported(),
is_btf_type_tag_supported(),
is_btf_enum64_supported(),
))
} else {
None
@ -474,36 +475,15 @@ impl<'a> BpfLoader<'a> {
}
}
}
let mut map = MapData {
obj,
fd: None,
pinned: false,
btf_fd: btf_fd.as_ref().map(Arc::clone),
};
let fd = match map.obj.pinning() {
let btf_fd = btf_fd.as_deref().map(|fd| fd.as_fd());
let mut map = match obj.pinning() {
PinningType::None => MapData::create(obj, &name, btf_fd)?,
PinningType::ByName => {
let path = match &map_pin_path {
Some(p) => p,
None => return Err(BpfError::NoPinPath),
};
// try to open map in case it's already pinned
match map.open_pinned(&name, path) {
Ok(fd) => {
map.pinned = true;
fd as RawFd
}
Err(_) => {
let fd = map.create(&name)?;
map.pin(&name, path).map_err(|error| MapError::PinError {
name: Some(name.to_string()),
error,
})?;
fd
}
}
let path = map_pin_path.as_ref().ok_or(BpfError::NoPinPath)?;
MapData::create_pinned(path, obj, &name, btf_fd)?
}
PinningType::None => map.create(&name)?,
};
let fd = map.fd;
if !map.obj.data().is_empty() && map.obj.section_kind() != BpfSectionKind::Bss {
bpf_map_update_elem_ptr(fd, &0 as *const _, map.obj.data_mut().as_mut_ptr(), 0)
.map_err(|(_, io_error)| SyscallError {

@ -39,8 +39,6 @@ impl<T: Borrow<MapData>, V: Pod> Array<T, V> {
let data = map.borrow();
check_kv_size::<u32, V>(data)?;
let _fd = data.fd_or_err()?;
Ok(Array {
inner: map,
_v: PhantomData,
@ -63,7 +61,7 @@ impl<T: Borrow<MapData>, V: Pod> Array<T, V> {
pub fn get(&self, index: &u32, flags: u64) -> Result<V, MapError> {
let data = self.inner.borrow();
check_bounds(data, *index)?;
let fd = data.fd_or_err()?;
let fd = data.fd;
let value =
bpf_map_lookup_elem(fd, index, flags).map_err(|(_, io_error)| SyscallError {
@ -90,7 +88,7 @@ impl<T: BorrowMut<MapData>, V: Pod> Array<T, V> {
pub fn set(&mut self, index: u32, value: impl Borrow<V>, flags: u64) -> Result<(), MapError> {
let data = self.inner.borrow_mut();
check_bounds(data, index)?;
let fd = data.fd_or_err()?;
let fd = data.fd;
bpf_map_update_elem(fd, Some(&index), value.borrow(), flags).map_err(|(_, io_error)| {
SyscallError {
call: "bpf_map_update_elem",

@ -58,8 +58,6 @@ impl<T: Borrow<MapData>, V: Pod> PerCpuArray<T, V> {
let data = map.borrow();
check_kv_size::<u32, V>(data)?;
let _fd = data.fd_or_err()?;
Ok(PerCpuArray {
inner: map,
_v: PhantomData,
@ -82,7 +80,7 @@ impl<T: Borrow<MapData>, V: Pod> PerCpuArray<T, V> {
pub fn get(&self, index: &u32, flags: u64) -> Result<PerCpuValues<V>, MapError> {
let data = self.inner.borrow();
check_bounds(data, *index)?;
let fd = data.fd_or_err()?;
let fd = data.fd;
let value = bpf_map_lookup_elem_per_cpu(fd, index, flags).map_err(|(_, io_error)| {
SyscallError {
@ -110,7 +108,7 @@ impl<T: BorrowMut<MapData>, V: Pod> PerCpuArray<T, V> {
pub fn set(&mut self, index: u32, values: PerCpuValues<V>, flags: u64) -> Result<(), MapError> {
let data = self.inner.borrow_mut();
check_bounds(data, index)?;
let fd = data.fd_or_err()?;
let fd = data.fd;
bpf_map_update_elem_per_cpu(fd, &index, &values, flags).map_err(|(_, io_error)| {
SyscallError {

@ -2,7 +2,7 @@
use std::{
borrow::{Borrow, BorrowMut},
os::fd::{AsRawFd, RawFd},
os::fd::{AsFd as _, AsRawFd as _, RawFd},
};
use crate::{
@ -37,13 +37,13 @@ use crate::{
/// let flags = 0;
///
/// // bpf_tail_call(ctx, JUMP_TABLE, 0) will jump to prog_0
/// prog_array.set(0, prog_0_fd, flags);
/// prog_array.set(0, &prog_0_fd, flags);
///
/// // bpf_tail_call(ctx, JUMP_TABLE, 1) will jump to prog_1
/// prog_array.set(1, prog_1_fd, flags);
/// prog_array.set(1, &prog_1_fd, flags);
///
/// // bpf_tail_call(ctx, JUMP_TABLE, 2) will jump to prog_2
/// prog_array.set(2, prog_2_fd, flags);
/// prog_array.set(2, &prog_2_fd, flags);
/// # Ok::<(), aya::BpfError>(())
/// ```
#[doc(alias = "BPF_MAP_TYPE_PROG_ARRAY")]
@ -56,8 +56,6 @@ impl<T: Borrow<MapData>> ProgramArray<T> {
let data = map.borrow();
check_kv_size::<u32, RawFd>(data)?;
let _fd = data.fd_or_err()?;
Ok(ProgramArray { inner: map })
}
@ -73,11 +71,12 @@ impl<T: BorrowMut<MapData>> ProgramArray<T> {
///
/// When an eBPF program calls `bpf_tail_call(ctx, prog_array, index)`, control
/// flow will jump to `program`.
pub fn set(&mut self, index: u32, program: ProgramFd, flags: u64) -> Result<(), MapError> {
pub fn set(&mut self, index: u32, program: &ProgramFd, flags: u64) -> Result<(), MapError> {
let data = self.inner.borrow_mut();
check_bounds(data, index)?;
let fd = data.fd_or_err()?;
let prog_fd = program.as_raw_fd();
let fd = data.fd;
let prog_fd = program.as_fd();
let prog_fd = prog_fd.as_raw_fd();
bpf_map_update_elem(fd, Some(&index), &prog_fd, flags).map_err(|(_, io_error)| {
SyscallError {
@ -95,7 +94,7 @@ impl<T: BorrowMut<MapData>> ProgramArray<T> {
pub fn clear_index(&mut self, index: &u32) -> Result<(), MapError> {
let data = self.inner.borrow_mut();
check_bounds(data, *index)?;
let fd = self.inner.borrow_mut().fd_or_err()?;
let fd = self.inner.borrow_mut().fd;
bpf_map_delete_elem(fd, index)
.map(|_| ())

@ -1,5 +1,8 @@
//! A Bloom Filter.
use std::{borrow::Borrow, marker::PhantomData};
use std::{
borrow::{Borrow, BorrowMut},
marker::PhantomData,
};
use crate::{
maps::{check_v_size, MapData, MapError},
@ -41,8 +44,6 @@ impl<T: Borrow<MapData>, V: Pod> BloomFilter<T, V> {
let data = map.borrow();
check_v_size::<V>(data)?;
let _ = data.fd_or_err()?;
Ok(BloomFilter {
inner: map,
_v: PhantomData,
@ -51,7 +52,7 @@ impl<T: Borrow<MapData>, V: Pod> BloomFilter<T, V> {
/// Query the existence of the element.
pub fn contains(&self, mut value: &V, flags: u64) -> Result<(), MapError> {
let fd = self.inner.borrow().fd_or_err()?;
let fd = self.inner.borrow().fd;
bpf_map_lookup_elem_ptr::<u32, _>(fd, None, &mut value, flags)
.map_err(|(_, io_error)| SyscallError {
@ -61,10 +62,12 @@ impl<T: Borrow<MapData>, V: Pod> BloomFilter<T, V> {
.ok_or(MapError::ElementNotFound)?;
Ok(())
}
}
impl<T: BorrowMut<MapData>, V: Pod> BloomFilter<T, V> {
/// Inserts a value into the map.
pub fn insert(&self, value: impl Borrow<V>, flags: u64) -> Result<(), MapError> {
let fd = self.inner.borrow().fd_or_err()?;
pub fn insert(&mut self, value: impl Borrow<V>, flags: u64) -> Result<(), MapError> {
let fd = self.inner.borrow_mut().fd;
bpf_map_push_elem(fd, value.borrow(), flags).map_err(|(_, io_error)| SyscallError {
call: "bpf_map_push_elem",
io_error,
@ -106,18 +109,24 @@ mod tests {
})
}
fn new_map(obj: obj::Map) -> MapData {
override_syscall(|call| match call {
Syscall::Bpf {
cmd: bpf_cmd::BPF_MAP_CREATE,
..
} => Ok(1337),
call => panic!("unexpected syscall {:?}", call),
});
MapData::create(obj, "foo", None).unwrap()
}
fn sys_error(value: i32) -> SysResult<c_long> {
Err((-1, io::Error::from_raw_os_error(value)))
}
#[test]
fn test_wrong_value_size() {
let map = MapData {
obj: new_obj_map(),
fd: None,
pinned: false,
btf_fd: None,
};
let map = new_map(new_obj_map());
assert_matches!(
BloomFilter::<_, u16>::new(&map),
Err(MapError::InvalidValueSize {
@ -129,26 +138,21 @@ mod tests {
#[test]
fn test_try_from_wrong_map() {
let map_data = MapData {
obj: obj::Map::Legacy(LegacyMap {
def: bpf_map_def {
map_type: BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32,
key_size: 4,
value_size: 4,
max_entries: 1024,
..Default::default()
},
section_index: 0,
section_kind: BpfSectionKind::Maps,
symbol_index: None,
data: Vec::new(),
}),
fd: None,
pinned: false,
btf_fd: None,
};
let map = Map::PerfEventArray(map_data);
let map = new_map(obj::Map::Legacy(LegacyMap {
def: bpf_map_def {
map_type: BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32,
key_size: 4,
value_size: 4,
max_entries: 1024,
..Default::default()
},
section_index: 0,
section_kind: BpfSectionKind::Maps,
symbol_index: None,
data: Vec::new(),
}));
let map = Map::PerfEventArray(map);
assert_matches!(
BloomFilter::<_, u32>::try_from(&map),
@ -156,57 +160,27 @@ mod tests {
);
}
#[test]
fn test_new_not_created() {
let mut map = MapData {
obj: new_obj_map(),
fd: None,
pinned: false,
btf_fd: None,
};
assert_matches!(
BloomFilter::<_, u32>::new(&mut map),
Err(MapError::NotCreated { .. })
);
}
#[test]
fn test_new_ok() {
let mut map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
assert!(BloomFilter::<_, u32>::new(&mut map).is_ok());
let map = new_map(new_obj_map());
assert!(BloomFilter::<_, u32>::new(&map).is_ok());
}
#[test]
fn test_try_from_ok() {
let map_data = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let map = Map::BloomFilter(map_data);
let map = new_map(new_obj_map());
let map = Map::BloomFilter(map);
assert!(BloomFilter::<_, u32>::try_from(&map).is_ok())
}
#[test]
fn test_insert_syscall_error() {
override_syscall(|_| sys_error(EFAULT));
let mut map = new_map(new_obj_map());
let mut bloom_filter = BloomFilter::<_, u32>::new(&mut map).unwrap();
let mut map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let bloom_filter = BloomFilter::<_, u32>::new(&mut map).unwrap();
override_syscall(|_| sys_error(EFAULT));
assert_matches!(
bloom_filter.insert(1, 0),
@ -216,6 +190,9 @@ mod tests {
#[test]
fn test_insert_ok() {
let mut map = new_map(new_obj_map());
let mut bloom_filter = BloomFilter::<_, u32>::new(&mut map).unwrap();
override_syscall(|call| match call {
Syscall::Bpf {
cmd: bpf_cmd::BPF_MAP_UPDATE_ELEM,
@ -224,28 +201,16 @@ mod tests {
_ => sys_error(EFAULT),
});
let mut map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let bloom_filter = BloomFilter::<_, u32>::new(&mut map).unwrap();
assert!(bloom_filter.insert(0, 42).is_ok());
}
#[test]
fn test_contains_syscall_error() {
override_syscall(|_| sys_error(EFAULT));
let map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let map = new_map(new_obj_map());
let bloom_filter = BloomFilter::<_, u32>::new(&map).unwrap();
override_syscall(|_| sys_error(EFAULT));
assert_matches!(
bloom_filter.contains(&1, 0),
Err(MapError::SyscallError(SyscallError { call: "bpf_map_lookup_elem", io_error })) if io_error.raw_os_error() == Some(EFAULT)
@ -254,6 +219,9 @@ mod tests {
#[test]
fn test_contains_not_found() {
let map = new_map(new_obj_map());
let bloom_filter = BloomFilter::<_, u32>::new(&map).unwrap();
override_syscall(|call| match call {
Syscall::Bpf {
cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
@ -261,13 +229,6 @@ mod tests {
} => sys_error(ENOENT),
_ => sys_error(EFAULT),
});
let map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let bloom_filter = BloomFilter::<_, u32>::new(&map).unwrap();
assert_matches!(bloom_filter.contains(&1, 0), Err(MapError::ElementNotFound));
}

@ -42,7 +42,6 @@ impl<T: Borrow<MapData>, K: Pod, V: Pod> HashMap<T, K, V> {
pub(crate) fn new(map: T) -> Result<HashMap<T, K, V>, MapError> {
let data = map.borrow();
check_kv_size::<K, V>(data)?;
let _ = data.fd_or_err()?;
Ok(HashMap {
inner: map,
@ -53,7 +52,7 @@ impl<T: Borrow<MapData>, K: Pod, V: Pod> HashMap<T, K, V> {
/// Returns a copy of the value associated with the key.
pub fn get(&self, key: &K, flags: u64) -> Result<V, MapError> {
let fd = self.inner.borrow().fd_or_err()?;
let fd = self.inner.borrow().fd;
let value = bpf_map_lookup_elem(fd, key, flags).map_err(|(_, io_error)| SyscallError {
call: "bpf_map_lookup_elem",
io_error,
@ -137,18 +136,24 @@ mod tests {
})
}
fn new_map(obj: obj::Map) -> MapData {
override_syscall(|call| match call {
Syscall::Bpf {
cmd: bpf_cmd::BPF_MAP_CREATE,
..
} => Ok(1337),
call => panic!("unexpected syscall {:?}", call),
});
MapData::create(obj, "foo", None).unwrap()
}
fn sys_error(value: i32) -> SysResult<c_long> {
Err((-1, io::Error::from_raw_os_error(value)))
}
#[test]
fn test_wrong_key_size() {
let map = MapData {
obj: new_obj_map(),
fd: None,
pinned: false,
btf_fd: None,
};
let map = new_map(new_obj_map());
assert_matches!(
HashMap::<_, u8, u32>::new(&map),
Err(MapError::InvalidKeySize {
@ -160,12 +165,7 @@ mod tests {
#[test]
fn test_wrong_value_size() {
let map = MapData {
obj: new_obj_map(),
fd: None,
pinned: false,
btf_fd: None,
};
let map = new_map(new_obj_map());
assert_matches!(
HashMap::<_, u32, u16>::new(&map),
Err(MapError::InvalidValueSize {
@ -177,14 +177,8 @@ mod tests {
#[test]
fn test_try_from_wrong_map() {
let map_data = MapData {
obj: new_obj_map(),
fd: None,
pinned: false,
btf_fd: None,
};
let map = Map::Array(map_data);
let map = new_map(new_obj_map());
let map = Map::Array(map);
assert_matches!(
HashMap::<_, u8, u32>::try_from(&map),
Err(MapError::InvalidMapType { .. })
@ -193,14 +187,8 @@ mod tests {
#[test]
fn test_try_from_wrong_map_values() {
let map_data = MapData {
obj: new_obj_map(),
fd: None,
pinned: false,
btf_fd: None,
};
let map = Map::HashMap(map_data);
let map = new_map(new_obj_map());
let map = Map::HashMap(map);
assert_matches!(
HashMap::<_, u32, u16>::try_from(&map),
Err(MapError::InvalidValueSize {
@ -210,84 +198,46 @@ mod tests {
);
}
#[test]
fn test_new_not_created() {
let mut map = MapData {
obj: new_obj_map(),
fd: None,
pinned: false,
btf_fd: None,
};
assert_matches!(
HashMap::<_, u32, u32>::new(&mut map),
Err(MapError::NotCreated { .. })
);
}
#[test]
fn test_new_ok() {
let mut map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
assert!(HashMap::<_, u32, u32>::new(&mut map).is_ok());
let map = new_map(new_obj_map());
assert!(HashMap::<_, u32, u32>::new(&map).is_ok());
}
#[test]
fn test_try_from_ok() {
let map_data = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let map = Map::HashMap(map_data);
let map = new_map(new_obj_map());
let map = Map::HashMap(map);
assert!(HashMap::<_, u32, u32>::try_from(&map).is_ok())
}
#[test]
fn test_try_from_ok_lru() {
let map_data = MapData {
obj: obj::Map::Legacy(LegacyMap {
def: bpf_map_def {
map_type: BPF_MAP_TYPE_LRU_HASH as u32,
key_size: 4,
value_size: 4,
max_entries: 1024,
..Default::default()
},
section_index: 0,
section_kind: BpfSectionKind::Maps,
symbol_index: None,
data: Vec::new(),
}),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let map = Map::HashMap(map_data);
let map = new_map(obj::Map::Legacy(LegacyMap {
def: bpf_map_def {
map_type: BPF_MAP_TYPE_LRU_HASH as u32,
key_size: 4,
value_size: 4,
max_entries: 1024,
..Default::default()
},
section_index: 0,
section_kind: BpfSectionKind::Maps,
symbol_index: None,
data: Vec::new(),
}));
let map = Map::HashMap(map);
assert!(HashMap::<_, u32, u32>::try_from(&map).is_ok())
}
#[test]
fn test_insert_syscall_error() {
override_syscall(|_| sys_error(EFAULT));
let mut map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let mut map = new_map(new_obj_map());
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
override_syscall(|_| sys_error(EFAULT));
assert_matches!(
hm.insert(1, 42, 0),
Err(MapError::SyscallError(SyscallError { call: "bpf_map_update_elem", io_error })) if io_error.raw_os_error() == Some(EFAULT)
@ -296,6 +246,9 @@ mod tests {
#[test]
fn test_insert_ok() {
let mut map = new_map(new_obj_map());
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
override_syscall(|call| match call {
Syscall::Bpf {
cmd: bpf_cmd::BPF_MAP_UPDATE_ELEM,
@ -304,19 +257,14 @@ mod tests {
_ => sys_error(EFAULT),
});
let mut map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
assert!(hm.insert(1, 42, 0).is_ok());
}
#[test]
fn test_insert_boxed_ok() {
let mut map = new_map(new_obj_map());
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
override_syscall(|call| match call {
Syscall::Bpf {
cmd: bpf_cmd::BPF_MAP_UPDATE_ELEM,
@ -325,29 +273,16 @@ mod tests {
_ => sys_error(EFAULT),
});
let mut map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
assert!(hm.insert(Box::new(1), Box::new(42), 0).is_ok());
}
#[test]
fn test_remove_syscall_error() {
override_syscall(|_| sys_error(EFAULT));
let mut map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let mut map = new_map(new_obj_map());
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
override_syscall(|_| sys_error(EFAULT));
assert_matches!(
hm.remove(&1),
Err(MapError::SyscallError(SyscallError { call: "bpf_map_delete_elem", io_error })) if io_error.raw_os_error() == Some(EFAULT)
@ -356,6 +291,9 @@ mod tests {
#[test]
fn test_remove_ok() {
let mut map = new_map(new_obj_map());
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
override_syscall(|call| match call {
Syscall::Bpf {
cmd: bpf_cmd::BPF_MAP_DELETE_ELEM,
@ -364,26 +302,13 @@ mod tests {
_ => sys_error(EFAULT),
});
let mut map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
assert!(hm.remove(&1).is_ok());
}
#[test]
fn test_get_syscall_error() {
let map = new_map(new_obj_map());
override_syscall(|_| sys_error(EFAULT));
let map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
assert_matches!(
@ -394,6 +319,7 @@ mod tests {
#[test]
fn test_get_not_found() {
let map = new_map(new_obj_map());
override_syscall(|call| match call {
Syscall::Bpf {
cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
@ -401,12 +327,6 @@ mod tests {
} => sys_error(ENOENT),
_ => sys_error(EFAULT),
});
let map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
assert_matches!(hm.get(&1, 0), Err(MapError::KeyNotFound));
@ -431,6 +351,7 @@ mod tests {
#[test]
fn test_keys_empty() {
let map = new_map(new_obj_map());
override_syscall(|call| match call {
Syscall::Bpf {
cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
@ -438,12 +359,6 @@ mod tests {
} => sys_error(ENOENT),
_ => sys_error(EFAULT),
});
let map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
let keys = hm.keys().collect::<Result<Vec<_>, _>>();
assert_matches!(keys, Ok(ks) if ks.is_empty())
@ -479,6 +394,8 @@ mod tests {
// to support stable as well.
#[cfg_attr(miri, ignore)]
fn test_keys() {
let map = new_map(new_obj_map());
override_syscall(|call| match call {
Syscall::Bpf {
cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
@ -487,12 +404,6 @@ mod tests {
_ => sys_error(EFAULT),
});
let map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
let keys = hm.keys().collect::<Result<Vec<_>, _>>().unwrap();
@ -505,6 +416,7 @@ mod tests {
// to support stable as well.
#[cfg_attr(miri, ignore)]
fn test_keys_error() {
let map = new_map(new_obj_map());
override_syscall(|call| match call {
Syscall::Bpf {
cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
@ -520,12 +432,6 @@ mod tests {
}
_ => sys_error(EFAULT),
});
let map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
let mut keys = hm.keys();
@ -547,6 +453,7 @@ mod tests {
// to support stable as well.
#[cfg_attr(miri, ignore)]
fn test_iter() {
let map = new_map(new_obj_map());
override_syscall(|call| match call {
Syscall::Bpf {
cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
@ -558,12 +465,6 @@ mod tests {
} => lookup_elem(attr),
_ => sys_error(EFAULT),
});
let map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
let items = hm.iter().collect::<Result<Vec<_>, _>>().unwrap();
assert_eq!(&items, &[(10, 100), (20, 200), (30, 300)])
@ -575,6 +476,7 @@ mod tests {
// to support stable as well.
#[cfg_attr(miri, ignore)]
fn test_iter_key_deleted() {
let map = new_map(new_obj_map());
override_syscall(|call| match call {
Syscall::Bpf {
cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
@ -596,12 +498,6 @@ mod tests {
}
_ => sys_error(EFAULT),
});
let map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
let items = hm.iter().collect::<Result<Vec<_>, _>>().unwrap();
@ -614,6 +510,7 @@ mod tests {
// to support stable as well.
#[cfg_attr(miri, ignore)]
fn test_iter_key_error() {
let map = new_map(new_obj_map());
override_syscall(|call| match call {
Syscall::Bpf {
cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
@ -635,12 +532,6 @@ mod tests {
} => lookup_elem(attr),
_ => sys_error(EFAULT),
});
let map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
let mut iter = hm.iter();
@ -662,6 +553,7 @@ mod tests {
// to support stable as well.
#[cfg_attr(miri, ignore)]
fn test_iter_value_error() {
let map = new_map(new_obj_map());
override_syscall(|call| match call {
Syscall::Bpf {
cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
@ -683,12 +575,6 @@ mod tests {
}
_ => sys_error(EFAULT),
});
let map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
let mut iter = hm.iter();

@ -20,7 +20,7 @@ pub(crate) fn insert<K: Pod, V: Pod>(
value: &V,
flags: u64,
) -> Result<(), MapError> {
let fd = map.fd_or_err()?;
let fd = map.fd;
bpf_map_update_elem(fd, Some(key), value, flags).map_err(|(_, io_error)| SyscallError {
call: "bpf_map_update_elem",
io_error,
@ -30,7 +30,7 @@ pub(crate) fn insert<K: Pod, V: Pod>(
}
pub(crate) fn remove<K: Pod>(map: &MapData, key: &K) -> Result<(), MapError> {
let fd = map.fd_or_err()?;
let fd = map.fd;
bpf_map_delete_elem(fd, key)
.map(|_| ())
.map_err(|(_, io_error)| {

@ -52,8 +52,6 @@ impl<T: Borrow<MapData>, K: Pod, V: Pod> PerCpuHashMap<T, K, V> {
let data = map.borrow();
check_kv_size::<K, V>(data)?;
let _ = data.fd_or_err()?;
Ok(PerCpuHashMap {
inner: map,
_k: PhantomData,
@ -63,7 +61,7 @@ impl<T: Borrow<MapData>, K: Pod, V: Pod> PerCpuHashMap<T, K, V> {
/// Returns a slice of values - one for each CPU - associated with the key.
pub fn get(&self, key: &K, flags: u64) -> Result<PerCpuValues<V>, MapError> {
let fd = self.inner.borrow().fd_or_err()?;
let fd = self.inner.borrow().fd;
let values =
bpf_map_lookup_elem_per_cpu(fd, key, flags).map_err(|(_, io_error)| SyscallError {
call: "bpf_map_lookup_elem",
@ -120,7 +118,7 @@ impl<T: BorrowMut<MapData>, K: Pod, V: Pod> PerCpuHashMap<T, K, V> {
values: PerCpuValues<V>,
flags: u64,
) -> Result<(), MapError> {
let fd = self.inner.borrow_mut().fd_or_err()?;
let fd = self.inner.borrow_mut().fd;
bpf_map_update_elem_per_cpu(fd, key.borrow(), &values, flags).map_err(
|(_, io_error)| SyscallError {
call: "bpf_map_update_elem",

@ -117,8 +117,6 @@ impl<T: Borrow<MapData>, K: Pod, V: Pod> LpmTrie<T, K, V> {
let data = map.borrow();
check_kv_size::<Key<K>, V>(data)?;
let _ = data.fd_or_err()?;
Ok(LpmTrie {
inner: map,
_k: PhantomData,
@ -128,7 +126,7 @@ impl<T: Borrow<MapData>, K: Pod, V: Pod> LpmTrie<T, K, V> {
/// Returns a copy of the value associated with the longest prefix matching key in the LpmTrie.
pub fn get(&self, key: &Key<K>, flags: u64) -> Result<V, MapError> {
let fd = self.inner.borrow().fd_or_err()?;
let fd = self.inner.borrow().fd;
let value = bpf_map_lookup_elem(fd, key, flags).map_err(|(_, io_error)| SyscallError {
call: "bpf_map_lookup_elem",
io_error,
@ -157,7 +155,7 @@ impl<T: BorrowMut<MapData>, K: Pod, V: Pod> LpmTrie<T, K, V> {
value: impl Borrow<V>,
flags: u64,
) -> Result<(), MapError> {
let fd = self.inner.borrow().fd_or_err()?;
let fd = self.inner.borrow().fd;
bpf_map_update_elem(fd, Some(key), value.borrow(), flags).map_err(|(_, io_error)| {
SyscallError {
call: "bpf_map_update_elem",
@ -172,7 +170,7 @@ impl<T: BorrowMut<MapData>, K: Pod, V: Pod> LpmTrie<T, K, V> {
///
/// Both the prefix and data must match exactly - this method does not do a longest prefix match.
pub fn remove(&mut self, key: &Key<K>) -> Result<(), MapError> {
let fd = self.inner.borrow().fd_or_err()?;
let fd = self.inner.borrow().fd;
bpf_map_delete_elem(fd, key)
.map(|_| ())
.map_err(|(_, io_error)| {
@ -228,18 +226,24 @@ mod tests {
})
}
fn new_map(obj: obj::Map) -> MapData {
override_syscall(|call| match call {
Syscall::Bpf {
cmd: bpf_cmd::BPF_MAP_CREATE,
..
} => Ok(1337),
call => panic!("unexpected syscall {:?}", call),
});
MapData::create(obj, "foo", None).unwrap()
}
fn sys_error(value: i32) -> SysResult<c_long> {
Err((-1, io::Error::from_raw_os_error(value)))
}
#[test]
fn test_wrong_key_size() {
let map = MapData {
obj: new_obj_map(),
fd: None,
pinned: false,
btf_fd: None,
};
let map = new_map(new_obj_map());
assert_matches!(
LpmTrie::<_, u16, u32>::new(&map),
Err(MapError::InvalidKeySize {
@ -251,12 +255,7 @@ mod tests {
#[test]
fn test_wrong_value_size() {
let map = MapData {
obj: new_obj_map(),
fd: None,
pinned: false,
btf_fd: None,
};
let map = new_map(new_obj_map());
assert_matches!(
LpmTrie::<_, u32, u16>::new(&map),
Err(MapError::InvalidValueSize {
@ -268,26 +267,21 @@ mod tests {
#[test]
fn test_try_from_wrong_map() {
let map_data = MapData {
obj: obj::Map::Legacy(LegacyMap {
def: bpf_map_def {
map_type: BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32,
key_size: 4,
value_size: 4,
max_entries: 1024,
..Default::default()
},
section_index: 0,
section_kind: BpfSectionKind::Maps,
symbol_index: None,
data: Vec::new(),
}),
fd: None,
btf_fd: None,
pinned: false,
};
let map = Map::PerfEventArray(map_data);
let map = new_map(obj::Map::Legacy(LegacyMap {
def: bpf_map_def {
map_type: BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32,
key_size: 4,
value_size: 4,
max_entries: 1024,
..Default::default()
},
section_index: 0,
section_kind: BpfSectionKind::Maps,
symbol_index: None,
data: Vec::new(),
}));
let map = Map::PerfEventArray(map);
assert_matches!(
LpmTrie::<_, u32, u32>::try_from(&map),
@ -295,59 +289,30 @@ mod tests {
);
}
#[test]
fn test_new_not_created() {
let mut map = MapData {
obj: new_obj_map(),
fd: None,
pinned: false,
btf_fd: None,
};
assert_matches!(
LpmTrie::<_, u32, u32>::new(&mut map),
Err(MapError::NotCreated { .. })
);
}
#[test]
fn test_new_ok() {
let mut map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
assert!(LpmTrie::<_, u32, u32>::new(&mut map).is_ok());
let map = new_map(new_obj_map());
assert!(LpmTrie::<_, u32, u32>::new(&map).is_ok());
}
#[test]
fn test_try_from_ok() {
let map_data = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let map = Map::LpmTrie(map_data);
let map = new_map(new_obj_map());
let map = Map::LpmTrie(map);
assert!(LpmTrie::<_, u32, u32>::try_from(&map).is_ok())
}
#[test]
fn test_insert_syscall_error() {
override_syscall(|_| sys_error(EFAULT));
let mut map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let mut map = new_map(new_obj_map());
let mut trie = LpmTrie::<_, u32, u32>::new(&mut map).unwrap();
let ipaddr = Ipv4Addr::new(8, 8, 8, 8);
let key = Key::new(16, u32::from(ipaddr).to_be());
override_syscall(|_| sys_error(EFAULT));
assert_matches!(
trie.insert(&key, 1, 0),
Err(MapError::SyscallError(SyscallError { call: "bpf_map_update_elem", io_error })) if io_error.raw_os_error() == Some(EFAULT)
@ -356,6 +321,11 @@ mod tests {
#[test]
fn test_insert_ok() {
let mut map = new_map(new_obj_map());
let mut trie = LpmTrie::<_, u32, u32>::new(&mut map).unwrap();
let ipaddr = Ipv4Addr::new(8, 8, 8, 8);
let key = Key::new(16, u32::from(ipaddr).to_be());
override_syscall(|call| match call {
Syscall::Bpf {
cmd: bpf_cmd::BPF_MAP_UPDATE_ELEM,
@ -364,32 +334,18 @@ mod tests {
_ => sys_error(EFAULT),
});
let mut map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let mut trie = LpmTrie::<_, u32, u32>::new(&mut map).unwrap();
let ipaddr = Ipv4Addr::new(8, 8, 8, 8);
let key = Key::new(16, u32::from(ipaddr).to_be());
assert!(trie.insert(&key, 1, 0).is_ok());
}
#[test]
fn test_remove_syscall_error() {
override_syscall(|_| sys_error(EFAULT));
let mut map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let mut map = new_map(new_obj_map());
let mut trie = LpmTrie::<_, u32, u32>::new(&mut map).unwrap();
let ipaddr = Ipv4Addr::new(8, 8, 8, 8);
let key = Key::new(16, u32::from(ipaddr).to_be());
override_syscall(|_| sys_error(EFAULT));
assert_matches!(
trie.remove(&key),
Err(MapError::SyscallError(SyscallError { call: "bpf_map_delete_elem", io_error })) if io_error.raw_os_error() == Some(EFAULT)
@ -398,6 +354,11 @@ mod tests {
#[test]
fn test_remove_ok() {
let mut map = new_map(new_obj_map());
let mut trie = LpmTrie::<_, u32, u32>::new(&mut map).unwrap();
let ipaddr = Ipv4Addr::new(8, 8, 8, 8);
let key = Key::new(16, u32::from(ipaddr).to_be());
override_syscall(|call| match call {
Syscall::Bpf {
cmd: bpf_cmd::BPF_MAP_DELETE_ELEM,
@ -406,31 +367,18 @@ mod tests {
_ => sys_error(EFAULT),
});
let mut map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let mut trie = LpmTrie::<_, u32, u32>::new(&mut map).unwrap();
let ipaddr = Ipv4Addr::new(8, 8, 8, 8);
let key = Key::new(16, u32::from(ipaddr).to_be());
assert!(trie.remove(&key).is_ok());
}
#[test]
fn test_get_syscall_error() {
override_syscall(|_| sys_error(EFAULT));
let map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let map = new_map(new_obj_map());
let trie = LpmTrie::<_, u32, u32>::new(&map).unwrap();
let ipaddr = Ipv4Addr::new(8, 8, 8, 8);
let key = Key::new(16, u32::from(ipaddr).to_be());
override_syscall(|_| sys_error(EFAULT));
assert_matches!(
trie.get(&key, 0),
Err(MapError::SyscallError(SyscallError { call: "bpf_map_lookup_elem", io_error })) if io_error.raw_os_error() == Some(EFAULT)
@ -439,6 +387,11 @@ mod tests {
#[test]
fn test_get_not_found() {
let map = new_map(new_obj_map());
let trie = LpmTrie::<_, u32, u32>::new(&map).unwrap();
let ipaddr = Ipv4Addr::new(8, 8, 8, 8);
let key = Key::new(16, u32::from(ipaddr).to_be());
override_syscall(|call| match call {
Syscall::Bpf {
cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
@ -446,15 +399,6 @@ mod tests {
} => sys_error(ENOENT),
_ => sys_error(EFAULT),
});
let map = MapData {
obj: new_obj_map(),
fd: Some(42),
pinned: false,
btf_fd: None,
};
let trie = LpmTrie::<_, u32, u32>::new(&map).unwrap();
let ipaddr = Ipv4Addr::new(8, 8, 8, 8);
let key = Key::new(16, u32::from(ipaddr).to_be());
assert_matches!(trie.get(&key, 0), Err(MapError::KeyNotFound));
}

@ -42,10 +42,9 @@ use std::{
marker::PhantomData,
mem,
ops::Deref,
os::fd::{AsFd as _, AsRawFd, IntoRawFd as _, OwnedFd, RawFd},
os::fd::{AsFd as _, AsRawFd, BorrowedFd, IntoRawFd as _, OwnedFd, RawFd},
path::Path,
ptr,
sync::Arc,
};
use crate::util::KernelVersion;
@ -104,17 +103,6 @@ pub enum MapError {
name: String,
},
/// The map has not been created
#[error("the map has not been created")]
NotCreated,
/// The map has already been created
#[error("the map `{name}` has already been created")]
AlreadyCreated {
/// Map name
name: String,
},
/// Failed to create map
#[error("failed to create map `{name}` with code {code}")]
CreateError {
@ -481,62 +469,71 @@ pub(crate) fn check_v_size<V>(map: &MapData) -> Result<(), MapError> {
#[derive(Debug)]
pub struct MapData {
pub(crate) obj: obj::Map,
pub(crate) fd: Option<RawFd>,
pub(crate) btf_fd: Option<Arc<OwnedFd>>,
pub(crate) fd: RawFd,
/// Indicates if this map has been pinned to bpffs
pub pinned: bool,
}
impl MapData {
/// Creates a new map with the provided `name`
pub fn create(&mut self, name: &str) -> Result<RawFd, MapError> {
if self.fd.is_some() {
return Err(MapError::AlreadyCreated { name: name.into() });
}
pub fn create(
obj: obj::Map,
name: &str,
btf_fd: Option<BorrowedFd<'_>>,
) -> Result<Self, MapError> {
let c_name = CString::new(name).map_err(|_| MapError::InvalidName { name: name.into() })?;
#[cfg(not(test))]
let kernel_version = KernelVersion::current().unwrap();
#[cfg(test)]
let kernel_version = KernelVersion::new(0xff, 0xff, 0xff);
let fd = bpf_create_map(
&c_name,
&self.obj,
self.btf_fd.as_ref().map(|f| f.as_fd()),
kernel_version,
)
.map_err(|(code, io_error)| {
if kernel_version < KernelVersion::new(5, 11, 0) {
maybe_warn_rlimit();
}
let fd =
bpf_create_map(&c_name, &obj, btf_fd, kernel_version).map_err(|(code, io_error)| {
if kernel_version < KernelVersion::new(5, 11, 0) {
maybe_warn_rlimit();
}
MapError::CreateError {
name: name.into(),
code,
io_error,
}
})?;
MapError::CreateError {
name: name.into(),
code,
io_error,
}
})?;
Ok(*self.fd.insert(fd as RawFd))
Ok(Self {
obj,
fd: fd as RawFd,
pinned: false,
})
}
pub(crate) fn open_pinned<P: AsRef<Path>>(
&mut self,
name: &str,
pub(crate) fn create_pinned<P: AsRef<Path>>(
path: P,
) -> Result<RawFd, MapError> {
if self.fd.is_some() {
return Err(MapError::AlreadyCreated { name: name.into() });
}
obj: obj::Map,
name: &str,
btf_fd: Option<BorrowedFd<'_>>,
) -> Result<Self, MapError> {
// try to open map in case it's already pinned
let map_path = path.as_ref().join(name);
let path_string = CString::new(map_path.to_str().unwrap()).unwrap();
let fd = bpf_get_object(&path_string).map_err(|(_, io_error)| SyscallError {
match bpf_get_object(&path_string).map_err(|(_, io_error)| SyscallError {
call: "BPF_OBJ_GET",
io_error,
})?;
Ok(*self.fd.insert(fd.into_raw_fd()))
}) {
Ok(fd) => Ok(Self {
obj,
fd: fd.into_raw_fd(),
pinned: false,
}),
Err(_) => {
let mut map = Self::create(obj, name, btf_fd)?;
map.pin(name, path).map_err(|error| MapError::PinError {
name: Some(name.into()),
error,
})?;
Ok(map)
}
}
}
/// Loads a map from a pinned path in bpffs.
@ -560,8 +557,7 @@ impl MapData {
Ok(MapData {
obj: parse_map_info(info, PinningType::ByName),
fd: Some(fd.into_raw_fd()),
btf_fd: None,
fd: fd.into_raw_fd(),
pinned: true,
})
}
@ -576,61 +572,54 @@ impl MapData {
Ok(MapData {
obj: parse_map_info(info, PinningType::None),
fd: Some(fd.into_raw_fd()),
btf_fd: None,
fd: fd.into_raw_fd(),
pinned: false,
})
}
pub(crate) fn fd_or_err(&self) -> Result<RawFd, MapError> {
self.fd.ok_or(MapError::NotCreated)
}
pub(crate) fn pin<P: AsRef<Path>>(&mut self, name: &str, path: P) -> Result<(), PinError> {
if self.pinned {
let Self { fd, pinned, obj: _ } = self;
if *pinned {
return Err(PinError::AlreadyPinned { name: name.into() });
}
let map_path = path.as_ref().join(name);
let fd = self.fd.ok_or(PinError::NoFd {
name: name.to_string(),
})?;
let path_string = CString::new(map_path.to_string_lossy().into_owned()).map_err(|e| {
PinError::InvalidPinPath {
error: e.to_string(),
}
})?;
bpf_pin_object(fd, &path_string).map_err(|(_, io_error)| SyscallError {
bpf_pin_object(*fd, &path_string).map_err(|(_, io_error)| SyscallError {
call: "BPF_OBJ_PIN",
io_error,
})?;
self.pinned = true;
*pinned = true;
Ok(())
}
/// Returns the file descriptor of the map.
///
/// Can be converted to [`RawFd`] using [`AsRawFd`].
pub fn fd(&self) -> Option<MapFd> {
self.fd.map(MapFd)
pub fn fd(&self) -> MapFd {
MapFd(self.fd)
}
}
impl Drop for MapData {
fn drop(&mut self) {
// TODO: Replace this with an OwnedFd once that is stabilized.
if let Some(fd) = self.fd.take() {
unsafe { libc::close(fd) };
}
//
// SAFETY: `drop` is only called once.
unsafe { libc::close(self.fd) };
}
}
impl Clone for MapData {
fn clone(&self) -> MapData {
MapData {
obj: self.obj.clone(),
fd: self.fd.map(|fd| unsafe { libc::dup(fd) }),
btf_fd: self.btf_fd.as_ref().map(Arc::clone),
pinned: self.pinned,
fn clone(&self) -> Self {
let Self { obj, fd, pinned } = self;
Self {
obj: obj.clone(),
fd: unsafe { libc::dup(*fd) },
pinned: *pinned,
}
}
}
@ -669,14 +658,7 @@ impl<K: Pod> Iterator for MapKeys<'_, K> {
return None;
}
let fd = match self.map.fd_or_err() {
Ok(fd) => fd,
Err(e) => {
self.err = true;
return Some(Err(e));
}
};
let fd = self.map.fd;
let key =
bpf_map_get_next_key(fd, self.key.as_ref()).map_err(|(_, io_error)| SyscallError {
call: "bpf_map_get_next_key",
@ -859,15 +841,6 @@ mod tests {
})
}
fn new_map() -> MapData {
MapData {
obj: new_obj_map(),
fd: None,
pinned: false,
btf_fd: None,
}
}
#[test]
fn test_create() {
override_syscall(|call| match call {
@ -878,29 +851,27 @@ mod tests {
_ => Err((-1, io::Error::from_raw_os_error(EFAULT))),
});
let mut map = new_map();
assert_matches!(map.create("foo"), Ok(42));
assert_eq!(map.fd, Some(42));
assert_matches!(map.create("foo"), Err(MapError::AlreadyCreated { .. }));
assert_matches!(
MapData::create(new_obj_map(), "foo", None),
Ok(MapData {
obj: _,
fd: 42,
pinned: false
})
);
}
#[test]
fn test_create_failed() {
override_syscall(|_| Err((-42, io::Error::from_raw_os_error(EFAULT))));
let mut map = new_map();
let ret = map.create("foo");
assert_matches!(ret, Err(MapError::CreateError { .. }));
if let Err(MapError::CreateError {
name,
code,
io_error,
}) = ret
{
assert_eq!(name, "foo");
assert_eq!(code, -42);
assert_eq!(io_error.raw_os_error(), Some(EFAULT));
}
assert_eq!(map.fd, None);
assert_matches!(
MapData::create(new_obj_map(), "foo", None),
Err(MapError::CreateError { name, code, io_error }) => {
assert_eq!(name, "foo");
assert_eq!(code, -42);
assert_eq!(io_error.raw_os_error(), Some(EFAULT));
}
);
}
}

@ -1,7 +1,7 @@
use bytes::BytesMut;
use std::{
borrow::{Borrow, BorrowMut},
os::fd::{AsRawFd, RawFd},
os::fd::{AsRawFd as _, RawFd},
};
// See https://doc.rust-lang.org/cargo/reference/features.html#mutually-exclusive-features.

@ -162,8 +162,6 @@ pub struct PerfEventArray<T> {
impl<T: Borrow<MapData>> PerfEventArray<T> {
pub(crate) fn new(map: T) -> Result<PerfEventArray<T>, MapError> {
let _fd = map.borrow().fd_or_err()?;
Ok(PerfEventArray {
map: Arc::new(map),
page_size: page_size(),
@ -184,7 +182,7 @@ impl<T: BorrowMut<MapData> + Borrow<MapData>> PerfEventArray<T> {
// this cannot fail as new() checks that the fd is open
let map_data: &MapData = self.map.deref().borrow();
let map_fd = map_data.fd_or_err().unwrap();
let map_fd = map_data.fd;
let buf = PerfBuffer::open(index, self.page_size, page_count.unwrap_or(2))?;
bpf_map_update_elem(map_fd, Some(&index), &buf.as_raw_fd(), 0)
.map_err(|(_, io_error)| io_error)?;

@ -38,8 +38,6 @@ impl<T: Borrow<MapData>, V: Pod> Queue<T, V> {
let data = map.borrow();
check_kv_size::<(), V>(data)?;
let _fd = data.fd_or_err()?;
Ok(Queue {
inner: map,
_v: PhantomData,
@ -62,7 +60,7 @@ impl<T: BorrowMut<MapData>, V: Pod> Queue<T, V> {
/// Returns [`MapError::ElementNotFound`] if the queue is empty, [`MapError::SyscallError`]
/// if `bpf_map_lookup_and_delete_elem` fails.
pub fn pop(&mut self, flags: u64) -> Result<V, MapError> {
let fd = self.inner.borrow().fd_or_err()?;
let fd = self.inner.borrow().fd;
let value = bpf_map_lookup_and_delete_elem::<u32, _>(fd, None, flags).map_err(
|(_, io_error)| SyscallError {
@ -79,7 +77,7 @@ impl<T: BorrowMut<MapData>, V: Pod> Queue<T, V> {
///
/// [`MapError::SyscallError`] if `bpf_map_update_elem` fails.
pub fn push(&mut self, value: impl Borrow<V>, flags: u64) -> Result<(), MapError> {
let fd = self.inner.borrow().fd_or_err()?;
let fd = self.inner.borrow().fd;
bpf_map_push_elem(fd, value.borrow(), flags).map_err(|(_, io_error)| SyscallError {
call: "bpf_map_push_elem",
io_error,

@ -72,7 +72,6 @@ impl<T: Borrow<MapData>, K: Pod> SockHash<T, K> {
pub(crate) fn new(map: T) -> Result<SockHash<T, K>, MapError> {
let data = map.borrow();
check_kv_size::<K, u32>(data)?;
let _ = data.fd_or_err()?;
Ok(SockHash {
inner: map,
@ -82,7 +81,7 @@ impl<T: Borrow<MapData>, K: Pod> SockHash<T, K> {
/// Returns the fd of the socket stored at the given key.
pub fn get(&self, key: &K, flags: u64) -> Result<RawFd, MapError> {
let fd = self.inner.borrow().fd_or_err()?;
let fd = self.inner.borrow().fd;
let value = bpf_map_lookup_elem(fd, key, flags).map_err(|(_, io_error)| SyscallError {
call: "bpf_map_lookup_elem",
io_error,
@ -107,7 +106,7 @@ impl<T: Borrow<MapData>, K: Pod> SockHash<T, K> {
/// The returned file descriptor can be used to attach programs that work with
/// socket maps, like [`SkMsg`](crate::programs::SkMsg) and [`SkSkb`](crate::programs::SkSkb).
pub fn fd(&self) -> Result<SockMapFd, MapError> {
Ok(SockMapFd(self.inner.borrow().fd_or_err()?))
Ok(SockMapFd(self.inner.borrow().fd))
}
}

@ -49,8 +49,6 @@ impl<T: Borrow<MapData>> SockMap<T> {
let data = map.borrow();
check_kv_size::<u32, RawFd>(data)?;
let _fd = data.fd_or_err()?;
Ok(SockMap { inner: map })
}
@ -65,7 +63,7 @@ impl<T: Borrow<MapData>> SockMap<T> {
/// The returned file descriptor can be used to attach programs that work with
/// socket maps, like [`SkMsg`](crate::programs::SkMsg) and [`SkSkb`](crate::programs::SkSkb).
pub fn fd(&self) -> Result<SockMapFd, MapError> {
Ok(SockMapFd(self.inner.borrow().fd_or_err()?))
Ok(SockMapFd(self.inner.borrow().fd))
}
}
@ -73,7 +71,7 @@ impl<T: BorrowMut<MapData>> SockMap<T> {
/// Stores a socket into the map.
pub fn set<I: AsRawFd>(&mut self, index: u32, socket: &I, flags: u64) -> Result<(), MapError> {
let data = self.inner.borrow_mut();
let fd = data.fd_or_err()?;
let fd = data.fd;
check_bounds(data, index)?;
bpf_map_update_elem(fd, Some(&index), &socket.as_raw_fd(), flags).map_err(
|(_, io_error)| SyscallError {
@ -87,7 +85,7 @@ impl<T: BorrowMut<MapData>> SockMap<T> {
/// Removes the socket stored at `index` from the map.
pub fn clear_index(&mut self, index: &u32) -> Result<(), MapError> {
let data = self.inner.borrow_mut();
let fd = data.fd_or_err()?;
let fd = data.fd;
check_bounds(data, *index)?;
bpf_map_delete_elem(fd, index)
.map(|_| ())

@ -38,8 +38,6 @@ impl<T: Borrow<MapData>, V: Pod> Stack<T, V> {
let data = map.borrow();
check_kv_size::<(), V>(data)?;
let _fd = data.fd_or_err()?;
Ok(Stack {
inner: map,
_v: PhantomData,
@ -62,7 +60,7 @@ impl<T: BorrowMut<MapData>, V: Pod> Stack<T, V> {
/// Returns [`MapError::ElementNotFound`] if the stack is empty, [`MapError::SyscallError`]
/// if `bpf_map_lookup_and_delete_elem` fails.
pub fn pop(&mut self, flags: u64) -> Result<V, MapError> {
let fd = self.inner.borrow().fd_or_err()?;
let fd = self.inner.borrow().fd;
let value = bpf_map_lookup_and_delete_elem::<u32, _>(fd, None, flags).map_err(
|(_, io_error)| SyscallError {
@ -79,7 +77,7 @@ impl<T: BorrowMut<MapData>, V: Pod> Stack<T, V> {
///
/// [`MapError::SyscallError`] if `bpf_map_update_elem` fails.
pub fn push(&mut self, value: impl Borrow<V>, flags: u64) -> Result<(), MapError> {
let fd = self.inner.borrow().fd_or_err()?;
let fd = self.inner.borrow().fd;
bpf_map_update_elem(fd, None::<&u32>, value.borrow(), flags).map_err(|(_, io_error)| {
SyscallError {
call: "bpf_map_update_elem",

@ -1,12 +1,7 @@
//! A hash map of kernel or user space stack traces.
//!
//! See [`StackTraceMap`] for documentation and examples.
use std::{
borrow::{Borrow, Cow},
fs, io, mem,
path::Path,
str::FromStr,
};
use std::{borrow::Borrow, fs, io, mem, path::Path, str::FromStr};
use crate::{
maps::{IterableMap, MapData, MapError, MapIter, MapKeys},
@ -51,15 +46,19 @@ use crate::{
/// // here we resolve symbol names using kernel symbols. If this was a user space stack (for
/// // example captured from a uprobe), you'd have to load the symbols using some other mechanism
/// // (eg loading the target binary debuginfo)
/// for frame in stack_trace.resolve(&ksyms).frames() {
/// println!(
/// "{:#x} {}",
/// frame.ip,
/// frame
/// .symbol_name
/// .as_deref()
/// .unwrap_or("[unknown symbol name]")
/// );
/// for frame in stack_trace.frames() {
/// if let Some(sym) = ksyms.range(..=frame.ip).next_back().map(|(_, s)| s) {
/// println!(
/// "{:#x} {}",
/// frame.ip,
/// sym
/// );
/// } else {
/// println!(
/// "{:#x}",
/// frame.ip
/// );
/// }
/// }
///
/// # Ok::<(), Error>(())
@ -90,7 +89,6 @@ impl<T: Borrow<MapData>> StackTraceMap<T> {
if size > max_stack_depth * mem::size_of::<u64>() {
return Err(MapError::InvalidValueSize { size, expected });
}
let _fd = data.fd_or_err()?;
Ok(StackTraceMap {
inner: map,
@ -105,7 +103,7 @@ impl<T: Borrow<MapData>> StackTraceMap<T> {
/// Returns [`MapError::KeyNotFound`] if there is no stack trace with the
/// given `stack_id`, or [`MapError::SyscallError`] if `bpf_map_lookup_elem` fails.
pub fn get(&self, stack_id: &u32, flags: u64) -> Result<StackTrace, MapError> {
let fd = self.inner.borrow().fd_or_err()?;
let fd = self.inner.borrow().fd;
let mut frames = vec![0; self.max_stack_depth];
bpf_map_lookup_elem_ptr(fd, Some(stack_id), frames.as_mut_ptr(), flags)
@ -118,10 +116,7 @@ impl<T: Borrow<MapData>> StackTraceMap<T> {
let frames = frames
.into_iter()
.take_while(|ip| *ip != 0)
.map(|ip| StackFrame {
ip,
symbol_name: None,
})
.map(|ip| StackFrame { ip })
.collect::<Vec<_>>();
Ok(StackTrace {
@ -162,12 +157,6 @@ impl<'a, T: Borrow<MapData>> IntoIterator for &'a StackTraceMap<T> {
}
}
/// A resolver for symbols based on an address obtained from a stack trace.
pub trait SymbolResolver {
/// Resolve a symbol for a given address, if possible.
fn resolve_symbol(&self, addr: u64) -> Option<Cow<'_, str>>;
}
/// A kernel or user space stack trace.
///
/// See the [`StackTraceMap`] documentation for examples.
@ -178,19 +167,6 @@ pub struct StackTrace {
}
impl StackTrace {
/// Resolves symbol names using the given symbol map.
///
/// You can use [`util::kernel_symbols()`](crate::util::kernel_symbols) to load kernel symbols. For
/// user-space traces you need to provide the symbols, for example loading
/// them from debug info.
pub fn resolve<R: SymbolResolver>(&mut self, symbols: &R) -> &StackTrace {
for frame in self.frames.iter_mut() {
frame.symbol_name = symbols.resolve_symbol(frame.ip).map(|s| s.into_owned())
}
self
}
/// Returns the frames in this stack trace.
pub fn frames(&self) -> &[StackFrame] {
&self.frames
@ -201,11 +177,6 @@ impl StackTrace {
pub struct StackFrame {
/// The instruction pointer of this frame.
pub ip: u64,
/// The symbol name corresponding to the start of this frame.
///
/// Set to `Some()` if the frame address can be found in the symbols passed
/// to [`StackTrace::resolve`].
pub symbol_name: Option<String>,
}
fn sysctl<T: FromStr>(key: &str) -> Result<T, io::Error> {

@ -1,7 +1,7 @@
//! Cgroup device programs.
use crate::util::KernelVersion;
use std::os::fd::AsRawFd;
use std::os::fd::{AsFd as _, AsRawFd};
use crate::{
generated::{bpf_attach_type::BPF_CGROUP_DEVICE, bpf_prog_type::BPF_PROG_TYPE_CGROUP_DEVICE},
@ -61,7 +61,9 @@ impl CgroupDevice {
///
/// The returned value can be used to detach, see [CgroupDevice::detach]
pub fn attach<T: AsRawFd>(&mut self, cgroup: T) -> Result<CgroupDeviceLinkId, ProgramError> {
let prog_fd = self.data.fd_or_err()?;
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let prog_fd = prog_fd.as_raw_fd();
let cgroup_fd = cgroup.as_raw_fd();
if KernelVersion::current().unwrap() >= KernelVersion::new(5, 7, 0) {

@ -1,7 +1,11 @@
//! Cgroup skb programs.
use crate::util::KernelVersion;
use std::{hash::Hash, os::fd::AsRawFd, path::Path};
use std::{
hash::Hash,
os::fd::{AsFd as _, AsRawFd},
path::Path,
};
use crate::{
generated::{
@ -88,7 +92,9 @@ impl CgroupSkb {
cgroup: T,
attach_type: CgroupSkbAttachType,
) -> Result<CgroupSkbLinkId, ProgramError> {
let prog_fd = self.data.fd_or_err()?;
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let prog_fd = prog_fd.as_raw_fd();
let cgroup_fd = cgroup.as_raw_fd();
let attach_type = match attach_type {

@ -3,7 +3,11 @@
pub use aya_obj::programs::CgroupSockAttachType;
use crate::util::KernelVersion;
use std::{hash::Hash, os::fd::AsRawFd, path::Path};
use std::{
hash::Hash,
os::fd::{AsFd as _, AsRawFd},
path::Path,
};
use crate::{
generated::bpf_prog_type::BPF_PROG_TYPE_CGROUP_SOCK,
@ -67,7 +71,9 @@ impl CgroupSock {
///
/// The returned value can be used to detach, see [CgroupSock::detach].
pub fn attach<T: AsRawFd>(&mut self, cgroup: T) -> Result<CgroupSockLinkId, ProgramError> {
let prog_fd = self.data.fd_or_err()?;
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let prog_fd = prog_fd.as_raw_fd();
let cgroup_fd = cgroup.as_raw_fd();
let attach_type = self.data.expected_attach_type.unwrap();
if KernelVersion::current().unwrap() >= KernelVersion::new(5, 7, 0) {

@ -3,7 +3,11 @@
pub use aya_obj::programs::CgroupSockAddrAttachType;
use crate::util::KernelVersion;
use std::{hash::Hash, os::fd::AsRawFd, path::Path};
use std::{
hash::Hash,
os::fd::{AsFd as _, AsRawFd},
path::Path,
};
use crate::{
generated::bpf_prog_type::BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
@ -68,7 +72,9 @@ impl CgroupSockAddr {
///
/// The returned value can be used to detach, see [CgroupSockAddr::detach].
pub fn attach<T: AsRawFd>(&mut self, cgroup: T) -> Result<CgroupSockAddrLinkId, ProgramError> {
let prog_fd = self.data.fd_or_err()?;
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let prog_fd = prog_fd.as_raw_fd();
let cgroup_fd = cgroup.as_raw_fd();
let attach_type = self.data.expected_attach_type.unwrap();
if KernelVersion::current().unwrap() >= KernelVersion::new(5, 7, 0) {

@ -3,7 +3,11 @@
pub use aya_obj::programs::CgroupSockoptAttachType;
use crate::util::KernelVersion;
use std::{hash::Hash, os::fd::AsRawFd, path::Path};
use std::{
hash::Hash,
os::fd::{AsFd as _, AsRawFd},
path::Path,
};
use crate::{
generated::bpf_prog_type::BPF_PROG_TYPE_CGROUP_SOCKOPT,
@ -65,7 +69,9 @@ impl CgroupSockopt {
///
/// The returned value can be used to detach, see [CgroupSockopt::detach].
pub fn attach<T: AsRawFd>(&mut self, cgroup: T) -> Result<CgroupSockoptLinkId, ProgramError> {
let prog_fd = self.data.fd_or_err()?;
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let prog_fd = prog_fd.as_raw_fd();
let cgroup_fd = cgroup.as_raw_fd();
let attach_type = self.data.expected_attach_type.unwrap();
if KernelVersion::current().unwrap() >= KernelVersion::new(5, 7, 0) {

@ -1,7 +1,10 @@
//! Cgroup sysctl programs.
use crate::util::KernelVersion;
use std::{hash::Hash, os::fd::AsRawFd};
use std::{
hash::Hash,
os::fd::{AsFd as _, AsRawFd},
};
use crate::{
generated::{bpf_attach_type::BPF_CGROUP_SYSCTL, bpf_prog_type::BPF_PROG_TYPE_CGROUP_SYSCTL},
@ -60,7 +63,9 @@ impl CgroupSysctl {
///
/// The returned value can be used to detach, see [CgroupSysctl::detach].
pub fn attach<T: AsRawFd>(&mut self, cgroup: T) -> Result<CgroupSysctlLinkId, ProgramError> {
let prog_fd = self.data.fd_or_err()?;
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let prog_fd = prog_fd.as_raw_fd();
let cgroup_fd = cgroup.as_raw_fd();
if KernelVersion::current().unwrap() >= KernelVersion::new(5, 7, 0) {

@ -1,5 +1,5 @@
//! Extension programs.
use std::os::fd::{AsRawFd, RawFd};
use std::os::fd::{AsFd as _, AsRawFd as _, BorrowedFd, OwnedFd};
use thiserror::Error;
use object::Endianness;
@ -42,6 +42,7 @@ pub enum ExtensionError {
/// prog.attach("eth0", XdpFlags::default())?;
///
/// let prog_fd = prog.fd().unwrap();
/// let prog_fd = prog_fd.try_clone().unwrap();
/// let ext: &mut Extension = bpf.program_mut("extension").unwrap().try_into()?;
/// ext.load(prog_fd, "function_to_replace")?;
/// ext.attach()?;
@ -69,11 +70,10 @@ impl Extension {
/// There are no restrictions on what functions may be replaced, so you could replace
/// the main entry point of your program with an extension.
pub fn load(&mut self, program: ProgramFd, func_name: &str) -> Result<(), ProgramError> {
let target_prog_fd = program.as_raw_fd();
let (btf_fd, btf_id) = get_btf_info(target_prog_fd, func_name)?;
let (btf_fd, btf_id) = get_btf_info(program.as_fd(), func_name)?;
self.data.attach_btf_obj_fd = Some(btf_fd as u32);
self.data.attach_prog_fd = Some(target_prog_fd);
self.data.attach_btf_obj_fd = Some(btf_fd);
self.data.attach_prog_fd = Some(program);
self.data.attach_btf_id = Some(btf_id);
load_program(BPF_PROG_TYPE_EXT, &mut self.data)
}
@ -86,8 +86,16 @@ impl Extension {
/// The returned value can be used to detach the extension and restore the
/// original function, see [Extension::detach].
pub fn attach(&mut self) -> Result<ExtensionLinkId, ProgramError> {
let prog_fd = self.data.fd_or_err()?;
let target_fd = self.data.attach_prog_fd.ok_or(ProgramError::NotLoaded)?;
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let prog_fd = prog_fd.as_raw_fd();
let target_fd = self
.data
.attach_prog_fd
.as_ref()
.ok_or(ProgramError::NotLoaded)?;
let target_fd = target_fd.as_fd();
let target_fd = target_fd.as_raw_fd();
let btf_id = self.data.attach_btf_id.ok_or(ProgramError::NotLoaded)?;
// the attach type must be set as 0, which is bpf_attach_type::BPF_CGROUP_INET_INGRESS
let link_fd = bpf_link_create(prog_fd, target_fd, BPF_CGROUP_INET_INGRESS, Some(btf_id), 0)
@ -113,18 +121,26 @@ impl Extension {
/// original function, see [Extension::detach].
pub fn attach_to_program(
&mut self,
program: ProgramFd,
program: &ProgramFd,
func_name: &str,
) -> Result<ExtensionLinkId, ProgramError> {
let target_fd = program.as_raw_fd();
let target_fd = program.as_fd();
let (_, btf_id) = get_btf_info(target_fd, func_name)?;
let prog_fd = self.data.fd_or_err()?;
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let prog_fd = prog_fd.as_raw_fd();
// the attach type must be set as 0, which is bpf_attach_type::BPF_CGROUP_INET_INGRESS
let link_fd = bpf_link_create(prog_fd, target_fd, BPF_CGROUP_INET_INGRESS, Some(btf_id), 0)
.map_err(|(_, io_error)| SyscallError {
call: "bpf_link_create",
io_error,
})?;
let link_fd = bpf_link_create(
prog_fd,
target_fd.as_raw_fd(),
BPF_CGROUP_INET_INGRESS,
Some(btf_id),
0,
)
.map_err(|(_, io_error)| SyscallError {
call: "bpf_link_create",
io_error,
})?;
self.data
.links
.insert(ExtensionLink::new(FdLink::new(link_fd)))
@ -149,9 +165,9 @@ impl Extension {
/// Retrieves the FD of the BTF object for the provided `prog_fd` and the BTF ID of the function
/// with the name `func_name` within that BTF object.
fn get_btf_info(prog_fd: i32, func_name: &str) -> Result<(RawFd, u32), ProgramError> {
fn get_btf_info(prog_fd: BorrowedFd<'_>, func_name: &str) -> Result<(OwnedFd, u32), ProgramError> {
// retrieve program information
let info = sys::bpf_prog_get_info_by_fd(prog_fd)?;
let info = sys::bpf_prog_get_info_by_fd(prog_fd, &mut [])?;
// btf_id refers to the ID of the program btf that was loaded with bpf(BPF_BTF_LOAD)
if info.btf_id == 0 {
@ -159,36 +175,23 @@ fn get_btf_info(prog_fd: i32, func_name: &str) -> Result<(RawFd, u32), ProgramEr
}
// the bpf fd of the BTF object
let btf_fd = sys::bpf_btf_get_fd_by_id(info.btf_id).map_err(|io_error| SyscallError {
call: "bpf_btf_get_fd_by_id",
io_error,
})?;
let btf_fd = sys::bpf_btf_get_fd_by_id(info.btf_id)?;
// we need to read the btf bytes into a buffer but we don't know the size ahead of time.
// assume 4kb. if this is too small we can resize based on the size obtained in the response.
let mut buf = vec![0u8; 4096];
let btf_info = match sys::btf_obj_get_info_by_fd(btf_fd, &buf) {
Ok(info) => {
if info.btf_size > buf.len() as u32 {
buf.resize(info.btf_size as usize, 0u8);
let btf_info =
sys::btf_obj_get_info_by_fd(btf_fd, &buf).map_err(|io_error| SyscallError {
call: "bpf_prog_get_info_by_fd",
io_error,
})?;
Ok(btf_info)
} else {
Ok(info)
}
loop {
let info = sys::btf_obj_get_info_by_fd(btf_fd.as_fd(), &mut buf)?;
let btf_size = info.btf_size as usize;
if btf_size > buf.len() {
buf.resize(btf_size, 0u8);
continue;
}
Err(io_error) => Err(SyscallError {
call: "bpf_prog_get_info_by_fd",
io_error,
}),
}?;
buf.truncate(btf_size);
break;
}
let btf = Btf::parse(&buf[0..btf_info.btf_size as usize], Endianness::default())
.map_err(ProgramError::Btf)?;
let btf = Btf::parse(&buf, Endianness::default()).map_err(ProgramError::Btf)?;
let btf_id = btf
.id_by_type_name_kind(func_name, BtfKind::Func)

@ -1,13 +1,10 @@
//! Lirc programs.
use std::os::fd::{AsRawFd, IntoRawFd as _, RawFd};
use std::os::fd::{AsFd as _, AsRawFd, BorrowedFd, IntoRawFd as _, RawFd};
use crate::{
generated::{bpf_attach_type::BPF_LIRC_MODE2, bpf_prog_type::BPF_PROG_TYPE_LIRC_MODE2},
programs::{load_program, query, Link, ProgramData, ProgramError, ProgramInfo},
sys::{
bpf_prog_attach, bpf_prog_detach, bpf_prog_get_fd_by_id, bpf_prog_get_info_by_fd,
SyscallError,
},
sys::{bpf_prog_attach, bpf_prog_detach, bpf_prog_get_fd_by_id, SyscallError},
};
use libc::{close, dup};
@ -64,7 +61,9 @@ impl LircMode2 {
///
/// The returned value can be used to detach, see [LircMode2::detach].
pub fn attach<T: AsRawFd>(&mut self, lircdev: T) -> Result<LircLinkId, ProgramError> {
let prog_fd = self.data.fd_or_err()?;
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let prog_fd = prog_fd.as_raw_fd();
let lircdev_fd = lircdev.as_raw_fd();
bpf_prog_attach(prog_fd, lircdev_fd, BPF_LIRC_MODE2).map_err(|(_, io_error)| {
@ -131,9 +130,9 @@ impl LircLink {
/// Get ProgramInfo from this link
pub fn info(&self) -> Result<ProgramInfo, ProgramError> {
bpf_prog_get_info_by_fd(self.prog_fd)
.map(ProgramInfo)
.map_err(Into::into)
// SAFETY: TODO(https://github.com/aya-rs/aya/issues/612): make this safe by not holding `RawFd`s.
let prog_fd = unsafe { BorrowedFd::borrow_raw(self.prog_fd) };
ProgramInfo::new_from_fd(prog_fd)
}
}

@ -68,9 +68,11 @@ use libc::ENOSPC;
use std::{
ffi::CString,
io,
os::fd::{AsFd, AsRawFd, IntoRawFd as _, OwnedFd, RawFd},
num::NonZeroU32,
os::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd},
path::{Path, PathBuf},
sync::Arc,
time::{Duration, SystemTime},
};
use thiserror::Error;
@ -108,6 +110,7 @@ use crate::{
maps::MapError,
obj::{self, btf::BtfError, Function, VerifierLog},
pin::PinError,
programs::utils::{boot_time, get_fdinfo},
sys::{
bpf_btf_get_fd_by_id, bpf_get_object, bpf_link_get_fd_by_id, bpf_link_get_info_by_fd,
bpf_load_program, bpf_pin_object, bpf_prog_get_fd_by_id, bpf_prog_get_info_by_fd,
@ -211,12 +214,23 @@ pub enum ProgramError {
}
/// A [`Program`] file descriptor.
#[derive(Copy, Clone)]
pub struct ProgramFd(RawFd);
#[derive(Debug)]
pub struct ProgramFd(OwnedFd);
impl ProgramFd {
/// Creates a new `ProgramFd` instance that shares the same underlying file
/// description as the existing `ProgramFd` instance.
pub fn try_clone(&self) -> Result<Self, ProgramError> {
let Self(inner) = self;
let inner = inner.try_clone()?;
Ok(Self(inner))
}
}
impl AsRawFd for ProgramFd {
fn as_raw_fd(&self) -> RawFd {
self.0
impl AsFd for ProgramFd {
fn as_fd(&self) -> BorrowedFd<'_> {
let Self(fd) = self;
fd.as_fd()
}
}
@ -368,8 +382,7 @@ impl Program {
/// Returns the file descriptor of a program.
///
/// Can be used to add a program to a [`crate::maps::ProgramArray`] or attach an [`Extension`] program.
/// Can be converted to [`RawFd`] using [`AsRawFd`].
pub fn fd(&self) -> Option<ProgramFd> {
pub fn fd(&self) -> Result<&ProgramFd, ProgramError> {
match self {
Program::KProbe(p) => p.fd(),
Program::UProbe(p) => p.fd(),
@ -403,12 +416,12 @@ impl Program {
pub(crate) struct ProgramData<T: Link> {
pub(crate) name: Option<String>,
pub(crate) obj: Option<(obj::Program, obj::Function)>,
pub(crate) fd: Option<RawFd>,
pub(crate) fd: Option<ProgramFd>,
pub(crate) links: LinkMap<T>,
pub(crate) expected_attach_type: Option<bpf_attach_type>,
pub(crate) attach_btf_obj_fd: Option<u32>,
pub(crate) attach_btf_obj_fd: Option<OwnedFd>,
pub(crate) attach_btf_id: Option<u32>,
pub(crate) attach_prog_fd: Option<RawFd>,
pub(crate) attach_prog_fd: Option<ProgramFd>,
pub(crate) btf_fd: Option<Arc<OwnedFd>>,
pub(crate) verifier_log_level: VerifierLogLevel,
pub(crate) path: Option<PathBuf>,
@ -450,21 +463,14 @@ impl<T: Link> ProgramData<T> {
} else {
None
};
let attach_btf_obj_fd = if info.attach_btf_obj_id > 0 {
let fd =
bpf_btf_get_fd_by_id(info.attach_btf_obj_id).map_err(|io_error| SyscallError {
call: "bpf_btf_get_fd_by_id",
io_error,
})?;
Some(fd as u32)
} else {
None
};
let attach_btf_obj_fd = (info.attach_btf_obj_id != 0)
.then(|| bpf_btf_get_fd_by_id(info.attach_btf_obj_id))
.transpose()?;
Ok(ProgramData {
name,
obj: None,
fd: Some(fd.into_raw_fd()),
fd: Some(ProgramFd(fd)),
links: LinkMap::new(),
expected_attach_type: None,
attach_btf_obj_fd,
@ -483,20 +489,21 @@ impl<T: Link> ProgramData<T> {
) -> Result<ProgramData<T>, ProgramError> {
let path_string =
CString::new(path.as_ref().as_os_str().to_string_lossy().as_bytes()).unwrap();
let fd = bpf_get_object(&path_string).map_err(|(_, io_error)| SyscallError {
call: "bpf_obj_get",
io_error,
})?;
let info = bpf_prog_get_info_by_fd(fd.as_raw_fd())?;
let name = ProgramInfo(info).name_as_str().map(|s| s.to_string());
ProgramData::from_bpf_prog_info(name, fd, path.as_ref(), info, verifier_log_level)
let info = ProgramInfo::new_from_fd(fd.as_fd())?;
let name = info.name_as_str().map(|s| s.to_string());
ProgramData::from_bpf_prog_info(name, fd, path.as_ref(), info.0, verifier_log_level)
}
}
impl<T: Link> ProgramData<T> {
fn fd_or_err(&self) -> Result<RawFd, ProgramError> {
self.fd.ok_or(ProgramError::NotLoaded)
fn fd(&self) -> Result<&ProgramFd, ProgramError> {
self.fd.as_ref().ok_or(ProgramError::NotLoaded)
}
pub(crate) fn take_link(&mut self, link_id: T::Id) -> Result<T, ProgramError> {
@ -506,15 +513,14 @@ impl<T: Link> ProgramData<T> {
fn unload_program<T: Link>(data: &mut ProgramData<T>) -> Result<(), ProgramError> {
data.links.remove_all()?;
let fd = data.fd.take().ok_or(ProgramError::NotLoaded)?;
unsafe {
libc::close(fd);
}
Ok(())
data.fd
.take()
.ok_or(ProgramError::NotLoaded)
.map(|ProgramFd { .. }| ())
}
fn pin_program<T: Link, P: AsRef<Path>>(data: &ProgramData<T>, path: P) -> Result<(), PinError> {
let fd = data.fd.ok_or(PinError::NoFd {
let fd = data.fd.as_ref().ok_or(PinError::NoFd {
name: data
.name
.as_deref()
@ -526,7 +532,7 @@ fn pin_program<T: Link, P: AsRef<Path>>(data: &ProgramData<T>, path: P) -> Resul
error: e.to_string(),
}
})?;
bpf_pin_object(fd, &path_string).map_err(|(_, io_error)| SyscallError {
bpf_pin_object(fd.as_fd().as_raw_fd(), &path_string).map_err(|(_, io_error)| SyscallError {
call: "BPF_OBJ_PIN",
io_error,
})?;
@ -604,9 +610,9 @@ fn load_program<T: Link>(
kernel_version: target_kernel_version,
expected_attach_type: *expected_attach_type,
prog_btf_fd: btf_fd.as_ref().map(|f| f.as_fd()),
attach_btf_obj_fd: *attach_btf_obj_fd,
attach_btf_obj_fd: attach_btf_obj_fd.as_ref().map(|fd| fd.as_fd()),
attach_btf_id: *attach_btf_id,
attach_prog_fd: *attach_prog_fd,
attach_prog_fd: attach_prog_fd.as_ref().map(|fd| fd.as_fd()),
func_info_rec_size: *func_info_rec_size,
func_info: func_info.clone(),
line_info_rec_size: *line_info_rec_size,
@ -620,7 +626,7 @@ fn load_program<T: Link>(
match ret {
Ok(prog_fd) => {
*fd = Some(prog_fd as RawFd);
*fd = Some(ProgramFd(prog_fd));
Ok(())
}
Err((_, io_error)) => Err(ProgramError::LoadError {
@ -725,8 +731,8 @@ macro_rules! impl_fd {
$(
impl $struct_name {
/// Returns the file descriptor of this Program.
pub fn fd(&self) -> Option<ProgramFd> {
self.data.fd.map(|fd| ProgramFd(fd))
pub fn fd(&self) -> Result<&ProgramFd, ProgramError> {
self.data.fd()
}
}
)+
@ -908,11 +914,63 @@ impl_try_from_program!(
CgroupDevice,
);
/// Returns information about a loaded program with the [`ProgramInfo`] structure.
///
/// This information is populated at load time by the kernel and can be used
/// to correlate a given [`Program`] to it's corresponding [`ProgramInfo`]
/// metadata.
macro_rules! impl_program_info {
($($struct_name:ident),+ $(,)?) => {
$(
impl $struct_name {
/// Returns the file descriptor of this Program.
pub fn program_info(&self) -> Result<ProgramInfo, ProgramError> {
let ProgramFd(fd) = self.fd()?;
ProgramInfo::new_from_fd(fd.as_fd())
}
}
)+
}
}
impl_program_info!(
KProbe,
UProbe,
TracePoint,
SocketFilter,
Xdp,
SkMsg,
SkSkb,
SchedClassifier,
CgroupSkb,
CgroupSysctl,
CgroupSockopt,
LircMode2,
PerfEvent,
Lsm,
RawTracePoint,
BtfTracePoint,
FEntry,
FExit,
Extension,
CgroupSockAddr,
SkLookup,
SockOps,
CgroupSock,
CgroupDevice,
);
/// Provides information about a loaded program, like name, id and statistics
#[derive(Debug)]
pub struct ProgramInfo(bpf_prog_info);
impl ProgramInfo {
fn new_from_fd(fd: BorrowedFd<'_>) -> Result<Self, ProgramError> {
let info = bpf_prog_get_info_by_fd(fd, &mut [])?;
Ok(Self(info))
}
/// The name of the program as was provided when it was load. This is limited to 16 bytes
pub fn name(&self) -> &[u8] {
let length = self
@ -928,23 +986,89 @@ impl ProgramInfo {
unsafe { std::slice::from_raw_parts(self.0.name.as_ptr() as *const _, length) }
}
/// The name of the program as a &str. If the name was not valid unicode, None is returned
/// The name of the program as a &str. If the name was not valid unicode, None is returned.
pub fn name_as_str(&self) -> Option<&str> {
std::str::from_utf8(self.name()).ok()
}
/// The program id for this program. Each program has a unique id.
/// The id for this program. Each program has a unique id.
pub fn id(&self) -> u32 {
self.0.id
}
/// Returns the fd associated with the program.
/// The program tag.
///
/// The program tag is a SHA sum of the program's instructions which be used as an alternative to
/// [`Self::id()`]". A program's id can vary every time it's loaded or unloaded, but the tag
/// will remain the same.
pub fn tag(&self) -> u64 {
u64::from_be_bytes(self.0.tag)
}
/// The program type as defined by the linux kernel enum
/// [`bpf_prog_type`](https://elixir.bootlin.com/linux/v6.4.4/source/include/uapi/linux/bpf.h#L948).
pub fn program_type(&self) -> u32 {
self.0.type_
}
/// Returns true if the program is defined with a GPL-compatible license.
pub fn gpl_compatible(&self) -> bool {
self.0.gpl_compatible() != 0
}
/// The ids of the maps used by the program.
pub fn map_ids(&self) -> Result<Vec<u32>, ProgramError> {
let ProgramFd(fd) = self.fd()?;
let mut map_ids = vec![0u32; self.0.nr_map_ids as usize];
bpf_prog_get_info_by_fd(fd.as_fd(), &mut map_ids)?;
Ok(map_ids)
}
/// The btf id for the program.
pub fn btf_id(&self) -> Option<NonZeroU32> {
NonZeroU32::new(self.0.btf_id)
}
/// The size in bytes of the program's translated eBPF bytecode, which is
/// the bytecode after it has been passed though the verifier where it was
/// possibly modified by the kernel.
pub fn size_translated(&self) -> u32 {
self.0.xlated_prog_len
}
/// The size in bytes of the program's JIT-compiled machine code.
pub fn size_jitted(&self) -> u32 {
self.0.jited_prog_len
}
/// How much memory in bytes has been allocated and locked for the program.
pub fn memory_locked(&self) -> Result<u32, ProgramError> {
get_fdinfo(self.fd()?.as_fd(), "memlock")
}
/// The number of verified instructions in the program.
///
/// This may be less than the total number of instructions in the compiled
/// program due to dead code elimination in the verifier.
pub fn verified_instruction_count(&self) -> u32 {
self.0.verified_insns
}
/// The time the program was loaded.
pub fn loaded_at(&self) -> SystemTime {
boot_time() + Duration::from_nanos(self.0.load_time)
}
/// Returns a file descriptor referencing the program.
///
/// The returned fd must be closed when no longer needed.
pub fn fd(&self) -> Result<RawFd, ProgramError> {
/// The returned file descriptor can be closed at any time and doing so does
/// not influence the life cycle of the program.
pub fn fd(&self) -> Result<ProgramFd, ProgramError> {
let Self(info) = self;
let fd = bpf_prog_get_fd_by_id(info.id)?;
Ok(fd.into_raw_fd())
Ok(ProgramFd(fd))
}
/// Loads a program from a pinned path in bpffs.
@ -955,7 +1079,7 @@ impl ProgramInfo {
io_error,
})?;
let info = bpf_prog_get_info_by_fd(fd.as_raw_fd())?;
let info = bpf_prog_get_info_by_fd(fd.as_fd(), &mut [])?;
Ok(ProgramInfo(info))
}
}
@ -991,7 +1115,7 @@ pub fn loaded_programs() -> impl Iterator<Item = Result<ProgramInfo, ProgramErro
})
.map(|fd| {
let fd = fd?;
bpf_prog_get_info_by_fd(fd.as_raw_fd())
bpf_prog_get_info_by_fd(fd.as_fd(), &mut [])
})
.map(|result| result.map(ProgramInfo).map_err(Into::into))
}

@ -1,6 +1,6 @@
//! Perf event programs.
use std::os::fd::AsFd as _;
use std::os::fd::{AsFd as _, AsRawFd as _};
pub use crate::generated::{
perf_hw_cache_id, perf_hw_cache_op_id, perf_hw_cache_op_result_id, perf_hw_id, perf_sw_ids,
@ -146,6 +146,9 @@ impl PerfEvent {
scope: PerfEventScope,
sample_policy: SamplePolicy,
) -> Result<PerfEventLinkId, ProgramError> {
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let prog_fd = prog_fd.as_raw_fd();
let (sample_period, sample_frequency) = match sample_policy {
SamplePolicy::Period(period) => (period, None),
SamplePolicy::Frequency(frequency) => (0, Some(frequency)),
@ -172,7 +175,7 @@ impl PerfEvent {
io_error,
})?;
let link = perf_attach(self.data.fd_or_err()?, fd)?;
let link = perf_attach(prog_fd, fd)?;
self.data.links.insert(PerfEventLink::new(link))
}

@ -3,7 +3,7 @@ use libc::pid_t;
use std::{
fs::{self, OpenOptions},
io::{self, Write},
os::fd::OwnedFd,
os::fd::{AsFd as _, AsRawFd as _, OwnedFd},
path::Path,
process,
sync::atomic::{AtomicUsize, Ordering},
@ -57,19 +57,17 @@ pub(crate) fn attach<T: Link + From<PerfLinkInner>>(
) -> Result<T::Id, ProgramError> {
// https://github.com/torvalds/linux/commit/e12f03d7031a977356e3d7b75a68c2185ff8d155
// Use debugfs to create probe
if KernelVersion::current().unwrap() < KernelVersion::new(4, 17, 0) {
let prog_fd = program_data.fd()?;
let prog_fd = prog_fd.as_fd();
let prog_fd = prog_fd.as_raw_fd();
let link = if KernelVersion::current().unwrap() < KernelVersion::new(4, 17, 0) {
let (fd, event_alias) = create_as_trace_point(kind, fn_name, offset, pid)?;
let link = T::from(perf_attach_debugfs(
program_data.fd_or_err()?,
fd,
ProbeEvent { kind, event_alias },
)?);
return program_data.links.insert(link);
};
let fd = create_as_probe(kind, fn_name, offset, pid)?;
let link = T::from(perf_attach(program_data.fd_or_err()?, fd)?);
program_data.links.insert(link)
perf_attach_debugfs(prog_fd, fd, ProbeEvent { kind, event_alias })
} else {
let fd = create_as_probe(kind, fn_name, offset, pid)?;
perf_attach(prog_fd, fd)
}?;
program_data.links.insert(T::from(link))
}
pub(crate) fn detach_debug_fs(event: ProbeEvent) -> Result<(), ProgramError> {

@ -1,4 +1,4 @@
use std::os::fd::AsRawFd;
use std::os::fd::{AsFd as _, AsRawFd};
use crate::{
generated::{bpf_attach_type::BPF_SK_LOOKUP, bpf_prog_type::BPF_PROG_TYPE_SK_LOOKUP},
@ -61,7 +61,9 @@ impl SkLookup {
///
/// The returned value can be used to detach, see [SkLookup::detach].
pub fn attach<T: AsRawFd>(&mut self, netns: T) -> Result<SkLookupLinkId, ProgramError> {
let prog_fd = self.data.fd_or_err()?;
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let prog_fd = prog_fd.as_raw_fd();
let netns_fd = netns.as_raw_fd();
let link_fd = bpf_link_create(prog_fd, netns_fd, BPF_SK_LOOKUP, None, 0).map_err(

@ -1,6 +1,6 @@
//! Skmsg programs.
use std::os::fd::AsRawFd;
use std::os::fd::{AsFd as _, AsRawFd as _};
use crate::{
generated::{bpf_attach_type::BPF_SK_MSG_VERDICT, bpf_prog_type::BPF_PROG_TYPE_SK_MSG},
@ -79,7 +79,9 @@ impl SkMsg {
///
/// The returned value can be used to detach, see [SkMsg::detach].
pub fn attach(&mut self, map: SockMapFd) -> Result<SkMsgLinkId, ProgramError> {
let prog_fd = self.data.fd_or_err()?;
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let prog_fd = prog_fd.as_raw_fd();
let map_fd = map.as_raw_fd();
bpf_prog_attach(prog_fd, map_fd, BPF_SK_MSG_VERDICT).map_err(|(_, io_error)| {

@ -1,6 +1,9 @@
//! Skskb programs.
use std::{os::fd::AsRawFd, path::Path};
use std::{
os::fd::{AsFd as _, AsRawFd as _},
path::Path,
};
use crate::{
generated::{
@ -72,7 +75,9 @@ impl SkSkb {
///
/// The returned value can be used to detach, see [SkSkb::detach].
pub fn attach(&mut self, map: SockMapFd) -> Result<SkSkbLinkId, ProgramError> {
let prog_fd = self.data.fd_or_err()?;
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let prog_fd = prog_fd.as_raw_fd();
let map_fd = map.as_raw_fd();
let attach_type = match self.kind {

@ -1,5 +1,5 @@
//! Socket option programs.
use std::os::fd::AsRawFd;
use std::os::fd::{AsFd as _, AsRawFd};
use crate::{
generated::{bpf_attach_type::BPF_CGROUP_SOCK_OPS, bpf_prog_type::BPF_PROG_TYPE_SOCK_OPS},
@ -59,7 +59,9 @@ impl SockOps {
///
/// The returned value can be used to detach, see [SockOps::detach].
pub fn attach<T: AsRawFd>(&mut self, cgroup: T) -> Result<SockOpsLinkId, ProgramError> {
let prog_fd = self.data.fd_or_err()?;
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let prog_fd = prog_fd.as_raw_fd();
let cgroup_fd = cgroup.as_raw_fd();
bpf_prog_attach(prog_fd, cgroup_fd, BPF_CGROUP_SOCK_OPS).map_err(|(_, io_error)| {

@ -2,7 +2,7 @@
use libc::{setsockopt, SOL_SOCKET};
use std::{
io, mem,
os::fd::{AsRawFd, RawFd},
os::fd::{AsFd as _, AsRawFd, RawFd},
};
use thiserror::Error;
@ -73,7 +73,9 @@ impl SocketFilter {
///
/// The returned value can be used to detach from the socket, see [SocketFilter::detach].
pub fn attach<T: AsRawFd>(&mut self, socket: T) -> Result<SocketFilterLinkId, ProgramError> {
let prog_fd = self.data.fd_or_err()?;
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let prog_fd = prog_fd.as_raw_fd();
let socket = socket.as_raw_fd();
let ret = unsafe {

@ -4,6 +4,7 @@ use thiserror::Error;
use std::{
ffi::{CStr, CString},
io,
os::fd::{AsFd as _, AsRawFd as _},
path::Path,
};
@ -152,7 +153,9 @@ impl SchedClassifier {
attach_type: TcAttachType,
options: TcOptions,
) -> Result<SchedClassifierLinkId, ProgramError> {
let prog_fd = self.data.fd_or_err()?;
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let prog_fd = prog_fd.as_raw_fd();
let if_index = ifindex_from_ifname(interface)
.map_err(|io_error| TcError::NetlinkError { io_error })?;
let (priority, handle) = unsafe {

@ -1,5 +1,9 @@
//! Tracepoint programs.
use std::{fs, io, os::fd::AsFd as _, path::Path};
use std::{
fs, io,
os::fd::{AsFd as _, AsRawFd as _},
path::Path,
};
use thiserror::Error;
use crate::{
@ -78,6 +82,9 @@ impl TracePoint {
///
/// The returned value can be used to detach, see [TracePoint::detach].
pub fn attach(&mut self, category: &str, name: &str) -> Result<TracePointLinkId, ProgramError> {
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let prog_fd = prog_fd.as_raw_fd();
let tracefs = find_tracefs_path()?;
let id = read_sys_fs_trace_point_id(tracefs, category, name)?;
let fd =
@ -86,7 +93,7 @@ impl TracePoint {
io_error,
})?;
let link = perf_attach(self.data.fd_or_err()?, fd)?;
let link = perf_attach(prog_fd, fd)?;
self.data.links.insert(TracePointLink::new(link))
}

@ -1,5 +1,12 @@
//! Common functions shared between multiple eBPF program types.
use std::{ffi::CStr, io, path::Path};
use std::{
ffi::CStr,
fs::File,
io::{self, BufRead, BufReader},
os::fd::{AsFd as _, AsRawFd as _, BorrowedFd},
path::Path,
time::{Duration, SystemTime, UNIX_EPOCH},
};
use crate::{
programs::{FdLink, Link, ProgramData, ProgramError},
@ -11,8 +18,9 @@ pub(crate) fn attach_raw_tracepoint<T: Link + From<FdLink>>(
program_data: &mut ProgramData<T>,
tp_name: Option<&CStr>,
) -> Result<T::Id, ProgramError> {
let prog_fd = program_data.fd_or_err()?;
let prog_fd = program_data.fd()?;
let prog_fd = prog_fd.as_fd();
let prog_fd = prog_fd.as_raw_fd();
let pfd =
bpf_raw_tracepoint_open(tp_name, prog_fd).map_err(|(_code, io_error)| SyscallError {
call: "bpf_raw_tracepoint_open",
@ -22,7 +30,7 @@ pub(crate) fn attach_raw_tracepoint<T: Link + From<FdLink>>(
program_data.links.insert(FdLink::new(pfd).into())
}
/// Find tracefs filesystem path
/// Find tracefs filesystem path.
pub(crate) fn find_tracefs_path() -> Result<&'static Path, ProgramError> {
lazy_static::lazy_static! {
static ref TRACE_FS: Option<&'static Path> = {
@ -50,3 +58,40 @@ pub(crate) fn find_tracefs_path() -> Result<&'static Path, ProgramError> {
.as_deref()
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "tracefs not found").into())
}
/// The time at which the system is booted.
pub(crate) fn boot_time() -> SystemTime {
let get_time = |clock_id| {
let mut time = unsafe { std::mem::zeroed::<libc::timespec>() };
assert_eq!(
unsafe { libc::clock_gettime(clock_id, &mut time) },
0,
"clock_gettime({}, _)",
clock_id
);
let libc::timespec { tv_sec, tv_nsec } = time;
Duration::new(tv_sec as u64, tv_nsec as u32)
};
let since_boot = get_time(libc::CLOCK_BOOTTIME);
let since_epoch = get_time(libc::CLOCK_REALTIME);
UNIX_EPOCH + since_boot - since_epoch
}
/// Get the specified information from a file descriptor's fdinfo.
pub(crate) fn get_fdinfo(fd: BorrowedFd, key: &str) -> Result<u32, ProgramError> {
let info = File::open(format!("/proc/self/fdinfo/{}", fd.as_raw_fd()))?;
let reader = BufReader::new(info);
for line in reader.lines() {
let line = line.map_err(ProgramError::IOError)?;
if !line.contains(key) {
continue;
}
let (_key, val) = line.rsplit_once('\t').unwrap();
return Ok(val.parse().unwrap());
}
Ok(0)
}

@ -8,7 +8,7 @@ use std::{
ffi::CString,
hash::Hash,
io,
os::fd::{AsFd as _, RawFd},
os::fd::{AsFd as _, AsRawFd as _, RawFd},
};
use thiserror::Error;
@ -128,7 +128,9 @@ impl Xdp {
if_index: u32,
flags: XdpFlags,
) -> Result<XdpLinkId, ProgramError> {
let prog_fd = self.data.fd_or_err()?;
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let prog_fd = prog_fd.as_raw_fd();
let if_index = if_index as RawFd;
if KernelVersion::current().unwrap() >= KernelVersion::new(5, 9, 0) {
@ -174,7 +176,9 @@ impl Xdp {
///
/// Ownership of the link will transfer to this program.
pub fn attach_to_link(&mut self, link: XdpLink) -> Result<XdpLinkId, ProgramError> {
let prog_fd = self.data.fd_or_err()?;
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let prog_fd = prog_fd.as_raw_fd();
match link.into_inner() {
XdpLinkInner::FdLink(fd_link) => {
let link_fd = fd_link.fd;

@ -3,13 +3,14 @@ use std::{
ffi::{CStr, CString},
io, iter,
mem::{self, MaybeUninit},
os::fd::{AsRawFd, BorrowedFd, FromRawFd as _, OwnedFd, RawFd},
os::fd::{AsFd as _, AsRawFd as _, BorrowedFd, FromRawFd as _, OwnedFd, RawFd},
slice,
};
use crate::util::KernelVersion;
use libc::{c_char, c_long, close, ENOENT, ENOSPC};
use libc::{c_char, c_long, ENOENT, ENOSPC};
use obj::{
btf::{BtfEnum64, Enum64},
maps::{bpf_map_def, LegacyMap},
BpfSectionKind, VerifierLog,
};
@ -117,9 +118,9 @@ pub(crate) struct BpfLoadProgramAttrs<'a> {
pub(crate) kernel_version: u32,
pub(crate) expected_attach_type: Option<bpf_attach_type>,
pub(crate) prog_btf_fd: Option<BorrowedFd<'a>>,
pub(crate) attach_btf_obj_fd: Option<u32>,
pub(crate) attach_btf_obj_fd: Option<BorrowedFd<'a>>,
pub(crate) attach_btf_id: Option<u32>,
pub(crate) attach_prog_fd: Option<RawFd>,
pub(crate) attach_prog_fd: Option<BorrowedFd<'a>>,
pub(crate) func_info_rec_size: usize,
pub(crate) func_info: FuncSecInfo,
pub(crate) line_info_rec_size: usize,
@ -131,7 +132,7 @@ pub(crate) fn bpf_load_program(
aya_attr: &BpfLoadProgramAttrs,
log_buf: &mut [u8],
verifier_log_level: VerifierLogLevel,
) -> SysResult<c_long> {
) -> SysResult<OwnedFd> {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let u = unsafe { &mut attr.__bindgen_anon_3 };
@ -180,16 +181,16 @@ pub(crate) fn bpf_load_program(
u.log_size = log_buf.len() as u32;
}
if let Some(v) = aya_attr.attach_btf_obj_fd {
u.__bindgen_anon_1.attach_btf_obj_fd = v;
u.__bindgen_anon_1.attach_btf_obj_fd = v.as_raw_fd() as _;
}
if let Some(v) = aya_attr.attach_prog_fd {
u.__bindgen_anon_1.attach_prog_fd = v as u32;
u.__bindgen_anon_1.attach_prog_fd = v.as_raw_fd() as u32;
}
if let Some(v) = aya_attr.attach_btf_id {
u.attach_btf_id = v;
}
sys_bpf(bpf_cmd::BPF_PROG_LOAD, &mut attr)
bpf_prog_load(&mut attr)
}
fn lookup<K: Pod, V: Pod>(
@ -476,10 +477,14 @@ pub(crate) fn bpf_prog_get_fd_by_id(prog_id: u32) -> Result<OwnedFd, SyscallErro
})
}
fn bpf_obj_get_info_by_fd<T>(fd: BorrowedFd<'_>) -> Result<T, SyscallError> {
fn bpf_obj_get_info_by_fd<T, F: FnOnce(&mut T)>(
fd: BorrowedFd<'_>,
init: F,
) -> Result<T, SyscallError> {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
// info gets entirely populated by the kernel
let info = MaybeUninit::zeroed();
let mut info = unsafe { mem::zeroed() };
init(&mut info);
attr.info.bpf_fd = fd.as_raw_fd() as u32;
attr.info.info = &info as *const _ as u64;
@ -488,7 +493,7 @@ fn bpf_obj_get_info_by_fd<T>(fd: BorrowedFd<'_>) -> Result<T, SyscallError> {
match sys_bpf(bpf_cmd::BPF_OBJ_GET_INFO_BY_FD, &mut attr) {
Ok(code) => {
assert_eq!(code, 0);
Ok(unsafe { info.assume_init() })
Ok(info)
}
Err((code, io_error)) => {
assert_eq!(code, -1);
@ -500,13 +505,18 @@ fn bpf_obj_get_info_by_fd<T>(fd: BorrowedFd<'_>) -> Result<T, SyscallError> {
}
}
pub(crate) fn bpf_prog_get_info_by_fd(fd: RawFd) -> Result<bpf_prog_info, SyscallError> {
let fd = unsafe { BorrowedFd::borrow_raw(fd) };
bpf_obj_get_info_by_fd::<bpf_prog_info>(fd)
pub(crate) fn bpf_prog_get_info_by_fd(
fd: BorrowedFd<'_>,
map_ids: &mut [u32],
) -> Result<bpf_prog_info, SyscallError> {
bpf_obj_get_info_by_fd(fd, |info: &mut bpf_prog_info| {
info.nr_map_ids = map_ids.len() as _;
info.map_ids = map_ids.as_mut_ptr() as _;
})
}
pub(crate) fn bpf_map_get_info_by_fd(fd: BorrowedFd<'_>) -> Result<bpf_map_info, SyscallError> {
bpf_obj_get_info_by_fd::<bpf_map_info>(fd)
bpf_obj_get_info_by_fd(fd, |_| {})
}
pub(crate) fn bpf_link_get_fd_by_id(link_id: u32) -> Result<OwnedFd, SyscallError> {
@ -524,26 +534,17 @@ pub(crate) fn bpf_link_get_fd_by_id(link_id: u32) -> Result<OwnedFd, SyscallErro
}
pub(crate) fn bpf_link_get_info_by_fd(fd: BorrowedFd<'_>) -> Result<bpf_link_info, SyscallError> {
bpf_obj_get_info_by_fd::<bpf_link_info>(fd)
bpf_obj_get_info_by_fd(fd, |_| {})
}
pub(crate) fn btf_obj_get_info_by_fd(
prog_fd: RawFd,
buf: &[u8],
) -> Result<bpf_btf_info, io::Error> {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let mut info = unsafe { mem::zeroed::<bpf_btf_info>() };
let buf_size = buf.len() as u32;
info.btf = buf.as_ptr() as u64;
info.btf_size = buf_size;
attr.info.bpf_fd = prog_fd as u32;
attr.info.info = &info as *const bpf_btf_info as u64;
attr.info.info_len = mem::size_of::<bpf_btf_info>() as u32;
match sys_bpf(bpf_cmd::BPF_OBJ_GET_INFO_BY_FD, &mut attr) {
Ok(_) => Ok(info),
Err((_, err)) => Err(err),
}
fd: BorrowedFd<'_>,
buf: &mut [u8],
) -> Result<bpf_btf_info, SyscallError> {
bpf_obj_get_info_by_fd(fd, |info: &mut bpf_btf_info| {
info.btf = buf.as_mut_ptr() as _;
info.btf_size = buf.len() as _;
})
}
pub(crate) fn bpf_raw_tracepoint_open(name: Option<&CStr>, prog_fd: RawFd) -> SysResult<OwnedFd> {
@ -592,14 +593,18 @@ unsafe fn fd_sys_bpf(cmd: bpf_cmd, attr: &mut bpf_attr) -> SysResult<OwnedFd> {
Ok(OwnedFd::from_raw_fd(fd))
}
pub(crate) fn bpf_btf_get_fd_by_id(id: u32) -> Result<RawFd, io::Error> {
pub(crate) fn bpf_btf_get_fd_by_id(id: u32) -> Result<OwnedFd, SyscallError> {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
attr.__bindgen_anon_6.__bindgen_anon_1.btf_id = id;
match sys_bpf(bpf_cmd::BPF_BTF_GET_FD_BY_ID, &mut attr) {
Ok(v) => Ok(v as RawFd),
Err((_, err)) => Err(err),
}
// SAFETY: BPF_BTF_GET_FD_BY_ID returns a new file descriptor.
unsafe { fd_sys_bpf(bpf_cmd::BPF_BTF_GET_FD_BY_ID, &mut attr) }.map_err(|(code, io_error)| {
assert_eq!(code, -1);
SyscallError {
call: "bpf_btf_get_fd_by_id",
io_error,
}
})
}
pub(crate) fn is_prog_name_supported() -> bool {
@ -627,14 +632,7 @@ pub(crate) fn is_prog_name_supported() -> bool {
u.insns = insns.as_ptr() as u64;
u.prog_type = bpf_prog_type::BPF_PROG_TYPE_SOCKET_FILTER as u32;
match sys_bpf(bpf_cmd::BPF_PROG_LOAD, &mut attr) {
Ok(v) => {
let fd = v as RawFd;
unsafe { close(fd) };
true
}
Err(_) => false,
}
bpf_prog_load(&mut attr).is_ok()
}
pub(crate) fn is_probe_read_kernel_supported() -> bool {
@ -658,14 +656,7 @@ pub(crate) fn is_probe_read_kernel_supported() -> bool {
u.insns = insns.as_ptr() as u64;
u.prog_type = bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT as u32;
match sys_bpf(bpf_cmd::BPF_PROG_LOAD, &mut attr) {
Ok(v) => {
let fd = v as RawFd;
unsafe { close(fd) };
true
}
Err(_) => false,
}
bpf_prog_load(&mut attr).is_ok()
}
pub(crate) fn is_perf_link_supported() -> bool {
@ -685,18 +676,18 @@ pub(crate) fn is_perf_link_supported() -> bool {
u.insns = insns.as_ptr() as u64;
u.prog_type = bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT as u32;
if let Ok(fd) = sys_bpf(bpf_cmd::BPF_PROG_LOAD, &mut attr) {
if let Err((_, e)) =
if let Ok(fd) = bpf_prog_load(&mut attr) {
let fd = fd.as_fd();
let fd = fd.as_raw_fd();
matches!(
// Uses an invalid target FD so we get EBADF if supported.
bpf_link_create(fd as i32, -1, bpf_attach_type::BPF_PERF_EVENT, None, 0)
{
bpf_link_create(fd, -1, bpf_attach_type::BPF_PERF_EVENT, None, 0),
// Returns EINVAL if unsupported. EBADF if supported.
let res = e.raw_os_error() == Some(libc::EBADF);
unsafe { libc::close(fd as i32) };
return res;
}
Err((_, e)) if e.raw_os_error() == Some(libc::EBADF),
)
} else {
false
}
false
}
pub(crate) fn is_bpf_global_data_supported() -> bool {
@ -713,8 +704,8 @@ pub(crate) fn is_bpf_global_data_supported() -> bool {
let mut insns = copy_instructions(prog).unwrap();
let mut map_data = MapData {
obj: obj::Map::Legacy(LegacyMap {
let map = MapData::create(
obj::Map::Legacy(LegacyMap {
def: bpf_map_def {
map_type: bpf_map_type::BPF_MAP_TYPE_ARRAY as u32,
key_size: 4,
@ -727,13 +718,12 @@ pub(crate) fn is_bpf_global_data_supported() -> bool {
symbol_index: None,
data: Vec::new(),
}),
fd: None,
pinned: false,
btf_fd: None,
};
"aya_global",
None,
);
if let Ok(map_fd) = map_data.create("aya_global") {
insns[0].imm = map_fd;
if let Ok(map) = map {
insns[0].imm = map.fd;
let gpl = b"GPL\0";
u.license = gpl.as_ptr() as u64;
@ -741,16 +731,10 @@ pub(crate) fn is_bpf_global_data_supported() -> bool {
u.insns = insns.as_ptr() as u64;
u.prog_type = bpf_prog_type::BPF_PROG_TYPE_SOCKET_FILTER as u32;
if let Ok(v) = sys_bpf(bpf_cmd::BPF_PROG_LOAD, &mut attr) {
let fd = v as RawFd;
unsafe { close(fd) };
return true;
}
bpf_prog_load(&mut attr).is_ok()
} else {
false
}
false
}
pub(crate) fn is_bpf_cookie_supported() -> bool {
@ -770,14 +754,7 @@ pub(crate) fn is_bpf_cookie_supported() -> bool {
u.insns = insns.as_ptr() as u64;
u.prog_type = bpf_prog_type::BPF_PROG_TYPE_KPROBE as u32;
match sys_bpf(bpf_cmd::BPF_PROG_LOAD, &mut attr) {
Ok(v) => {
let fd = v as RawFd;
unsafe { close(fd) };
true
}
Err(_) => false,
}
bpf_prog_load(&mut attr).is_ok()
}
pub(crate) fn is_btf_supported() -> bool {
@ -873,6 +850,22 @@ pub(crate) fn is_btf_datasec_supported() -> bool {
bpf_load_btf(btf_bytes.as_slice(), &mut [], Default::default()).is_ok()
}
pub(crate) fn is_btf_enum64_supported() -> bool {
let mut btf = Btf::new();
let name_offset = btf.add_string("enum64");
let enum_64_type = BtfType::Enum64(Enum64::new(
name_offset,
true,
vec![BtfEnum64::new(btf.add_string("a"), 1)],
));
btf.add_type(enum_64_type);
let btf_bytes = btf.to_bytes();
bpf_load_btf(btf_bytes.as_slice(), &mut [], Default::default()).is_ok()
}
pub(crate) fn is_btf_float_supported() -> bool {
let mut btf = Btf::new();
let name_offset = btf.add_string("float");
@ -920,6 +913,11 @@ pub(crate) fn is_btf_type_tag_supported() -> bool {
bpf_load_btf(btf_bytes.as_slice(), &mut [], Default::default()).is_ok()
}
fn bpf_prog_load(attr: &mut bpf_attr) -> SysResult<OwnedFd> {
// SAFETY: BPF_PROG_LOAD returns a new file descriptor.
unsafe { fd_sys_bpf(bpf_cmd::BPF_PROG_LOAD, attr) }
}
fn sys_bpf(cmd: bpf_cmd, attr: &mut bpf_attr) -> SysResult<c_long> {
syscall(Syscall::Bpf { cmd, attr })
}

@ -1,18 +1,18 @@
//! Utility functions.
use std::{
borrow::Cow,
collections::BTreeMap,
error::Error,
ffi::{CStr, CString},
fs::{self, File},
io::{self, BufRead, BufReader},
mem, slice,
mem,
num::ParseIntError,
slice,
str::{FromStr, Utf8Error},
};
use crate::{
generated::{TC_H_MAJ_MASK, TC_H_MIN_MASK},
maps::stack_trace::SymbolResolver,
Pod,
};
@ -203,36 +203,33 @@ fn parse_cpu_ranges(data: &str) -> Result<Vec<u32>, ()> {
Ok(cpus)
}
/// The simplest resolver: a direct map from addresses to strings.
pub type SimpleSymbolResolver = BTreeMap<u64, String>;
impl SymbolResolver for SimpleSymbolResolver {
fn resolve_symbol(&self, addr: u64) -> Option<Cow<'_, str>> {
self.range(..=addr).next_back().map(|(_, s)| s.into())
}
}
/// Loads kernel symbols from `/proc/kallsyms`.
///
/// The symbols can be passed to [`StackTrace::resolve`](crate::maps::stack_trace::StackTrace::resolve).
pub fn kernel_symbols() -> Result<SimpleSymbolResolver, io::Error> {
/// See [`crate::maps::StackTraceMap`] for an example on how to use this to resolve kernel addresses to symbols.
pub fn kernel_symbols() -> Result<BTreeMap<u64, String>, io::Error> {
let mut reader = BufReader::new(File::open("/proc/kallsyms")?);
parse_kernel_symbols(&mut reader)
}
fn parse_kernel_symbols(reader: impl BufRead) -> Result<SimpleSymbolResolver, io::Error> {
let mut syms = SimpleSymbolResolver::new();
for line in reader.lines() {
let line = line?;
let parts = line.splitn(4, ' ').collect::<Vec<_>>();
let addr = u64::from_str_radix(parts[0], 16)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, line.clone()))?;
let name = parts[2].to_owned();
syms.insert(addr, name);
}
Ok(syms)
fn parse_kernel_symbols(reader: impl BufRead) -> Result<BTreeMap<u64, String>, io::Error> {
reader
.lines()
.map(|line| {
let line = line?;
(|| {
let mut parts = line.splitn(4, ' ');
let addr = parts.next()?;
let _kind = parts.next()?;
let name = parts.next()?;
let addr = match u64::from_str_radix(addr, 16) {
Ok(addr) => Some(addr),
Err(ParseIntError { .. }) => None,
}?;
Some((addr, name.to_owned()))
})()
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, line.clone()))
})
.collect()
}
/// Returns the prefix used by syscalls.

@ -3,9 +3,9 @@
// https://elixir.bootlin.com/linux/v5.13/source/include/uapi/linux/types.h
typedef __u32 __bitwise __wsum;
#include "bpf_helpers.h"
#include <linux/bpf.h>
#include <linux/ptrace.h>
#include <sys/socket.h>
// needed for TC_ACT_*
#include <linux/pkt_cls.h>
#include "bpf_helpers.h"
#include <linux/ptrace.h>
#include <sys/socket.h>

@ -1,10 +1,9 @@
#![no_std]
#![warn(clippy::cast_lossless, clippy::cast_sign_loss)]
use aya_bpf::{
macros::map,
maps::{PerCpuArray, PerfEventByteArray},
};
#[cfg(target_arch = "bpf")]
use aya_bpf::macros::map;
use aya_bpf::maps::{PerCpuArray, PerfEventByteArray};
pub use aya_log_common::{write_record_header, Level, WriteToBuf, LOG_BUF_CAPACITY};
pub use aya_log_ebpf_macros::{debug, error, info, log, trace, warn};
@ -15,11 +14,19 @@ pub struct LogBuf {
}
#[doc(hidden)]
#[map]
// This cfg_attr prevents compilation failures on macOS where the generated section name doesn't
// meet mach-o's requirements. We wouldn't ordinarily build this crate for macOS, but we do so
// because the integration-test crate depends on this crate transitively. See comment in
// test/integration-test/Cargo.toml.
#[cfg_attr(target_arch = "bpf", map)]
pub static mut AYA_LOG_BUF: PerCpuArray<LogBuf> = PerCpuArray::with_max_entries(1, 0);
#[doc(hidden)]
#[map]
// This cfg_attr prevents compilation failures on macOS where the generated section name doesn't
// meet mach-o's requirements. We wouldn't ordinarily build this crate for macOS, but we do so
// because the integration-test crate depends on this crate transitively. See comment in
// test/integration-test/Cargo.toml.
#[cfg_attr(target_arch = "bpf", map)]
pub static mut AYA_LOGS: PerfEventByteArray = PerfEventByteArray::new(0);
#[doc(hidden)]

@ -0,0 +1,10 @@
[package]
name = "init"
version = "0.1.0"
authors = ["Tamir Duberstein <tamird@gmail.com>"]
edition = "2021"
publish = false
[dependencies]
anyhow = { workspace = true, features = ["std"] }
nix = { workspace = true, features = ["fs", "mount", "reboot"] }

@ -0,0 +1,166 @@
//! init is the first process started by the kernel.
//!
//! This implementation creates the minimal mounts required to run BPF programs, runs all binaries
//! in /bin, prints a final message ("init: success|failure"), and powers off the machine.
use anyhow::Context as _;
#[derive(Debug)]
struct Errors(Vec<anyhow::Error>);
impl std::fmt::Display for Errors {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self(errors) = self;
for (i, error) in errors.iter().enumerate() {
if i != 0 {
writeln!(f)?;
}
write!(f, "{:?}", error)?;
}
Ok(())
}
}
impl std::error::Error for Errors {}
fn run() -> anyhow::Result<()> {
const RXRXRX: nix::sys::stat::Mode = nix::sys::stat::Mode::empty()
.union(nix::sys::stat::Mode::S_IRUSR)
.union(nix::sys::stat::Mode::S_IXUSR)
.union(nix::sys::stat::Mode::S_IRGRP)
.union(nix::sys::stat::Mode::S_IXGRP)
.union(nix::sys::stat::Mode::S_IROTH)
.union(nix::sys::stat::Mode::S_IXOTH);
struct Mount {
source: &'static str,
target: &'static str,
fstype: &'static str,
flags: nix::mount::MsFlags,
data: Option<&'static str>,
target_mode: Option<nix::sys::stat::Mode>,
}
for Mount {
source,
target,
fstype,
flags,
data,
target_mode,
} in [
Mount {
source: "proc",
target: "/proc",
fstype: "proc",
flags: nix::mount::MsFlags::empty(),
data: None,
target_mode: Some(RXRXRX),
},
Mount {
source: "sysfs",
target: "/sys",
fstype: "sysfs",
flags: nix::mount::MsFlags::empty(),
data: None,
target_mode: Some(RXRXRX),
},
Mount {
source: "debugfs",
target: "/sys/kernel/debug",
fstype: "debugfs",
flags: nix::mount::MsFlags::empty(),
data: None,
target_mode: None,
},
Mount {
source: "bpffs",
target: "/sys/fs/bpf",
fstype: "bpf",
flags: nix::mount::MsFlags::empty(),
data: None,
target_mode: None,
},
] {
match target_mode {
None => {
// Must exist.
let nix::sys::stat::FileStat { st_mode, .. } = nix::sys::stat::stat(target)
.with_context(|| format!("stat({target}) failed"))?;
let s_flag = nix::sys::stat::SFlag::from_bits_truncate(st_mode);
if !s_flag.contains(nix::sys::stat::SFlag::S_IFDIR) {
anyhow::bail!("{target} is not a directory");
}
}
Some(target_mode) => {
// Must not exist.
nix::unistd::mkdir(target, target_mode)
.with_context(|| format!("mkdir({target}) failed"))?;
}
}
nix::mount::mount(Some(source), target, Some(fstype), flags, data).with_context(|| {
format!("mount({source}, {target}, {fstype}, {flags:?}, {data:?}) failed")
})?;
}
// By contract we run everything in /bin and assume they're rust test binaries.
//
// If the user requested command line arguments, they're named init.arg={}.
// Read kernel parameters from /proc/cmdline. They're space separated on a single line.
let cmdline = std::fs::read_to_string("/proc/cmdline")
.with_context(|| "read_to_string(/proc/cmdline) failed")?;
let args = cmdline
.split_whitespace()
.filter_map(|parameter| {
parameter
.strip_prefix("init.arg=")
.map(std::ffi::OsString::from)
})
.collect::<Vec<_>>();
// Iterate files in /bin.
let read_dir = std::fs::read_dir("/bin").context("read_dir(/bin) failed")?;
let errors = read_dir
.filter_map(|entry| {
match (|| {
let entry = entry.context("read_dir(/bin) failed")?;
let path = entry.path();
let status = std::process::Command::new(&path)
.args(&args)
.status()
.with_context(|| format!("failed to execute {}", path.display()))?;
if status.code() == Some(0) {
Ok(())
} else {
Err(anyhow::anyhow!("{} failed: {status:?}", path.display()))
}
})() {
Ok(()) => None,
Err(err) => Some(err),
}
})
.collect::<Vec<_>>();
if errors.is_empty() {
Ok(())
} else {
Err(Errors(errors).into())
}
}
fn main() {
match run() {
Ok(()) => {
println!("init: success");
}
Err(err) => {
println!("{err:?}");
println!("init: failure");
}
}
let how = nix::sys::reboot::RebootMode::RB_POWER_OFF;
let _: std::convert::Infallible = nix::sys::reboot::reboot(how)
.unwrap_or_else(|err| panic!("reboot({how:?}) failed: {err:?}"));
}

@ -3,21 +3,15 @@ Aya Integration Tests
The aya integration test suite is a set of tests to ensure that
common usage behaviours work on real Linux distros
## Prerequisites
### Linux
To run locally all you need is:
1. Rust nightly
1. `cargo install bpf-linker`
## Prerequisites
### Other OSs
You'll need:
1. A POSIX shell
1. `rustup target add x86_64-unknown-linux-musl`
1. `rustup toolchain install nightly`
1. `rustup target add {aarch64,x86_64}-unknown-linux-musl`
1. `cargo install bpf-linker`
1. Install `qemu` and `cloud-init-utils` package - or any package that provides `cloud-localds`
1. (virtualized only) `qemu`
## Usage
@ -26,15 +20,13 @@ From the root of this repository:
### Native
```
cargo xtask integration-test
cargo xtask integration-test local
```
### Virtualized
```
mkdir -p integration-test-binaries
cargo xtask build-integration-test | xargs -I % cp % integration-test-binaries
./test/run.sh integration-test-binaries
cargo xtask integration-test vm
```
### Writing an integration test

@ -1,264 +0,0 @@
#!/bin/bash
VERBOSITY=0
TEMP_D=""
DEF_DISK_FORMAT="raw"
DEF_FILESYSTEM="iso9660"
CR="
"
error() { echo "$@" 1>&2; }
fail() { [ $# -eq 0 ] || error "$@"; exit 1; }
Usage() {
cat <<EOF
Usage: ${0##*/} [ options ] output user-data [meta-data]
Create a disk for cloud-init to utilize nocloud
options:
-h | --help show usage
-d | --disk-format D disk format to output. default: raw
can be anything supported by qemu-img or
tar, tar-seed-local, tar-seed-net
-H | --hostname H set hostname in metadata to H
-f | --filesystem F filesystem format (vfat or iso), default: iso9660
-i | --interfaces F write network interfaces file into metadata
-N | --network-config F write network config file to local datasource
-m | --dsmode M add 'dsmode' ('local' or 'net') to the metadata
default in cloud-init is 'net', meaning network is
required.
-V | --vendor-data F vendor-data file
-v | --verbose increase verbosity
Note, --dsmode, --hostname, and --interfaces are incompatible
with metadata.
Example:
* cat my-user-data
#cloud-config
password: passw0rd
chpasswd: { expire: False }
ssh_pwauth: True
* echo "instance-id: \$(uuidgen || echo i-abcdefg)" > my-meta-data
* ${0##*/} my-seed.img my-user-data my-meta-data
* kvm -net nic -net user,hostfwd=tcp::2222-:22 \\
-drive file=disk1.img,if=virtio -drive file=my-seed.img,if=virtio
* ssh -p 2222 ubuntu@localhost
EOF
}
bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || error "$@"; exit 1; }
cleanup() {
[ -z "${TEMP_D}" -o ! -d "${TEMP_D}" ] || rm -Rf "${TEMP_D}"
}
debug() {
local level=${1}; shift;
[ "${level}" -gt "${VERBOSITY}" ] && return
error "${@}"
}
has_cmd() {
command -v "$1" >/dev/null 2>&1
}
short_opts="hH:i:d:f:m:N:o:V:v"
long_opts="disk-format:,dsmode:,filesystem:,help,hostname:,interfaces:,"
long_opts="${long_opts}network-config:,output:,vendor-data:,verbose"
getopt_out=$(getopt -n "${0##*/}" \
-o "${short_opts}" -l "${long_opts}" -- "$@") &&
eval set -- "${getopt_out}" ||
bad_Usage
## <<insert default variables here>>
output=""
userdata=""
metadata=""
vendordata=""
filesystem=""
diskformat=$DEF_DISK_FORMAT
interfaces=_unset
dsmode=""
hostname=""
ncname="network-config"
while [ $# -ne 0 ]; do
cur=${1}; next=${2};
case "$cur" in
-h|--help) Usage ; exit 0;;
-d|--disk-format) diskformat=$next; shift;;
-f|--filesystem) filesystem=$next; shift;;
-H|--hostname) hostname=$next; shift;;
-i|--interfaces) interfaces=$next; shift;;
-N|--network-config) netcfg=$next; shift;;
-m|--dsmode) dsmode=$next; shift;;
-v|--verbose) VERBOSITY=$((${VERBOSITY}+1));;
-V|--vendor-data) vendordata="$next";;
--) shift; break;;
esac
shift;
done
## check arguments here
## how many args do you expect?
echo $1
echo $2
echo $3
[ $# -ge 2 ] || bad_Usage "must provide output, userdata"
[ $# -le 3 ] || bad_Usage "confused by additional args"
output=$1
userdata=$2
metadata=$3
if [ -n "$metadata" ]; then
[ "$interfaces" = "_unset" -a -z "$dsmode" -a -z "$hostname" ] ||
fail "metadata is incompatible with:" \
"--interfaces, --hostname, --dsmode"
fi
case "$diskformat" in
tar|tar-seed-local|tar-seed-net)
if [ "${filesystem:-tar}" != "tar" ]; then
fail "diskformat=tar is incompatible with filesystem"
fi
filesystem="$diskformat"
;;
tar*)
fail "supported 'tar' formats are tar, tar-seed-local, tar-seed-net"
esac
if [ -z "$filesystem" ]; then
filesystem="$DEF_FILESYSTEM"
fi
if [ "$filesystem" = "iso" ]; then
filesystem="iso9660"
fi
case "$filesystem" in
tar*)
has_cmd tar ||
fail "missing 'tar'. Required for --filesystem=$filesystem";;
vfat)
has_cmd mkfs.vfat ||
fail "missing 'mkfs.vfat'. Required for --filesystem=vfat."
has_cmd mcopy ||
fail "missing 'mcopy'. Required for --filesystem=vfat."
;;
iso9660)
has_cmd mkisofs ||
fail "missing 'mkisofs'. Required for --filesystem=iso9660."
;;
*) fail "unknown filesystem $filesystem";;
esac
case "$diskformat" in
tar*|raw) :;;
*) has_cmd "qemu-img" ||
fail "missing 'qemu-img'. Required for --disk-format=$diskformat."
esac
[ "$interfaces" = "_unset" -o -r "$interfaces" ] ||
fail "$interfaces: not a readable file"
TEMP_D=$(mktemp -d "${TMPDIR:-/tmp}/${0##*/}.XXXXXX") ||
fail "failed to make tempdir"
trap cleanup EXIT
files=( "${TEMP_D}/user-data" "${TEMP_D}/meta-data" )
if [ -n "$metadata" ]; then
cp "$metadata" "$TEMP_D/meta-data" || fail "$metadata: failed to copy"
else
instance_id="iid-local01"
iface_data=""
[ "$interfaces" != "_unset" ] &&
iface_data=$(sed ':a;N;$!ba;s/\n/\\n/g' "$interfaces")
# write json formatted user-data (json is a subset of yaml)
mdata=""
for kv in "instance-id:$instance_id" "local-hostname:$hostname" \
"interfaces:${iface_data}" "dsmode:$dsmode"; do
key=${kv%%:*}
val=${kv#*:}
[ -n "$val" ] || continue
mdata="${mdata:+${mdata},${CR}}\"$key\": \"$val\""
done
printf "{\n%s\n}\n" "$mdata" > "${TEMP_D}/meta-data"
fi
if [ -n "$netcfg" ]; then
cp "$netcfg" "${TEMP_D}/$ncname" ||
fail "failed to copy network config"
files[${#files[@]}]="$TEMP_D/$ncname"
fi
if [ -n "$vendordata" ]; then
cp "$vendordata" "${TEMP_D}/vendor-data" ||
fail "failed to copy vendor data"
files[${#files[@]}]="$TEMP_D/vendor-data"
fi
files_rel=( )
for f in "${files[@]}"; do
files_rel[${#files_rel[@]}]="${f#${TEMP_D}/}"
done
if [ "$userdata" = "-" ]; then
cat > "$TEMP_D/user-data" || fail "failed to read from stdin"
else
cp "$userdata" "$TEMP_D/user-data" || fail "$userdata: failed to copy"
fi
## alternatively, create a vfat filesystem with same files
img="$TEMP_D/seed-data"
tar_opts=( --owner=root --group=root )
case "$filesystem" in
tar)
tar "${tar_opts[@]}" -C "${TEMP_D}" -cf "$img" "${files_rel[@]}" ||
fail "failed to create tarball of ${files_rel[*]}"
;;
tar-seed-local|tar-seed-net)
if [ "$filesystem" = "tar-seed-local" ]; then
path="var/lib/cloud/seed/nocloud"
else
path="var/lib/cloud/seed/nocloud-net"
fi
mkdir -p "${TEMP_D}/${path}" ||
fail "failed making path for seed files"
mv "${files[@]}" "${TEMP_D}/$path" ||
fail "failed moving files"
tar "${tar_opts[@]}" -C "${TEMP_D}" -cf "$img" "${path}" ||
fail "failed to create tarball with $path"
;;
iso9660)
mkisofs -output "$img" -volid cidata \
-joliet -rock "${files[@]}" > "$TEMP_D/err" 2>&1 ||
{ cat "$TEMP_D/err" 1>&2; fail "failed to mkisofs"; }
;;
vfat)
truncate -s 128K "$img" || fail "failed truncate image"
out=$(mkfs.vfat -n cidata "$img" 2>&1) ||
{ error "failed: mkfs.vfat -n cidata $img"; error "$out"; }
mcopy -oi "$img" "${files[@]}" :: ||
fail "failed to copy user-data, meta-data to img"
;;
esac
[ "$output" = "-" ] && output="$TEMP_D/final"
if [ "${diskformat#tar}" != "$diskformat" -o "$diskformat" = "raw" ]; then
cp "$img" "$output" ||
fail "failed to copy image to $output"
else
qemu-img convert -f raw -O "$diskformat" "$img" "$output" ||
fail "failed to convert to disk format $diskformat"
fi
[ "$output" != "$TEMP_D/final" ] || { cat "$output" && output="-"; } ||
fail "failed to write to -"
debug 1 "wrote ${output} with filesystem=$filesystem and diskformat=$diskformat"
# vi: ts=4 noexpandtab

@ -7,7 +7,14 @@ use aya_log_ebpf::{debug, error, info, trace, warn};
#[uprobe]
pub fn test_log(ctx: ProbeContext) {
debug!(&ctx, "Hello from eBPF!");
error!(&ctx, "{}, {}, {}", 69, 420i32, "wao");
error!(
&ctx,
"{}, {}, {}, {:x}",
69,
420i32,
"wao",
"wao".as_bytes()
);
let ipv4 = 167772161u32; // 10.0.0.1
let ipv6 = [
32u8, 1u8, 13u8, 184u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8,

@ -5,7 +5,7 @@ edition = "2021"
publish = false
[dependencies]
anyhow = { workspace = true, default-features = true }
anyhow = { workspace = true, features = ["std"] }
assert_matches = { workspace = true }
aya = { workspace = true }
aya-log = { workspace = true }
@ -16,10 +16,7 @@ netns-rs = { workspace = true }
object = { workspace = true }
rbpf = { workspace = true }
test-case = { workspace = true }
tokio = { workspace = true, default-features = false, features = [
"macros",
"time",
] }
tokio = { workspace = true, features = ["macros", "time"] }
[build-dependencies]
cargo_metadata = { workspace = true }

@ -1,10 +1,9 @@
#include <linux/bpf.h>
// clang-format off
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
// clang-format on
SEC("xdp")
int xdp_drop(struct xdp_md *ctx)
{
return XDP_DROP;
}
int xdp_drop(struct xdp_md *ctx) { return XDP_DROP; }
char _license[] SEC("license") = "GPL";

@ -1,10 +1,9 @@
#include <linux/bpf.h>
// clang-format off
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
// clang-format on
SEC("xdp")
int xdp_pass(struct xdp_md *ctx)
{
return XDP_PASS;
}
int xdp_pass(struct xdp_md *ctx) { return XDP_PASS; }
char _license[] SEC("license") = "GPL";

@ -1,30 +1,30 @@
#include <linux/bpf.h>
// clang-format off
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
// clang-format on
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, __u32);
__type(value, __u64);
__uint(max_entries, 1);
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, __u32);
__type(value, __u64);
__uint(max_entries, 1);
} map_1 SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, __u32);
__type(value, __u64);
__uint(max_entries, 1);
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, __u32);
__type(value, __u64);
__uint(max_entries, 1);
} map_2 SEC(".maps");
SEC("tracepoint")
int bpf_prog(void *ctx)
{
__u32 key = 0;
__u64 twenty_four = 24;
__u64 forty_two = 42;
bpf_map_update_elem(&map_1, &key, &twenty_four, BPF_ANY);
bpf_map_update_elem(&map_2, &key, &forty_two, BPF_ANY);
return 0;
int bpf_prog(void *ctx) {
__u32 key = 0;
__u64 twenty_four = 24;
__u64 forty_two = 42;
bpf_map_update_elem(&map_1, &key, &twenty_four, BPF_ANY);
bpf_map_update_elem(&map_2, &key, &forty_two, BPF_ANY);
return 0;
}
char _license[] SEC("license") = "GPL";

@ -1,10 +1,10 @@
// clang-format off
#include <linux/bpf.h>
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
// clang-format on
char _license[] __attribute__((section("license"), used)) = "GPL";
char _license[] SEC("license") = "GPL";
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
@ -19,12 +19,17 @@ long set_output(__u64 value) {
}
struct relocated_struct_with_scalars {
#ifndef TARGET
__u8 a;
#endif
__u8 b;
__u8 c;
#ifdef TARGET
__u8 d;
#endif
};
__attribute__((noinline)) int field_global() {
__noinline int field_global() {
struct relocated_struct_with_scalars s = {1, 2, 3};
return set_output(__builtin_preserve_access_index(s.b));
}
@ -32,11 +37,16 @@ __attribute__((noinline)) int field_global() {
SEC("uprobe") int field(void *ctx) { return field_global(); }
struct relocated_struct_with_pointer {
#ifndef TARGET
struct relocated_struct_with_pointer *first;
#endif
struct relocated_struct_with_pointer *second;
#ifdef TARGET
struct relocated_struct_with_pointer *first;
#endif
};
__attribute__((noinline)) int pointer_global() {
__noinline int pointer_global() {
struct relocated_struct_with_pointer s = {
(struct relocated_struct_with_pointer *)42,
(struct relocated_struct_with_pointer *)21,
@ -46,49 +56,86 @@ __attribute__((noinline)) int pointer_global() {
SEC("uprobe") int pointer(void *ctx) { return pointer_global(); }
__attribute__((noinline)) int struct_flavors_global() {
__noinline int struct_flavors_global() {
struct relocated_struct_with_scalars s = {1, 2, 3};
#ifndef TARGET
if (bpf_core_field_exists(s.a)) {
return set_output(__builtin_preserve_access_index(s.a));
#else
if (bpf_core_field_exists(s.d)) {
return set_output(__builtin_preserve_access_index(s.d));
#endif
} else {
return set_output(__builtin_preserve_access_index(s.b));
return set_output(__builtin_preserve_access_index(s.c));
}
}
SEC("uprobe") int struct_flavors(void *ctx) { return struct_flavors_global(); }
enum relocated_enum_unsigned_32 { U32 = 0xAAAAAAAA };
enum relocated_enum_unsigned_32 {
U32_VAL =
#ifndef TARGET
0xAAAAAAAA
#else
0xBBBBBBBB
#endif
};
__attribute__((noinline)) int enum_unsigned_32_global() {
return set_output(bpf_core_enum_value(enum relocated_enum_unsigned_32, U32));
__noinline int enum_unsigned_32_global() {
return set_output(
bpf_core_enum_value(enum relocated_enum_unsigned_32, U32_VAL));
}
SEC("uprobe") int enum_unsigned_32(void *ctx) {
return enum_unsigned_32_global();
}
enum relocated_enum_signed_32 { S32 = -0x7AAAAAAA };
enum relocated_enum_signed_32 {
S32_VAL =
#ifndef TARGET
-0x7AAAAAAA
#else
-0x7BBBBBBB
#endif
};
__attribute__((noinline)) int enum_signed_32_global() {
return set_output(bpf_core_enum_value(enum relocated_enum_signed_32, S32));
__noinline int enum_signed_32_global() {
return set_output(
bpf_core_enum_value(enum relocated_enum_signed_32, S32_VAL));
}
SEC("uprobe") int enum_signed_32(void *ctx) { return enum_signed_32_global(); }
enum relocated_enum_unsigned_64 { U64 = 0xAAAAAAAABBBBBBBB };
enum relocated_enum_unsigned_64 {
U64_VAL =
#ifndef TARGET
0xAAAAAAAABBBBBBBB
#else
0xCCCCCCCCDDDDDDDD
#endif
};
__attribute__((noinline)) int enum_unsigned_64_global() {
return set_output(bpf_core_enum_value(enum relocated_enum_unsigned_64, U64));
__noinline int enum_unsigned_64_global() {
return set_output(
bpf_core_enum_value(enum relocated_enum_unsigned_64, U64_VAL));
}
SEC("uprobe") int enum_unsigned_64(void *ctx) {
return enum_unsigned_64_global();
}
enum relocated_enum_signed_64 { u64 = -0xAAAAAAABBBBBBBB };
enum relocated_enum_signed_64 {
S64_VAL =
#ifndef TARGET
-0xAAAAAAABBBBBBBB
#else
-0xCCCCCCCDDDDDDDD
#endif
};
__attribute__((noinline)) int enum_signed_64_global() {
return set_output(bpf_core_enum_value(enum relocated_enum_signed_64, u64));
__noinline int enum_signed_64_global() {
return set_output(
bpf_core_enum_value(enum relocated_enum_signed_64, S64_VAL));
}
SEC("uprobe") int enum_signed_64(void *ctx) { return enum_signed_64_global(); }

@ -1,77 +0,0 @@
// clang-format off
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
// clang-format on
#include <stdlib.h>
long set_output(__u64 value) { exit((int)value); }
struct relocated_struct_with_scalars {
__u8 b;
__u8 c;
__u8 d;
};
__attribute__((noinline)) int field_global() {
struct relocated_struct_with_scalars s = {1, 2, 3};
return set_output(__builtin_preserve_access_index(s.b));
}
struct relocated_struct_with_pointer {
struct relocated_struct_with_pointer *second;
struct relocated_struct_with_pointer *first;
};
__attribute__((noinline)) int pointer_global() {
struct relocated_struct_with_pointer s = {
(struct relocated_struct_with_pointer *)42,
(struct relocated_struct_with_pointer *)21,
};
return set_output((__u64)__builtin_preserve_access_index(s.first));
}
__attribute__((noinline)) int struct_flavors_global() {
struct relocated_struct_with_scalars s = {1, 2, 3};
if (bpf_core_field_exists(s.b)) {
return set_output(__builtin_preserve_access_index(s.b));
} else {
return set_output(__builtin_preserve_access_index(s.c));
}
}
enum relocated_enum_unsigned_32 { U32 = 0xBBBBBBBB };
__attribute__((noinline)) int enum_unsigned_32_global() {
return set_output(bpf_core_enum_value(enum relocated_enum_unsigned_32, U32));
}
enum relocated_enum_signed_32 { S32 = -0x7BBBBBBB };
__attribute__((noinline)) int enum_signed_32_global() {
return set_output(bpf_core_enum_value(enum relocated_enum_signed_32, S32));
}
enum relocated_enum_unsigned_64 { U64 = 0xCCCCCCCCDDDDDDDD };
__attribute__((noinline)) int enum_unsigned_64_global() {
return set_output(bpf_core_enum_value(enum relocated_enum_unsigned_64, U64));
}
enum relocated_enum_signed_64 { u64 = -0xCCCCCCCDDDDDDDD };
__attribute__((noinline)) int enum_signed_64_global() {
return set_output(bpf_core_enum_value(enum relocated_enum_signed_64, u64));
}
// Avoids dead code elimination by the compiler.
int main() {
field_global();
pointer_global();
struct_flavors_global();
enum_unsigned_32_global();
enum_signed_32_global();
enum_unsigned_64_global();
enum_signed_64_global();
}

@ -1,28 +1,25 @@
#include <linux/bpf.h>
// clang-format off
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
// clang-format on
char _license[] SEC("license") = "GPL";
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, __u32);
__type(value, __u64);
__uint(max_entries, 2);
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, __u32);
__type(value, __u64);
__uint(max_entries, 2);
} RESULTS SEC(".maps");
static __u64
inc_cb(void *map, __u32 *key, void *val,
void *data)
{
__u64 *value = val;
*value += 1;
return 0;
static __u64 inc_cb(void *map, __u32 *key, void *val, void *data) {
__u64 *value = val;
*value += 1;
return 0;
}
SEC("uprobe/test_text_64_64_reloc")
int test_text_64_64_reloc(struct pt_regs *ctx)
{
bpf_for_each_map_elem(&RESULTS, inc_cb, NULL, 0);
return 0;
int test_text_64_64_reloc(struct pt_regs *ctx) {
bpf_for_each_map_elem(&RESULTS, inc_cb, NULL, 0);
return 0;
}

@ -4,7 +4,7 @@ use std::{
fs,
io::{BufRead as _, BufReader},
path::PathBuf,
process::{Child, Command, Stdio},
process::{Child, Command, Output, Stdio},
};
use cargo_metadata::{
@ -64,20 +64,14 @@ fn main() {
panic!("unsupported endian={:?}", endian)
};
const C_BPF: &[(&str, &str)] = &[
("ext.bpf.c", "ext.bpf.o"),
("main.bpf.c", "main.bpf.o"),
("multimap-btf.bpf.c", "multimap-btf.bpf.o"),
("reloc.bpf.c", "reloc.bpf.o"),
("text_64_64_reloc.c", "text_64_64_reloc.o"),
const C_BPF: &[(&str, bool)] = &[
("ext.bpf.c", false),
("main.bpf.c", false),
("multimap-btf.bpf.c", false),
("reloc.bpf.c", true),
("text_64_64_reloc.c", false),
];
let c_bpf = C_BPF.iter().map(|(src, dst)| (src, out_dir.join(dst)));
const C_BTF: &[(&str, &str)] = &[("reloc.btf.c", "reloc.btf.o")];
let c_btf = C_BTF.iter().map(|(src, dst)| (src, out_dir.join(dst)));
if build_integration_bpf {
let libbpf_dir = manifest_dir
.parent()
@ -116,65 +110,67 @@ fn main() {
target_arch.push(arch);
};
for (src, dst) in c_bpf {
let src = bpf_dir.join(src);
println!("cargo:rerun-if-changed={}", src.to_str().unwrap());
// NB: libbpf's documentation suggests that vmlinux.h be generated by running `bpftool btf
// dump file /sys/kernel/btf/vmlinux format c`; this allows CO-RE to work.
//
// However in our tests we do not make use of kernel data structures, and so any vmlinux.h
// which defines the constants we need (e.g. `__u8`, `__u64`, `BPF_MAP_TYPE_ARRAY`,
// `BPF_ANY`, `XDP_PASS`, `XDP_DROP`, etc.) will suffice. Since we already have a libbpf
// submodule which happens to include such a file, we use it.
let libbpf_vmlinux_dir = libbpf_dir.join(".github/actions/build-selftests");
exec(
Command::new("clang")
.arg("-I")
.arg(&libbpf_headers_dir)
.args(["-g", "-O2", "-target", target, "-c"])
.arg(&target_arch)
.arg(src)
.arg("-o")
.arg(dst),
)
.unwrap();
}
let clang = || {
let mut cmd = Command::new("clang");
cmd.arg("-nostdlibinc")
.arg("-I")
.arg(&libbpf_headers_dir)
.arg("-I")
.arg(&libbpf_vmlinux_dir)
.args(["-g", "-O2", "-target", target, "-c"])
.arg(&target_arch);
cmd
};
for (src, dst) in c_btf {
for (src, build_btf) in C_BPF {
let dst = out_dir.join(src).with_extension("o");
let src = bpf_dir.join(src);
println!("cargo:rerun-if-changed={}", src.to_str().unwrap());
let mut cmd = Command::new("clang");
cmd.arg("-I")
.arg(&libbpf_headers_dir)
.args(["-g", "-target", target, "-c"])
.arg(&target_arch)
.arg(src)
.args(["-o", "-"]);
let mut child = cmd
.stdout(Stdio::piped())
.spawn()
.unwrap_or_else(|err| panic!("failed to spawn {cmd:?}: {err}"));
let Child { stdout, .. } = &mut child;
let stdout = stdout.take().unwrap();
let mut output = OsString::new();
output.push(".BTF=");
output.push(dst);
exec(
// NB: objcopy doesn't support reading from stdin, so we have to use llvm-objcopy.
Command::new("llvm-objcopy")
.arg("--dump-section")
.arg(output)
.arg("-")
.stdin(stdout),
)
.unwrap();
let status = child
.wait()
.unwrap_or_else(|err| panic!("failed to wait for {cmd:?}: {err}"));
match status.code() {
Some(code) => match code {
0 => {}
code => panic!("{cmd:?} exited with status code {code}"),
},
None => panic!("{cmd:?} terminated by signal"),
exec(clang().arg(&src).arg("-o").arg(&dst)).unwrap();
if *build_btf {
let mut cmd = clang();
let mut child = cmd
.arg("-DTARGET")
.arg(&src)
.args(["-o", "-"])
.stdout(Stdio::piped())
.spawn()
.unwrap_or_else(|err| panic!("failed to spawn {cmd:?}: {err}"));
let Child { stdout, .. } = &mut child;
let stdout = stdout.take().unwrap();
let dst = dst.with_extension("target.o");
let mut output = OsString::new();
output.push(".BTF=");
output.push(dst);
exec(
// NB: objcopy doesn't support reading from stdin, so we have to use llvm-objcopy.
Command::new("llvm-objcopy")
.arg("--dump-section")
.arg(output)
.arg("-")
.stdin(stdout),
)
.unwrap();
let output = child
.wait_with_output()
.unwrap_or_else(|err| panic!("failed to wait for {cmd:?}: {err}"));
let Output { status, .. } = &output;
assert_eq!(status.code(), Some(0), "{cmd:?} failed: {output:?}");
}
}
@ -257,13 +253,7 @@ fn main() {
let status = child
.wait()
.unwrap_or_else(|err| panic!("failed to wait for {cmd:?}: {err}"));
match status.code() {
Some(code) => match code {
0 => {}
code => panic!("{cmd:?} exited with status code {code}"),
},
None => panic!("{cmd:?} terminated by signal"),
}
assert_eq!(status.code(), Some(0), "{cmd:?} failed: {status:?}");
stderr.join().map_err(std::panic::resume_unwind).unwrap();
@ -273,8 +263,13 @@ fn main() {
.unwrap_or_else(|err| panic!("failed to copy {binary:?} to {dst:?}: {err}"));
}
} else {
for (_src, dst) in c_bpf.chain(c_btf) {
for (src, build_btf) in C_BPF {
let dst = out_dir.join(src).with_extension("o");
fs::write(&dst, []).unwrap_or_else(|err| panic!("failed to create {dst:?}: {err}"));
if *build_btf {
let dst = dst.with_extension("target.o");
fs::write(&dst, []).unwrap_or_else(|err| panic!("failed to create {dst:?}: {err}"));
}
}
let Package { targets, .. } = integration_ebpf_package;

@ -5,7 +5,8 @@ pub const MAIN: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/main.b
pub const MULTIMAP_BTF: &[u8] =
include_bytes_aligned!(concat!(env!("OUT_DIR"), "/multimap-btf.bpf.o"));
pub const RELOC_BPF: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/reloc.bpf.o"));
pub const RELOC_BTF: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/reloc.btf.o"));
pub const RELOC_BTF: &[u8] =
include_bytes_aligned!(concat!(env!("OUT_DIR"), "/reloc.bpf.target.o"));
pub const TEXT_64_64_RELOC: &[u8] =
include_bytes_aligned!(concat!(env!("OUT_DIR"), "/text_64_64_reloc.o"));

@ -2,20 +2,20 @@ use test_case::test_case;
use aya::{maps::Array, programs::UProbe, util::KernelVersion, BpfLoader, Btf, Endianness};
#[test_case("field", false, None, 2)]
#[test_case("field", true, None, 1)]
#[test_case("enum_signed_32", false, Some((KernelVersion::new(6, 0, 0), "https://github.com/torvalds/linux/commit/6089fb3")), -0x7AAAAAAAi32 as u64)]
#[test_case("enum_signed_32", true, Some((KernelVersion::new(6, 0, 0), "https://github.com/torvalds/linux/commit/6089fb3")), -0x7BBBBBBBi32 as u64)]
#[test_case("enum_signed_64", false, Some((KernelVersion::new(6, 0, 0), "https://github.com/torvalds/linux/commit/6089fb3")), -0xAAAAAAABBBBBBBBi64 as u64)]
#[test_case("enum_signed_64", true, Some((KernelVersion::new(6, 0, 0), "https://github.com/torvalds/linux/commit/6089fb3")), -0xCCCCCCCDDDDDDDDi64 as u64)]
#[test_case("enum_unsigned_32", false, None, 0xAAAAAAAA)]
#[test_case("enum_unsigned_32", true, None, 0xBBBBBBBB)]
#[test_case("enum_unsigned_64", false, Some((KernelVersion::new(6, 0, 0), "https://github.com/torvalds/linux/commit/6089fb3")), 0xAAAAAAAABBBBBBBB)]
#[test_case("enum_unsigned_64", true, Some((KernelVersion::new(6, 0, 0), "https://github.com/torvalds/linux/commit/6089fb3")), 0xCCCCCCCCDDDDDDDD)]
#[test_case("field", false, None, 2)]
#[test_case("field", true, None, 1)]
#[test_case("pointer", false, None, 42)]
#[test_case("pointer", true, None, 21)]
#[test_case("struct_flavors", false, None, 1)]
#[test_case("struct_flavors", true, None, 1)]
#[test_case("enum_signed_32", false, Some((KernelVersion::new(6, 0, 0), "https://github.com/torvalds/linux/commit/6089fb3")), -0x7AAAAAAAi32 as u64)]
#[test_case("enum_signed_32", true, Some((KernelVersion::new(6, 0, 0), "https://github.com/torvalds/linux/commit/6089fb3")), -0x7BBBBBBBi32 as u64)]
#[test_case("enum_unsigned_64", false, Some((KernelVersion::new(6, 0, 0), "https://github.com/torvalds/linux/commit/6089fb3")), 0xAAAAAAAABBBBBBBB)]
#[test_case("enum_unsigned_64", true, Some((KernelVersion::new(6, 0, 0), "https://github.com/torvalds/linux/commit/6089fb3")), 0xCCCCCCCCDDDDDDDD)]
#[test_case("enum_signed_64", false, Some((KernelVersion::new(6, 0, 0), "https://github.com/torvalds/linux/commit/6089fb3")), -0xAAAAAAABBBBBBBBi64 as u64)]
#[test_case("enum_signed_64", true, Some((KernelVersion::new(6, 0, 0), "https://github.com/torvalds/linux/commit/6089fb3")), -0xCCCCCCCDDDDDDDDi64 as u64)]
#[test_case("struct_flavors", true, None, 2)]
fn relocation_tests(
program: &str,
with_relocations: bool,

@ -70,12 +70,16 @@ async fn log() {
let mut logs = 0;
let records = loop {
tokio::time::sleep(std::time::Duration::from_millis(10)).await;
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
let records = captured_logs.lock().unwrap();
if records.len() == logs {
let len = records.len();
if len == 0 {
continue;
}
if len == logs {
break records;
}
logs = records.len();
logs = len;
};
let mut records = records.iter();
@ -92,7 +96,7 @@ async fn log() {
assert_eq!(
records.next(),
Some(&CapturedLog {
body: "69, 420, wao".into(),
body: "69, 420, wao, 77616f".into(),
level: Level::Error,
target: "log".into(),
})

@ -74,7 +74,7 @@ fn use_map_with_rbpf() {
object
.relocate_maps(
maps.iter()
.map(|(s, (fd, map))| (s.as_ref() as &str, Some(*fd), map)),
.map(|(s, (fd, map))| (s.as_ref() as &str, *fd, map)),
&text_sections,
)
.expect("Relocation failed");

@ -1,5 +1,5 @@
use aya::{
programs::{Extension, TracePoint, Xdp, XdpFlags},
programs::{loaded_programs, Extension, TracePoint, Xdp, XdpFlags},
util::KernelVersion,
Bpf, BpfLoader,
};
@ -63,5 +63,36 @@ fn extension() {
.load(crate::EXT)
.unwrap();
let drop_: &mut Extension = bpf.program_mut("xdp_drop").unwrap().try_into().unwrap();
drop_.load(pass.fd().unwrap(), "xdp_pass").unwrap();
drop_
.load(pass.fd().unwrap().try_clone().unwrap(), "xdp_pass")
.unwrap();
}
#[test]
fn list_loaded_programs() {
// Load a program.
let mut bpf = Bpf::load(crate::PASS).unwrap();
let dispatcher: &mut Xdp = bpf.program_mut("pass").unwrap().try_into().unwrap();
dispatcher.load().unwrap();
dispatcher.attach("lo", XdpFlags::default()).unwrap();
// Ensure the loaded_programs() api doesn't panic.
let prog = loaded_programs()
.map(|p| p.unwrap())
.find(|p| p.name_as_str().unwrap() == "pass")
.unwrap();
// Ensure all relevant helper functions don't panic.
prog.name();
prog.id();
prog.tag();
prog.program_type();
prog.gpl_compatible();
prog.map_ids().unwrap();
prog.btf_id();
prog.size_translated();
prog.memory_locked().unwrap();
prog.verified_instruction_count();
prog.loaded_at();
prog.fd().unwrap();
}

@ -1,241 +0,0 @@
#!/usr/bin/env bash
set -ex
if [ "$(uname -s)" = "Darwin" ]; then
PATH="$(dirname "$(brew list gnu-getopt | grep "bin/getopt$")"):$PATH"
export PATH
fi
AYA_SOURCE_DIR="$(realpath "$(dirname "$0")"/..)"
# Temporary directory for tests to use.
AYA_TMPDIR="${AYA_SOURCE_DIR}/.tmp"
# Directory for VM images
AYA_IMGDIR=${AYA_TMPDIR}
if [ -z "${AYA_BUILD_TARGET}" ]; then
AYA_BUILD_TARGET=$(rustc -vV | sed -n 's|host: ||p')
fi
AYA_HOST_ARCH=$(uname -m)
if [ "${AYA_HOST_ARCH}" = "arm64" ]; then
AYA_HOST_ARCH="aarch64"
fi
if [ -z "${AYA_GUEST_ARCH}" ]; then
AYA_GUEST_ARCH="${AYA_HOST_ARCH}"
fi
if [ "${AYA_GUEST_ARCH}" = "aarch64" ]; then
if [ -z "${AARCH64_UEFI}" ]; then
AARCH64_UEFI="$(brew list qemu -1 -v | grep edk2-aarch64-code.fd)"
fi
fi
if [ -z "$AYA_MUSL_TARGET" ]; then
AYA_MUSL_TARGET=${AYA_GUEST_ARCH}-unknown-linux-musl
fi
# Test Image
if [ -z "${AYA_TEST_IMAGE}" ]; then
AYA_TEST_IMAGE="fedora38"
fi
case "${AYA_TEST_IMAGE}" in
fedora*) AYA_SSH_USER="fedora";;
centos*) AYA_SSH_USER="centos";;
esac
download_images() {
mkdir -p "${AYA_IMGDIR}"
case $1 in
fedora37)
if [ ! -f "${AYA_IMGDIR}/fedora37.${AYA_GUEST_ARCH}.qcow2" ]; then
IMAGE="Fedora-Cloud-Base-37-1.7.${AYA_GUEST_ARCH}.qcow2"
IMAGE_URL="https://download.fedoraproject.org/pub/fedora/linux/releases/37/Cloud/${AYA_GUEST_ARCH}/images"
echo "Downloading: ${IMAGE}, this may take a while..."
curl -o "${AYA_IMGDIR}/fedora37.${AYA_GUEST_ARCH}.qcow2" -sSL "${IMAGE_URL}/${IMAGE}"
fi
;;
fedora38)
if [ ! -f "${AYA_IMGDIR}/fedora38.${AYA_GUEST_ARCH}.qcow2" ]; then
IMAGE="Fedora-Cloud-Base-38_Beta-1.3.${AYA_GUEST_ARCH}.qcow2"
IMAGE_URL="https://fr2.rpmfind.net/linux/fedora/linux/releases/test/38_Beta/Cloud/${AYA_GUEST_ARCH}/images"
echo "Downloading: ${IMAGE}, this may take a while..."
curl -o "${AYA_IMGDIR}/fedora38.${AYA_GUEST_ARCH}.qcow2" -sSL "${IMAGE_URL}/${IMAGE}"
fi
;;
centos8)
if [ ! -f "${AYA_IMGDIR}/centos8.${AYA_GUEST_ARCH}.qcow2" ]; then
IMAGE="CentOS-8-GenericCloud-8.4.2105-20210603.0.${AYA_GUEST_ARCH}.qcow2"
IMAGE_URL="https://cloud.centos.org/centos/8/${AYA_GUEST_ARCH}/images"
echo "Downloading: ${IMAGE}, this may take a while..."
curl -o "${AYA_IMGDIR}/centos8.${AYA_GUEST_ARCH}.qcow2" -sSL "${IMAGE_URL}/${IMAGE}"
fi
;;
*)
echo "$1 is not a recognized image name"
return 1
;;
esac
}
start_vm() {
download_images "${AYA_TEST_IMAGE}"
# prepare config
cat > "${AYA_TMPDIR}/metadata.yaml" <<EOF
instance-id: iid-local01
local-hostname: test
EOF
if [ ! -f "${AYA_TMPDIR}/test_rsa" ]; then
ssh-keygen -t rsa -b 4096 -f "${AYA_TMPDIR}/test_rsa" -N "" -C "" -q
pub_key=$(cat "${AYA_TMPDIR}/test_rsa.pub")
fi
if [ ! -f "${AYA_TMPDIR}/ssh_config" ]; then
cat > "${AYA_TMPDIR}/ssh_config" <<EOF
StrictHostKeyChecking=no
UserKnownHostsFile=/dev/null
GlobalKnownHostsFile=/dev/null
EOF
fi
cat > "${AYA_TMPDIR}/user-data.yaml" <<EOF
#cloud-config
ssh_authorized_keys:
- ${pub_key}
EOF
"$AYA_SOURCE_DIR"/test/cloud-localds "${AYA_TMPDIR}/seed.img" "${AYA_TMPDIR}/user-data.yaml" "${AYA_TMPDIR}/metadata.yaml"
case "${AYA_GUEST_ARCH}" in
x86_64)
QEMU=qemu-system-x86_64
machine="q35"
cpu="qemu64"
nr_cpus="$(nproc --all)"
if [ "${AYA_HOST_ARCH}" = "${AYA_GUEST_ARCH}" ]; then
if [ -c /dev/kvm ]; then
machine="${machine},accel=kvm"
cpu="host"
elif [ "$(uname -s)" = "Darwin" ]; then
machine="${machine},accel=hvf"
cpu="host"
fi
fi
;;
aarch64)
QEMU=qemu-system-aarch64
machine="virt"
cpu="cortex-a57"
uefi=("-drive" "file=${AARCH64_UEFI},if=pflash,format=raw,readonly=on")
if [ "${AYA_HOST_ARCH}" = "${AYA_GUEST_ARCH}" ]; then
if [ -c /dev/kvm ]; then
machine="${machine},accel=kvm"
cpu="host"
nr_cpus="$(nproc --all)"
elif [ "$(uname -s)" = "Darwin" ]; then
machine="${machine},accel=hvf,highmem=off"
cpu="cortex-a72"
# nrpoc --all on apple silicon returns the two extra fancy
# cores and then qemu complains that nr_cpus > actual_cores
nr_cpus=8
fi
fi
;;
*)
echo "${AYA_GUEST_ARCH} is not supported"
return 1
;;
esac
if [ ! -f "${AYA_IMGDIR}/vm.qcow2" ]; then
echo "Creating VM image"
qemu-img create -F qcow2 -f qcow2 -o backing_file="${AYA_IMGDIR}/${AYA_TEST_IMAGE}.${AYA_GUEST_ARCH}.qcow2" "${AYA_IMGDIR}/vm.qcow2" || return 1
else
echo "Reusing existing VM image"
fi
$QEMU \
-machine "${machine}" \
-cpu "${cpu}" \
-m 3G \
-smp "${nr_cpus}" \
-display none \
-monitor none \
-daemonize \
-pidfile "${AYA_TMPDIR}/vm.pid" \
-device virtio-net-pci,netdev=net0 \
-netdev user,id=net0,hostfwd=tcp::2222-:22 \
"${uefi[@]}" \
-drive if=virtio,format=qcow2,file="${AYA_IMGDIR}/vm.qcow2" \
-drive if=virtio,format=raw,file="${AYA_TMPDIR}/seed.img" || return 1
trap cleanup_vm EXIT
echo "Waiting for SSH on port 2222..."
retry=0
max_retries=300
while ! ssh -q -F "${AYA_TMPDIR}/ssh_config" -o ConnectTimeout=1 -i "${AYA_TMPDIR}/test_rsa" "${AYA_SSH_USER}"@localhost -p 2222 echo "Hello VM"; do
retry=$((retry+1))
if [ ${retry} -gt ${max_retries} ]; then
echo "Unable to connect to VM"
return 1
fi
sleep 1
done
echo "VM launched"
exec_vm uname -a
echo "Enabling testing repositories"
exec_vm sudo dnf config-manager --set-enabled updates-testing
exec_vm sudo dnf config-manager --set-enabled updates-testing-modular
}
scp_vm() {
local=$1
remote=$(basename "$1")
scp -q -F "${AYA_TMPDIR}/ssh_config" \
-i "${AYA_TMPDIR}/test_rsa" \
-P 2222 "${local}" \
"${AYA_SSH_USER}@localhost:${remote}"
}
rsync_vm() {
rsync -a -e "ssh -p 2222 -F ${AYA_TMPDIR}/ssh_config -i ${AYA_TMPDIR}/test_rsa" "$1" "$AYA_SSH_USER"@localhost:
}
exec_vm() {
ssh -q -F "${AYA_TMPDIR}/ssh_config" \
-i "${AYA_TMPDIR}/test_rsa" \
-p 2222 \
"${AYA_SSH_USER}"@localhost \
"$@"
}
stop_vm() {
if [ -f "${AYA_TMPDIR}/vm.pid" ]; then
echo "Stopping VM forcefully"
kill -9 "$(cat "${AYA_TMPDIR}/vm.pid")"
rm "${AYA_TMPDIR}/vm.pid"
fi
}
cleanup_vm() {
if ! stop_vm; then
rm -f "${AYA_IMGDIR}/vm.qcow2"
fi
}
start_vm
trap cleanup_vm EXIT
# make sure we always use fresh sources (also see comment at the end)
rsync_vm "$*"
exec_vm "find $* -type f -executable -print0 | xargs -0 -I {} sudo {} --test-threads=1"
# we rm and sync but it doesn't seem to work reliably - I guess we could sleep a
# few seconds after but ain't nobody got time for that. Instead we also rm
# before rsyncing.
exec_vm "rm -rf $*; sync"

@ -6,10 +6,10 @@ edition = "2021"
publish = false
[dependencies]
anyhow = { workspace = true, default-features = true }
anyhow = { workspace = true, features = ["std"] }
aya-tool = { workspace = true }
cargo_metadata = { workspace = true }
clap = { workspace = true, default-features = true, features = ["derive"] }
clap = { workspace = true, features = ["derive"] }
dialoguer = { workspace = true }
diff = { workspace = true }
indoc = { workspace = true }

@ -18,6 +18,8 @@ pub aya_log_common::Argument::U32
pub aya_log_common::Argument::U64
pub aya_log_common::Argument::U8
pub aya_log_common::Argument::Usize
impl core::convert::From<aya_log_common::Argument> for u8
pub fn u8::from(enum_value: aya_log_common::Argument) -> Self
impl core::clone::Clone for aya_log_common::Argument
pub fn aya_log_common::Argument::clone(&self) -> aya_log_common::Argument
impl core::fmt::Debug for aya_log_common::Argument
@ -52,7 +54,7 @@ pub aya_log_common::DisplayHint::LowerMac
pub aya_log_common::DisplayHint::UpperHex
pub aya_log_common::DisplayHint::UpperMac
impl aya_log_common::WriteToBuf for aya_log_common::DisplayHint
pub fn aya_log_common::DisplayHint::write(self, buf: &mut [u8]) -> core::result::Result<usize, ()>
pub fn aya_log_common::DisplayHint::write(self, buf: &mut [u8]) -> core::option::Option<core::num::nonzero::NonZeroUsize>
impl core::convert::From<aya_log_common::DisplayHint> for u8
pub fn u8::from(enum_value: aya_log_common::DisplayHint) -> Self
impl core::clone::Clone for aya_log_common::DisplayHint
@ -134,6 +136,8 @@ pub aya_log_common::RecordField::Line
pub aya_log_common::RecordField::Module
pub aya_log_common::RecordField::NumArgs
pub aya_log_common::RecordField::Target = 1
impl core::convert::From<aya_log_common::RecordField> for u8
pub fn u8::from(enum_value: aya_log_common::RecordField) -> Self
impl core::clone::Clone for aya_log_common::RecordField
pub fn aya_log_common::RecordField::clone(&self) -> aya_log_common::RecordField
impl core::fmt::Debug for aya_log_common::RecordField
@ -212,41 +216,41 @@ impl aya_log_common::UpperHexFormatter for usize
pub trait aya_log_common::UpperMacFormatter
impl aya_log_common::UpperMacFormatter for [u8; 6]
pub trait aya_log_common::WriteToBuf
pub fn aya_log_common::WriteToBuf::write(self, buf: &mut [u8]) -> core::result::Result<usize, ()>
pub fn aya_log_common::WriteToBuf::write(self, buf: &mut [u8]) -> core::option::Option<core::num::nonzero::NonZeroUsize>
impl aya_log_common::WriteToBuf for &[u8]
pub fn &[u8]::write(self, buf: &mut [u8]) -> core::result::Result<usize, ()>
pub fn &[u8]::write(self, buf: &mut [u8]) -> core::option::Option<core::num::nonzero::NonZeroUsize>
impl aya_log_common::WriteToBuf for &str
pub fn &str::write(self, buf: &mut [u8]) -> core::result::Result<usize, ()>
pub fn &str::write(self, buf: &mut [u8]) -> core::option::Option<core::num::nonzero::NonZeroUsize>
impl aya_log_common::WriteToBuf for [u16; 8]
pub fn [u16; 8]::write(self, buf: &mut [u8]) -> core::result::Result<usize, ()>
pub fn [u16; 8]::write(self, buf: &mut [u8]) -> core::option::Option<core::num::nonzero::NonZeroUsize>
impl aya_log_common::WriteToBuf for [u8; 16]
pub fn [u8; 16]::write(self, buf: &mut [u8]) -> core::result::Result<usize, ()>
pub fn [u8; 16]::write(self, buf: &mut [u8]) -> core::option::Option<core::num::nonzero::NonZeroUsize>
impl aya_log_common::WriteToBuf for [u8; 6]
pub fn [u8; 6]::write(self, buf: &mut [u8]) -> core::result::Result<usize, ()>
pub fn [u8; 6]::write(self, buf: &mut [u8]) -> core::option::Option<core::num::nonzero::NonZeroUsize>
impl aya_log_common::WriteToBuf for aya_log_common::DisplayHint
pub fn aya_log_common::DisplayHint::write(self, buf: &mut [u8]) -> core::result::Result<usize, ()>
pub fn aya_log_common::DisplayHint::write(self, buf: &mut [u8]) -> core::option::Option<core::num::nonzero::NonZeroUsize>
impl aya_log_common::WriteToBuf for f32
pub fn f32::write(self, buf: &mut [u8]) -> core::result::Result<usize, ()>
pub fn f32::write(self, buf: &mut [u8]) -> core::option::Option<core::num::nonzero::NonZeroUsize>
impl aya_log_common::WriteToBuf for f64
pub fn f64::write(self, buf: &mut [u8]) -> core::result::Result<usize, ()>
pub fn f64::write(self, buf: &mut [u8]) -> core::option::Option<core::num::nonzero::NonZeroUsize>
impl aya_log_common::WriteToBuf for i16
pub fn i16::write(self, buf: &mut [u8]) -> core::result::Result<usize, ()>
pub fn i16::write(self, buf: &mut [u8]) -> core::option::Option<core::num::nonzero::NonZeroUsize>
impl aya_log_common::WriteToBuf for i32
pub fn i32::write(self, buf: &mut [u8]) -> core::result::Result<usize, ()>
pub fn i32::write(self, buf: &mut [u8]) -> core::option::Option<core::num::nonzero::NonZeroUsize>
impl aya_log_common::WriteToBuf for i64
pub fn i64::write(self, buf: &mut [u8]) -> core::result::Result<usize, ()>
pub fn i64::write(self, buf: &mut [u8]) -> core::option::Option<core::num::nonzero::NonZeroUsize>
impl aya_log_common::WriteToBuf for i8
pub fn i8::write(self, buf: &mut [u8]) -> core::result::Result<usize, ()>
pub fn i8::write(self, buf: &mut [u8]) -> core::option::Option<core::num::nonzero::NonZeroUsize>
impl aya_log_common::WriteToBuf for isize
pub fn isize::write(self, buf: &mut [u8]) -> core::result::Result<usize, ()>
pub fn isize::write(self, buf: &mut [u8]) -> core::option::Option<core::num::nonzero::NonZeroUsize>
impl aya_log_common::WriteToBuf for u16
pub fn u16::write(self, buf: &mut [u8]) -> core::result::Result<usize, ()>
pub fn u16::write(self, buf: &mut [u8]) -> core::option::Option<core::num::nonzero::NonZeroUsize>
impl aya_log_common::WriteToBuf for u32
pub fn u32::write(self, buf: &mut [u8]) -> core::result::Result<usize, ()>
pub fn u32::write(self, buf: &mut [u8]) -> core::option::Option<core::num::nonzero::NonZeroUsize>
impl aya_log_common::WriteToBuf for u64
pub fn u64::write(self, buf: &mut [u8]) -> core::result::Result<usize, ()>
pub fn u64::write(self, buf: &mut [u8]) -> core::option::Option<core::num::nonzero::NonZeroUsize>
impl aya_log_common::WriteToBuf for u8
pub fn u8::write(self, buf: &mut [u8]) -> core::result::Result<usize, ()>
pub fn u8::write(self, buf: &mut [u8]) -> core::option::Option<core::num::nonzero::NonZeroUsize>
impl aya_log_common::WriteToBuf for usize
pub fn usize::write(self, buf: &mut [u8]) -> core::result::Result<usize, ()>
pub fn usize::write(self, buf: &mut [u8]) -> core::option::Option<core::num::nonzero::NonZeroUsize>
pub type aya_log_common::LogValueLength = u16

@ -19,8 +19,6 @@ impl core::marker::Sync for aya_log::Error
impl core::marker::Unpin for aya_log::Error
impl !core::panic::unwind_safe::RefUnwindSafe for aya_log::Error
impl !core::panic::unwind_safe::UnwindSafe for aya_log::Error
impl<E> core::any::Provider for aya_log::Error where E: core::error::Error + core::marker::Sized
pub fn aya_log::Error::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya_log::Error where U: core::convert::From<T>
pub fn aya_log::Error::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya_log::Error where U: core::convert::Into<T>

@ -51,8 +51,6 @@ impl core::marker::Sync for aya_obj::btf::BtfError
impl core::marker::Unpin for aya_obj::btf::BtfError
impl !core::panic::unwind_safe::RefUnwindSafe for aya_obj::btf::BtfError
impl !core::panic::unwind_safe::UnwindSafe for aya_obj::btf::BtfError
impl<E> core::any::Provider for aya_obj::btf::BtfError where E: core::error::Error + core::marker::Sized
pub fn aya_obj::btf::BtfError::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya_obj::btf::BtfError where U: core::convert::From<T>
pub fn aya_obj::btf::BtfError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya_obj::btf::BtfError where U: core::convert::Into<T>
@ -384,6 +382,8 @@ pub fn aya_obj::btf::Btf::from(t: T) -> T
#[repr(C)] pub struct aya_obj::btf::BtfEnum
pub aya_obj::btf::BtfEnum::name_offset: u32
pub aya_obj::btf::BtfEnum::value: u32
impl aya_obj::btf::BtfEnum
pub fn aya_obj::btf::BtfEnum::new(name_offset: u32, value: u32) -> Self
impl core::clone::Clone for aya_obj::btf::BtfEnum
pub fn aya_obj::btf::BtfEnum::clone(&self) -> aya_obj::btf::BtfEnum
impl core::fmt::Debug for aya_obj::btf::BtfEnum
@ -414,6 +414,8 @@ pub fn aya_obj::btf::BtfEnum::borrow_mut(&mut self) -> &mut T
impl<T> core::convert::From<T> for aya_obj::btf::BtfEnum
pub fn aya_obj::btf::BtfEnum::from(t: T) -> T
#[repr(C)] pub struct aya_obj::btf::BtfEnum64
impl aya_obj::btf::BtfEnum64
pub fn aya_obj::btf::BtfEnum64::new(name_offset: u32, value: u64) -> Self
impl core::clone::Clone for aya_obj::btf::BtfEnum64
pub fn aya_obj::btf::BtfEnum64::clone(&self) -> aya_obj::btf::BtfEnum64
impl core::fmt::Debug for aya_obj::btf::BtfEnum64
@ -477,6 +479,7 @@ pub struct aya_obj::btf::BtfFeatures
impl aya_obj::btf::BtfFeatures
pub fn aya_obj::btf::BtfFeatures::btf_datasec(&self) -> bool
pub fn aya_obj::btf::BtfFeatures::btf_decl_tag(&self) -> bool
pub fn aya_obj::btf::BtfFeatures::btf_enum64(&self) -> bool
pub fn aya_obj::btf::BtfFeatures::btf_float(&self) -> bool
pub fn aya_obj::btf::BtfFeatures::btf_func(&self) -> bool
pub fn aya_obj::btf::BtfFeatures::btf_func_global(&self) -> bool
@ -552,8 +555,6 @@ impl core::marker::Sync for aya_obj::btf::BtfRelocationError
impl core::marker::Unpin for aya_obj::btf::BtfRelocationError
impl !core::panic::unwind_safe::RefUnwindSafe for aya_obj::btf::BtfRelocationError
impl !core::panic::unwind_safe::UnwindSafe for aya_obj::btf::BtfRelocationError
impl<E> core::any::Provider for aya_obj::btf::BtfRelocationError where E: core::error::Error + core::marker::Sized
pub fn aya_obj::btf::BtfRelocationError::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya_obj::btf::BtfRelocationError where U: core::convert::From<T>
pub fn aya_obj::btf::BtfRelocationError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya_obj::btf::BtfRelocationError where U: core::convert::Into<T>
@ -701,7 +702,7 @@ impl<T> core::convert::From<T> for aya_obj::btf::DeclTag
pub fn aya_obj::btf::DeclTag::from(t: T) -> T
#[repr(C)] pub struct aya_obj::btf::Enum
impl aya_obj::btf::Enum
pub fn aya_obj::btf::Enum::new(name_offset: u32, variants: alloc::vec::Vec<aya_obj::btf::BtfEnum>) -> Self
pub fn aya_obj::btf::Enum::new(name_offset: u32, signed: bool, variants: alloc::vec::Vec<aya_obj::btf::BtfEnum>) -> Self
impl core::clone::Clone for aya_obj::btf::Enum
pub fn aya_obj::btf::Enum::clone(&self) -> aya_obj::btf::Enum
impl core::fmt::Debug for aya_obj::btf::Enum
@ -732,6 +733,8 @@ pub fn aya_obj::btf::Enum::borrow_mut(&mut self) -> &mut T
impl<T> core::convert::From<T> for aya_obj::btf::Enum
pub fn aya_obj::btf::Enum::from(t: T) -> T
#[repr(C)] pub struct aya_obj::btf::Enum64
impl aya_obj::btf::Enum64
pub fn aya_obj::btf::Enum64::new(name_offset: u32, signed: bool, variants: alloc::vec::Vec<aya_obj::btf::BtfEnum64>) -> Self
impl core::clone::Clone for aya_obj::btf::Enum64
pub fn aya_obj::btf::Enum64::clone(&self) -> aya_obj::btf::Enum64
impl core::fmt::Debug for aya_obj::btf::Enum64
@ -5452,8 +5455,6 @@ impl core::marker::Sync for aya_obj::maps::PinningError
impl core::marker::Unpin for aya_obj::maps::PinningError
impl core::panic::unwind_safe::RefUnwindSafe for aya_obj::maps::PinningError
impl core::panic::unwind_safe::UnwindSafe for aya_obj::maps::PinningError
impl<E> core::any::Provider for aya_obj::maps::PinningError where E: core::error::Error + core::marker::Sized
pub fn aya_obj::maps::PinningError::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya_obj::maps::PinningError where U: core::convert::From<T>
pub fn aya_obj::maps::PinningError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya_obj::maps::PinningError where U: core::convert::Into<T>
@ -5791,8 +5792,6 @@ impl core::marker::Sync for aya_obj::ParseError
impl core::marker::Unpin for aya_obj::ParseError
impl !core::panic::unwind_safe::RefUnwindSafe for aya_obj::ParseError
impl !core::panic::unwind_safe::UnwindSafe for aya_obj::ParseError
impl<E> core::any::Provider for aya_obj::ParseError where E: core::error::Error + core::marker::Sized
pub fn aya_obj::ParseError::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya_obj::ParseError where U: core::convert::From<T>
pub fn aya_obj::ParseError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya_obj::ParseError where U: core::convert::Into<T>
@ -5973,7 +5972,7 @@ impl aya_obj::Object
pub fn aya_obj::Object::relocate_btf(&mut self, target_btf: &aya_obj::btf::Btf) -> core::result::Result<(), aya_obj::btf::BtfRelocationError>
impl aya_obj::Object
pub fn aya_obj::Object::relocate_calls(&mut self, text_sections: &std::collections::hash::set::HashSet<usize>) -> core::result::Result<(), aya_obj::relocation::BpfRelocationError>
pub fn aya_obj::Object::relocate_maps<'a, I: core::iter::traits::iterator::Iterator<Item = (&'a str, core::option::Option<i32>, &'a aya_obj::maps::Map)>>(&mut self, maps: I, text_sections: &std::collections::hash::set::HashSet<usize>) -> core::result::Result<(), aya_obj::relocation::BpfRelocationError>
pub fn aya_obj::Object::relocate_maps<'a, I: core::iter::traits::iterator::Iterator<Item = (&'a str, i32, &'a aya_obj::maps::Map)>>(&mut self, maps: I, text_sections: &std::collections::hash::set::HashSet<usize>) -> core::result::Result<(), aya_obj::relocation::BpfRelocationError>
impl core::clone::Clone for aya_obj::Object
pub fn aya_obj::Object::clone(&self) -> aya_obj::Object
impl core::fmt::Debug for aya_obj::Object
@ -6289,9 +6288,6 @@ pub enum aya_obj::relocation::RelocationError
pub aya_obj::relocation::RelocationError::InvalidRelocationOffset
pub aya_obj::relocation::RelocationError::InvalidRelocationOffset::offset: u64
pub aya_obj::relocation::RelocationError::InvalidRelocationOffset::relocation_number: usize
pub aya_obj::relocation::RelocationError::MapNotCreated
pub aya_obj::relocation::RelocationError::MapNotCreated::name: alloc::string::String
pub aya_obj::relocation::RelocationError::MapNotCreated::section_index: usize
pub aya_obj::relocation::RelocationError::SectionNotFound
pub aya_obj::relocation::RelocationError::SectionNotFound::section_index: usize
pub aya_obj::relocation::RelocationError::SectionNotFound::symbol_index: usize
@ -6314,8 +6310,6 @@ impl core::marker::Sync for aya_obj::relocation::RelocationError
impl core::marker::Unpin for aya_obj::relocation::RelocationError
impl core::panic::unwind_safe::RefUnwindSafe for aya_obj::relocation::RelocationError
impl core::panic::unwind_safe::UnwindSafe for aya_obj::relocation::RelocationError
impl<E> core::any::Provider for aya_obj::relocation::RelocationError where E: core::error::Error + core::marker::Sized
pub fn aya_obj::relocation::RelocationError::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya_obj::relocation::RelocationError where U: core::convert::From<T>
pub fn aya_obj::relocation::RelocationError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya_obj::relocation::RelocationError where U: core::convert::Into<T>
@ -6346,8 +6340,6 @@ impl core::marker::Sync for aya_obj::relocation::BpfRelocationError
impl core::marker::Unpin for aya_obj::relocation::BpfRelocationError
impl core::panic::unwind_safe::RefUnwindSafe for aya_obj::relocation::BpfRelocationError
impl core::panic::unwind_safe::UnwindSafe for aya_obj::relocation::BpfRelocationError
impl<E> core::any::Provider for aya_obj::relocation::BpfRelocationError where E: core::error::Error + core::marker::Sized
pub fn aya_obj::relocation::BpfRelocationError::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya_obj::relocation::BpfRelocationError where U: core::convert::From<T>
pub fn aya_obj::relocation::BpfRelocationError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya_obj::relocation::BpfRelocationError where U: core::convert::Into<T>
@ -6512,8 +6504,6 @@ impl core::marker::Sync for aya_obj::ParseError
impl core::marker::Unpin for aya_obj::ParseError
impl !core::panic::unwind_safe::RefUnwindSafe for aya_obj::ParseError
impl !core::panic::unwind_safe::UnwindSafe for aya_obj::ParseError
impl<E> core::any::Provider for aya_obj::ParseError where E: core::error::Error + core::marker::Sized
pub fn aya_obj::ParseError::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya_obj::ParseError where U: core::convert::From<T>
pub fn aya_obj::ParseError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya_obj::ParseError where U: core::convert::Into<T>
@ -6694,7 +6684,7 @@ impl aya_obj::Object
pub fn aya_obj::Object::relocate_btf(&mut self, target_btf: &aya_obj::btf::Btf) -> core::result::Result<(), aya_obj::btf::BtfRelocationError>
impl aya_obj::Object
pub fn aya_obj::Object::relocate_calls(&mut self, text_sections: &std::collections::hash::set::HashSet<usize>) -> core::result::Result<(), aya_obj::relocation::BpfRelocationError>
pub fn aya_obj::Object::relocate_maps<'a, I: core::iter::traits::iterator::Iterator<Item = (&'a str, core::option::Option<i32>, &'a aya_obj::maps::Map)>>(&mut self, maps: I, text_sections: &std::collections::hash::set::HashSet<usize>) -> core::result::Result<(), aya_obj::relocation::BpfRelocationError>
pub fn aya_obj::Object::relocate_maps<'a, I: core::iter::traits::iterator::Iterator<Item = (&'a str, i32, &'a aya_obj::maps::Map)>>(&mut self, maps: I, text_sections: &std::collections::hash::set::HashSet<usize>) -> core::result::Result<(), aya_obj::relocation::BpfRelocationError>
impl core::clone::Clone for aya_obj::Object
pub fn aya_obj::Object::clone(&self) -> aya_obj::Object
impl core::fmt::Debug for aya_obj::Object

@ -25,8 +25,6 @@ impl core::marker::Sync for aya_tool::generate::Error
impl core::marker::Unpin for aya_tool::generate::Error
impl !core::panic::unwind_safe::RefUnwindSafe for aya_tool::generate::Error
impl !core::panic::unwind_safe::UnwindSafe for aya_tool::generate::Error
impl<E> core::any::Provider for aya_tool::generate::Error where E: core::error::Error + core::marker::Sized
pub fn aya_tool::generate::Error::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya_tool::generate::Error where U: core::convert::From<T>
pub fn aya_tool::generate::Error::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya_tool::generate::Error where U: core::convert::Into<T>

@ -91,7 +91,7 @@ impl<T: core::borrow::Borrow<aya::maps::MapData>> aya::maps::ProgramArray<T>
pub fn aya::maps::ProgramArray<T>::indices(&self) -> aya::maps::MapKeys<'_, u32>
impl<T: core::borrow::BorrowMut<aya::maps::MapData>> aya::maps::ProgramArray<T>
pub fn aya::maps::ProgramArray<T>::clear_index(&mut self, index: &u32) -> core::result::Result<(), aya::maps::MapError>
pub fn aya::maps::ProgramArray<T>::set(&mut self, index: u32, program: aya::programs::ProgramFd, flags: u64) -> core::result::Result<(), aya::maps::MapError>
pub fn aya::maps::ProgramArray<T>::set(&mut self, index: u32, program: &aya::programs::ProgramFd, flags: u64) -> core::result::Result<(), aya::maps::MapError>
impl core::convert::TryFrom<aya::maps::Map> for aya::maps::ProgramArray<aya::maps::MapData>
pub type aya::maps::ProgramArray<aya::maps::MapData>::Error = aya::maps::MapError
pub fn aya::maps::ProgramArray<aya::maps::MapData>::try_from(map: aya::maps::Map) -> core::result::Result<aya::maps::ProgramArray<aya::maps::MapData>, aya::maps::MapError>
@ -126,7 +126,8 @@ pub mod aya::maps::bloom_filter
pub struct aya::maps::bloom_filter::BloomFilter<T, V: aya::Pod>
impl<T: core::borrow::Borrow<aya::maps::MapData>, V: aya::Pod> aya::maps::bloom_filter::BloomFilter<T, V>
pub fn aya::maps::bloom_filter::BloomFilter<T, V>::contains(&self, value: &V, flags: u64) -> core::result::Result<(), aya::maps::MapError>
pub fn aya::maps::bloom_filter::BloomFilter<T, V>::insert(&self, value: impl core::borrow::Borrow<V>, flags: u64) -> core::result::Result<(), aya::maps::MapError>
impl<T: core::borrow::BorrowMut<aya::maps::MapData>, V: aya::Pod> aya::maps::bloom_filter::BloomFilter<T, V>
pub fn aya::maps::bloom_filter::BloomFilter<T, V>::insert(&mut self, value: impl core::borrow::Borrow<V>, flags: u64) -> core::result::Result<(), aya::maps::MapError>
impl<'a, V: aya::Pod> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::bloom_filter::BloomFilter<&'a aya::maps::MapData, V>
pub type aya::maps::bloom_filter::BloomFilter<&'a aya::maps::MapData, V>::Error = aya::maps::MapError
pub fn aya::maps::bloom_filter::BloomFilter<&'a aya::maps::MapData, V>::try_from(map: &'a aya::maps::Map) -> core::result::Result<aya::maps::bloom_filter::BloomFilter<&'a aya::maps::MapData, V>, aya::maps::MapError>
@ -352,8 +353,6 @@ impl core::marker::Sync for aya::maps::perf::PerfBufferError
impl core::marker::Unpin for aya::maps::perf::PerfBufferError
impl !core::panic::unwind_safe::RefUnwindSafe for aya::maps::perf::PerfBufferError
impl !core::panic::unwind_safe::UnwindSafe for aya::maps::perf::PerfBufferError
impl<E> core::any::Provider for aya::maps::perf::PerfBufferError where E: core::error::Error + core::marker::Sized
pub fn aya::maps::perf::PerfBufferError::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya::maps::perf::PerfBufferError where U: core::convert::From<T>
pub fn aya::maps::perf::PerfBufferError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::maps::perf::PerfBufferError where U: core::convert::Into<T>
@ -708,7 +707,6 @@ pub fn aya::maps::stack::Stack<T, V>::from(t: T) -> T
pub mod aya::maps::stack_trace
pub struct aya::maps::stack_trace::StackFrame
pub aya::maps::stack_trace::StackFrame::ip: u64
pub aya::maps::stack_trace::StackFrame::symbol_name: core::option::Option<alloc::string::String>
impl core::marker::Send for aya::maps::stack_trace::StackFrame
impl core::marker::Sync for aya::maps::stack_trace::StackFrame
impl core::marker::Unpin for aya::maps::stack_trace::StackFrame
@ -734,7 +732,6 @@ pub struct aya::maps::stack_trace::StackTrace
pub aya::maps::stack_trace::StackTrace::id: u32
impl aya::maps::stack_trace::StackTrace
pub fn aya::maps::stack_trace::StackTrace::frames(&self) -> &[aya::maps::stack_trace::StackFrame]
pub fn aya::maps::stack_trace::StackTrace::resolve<R: aya::maps::stack_trace::SymbolResolver>(&mut self, symbols: &R) -> &aya::maps::stack_trace::StackTrace
impl<T: core::borrow::Borrow<aya::maps::MapData>> aya::maps::IterableMap<u32, aya::maps::stack_trace::StackTrace> for aya::maps::stack_trace::StackTraceMap<T>
pub fn aya::maps::stack_trace::StackTraceMap<T>::get(&self, index: &u32) -> core::result::Result<aya::maps::stack_trace::StackTrace, aya::maps::MapError>
pub fn aya::maps::stack_trace::StackTraceMap<T>::map(&self) -> &aya::maps::MapData
@ -803,10 +800,6 @@ impl<T> core::borrow::BorrowMut<T> for aya::maps::stack_trace::StackTraceMap<T>
pub fn aya::maps::stack_trace::StackTraceMap<T>::borrow_mut(&mut self) -> &mut T
impl<T> core::convert::From<T> for aya::maps::stack_trace::StackTraceMap<T>
pub fn aya::maps::stack_trace::StackTraceMap<T>::from(t: T) -> T
pub trait aya::maps::stack_trace::SymbolResolver
pub fn aya::maps::stack_trace::SymbolResolver::resolve_symbol(&self, addr: u64) -> core::option::Option<alloc::borrow::Cow<'_, str>>
impl aya::maps::stack_trace::SymbolResolver for aya::util::SimpleSymbolResolver
pub fn aya::util::SimpleSymbolResolver::resolve_symbol(&self, addr: u64) -> core::option::Option<alloc::borrow::Cow<'_, str>>
pub enum aya::maps::Map
pub aya::maps::Map::Array(aya::maps::MapData)
pub aya::maps::Map::BloomFilter(aya::maps::MapData)
@ -974,8 +967,6 @@ pub fn aya::maps::Map::borrow_mut(&mut self) -> &mut T
impl<T> core::convert::From<T> for aya::maps::Map
pub fn aya::maps::Map::from(t: T) -> T
pub enum aya::maps::MapError
pub aya::maps::MapError::AlreadyCreated
pub aya::maps::MapError::AlreadyCreated::name: alloc::string::String
pub aya::maps::MapError::CreateError
pub aya::maps::MapError::CreateError::code: libc::unix::linux_like::linux::gnu::b64::x86_64::not_x32::c_long
pub aya::maps::MapError::CreateError::io_error: std::io::error::Error
@ -992,7 +983,6 @@ pub aya::maps::MapError::InvalidValueSize
pub aya::maps::MapError::InvalidValueSize::expected: usize
pub aya::maps::MapError::InvalidValueSize::size: usize
pub aya::maps::MapError::KeyNotFound
pub aya::maps::MapError::NotCreated
pub aya::maps::MapError::OutOfBounds
pub aya::maps::MapError::OutOfBounds::index: u32
pub aya::maps::MapError::OutOfBounds::max_entries: u32
@ -1018,8 +1008,6 @@ impl core::marker::Sync for aya::maps::MapError
impl core::marker::Unpin for aya::maps::MapError
impl !core::panic::unwind_safe::RefUnwindSafe for aya::maps::MapError
impl !core::panic::unwind_safe::UnwindSafe for aya::maps::MapError
impl<E> core::any::Provider for aya::maps::MapError where E: core::error::Error + core::marker::Sized
pub fn aya::maps::MapError::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya::maps::MapError where U: core::convert::From<T>
pub fn aya::maps::MapError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::maps::MapError where U: core::convert::Into<T>
@ -1114,7 +1102,8 @@ pub fn aya::maps::perf::AsyncPerfEventArray<T>::from(t: T) -> T
pub struct aya::maps::BloomFilter<T, V: aya::Pod>
impl<T: core::borrow::Borrow<aya::maps::MapData>, V: aya::Pod> aya::maps::bloom_filter::BloomFilter<T, V>
pub fn aya::maps::bloom_filter::BloomFilter<T, V>::contains(&self, value: &V, flags: u64) -> core::result::Result<(), aya::maps::MapError>
pub fn aya::maps::bloom_filter::BloomFilter<T, V>::insert(&self, value: impl core::borrow::Borrow<V>, flags: u64) -> core::result::Result<(), aya::maps::MapError>
impl<T: core::borrow::BorrowMut<aya::maps::MapData>, V: aya::Pod> aya::maps::bloom_filter::BloomFilter<T, V>
pub fn aya::maps::bloom_filter::BloomFilter<T, V>::insert(&mut self, value: impl core::borrow::Borrow<V>, flags: u64) -> core::result::Result<(), aya::maps::MapError>
impl<'a, V: aya::Pod> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::bloom_filter::BloomFilter<&'a aya::maps::MapData, V>
pub type aya::maps::bloom_filter::BloomFilter<&'a aya::maps::MapData, V>::Error = aya::maps::MapError
pub fn aya::maps::bloom_filter::BloomFilter<&'a aya::maps::MapData, V>::try_from(map: &'a aya::maps::Map) -> core::result::Result<aya::maps::bloom_filter::BloomFilter<&'a aya::maps::MapData, V>, aya::maps::MapError>
@ -1236,12 +1225,12 @@ pub fn aya::maps::lpm_trie::LpmTrie<T, K, V>::from(t: T) -> T
pub struct aya::maps::MapData
pub aya::maps::MapData::pinned: bool
impl aya::maps::MapData
pub fn aya::maps::MapData::create(&mut self, name: &str) -> core::result::Result<std::os::fd::raw::RawFd, aya::maps::MapError>
pub fn aya::maps::MapData::fd(&self) -> core::option::Option<aya::maps::MapFd>
pub fn aya::maps::MapData::create(obj: aya_obj::maps::Map, name: &str, btf_fd: core::option::Option<std::os::fd::owned::BorrowedFd<'_>>) -> core::result::Result<Self, aya::maps::MapError>
pub fn aya::maps::MapData::fd(&self) -> aya::maps::MapFd
pub fn aya::maps::MapData::from_fd(fd: std::os::fd::owned::OwnedFd) -> core::result::Result<aya::maps::MapData, aya::maps::MapError>
pub fn aya::maps::MapData::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<aya::maps::MapData, aya::maps::MapError>
impl core::clone::Clone for aya::maps::MapData
pub fn aya::maps::MapData::clone(&self) -> aya::maps::MapData
pub fn aya::maps::MapData::clone(&self) -> Self
impl core::ops::drop::Drop for aya::maps::MapData
pub fn aya::maps::MapData::drop(&mut self)
impl core::fmt::Debug for aya::maps::MapData
@ -1508,7 +1497,7 @@ impl<T: core::borrow::Borrow<aya::maps::MapData>> aya::maps::ProgramArray<T>
pub fn aya::maps::ProgramArray<T>::indices(&self) -> aya::maps::MapKeys<'_, u32>
impl<T: core::borrow::BorrowMut<aya::maps::MapData>> aya::maps::ProgramArray<T>
pub fn aya::maps::ProgramArray<T>::clear_index(&mut self, index: &u32) -> core::result::Result<(), aya::maps::MapError>
pub fn aya::maps::ProgramArray<T>::set(&mut self, index: u32, program: aya::programs::ProgramFd, flags: u64) -> core::result::Result<(), aya::maps::MapError>
pub fn aya::maps::ProgramArray<T>::set(&mut self, index: u32, program: &aya::programs::ProgramFd, flags: u64) -> core::result::Result<(), aya::maps::MapError>
impl core::convert::TryFrom<aya::maps::Map> for aya::maps::ProgramArray<aya::maps::MapData>
pub type aya::maps::ProgramArray<aya::maps::MapData>::Error = aya::maps::MapError
pub fn aya::maps::ProgramArray<aya::maps::MapData>::try_from(map: aya::maps::Map) -> core::result::Result<aya::maps::ProgramArray<aya::maps::MapData>, aya::maps::MapError>
@ -1778,8 +1767,6 @@ impl core::marker::Sync for aya::pin::PinError
impl core::marker::Unpin for aya::pin::PinError
impl !core::panic::unwind_safe::RefUnwindSafe for aya::pin::PinError
impl !core::panic::unwind_safe::UnwindSafe for aya::pin::PinError
impl<E> core::any::Provider for aya::pin::PinError where E: core::error::Error + core::marker::Sized
pub fn aya::pin::PinError::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya::pin::PinError where U: core::convert::From<T>
pub fn aya::pin::PinError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::pin::PinError where U: core::convert::Into<T>
@ -1810,13 +1797,15 @@ pub fn aya::programs::cgroup_device::CgroupDevice::detach(&mut self, link_id: ay
pub fn aya::programs::cgroup_device::CgroupDevice::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::cgroup_device::CgroupDevice::take_link(&mut self, link_id: aya::programs::cgroup_device::CgroupDeviceLinkId) -> core::result::Result<aya::programs::cgroup_device::CgroupDeviceLink, aya::programs::ProgramError>
impl aya::programs::cgroup_device::CgroupDevice
pub fn aya::programs::cgroup_device::CgroupDevice::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::cgroup_device::CgroupDevice::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::cgroup_device::CgroupDevice
pub fn aya::programs::cgroup_device::CgroupDevice::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::cgroup_device::CgroupDevice
pub fn aya::programs::cgroup_device::CgroupDevice::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::cgroup_device::CgroupDevice::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::cgroup_device::CgroupDevice
pub fn aya::programs::cgroup_device::CgroupDevice::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::cgroup_device::CgroupDevice
pub fn aya::programs::cgroup_device::CgroupDevice::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::cgroup_device::CgroupDevice
pub fn aya::programs::cgroup_device::CgroupDevice::drop(&mut self)
@ -1953,11 +1942,13 @@ pub fn aya::programs::cgroup_skb::CgroupSkb::from_pin<P: core::convert::AsRef<st
pub fn aya::programs::cgroup_skb::CgroupSkb::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::cgroup_skb::CgroupSkb::take_link(&mut self, link_id: aya::programs::cgroup_skb::CgroupSkbLinkId) -> core::result::Result<aya::programs::cgroup_skb::CgroupSkbLink, aya::programs::ProgramError>
impl aya::programs::cgroup_skb::CgroupSkb
pub fn aya::programs::cgroup_skb::CgroupSkb::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::cgroup_skb::CgroupSkb::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::cgroup_skb::CgroupSkb
pub fn aya::programs::cgroup_skb::CgroupSkb::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::cgroup_skb::CgroupSkb::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::cgroup_skb::CgroupSkb
pub fn aya::programs::cgroup_skb::CgroupSkb::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::cgroup_skb::CgroupSkb
pub fn aya::programs::cgroup_skb::CgroupSkb::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::cgroup_skb::CgroupSkb
pub fn aya::programs::cgroup_skb::CgroupSkb::drop(&mut self)
@ -2061,11 +2052,13 @@ pub fn aya::programs::cgroup_sock::CgroupSock::from_pin<P: core::convert::AsRef<
pub fn aya::programs::cgroup_sock::CgroupSock::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::cgroup_sock::CgroupSock::take_link(&mut self, link_id: aya::programs::cgroup_sock::CgroupSockLinkId) -> core::result::Result<aya::programs::cgroup_sock::CgroupSockLink, aya::programs::ProgramError>
impl aya::programs::cgroup_sock::CgroupSock
pub fn aya::programs::cgroup_sock::CgroupSock::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::cgroup_sock::CgroupSock::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::cgroup_sock::CgroupSock
pub fn aya::programs::cgroup_sock::CgroupSock::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::cgroup_sock::CgroupSock::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::cgroup_sock::CgroupSock
pub fn aya::programs::cgroup_sock::CgroupSock::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::cgroup_sock::CgroupSock
pub fn aya::programs::cgroup_sock::CgroupSock::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::cgroup_sock::CgroupSock
pub fn aya::programs::cgroup_sock::CgroupSock::drop(&mut self)
@ -2169,11 +2162,13 @@ pub fn aya::programs::cgroup_sock_addr::CgroupSockAddr::from_pin<P: core::conver
pub fn aya::programs::cgroup_sock_addr::CgroupSockAddr::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::cgroup_sock_addr::CgroupSockAddr::take_link(&mut self, link_id: aya::programs::cgroup_sock_addr::CgroupSockAddrLinkId) -> core::result::Result<aya::programs::cgroup_sock_addr::CgroupSockAddrLink, aya::programs::ProgramError>
impl aya::programs::cgroup_sock_addr::CgroupSockAddr
pub fn aya::programs::cgroup_sock_addr::CgroupSockAddr::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::cgroup_sock_addr::CgroupSockAddr::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::cgroup_sock_addr::CgroupSockAddr
pub fn aya::programs::cgroup_sock_addr::CgroupSockAddr::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::cgroup_sock_addr::CgroupSockAddr::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::cgroup_sock_addr::CgroupSockAddr
pub fn aya::programs::cgroup_sock_addr::CgroupSockAddr::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::cgroup_sock_addr::CgroupSockAddr
pub fn aya::programs::cgroup_sock_addr::CgroupSockAddr::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::cgroup_sock_addr::CgroupSockAddr
pub fn aya::programs::cgroup_sock_addr::CgroupSockAddr::drop(&mut self)
@ -2277,11 +2272,13 @@ pub fn aya::programs::cgroup_sockopt::CgroupSockopt::from_pin<P: core::convert::
pub fn aya::programs::cgroup_sockopt::CgroupSockopt::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::cgroup_sockopt::CgroupSockopt::take_link(&mut self, link_id: aya::programs::cgroup_sockopt::CgroupSockoptLinkId) -> core::result::Result<aya::programs::cgroup_sockopt::CgroupSockoptLink, aya::programs::ProgramError>
impl aya::programs::cgroup_sockopt::CgroupSockopt
pub fn aya::programs::cgroup_sockopt::CgroupSockopt::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::cgroup_sockopt::CgroupSockopt::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::cgroup_sockopt::CgroupSockopt
pub fn aya::programs::cgroup_sockopt::CgroupSockopt::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::cgroup_sockopt::CgroupSockopt::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::cgroup_sockopt::CgroupSockopt
pub fn aya::programs::cgroup_sockopt::CgroupSockopt::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::cgroup_sockopt::CgroupSockopt
pub fn aya::programs::cgroup_sockopt::CgroupSockopt::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::cgroup_sockopt::CgroupSockopt
pub fn aya::programs::cgroup_sockopt::CgroupSockopt::drop(&mut self)
@ -2383,13 +2380,15 @@ pub fn aya::programs::cgroup_sysctl::CgroupSysctl::detach(&mut self, link_id: ay
pub fn aya::programs::cgroup_sysctl::CgroupSysctl::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::cgroup_sysctl::CgroupSysctl::take_link(&mut self, link_id: aya::programs::cgroup_sysctl::CgroupSysctlLinkId) -> core::result::Result<aya::programs::cgroup_sysctl::CgroupSysctlLink, aya::programs::ProgramError>
impl aya::programs::cgroup_sysctl::CgroupSysctl
pub fn aya::programs::cgroup_sysctl::CgroupSysctl::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::cgroup_sysctl::CgroupSysctl::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::cgroup_sysctl::CgroupSysctl
pub fn aya::programs::cgroup_sysctl::CgroupSysctl::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::cgroup_sysctl::CgroupSysctl
pub fn aya::programs::cgroup_sysctl::CgroupSysctl::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::cgroup_sysctl::CgroupSysctl::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::cgroup_sysctl::CgroupSysctl
pub fn aya::programs::cgroup_sysctl::CgroupSysctl::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::cgroup_sysctl::CgroupSysctl
pub fn aya::programs::cgroup_sysctl::CgroupSysctl::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::cgroup_sysctl::CgroupSysctl
pub fn aya::programs::cgroup_sysctl::CgroupSysctl::drop(&mut self)
@ -2498,8 +2497,6 @@ impl core::marker::Sync for aya::programs::extension::ExtensionError
impl core::marker::Unpin for aya::programs::extension::ExtensionError
impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::extension::ExtensionError
impl core::panic::unwind_safe::UnwindSafe for aya::programs::extension::ExtensionError
impl<E> core::any::Provider for aya::programs::extension::ExtensionError where E: core::error::Error + core::marker::Sized
pub fn aya::programs::extension::ExtensionError::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya::programs::extension::ExtensionError where U: core::convert::From<T>
pub fn aya::programs::extension::ExtensionError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::programs::extension::ExtensionError where U: core::convert::Into<T>
@ -2521,18 +2518,20 @@ pub fn aya::programs::extension::ExtensionError::from(t: T) -> T
pub struct aya::programs::extension::Extension
impl aya::programs::extension::Extension
pub fn aya::programs::extension::Extension::attach(&mut self) -> core::result::Result<aya::programs::extension::ExtensionLinkId, aya::programs::ProgramError>
pub fn aya::programs::extension::Extension::attach_to_program(&mut self, program: aya::programs::ProgramFd, func_name: &str) -> core::result::Result<aya::programs::extension::ExtensionLinkId, aya::programs::ProgramError>
pub fn aya::programs::extension::Extension::attach_to_program(&mut self, program: &aya::programs::ProgramFd, func_name: &str) -> core::result::Result<aya::programs::extension::ExtensionLinkId, aya::programs::ProgramError>
pub fn aya::programs::extension::Extension::detach(&mut self, link_id: aya::programs::extension::ExtensionLinkId) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::extension::Extension::load(&mut self, program: aya::programs::ProgramFd, func_name: &str) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::extension::Extension::take_link(&mut self, link_id: aya::programs::extension::ExtensionLinkId) -> core::result::Result<aya::programs::extension::ExtensionLink, aya::programs::ProgramError>
impl aya::programs::extension::Extension
pub fn aya::programs::extension::Extension::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::extension::Extension::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::extension::Extension
pub fn aya::programs::extension::Extension::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::extension::Extension
pub fn aya::programs::extension::Extension::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::extension::Extension::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::extension::Extension
pub fn aya::programs::extension::Extension::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::extension::Extension
pub fn aya::programs::extension::Extension::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::extension::Extension
pub fn aya::programs::extension::Extension::drop(&mut self)
@ -2638,13 +2637,15 @@ pub fn aya::programs::fentry::FEntry::detach(&mut self, link_id: aya::programs::
pub fn aya::programs::fentry::FEntry::load(&mut self, fn_name: &str, btf: &aya_obj::btf::btf::Btf) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::fentry::FEntry::take_link(&mut self, link_id: aya::programs::fentry::FEntryLinkId) -> core::result::Result<aya::programs::fentry::FEntryLink, aya::programs::ProgramError>
impl aya::programs::fentry::FEntry
pub fn aya::programs::fentry::FEntry::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::fentry::FEntry::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::fentry::FEntry
pub fn aya::programs::fentry::FEntry::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::fentry::FEntry
pub fn aya::programs::fentry::FEntry::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::fentry::FEntry::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::fentry::FEntry
pub fn aya::programs::fentry::FEntry::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::fentry::FEntry
pub fn aya::programs::fentry::FEntry::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::fentry::FEntry
pub fn aya::programs::fentry::FEntry::drop(&mut self)
@ -2750,13 +2751,15 @@ pub fn aya::programs::fexit::FExit::detach(&mut self, link_id: aya::programs::fe
pub fn aya::programs::fexit::FExit::load(&mut self, fn_name: &str, btf: &aya_obj::btf::btf::Btf) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::fexit::FExit::take_link(&mut self, link_id: aya::programs::fexit::FExitLinkId) -> core::result::Result<aya::programs::fexit::FExitLink, aya::programs::ProgramError>
impl aya::programs::fexit::FExit
pub fn aya::programs::fexit::FExit::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::fexit::FExit::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::fexit::FExit
pub fn aya::programs::fexit::FExit::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::fexit::FExit
pub fn aya::programs::fexit::FExit::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::fexit::FExit::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::fexit::FExit
pub fn aya::programs::fexit::FExit::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::fexit::FExit
pub fn aya::programs::fexit::FExit::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::fexit::FExit
pub fn aya::programs::fexit::FExit::drop(&mut self)
@ -2872,8 +2875,6 @@ impl core::marker::Sync for aya::programs::kprobe::KProbeError
impl core::marker::Unpin for aya::programs::kprobe::KProbeError
impl !core::panic::unwind_safe::RefUnwindSafe for aya::programs::kprobe::KProbeError
impl !core::panic::unwind_safe::UnwindSafe for aya::programs::kprobe::KProbeError
impl<E> core::any::Provider for aya::programs::kprobe::KProbeError where E: core::error::Error + core::marker::Sized
pub fn aya::programs::kprobe::KProbeError::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya::programs::kprobe::KProbeError where U: core::convert::From<T>
pub fn aya::programs::kprobe::KProbeError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::programs::kprobe::KProbeError where U: core::convert::Into<T>
@ -2901,11 +2902,13 @@ pub fn aya::programs::kprobe::KProbe::kind(&self) -> aya::programs::ProbeKind
pub fn aya::programs::kprobe::KProbe::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::kprobe::KProbe::take_link(&mut self, link_id: aya::programs::kprobe::KProbeLinkId) -> core::result::Result<aya::programs::kprobe::KProbeLink, aya::programs::ProgramError>
impl aya::programs::kprobe::KProbe
pub fn aya::programs::kprobe::KProbe::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::kprobe::KProbe::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::kprobe::KProbe
pub fn aya::programs::kprobe::KProbe::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::kprobe::KProbe::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::kprobe::KProbe
pub fn aya::programs::kprobe::KProbe::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::kprobe::KProbe
pub fn aya::programs::kprobe::KProbe::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::kprobe::KProbe
pub fn aya::programs::kprobe::KProbe::drop(&mut self)
@ -3020,8 +3023,6 @@ impl core::marker::Sync for aya::programs::links::LinkError
impl core::marker::Unpin for aya::programs::links::LinkError
impl !core::panic::unwind_safe::RefUnwindSafe for aya::programs::links::LinkError
impl !core::panic::unwind_safe::UnwindSafe for aya::programs::links::LinkError
impl<E> core::any::Provider for aya::programs::links::LinkError where E: core::error::Error + core::marker::Sized
pub fn aya::programs::links::LinkError::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya::programs::links::LinkError where U: core::convert::From<T>
pub fn aya::programs::links::LinkError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::programs::links::LinkError where U: core::convert::Into<T>
@ -3399,13 +3400,15 @@ pub fn aya::programs::lirc_mode2::LircMode2::load(&mut self) -> core::result::Re
pub fn aya::programs::lirc_mode2::LircMode2::query<T: std::os::fd::raw::AsRawFd>(target_fd: T) -> core::result::Result<alloc::vec::Vec<aya::programs::lirc_mode2::LircLink>, aya::programs::ProgramError>
pub fn aya::programs::lirc_mode2::LircMode2::take_link(&mut self, link_id: aya::programs::lirc_mode2::LircLinkId) -> core::result::Result<aya::programs::lirc_mode2::LircLink, aya::programs::ProgramError>
impl aya::programs::lirc_mode2::LircMode2
pub fn aya::programs::lirc_mode2::LircMode2::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::lirc_mode2::LircMode2::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::lirc_mode2::LircMode2
pub fn aya::programs::lirc_mode2::LircMode2::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::lirc_mode2::LircMode2
pub fn aya::programs::lirc_mode2::LircMode2::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::lirc_mode2::LircMode2::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::lirc_mode2::LircMode2
pub fn aya::programs::lirc_mode2::LircMode2::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::lirc_mode2::LircMode2
pub fn aya::programs::lirc_mode2::LircMode2::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::lirc_mode2::LircMode2
pub fn aya::programs::lirc_mode2::LircMode2::drop(&mut self)
@ -3446,13 +3449,15 @@ pub fn aya::programs::lsm::Lsm::detach(&mut self, link_id: aya::programs::lsm::L
pub fn aya::programs::lsm::Lsm::load(&mut self, lsm_hook_name: &str, btf: &aya_obj::btf::btf::Btf) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::lsm::Lsm::take_link(&mut self, link_id: aya::programs::lsm::LsmLinkId) -> core::result::Result<aya::programs::lsm::LsmLink, aya::programs::ProgramError>
impl aya::programs::lsm::Lsm
pub fn aya::programs::lsm::Lsm::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::lsm::Lsm::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::lsm::Lsm
pub fn aya::programs::lsm::Lsm::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::lsm::Lsm
pub fn aya::programs::lsm::Lsm::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::lsm::Lsm::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::lsm::Lsm
pub fn aya::programs::lsm::Lsm::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::lsm::Lsm
pub fn aya::programs::lsm::Lsm::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::lsm::Lsm
pub fn aya::programs::lsm::Lsm::drop(&mut self)
@ -3731,13 +3736,15 @@ pub fn aya::programs::perf_event::PerfEvent::detach(&mut self, link_id: aya::pro
pub fn aya::programs::perf_event::PerfEvent::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::perf_event::PerfEvent::take_link(&mut self, link_id: aya::programs::perf_event::PerfEventLinkId) -> core::result::Result<aya::programs::perf_event::PerfEventLink, aya::programs::ProgramError>
impl aya::programs::perf_event::PerfEvent
pub fn aya::programs::perf_event::PerfEvent::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::perf_event::PerfEvent::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::perf_event::PerfEvent
pub fn aya::programs::perf_event::PerfEvent::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::perf_event::PerfEvent
pub fn aya::programs::perf_event::PerfEvent::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::perf_event::PerfEvent::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::perf_event::PerfEvent
pub fn aya::programs::perf_event::PerfEvent::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::perf_event::PerfEvent
pub fn aya::programs::perf_event::PerfEvent::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::perf_event::PerfEvent
pub fn aya::programs::perf_event::PerfEvent::drop(&mut self)
@ -3896,8 +3903,6 @@ impl core::marker::Sync for aya::programs::tc::TcError
impl core::marker::Unpin for aya::programs::tc::TcError
impl !core::panic::unwind_safe::RefUnwindSafe for aya::programs::tc::TcError
impl !core::panic::unwind_safe::UnwindSafe for aya::programs::tc::TcError
impl<E> core::any::Provider for aya::programs::tc::TcError where E: core::error::Error + core::marker::Sized
pub fn aya::programs::tc::TcError::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya::programs::tc::TcError where U: core::convert::From<T>
pub fn aya::programs::tc::TcError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::programs::tc::TcError where U: core::convert::Into<T>
@ -3925,11 +3930,13 @@ pub fn aya::programs::tc::SchedClassifier::from_pin<P: core::convert::AsRef<std:
pub fn aya::programs::tc::SchedClassifier::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::tc::SchedClassifier::take_link(&mut self, link_id: aya::programs::tc::SchedClassifierLinkId) -> core::result::Result<aya::programs::tc::SchedClassifierLink, aya::programs::ProgramError>
impl aya::programs::tc::SchedClassifier
pub fn aya::programs::tc::SchedClassifier::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::tc::SchedClassifier::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::tc::SchedClassifier
pub fn aya::programs::tc::SchedClassifier::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::tc::SchedClassifier::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::tc::SchedClassifier
pub fn aya::programs::tc::SchedClassifier::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::tc::SchedClassifier
pub fn aya::programs::tc::SchedClassifier::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::tc::SchedClassifier
pub fn aya::programs::tc::SchedClassifier::drop(&mut self)
@ -4063,13 +4070,15 @@ pub fn aya::programs::tp_btf::BtfTracePoint::detach(&mut self, link_id: aya::pro
pub fn aya::programs::tp_btf::BtfTracePoint::load(&mut self, tracepoint: &str, btf: &aya_obj::btf::btf::Btf) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::tp_btf::BtfTracePoint::take_link(&mut self, link_id: aya::programs::tp_btf::BtfTracePointLinkId) -> core::result::Result<aya::programs::tp_btf::BtfTracePointLink, aya::programs::ProgramError>
impl aya::programs::tp_btf::BtfTracePoint
pub fn aya::programs::tp_btf::BtfTracePoint::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::tp_btf::BtfTracePoint::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::tp_btf::BtfTracePoint
pub fn aya::programs::tp_btf::BtfTracePoint::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::tp_btf::BtfTracePoint
pub fn aya::programs::tp_btf::BtfTracePoint::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::tp_btf::BtfTracePoint::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::tp_btf::BtfTracePoint
pub fn aya::programs::tp_btf::BtfTracePoint::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::tp_btf::BtfTracePoint
pub fn aya::programs::tp_btf::BtfTracePoint::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::tp_btf::BtfTracePoint
pub fn aya::programs::tp_btf::BtfTracePoint::drop(&mut self)
@ -4185,8 +4194,6 @@ impl core::marker::Sync for aya::programs::trace_point::TracePointError
impl core::marker::Unpin for aya::programs::trace_point::TracePointError
impl !core::panic::unwind_safe::RefUnwindSafe for aya::programs::trace_point::TracePointError
impl !core::panic::unwind_safe::UnwindSafe for aya::programs::trace_point::TracePointError
impl<E> core::any::Provider for aya::programs::trace_point::TracePointError where E: core::error::Error + core::marker::Sized
pub fn aya::programs::trace_point::TracePointError::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya::programs::trace_point::TracePointError where U: core::convert::From<T>
pub fn aya::programs::trace_point::TracePointError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::programs::trace_point::TracePointError where U: core::convert::Into<T>
@ -4212,13 +4219,15 @@ pub fn aya::programs::trace_point::TracePoint::detach(&mut self, link_id: aya::p
pub fn aya::programs::trace_point::TracePoint::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::trace_point::TracePoint::take_link(&mut self, link_id: aya::programs::trace_point::TracePointLinkId) -> core::result::Result<aya::programs::trace_point::TracePointLink, aya::programs::ProgramError>
impl aya::programs::trace_point::TracePoint
pub fn aya::programs::trace_point::TracePoint::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::trace_point::TracePoint::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::trace_point::TracePoint
pub fn aya::programs::trace_point::TracePoint::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::trace_point::TracePoint
pub fn aya::programs::trace_point::TracePoint::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::trace_point::TracePoint::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::trace_point::TracePoint
pub fn aya::programs::trace_point::TracePoint::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::trace_point::TracePoint
pub fn aya::programs::trace_point::TracePoint::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::trace_point::TracePoint
pub fn aya::programs::trace_point::TracePoint::drop(&mut self)
@ -4343,8 +4352,6 @@ impl core::marker::Sync for aya::programs::uprobe::UProbeError
impl core::marker::Unpin for aya::programs::uprobe::UProbeError
impl !core::panic::unwind_safe::RefUnwindSafe for aya::programs::uprobe::UProbeError
impl !core::panic::unwind_safe::UnwindSafe for aya::programs::uprobe::UProbeError
impl<E> core::any::Provider for aya::programs::uprobe::UProbeError where E: core::error::Error + core::marker::Sized
pub fn aya::programs::uprobe::UProbeError::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya::programs::uprobe::UProbeError where U: core::convert::From<T>
pub fn aya::programs::uprobe::UProbeError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::programs::uprobe::UProbeError where U: core::convert::Into<T>
@ -4372,11 +4379,13 @@ pub fn aya::programs::uprobe::UProbe::kind(&self) -> aya::programs::ProbeKind
pub fn aya::programs::uprobe::UProbe::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::uprobe::UProbe::take_link(&mut self, link_id: aya::programs::uprobe::UProbeLinkId) -> core::result::Result<aya::programs::uprobe::UProbeLink, aya::programs::ProgramError>
impl aya::programs::uprobe::UProbe
pub fn aya::programs::uprobe::UProbe::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::uprobe::UProbe::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::uprobe::UProbe
pub fn aya::programs::uprobe::UProbe::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::uprobe::UProbe::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::uprobe::UProbe
pub fn aya::programs::uprobe::UProbe::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::uprobe::UProbe
pub fn aya::programs::uprobe::UProbe::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::uprobe::UProbe
pub fn aya::programs::uprobe::UProbe::drop(&mut self)
@ -4493,8 +4502,6 @@ impl core::marker::Sync for aya::programs::xdp::XdpError
impl core::marker::Unpin for aya::programs::xdp::XdpError
impl !core::panic::unwind_safe::RefUnwindSafe for aya::programs::xdp::XdpError
impl !core::panic::unwind_safe::UnwindSafe for aya::programs::xdp::XdpError
impl<E> core::any::Provider for aya::programs::xdp::XdpError where E: core::error::Error + core::marker::Sized
pub fn aya::programs::xdp::XdpError::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya::programs::xdp::XdpError where U: core::convert::From<T>
pub fn aya::programs::xdp::XdpError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::programs::xdp::XdpError where U: core::convert::Into<T>
@ -4522,13 +4529,15 @@ pub fn aya::programs::xdp::Xdp::detach(&mut self, link_id: aya::programs::xdp::X
pub fn aya::programs::xdp::Xdp::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::xdp::Xdp::take_link(&mut self, link_id: aya::programs::xdp::XdpLinkId) -> core::result::Result<aya::programs::xdp::XdpLink, aya::programs::ProgramError>
impl aya::programs::xdp::Xdp
pub fn aya::programs::xdp::Xdp::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::xdp::Xdp::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::xdp::Xdp
pub fn aya::programs::xdp::Xdp::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::xdp::Xdp
pub fn aya::programs::xdp::Xdp::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::xdp::Xdp::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::xdp::Xdp
pub fn aya::programs::xdp::Xdp::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::xdp::Xdp
pub fn aya::programs::xdp::Xdp::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::xdp::Xdp
pub fn aya::programs::xdp::Xdp::drop(&mut self)
@ -4651,9 +4660,6 @@ impl core::marker::Sync for aya::programs::xdp::XdpFlags
impl core::marker::Unpin for aya::programs::xdp::XdpFlags
impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::xdp::XdpFlags
impl core::panic::unwind_safe::UnwindSafe for aya::programs::xdp::XdpFlags
impl<B> bitflags::traits::BitFlags for aya::programs::xdp::XdpFlags where B: bitflags::traits::Flags
pub type aya::programs::xdp::XdpFlags::Iter = bitflags::iter::Iter<B>
pub type aya::programs::xdp::XdpFlags::IterNames = bitflags::iter::IterNames<B>
impl<T, U> core::convert::Into<U> for aya::programs::xdp::XdpFlags where U: core::convert::From<T>
pub fn aya::programs::xdp::XdpFlags::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::programs::xdp::XdpFlags where U: core::convert::Into<T>
@ -4788,8 +4794,6 @@ impl core::marker::Sync for aya::programs::extension::ExtensionError
impl core::marker::Unpin for aya::programs::extension::ExtensionError
impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::extension::ExtensionError
impl core::panic::unwind_safe::UnwindSafe for aya::programs::extension::ExtensionError
impl<E> core::any::Provider for aya::programs::extension::ExtensionError where E: core::error::Error + core::marker::Sized
pub fn aya::programs::extension::ExtensionError::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya::programs::extension::ExtensionError where U: core::convert::From<T>
pub fn aya::programs::extension::ExtensionError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::programs::extension::ExtensionError where U: core::convert::Into<T>
@ -4825,8 +4829,6 @@ impl core::marker::Sync for aya::programs::kprobe::KProbeError
impl core::marker::Unpin for aya::programs::kprobe::KProbeError
impl !core::panic::unwind_safe::RefUnwindSafe for aya::programs::kprobe::KProbeError
impl !core::panic::unwind_safe::UnwindSafe for aya::programs::kprobe::KProbeError
impl<E> core::any::Provider for aya::programs::kprobe::KProbeError where E: core::error::Error + core::marker::Sized
pub fn aya::programs::kprobe::KProbeError::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya::programs::kprobe::KProbeError where U: core::convert::From<T>
pub fn aya::programs::kprobe::KProbeError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::programs::kprobe::KProbeError where U: core::convert::Into<T>
@ -4982,7 +4984,7 @@ pub aya::programs::Program::TracePoint(aya::programs::trace_point::TracePoint)
pub aya::programs::Program::UProbe(aya::programs::uprobe::UProbe)
pub aya::programs::Program::Xdp(aya::programs::xdp::Xdp)
impl aya::programs::Program
pub fn aya::programs::Program::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::Program::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
pub fn aya::programs::Program::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::Program::prog_type(&self) -> aya_obj::generated::linux_bindings_x86_64::bpf_prog_type
pub fn aya::programs::Program::unload(self) -> core::result::Result<(), aya::programs::ProgramError>
@ -5210,8 +5212,6 @@ impl core::marker::Sync for aya::programs::ProgramError
impl core::marker::Unpin for aya::programs::ProgramError
impl !core::panic::unwind_safe::RefUnwindSafe for aya::programs::ProgramError
impl !core::panic::unwind_safe::UnwindSafe for aya::programs::ProgramError
impl<E> core::any::Provider for aya::programs::ProgramError where E: core::error::Error + core::marker::Sized
pub fn aya::programs::ProgramError::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya::programs::ProgramError where U: core::convert::From<T>
pub fn aya::programs::ProgramError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::programs::ProgramError where U: core::convert::Into<T>
@ -5311,8 +5311,6 @@ impl core::marker::Sync for aya::programs::SocketFilterError
impl core::marker::Unpin for aya::programs::SocketFilterError
impl !core::panic::unwind_safe::RefUnwindSafe for aya::programs::SocketFilterError
impl !core::panic::unwind_safe::UnwindSafe for aya::programs::SocketFilterError
impl<E> core::any::Provider for aya::programs::SocketFilterError where E: core::error::Error + core::marker::Sized
pub fn aya::programs::SocketFilterError::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya::programs::SocketFilterError where U: core::convert::From<T>
pub fn aya::programs::SocketFilterError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::programs::SocketFilterError where U: core::convert::Into<T>
@ -5389,8 +5387,6 @@ impl core::marker::Sync for aya::programs::tc::TcError
impl core::marker::Unpin for aya::programs::tc::TcError
impl !core::panic::unwind_safe::RefUnwindSafe for aya::programs::tc::TcError
impl !core::panic::unwind_safe::UnwindSafe for aya::programs::tc::TcError
impl<E> core::any::Provider for aya::programs::tc::TcError where E: core::error::Error + core::marker::Sized
pub fn aya::programs::tc::TcError::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya::programs::tc::TcError where U: core::convert::From<T>
pub fn aya::programs::tc::TcError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::programs::tc::TcError where U: core::convert::Into<T>
@ -5426,8 +5422,6 @@ impl core::marker::Sync for aya::programs::trace_point::TracePointError
impl core::marker::Unpin for aya::programs::trace_point::TracePointError
impl !core::panic::unwind_safe::RefUnwindSafe for aya::programs::trace_point::TracePointError
impl !core::panic::unwind_safe::UnwindSafe for aya::programs::trace_point::TracePointError
impl<E> core::any::Provider for aya::programs::trace_point::TracePointError where E: core::error::Error + core::marker::Sized
pub fn aya::programs::trace_point::TracePointError::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya::programs::trace_point::TracePointError where U: core::convert::From<T>
pub fn aya::programs::trace_point::TracePointError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::programs::trace_point::TracePointError where U: core::convert::Into<T>
@ -5470,8 +5464,6 @@ impl core::marker::Sync for aya::programs::uprobe::UProbeError
impl core::marker::Unpin for aya::programs::uprobe::UProbeError
impl !core::panic::unwind_safe::RefUnwindSafe for aya::programs::uprobe::UProbeError
impl !core::panic::unwind_safe::UnwindSafe for aya::programs::uprobe::UProbeError
impl<E> core::any::Provider for aya::programs::uprobe::UProbeError where E: core::error::Error + core::marker::Sized
pub fn aya::programs::uprobe::UProbeError::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya::programs::uprobe::UProbeError where U: core::convert::From<T>
pub fn aya::programs::uprobe::UProbeError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::programs::uprobe::UProbeError where U: core::convert::Into<T>
@ -5506,8 +5498,6 @@ impl core::marker::Sync for aya::programs::xdp::XdpError
impl core::marker::Unpin for aya::programs::xdp::XdpError
impl !core::panic::unwind_safe::RefUnwindSafe for aya::programs::xdp::XdpError
impl !core::panic::unwind_safe::UnwindSafe for aya::programs::xdp::XdpError
impl<E> core::any::Provider for aya::programs::xdp::XdpError where E: core::error::Error + core::marker::Sized
pub fn aya::programs::xdp::XdpError::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya::programs::xdp::XdpError where U: core::convert::From<T>
pub fn aya::programs::xdp::XdpError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::programs::xdp::XdpError where U: core::convert::Into<T>
@ -5533,13 +5523,15 @@ pub fn aya::programs::tp_btf::BtfTracePoint::detach(&mut self, link_id: aya::pro
pub fn aya::programs::tp_btf::BtfTracePoint::load(&mut self, tracepoint: &str, btf: &aya_obj::btf::btf::Btf) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::tp_btf::BtfTracePoint::take_link(&mut self, link_id: aya::programs::tp_btf::BtfTracePointLinkId) -> core::result::Result<aya::programs::tp_btf::BtfTracePointLink, aya::programs::ProgramError>
impl aya::programs::tp_btf::BtfTracePoint
pub fn aya::programs::tp_btf::BtfTracePoint::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::tp_btf::BtfTracePoint::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::tp_btf::BtfTracePoint
pub fn aya::programs::tp_btf::BtfTracePoint::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::tp_btf::BtfTracePoint
pub fn aya::programs::tp_btf::BtfTracePoint::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::tp_btf::BtfTracePoint::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::tp_btf::BtfTracePoint
pub fn aya::programs::tp_btf::BtfTracePoint::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::tp_btf::BtfTracePoint
pub fn aya::programs::tp_btf::BtfTracePoint::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::tp_btf::BtfTracePoint
pub fn aya::programs::tp_btf::BtfTracePoint::drop(&mut self)
@ -5579,13 +5571,15 @@ pub fn aya::programs::cgroup_device::CgroupDevice::detach(&mut self, link_id: ay
pub fn aya::programs::cgroup_device::CgroupDevice::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::cgroup_device::CgroupDevice::take_link(&mut self, link_id: aya::programs::cgroup_device::CgroupDeviceLinkId) -> core::result::Result<aya::programs::cgroup_device::CgroupDeviceLink, aya::programs::ProgramError>
impl aya::programs::cgroup_device::CgroupDevice
pub fn aya::programs::cgroup_device::CgroupDevice::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::cgroup_device::CgroupDevice::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::cgroup_device::CgroupDevice
pub fn aya::programs::cgroup_device::CgroupDevice::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::cgroup_device::CgroupDevice
pub fn aya::programs::cgroup_device::CgroupDevice::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::cgroup_device::CgroupDevice::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::cgroup_device::CgroupDevice
pub fn aya::programs::cgroup_device::CgroupDevice::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::cgroup_device::CgroupDevice
pub fn aya::programs::cgroup_device::CgroupDevice::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::cgroup_device::CgroupDevice
pub fn aya::programs::cgroup_device::CgroupDevice::drop(&mut self)
@ -5627,11 +5621,13 @@ pub fn aya::programs::cgroup_skb::CgroupSkb::from_pin<P: core::convert::AsRef<st
pub fn aya::programs::cgroup_skb::CgroupSkb::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::cgroup_skb::CgroupSkb::take_link(&mut self, link_id: aya::programs::cgroup_skb::CgroupSkbLinkId) -> core::result::Result<aya::programs::cgroup_skb::CgroupSkbLink, aya::programs::ProgramError>
impl aya::programs::cgroup_skb::CgroupSkb
pub fn aya::programs::cgroup_skb::CgroupSkb::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::cgroup_skb::CgroupSkb::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::cgroup_skb::CgroupSkb
pub fn aya::programs::cgroup_skb::CgroupSkb::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::cgroup_skb::CgroupSkb::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::cgroup_skb::CgroupSkb
pub fn aya::programs::cgroup_skb::CgroupSkb::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::cgroup_skb::CgroupSkb
pub fn aya::programs::cgroup_skb::CgroupSkb::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::cgroup_skb::CgroupSkb
pub fn aya::programs::cgroup_skb::CgroupSkb::drop(&mut self)
@ -5672,11 +5668,13 @@ pub fn aya::programs::cgroup_sock::CgroupSock::from_pin<P: core::convert::AsRef<
pub fn aya::programs::cgroup_sock::CgroupSock::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::cgroup_sock::CgroupSock::take_link(&mut self, link_id: aya::programs::cgroup_sock::CgroupSockLinkId) -> core::result::Result<aya::programs::cgroup_sock::CgroupSockLink, aya::programs::ProgramError>
impl aya::programs::cgroup_sock::CgroupSock
pub fn aya::programs::cgroup_sock::CgroupSock::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::cgroup_sock::CgroupSock::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::cgroup_sock::CgroupSock
pub fn aya::programs::cgroup_sock::CgroupSock::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::cgroup_sock::CgroupSock::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::cgroup_sock::CgroupSock
pub fn aya::programs::cgroup_sock::CgroupSock::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::cgroup_sock::CgroupSock
pub fn aya::programs::cgroup_sock::CgroupSock::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::cgroup_sock::CgroupSock
pub fn aya::programs::cgroup_sock::CgroupSock::drop(&mut self)
@ -5717,11 +5715,13 @@ pub fn aya::programs::cgroup_sock_addr::CgroupSockAddr::from_pin<P: core::conver
pub fn aya::programs::cgroup_sock_addr::CgroupSockAddr::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::cgroup_sock_addr::CgroupSockAddr::take_link(&mut self, link_id: aya::programs::cgroup_sock_addr::CgroupSockAddrLinkId) -> core::result::Result<aya::programs::cgroup_sock_addr::CgroupSockAddrLink, aya::programs::ProgramError>
impl aya::programs::cgroup_sock_addr::CgroupSockAddr
pub fn aya::programs::cgroup_sock_addr::CgroupSockAddr::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::cgroup_sock_addr::CgroupSockAddr::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::cgroup_sock_addr::CgroupSockAddr
pub fn aya::programs::cgroup_sock_addr::CgroupSockAddr::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::cgroup_sock_addr::CgroupSockAddr::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::cgroup_sock_addr::CgroupSockAddr
pub fn aya::programs::cgroup_sock_addr::CgroupSockAddr::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::cgroup_sock_addr::CgroupSockAddr
pub fn aya::programs::cgroup_sock_addr::CgroupSockAddr::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::cgroup_sock_addr::CgroupSockAddr
pub fn aya::programs::cgroup_sock_addr::CgroupSockAddr::drop(&mut self)
@ -5762,11 +5762,13 @@ pub fn aya::programs::cgroup_sockopt::CgroupSockopt::from_pin<P: core::convert::
pub fn aya::programs::cgroup_sockopt::CgroupSockopt::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::cgroup_sockopt::CgroupSockopt::take_link(&mut self, link_id: aya::programs::cgroup_sockopt::CgroupSockoptLinkId) -> core::result::Result<aya::programs::cgroup_sockopt::CgroupSockoptLink, aya::programs::ProgramError>
impl aya::programs::cgroup_sockopt::CgroupSockopt
pub fn aya::programs::cgroup_sockopt::CgroupSockopt::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::cgroup_sockopt::CgroupSockopt::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::cgroup_sockopt::CgroupSockopt
pub fn aya::programs::cgroup_sockopt::CgroupSockopt::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::cgroup_sockopt::CgroupSockopt::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::cgroup_sockopt::CgroupSockopt
pub fn aya::programs::cgroup_sockopt::CgroupSockopt::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::cgroup_sockopt::CgroupSockopt
pub fn aya::programs::cgroup_sockopt::CgroupSockopt::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::cgroup_sockopt::CgroupSockopt
pub fn aya::programs::cgroup_sockopt::CgroupSockopt::drop(&mut self)
@ -5806,13 +5808,15 @@ pub fn aya::programs::cgroup_sysctl::CgroupSysctl::detach(&mut self, link_id: ay
pub fn aya::programs::cgroup_sysctl::CgroupSysctl::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::cgroup_sysctl::CgroupSysctl::take_link(&mut self, link_id: aya::programs::cgroup_sysctl::CgroupSysctlLinkId) -> core::result::Result<aya::programs::cgroup_sysctl::CgroupSysctlLink, aya::programs::ProgramError>
impl aya::programs::cgroup_sysctl::CgroupSysctl
pub fn aya::programs::cgroup_sysctl::CgroupSysctl::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::cgroup_sysctl::CgroupSysctl::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::cgroup_sysctl::CgroupSysctl
pub fn aya::programs::cgroup_sysctl::CgroupSysctl::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::cgroup_sysctl::CgroupSysctl
pub fn aya::programs::cgroup_sysctl::CgroupSysctl::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::cgroup_sysctl::CgroupSysctl::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::cgroup_sysctl::CgroupSysctl
pub fn aya::programs::cgroup_sysctl::CgroupSysctl::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::cgroup_sysctl::CgroupSysctl
pub fn aya::programs::cgroup_sysctl::CgroupSysctl::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::cgroup_sysctl::CgroupSysctl
pub fn aya::programs::cgroup_sysctl::CgroupSysctl::drop(&mut self)
@ -5848,18 +5852,20 @@ pub fn aya::programs::cgroup_sysctl::CgroupSysctl::from(t: T) -> T
pub struct aya::programs::Extension
impl aya::programs::extension::Extension
pub fn aya::programs::extension::Extension::attach(&mut self) -> core::result::Result<aya::programs::extension::ExtensionLinkId, aya::programs::ProgramError>
pub fn aya::programs::extension::Extension::attach_to_program(&mut self, program: aya::programs::ProgramFd, func_name: &str) -> core::result::Result<aya::programs::extension::ExtensionLinkId, aya::programs::ProgramError>
pub fn aya::programs::extension::Extension::attach_to_program(&mut self, program: &aya::programs::ProgramFd, func_name: &str) -> core::result::Result<aya::programs::extension::ExtensionLinkId, aya::programs::ProgramError>
pub fn aya::programs::extension::Extension::detach(&mut self, link_id: aya::programs::extension::ExtensionLinkId) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::extension::Extension::load(&mut self, program: aya::programs::ProgramFd, func_name: &str) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::extension::Extension::take_link(&mut self, link_id: aya::programs::extension::ExtensionLinkId) -> core::result::Result<aya::programs::extension::ExtensionLink, aya::programs::ProgramError>
impl aya::programs::extension::Extension
pub fn aya::programs::extension::Extension::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::extension::Extension::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::extension::Extension
pub fn aya::programs::extension::Extension::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::extension::Extension
pub fn aya::programs::extension::Extension::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::extension::Extension::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::extension::Extension
pub fn aya::programs::extension::Extension::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::extension::Extension
pub fn aya::programs::extension::Extension::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::extension::Extension
pub fn aya::programs::extension::Extension::drop(&mut self)
@ -5899,13 +5905,15 @@ pub fn aya::programs::fentry::FEntry::detach(&mut self, link_id: aya::programs::
pub fn aya::programs::fentry::FEntry::load(&mut self, fn_name: &str, btf: &aya_obj::btf::btf::Btf) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::fentry::FEntry::take_link(&mut self, link_id: aya::programs::fentry::FEntryLinkId) -> core::result::Result<aya::programs::fentry::FEntryLink, aya::programs::ProgramError>
impl aya::programs::fentry::FEntry
pub fn aya::programs::fentry::FEntry::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::fentry::FEntry::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::fentry::FEntry
pub fn aya::programs::fentry::FEntry::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::fentry::FEntry
pub fn aya::programs::fentry::FEntry::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::fentry::FEntry::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::fentry::FEntry
pub fn aya::programs::fentry::FEntry::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::fentry::FEntry
pub fn aya::programs::fentry::FEntry::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::fentry::FEntry
pub fn aya::programs::fentry::FEntry::drop(&mut self)
@ -5945,13 +5953,15 @@ pub fn aya::programs::fexit::FExit::detach(&mut self, link_id: aya::programs::fe
pub fn aya::programs::fexit::FExit::load(&mut self, fn_name: &str, btf: &aya_obj::btf::btf::Btf) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::fexit::FExit::take_link(&mut self, link_id: aya::programs::fexit::FExitLinkId) -> core::result::Result<aya::programs::fexit::FExitLink, aya::programs::ProgramError>
impl aya::programs::fexit::FExit
pub fn aya::programs::fexit::FExit::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::fexit::FExit::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::fexit::FExit
pub fn aya::programs::fexit::FExit::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::fexit::FExit
pub fn aya::programs::fexit::FExit::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::fexit::FExit::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::fexit::FExit
pub fn aya::programs::fexit::FExit::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::fexit::FExit
pub fn aya::programs::fexit::FExit::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::fexit::FExit
pub fn aya::programs::fexit::FExit::drop(&mut self)
@ -5993,11 +6003,13 @@ pub fn aya::programs::kprobe::KProbe::kind(&self) -> aya::programs::ProbeKind
pub fn aya::programs::kprobe::KProbe::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::kprobe::KProbe::take_link(&mut self, link_id: aya::programs::kprobe::KProbeLinkId) -> core::result::Result<aya::programs::kprobe::KProbeLink, aya::programs::ProgramError>
impl aya::programs::kprobe::KProbe
pub fn aya::programs::kprobe::KProbe::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::kprobe::KProbe::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::kprobe::KProbe
pub fn aya::programs::kprobe::KProbe::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::kprobe::KProbe::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::kprobe::KProbe
pub fn aya::programs::kprobe::KProbe::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::kprobe::KProbe
pub fn aya::programs::kprobe::KProbe::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::kprobe::KProbe
pub fn aya::programs::kprobe::KProbe::drop(&mut self)
@ -6038,13 +6050,15 @@ pub fn aya::programs::lirc_mode2::LircMode2::load(&mut self) -> core::result::Re
pub fn aya::programs::lirc_mode2::LircMode2::query<T: std::os::fd::raw::AsRawFd>(target_fd: T) -> core::result::Result<alloc::vec::Vec<aya::programs::lirc_mode2::LircLink>, aya::programs::ProgramError>
pub fn aya::programs::lirc_mode2::LircMode2::take_link(&mut self, link_id: aya::programs::lirc_mode2::LircLinkId) -> core::result::Result<aya::programs::lirc_mode2::LircLink, aya::programs::ProgramError>
impl aya::programs::lirc_mode2::LircMode2
pub fn aya::programs::lirc_mode2::LircMode2::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::lirc_mode2::LircMode2::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::lirc_mode2::LircMode2
pub fn aya::programs::lirc_mode2::LircMode2::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::lirc_mode2::LircMode2
pub fn aya::programs::lirc_mode2::LircMode2::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::lirc_mode2::LircMode2::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::lirc_mode2::LircMode2
pub fn aya::programs::lirc_mode2::LircMode2::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::lirc_mode2::LircMode2
pub fn aya::programs::lirc_mode2::LircMode2::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::lirc_mode2::LircMode2
pub fn aya::programs::lirc_mode2::LircMode2::drop(&mut self)
@ -6084,13 +6098,15 @@ pub fn aya::programs::lsm::Lsm::detach(&mut self, link_id: aya::programs::lsm::L
pub fn aya::programs::lsm::Lsm::load(&mut self, lsm_hook_name: &str, btf: &aya_obj::btf::btf::Btf) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::lsm::Lsm::take_link(&mut self, link_id: aya::programs::lsm::LsmLinkId) -> core::result::Result<aya::programs::lsm::LsmLink, aya::programs::ProgramError>
impl aya::programs::lsm::Lsm
pub fn aya::programs::lsm::Lsm::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::lsm::Lsm::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::lsm::Lsm
pub fn aya::programs::lsm::Lsm::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::lsm::Lsm
pub fn aya::programs::lsm::Lsm::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::lsm::Lsm::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::lsm::Lsm
pub fn aya::programs::lsm::Lsm::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::lsm::Lsm
pub fn aya::programs::lsm::Lsm::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::lsm::Lsm
pub fn aya::programs::lsm::Lsm::drop(&mut self)
@ -6130,13 +6146,15 @@ pub fn aya::programs::perf_event::PerfEvent::detach(&mut self, link_id: aya::pro
pub fn aya::programs::perf_event::PerfEvent::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::perf_event::PerfEvent::take_link(&mut self, link_id: aya::programs::perf_event::PerfEventLinkId) -> core::result::Result<aya::programs::perf_event::PerfEventLink, aya::programs::ProgramError>
impl aya::programs::perf_event::PerfEvent
pub fn aya::programs::perf_event::PerfEvent::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::perf_event::PerfEvent::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::perf_event::PerfEvent
pub fn aya::programs::perf_event::PerfEvent::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::perf_event::PerfEvent
pub fn aya::programs::perf_event::PerfEvent::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::perf_event::PerfEvent::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::perf_event::PerfEvent
pub fn aya::programs::perf_event::PerfEvent::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::perf_event::PerfEvent
pub fn aya::programs::perf_event::PerfEvent::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::perf_event::PerfEvent
pub fn aya::programs::perf_event::PerfEvent::drop(&mut self)
@ -6170,11 +6188,12 @@ pub fn aya::programs::perf_event::PerfEvent::borrow_mut(&mut self) -> &mut T
impl<T> core::convert::From<T> for aya::programs::perf_event::PerfEvent
pub fn aya::programs::perf_event::PerfEvent::from(t: T) -> T
pub struct aya::programs::ProgramFd(_)
impl std::os::fd::raw::AsRawFd for aya::programs::ProgramFd
pub fn aya::programs::ProgramFd::as_raw_fd(&self) -> std::os::fd::raw::RawFd
impl core::clone::Clone for aya::programs::ProgramFd
pub fn aya::programs::ProgramFd::clone(&self) -> aya::programs::ProgramFd
impl core::marker::Copy for aya::programs::ProgramFd
impl aya::programs::ProgramFd
pub fn aya::programs::ProgramFd::try_clone(&self) -> core::result::Result<Self, aya::programs::ProgramError>
impl std::os::fd::owned::AsFd for aya::programs::ProgramFd
pub fn aya::programs::ProgramFd::as_fd(&self) -> std::os::fd::owned::BorrowedFd<'_>
impl core::fmt::Debug for aya::programs::ProgramFd
pub fn aya::programs::ProgramFd::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
impl core::marker::Send for aya::programs::ProgramFd
impl core::marker::Sync for aya::programs::ProgramFd
impl core::marker::Unpin for aya::programs::ProgramFd
@ -6188,10 +6207,6 @@ pub fn aya::programs::ProgramFd::try_from(value: U) -> core::result::Result<T, <
impl<T, U> core::convert::TryInto<U> for aya::programs::ProgramFd where U: core::convert::TryFrom<T>
pub type aya::programs::ProgramFd::Error = <U as core::convert::TryFrom<T>>::Error
pub fn aya::programs::ProgramFd::try_into(self) -> core::result::Result<U, <U as core::convert::TryFrom<T>>::Error>
impl<T> alloc::borrow::ToOwned for aya::programs::ProgramFd where T: core::clone::Clone
pub type aya::programs::ProgramFd::Owned = T
pub fn aya::programs::ProgramFd::clone_into(&self, target: &mut T)
pub fn aya::programs::ProgramFd::to_owned(&self) -> T
impl<T> core::any::Any for aya::programs::ProgramFd where T: 'static + core::marker::Sized
pub fn aya::programs::ProgramFd::type_id(&self) -> core::any::TypeId
impl<T> core::borrow::Borrow<T> for aya::programs::ProgramFd where T: core::marker::Sized
@ -6202,11 +6217,21 @@ impl<T> core::convert::From<T> for aya::programs::ProgramFd
pub fn aya::programs::ProgramFd::from(t: T) -> T
pub struct aya::programs::ProgramInfo(_)
impl aya::programs::ProgramInfo
pub fn aya::programs::ProgramInfo::fd(&self) -> core::result::Result<std::os::fd::raw::RawFd, aya::programs::ProgramError>
pub fn aya::programs::ProgramInfo::btf_id(&self) -> core::option::Option<core::num::nonzero::NonZeroU32>
pub fn aya::programs::ProgramInfo::fd(&self) -> core::result::Result<aya::programs::ProgramFd, aya::programs::ProgramError>
pub fn aya::programs::ProgramInfo::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
pub fn aya::programs::ProgramInfo::gpl_compatible(&self) -> bool
pub fn aya::programs::ProgramInfo::id(&self) -> u32
pub fn aya::programs::ProgramInfo::loaded_at(&self) -> std::time::SystemTime
pub fn aya::programs::ProgramInfo::map_ids(&self) -> core::result::Result<alloc::vec::Vec<u32>, aya::programs::ProgramError>
pub fn aya::programs::ProgramInfo::memory_locked(&self) -> core::result::Result<u32, aya::programs::ProgramError>
pub fn aya::programs::ProgramInfo::name(&self) -> &[u8]
pub fn aya::programs::ProgramInfo::name_as_str(&self) -> core::option::Option<&str>
pub fn aya::programs::ProgramInfo::program_type(&self) -> u32
pub fn aya::programs::ProgramInfo::size_jitted(&self) -> u32
pub fn aya::programs::ProgramInfo::size_translated(&self) -> u32
pub fn aya::programs::ProgramInfo::tag(&self) -> u64
pub fn aya::programs::ProgramInfo::verified_instruction_count(&self) -> u32
impl core::fmt::Debug for aya::programs::ProgramInfo
pub fn aya::programs::ProgramInfo::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
impl core::marker::Send for aya::programs::ProgramInfo
@ -6237,13 +6262,15 @@ pub fn aya::programs::RawTracePoint::detach(&mut self, link_id: RawTracePointLin
pub fn aya::programs::RawTracePoint::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::RawTracePoint::take_link(&mut self, link_id: RawTracePointLinkId) -> core::result::Result<RawTracePointLink, aya::programs::ProgramError>
impl aya::programs::RawTracePoint
pub fn aya::programs::RawTracePoint::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::RawTracePoint::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::RawTracePoint
pub fn aya::programs::RawTracePoint::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::RawTracePoint
pub fn aya::programs::RawTracePoint::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::RawTracePoint::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::RawTracePoint
pub fn aya::programs::RawTracePoint::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::RawTracePoint
pub fn aya::programs::RawTracePoint::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::RawTracePoint
pub fn aya::programs::RawTracePoint::drop(&mut self)
@ -6285,11 +6312,13 @@ pub fn aya::programs::tc::SchedClassifier::from_pin<P: core::convert::AsRef<std:
pub fn aya::programs::tc::SchedClassifier::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::tc::SchedClassifier::take_link(&mut self, link_id: aya::programs::tc::SchedClassifierLinkId) -> core::result::Result<aya::programs::tc::SchedClassifierLink, aya::programs::ProgramError>
impl aya::programs::tc::SchedClassifier
pub fn aya::programs::tc::SchedClassifier::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::tc::SchedClassifier::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::tc::SchedClassifier
pub fn aya::programs::tc::SchedClassifier::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::tc::SchedClassifier::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::tc::SchedClassifier
pub fn aya::programs::tc::SchedClassifier::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::tc::SchedClassifier
pub fn aya::programs::tc::SchedClassifier::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::tc::SchedClassifier
pub fn aya::programs::tc::SchedClassifier::drop(&mut self)
@ -6329,13 +6358,15 @@ pub fn aya::programs::SkLookup::detach(&mut self, link_id: SkLookupLinkId) -> co
pub fn aya::programs::SkLookup::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::SkLookup::take_link(&mut self, link_id: SkLookupLinkId) -> core::result::Result<SkLookupLink, aya::programs::ProgramError>
impl aya::programs::SkLookup
pub fn aya::programs::SkLookup::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::SkLookup::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::SkLookup
pub fn aya::programs::SkLookup::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::SkLookup
pub fn aya::programs::SkLookup::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::SkLookup::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::SkLookup
pub fn aya::programs::SkLookup::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::SkLookup
pub fn aya::programs::SkLookup::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::SkLookup
pub fn aya::programs::SkLookup::drop(&mut self)
@ -6375,13 +6406,15 @@ pub fn aya::programs::SkMsg::detach(&mut self, link_id: SkMsgLinkId) -> core::re
pub fn aya::programs::SkMsg::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::SkMsg::take_link(&mut self, link_id: SkMsgLinkId) -> core::result::Result<SkMsgLink, aya::programs::ProgramError>
impl aya::programs::SkMsg
pub fn aya::programs::SkMsg::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::SkMsg::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::SkMsg
pub fn aya::programs::SkMsg::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::SkMsg
pub fn aya::programs::SkMsg::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::SkMsg::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::SkMsg
pub fn aya::programs::SkMsg::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::SkMsg
pub fn aya::programs::SkMsg::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::SkMsg
pub fn aya::programs::SkMsg::drop(&mut self)
@ -6422,11 +6455,13 @@ pub fn aya::programs::SkSkb::from_pin<P: core::convert::AsRef<std::path::Path>>(
pub fn aya::programs::SkSkb::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::SkSkb::take_link(&mut self, link_id: SkSkbLinkId) -> core::result::Result<SkSkbLink, aya::programs::ProgramError>
impl aya::programs::SkSkb
pub fn aya::programs::SkSkb::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::SkSkb::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::SkSkb
pub fn aya::programs::SkSkb::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::SkSkb::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::SkSkb
pub fn aya::programs::SkSkb::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::SkSkb
pub fn aya::programs::SkSkb::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::SkSkb
pub fn aya::programs::SkSkb::drop(&mut self)
@ -6466,13 +6501,15 @@ pub fn aya::programs::SockOps::detach(&mut self, link_id: SockOpsLinkId) -> core
pub fn aya::programs::SockOps::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::SockOps::take_link(&mut self, link_id: SockOpsLinkId) -> core::result::Result<SockOpsLink, aya::programs::ProgramError>
impl aya::programs::SockOps
pub fn aya::programs::SockOps::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::SockOps::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::SockOps
pub fn aya::programs::SockOps::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::SockOps
pub fn aya::programs::SockOps::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::SockOps::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::SockOps
pub fn aya::programs::SockOps::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::SockOps
pub fn aya::programs::SockOps::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::SockOps
pub fn aya::programs::SockOps::drop(&mut self)
@ -6512,13 +6549,15 @@ pub fn aya::programs::SocketFilter::detach(&mut self, link_id: SocketFilterLinkI
pub fn aya::programs::SocketFilter::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::SocketFilter::take_link(&mut self, link_id: SocketFilterLinkId) -> core::result::Result<SocketFilterLink, aya::programs::ProgramError>
impl aya::programs::SocketFilter
pub fn aya::programs::SocketFilter::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::SocketFilter::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::SocketFilter
pub fn aya::programs::SocketFilter::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::SocketFilter
pub fn aya::programs::SocketFilter::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::SocketFilter::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::SocketFilter
pub fn aya::programs::SocketFilter::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::SocketFilter
pub fn aya::programs::SocketFilter::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::SocketFilter
pub fn aya::programs::SocketFilter::drop(&mut self)
@ -6558,13 +6597,15 @@ pub fn aya::programs::trace_point::TracePoint::detach(&mut self, link_id: aya::p
pub fn aya::programs::trace_point::TracePoint::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::trace_point::TracePoint::take_link(&mut self, link_id: aya::programs::trace_point::TracePointLinkId) -> core::result::Result<aya::programs::trace_point::TracePointLink, aya::programs::ProgramError>
impl aya::programs::trace_point::TracePoint
pub fn aya::programs::trace_point::TracePoint::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::trace_point::TracePoint::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::trace_point::TracePoint
pub fn aya::programs::trace_point::TracePoint::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::trace_point::TracePoint
pub fn aya::programs::trace_point::TracePoint::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::trace_point::TracePoint::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::trace_point::TracePoint
pub fn aya::programs::trace_point::TracePoint::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::trace_point::TracePoint
pub fn aya::programs::trace_point::TracePoint::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::trace_point::TracePoint
pub fn aya::programs::trace_point::TracePoint::drop(&mut self)
@ -6606,11 +6647,13 @@ pub fn aya::programs::uprobe::UProbe::kind(&self) -> aya::programs::ProbeKind
pub fn aya::programs::uprobe::UProbe::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::uprobe::UProbe::take_link(&mut self, link_id: aya::programs::uprobe::UProbeLinkId) -> core::result::Result<aya::programs::uprobe::UProbeLink, aya::programs::ProgramError>
impl aya::programs::uprobe::UProbe
pub fn aya::programs::uprobe::UProbe::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::uprobe::UProbe::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::uprobe::UProbe
pub fn aya::programs::uprobe::UProbe::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::uprobe::UProbe::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::uprobe::UProbe
pub fn aya::programs::uprobe::UProbe::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::uprobe::UProbe
pub fn aya::programs::uprobe::UProbe::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::uprobe::UProbe
pub fn aya::programs::uprobe::UProbe::drop(&mut self)
@ -6652,13 +6695,15 @@ pub fn aya::programs::xdp::Xdp::detach(&mut self, link_id: aya::programs::xdp::X
pub fn aya::programs::xdp::Xdp::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
pub fn aya::programs::xdp::Xdp::take_link(&mut self, link_id: aya::programs::xdp::XdpLinkId) -> core::result::Result<aya::programs::xdp::XdpLink, aya::programs::ProgramError>
impl aya::programs::xdp::Xdp
pub fn aya::programs::xdp::Xdp::fd(&self) -> core::option::Option<aya::programs::ProgramFd>
pub fn aya::programs::xdp::Xdp::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::xdp::Xdp
pub fn aya::programs::xdp::Xdp::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::xdp::Xdp
pub fn aya::programs::xdp::Xdp::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
pub fn aya::programs::xdp::Xdp::unpin(self) -> core::result::Result<(), std::io::error::Error>
impl aya::programs::xdp::Xdp
pub fn aya::programs::xdp::Xdp::program_info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::xdp::Xdp
pub fn aya::programs::xdp::Xdp::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl core::ops::drop::Drop for aya::programs::xdp::Xdp
pub fn aya::programs::xdp::Xdp::drop(&mut self)
@ -6781,9 +6826,6 @@ impl core::marker::Sync for aya::programs::xdp::XdpFlags
impl core::marker::Unpin for aya::programs::xdp::XdpFlags
impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::xdp::XdpFlags
impl core::panic::unwind_safe::UnwindSafe for aya::programs::xdp::XdpFlags
impl<B> bitflags::traits::BitFlags for aya::programs::xdp::XdpFlags where B: bitflags::traits::Flags
pub type aya::programs::xdp::XdpFlags::Iter = bitflags::iter::Iter<B>
pub type aya::programs::xdp::XdpFlags::IterNames = bitflags::iter::IterNames<B>
impl<T, U> core::convert::Into<U> for aya::programs::xdp::XdpFlags where U: core::convert::From<T>
pub fn aya::programs::xdp::XdpFlags::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::programs::xdp::XdpFlags where U: core::convert::Into<T>
@ -6935,11 +6977,10 @@ impl<T> core::borrow::BorrowMut<T> for aya::util::KernelVersion where T: core::m
pub fn aya::util::KernelVersion::borrow_mut(&mut self) -> &mut T
impl<T> core::convert::From<T> for aya::util::KernelVersion
pub fn aya::util::KernelVersion::from(t: T) -> T
pub fn aya::util::kernel_symbols() -> core::result::Result<aya::util::SimpleSymbolResolver, std::io::error::Error>
pub fn aya::util::kernel_symbols() -> core::result::Result<alloc::collections::btree::map::BTreeMap<u64, alloc::string::String>, std::io::error::Error>
pub fn aya::util::nr_cpus() -> core::result::Result<usize, std::io::error::Error>
pub fn aya::util::online_cpus() -> core::result::Result<alloc::vec::Vec<u32>, std::io::error::Error>
pub fn aya::util::syscall_prefix() -> core::result::Result<&'static str, std::io::error::Error>
pub type aya::util::SimpleSymbolResolver = alloc::collections::btree::map::BTreeMap<u64, alloc::string::String>
pub macro aya::include_bytes_aligned!
pub enum aya::BpfError
pub aya::BpfError::BtfError(aya_obj::btf::btf::BtfError)
@ -6980,8 +7021,6 @@ impl core::marker::Sync for aya::BpfError
impl core::marker::Unpin for aya::BpfError
impl !core::panic::unwind_safe::RefUnwindSafe for aya::BpfError
impl !core::panic::unwind_safe::UnwindSafe for aya::BpfError
impl<E> core::any::Provider for aya::BpfError where E: core::error::Error + core::marker::Sized
pub fn aya::BpfError::provide<'a>(&'a self, demand: &mut core::any::Demand<'a>)
impl<T, U> core::convert::Into<U> for aya::BpfError where U: core::convert::From<T>
pub fn aya::BpfError::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::BpfError where U: core::convert::Into<T>
@ -7187,9 +7226,6 @@ impl core::marker::Sync for aya::VerifierLogLevel
impl core::marker::Unpin for aya::VerifierLogLevel
impl core::panic::unwind_safe::RefUnwindSafe for aya::VerifierLogLevel
impl core::panic::unwind_safe::UnwindSafe for aya::VerifierLogLevel
impl<B> bitflags::traits::BitFlags for aya::VerifierLogLevel where B: bitflags::traits::Flags
pub type aya::VerifierLogLevel::Iter = bitflags::iter::Iter<B>
pub type aya::VerifierLogLevel::IterNames = bitflags::iter::IterNames<B>
impl<T, U> core::convert::Into<U> for aya::VerifierLogLevel where U: core::convert::From<T>
pub fn aya::VerifierLogLevel::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::VerifierLogLevel where U: core::convert::Into<T>

@ -1,4 +1,4 @@
use anyhow::{anyhow, Context as _, Result};
use anyhow::{bail, Context as _, Result};
use std::{
fs,
path::{Path, PathBuf},
@ -13,13 +13,10 @@ pub fn exec(cmd: &mut Command) -> Result<()> {
let status = cmd
.status()
.with_context(|| format!("failed to run {cmd:?}"))?;
match status.code() {
Some(code) => match code {
0 => Ok(()),
code => Err(anyhow!("{cmd:?} exited with code {code}")),
},
None => Err(anyhow!("{cmd:?} terminated by signal")),
if status.code() != Some(0) {
bail!("{cmd:?} failed: {status:?}")
}
Ok(())
}
// Create a symlink in the out directory to work around the fact that cargo ignores anything

@ -19,7 +19,6 @@ pub struct XtaskOptions {
enum Subcommand {
Codegen(codegen::Options),
Docs,
BuildIntegrationTest(run::BuildOptions),
IntegrationTest(run::Options),
PublicApi(public_api::Options),
}
@ -45,17 +44,6 @@ fn main() -> Result<()> {
match command {
Subcommand::Codegen(opts) => codegen::codegen(opts, libbpf_dir),
Subcommand::Docs => docs::docs(metadata),
Subcommand::BuildIntegrationTest(opts) => {
let binaries = run::build(opts)?;
let mut stdout = std::io::stdout();
for (_name, binary) in binaries {
use std::{io::Write as _, os::unix::ffi::OsStrExt as _};
stdout.write_all(binary.as_os_str().as_bytes())?;
stdout.write_all("\n".as_bytes())?;
}
Ok(())
}
Subcommand::IntegrationTest(opts) => run::run(opts),
Subcommand::PublicApi(opts) => public_api::public_api(opts, metadata),
}

@ -62,7 +62,7 @@ pub fn public_api(options: Options, metadata: Metadata) -> Result<()> {
});
if !errors.is_empty() {
bail!("public API errors:\n{errors}");
bail!("public API errors:\n{errors}")
}
Ok(())
}

@ -1,47 +1,68 @@
use std::{
env::consts::{ARCH, OS},
ffi::OsString,
fmt::Write as _,
io::BufReader,
path::PathBuf,
process::{Child, Command, Stdio},
fs::{copy, create_dir_all, metadata, File},
io::{BufRead as _, BufReader, ErrorKind, Write as _},
path::{Path, PathBuf},
process::{Child, ChildStdin, Command, Output, Stdio},
sync::{Arc, Mutex},
thread,
};
use anyhow::{bail, Context as _, Result};
use anyhow::{anyhow, bail, Context as _, Result};
use cargo_metadata::{Artifact, CompilerMessage, Message, Target};
use clap::Parser;
use xtask::AYA_BUILD_INTEGRATION_BPF;
use xtask::{exec, AYA_BUILD_INTEGRATION_BPF};
#[derive(Debug, Parser)]
pub struct BuildOptions {
/// Arguments to pass to `cargo build`.
#[clap(long)]
pub cargo_arg: Vec<OsString>,
#[derive(Parser)]
enum Environment {
/// Runs the integration tests locally.
Local {
/// The command used to wrap your application.
#[clap(short, long, default_value = "sudo -E")]
runner: String,
},
/// Runs the integration tests in a VM.
VM {
/// The kernel images to use.
///
/// You can download some images with:
///
/// wget --accept-regex '.*/linux-image-[0-9\.-]+-cloud-.*-unsigned*' \
/// --recursive ftp://ftp.us.debian.org/debian/pool/main/l/linux/
///
/// You can then extract them with:
///
/// find . -name '*.deb' -print0 \
/// | xargs -0 -I {} sh -c "dpkg --fsys-tarfile {} \
/// | tar --wildcards --extract '*vmlinuz*' --file -"
#[clap(required = true)]
kernel_image: Vec<PathBuf>,
},
}
#[derive(Debug, Parser)]
#[derive(Parser)]
pub struct Options {
#[command(flatten)]
pub build_options: BuildOptions,
/// The command used to wrap your application.
#[clap(short, long, default_value = "sudo -E")]
pub runner: String,
#[clap(subcommand)]
environment: Environment,
/// Arguments to pass to your application.
#[clap(last = true)]
pub run_args: Vec<OsString>,
#[clap(global = true, last = true)]
run_args: Vec<OsString>,
}
/// Build the project
pub fn build(opts: BuildOptions) -> Result<Vec<(String, PathBuf)>> {
let BuildOptions { cargo_arg } = opts;
pub fn build<F>(target: Option<&str>, f: F) -> Result<Vec<(String, PathBuf)>>
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.env(AYA_BUILD_INTEGRATION_BPF, "true")
.args([
"build",
"--tests",
"--message-format=json",
"--package=integration-test",
])
.args(cargo_arg);
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]);
}
f(&mut cmd);
let mut child = cmd
.stdout(Stdio::piped())
@ -77,57 +98,457 @@ pub fn build(opts: BuildOptions) -> Result<Vec<(String, PathBuf)>> {
let status = child
.wait()
.with_context(|| format!("failed to wait for {cmd:?}"))?;
match status.code() {
Some(code) => match code {
0 => {}
code => bail!("{cmd:?} exited with status code {code}"),
},
None => bail!("{cmd:?} terminated by signal"),
if status.code() != Some(0) {
bail!("{cmd:?} failed: {status:?}")
}
Ok(executables)
}
/// Build and run the project
#[derive(Debug)]
struct Errors(Vec<anyhow::Error>);
impl std::fmt::Display for Errors {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self(errors) = self;
for (i, error) in errors.iter().enumerate() {
if i != 0 {
writeln!(f)?;
}
write!(f, "{:?}", error)?;
}
Ok(())
}
}
impl std::error::Error for Errors {}
/// Build and run the project.
pub fn run(opts: Options) -> Result<()> {
let Options {
build_options,
runner,
environment,
run_args,
} = opts;
let binaries = build(build_options).context("error while building userspace application")?;
let mut args = runner.trim().split_terminator(' ');
let runner = args.next().ok_or(anyhow::anyhow!("no first argument"))?;
let args = args.collect::<Vec<_>>();
let mut failures = String::new();
for (name, binary) in binaries {
let mut cmd = Command::new(runner);
let cmd = cmd
.args(args.iter())
.arg(binary)
.args(run_args.iter())
.arg("--test-threads=1");
println!("{} running {cmd:?}", name);
let status = cmd
.status()
.with_context(|| format!("failed to run {cmd:?}"))?;
match status.code() {
Some(code) => match code {
0 => {}
code => writeln!(&mut failures, "{} exited with status code {code}", name)
.context("String write failed")?,
},
None => writeln!(&mut failures, "{} terminated by signal", name)
.context("String write failed")?,
}
type Binary = (String, PathBuf);
fn binaries(target: Option<&str>) -> Result<Vec<(&str, Vec<Binary>)>> {
["dev", "release"]
.into_iter()
.map(|profile| {
let binaries = build(target, |cmd| {
cmd.env(AYA_BUILD_INTEGRATION_BPF, "true").args([
"--package",
"integration-test",
"--tests",
"--profile",
profile,
])
})?;
anyhow::Ok((profile, binaries))
})
.collect()
}
if failures.is_empty() {
Ok(())
} else {
Err(anyhow::anyhow!("failures:\n{}", failures))
// Use --test-threads=1 to prevent tests from interacting with shared
// kernel state due to the lack of inter-test isolation.
let default_args = [OsString::from("--test-threads=1")];
let run_args = default_args.iter().chain(run_args.iter());
match environment {
Environment::Local { runner } => {
let mut args = runner.trim().split_terminator(' ');
let runner = args.next().ok_or(anyhow!("no first argument"))?;
let args = args.collect::<Vec<_>>();
let binaries = binaries(None)?;
let mut failures = String::new();
for (profile, binaries) in binaries {
for (name, binary) in binaries {
let mut cmd = Command::new(runner);
let cmd = cmd.args(args.iter()).arg(binary).args(run_args.clone());
println!("{profile}:{name} running {cmd:?}");
let status = cmd
.status()
.with_context(|| format!("failed to run {cmd:?}"))?;
if status.code() != Some(0) {
writeln!(&mut failures, "{profile}:{name} failed: {status:?}")
.context("String write failed")?
}
}
}
if failures.is_empty() {
Ok(())
} else {
Err(anyhow!("failures:\n{}", failures))
}
}
Environment::VM { kernel_image } => {
// The user has asked us to run the tests on a VM. This is involved; strap in.
//
// We need tools to build the initramfs; we use gen_init_cpio from the Linux repository,
// taking care to cache it.
//
// Then we iterate the kernel images, using the `file` program to guess the target
// architecture. We then build the init program and our test binaries for that
// architecture, and use gen_init_cpio to build an initramfs containing the test
// binaries. We're almost ready to run the VM.
//
// We consult our OS, our architecture, and the target architecture to determine if
// hardware acceleration is available, and then start QEMU with the provided kernel
// image and the initramfs we built.
//
// We consume the output of QEMU, looking for the output of our init program. This is
// the only way to distinguish success from failure. We batch up the errors across all
// VM images and report to the user. The end.
let cache_dir = Path::new("test/.tmp");
create_dir_all(cache_dir).context("failed to create cache dir")?;
let gen_init_cpio = cache_dir.join("gen_init_cpio");
if !gen_init_cpio
.try_exists()
.context("failed to check existence of gen_init_cpio")?
{
let mut curl = Command::new("curl");
curl.args([
"-sfSL",
"https://raw.githubusercontent.com/torvalds/linux/master/usr/gen_init_cpio.c",
]);
let mut curl_child = curl
.stdout(Stdio::piped())
.spawn()
.with_context(|| format!("failed to spawn {curl:?}"))?;
let Child { stdout, .. } = &mut curl_child;
let curl_stdout = stdout.take().unwrap();
let mut clang = Command::new("clang");
let clang = exec(
clang
.args(["-g", "-O2", "-x", "c", "-", "-o"])
.arg(&gen_init_cpio)
.stdin(curl_stdout),
);
let output = curl_child
.wait_with_output()
.with_context(|| format!("failed to wait for {curl:?}"))?;
let Output { status, .. } = &output;
if status.code() != Some(0) {
bail!("{curl:?} failed: {output:?}")
}
// Check the result of clang *after* checking curl; in case the download failed,
// only curl's output will be useful.
clang?;
}
let mut errors = Vec::new();
for kernel_image in kernel_image {
// Guess the guest architecture.
let mut cmd = Command::new("file");
let output = cmd
.arg("--brief")
.arg(&kernel_image)
.output()
.with_context(|| format!("failed to run {cmd:?}"))?;
let Output { status, .. } = &output;
if status.code() != Some(0) {
bail!("{cmd:?} failed: {output:?}")
}
let Output { stdout, .. } = output;
// Now parse the output of the file command, which looks something like
//
// - Linux kernel ARM64 boot executable Image, little-endian, 4K pages
//
// - Linux kernel x86 boot executable bzImage, version 6.1.0-10-cloud-amd64 [..]
let stdout = String::from_utf8(stdout)
.with_context(|| format!("invalid UTF-8 in {cmd:?} stdout"))?;
let (_, stdout) = stdout
.split_once("Linux kernel")
.ok_or_else(|| anyhow!("failed to parse {cmd:?} stdout: {stdout}"))?;
let (guest_arch, _) = stdout
.split_once("boot executable")
.ok_or_else(|| anyhow!("failed to parse {cmd:?} stdout: {stdout}"))?;
let guest_arch = guest_arch.trim();
let (guest_arch, machine, cpu) = match guest_arch {
"ARM64" => ("aarch64", Some("virt"), Some("cortex-a57")),
"x86" => ("x86_64", Some("q35"), Some("qemu64")),
guest_arch => (guest_arch, None, None),
};
let target = format!("{guest_arch}-unknown-linux-musl");
// Build our init program. The contract is that it will run anything it finds in /bin.
let init = build(Some(&target), |cmd| {
cmd.args(["--package", "init", "--profile", "release"])
})
.context("building init program failed")?;
let init = match &*init {
[(name, init)] => {
if name != "init" {
bail!("expected init program to be named init, found {name}")
}
init
}
init => bail!("expected exactly one init program, found {init:?}"),
};
let binaries = binaries(Some(&target))?;
let tmp_dir = tempfile::tempdir().context("tempdir failed")?;
let initrd_image = tmp_dir.path().join("qemu-initramfs.img");
let initrd_image_file = File::create(&initrd_image).with_context(|| {
format!("failed to create {} for writing", initrd_image.display())
})?;
let mut gen_init_cpio = Command::new(&gen_init_cpio);
let mut gen_init_cpio_child = gen_init_cpio
.arg("-")
.stdin(Stdio::piped())
.stdout(initrd_image_file)
.spawn()
.with_context(|| format!("failed to spawn {gen_init_cpio:?}"))?;
let Child { stdin, .. } = &mut gen_init_cpio_child;
let mut stdin = stdin.take().unwrap();
use std::os::unix::ffi::OsStrExt as _;
// Send input into gen_init_cpio which looks something like
//
// file /init path-to-init 0755 0 0
// dir /bin 0755 0 0
// file /bin/foo path-to-foo 0755 0 0
// file /bin/bar path-to-bar 0755 0 0
for bytes in [
"file /init ".as_bytes(),
init.as_os_str().as_bytes(),
" 0755 0 0\n".as_bytes(),
"dir /bin 0755 0 0\n".as_bytes(),
] {
stdin.write_all(bytes).expect("write");
}
for (profile, binaries) in binaries {
for (name, binary) in binaries {
let name = format!("{}-{}", profile, name);
let path = tmp_dir.path().join(&name);
copy(&binary, &path).with_context(|| {
format!("copy({}, {}) failed", binary.display(), path.display())
})?;
for bytes in [
"file /bin/".as_bytes(),
name.as_bytes(),
" ".as_bytes(),
path.as_os_str().as_bytes(),
" 0755 0 0\n".as_bytes(),
] {
stdin.write_all(bytes).expect("write");
}
}
}
// Must explicitly close to signal EOF.
drop(stdin);
let output = gen_init_cpio_child
.wait_with_output()
.with_context(|| format!("failed to wait for {gen_init_cpio:?}"))?;
let Output { status, .. } = &output;
if status.code() != Some(0) {
bail!("{gen_init_cpio:?} failed: {output:?}")
}
copy(&initrd_image, "/tmp/initrd.img").context("copy failed")?;
let mut qemu = Command::new(format!("qemu-system-{guest_arch}"));
if let Some(machine) = machine {
qemu.args(["-machine", machine]);
}
if guest_arch == ARCH {
match OS {
"linux" => match metadata("/dev/kvm") {
Ok(metadata) => {
use std::os::unix::fs::FileTypeExt as _;
if metadata.file_type().is_char_device() {
qemu.args(["-accel", "kvm"]);
}
}
Err(error) => {
if error.kind() != ErrorKind::NotFound {
Err(error).context("failed to check existence of /dev/kvm")?;
}
}
},
"macos" => {
qemu.args(["-accel", "hvf"]);
}
os => bail!("unsupported OS: {os}"),
}
} else if let Some(cpu) = cpu {
qemu.args(["-cpu", cpu]);
}
let console = OsString::from("ttyS0");
let mut kernel_args = std::iter::once(("console", &console))
.chain(run_args.clone().map(|run_arg| ("init.arg", run_arg)))
.enumerate()
.fold(OsString::new(), |mut acc, (i, (k, v))| {
if i != 0 {
acc.push(" ");
}
acc.push(k);
acc.push("=");
acc.push(v);
acc
});
// We sometimes see kernel panics containing:
//
// [ 0.064000] Kernel panic - not syncing: IO-APIC + timer doesn't work! Boot with apic=debug and send a report. Then try booting with the 'noapic' option.
//
// Heed the advice and boot with noapic. We don't know why this happens.
kernel_args.push(" noapic");
qemu.args(["-no-reboot", "-nographic", "-m", "512M", "-smp", "2"])
.arg("-append")
.arg(kernel_args)
.arg("-kernel")
.arg(&kernel_image)
.arg("-initrd")
.arg(&initrd_image);
if guest_arch == "aarch64" {
match OS {
"linux" => {
let mut cmd = Command::new("locate");
let output = cmd
.arg("QEMU_EFI.fd")
.output()
.with_context(|| format!("failed to run {cmd:?}"))?;
let Output { status, .. } = &output;
if status.code() != Some(0) {
bail!("{qemu:?} failed: {output:?}")
}
let Output { stdout, .. } = output;
let bios = String::from_utf8(stdout)
.with_context(|| format!("failed to parse output of {cmd:?}"))?;
qemu.args(["-bios", bios.trim()]);
}
"macos" => {
let mut cmd = Command::new("brew");
let output = cmd
.args(["list", "qemu", "-1", "-v"])
.output()
.with_context(|| format!("failed to run {cmd:?}"))?;
let Output { status, .. } = &output;
if status.code() != Some(0) {
bail!("{qemu:?} failed: {output:?}")
}
let Output { stdout, .. } = output;
let output = String::from_utf8(stdout)
.with_context(|| format!("failed to parse output of {cmd:?}"))?;
const NAME: &str = "edk2-aarch64-code.fd";
let bios = output.lines().find(|line| line.contains(NAME)).ok_or_else(
|| anyhow!("failed to find {NAME} in output of {cmd:?}: {output}"),
)?;
qemu.args(["-bios", bios.trim()]);
}
os => bail!("unsupported OS: {os}"),
};
}
let mut qemu_child = qemu
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.with_context(|| format!("failed to spawn {qemu:?}"))?;
let Child {
stdin,
stdout,
stderr,
..
} = &mut qemu_child;
let stdin = stdin.take().unwrap();
let stdin = Arc::new(Mutex::new(stdin));
let stdout = stdout.take().unwrap();
let stdout = BufReader::new(stdout);
let stderr = stderr.take().unwrap();
let stderr = BufReader::new(stderr);
fn terminate_if_contains_kernel_panic(
line: &str,
stdin: &Arc<Mutex<ChildStdin>>,
) -> anyhow::Result<()> {
if line.contains("end Kernel panic") {
println!("kernel panic detected; terminating QEMU");
let mut stdin = stdin.lock().unwrap();
stdin
.write_all(&[0x01, b'x'])
.context("failed to write to stdin")?;
println!("waiting for QEMU to terminate");
}
Ok(())
}
let stderr = {
let stdin = stdin.clone();
thread::Builder::new()
.spawn(move || {
for line in stderr.lines() {
let line = line.context("failed to read line from stderr")?;
eprintln!("{}", line);
// Try to get QEMU to exit on kernel panic; otherwise it might hang indefinitely.
terminate_if_contains_kernel_panic(&line, &stdin)?;
}
anyhow::Ok(())
})
.unwrap()
};
let mut outcome = None;
for line in stdout.lines() {
let line = line.context("failed to read line from stdout")?;
println!("{}", line);
// Try to get QEMU to exit on kernel panic; otherwise it might hang indefinitely.
terminate_if_contains_kernel_panic(&line, &stdin)?;
// The init program will print "init: success" or "init: failure" to indicate
// the outcome of running the binaries it found in /bin.
if let Some(line) = line.strip_prefix("init: ") {
let previous = match line {
"success" => outcome.replace(Ok(())),
"failure" => outcome.replace(Err(())),
line => bail!("unexpected init output: {}", line),
};
if let Some(previous) = previous {
bail!("multiple exit status: previous={previous:?}, current={line}");
}
}
}
let output = qemu_child
.wait_with_output()
.with_context(|| format!("failed to wait for {qemu:?}"))?;
let Output { status, .. } = &output;
if status.code() != Some(0) {
bail!("{qemu:?} failed: {output:?}")
}
stderr.join().unwrap()?;
let outcome = outcome.ok_or(anyhow!("init did not exit"))?;
match outcome {
Ok(()) => {}
Err(()) => {
errors.push(anyhow!("VM binaries failed on {}", kernel_image.display()))
}
}
}
if errors.is_empty() {
Ok(())
} else {
Err(Errors(errors).into())
}
}
}
}

Loading…
Cancel
Save