aya-obj: add integration tests against rbpf

pull/475/head
Shenghui Ye 2 years ago
parent e52497cb9c
commit 311ead6760

@ -1 +1,42 @@
# aya-obj # aya-obj - an eBPF object file loading library
## Overview
eBPF programs written with [libbpf] or [aya-bpf] are usually compiled
into an ELF object file, using various section to store information
about the eBPF programs.
`aya-obj` is a library that loads, parses and processes such eBPF
object files.
[libbpf]: https://github.com/libbpf/libbpf
[aya-bpf]: https://github.com/aya-rs/aya
## Example
This example loads a simple eBPF program and runs it with [rbpf].
```rust
use aya_bpf::Object;
// Parse the object file
let bytes = std::fs::read("program.o").unwrap();
let mut object = Object::parse(bytes).unwrap();
// Relocate the programs
object.relocate_calls().unwrap();
object.relocate_maps(std::iter::empty()).unwrap();
// Run with rbpf
let program = object.programs.iter().next().unwrap().1;
let instructions = &program.function.instructions;
let data = unsafe {
from_raw_parts(
instructions.as_ptr() as *const u8,
instructions.len() * size_of::<bpf_insn>(),
)
};
let vm = rbpf::EbpfVmNoData::new(Some(data)).unwrap();
let _return = vm.execute_program().unwrap();
```
[rbpf]: https://github.com/qmonnet/rbpf

@ -1,4 +1,45 @@
//! A library for loading and relocating eBPF object files. //! A library for loading and relocating eBPF object files.
//!
//! ## Overview
//!
//! eBPF programs written with [libbpf] or [aya-bpf] are usually compiled
//! into an ELF object file, using various section to store information
//! about the eBPF programs.
//!
//! `aya-obj` is a library that loads, parses and processes such eBPF
//! object files.
//!
//! [libbpf]: https://github.com/libbpf/libbpf
//! [aya-bpf]: https://github.com/aya-rs/aya
//!
//! ## Example
//!
//! This example loads a simple eBPF program and runs it with [rbpf].
//!
//! ```no_run
//! use aya_bpf::Object;
//!
//! // Parse the object file
//! let bytes = std::fs::read("program.o").unwrap();
//! let mut object = Object::parse(bytes).unwrap();
//! // Relocate the programs
//! object.relocate_calls().unwrap();
//! object.relocate_maps(std::iter::empty()).unwrap();
//!
//! // Run with rbpf
//! let program = object.programs.iter().next().unwrap().1;
//! let instructions = &program.function.instructions;
//! let data = unsafe {
//! from_raw_parts(
//! instructions.as_ptr() as *const u8,
//! instructions.len() * size_of::<bpf_insn>(),
//! )
//! };
//! let vm = rbpf::EbpfVmNoData::new(Some(data)).unwrap();
//! let _return = vm.execute_program().unwrap();
//! ```
//!
//! [rbpf]: https://github.com/qmonnet/rbpf
#![doc( #![doc(
html_logo_url = "https://aya-rs.dev/assets/images/crabby.svg", html_logo_url = "https://aya-rs.dev/assets/images/crabby.svg",

@ -7,6 +7,7 @@ publish = false
[dependencies] [dependencies]
anyhow = "1" anyhow = "1"
aya = { path = "../../aya" } aya = { path = "../../aya" }
aya-obj = { path = "../../aya-obj" }
clap = { version = "4", features = ["derive"] } clap = { version = "4", features = ["derive"] }
env_logger = "0.10" env_logger = "0.10"
inventory = "0.2" inventory = "0.2"
@ -15,4 +16,5 @@ lazy_static = "1"
libc = { version = "0.2.105" } libc = { version = "0.2.105" }
log = "0.4" log = "0.4"
object = { version = "0.30", default-features = false, features = ["std", "read_core", "elf"] } object = { version = "0.30", default-features = false, features = ["std", "read_core", "elf"] }
rbpf = "0.1.0"
regex = "1" regex = "1"

