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 //! // Relocate the programs
//! #[cfg(feature = "std")] //! #[cfg(feature = "std")]
//! let text_sections = std::collections::HashSet::new(); //! let text_sections = std::collections::HashSet::new();
//! #[cfg(feature = "std")]
//! let ignore_maps = std::collections::HashMap::new();
//! #[cfg(not(feature = "std"))] //! #[cfg(not(feature = "std"))]
//! let text_sections = hashbrown::HashSet::new(); //! let text_sections = hashbrown::HashSet::new();
//! #[cfg(not(feature = "std"))]
//! let ignore_maps = hashbrown::HashMap::new();
//! object.relocate_calls(&text_sections).unwrap(); //! 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 //! // Run with rbpf
//! let function = object.functions.get(&object.programs["prog_name"].function_key()).unwrap(); //! let function = object.functions.get(&object.programs["prog_name"].function_key()).unwrap();

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

@ -139,6 +139,7 @@ pub struct EbpfLoader<'a> {
extensions: HashSet<&'a str>, extensions: HashSet<&'a str>,
verifier_log_level: VerifierLogLevel, verifier_log_level: VerifierLogLevel,
allow_unsupported_maps: bool, allow_unsupported_maps: bool,
ignore_maps_by_name: &'a [&'a str],
} }
/// Builder style API for advanced loading of eBPF programs. /// Builder style API for advanced loading of eBPF programs.
@ -177,6 +178,7 @@ impl<'a> EbpfLoader<'a> {
extensions: HashSet::new(), extensions: HashSet::new(),
verifier_log_level: VerifierLogLevel::default(), verifier_log_level: VerifierLogLevel::default(),
allow_unsupported_maps: false, allow_unsupported_maps: false,
ignore_maps_by_name: &[],
} }
} }
@ -226,6 +228,32 @@ impl<'a> EbpfLoader<'a> {
self 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. /// Sets the base directory path for pinned maps.
/// ///
/// Pinned maps will be loaded from `path/MAP_NAME`. /// Pinned maps will be loaded from `path/MAP_NAME`.
@ -396,6 +424,7 @@ impl<'a> EbpfLoader<'a> {
extensions, extensions,
verifier_log_level, verifier_log_level,
allow_unsupported_maps, allow_unsupported_maps,
ignore_maps_by_name,
} = self; } = self;
let mut obj = Object::parse(data)?; let mut obj = Object::parse(data)?;
obj.patch_map_data(globals.clone())?; obj.patch_map_data(globals.clone())?;
@ -461,12 +490,22 @@ impl<'a> EbpfLoader<'a> {
obj.relocate_btf(btf)?; obj.relocate_btf(btf)?;
} }
let mut maps = HashMap::new(); let mut maps = HashMap::new();
let mut ignored_maps = HashMap::new();
for (name, mut obj) in obj.maps.drain() { for (name, mut obj) in obj.maps.drain() {
if let (false, EbpfSectionKind::Bss | EbpfSectionKind::Data | EbpfSectionKind::Rodata) = if let (false, EbpfSectionKind::Bss | EbpfSectionKind::Data | EbpfSectionKind::Rodata) =
(FEATURES.bpf_global_data(), obj.section_kind()) (FEATURES.bpf_global_data(), obj.section_kind())
{ {
continue; 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> { let num_cpus = || -> Result<u32, EbpfError> {
Ok(possible_cpus() Ok(possible_cpus()
.map_err(|error| EbpfError::FileError { .map_err(|error| EbpfError::FileError {
@ -510,7 +549,6 @@ impl<'a> EbpfLoader<'a> {
map.finalize()?; map.finalize()?;
maps.insert(name, map); maps.insert(name, map);
} }
let text_sections = obj let text_sections = obj
.functions .functions
.keys() .keys()
@ -521,6 +559,7 @@ impl<'a> EbpfLoader<'a> {
maps.iter() maps.iter()
.map(|(s, data)| (s.as_str(), data.fd().as_fd().as_raw_fd(), data.obj())), .map(|(s, data)| (s.as_str(), data.fd().as_fd().as_raw_fd(), data.obj())),
&text_sections, &text_sections,
&ignored_maps,
)?; )?;
obj.relocate_calls(&text_sections)?; obj.relocate_calls(&text_sections)?;
obj.sanitize_functions(&FEATURES); obj.sanitize_functions(&FEATURES);

@ -61,6 +61,10 @@ path = "src/xdp_sec.rs"
name = "ring_buf" name = "ring_buf"
path = "src/ring_buf.rs" path = "src/ring_buf.rs"
[[bin]]
name = "ignore_map"
path = "src/ignore_map.rs"
[[bin]] [[bin]]
name = "memmove_test" name = "memmove_test"
path = "src/memmove_test.rs" 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 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 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 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 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")); pub const SIMPLE_PROG: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/simple_prog"));

@ -84,11 +84,13 @@ fn use_map_with_rbpf() {
.iter() .iter()
.map(|((section_index, _), _)| *section_index) .map(|((section_index, _), _)| *section_index)
.collect(); .collect();
let disable_maps = HashMap::new();
object object
.relocate_maps( .relocate_maps(
maps.iter() maps.iter()
.map(|(s, (fd, map))| (s.as_ref() as &str, *fd, map)), .map(|(s, (fd, map))| (s.as_ref() as &str, *fd, map)),
&text_sections, &text_sections,
&disable_maps,
) )
.expect("Relocation failed"); .expect("Relocation failed");
// Actually there is no local function call involved. // 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; 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] #[test]
fn relocations() { fn relocations() {
let bpf = load_and_attach("test_64_32_call_relocs", crate::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); 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 { fn load_and_attach(name: &str, bytes: &[u8]) -> Ebpf {
let mut bpf = Ebpf::load(bytes).unwrap(); let mut bpf = Ebpf::load(bytes).unwrap();

Loading…
Cancel
Save