use std::path::PathBuf; use anyhow::anyhow; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use structopt::StructOpt; use syn::{ self, parse_str, punctuated::Punctuated, token::Comma, visit_mut::{self, VisitMut}, AngleBracketedGenericArguments, ForeignItemStatic, GenericArgument, Ident, Item, Path, PathArguments::AngleBracketed, Type, }; use aya_gen::getters::{generate_getters_for_items, probe_read_getter}; use crate::codegen::{ bindings::{self, bindgen}, Architecture, }; #[derive(StructOpt)] pub struct CodegenOptions { #[structopt(long)] arch: Architecture, #[structopt(long)] libbpf_dir: PathBuf, } pub fn codegen(opts: CodegenOptions) -> Result<(), anyhow::Error> { let dir = PathBuf::from("bpf/aya-bpf-bindings"); let generated = dir.join("src").join(opts.arch.to_string()); let types = ["bpf_map_.*"]; let vars = ["BPF_.*", "bpf_.*"]; let mut cmd = bindgen(&types, &vars); cmd.arg(&*dir.join("include/bindings.h").to_string_lossy()); cmd.arg("--"); cmd.arg("-I").arg(opts.libbpf_dir.join("src")); let output = cmd.output()?; let bindings = std::str::from_utf8(&output.stdout)?; if !output.status.success() { eprintln!("{}", std::str::from_utf8(&output.stderr)?); return Err(anyhow!("bindgen failed: {}", output.status)); } // delete the helpers, then rewrite them in helpers.rs let mut tree = parse_str::(bindings).unwrap(); let mut tx = RewriteBpfHelpers { helpers: Vec::new(), }; tx.visit_file_mut(&mut tree); bindings::write( &tree.to_token_stream().to_string(), "", &generated.join("bindings.rs"), )?; bindings::write( &tx.helpers.join(""), "use super::bindings::*;", &generated.join("helpers.rs"), )?; let bpf_probe_read = syn::parse_str::("crate::bpf_probe_read").unwrap(); bindings::write( &generate_getters_for_items(&tree.items, |getter| { probe_read_getter(getter, &bpf_probe_read) }) .to_string(), "use super::bindings::*;", &generated.join("getters.rs"), )?; Ok(()) } struct RewriteBpfHelpers { helpers: Vec, } impl VisitMut for RewriteBpfHelpers { fn visit_item_mut(&mut self, item: &mut Item) { visit_mut::visit_item_mut(self, item); if let Item::ForeignMod(_) = item { *item = Item::Verbatim(TokenStream::new()) } } fn visit_foreign_item_static_mut(&mut self, static_item: &mut ForeignItemStatic) { if let Type::Path(path) = &*static_item.ty { let ident = &static_item.ident; let ident_str = ident.to_string(); let last = path.path.segments.last().unwrap(); let ty_ident = last.ident.to_string(); if ident_str.starts_with("bpf_") && ty_ident == "Option" { let fn_ty = match &last.arguments { AngleBracketed(AngleBracketedGenericArguments { args, .. }) => { args.first().unwrap() } _ => panic!(), }; let mut ty_s = quote! { #[inline(always)] pub #fn_ty } .to_string(); ty_s = ty_s.replace("fn (", &format!("fn {} (", ident_str)); let call_idx = self.helpers.len() + 1; let args: Punctuated = match fn_ty { GenericArgument::Type(Type::BareFn(f)) => f .inputs .iter() .map(|arg| arg.name.clone().unwrap().0) .collect(), _ => unreachable!(), }; let body = quote! { { let f: #fn_ty = ::core::mem::transmute(#call_idx); f(#args) } } .to_string(); ty_s.push_str(&body); let mut helper = ty_s; if helper.contains("printk") { helper = format!("/* {} */", helper); } self.helpers.push(helper); } } } }