pub use bindgen::Builder;
use bindgen::{self, callbacks::ParseCallbacks};
use std::io::{self, Write};
use std::path::Path;
use std::process::Command;
use std::str;
pub use crate::accessors::generate_read_accessors;
use crate::build_constants::{kernel_headers, BUILD_FLAGS};
use crate::CommandError;
use bpf_sys::type_gen::vmlinux_btf_dump;
pub fn get_builder_kernel_headers() -> Result<Builder, String> {
let kernel_headers =
kernel_headers().or_else(|_| Err("kernel headers not found".to_string()))?;
let mut flags: Vec<String> = kernel_headers
.iter()
.map(|dir| format!("-I{}", dir))
.collect();
flags.extend(BUILD_FLAGS.iter().map(|f| f.to_string()));
Ok(bindgen::builder()
.clang_args(&flags)
.use_core()
.ctypes_prefix("::cty")
.opaque_type("xregs_state")
.parse_callbacks(Box::new(Callbacks)))
}
pub fn get_builder_vmlinux(c_dump_file: impl AsRef<Path>) -> Result<Builder, String> {
vmlinux_btf_dump()
.or_else(|e| Err(format!("error on vmlinux_btf_dump: {:?}", e)))?
.generate(c_dump_file.as_ref())
.or_else(|e| Err(format!("error on VmlinuxBtfDump::generate: {:?}", e)))?;
Ok(bindgen::builder()
.use_core()
.ctypes_prefix("::cty")
.parse_callbacks(Box::new(Callbacks))
.header(c_dump_file.as_ref().to_str().unwrap()))
}
pub fn builder() -> Builder {
get_builder_kernel_headers().unwrap()
}
pub fn generate(builder: &Builder, extra_args: &[&str]) -> Result<String, String> {
let mut bindgen_flags = builder.command_line_flags();
let p = bindgen_flags
.iter()
.position(|arg| arg == "--")
.unwrap_or(bindgen_flags.len() - 1);
for (i, flag) in extra_args.iter().enumerate() {
bindgen_flags.insert(p + i, String::from(*flag));
}
let output = Command::new("bindgen")
.args(bindgen_flags)
.output()
.expect("error running bindgen");
if !output.status.success() {
return Err(String::from_utf8(output.stderr).unwrap());
}
io::stderr().write_all(&output.stderr).unwrap();
let bindings = String::from_utf8(output.stdout).unwrap();
Ok(bindings)
}
pub fn cmd_bindgen(header: &Path, extra_args: &[&str]) -> Result<(), CommandError> {
let (_temp, header) = if !header.exists() {
let path = header.to_str().unwrap();
let mut file = tempfile::Builder::new().suffix(".h").tempfile().unwrap();
write!(
&mut file,
r#"
#define KBUILD_MODNAME "cargo_bpf_bindings"
#include <linux/kconfig.h>
#include <{}>
"#,
path
)
.unwrap();
let header = file.path().to_owned();
(Some(file), header)
} else {
(None, header.to_owned())
};
let builder = builder().header(header.to_str().unwrap());
let bindings = generate(&builder, extra_args).map_err(CommandError)?;
let mut out = io::stdout();
writeln!(
&mut out,
r"
mod generated_bindings {{
#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)]
#![allow(clippy::all)]
{}
}}
pub use generated_bindings::*;
",
bindings
)
.unwrap();
Ok(())
}
#[derive(Debug)]
struct Callbacks;
impl ParseCallbacks for Callbacks {
fn item_name(&self, name: &str) -> Option<String> {
match name {
"u8" | "u16" | "u32" | "u64" => Some(format!("_cargo_bpf_{}", name)),
_ => None,
}
}
}