pull/968/merge
Martin Søes 7 months ago committed by GitHub
commit 1f8b1635db
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -39,10 +39,14 @@
//! // Relocate the programs
//! #[cfg(feature = "std")]
//! let text_sections = std::collections::HashSet::new();
//! #[cfg(feature = "std")]
//! let ignore_maps = std::collections::HashMap::new();
//! #[cfg(not(feature = "std"))]
//! let text_sections = hashbrown::HashSet::new();
//! #[cfg(not(feature = "std"))]
//! let ignore_maps = hashbrown::HashMap::new();
//! object.relocate_calls(&text_sections).unwrap();
//! object.relocate_maps(std::iter::empty(), &text_sections).unwrap();
//! object.relocate_maps(std::iter::empty(), &text_sections, &ignore_maps).unwrap();
//!
//! // Run with rbpf
//! let function = object.functions.get(&object.programs["prog_name"].function_key()).unwrap();

@ -108,9 +108,20 @@ impl Object {
&mut self,
maps: I,
text_sections: &HashSet<usize>,
ignored_maps: &HashMap<String, Map>,
) -> Result<(), EbpfRelocationError> {
let mut maps_by_section = HashMap::new();
let mut maps_by_symbol = HashMap::new();
let mut ignored_by_section = HashSet::new();
let mut ignored_by_symbol = HashSet::new();
for map in ignored_maps.values() {
ignored_by_section.insert(map.section_index());
if let Some(index) = map.symbol_index() {
ignored_by_symbol.insert(index);
}
}
for (name, fd, map) in maps {
maps_by_section.insert(map.section_index(), (name, fd, map));
if let Some(index) = map.symbol_index() {
@ -127,6 +138,8 @@ impl Object {
&maps_by_symbol,
&self.symbol_table,
text_sections,
&ignored_by_section,
&ignored_by_symbol,
)
.map_err(|error| EbpfRelocationError {
function: function.name.clone(),
@ -176,6 +189,7 @@ impl Object {
}
}
#[allow(clippy::too_many_arguments)]
fn relocate_maps<'a, I: Iterator<Item = &'a Relocation>>(
fun: &mut Function,
relocations: I,
@ -183,6 +197,8 @@ fn relocate_maps<'a, I: Iterator<Item = &'a Relocation>>(
maps_by_symbol: &HashMap<usize, (&str, std::os::fd::RawFd, &Map)>,
symbol_table: &HashMap<usize, Symbol>,
text_sections: &HashSet<usize>,
ignored_by_section: &HashSet<usize>,
ignored_by_symbol: &HashSet<usize>,
) -> Result<(), RelocationError> {
let section_offset = fun.section_offset;
let instructions = &mut fun.instructions;
@ -222,7 +238,9 @@ 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 ignored_by_symbol.contains(&rel.symbol_index) {
continue;
} else 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 {}",
@ -232,6 +250,8 @@ fn relocate_maps<'a, I: Iterator<Item = &'a Relocation>>(
);
debug_assert_eq!(map.symbol_index().unwrap(), rel.symbol_index);
m
} else if ignored_by_section.contains(&section_index) {
continue;
} else {
let Some(m) = maps_by_section.get(&section_index) else {
debug!("failed relocating map by section index {}", section_index);
@ -580,6 +600,8 @@ mod test {
&maps_by_symbol,
&symbol_table,
&HashSet::new(),
&HashSet::new(),
&HashSet::new(),
)
.unwrap();
@ -636,6 +658,8 @@ mod test {
&maps_by_symbol,
&symbol_table,
&HashSet::new(),
&HashSet::new(),
&HashSet::new(),
)
.unwrap();
@ -675,6 +699,8 @@ mod test {
&maps_by_symbol,
&symbol_table,
&HashSet::new(),
&HashSet::new(),
&HashSet::new(),
)
.unwrap();
@ -731,6 +757,8 @@ mod test {
&maps_by_symbol,
&symbol_table,
&HashSet::new(),
&HashSet::new(),
&HashSet::new(),
)
.unwrap();

@ -139,6 +139,7 @@ pub struct EbpfLoader<'a> {
extensions: HashSet<&'a str>,
verifier_log_level: VerifierLogLevel,
allow_unsupported_maps: bool,
ignore_maps_by_name: &'a [&'a str],
}
/// Builder style API for advanced loading of eBPF programs.
@ -177,6 +178,7 @@ impl<'a> EbpfLoader<'a> {
extensions: HashSet::new(),
verifier_log_level: VerifierLogLevel::default(),
allow_unsupported_maps: false,
ignore_maps_by_name: &[],
}
}
@ -226,6 +228,32 @@ impl<'a> EbpfLoader<'a> {
self
}
/// Allows programs containing unsupported maps for the current kernel to be loaded
/// by skipping map creation and relocation before loading.
///
/// This is useful when you have a single ebpf program containing e.g. a `RingBuf`
/// and a `PerfEventArray` and you decide which one to use before loading the bytecode.
///
/// Must be used with [`EbpfLoader::set_global`] to signal if the map is supported in the ebpf program.
///
/// # Example
///
/// ```no_run
/// use aya::EbpfLoader;
///
/// let ringbuf_supported = 0;
/// let ebpf = EbpfLoader::new()
/// .ignore_maps_by_name(&["RINGBUF"])
/// .set_global("RINGBUF_SUPPORTED", &ringbuf_supported, true)
/// .load_file("file.o")?;
/// # Ok::<(), aya::EbpfError>(())
/// ```
///
pub fn ignore_maps_by_name(&mut self, name: &'a [&'a str]) -> &mut Self {
self.ignore_maps_by_name = name;
self
}
/// Sets the base directory path for pinned maps.
///
/// Pinned maps will be loaded from `path/MAP_NAME`.
@ -396,6 +424,7 @@ impl<'a> EbpfLoader<'a> {
extensions,
verifier_log_level,
allow_unsupported_maps,
ignore_maps_by_name,
} = self;
let mut obj = Object::parse(data)?;
obj.patch_map_data(globals.clone())?;
@ -461,12 +490,22 @@ impl<'a> EbpfLoader<'a> {
obj.relocate_btf(btf)?;
}
let mut maps = HashMap::new();
let mut ignored_maps = HashMap::new();
for (name, mut obj) in obj.maps.drain() {
if let (false, EbpfSectionKind::Bss | EbpfSectionKind::Data | EbpfSectionKind::Rodata) =
(FEATURES.bpf_global_data(), obj.section_kind())
{
continue;
}
if ignore_maps_by_name.contains(&name.as_str()) {
ignored_maps.insert(name, obj);
// ignore map creation. The map is saved in `ignored_maps` and filtered out
// in `relocate_maps()` later on
continue;
}
let num_cpus = || -> Result<u32, EbpfError> {
Ok(possible_cpus()
.map_err(|error| EbpfError::FileError {
@ -510,7 +549,6 @@ impl<'a> EbpfLoader<'a> {
map.finalize()?;
maps.insert(name, map);
}
let text_sections = obj
.functions
.keys()
@ -521,6 +559,7 @@ impl<'a> EbpfLoader<'a> {
maps.iter()
.map(|(s, data)| (s.as_str(), data.fd().as_fd().as_raw_fd(), data.obj())),
&text_sections,
&ignored_maps,
)?;
obj.relocate_calls(&text_sections)?;
obj.sanitize_functions(&FEATURES);

@ -61,6 +61,10 @@ path = "src/xdp_sec.rs"
name = "ring_buf"
path = "src/ring_buf.rs"
[[bin]]
name = "ignore_map"
path = "src/ignore_map.rs"
[[bin]]
name = "memmove_test"
path = "src/memmove_test.rs"

@ -0,0 +1,32 @@
#![no_std]
#![no_main]
use aya_ebpf::{
macros::{map, uprobe},
maps::{PerfEventArray, RingBuf},
programs::ProbeContext,
};
#[no_mangle]
pub static RINGBUF_SUPPORTED: i32 = 0;
#[map]
static mut RINGBUF: RingBuf = RingBuf::with_byte_size(0, 0);
#[map]
static mut PERFBUF: PerfEventArray<u64> = PerfEventArray::with_max_entries(1, 0);
#[uprobe]
pub fn test_ignored_map_relocation(ctx: ProbeContext) {
if unsafe { core::ptr::read_volatile(&RINGBUF_SUPPORTED) == 1 } {
let _ = unsafe { RINGBUF.output(&1, 0).map_err(|_| 1u32) };
} else {
unsafe { PERFBUF.output(&ctx, &1, 0) };
}
}
#[cfg(not(test))]
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}

@ -22,6 +22,7 @@ pub const BPF_PROBE_READ: &[u8] =
pub const REDIRECT: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/redirect"));
pub const XDP_SEC: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/xdp_sec"));
pub const RING_BUF: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/ring_buf"));
pub const IGNORE_MAP: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/ignore_map"));
pub const MEMMOVE_TEST: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/memmove_test"));
pub const SIMPLE_PROG: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/simple_prog"));

@ -84,11 +84,13 @@ fn use_map_with_rbpf() {
.iter()
.map(|((section_index, _), _)| *section_index)
.collect();
let disable_maps = HashMap::new();
object
.relocate_maps(
maps.iter()
.map(|(s, (fd, map))| (s.as_ref() as &str, *fd, map)),
&text_sections,
&disable_maps,
)
.expect("Relocation failed");
// Actually there is no local function call involved.

@ -1,6 +1,19 @@
use aya::{programs::UProbe, util::KernelVersion, Ebpf};
use aya::{programs::UProbe, util::KernelVersion, Ebpf, EbpfLoader};
use test_log::test;
#[test]
fn ignored_map_relocation_by_name() {
let mut ebpf =
relocation_load_and_attach("test_ignored_map_relocation", crate::IGNORE_MAP, "RINGBUF");
let perf = ebpf.take_map("PERFBUF");
let ring = ebpf.take_map("RINGBUF");
assert!(perf.is_some());
assert!(ring.is_none());
}
#[test]
fn relocations() {
let bpf = load_and_attach("test_64_32_call_relocs", crate::RELOCATIONS);
@ -33,6 +46,27 @@ fn text_64_64_reloc() {
assert_eq!(m.get(&1, 0).unwrap(), 3);
}
fn relocation_load_and_attach(name: &str, bytes: &[u8], disable_map_name: &str) -> Ebpf {
let mut ebpf = EbpfLoader::new()
.ignore_maps_by_name(&[disable_map_name])
.set_global("RINGBUF_SUPPORTED", &0, true)
.load(bytes)
.unwrap();
let prog: &mut UProbe = ebpf.program_mut(name).unwrap().try_into().unwrap();
prog.load().unwrap();
prog.attach(
Some("trigger_relocations_program"),
0,
"/proc/self/exe",
None,
)
.unwrap();
ebpf
}
fn load_and_attach(name: &str, bytes: &[u8]) -> Ebpf {
let mut bpf = Ebpf::load(bytes).unwrap();

Loading…
Cancel
Save