1pub use bindgen::Builder;
9use bindgen::{self, callbacks::ParseCallbacks};
10use std::io::{self, Write};
11use std::path::Path;
12use std::process::Command;
13use std::str;
14
15pub use crate::accessors::generate_read_accessors;
16use crate::build_constants::{kernel_headers, BUILD_FLAGS};
17use crate::CommandError;
18use bpf_sys::type_gen::vmlinux_btf_dump;
19
20pub fn get_builder_kernel_headers() -> Result<Builder, String> {
27 let kernel_headers =
28 kernel_headers().or_else(|_| Err("kernel headers not found".to_string()))?;
29 let mut flags: Vec<String> = kernel_headers
30 .iter()
31 .map(|dir| format!("-I{}", dir))
32 .collect();
33 flags.extend(BUILD_FLAGS.iter().map(|f| f.to_string()));
34
35 Ok(bindgen::builder()
36 .clang_args(&flags)
37 .use_core()
38 .ctypes_prefix("::cty")
39 .opaque_type("xregs_state")
40 .parse_callbacks(Box::new(Callbacks)))
41}
42
43pub fn get_builder_vmlinux(c_dump_file: impl AsRef<Path>) -> Result<Builder, String> {
53 vmlinux_btf_dump()
54 .or_else(|e| Err(format!("error on vmlinux_btf_dump: {:?}", e)))?
55 .generate(c_dump_file.as_ref())
56 .or_else(|e| Err(format!("error on VmlinuxBtfDump::generate: {:?}", e)))?;
57
58 Ok(bindgen::builder()
59 .use_core()
60 .ctypes_prefix("::cty")
61 .parse_callbacks(Box::new(Callbacks))
62 .header(c_dump_file.as_ref().to_str().unwrap()))
63}
64
65pub fn builder() -> Builder {
66 get_builder_kernel_headers().unwrap()
67}
68
69pub fn generate(builder: &Builder, extra_args: &[&str]) -> Result<String, String> {
70 let mut bindgen_flags = builder.command_line_flags();
71 let p = bindgen_flags
72 .iter()
73 .position(|arg| arg == "--")
74 .unwrap_or(bindgen_flags.len() - 1);
75 for (i, flag) in extra_args.iter().enumerate() {
76 bindgen_flags.insert(p + i, String::from(*flag));
77 }
78 let output = Command::new("bindgen")
79 .args(bindgen_flags)
80 .output()
81 .expect("error running bindgen");
82 if !output.status.success() {
83 return Err(String::from_utf8(output.stderr).unwrap());
84 }
85 io::stderr().write_all(&output.stderr).unwrap();
86 let bindings = String::from_utf8(output.stdout).unwrap();
87 Ok(bindings)
88}
89
90pub fn cmd_bindgen(header: &Path, extra_args: &[&str]) -> Result<(), CommandError> {
91 let (_temp, header) = if !header.exists() {
92 let path = header.to_str().unwrap();
94 let mut file = tempfile::Builder::new().suffix(".h").tempfile().unwrap();
95 write!(
96 &mut file,
97 r#"
98#define KBUILD_MODNAME "cargo_bpf_bindings"
99#include <linux/kconfig.h>
100#include <{}>
101 "#,
102 path
103 )
104 .unwrap();
105 let header = file.path().to_owned();
106 (Some(file), header)
107 } else {
108 (None, header.to_owned())
109 };
110
111 let builder = builder().header(header.to_str().unwrap());
112 let bindings = generate(&builder, extra_args).map_err(CommandError)?;
113 let mut out = io::stdout();
114 writeln!(
115 &mut out,
116 r"
117mod generated_bindings {{
118#![allow(non_camel_case_types)]
119#![allow(non_upper_case_globals)]
120#![allow(clippy::all)]
121{}
122}}
123pub use generated_bindings::*;
124",
125 bindings
126 )
127 .unwrap();
128
129 Ok(())
130}
131
132#[derive(Debug)]
133struct Callbacks;
134
135impl ParseCallbacks for Callbacks {
136 fn item_name(&self, name: &str) -> Option<String> {
137 match name {
138 "u8" | "u16" | "u32" | "u64" => Some(format!("_cargo_bpf_{}", name)),
139 _ => None,
140 }
141 }
142}