Browse Source

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.
Shenghui Ye 2 years ago
parent
commit
772af17
4 changed files with 93 additions and 17 deletions
  1. 1 2
      aya-obj/README.md
  2. 1 2
      aya-obj/src/lib.rs
  3. 65 2
      aya-obj/src/obj.rs
  4. 26 11
      test/integration-test/src/tests/rbpf.rs

+ 1 - 2
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,

+ 1 - 2
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,

+ 65 - 2
aya-obj/src/obj.rs

@@ -48,7 +48,8 @@ pub struct Object {
     pub btf_ext: Option<BtfExt>,
     /// Referenced maps
     pub maps: HashMap<String, Map>,
-    /// Programs
+    /// A hash map of programs, using the program names parsed
+    /// in [ProgramSection]s as keys.
     pub programs: HashMap<String, Program>,
     /// Functions
     pub functions: HashMap<u64, Function>,
@@ -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 {

+ 26 - 11
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::<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]);
+
+        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