From 772af170aea2feccb5e98cc84125e9e31b9fbe9a Mon Sep 17 00:00:00 2001 From: Shenghui Ye Date: Thu, 29 Dec 2022 14:01:50 +0800 Subject: [PATCH] aya-obj: add documentation on program names This commit adds documentation on how program names are parsed from section names, as is used by `aya_obj::Object.programs` as HashMap keys, and updates the examples into using program names. --- aya-obj/README.md | 3 +- aya-obj/src/lib.rs | 3 +- aya-obj/src/obj.rs | 67 ++++++++++++++++++++++++- test/integration-test/src/tests/rbpf.rs | 37 ++++++++++---- 4 files changed, 93 insertions(+), 17 deletions(-) diff --git a/aya-obj/README.md b/aya-obj/README.md index cfc4cfae..3bb23916 100644 --- a/aya-obj/README.md +++ b/aya-obj/README.md @@ -27,8 +27,7 @@ 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 instructions = &object.programs["prog_name"].function.instructions; let data = unsafe { core::slice::from_raw_parts( instructions.as_ptr() as *const u8, diff --git a/aya-obj/src/lib.rs b/aya-obj/src/lib.rs index 4044eea3..f9d04d6e 100644 --- a/aya-obj/src/lib.rs +++ b/aya-obj/src/lib.rs @@ -27,8 +27,7 @@ //! object.relocate_maps(std::iter::empty()).unwrap(); //! //! // Run with rbpf -//! let program = object.programs.iter().next().unwrap().1; -//! let instructions = &program.function.instructions; +//! let instructions = &object.programs["prog_name"].function.instructions; //! let data = unsafe { //! core::slice::from_raw_parts( //! instructions.as_ptr() as *const u8, diff --git a/aya-obj/src/obj.rs b/aya-obj/src/obj.rs index 5c01c815..6a859190 100644 --- a/aya-obj/src/obj.rs +++ b/aya-obj/src/obj.rs @@ -48,7 +48,8 @@ pub struct Object { pub btf_ext: Option, /// Referenced maps pub maps: HashMap, - /// Programs + /// A hash map of programs, using the program names parsed + /// in [ProgramSection]s as keys. pub programs: HashMap, /// Functions pub functions: HashMap, @@ -97,7 +98,69 @@ pub struct Function { pub line_info_rec_size: usize, } -/// Sections containing eBPF programs +/// Section types containing eBPF programs +/// +/// # Section Name Parsing +/// +/// Section types are parsed from the section name strings. +/// +/// In order for Aya to treat a section as a [ProgramSection], +/// there are a few requirements: +/// - The section must be an executable code section. +/// - The section name must conform to [Program Types and ELF Sections]. +/// +/// [Program Types and ELF Sections]: https://docs.kernel.org/bpf/libbpf/program_types.html +/// +/// ## Program Name +/// +/// Each section name is parsed into a section type and a program name. +/// +/// Generally speaking, +/// - if the section name does not contain any slashes, +/// then the program name is just that section name; +/// - if there are some slashes, the name is `section_name.rsplitn(2, '/')[0]`, +/// - except for tracepoint programs, for which the name is +/// `section_name.splitn(2, '/')[1]`. +/// +/// ```rust +/// use aya_obj::ProgramSection; +/// use std::str::FromStr; +/// +/// assert_eq!( +/// ProgramSection::from_str("kprobe/do_unlinkat") +/// .unwrap().name(), +/// "do_unlinkat", +/// ); +/// assert_eq!( +/// ProgramSection::from_str("tracepoint/syscalls/sys_enter_openat") +/// .unwrap().name(), +/// "syscalls/sys_enter_openat", +/// ); +/// ``` +/// +/// The program name will be used in [Object] as references to each program. +/// +/// ## Unsupported Sections +/// +/// Currently, the following section names are not supported yet: +/// - `flow_dissector`: `BPF_PROG_TYPE_FLOW_DISSECTOR` +/// - `ksyscall+` or `kretsyscall+` +/// - `uprobe.s+` or `uretprobe.s+` +/// - `usdt+` +/// - `kprobe.multi+` or `kretprobe.multi+`: `BPF_TRACE_KPROBE_MULTI` +/// - `lsm_cgroup+` or `lsm.s+` +/// - `lwt_in`, `lwt_out`, `lwt_seg6local`, `lwt_xmit` +/// - `raw_tp.w+`, `raw_tracepoint.w+` +/// - `action` +/// - `sk_reuseport/migrate`, `sk_reuseport` +/// - `syscall` +/// - `struct_ops+` +/// - `fmod_ret+`, `fmod_ret.s+` +/// - `fentry.s+`, `fexit.s+` +/// - `iter+`, `iter.s+` +/// - `xdp.frags/cpumap`, `xdp/cpumap` +/// - `xdp.frags/devmap`, `xdp/devmap` +/// - `xdp.frags` #[derive(Debug, Clone)] #[allow(missing_docs)] pub enum ProgramSection { diff --git a/test/integration-test/src/tests/rbpf.rs b/test/integration-test/src/tests/rbpf.rs index a76bb162..92dfac74 100644 --- a/test/integration-test/src/tests/rbpf.rs +++ b/test/integration-test/src/tests/rbpf.rs @@ -2,7 +2,7 @@ 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 aya_obj::{generated::bpf_insn, Object, ProgramSection}; use super::{integration_test, IntegrationTest}; @@ -10,7 +10,14 @@ use super::{integration_test, IntegrationTest}; 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); + assert!(matches!( + object.programs["pass"].section, + ProgramSection::Xdp { .. } + )); + assert_eq!(object.programs["pass"].section.name(), "pass"); + let instructions = &object.programs["pass"].function.instructions; let data = unsafe { from_raw_parts( @@ -31,26 +38,34 @@ 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, + assert_eq!(object.programs.len(), 1); + assert!(matches!( + object.programs["tracepoint"].section, + ProgramSection::TracePoint { .. } + )); + assert_eq!(object.programs["tracepoint"].section.name(), "tracepoint"); + + // Initialize maps: + // - fd: 0xCAFE00 or 0xCAFE01 (the 0xCAFE00 part is used to distinguish fds from indices), // - 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())); + let mut maps = HashMap::new(); + let mut map_instances = vec![vec![0u64], vec![0u64]]; + for (name, map) in object.maps.iter() { assert_eq!(map.key_size(), size_of::() as u32); assert_eq!(map.value_size(), size_of::() as u32); assert_eq!( map.map_type(), aya_obj::generated::bpf_map_type::BPF_MAP_TYPE_ARRAY as u32 ); - map_instances.push(vec![0u64]); + + let map_id = if name == "map_1" { 0 } else { 1 }; + let fd = map_id as i32 | 0xCAFE00; + maps.insert(name.to_owned(), (fd, map.clone())); unsafe { - MULTIMAP_MAPS[if name == "map_1" { 0 } else { 1 }] = - &mut map_instances[map_id] as *mut _; + MULTIMAP_MAPS[map_id] = &mut map_instances[map_id] as *mut _; } } @@ -60,7 +75,7 @@ fn use_map_with_rbpf() { .map(|(s, (fd, map))| (s.as_ref() as &str, Some(*fd), map)), ) .expect("Relocation failed"); - // Actually there is no call involved. + // Actually there is no local function call involved. object.relocate_calls().unwrap(); // Executes the program