Skip to content

Commit f60ee6a

Browse files
alexgartrelldanielocfb
authored andcommitted
libbpf-rs: Change ObjectBuilder opts semantics
By stashing an opts struct internally and converting name from a String to a CString, we can return a reference to an opt struct. This allows us to stack the lifetimes so that we don't need to copy-paste the name_ptr logic everywhere we call it.
1 parent c309e85 commit f60ee6a

File tree

3 files changed

+61
-52
lines changed

3 files changed

+61
-52
lines changed

libbpf-cargo/src/gen/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -695,11 +695,11 @@ fn gen_skel_contents(_debug: bool, raw_obj_name: &str, obj_file_path: &Path) ->
695695
696696
impl<'a> SkelBuilder<'a> for {name}SkelBuilder {{
697697
type Output = Open{name}Skel<'a>;
698-
fn open(mut self) -> libbpf_rs::Result<Open{name}Skel<'a>> {{
698+
fn open(self) -> libbpf_rs::Result<Open{name}Skel<'a>> {{
699699
let mut skel_config = build_skel_config()?;
700-
let open_opts = self.obj_builder.opts(std::ptr::null());
700+
let open_opts = self.obj_builder.opts();
701701
702-
let ret = unsafe {{ libbpf_sys::bpf_object__open_skeleton(skel_config.get(), &open_opts) }};
702+
let ret = unsafe {{ libbpf_sys::bpf_object__open_skeleton(skel_config.get(), open_opts) }};
703703
if ret != 0 {{
704704
return Err(libbpf_rs::Error::System(-ret));
705705
}}

libbpf-rs/src/object.rs

Lines changed: 40 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use core::ffi::c_void;
22
use std::collections::HashMap;
33
use std::ffi::CStr;
4+
use std::ffi::CString;
45
use std::mem;
5-
use std::os::raw::c_char;
66
use std::path::Path;
77
use std::ptr::NonNull;
88
use std::ptr::{self};
@@ -20,22 +20,42 @@ use crate::Program;
2020
use crate::Result;
2121

2222
/// Builder for creating an [`OpenObject`]. Typically the entry point into libbpf-rs.
23-
#[derive(Default, Debug)]
23+
#[derive(Debug)]
2424
pub struct ObjectBuilder {
25-
name: String,
26-
relaxed_maps: bool,
25+
name: Option<CString>,
26+
27+
opts: libbpf_sys::bpf_object_open_opts,
28+
}
29+
30+
impl Default for ObjectBuilder {
31+
fn default() -> Self {
32+
let opts = libbpf_sys::bpf_object_open_opts {
33+
sz: mem::size_of::<libbpf_sys::bpf_object_open_opts>() as libbpf_sys::size_t,
34+
object_name: ptr::null(),
35+
relaxed_maps: false,
36+
pin_root_path: ptr::null(),
37+
kconfig: ptr::null(),
38+
btf_custom_path: ptr::null(),
39+
kernel_log_buf: ptr::null_mut(),
40+
kernel_log_size: 0,
41+
kernel_log_level: 0,
42+
..Default::default()
43+
};
44+
Self { name: None, opts }
45+
}
2746
}
2847

2948
impl ObjectBuilder {
3049
/// Override the generated name that would have been inferred from the constructor.
31-
pub fn name<T: AsRef<str>>(&mut self, name: T) -> &mut Self {
32-
self.name = name.as_ref().to_string();
33-
self
50+
pub fn name<T: AsRef<str>>(&mut self, name: T) -> Result<&mut Self> {
51+
self.name = Some(util::str_to_cstring(name.as_ref())?);
52+
self.opts.object_name = self.name.as_ref().map_or(ptr::null(), |p| p.as_ptr());
53+
Ok(self)
3454
}
3555

3656
/// Option to parse map definitions non-strictly, allowing extra attributes/data
3757
pub fn relaxed_maps(&mut self, relaxed_maps: bool) -> &mut Self {
38-
self.relaxed_maps = relaxed_maps;
58+
self.opts.relaxed_maps = relaxed_maps;
3959
self
4060
}
4161

@@ -52,20 +72,13 @@ impl ObjectBuilder {
5272
self
5373
}
5474

55-
/// Get an instance of libbpf_sys::bpf_object_open_opts.
56-
pub fn opts(&mut self, name: *const c_char) -> libbpf_sys::bpf_object_open_opts {
57-
libbpf_sys::bpf_object_open_opts {
58-
sz: mem::size_of::<libbpf_sys::bpf_object_open_opts>() as libbpf_sys::size_t,
59-
object_name: name,
60-
relaxed_maps: self.relaxed_maps,
61-
pin_root_path: ptr::null(),
62-
kconfig: ptr::null(),
63-
btf_custom_path: ptr::null(),
64-
kernel_log_buf: ptr::null_mut(),
65-
kernel_log_size: 0,
66-
kernel_log_level: 0,
67-
..Default::default()
68-
}
75+
/// Get the raw libbpf_sys::bpf_object_open_opts.
76+
///
77+
/// The internal pointers are tied to the lifetime of the
78+
/// ObjectBuilder, so be wary when copying the struct or otherwise
79+
/// handing the lifetime over to C.
80+
pub fn opts(&self) -> &libbpf_sys::bpf_object_open_opts {
81+
&self.opts
6982
}
7083

7184
/// Open an object using the provided path on the file system.
@@ -77,43 +90,23 @@ impl ObjectBuilder {
7790
let path_c = util::str_to_cstring(path_str)?;
7891
let path_ptr = path_c.as_ptr();
7992

80-
// Convert name to a C style pointer
81-
//
82-
// NB: we must hold onto a CString otherwise our pointer dangles
83-
let name = util::str_to_cstring(&self.name)?;
84-
let name_ptr = if !self.name.is_empty() {
85-
name.as_ptr()
86-
} else {
87-
ptr::null()
88-
};
89-
90-
let opts = self.opts(name_ptr);
93+
let opts = self.opts();
9194

9295
util::create_bpf_entity_checked(|| unsafe {
93-
libbpf_sys::bpf_object__open_file(path_ptr, &opts)
96+
libbpf_sys::bpf_object__open_file(path_ptr, opts)
9497
})
9598
.and_then(|ptr| unsafe { OpenObject::new(ptr) })
9699
}
97100

98101
/// Open an object from memory.
99-
pub fn open_memory<T: AsRef<str>>(&mut self, name: T, mem: &[u8]) -> Result<OpenObject> {
100-
// Convert name to a C style pointer
101-
//
102-
// NB: we must hold onto a CString otherwise our pointer dangles
103-
let name = util::str_to_cstring(name.as_ref())?;
104-
let name_ptr = if !name.to_bytes().is_empty() {
105-
name.as_ptr()
106-
} else {
107-
ptr::null()
108-
};
109-
110-
let opts = self.opts(name_ptr);
102+
pub fn open_memory(&mut self, mem: &[u8]) -> Result<OpenObject> {
103+
let opts = self.opts();
111104

112105
util::create_bpf_entity_checked(|| unsafe {
113106
libbpf_sys::bpf_object__open_mem(
114107
mem.as_ptr() as *const c_void,
115108
mem.len() as libbpf_sys::size_t,
116-
&opts,
109+
opts,
117110
)
118111
})
119112
.and_then(|ptr| unsafe { OpenObject::new(ptr) })

libbpf-rs/tests/test.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,28 @@ fn test_object_build_from_memory() {
120120
let contents = fs::read(obj_path).expect("failed to read object file");
121121
let mut builder = ObjectBuilder::default();
122122
let obj = builder
123-
.open_memory("memory name", &contents)
123+
.name("memory name")
124+
.unwrap()
125+
.open_memory(&contents)
124126
.expect("failed to build object");
125127
let name = obj.name().expect("failed to get object name");
126128
assert!(name == "memory name");
127129
}
128130

131+
#[test]
132+
fn test_object_build_from_memory_empty_name() {
133+
let obj_path = get_test_object_path("runqslower.bpf.o");
134+
let contents = fs::read(obj_path).expect("failed to read object file");
135+
let mut builder = ObjectBuilder::default();
136+
let obj = builder
137+
.name("")
138+
.unwrap()
139+
.open_memory(&contents)
140+
.expect("failed to build object");
141+
let name = obj.name().expect("failed to get object name");
142+
assert!(name.is_empty());
143+
}
144+
129145
/// Check that loading an object from an empty file fails as expected.
130146
#[test]
131147
fn test_sudo_object_load_invalid() {
@@ -140,7 +156,7 @@ fn test_sudo_object_load_invalid() {
140156
fn test_object_name() {
141157
let obj_path = get_test_object_path("runqslower.bpf.o");
142158
let mut builder = ObjectBuilder::default();
143-
builder.name("test name");
159+
builder.name("test name").unwrap();
144160
let obj = builder.open_file(obj_path).expect("failed to build object");
145161
let obj_name = obj.name().expect("failed to get object name");
146162
assert!(obj_name == "test name");

0 commit comments

Comments
 (0)