Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

libbpf-cargo: Support on-the-fly vmlinux.h extraction #613

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions libbpf-cargo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ path = "src/lib.rs"
# When turned on, link against system-installed libbpf instead of building
# and linking against vendored libbpf sources
novendor = ["libbpf-sys/novendor"]
bpftool = []

[dependencies]
anyhow = "1.0.1"
Expand Down
21 changes: 21 additions & 0 deletions libbpf-cargo/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,13 +279,16 @@ pub fn build(

// Only used in libbpf-cargo library
#[allow(dead_code)]
#[allow(clippy::too_many_arguments)]
pub fn build_single(
debug: bool,
source: &Path,
out: &Path,
clang: Option<&PathBuf>,
skip_clang_version_checks: bool,
options: &str,
bpftool: Option<&Path>,
vmlinux_h: Option<&Path>,
) -> Result<()> {
let clang = extract_clang_or_default(clang);
check_clang(debug, &clang, skip_clang_version_checks)?;
Expand All @@ -297,6 +300,24 @@ pub fn build_single(
options.to_string()
};

if let Some(vmlinux_h) = vmlinux_h {
let file = fs::File::create(vmlinux_h)?;
let bpftool = bpftool.unwrap_or_else(|| Path::new("bpftool"));
Command::new(bpftool)
.arg("btf")
.arg("dump")
.arg("file")
.arg("/sys/kernel/btf/vmlinux")
.arg("format")
.arg("c")
.stdout(file)
.status()?;
compiler_options += &vmlinux_h
.parent()
.map(|dir| format!(" -I{}", dir.display()))
.ok_or_else(|| anyhow!("Failed to find include directory for vmlinux.h"))?;
}

// Explicitly disable stack protector logic, which doesn't work with
// BPF. See https://lkml.org/lkml/2020/2/21/1000.
compiler_options += " -fno-stack-protector";
Expand Down
42 changes: 38 additions & 4 deletions libbpf-cargo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ pub struct SkeletonBuilder {
clang: Option<PathBuf>,
clang_args: String,
skip_clang_version_check: bool,
bpftool: Option<PathBuf>,
extract_vmlinux_header: bool,
vmlinux_h: Option<PathBuf>,
rustfmt: PathBuf,
dir: Option<TempDir>,
}
Expand All @@ -139,6 +142,9 @@ impl SkeletonBuilder {
clang: None,
clang_args: String::new(),
skip_clang_version_check: false,
bpftool: None,
extract_vmlinux_header: false,
vmlinux_h: None,
rustfmt: "rustfmt".into(),
dir: None,
}
Expand Down Expand Up @@ -202,6 +208,22 @@ impl SkeletonBuilder {
self
}

/// Specify which `bpftool` binary to use
///
/// Default searchs `$PATH` for `bpftool`
pub fn bpftool<P: AsRef<Path>>(&mut self, bpftool: P) -> &mut SkeletonBuilder {
self.bpftool = Some(bpftool.as_ref().to_path_buf());
self
}

/// Tell whether to extract vmlinux.h from the running kernel
///
/// Default is off
pub fn extract_vmlinux_header(&mut self, extract: bool) -> &mut SkeletonBuilder {
self.extract_vmlinux_header = extract;
self
}

/// Specify which `rustfmt` binary to use
///
/// Default searches `$PATH` for `rustfmt`
Expand Down Expand Up @@ -240,15 +262,25 @@ impl SkeletonBuilder {
)));
}

if self.obj.is_none() {
let name = filename.split('.').next().unwrap();
if self.obj.is_none() || self.extract_vmlinux_header {
let dir = tempdir().map_err(|e| Error::Build(e.into()))?;
let objfile = dir.path().join(format!("{name}.o"));
self.obj = Some(objfile);
// Hold onto tempdir so that it doesn't get deleted early
self.dir = Some(dir);
}

if self.obj.is_none() {
let name = filename.split('.').next().unwrap();
// SANITY: `dir` is guaranteed to be `Some` at this point.
let objfile = self.dir.as_ref().unwrap().path().join(format!("{name}.o"));
self.obj = Some(objfile);
}

if self.extract_vmlinux_header {
// SANITY: `dir` is guaranteed to be `Some` at this point.
let vmlinux_h = self.dir.as_ref().unwrap().path().join("vmlinux.h");
self.vmlinux_h = Some(vmlinux_h);
}

build::build_single(
self.debug,
source,
Expand All @@ -257,6 +289,8 @@ impl SkeletonBuilder {
self.clang.as_ref(),
self.skip_clang_version_check,
&self.clang_args,
self.bpftool.as_deref(),
self.vmlinux_h.as_deref(),
)
.map_err(Error::Build)?;

Expand Down
41 changes: 41 additions & 0 deletions libbpf-cargo/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,47 @@ fn test_skeleton_builder_clang_opts() {
.unwrap();
}

#[cfg(feature = "bpftool")]
#[test]
fn test_skeleton_builder_bpftool() {
let (_dir, proj_dir, _cargo_toml) = setup_temp_project();

// Add prog dir
create_dir(proj_dir.join("src/bpf")).expect("failed to create prog dir");

// Add a prog
let mut prog = OpenOptions::new()
.write(true)
.create(true)
.open(proj_dir.join("src/bpf/prog.bpf.c"))
.expect("failed to open prog.bpf.c");

write!(
prog,
r#"
#include "vmlinux.h"
"#,
)
.expect("failed to write prog.bpf.c");

let skel = NamedTempFile::new().unwrap();

// Should fail b/c vmlinux.h is not present
SkeletonBuilder::new()
.source(proj_dir.join("src/bpf/prog.bpf.c"))
.debug(true)
.build_and_generate(skel.path())
.unwrap_err();

// Should succeed b/c vmlinux.h is extracted
SkeletonBuilder::new()
.source(proj_dir.join("src/bpf/prog.bpf.c"))
.debug(true)
.extract_vmlinux_header(true)
.build_and_generate(skel.path())
.unwrap();
}

#[test]
fn test_skeleton_builder_arrays_ptrs() {
let (_dir, proj_dir, cargo_toml) = setup_temp_project();
Expand Down