cargo_bpf_lib/
bindgen.rs

1// Copyright 2019 Authors of Red Sift
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8pub 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
20/// Get `bindgen::Builder` that generates bindings using pre-installed kernel
21/// headers
22///
23/// Necessary include-directories for kernel headers are set to
24/// `bindgen::Builder`. So that users of this function can include kernel
25/// headers.
26pub 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
43/// Get `bindgen::Builder` that generates bindings using BTF of vmlinux
44///
45/// `c_dump_file` is a path of C header file where structs and enums will be
46/// written by
47/// [`VmlinuxBtfDump`](../../bpf-sys/type-gen/struct.VmlinuxBtfDump.html). It
48/// is just a temporary file that will be read by `bindgen` to generate rust
49/// bindings in the end. The file name of `c_dump_file` is used as an
50/// include-guard. e.g., if `c_dump_file` is "vmlinux.h" then the guard macro
51/// is `__VMLINUX_H__`
52pub 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        // try to find find the file in the kernel include path
93        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}