use super::{Arg, ArgIntent}; use crate::bil::*; use crate::intermediate_representation::ExternSymbol as IrExternSymbol; use crate::prelude::*; #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)] pub struct ExternSymbol { pub tid: Tid, pub address: String, pub name: String, pub calling_convention: Option<String>, pub arguments: Vec<Arg>, } impl ExternSymbol { /// Returns the return register of an extern symbol. /// Returns an error if the function has not exactly one return argument /// or if the return argument is not a register. pub fn get_unique_return_register(&self) -> Result<&crate::bil::variable::Variable, Error> { let return_args: Vec<_> = self .arguments .iter() .filter(|arg| arg.intent.is_output()) .collect(); if return_args.len() != 1 { return Err(anyhow!( "Wrong number of return register: Got {}, expected 1", return_args.len() )); } match &return_args[0].location { Expression::Var(var) => Ok(var), _ => Err(anyhow!("Return location is not a register")), } } /// Returns the parameter expression of an extern symbol. /// Returns an error if the function has not exactly one parameter argument. pub fn get_unique_parameter(&self) -> Result<&crate::bil::Expression, Error> { let param_args: Vec<_> = self .arguments .iter() .filter(|arg| arg.intent.is_input()) .collect(); if param_args.len() != 1 { return Err(anyhow!( "Wrong number of return register: Got {}, expected 1", param_args.len() )); } Ok(¶m_args[0].location) } } impl From<ExternSymbol> for IrExternSymbol { fn from(symbol: ExternSymbol) -> IrExternSymbol { let mut parameters = Vec::new(); let mut return_values = Vec::new(); for arg in symbol.arguments.into_iter() { if matches!( arg.intent, ArgIntent::Input | ArgIntent::Both | ArgIntent::Unknown ) { for ir_arg in arg.clone().into_ir_args() { parameters.push(ir_arg); } } if matches!( arg.intent, ArgIntent::Output | ArgIntent::Both | ArgIntent::Unknown ) { for ir_arg in arg.into_ir_args() { return_values.push(ir_arg); } } } IrExternSymbol { tid: symbol.tid, name: symbol.name, calling_convention: None, // We do not parse more than one calling convention from BAP at the moment. So we assume everything uses the standard one. parameters, return_values, no_return: false, // Last time I checked BAP had an attribute for non-returning functions, but did not actually set it. } } } #[cfg(test)] mod tests { use super::*; #[test] fn extern_symbol_serialization() { let symbol = ExternSymbol { tid: Tid::new("Tid"), address: "Somewhere".to_string(), name: "extern_fn".to_string(), calling_convention: Some("cconv".to_string()), arguments: Vec::new(), }; let json: String = serde_json::to_string_pretty(&symbol).unwrap(); println!("{}", json); let _symbol: ExternSymbol = serde_json::from_str(&json).unwrap(); } }