@ -6,6 +6,7 @@ use std::{ffi::CStr, mem};
pub mod elf; pub mod elf;
pub mod load; pub mod load;
pub mod rbpf;
pub mod smoke; pub mod smoke;
pub use integration_test_macros::integration_test; pub use integration_test_macros::integration_test;

@ -0,0 +1,99 @@
use core::{mem::size_of, ptr::null_mut, slice::from_raw_parts};
use std::collections::HashMap;
use aya::include_bytes_aligned;
use aya_obj::{generated::bpf_insn, Object};
use super::{integration_test, IntegrationTest};
#[integration_test]
fn run_with_rbpf() {
let bytes = include_bytes_aligned!("../../../../target/bpfel-unknown-none/debug/pass");
let object = Object::parse(bytes).unwrap();
assert_eq!(object.programs.len(), 1);
let instructions = &object.programs["pass"].function.instructions;
let data = unsafe {
from_raw_parts(
instructions.as_ptr() as *const u8,
instructions.len() * size_of::<bpf_insn>(),
)
};
// Use rbpf interpreter instead of JIT compiler to ensure platform compatibility.
let vm = rbpf::EbpfVmNoData::new(Some(data)).unwrap();
const XDP_PASS: u64 = 2;
assert_eq!(vm.execute_program().unwrap(), XDP_PASS);
}
static mut MULTIMAP_MAPS: [*mut Vec<u64>; 2] = [null_mut(), null_mut()];
#[integration_test]
fn use_map_with_rbpf() {
let bytes =
include_bytes_aligned!("../../../../target/bpfel-unknown-none/debug/multimap-btf.bpf.o");
let mut object = Object::parse(bytes).unwrap();
let mut maps = HashMap::new();
// Initializes maps:
// - fd: 0xCAFE00 or 0xCAFE01,
// - Note that rbpf does not convert fds into real pointers,
// so we keeps the pointers to our maps in MULTIMAP_MAPS, to be used in helpers.
let mut map_instances = Vec::new();
for (map_id, (name, map)) in object.maps.iter().enumerate() {
maps.insert(name.to_owned(), (map_id as i32 | 0xCAFE00, map.clone()));
assert_eq!(map.key_size(), size_of::<u32>() as u32);
assert_eq!(map.value_size(), size_of::<u64>() as u32);
assert_eq!(
map.map_type(),
aya_obj::generated::bpf_map_type::BPF_MAP_TYPE_ARRAY as u32
);
map_instances.push(vec![0u64]);
unsafe {
MULTIMAP_MAPS[if name == "map_1" { 0 } else { 1 }] =
&mut map_instances[map_id] as *mut _;
}
}
object
.relocate_maps(
maps.iter()
.map(|(s, (fd, map))| (s.as_ref() as &str, Some(*fd), map)),
)
.expect("Relocation failed");
// Actually there is no call involved.
object.relocate_calls().unwrap();
// Executes the program
assert_eq!(object.programs.len(), 1);
let instructions = &object.programs["tracepoint"].function.instructions;
let data = unsafe {
from_raw_parts(
instructions.as_ptr() as *const u8,
instructions.len() * size_of::<bpf_insn>(),
)
};
let mut vm = rbpf::EbpfVmNoData::new(Some(data)).unwrap();
vm.register_helper(2, bpf_map_update_elem_multimap)
.expect("Helper failed");
assert_eq!(vm.execute_program().unwrap(), 0);
assert_eq!(map_instances[0][0], 24);
assert_eq!(map_instances[1][0], 42);
unsafe {
MULTIMAP_MAPS[0] = null_mut();
MULTIMAP_MAPS[1] = null_mut();
}
}
fn bpf_map_update_elem_multimap(map: u64, key: u64, value: u64, _: u64, _: u64) -> u64 {
assert!(map == 0xCAFE00 || map == 0xCAFE01);
let key = *unsafe { (key as usize as *const u32).as_ref().unwrap() };
let value = *unsafe { (value as usize as *const u64).as_ref().unwrap() };
assert_eq!(key, 0);
unsafe {
let map_instance = MULTIMAP_MAPS[map as usize & 0xFF].as_mut().unwrap();
map_instance[0] = value;
}
0
}
Loading…
Cancel
Save