Unverified Commit decc1254 by van den Bosch Committed by GitHub

Test refactoring collection (#386)

parent dcbdac1e
...@@ -186,12 +186,12 @@ fn extract_results<'a>( ...@@ -186,12 +186,12 @@ fn extract_results<'a>(
/// ///
/// This uses the expression propagation of basic blocks, thus performs intra-basic-block insertion of expressions. /// This uses the expression propagation of basic blocks, thus performs intra-basic-block insertion of expressions.
fn insert_expressions( fn insert_expressions(
inseratables: HashMap<Tid, HashMap<Variable, Expression>>, insertables: HashMap<Tid, HashMap<Variable, Expression>>,
program: &mut Program, program: &mut Program,
) { ) {
for sub in program.subs.values_mut() { for sub in program.subs.values_mut() {
for block in sub.term.blocks.iter_mut() { for block in sub.term.blocks.iter_mut() {
block.propagate_input_expressions(inseratables.get(&block.tid).cloned()); propagate_input_expressions(block, insertables.get(&block.tid).cloned());
} }
} }
} }
...@@ -200,11 +200,132 @@ fn insert_expressions( ...@@ -200,11 +200,132 @@ fn insert_expressions(
fn merge_same_var_assignments(project: &mut Project) { fn merge_same_var_assignments(project: &mut Project) {
for sub in project.program.term.subs.values_mut() { for sub in project.program.term.subs.values_mut() {
for blk in sub.term.blocks.iter_mut() { for blk in sub.term.blocks.iter_mut() {
blk.merge_def_assignments_to_same_var(); merge_def_assignments_to_same_var(blk);
} }
} }
} }
/// Wherever possible, substitute input variables of expressions
/// with the input expression that defines the input variable.
///
/// Note that substitution is only possible
/// if the input variables of the input expression itself did not change since the definition of said variable.
///
/// The expression propagation allows more dead stores to be removed during
/// [dead variable elimination](crate::analysis::dead_variable_elimination).
pub fn propagate_input_expressions(
blk: &mut Term<Blk>,
apriori_insertable_expressions: Option<HashMap<Variable, Expression>>,
) {
let mut insertable_expressions = HashMap::new();
if let Some(insertables) = apriori_insertable_expressions {
insertable_expressions = insertables;
}
for def in blk.term.defs.iter_mut() {
match &mut def.term {
Def::Assign {
var,
value: expression,
} => {
// insert known input expressions
for (input_var, input_expr) in insertable_expressions.iter() {
expression.substitute_input_var(input_var, input_expr);
}
// expressions dependent on the assigned variable are no longer insertable
insertable_expressions.retain(|input_var, input_expr| {
input_var != var && !input_expr.input_vars().into_iter().any(|x| x == var)
});
// If the value of the assigned variable does not depend on the former value of the variable,
// then it is insertable for future expressions.
if !expression.input_vars().into_iter().any(|x| x == var) {
insertable_expressions.insert(var.clone(), expression.clone());
}
}
Def::Load {
var,
address: expression,
} => {
// insert known input expressions
for (input_var, input_expr) in insertable_expressions.iter() {
expression.substitute_input_var(input_var, input_expr);
}
// expressions dependent on the assigned variable are no longer insertable
insertable_expressions.retain(|input_var, input_expr| {
input_var != var && !input_expr.input_vars().into_iter().any(|x| x == var)
});
}
Def::Store { address, value } => {
// insert known input expressions
for (input_var, input_expr) in insertable_expressions.iter() {
address.substitute_input_var(input_var, input_expr);
value.substitute_input_var(input_var, input_expr);
}
}
}
}
for jump in blk.term.jmps.iter_mut() {
match &mut jump.term {
Jmp::Branch(_) | Jmp::Call { .. } | Jmp::CallOther { .. } => (),
Jmp::BranchInd(expr)
| Jmp::CBranch {
condition: expr, ..
}
| Jmp::CallInd { target: expr, .. }
| Jmp::Return(expr) => {
// insert known input expressions
for (input_var, input_expr) in insertable_expressions.iter() {
expr.substitute_input_var(input_var, input_expr);
}
}
}
}
}
/// Merge subsequent assignments to the same variable to a single assignment to that variable.
pub fn merge_def_assignments_to_same_var(blk: &mut Term<Blk>) {
let mut new_defs = Vec::new();
let mut last_def_opt = None;
for def in blk.term.defs.iter() {
if let Def::Assign {
var: current_var, ..
} = &def.term
{
if let Some(Term {
term:
Def::Assign {
var: last_var,
value: last_value,
},
..
}) = &last_def_opt
{
if current_var == last_var {
let mut substituted_def = def.clone();
substituted_def.substitute_input_var(last_var, last_value);
last_def_opt = Some(substituted_def);
} else {
new_defs.push(last_def_opt.unwrap());
last_def_opt = Some(def.clone());
}
} else if last_def_opt.is_some() {
panic!(); // Only assign-defs should be saved in last_def.
} else {
last_def_opt = Some(def.clone());
}
} else {
if let Some(last_def) = last_def_opt {
new_defs.push(last_def);
}
new_defs.push(def.clone());
last_def_opt = None;
}
}
if let Some(last_def) = last_def_opt {
new_defs.push(last_def);
}
blk.term.defs = new_defs;
}
/// Replaces variables by expressions that can be propagated within functions. /// Replaces variables by expressions that can be propagated within functions.
/// ///
/// This is performed by a fixpoint computation and might panic, if it does not stabilize. /// This is performed by a fixpoint computation and might panic, if it does not stabilize.
......
...@@ -95,6 +95,32 @@ fn get_mock_entry_block() -> Term<Blk> { ...@@ -95,6 +95,32 @@ fn get_mock_entry_block() -> Term<Blk> {
} }
#[test] #[test]
fn expression_propagation() {
use super::*;
let defs = defs![
"tid_1: X:8 = -(Y:8)",
"tid_2: Y:8 = X:8 + Y:8",
"tid_3: X:8 = -(X:8)",
"tid_4: Y:8 = -(Y:8)",
"tid_5: Y:8 = X:8 + Y:8"
];
let block = &mut Blk::mock();
block.term.defs = defs;
merge_def_assignments_to_same_var(block);
propagate_input_expressions(block, None);
let result_defs = defs![
"tid_1: X:8 = -(Y:8)",
"tid_2: Y:8 = -(Y:8) + Y:8",
"tid_3: X:8 = -(X:8)",
"tid_5: Y:8 = X:8 + -(Y:8)"
];
assert_eq!(block.term.defs, result_defs);
}
#[test]
/// Tests the propagation of insertable expressions among basic blocks. /// Tests the propagation of insertable expressions among basic blocks.
fn inter_block_propagation() { fn inter_block_propagation() {
let mut project = mock_project(); let mut project = mock_project();
......
...@@ -333,6 +333,7 @@ mod tests { ...@@ -333,6 +333,7 @@ mod tests {
create_computation_with_top_down_worklist_order, create_computation_with_top_down_worklist_order,
}, },
}, },
expr,
intermediate_representation::*, intermediate_representation::*,
}; };
use std::collections::{BTreeMap, HashMap, HashSet}; use std::collections::{BTreeMap, HashMap, HashSet};
...@@ -353,7 +354,7 @@ mod tests { ...@@ -353,7 +354,7 @@ mod tests {
let mut callee_block = new_block("callee block"); let mut callee_block = new_block("callee block");
callee_block.term.jmps.push(Term { callee_block.term.jmps.push(Term {
tid: Tid::new("ret"), tid: Tid::new("ret"),
term: Jmp::Return(Expression::const_from_i32(42)), term: Jmp::Return(expr!("42:4")),
}); });
let called_function = Term { let called_function = Term {
......
...@@ -235,14 +235,14 @@ pub mod tests { ...@@ -235,14 +235,14 @@ pub mod tests {
// main -> callee1 -> callee2 // main -> callee1 -> callee2
let mut func = Sub::mock("main"); let mut func = Sub::mock("main");
let mut call_blk = Blk::mock_with_tid("main_blk"); let mut call_blk = Blk::mock_with_tid("main_blk");
let call = Jmp::mock_call("callee1", None); let call = Jmp::call("call_callee1", "callee1", None);
call_blk.term.jmps.push(call); call_blk.term.jmps.push(call);
func.term.blocks.push(call_blk); func.term.blocks.push(call_blk);
project.program.term.subs.insert(Tid::new("main"), func); project.program.term.subs.insert(Tid::new("main"), func);
let mut func = Sub::mock("callee1"); let mut func = Sub::mock("callee1");
let mut call_blk = Blk::mock_with_tid("callee1_blk"); let mut call_blk = Blk::mock_with_tid("callee1_blk");
let call = Jmp::mock_call("callee2", None); let call = Jmp::call("call_callee2", "callee2", None);
call_blk.term.jmps.push(call); call_blk.term.jmps.push(call);
func.term.blocks.push(call_blk); func.term.blocks.push(call_blk);
project.program.term.subs.insert(Tid::new("callee1"), func); project.program.term.subs.insert(Tid::new("callee1"), func);
......
...@@ -512,6 +512,8 @@ pub fn get_entry_nodes_of_subs(graph: &Graph) -> HashMap<Tid, NodeIndex> { ...@@ -512,6 +512,8 @@ pub fn get_entry_nodes_of_subs(graph: &Graph) -> HashMap<Tid, NodeIndex> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::expr;
use super::*; use super::*;
use std::collections::{BTreeMap, BTreeSet}; use std::collections::{BTreeMap, BTreeSet};
use std::iter::FromIterator; use std::iter::FromIterator;
...@@ -526,7 +528,7 @@ mod tests { ...@@ -526,7 +528,7 @@ mod tests {
}; };
let return_term = Term { let return_term = Term {
tid: Tid::new("return".to_string()), tid: Tid::new("return".to_string()),
term: Jmp::Return(Expression::Const(Bitvector::zero(64.into()))), // The return term does not matter term: Jmp::Return(expr!("0:8")), // The return term does not matter
}; };
let jmp = Jmp::Branch(Tid::new("sub1_blk1")); let jmp = Jmp::Branch(Tid::new("sub1_blk1"));
let jmp_term = Term { let jmp_term = Term {
...@@ -559,7 +561,7 @@ mod tests { ...@@ -559,7 +561,7 @@ mod tests {
}; };
let cond_jump = Jmp::CBranch { let cond_jump = Jmp::CBranch {
target: Tid::new("sub1_blk1"), target: Tid::new("sub1_blk1"),
condition: Expression::Const(Bitvector::from_u8(0)), condition: expr!("0:1"),
}; };
let cond_jump_term = Term { let cond_jump_term = Term {
tid: Tid::new("cond_jump"), tid: Tid::new("cond_jump"),
...@@ -618,7 +620,7 @@ mod tests { ...@@ -618,7 +620,7 @@ mod tests {
fn add_indirect_jumps() { fn add_indirect_jumps() {
let indirect_jmp_term = Term { let indirect_jmp_term = Term {
tid: Tid::new("indrect_jmp".to_string()), tid: Tid::new("indrect_jmp".to_string()),
term: Jmp::BranchInd(Expression::Const(Bitvector::from_u32(0x1000))), // At the moment the expression does not matter term: Jmp::BranchInd(expr!("0x1000:4")), // At the moment the expression does not matter
}; };
let mut blk_tid = Tid::new("blk_00001000"); let mut blk_tid = Tid::new("blk_00001000");
blk_tid.address = "00001000".to_string(); blk_tid.address = "00001000".to_string();
......
use super::super::ValueDomain; use super::super::ValueDomain;
use super::*; use super::*;
use crate::{bitvec, def, expr, variable};
fn bv(value: i64) -> ValueDomain { fn bv(value: i64) -> ValueDomain {
ValueDomain::from(Bitvector::from_i64(value)) ValueDomain::from(bitvec!(format!("{value}:8")))
} }
fn new_id(time: &str, reg_name: &str) -> AbstractIdentifier { fn new_id(time: &str, reg_name: &str) -> AbstractIdentifier {
AbstractIdentifier::new( AbstractIdentifier::new(
Tid::new(time), Tid::new(time),
AbstractLocation::Register(Variable::mock(reg_name, ByteSize::new(8))), AbstractLocation::Register(variable!(format!("{reg_name}:8"))),
) )
} }
fn register(name: &str) -> Variable {
Variable {
name: name.into(),
size: ByteSize::new(8),
is_temp: false,
}
}
fn reg_add_term(name: &str, value: i64, tid_name: &str) -> Term<Def> {
let add_expr = Expression::Var(register(name)).plus_const(value);
Term {
tid: Tid::new(format!("{}", tid_name)),
term: Def::Assign {
var: register(name),
value: add_expr,
},
}
}
fn call_term(target_name: &str) -> Term<Jmp> { fn call_term(target_name: &str) -> Term<Jmp> {
Term { Term {
tid: Tid::new(format!("call_{}", target_name)), tid: Tid::new(format!("call_{}", target_name)),
...@@ -87,67 +69,56 @@ fn mock_context() -> Context<'static> { ...@@ -87,67 +69,56 @@ fn mock_context() -> Context<'static> {
fn context_problem_implementation() { fn context_problem_implementation() {
use crate::analysis::forward_interprocedural_fixpoint::Context as IpFpContext; use crate::analysis::forward_interprocedural_fixpoint::Context as IpFpContext;
use crate::analysis::pointer_inference::Data; use crate::analysis::pointer_inference::Data;
use Expression::*;
let context = mock_context(); let context = mock_context();
let mut state = State::new(&register("RSP"), Tid::new("main"), BTreeSet::new()); let mut state = State::new(&variable!("RSP:8"), Tid::new("main"), BTreeSet::new());
let def = Term { let def = def!["def: RSP:8 = RSP:8 + -16:8"];
tid: Tid::new("def"), let store_term = def!["Store at RSP:8 := 43:8"];
term: Def::Assign {
var: register("RSP"),
value: Var(register("RSP")).plus_const(-16),
},
};
let store_term = Term {
tid: Tid::new("store"),
term: Def::Store {
address: Var(register("RSP")),
value: Const(Bitvector::from_i64(42)),
},
};
// test update_def // test update_def
state = context.update_def(&state, &def).unwrap(); state = context.update_def(&state, &def).unwrap();
let stack_pointer = Data::from_target(new_id("main", "RSP"), bv(-16)); let stack_pointer = Data::from_target(new_id("main", "RSP"), bv(-16));
assert_eq!(state.eval(&Var(register("RSP"))), stack_pointer); assert_eq!(state.eval(&expr!("RSP:8")), stack_pointer);
state = context.update_def(&state, &store_term).unwrap(); state = context.update_def(&state, &store_term).unwrap();
// Test extern function handling // Test extern function handling
state.set_register(&register("RBP"), bv(13).into()); state.set_register(&variable!("RBP:8"), bv(13).into());
state.set_register(&register("RSI"), bv(14).into()); state.set_register(&variable!("RSI:8"), bv(14).into());
let malloc = call_term("malloc"); let malloc = call_term("malloc");
let mut state_after_malloc = context.update_call_stub(&state, &malloc).unwrap(); let mut state_after_malloc = context.update_call_stub(&state, &malloc).unwrap();
assert_eq!( assert_eq!(
state_after_malloc.get_register(&register("RAX")), state_after_malloc.get_register(&variable!("RAX:8")),
Data::from_target(new_id("call_malloc", "RAX"), bv(0)) Data::from_target(new_id("call_malloc", "RAX"), bv(0))
); );
assert_eq!(state_after_malloc.memory.get_num_objects(), 3); assert_eq!(state_after_malloc.memory.get_num_objects(), 3);
assert_eq!( assert_eq!(
state_after_malloc.get_register(&register("RSP")), state_after_malloc.get_register(&variable!("RSP:8")),
state state
.get_register(&register("RSP")) .get_register(&variable!("RSP:8"))
.bin_op(BinOpType::IntAdd, &bv(8).into()) .bin_op(BinOpType::IntAdd, &bv(8).into())
); );
assert_eq!( assert_eq!(
state_after_malloc.get_register(&register("RBP")), state_after_malloc.get_register(&variable!("RBP:8")),
bv(13).into() bv(13).into()
); );
assert!(state_after_malloc.get_register(&register("RSI")).is_top()); assert!(state_after_malloc
.get_register(&variable!("RSI:8"))
.is_top());
state_after_malloc.set_register( state_after_malloc.set_register(
&register("RBP"), &variable!("RBP:8"),
Data::from_target(new_id("call_malloc", "RAX"), bv(0)), Data::from_target(new_id("call_malloc", "RAX"), bv(0)),
); );
let free = call_term("free"); let free = call_term("free");
let state_after_free = context let state_after_free = context
.update_call_stub(&state_after_malloc, &free) .update_call_stub(&state_after_malloc, &free)
.unwrap(); .unwrap();
assert!(state_after_free.get_register(&register("RDX")).is_top()); assert!(state_after_free.get_register(&variable!("RDX:8")).is_top());
assert_eq!(state_after_free.memory.get_num_objects(), 3); assert_eq!(state_after_free.memory.get_num_objects(), 3);
assert_eq!( assert_eq!(
state_after_free.get_register(&register("RBP")), state_after_free.get_register(&variable!("RBP:8")),
Data::from_target(new_id("call_malloc", "RAX"), bv(0)) Data::from_target(new_id("call_malloc", "RAX"), bv(0))
); );
...@@ -155,16 +126,18 @@ fn context_problem_implementation() { ...@@ -155,16 +126,18 @@ fn context_problem_implementation() {
let state_after_other_fn = context.update_call_stub(&state, &other_extern_fn).unwrap(); let state_after_other_fn = context.update_call_stub(&state, &other_extern_fn).unwrap();
assert_eq!( assert_eq!(
state_after_other_fn.get_register(&register("RSP")), state_after_other_fn.get_register(&variable!("RSP:8")),
state state
.get_register(&register("RSP")) .get_register(&variable!("RSP:8"))
.bin_op(BinOpType::IntAdd, &bv(8).into()) .bin_op(BinOpType::IntAdd, &bv(8).into())
); );
assert_eq!( assert_eq!(
state_after_other_fn.get_register(&register("RBP")), state_after_other_fn.get_register(&variable!("RBP:8")),
bv(13).into() bv(13).into()
); );
assert!(state_after_other_fn.get_register(&register("RSI")).is_top()); assert!(state_after_other_fn
.get_register(&variable!("RSI:8"))
.is_top());
} }
#[test] #[test]
...@@ -176,13 +149,13 @@ fn update_return() { ...@@ -176,13 +149,13 @@ fn update_return() {
let callee_tid = Tid::new("callee"); let callee_tid = Tid::new("callee");
let state_before_return = State::from_fn_sig( let state_before_return = State::from_fn_sig(
context.fn_signatures.get(&callee_tid).unwrap(), context.fn_signatures.get(&callee_tid).unwrap(),
&register("RSP"), &variable!("RSP:8"),
callee_tid.clone(), callee_tid.clone(),
); );
let mut state_before_return = context let mut state_before_return = context
.update_def( .update_def(
&state_before_return, &state_before_return,
&reg_add_term("RSP", 8, "stack_offset_on_return_adjustment"), &def!["stack_offset_on_return_adjustment: RSP:8 = RSP:8 + 8:8"],
) )
.unwrap(); .unwrap();
...@@ -193,19 +166,19 @@ fn update_return() { ...@@ -193,19 +166,19 @@ fn update_return() {
Some(ObjectType::Heap), Some(ObjectType::Heap),
); );
state_before_return.set_register( state_before_return.set_register(
&register("RAX"), &variable!("RAX:8"),
Data::from_target(callee_created_heap_id.clone(), bv(16)), Data::from_target(callee_created_heap_id.clone(), bv(16)),
); );
state_before_return.set_register( state_before_return.set_register(
&register("RDX"), &variable!("RDX:8"),
Data::from_target(new_id("callee", "RDI"), bv(0)), Data::from_target(new_id("callee", "RDI"), bv(0)),
); );
let state_before_call = State::new(&register("RSP"), Tid::new("caller"), BTreeSet::new()); let state_before_call = State::new(&variable!("RSP:8"), Tid::new("caller"), BTreeSet::new());
let mut state_before_call = context let mut state_before_call = context
.update_def( .update_def(
&state_before_call, &state_before_call,
&reg_add_term("RSP", -16, "stack_offset_on_call_adjustment"), &def!["stack_offset_on_call_adjustment: RSP:8 = RSP:8 + -16:8"],
) )
.unwrap(); .unwrap();
let param_obj_id = new_id("caller_created_heap", "RAX"); let param_obj_id = new_id("caller_created_heap", "RAX");
...@@ -215,11 +188,11 @@ fn update_return() { ...@@ -215,11 +188,11 @@ fn update_return() {
Some(ObjectType::Heap), Some(ObjectType::Heap),
); );
state_before_call.set_register( state_before_call.set_register(
&register("RDI"), &variable!("RDI:8"),
Data::from_target(param_obj_id.clone(), bv(0).into()), Data::from_target(param_obj_id.clone(), bv(0).into()),
); );
state_before_call.set_register( state_before_call.set_register(
&register("RBX"), &variable!("RBX:8"),
Data::from_target(param_obj_id.clone(), bv(0).into()), Data::from_target(param_obj_id.clone(), bv(0).into()),
); );
...@@ -235,7 +208,7 @@ fn update_return() { ...@@ -235,7 +208,7 @@ fn update_return() {
assert_eq!(state.stack_id, new_id("caller", "RSP")); assert_eq!(state.stack_id, new_id("caller", "RSP"));
assert_eq!( assert_eq!(
state.get_register(&register("RAX")), state.get_register(&variable!("RAX:8")),
Data::from_target( Data::from_target(
callee_created_heap_id callee_created_heap_id
.with_path_hint(Tid::new("call_callee")) .with_path_hint(Tid::new("call_callee"))
...@@ -244,15 +217,15 @@ fn update_return() { ...@@ -244,15 +217,15 @@ fn update_return() {
) )
); );
assert_eq!( assert_eq!(
state.get_register(&register("RBX")), state.get_register(&variable!("RBX:8")),
Data::from_target(param_obj_id.clone(), bv(0).into()) Data::from_target(param_obj_id.clone(), bv(0).into())
); );
assert_eq!( assert_eq!(
state.get_register(&register("RDX")), state.get_register(&variable!("RDX:8")),
Data::from_target(param_obj_id.clone(), bv(0).into()) Data::from_target(param_obj_id.clone(), bv(0).into())
); );
assert_eq!( assert_eq!(
state.get_register(&register("RSP")), state.get_register(&variable!("RSP:8")),
Data::from_target(new_id("caller", "RSP"), bv(-8).into()) Data::from_target(new_id("caller", "RSP"), bv(-8).into())
); );
assert_eq!(state.memory.get_all_object_ids().len(), 4); assert_eq!(state.memory.get_all_object_ids().len(), 4);
...@@ -280,13 +253,13 @@ fn specialize_conditional() { ...@@ -280,13 +253,13 @@ fn specialize_conditional() {
let analysis_results = AnalysisResults::mock_from_project(&project); let analysis_results = AnalysisResults::mock_from_project(&project);
let context = Context::new(&analysis_results, config, log_sender); let context = Context::new(&analysis_results, config, log_sender);
let mut state = State::new(&register("RSP"), Tid::new("func"), BTreeSet::new()); let mut state = State::new(&variable!("RSP:8"), Tid::new("func"), BTreeSet::new());
state.set_register(&register("RAX"), IntervalDomain::mock(-10, 20).into()); state.set_register(&variable!("RAX:8"), IntervalDomain::mock(-10, 20).into());
let condition = Expression::BinOp { let condition = Expression::BinOp {
lhs: Box::new(Expression::Var(register("RAX"))), lhs: Box::new(expr!("RAX:8")),
op: BinOpType::IntSLessEqual, op: BinOpType::IntSLessEqual,
rhs: Box::new(Expression::Const(Bitvector::zero(ByteSize::new(8).into()))), rhs: Box::new(expr!("0:8")),
}; };
let block = Blk::mock(); let block = Blk::mock();
...@@ -294,20 +267,20 @@ fn specialize_conditional() { ...@@ -294,20 +267,20 @@ fn specialize_conditional() {
.specialize_conditional(&state, &condition, &block, false) .specialize_conditional(&state, &condition, &block, false)
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
result.get_register(&register("RAX")), result.get_register(&variable!("RAX:8")),
IntervalDomain::mock(1, 20).into() IntervalDomain::mock(1, 20).into()
); );
state.set_register(&register("RAX"), IntervalDomain::mock(0, 20).into()); state.set_register(&variable!("RAX:8"), IntervalDomain::mock(0, 20).into());
let result = context let result = context
.specialize_conditional(&state, &condition, &block, true) .specialize_conditional(&state, &condition, &block, true)
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
result.get_register(&register("RAX")), result.get_register(&variable!("RAX:8")),
IntervalDomain::mock_with_bounds(None, 0, 0, None).into() IntervalDomain::mock_with_bounds(None, 0, 0, None).into()
); );
state.set_register(&register("RAX"), IntervalDomain::mock(-20, 0).into()); state.set_register(&variable!("RAX:8"), IntervalDomain::mock(-20, 0).into());
let result = context.specialize_conditional(&state, &condition, &block, false); let result = context.specialize_conditional(&state, &condition, &block, false);
assert!(result.is_none()); assert!(result.is_none());
} }
...@@ -328,7 +301,7 @@ fn get_unsound_caller_ids() { ...@@ -328,7 +301,7 @@ fn get_unsound_caller_ids() {
let callee_tid = Tid::new("callee"); let callee_tid = Tid::new("callee");
let callee_state = State::from_fn_sig( let callee_state = State::from_fn_sig(
context.fn_signatures.get(&callee_tid).unwrap(), context.fn_signatures.get(&callee_tid).unwrap(),
&register("RSP"), &variable!("RSP:8"),
callee_tid.clone(), callee_tid.clone(),
); );
let callee_id_to_access_pattern_map = context.create_id_to_access_pattern_map(&callee_state); let callee_id_to_access_pattern_map = context.create_id_to_access_pattern_map(&callee_state);
...@@ -352,10 +325,10 @@ fn handle_extern_symbol_stubs() { ...@@ -352,10 +325,10 @@ fn handle_extern_symbol_stubs() {
extern_symbol.parameters = vec![Arg::mock_register("RDI", 8), Arg::mock_register("RSI", 8)]; extern_symbol.parameters = vec![Arg::mock_register("RDI", 8), Arg::mock_register("RSI", 8)];
state.set_register( state.set_register(
&Variable::mock("RDI", 8), &variable!("RDI:8"),
Data::from_target( Data::from_target(
AbstractIdentifier::mock("param", "RBX", 8), AbstractIdentifier::mock("param", "RBX", 8),
Bitvector::from_u64(0).into(), bitvec!("0:8").into(),
), ),
); );
let mut new_state = state.clone(); let mut new_state = state.clone();
...@@ -367,12 +340,12 @@ fn handle_extern_symbol_stubs() { ...@@ -367,12 +340,12 @@ fn handle_extern_symbol_stubs() {
new_state.set_register(&cconv.integer_return_register[0], return_value); new_state.set_register(&cconv.integer_return_register[0], return_value);
assert_eq!( assert_eq!(
new_state.get_register(&Variable::mock("RAX", 8)), new_state.get_register(&variable!("RAX:8")),
Data::from_target( Data::from_target(
AbstractIdentifier::mock("param", "RBX", 8), AbstractIdentifier::mock("param", "RBX", 8),
IntervalDomain::new_top(ByteSize::new(8)), IntervalDomain::new_top(ByteSize::new(8)),
) )
.merge(&Bitvector::from_u64(0).into()) .merge(&bitvec!("0:8").into())
); );
} }
...@@ -392,8 +365,8 @@ fn test_merge_global_mem_from_callee() { ...@@ -392,8 +365,8 @@ fn test_merge_global_mem_from_callee() {
let write = |state: &mut State, address: u64, value: u16| { let write = |state: &mut State, address: u64, value: u16| {
state state
.write_to_address( .write_to_address(
&Expression::Const(Bitvector::from_u64(address)), &expr!(format!("{address}:8")),
&Data::from(Bitvector::from_u16(value)), &Data::from(bitvec!(format!("{value}:2"))),
&context.project.runtime_memory_image, &context.project.runtime_memory_image,
) )
.unwrap(); .unwrap();
...@@ -401,7 +374,7 @@ fn test_merge_global_mem_from_callee() { ...@@ -401,7 +374,7 @@ fn test_merge_global_mem_from_callee() {
let load = |state: &State, address: u64| -> Data { let load = |state: &State, address: u64| -> Data {
state state
.load_value( .load_value(
&Expression::Const(Bitvector::from_u64(address)), &expr!(format!("{address}:8")),
ByteSize::new(2), ByteSize::new(2),
&context.project.runtime_memory_image, &context.project.runtime_memory_image,
) )
...@@ -419,10 +392,7 @@ fn test_merge_global_mem_from_callee() { ...@@ -419,10 +392,7 @@ fn test_merge_global_mem_from_callee() {
let callee_fn_sig = FunctionSignature::mock_x64(); let callee_fn_sig = FunctionSignature::mock_x64();
let replacement_map = BTreeMap::from([( let replacement_map = BTreeMap::from([(
callee_state.get_global_mem_id(), callee_state.get_global_mem_id(),
Data::from_target( Data::from_target(caller_state.get_global_mem_id(), bitvec!("0:8").into()),
caller_state.get_global_mem_id(),
Bitvector::from_u64(0).into(),
),
)]); )]);
context.merge_global_mem_from_callee( context.merge_global_mem_from_callee(
...@@ -433,9 +403,9 @@ fn test_merge_global_mem_from_callee() { ...@@ -433,9 +403,9 @@ fn test_merge_global_mem_from_callee() {
&Tid::new("call"), &Tid::new("call"),
); );
assert_eq!(load(&caller_state, 0x2000), Bitvector::from_u16(42).into()); assert_eq!(load(&caller_state, 0x2000), bitvec!("42:2").into());
let mut expected_result = Data::from(Bitvector::from_u16(2)); let mut expected_result = Data::from(bitvec!("2:2"));
expected_result.set_contains_top_flag(); expected_result.set_contains_top_flag();
assert_eq!(load(&caller_state, 0x2002), expected_result); assert_eq!(load(&caller_state, 0x2002), expected_result);
assert_eq!(load(&caller_state, 0x3000), Bitvector::from_u16(4).into()); assert_eq!(load(&caller_state, 0x3000), bitvec!("4:2").into());
} }
use super::*; use super::*;
use crate::intermediate_representation::Variable; use crate::{intermediate_representation::*, variable};
use std::collections::BTreeMap; use std::collections::BTreeMap;
fn new_abstract_object() -> AbstractObject { fn new_abstract_object() -> AbstractObject {
...@@ -23,7 +23,7 @@ fn bv(number: i64) -> ValueDomain { ...@@ -23,7 +23,7 @@ fn bv(number: i64) -> ValueDomain {
fn new_id(tid: &str, reg_name: &str) -> AbstractIdentifier { fn new_id(tid: &str, reg_name: &str) -> AbstractIdentifier {
AbstractIdentifier::new( AbstractIdentifier::new(
Tid::new(tid), Tid::new(tid),
AbstractLocation::Register(Variable::mock(reg_name, ByteSize::new(8))), AbstractLocation::Register(variable!(format!("{reg_name}:8"))),
) )
} }
......
use crate::intermediate_representation::Variable; use crate::intermediate_representation::*;
use crate::variable;
use super::super::ValueDomain; use super::super::ValueDomain;
use super::*; use super::*;
...@@ -10,7 +11,7 @@ fn bv(value: i64) -> ValueDomain { ...@@ -10,7 +11,7 @@ fn bv(value: i64) -> ValueDomain {
fn new_id(name: &str) -> AbstractIdentifier { fn new_id(name: &str) -> AbstractIdentifier {
AbstractIdentifier::new( AbstractIdentifier::new(
Tid::new("time0"), Tid::new("time0"),
AbstractLocation::Register(Variable::mock(name, ByteSize::new(8))), AbstractLocation::Register(variable!(format!("{name}:8"))),
) )
} }
......
...@@ -316,7 +316,7 @@ fn test_widening_hints_after_pointer_specialization() { ...@@ -316,7 +316,7 @@ fn test_widening_hints_after_pointer_specialization() {
.add_signed_greater_equal_bound(&Bitvector::from_i64(6)) .add_signed_greater_equal_bound(&Bitvector::from_i64(6))
.unwrap(); .unwrap();
let expected_val = Data::from_target(new_id("func_tid", "RSP"), offset_with_lower_bound); let expected_val = Data::from_target(new_id("func_tid", "RSP"), offset_with_lower_bound);
assert_eq!(state.get_register(&Variable::mock("RBX", 8)), expected_val); assert_eq!(state.get_register(&variable!("RBX:8")), expected_val);
} }
#[test] #[test]
...@@ -363,11 +363,11 @@ fn from_fn_sig() { ...@@ -363,11 +363,11 @@ fn from_fn_sig() {
Data::from_target(new_id("func", "RSP"), bv(0).into()) Data::from_target(new_id("func", "RSP"), bv(0).into())
); );
assert_eq!( assert_eq!(
state.get_register(&Variable::mock("RDI", 8)), state.get_register(&variable!("RDI:8")),
Data::from_target(new_id("func", "RDI"), bv(0).into()) Data::from_target(new_id("func", "RDI"), bv(0).into())
); );
assert_eq!( assert_eq!(
state.get_register(&Variable::mock("RSI", 8)), state.get_register(&variable!("RSI:8")),
Data::from_target(new_id("func", "RSI"), bv(0).into()) Data::from_target(new_id("func", "RSI"), bv(0).into())
); );
} }
......
...@@ -167,11 +167,11 @@ fn specialize_by_binop() { ...@@ -167,11 +167,11 @@ fn specialize_by_binop() {
); );
assert!(x.is_ok()); assert!(x.is_ok());
assert_eq!( assert_eq!(
state.get_register(&Variable::mock("FLAG1", 1u64)), state.get_register(&variable!("FLAG1:1")),
bitvec!("1:1").into() bitvec!("1:1").into()
); );
assert_eq!( assert_eq!(
state.get_register(&Variable::mock("FLAG2", 1u64)), state.get_register(&variable!("FLAG2:1")),
bitvec!("1:1").into() bitvec!("1:1").into()
); );
// Expr = (FLAG bool_and 1 = Const) // Expr = (FLAG bool_and 1 = Const)
......
...@@ -285,9 +285,7 @@ fn supports_commutative_and() { ...@@ -285,9 +285,7 @@ fn supports_commutative_and() {
Expression::BinOp { Expression::BinOp {
op: BinOpType::IntAnd, op: BinOpType::IntAnd,
lhs: Box::new(expr!("RSP:8")), lhs: Box::new(expr!("RSP:8")),
rhs: Box::new(Expression::const_from_apint(ApInt::from_u64( rhs: Box::new(expr!(format!("{}:8", 0xFFFFFFFF_FFFFFFFF_u64 << 4))),
0xFFFFFFFF_FFFFFFFF << 4, // 16 Byte alignment
))),
}, },
); );
let bitmask_and_var = Def::assign( let bitmask_and_var = Def::assign(
...@@ -330,9 +328,7 @@ fn skips_empty_blocks() { ...@@ -330,9 +328,7 @@ fn skips_empty_blocks() {
lhs: Box::new(Expression::Var( lhs: Box::new(Expression::Var(
Project::mock_x64().stack_pointer_register.clone(), Project::mock_x64().stack_pointer_register.clone(),
)), )),
rhs: Box::new(Expression::const_from_apint(ApInt::from_u64( rhs: Box::new(expr!(format!("{}:8", 0xFFFFFFFF_FFFFFFFF_u64 << 4))),
0xFFFFFFFF_FFFFFFFF << 4, // 16 Byte alignment
))),
}, },
); );
// get project with empty block // get project with empty block
......
...@@ -7,7 +7,7 @@ use crate::{ ...@@ -7,7 +7,7 @@ use crate::{
AbstractDomain, DataDomain, DomainInsertion, HasTop, IntervalDomain, TryToBitvec, AbstractDomain, DataDomain, DomainInsertion, HasTop, IntervalDomain, TryToBitvec,
}, },
analysis::string_abstraction::{context::Context, state::State}, analysis::string_abstraction::{context::Context, state::State},
intermediate_representation::ExternSymbol, intermediate_representation::*,
}; };
use std::collections::BTreeMap; use std::collections::BTreeMap;
...@@ -206,7 +206,8 @@ mod tests { ...@@ -206,7 +206,8 @@ mod tests {
context::symbol_calls::tests::Setup, context::symbol_calls::tests::Setup,
tests::mock_project_with_intraprocedural_control_flow, tests::mock_project_with_intraprocedural_control_flow,
}, },
intermediate_representation::{Bitvector, Tid, Variable}, intermediate_representation::{Bitvector, Tid},
variable,
}; };
use std::collections::{BTreeMap, BTreeSet}; use std::collections::{BTreeMap, BTreeSet};
...@@ -226,12 +227,12 @@ mod tests { ...@@ -226,12 +227,12 @@ mod tests {
let stack_id = AbstractIdentifier::new( let stack_id = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(), AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
); );
let heap_id = AbstractIdentifier::new( let heap_id = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("r5", 4)).unwrap(), AbstractLocation::from_var(&variable!("r5:4")).unwrap(),
); );
let mut parameter_pointer: DataDomain<IntervalDomain> = let mut parameter_pointer: DataDomain<IntervalDomain> =
...@@ -250,7 +251,7 @@ mod tests { ...@@ -250,7 +251,7 @@ mod tests {
setup setup
.pi_state_before_symbol_call .pi_state_before_symbol_call
.set_register(&Variable::mock("r1", 4), parameter_pointer); .set_register(&variable!("r1:4"), parameter_pointer);
setup setup
.state_before_call .state_before_call
...@@ -314,7 +315,7 @@ mod tests { ...@@ -314,7 +315,7 @@ mod tests {
let stack_id = AbstractIdentifier::new( let stack_id = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(), AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
); );
let expected_data: DataDomain<IntervalDomain> = let expected_data: DataDomain<IntervalDomain> =
...@@ -365,12 +366,12 @@ mod tests { ...@@ -365,12 +366,12 @@ mod tests {
let return_targets = setup let return_targets = setup
.pi_state_before_symbol_call .pi_state_before_symbol_call
.get_register(&Variable::mock("r0", 4)); .get_register(&variable!("r0:4"));
let input_target: DataDomain<IntervalDomain> = DataDomain::from( let input_target: DataDomain<IntervalDomain> = DataDomain::from(
setup setup
.pi_state_before_symbol_call .pi_state_before_symbol_call
.get_register(&Variable::mock("r1", 4)) .get_register(&variable!("r1:4"))
.get_absolute_value() .get_absolute_value()
.unwrap() .unwrap()
.clone(), .clone(),
...@@ -407,14 +408,14 @@ mod tests { ...@@ -407,14 +408,14 @@ mod tests {
let return_targets = setup let return_targets = setup
.pi_state_before_symbol_call .pi_state_before_symbol_call
.get_register(&Variable::mock("r0", 4)) .get_register(&variable!("r0:4"))
.get_relative_values() .get_relative_values()
.clone(); .clone();
let input_target: DataDomain<IntervalDomain> = DataDomain::from( let input_target: DataDomain<IntervalDomain> = DataDomain::from(
setup setup
.pi_state_before_symbol_call .pi_state_before_symbol_call
.get_register(&Variable::mock("r1", 4)) .get_register(&variable!("r1:4"))
.get_absolute_value() .get_absolute_value()
.unwrap() .unwrap()
.clone(), .clone(),
...@@ -462,11 +463,11 @@ mod tests { ...@@ -462,11 +463,11 @@ mod tests {
fn test_has_multiple_targets() { fn test_has_multiple_targets() {
let stack_id = AbstractIdentifier::new( let stack_id = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(), AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
); );
let heap_id = AbstractIdentifier::new( let heap_id = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("r5", 4)).unwrap(), AbstractLocation::from_var(&variable!("r5:4")).unwrap(),
); );
// Test Case 1: Only one relative target. // Test Case 1: Only one relative target.
let mut data: DataDomain<IntervalDomain> = DataDomain::mock_from_target_map( let mut data: DataDomain<IntervalDomain> = DataDomain::mock_from_target_map(
......
...@@ -199,7 +199,7 @@ mod tests { ...@@ -199,7 +199,7 @@ mod tests {
use crate::abstract_domain::{AbstractIdentifier, AbstractLocation, CharacterInclusionDomain}; use crate::abstract_domain::{AbstractIdentifier, AbstractLocation, CharacterInclusionDomain};
use crate::analysis::pointer_inference::PointerInference as PointerInferenceComputation; use crate::analysis::pointer_inference::PointerInference as PointerInferenceComputation;
use crate::analysis::string_abstraction::tests::mock_project_with_intraprocedural_control_flow; use crate::analysis::string_abstraction::tests::mock_project_with_intraprocedural_control_flow;
use crate::intermediate_representation::{Expression, Variable}; use crate::{expr, intermediate_representation::*, variable};
#[test] #[test]
fn test_handle_scanf_calls() { fn test_handle_scanf_calls() {
...@@ -222,7 +222,7 @@ mod tests { ...@@ -222,7 +222,7 @@ mod tests {
let stack_id = AbstractIdentifier::new( let stack_id = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(), AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
); );
assert!(new_state assert!(new_state
...@@ -285,7 +285,7 @@ mod tests { ...@@ -285,7 +285,7 @@ mod tests {
#[test] #[test]
fn test_create_abstract_domain_entries_for_function_return_values_with_known_values() { fn test_create_abstract_domain_entries_for_function_return_values_with_known_values() {
let r2_reg = Variable::mock("r2", 4); let r2_reg = variable!("r2:4");
let sscanf_symbol = ExternSymbol::mock_sscanf_symbol_arm(); let sscanf_symbol = ExternSymbol::mock_sscanf_symbol_arm();
let project = mock_project_with_intraprocedural_control_flow( let project = mock_project_with_intraprocedural_control_flow(
...@@ -299,7 +299,7 @@ mod tests { ...@@ -299,7 +299,7 @@ mod tests {
let stack_id = AbstractIdentifier::new( let stack_id = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(), AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
); );
let mut arg_to_value_map: HashMap<Arg, Option<String>> = HashMap::new(); let mut arg_to_value_map: HashMap<Arg, Option<String>> = HashMap::new();
...@@ -309,7 +309,7 @@ mod tests { ...@@ -309,7 +309,7 @@ mod tests {
data_type: Some(Datatype::Pointer), data_type: Some(Datatype::Pointer),
}; };
let stack_arg = Arg::Stack { let stack_arg = Arg::Stack {
address: Expression::Var(Variable::mock("sp", 4)), address: expr!("sp:4"),
size: ByteSize::new(4), size: ByteSize::new(4),
data_type: Some(Datatype::Pointer), data_type: Some(Datatype::Pointer),
}; };
...@@ -361,7 +361,7 @@ mod tests { ...@@ -361,7 +361,7 @@ mod tests {
#[test] #[test]
fn test_create_abstract_domain_entries_for_function_return_values_with_unknown_values() { fn test_create_abstract_domain_entries_for_function_return_values_with_unknown_values() {
let r1_reg = Variable::mock("r1", 4); let r1_reg = variable!("r1:4");
let scanf_symbol = ExternSymbol::mock_scanf_symbol_arm(); let scanf_symbol = ExternSymbol::mock_scanf_symbol_arm();
let project = mock_project_with_intraprocedural_control_flow( let project = mock_project_with_intraprocedural_control_flow(
...@@ -375,7 +375,7 @@ mod tests { ...@@ -375,7 +375,7 @@ mod tests {
let stack_id = AbstractIdentifier::new( let stack_id = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(), AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
); );
let mut arg_to_value_map: HashMap<Arg, Option<String>> = HashMap::new(); let mut arg_to_value_map: HashMap<Arg, Option<String>> = HashMap::new();
...@@ -384,7 +384,7 @@ mod tests { ...@@ -384,7 +384,7 @@ mod tests {
data_type: Some(Datatype::Pointer), data_type: Some(Datatype::Pointer),
}; };
let stack_arg = Arg::Stack { let stack_arg = Arg::Stack {
address: Expression::Var(Variable::mock("sp", 4)), address: expr!("sp:4"),
size: ByteSize::new(4), size: ByteSize::new(4),
data_type: Some(Datatype::Pointer), data_type: Some(Datatype::Pointer),
}; };
...@@ -448,7 +448,7 @@ mod tests { ...@@ -448,7 +448,7 @@ mod tests {
let stack_id = AbstractIdentifier::new( let stack_id = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(), AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
); );
let return_target: DataDomain<IntervalDomain> = let return_target: DataDomain<IntervalDomain> =
...@@ -532,7 +532,7 @@ mod tests { ...@@ -532,7 +532,7 @@ mod tests {
let stack_id = AbstractIdentifier::new( let stack_id = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(), AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
); );
let new_state = setup let new_state = setup
...@@ -585,7 +585,7 @@ mod tests { ...@@ -585,7 +585,7 @@ mod tests {
let stack_id = AbstractIdentifier::new( let stack_id = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(), AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
); );
let new_state = setup let new_state = setup
...@@ -734,7 +734,7 @@ mod tests { ...@@ -734,7 +734,7 @@ mod tests {
), ),
( (
Arg::Stack { Arg::Stack {
address: Expression::Var(Variable::mock("sp", 4)), address: expr!("sp:4"),
size: ByteSize::new(4), size: ByteSize::new(4),
data_type: Some(Datatype::Pointer), data_type: Some(Datatype::Pointer),
}, },
...@@ -742,7 +742,7 @@ mod tests { ...@@ -742,7 +742,7 @@ mod tests {
), ),
( (
Arg::Stack { Arg::Stack {
address: Expression::Var(Variable::mock("sp", 4)).plus_const(4), address: expr!("sp:4 + 4:4"),
size: ByteSize::new(4), size: ByteSize::new(4),
data_type: Some(Datatype::Pointer), data_type: Some(Datatype::Pointer),
}, },
......
...@@ -3,13 +3,13 @@ use std::collections::BTreeSet; ...@@ -3,13 +3,13 @@ use std::collections::BTreeSet;
use super::*; use super::*;
use crate::abstract_domain::{AbstractIdentifier, AbstractLocation}; use crate::abstract_domain::{AbstractIdentifier, AbstractLocation};
use crate::analysis::pointer_inference::PointerInference as PointerInferenceComputation; use crate::analysis::pointer_inference::PointerInference as PointerInferenceComputation;
use crate::intermediate_representation::{Bitvector, Expression, Tid, Variable};
use crate::{ use crate::{
abstract_domain::{CharacterInclusionDomain, CharacterSet}, abstract_domain::{CharacterInclusionDomain, CharacterSet},
analysis::string_abstraction::{ analysis::string_abstraction::{
context::symbol_calls::tests::Setup, tests::mock_project_with_intraprocedural_control_flow, context::symbol_calls::tests::Setup, tests::mock_project_with_intraprocedural_control_flow,
}, },
}; };
use crate::{bitvec, expr, intermediate_representation::*, variable};
#[test] #[test]
fn test_handle_sprintf_and_snprintf_calls() { fn test_handle_sprintf_and_snprintf_calls() {
...@@ -29,10 +29,10 @@ fn test_handle_sprintf_and_snprintf_calls() { ...@@ -29,10 +29,10 @@ fn test_handle_sprintf_and_snprintf_calls() {
let stack_id = AbstractIdentifier::new( let stack_id = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(), AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
); );
let return_pointer: DataDomain<IntervalDomain> = let return_pointer: DataDomain<IntervalDomain> =
DataDomain::from_target(stack_id, IntervalDomain::from(Bitvector::from_i32(-84))); DataDomain::from_target(stack_id, IntervalDomain::from(bitvec!("-84:4")));
assert_eq!( assert_eq!(
return_pointer, return_pointer,
...@@ -68,10 +68,10 @@ fn test_parse_format_string_and_add_new_string_domain() { ...@@ -68,10 +68,10 @@ fn test_parse_format_string_and_add_new_string_domain() {
let format_string_index: usize = 1; let format_string_index: usize = 1;
let stack_id = AbstractIdentifier::new( let stack_id = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(), AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
); );
let return_pointer: DataDomain<IntervalDomain> = let return_pointer: DataDomain<IntervalDomain> =
DataDomain::from_target(stack_id, IntervalDomain::from(Bitvector::from_i32(-84))); DataDomain::from_target(stack_id, IntervalDomain::from(bitvec!("-84:4")));
let project = mock_project_with_intraprocedural_control_flow( let project = mock_project_with_intraprocedural_control_flow(
vec![(sprintf_symbol.clone(), vec![true])], vec![(sprintf_symbol.clone(), vec![true])],
...@@ -174,15 +174,15 @@ fn test_create_string_domain_using_data_type_approximations() { ...@@ -174,15 +174,15 @@ fn test_create_string_domain_using_data_type_approximations() {
fn test_create_string_domain_using_constants_and_sub_domains() { fn test_create_string_domain_using_constants_and_sub_domains() {
let sprintf_symbol = ExternSymbol::mock_sprintf_symbol_arm(); let sprintf_symbol = ExternSymbol::mock_sprintf_symbol_arm();
let string_arg = Arg::Register { let string_arg = Arg::Register {
expr: Expression::Var(Variable::mock("r6", 4)), expr: Expression::Var(variable!("r6:4")),
data_type: Some(Datatype::Pointer), data_type: Some(Datatype::Pointer),
}; };
let integer_arg = Arg::Register { let integer_arg = Arg::Register {
expr: Expression::Var(Variable::mock("r7", 4)), expr: Expression::Var(variable!("r7:4")),
data_type: Some(Datatype::Integer), data_type: Some(Datatype::Integer),
}; };
let char_arg = Arg::Register { let char_arg = Arg::Register {
expr: Expression::Var(Variable::mock("r8", 4)), expr: Expression::Var(variable!("r8:4")),
data_type: Some(Datatype::Char), data_type: Some(Datatype::Char),
}; };
...@@ -199,27 +199,21 @@ fn test_create_string_domain_using_constants_and_sub_domains() { ...@@ -199,27 +199,21 @@ fn test_create_string_domain_using_constants_and_sub_domains() {
let mut setup: Setup<CharacterInclusionDomain> = Setup::new(&pi_results); let mut setup: Setup<CharacterInclusionDomain> = Setup::new(&pi_results);
setup.pi_state_before_symbol_call.set_register( setup.pi_state_before_symbol_call.set_register(
&Variable::mock("r6", 4), &variable!("r6:4"),
DataDomain::from(IntervalDomain::new( DataDomain::from(IntervalDomain::new(
Bitvector::from_u64(0x3002), bitvec!("0x3002:8"),
Bitvector::from_u64(0x3002), bitvec!("0x3002:8"),
)), )),
); );
setup.pi_state_before_symbol_call.set_register( setup.pi_state_before_symbol_call.set_register(
&Variable::mock("r7", 4), &variable!("r7:4"),
DataDomain::from(IntervalDomain::new( DataDomain::from(IntervalDomain::new(bitvec!("2:8"), bitvec!("2:8"))),
Bitvector::from_u64(2),
Bitvector::from_u64(2),
)),
); );
setup.pi_state_before_symbol_call.set_register( setup.pi_state_before_symbol_call.set_register(
&Variable::mock("r8", 4), &variable!("r8:4"),
DataDomain::from(IntervalDomain::new( DataDomain::from(IntervalDomain::new(bitvec!("0x42:8"), bitvec!("0x42:8"))),
Bitvector::from_u64(0x42),
Bitvector::from_u64(0x42),
)),
); );
let result_domain = setup let result_domain = setup
...@@ -341,15 +335,15 @@ fn test_no_specifiers() { ...@@ -341,15 +335,15 @@ fn test_no_specifiers() {
fn test_fetch_constant_and_domain_for_format_specifier() { fn test_fetch_constant_and_domain_for_format_specifier() {
let sprintf_symbol = ExternSymbol::mock_sprintf_symbol_arm(); let sprintf_symbol = ExternSymbol::mock_sprintf_symbol_arm();
let string_arg = Arg::Register { let string_arg = Arg::Register {
expr: Expression::Var(Variable::mock("r6", 4)), expr: expr!("r6:4"),
data_type: Some(Datatype::Pointer), data_type: Some(Datatype::Pointer),
}; };
let integer_arg = Arg::Register { let integer_arg = Arg::Register {
expr: Expression::Var(Variable::mock("r7", 4)), expr: expr!("r7:4"),
data_type: Some(Datatype::Integer), data_type: Some(Datatype::Integer),
}; };
let char_arg = Arg::Register { let char_arg = Arg::Register {
expr: Expression::Var(Variable::mock("r8", 4)), expr: expr!("r8:4"),
data_type: Some(Datatype::Char), data_type: Some(Datatype::Char),
}; };
...@@ -408,11 +402,8 @@ fn test_fetch_constant_and_domain_for_format_specifier() { ...@@ -408,11 +402,8 @@ fn test_fetch_constant_and_domain_for_format_specifier() {
// Test Case 4: Integer and tracked constant. // Test Case 4: Integer and tracked constant.
setup.pi_state_before_symbol_call.set_register( setup.pi_state_before_symbol_call.set_register(
&Variable::mock("r7", 4), &variable!("r7:4"),
DataDomain::from(IntervalDomain::new( DataDomain::from(IntervalDomain::new(bitvec!("2:8"), bitvec!("2:8"))),
Bitvector::from_u64(2),
Bitvector::from_u64(2),
)),
); );
assert_eq!( assert_eq!(
...@@ -429,11 +420,8 @@ fn test_fetch_constant_and_domain_for_format_specifier() { ...@@ -429,11 +420,8 @@ fn test_fetch_constant_and_domain_for_format_specifier() {
// Test Case 5: Char and tracked constant. // Test Case 5: Char and tracked constant.
setup.pi_state_before_symbol_call.set_register( setup.pi_state_before_symbol_call.set_register(
&Variable::mock("r8", 4), &variable!("r8:4"),
DataDomain::from(IntervalDomain::new( DataDomain::from(IntervalDomain::new(bitvec!("0x42:4"), bitvec!("0x42:4"))),
Bitvector::from_u32(0x42),
Bitvector::from_u32(0x42),
)),
); );
assert_eq!( assert_eq!(
...@@ -450,10 +438,10 @@ fn test_fetch_constant_and_domain_for_format_specifier() { ...@@ -450,10 +438,10 @@ fn test_fetch_constant_and_domain_for_format_specifier() {
// Test Case 6: String and tracked constant. // Test Case 6: String and tracked constant.
setup.pi_state_before_symbol_call.set_register( setup.pi_state_before_symbol_call.set_register(
&Variable::mock("r6", 4), &variable!("r6:4"),
DataDomain::from(IntervalDomain::new( DataDomain::from(IntervalDomain::new(
Bitvector::from_u32(0x3002), bitvec!("0x3002:4"),
Bitvector::from_u32(0x3002), bitvec!("0x3002:4"),
)), )),
); );
...@@ -471,22 +459,22 @@ fn test_fetch_constant_and_domain_for_format_specifier() { ...@@ -471,22 +459,22 @@ fn test_fetch_constant_and_domain_for_format_specifier() {
let stack_id = AbstractIdentifier::new( let stack_id = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(), AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
); );
let mut pointer: DataDomain<IntervalDomain> = DataDomain::from_target( let mut pointer: DataDomain<IntervalDomain> = DataDomain::from_target(
stack_id, stack_id,
IntervalDomain::new(Bitvector::from_i32(16), Bitvector::from_i32(16)), IntervalDomain::new(bitvec!("16:4"), bitvec!("16:4")),
); );
let heap_id = AbstractIdentifier::new( let heap_id = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("r9", 4)).unwrap(), AbstractLocation::from_var(&variable!("r9:4")).unwrap(),
); );
pointer.insert_relative_value( pointer.insert_relative_value(
heap_id.clone(), heap_id.clone(),
IntervalDomain::new(Bitvector::from_i32(0), Bitvector::from_i32(0)), IntervalDomain::new(bitvec!("0:4"), bitvec!("0:4")),
); );
setup setup
...@@ -499,7 +487,7 @@ fn test_fetch_constant_and_domain_for_format_specifier() { ...@@ -499,7 +487,7 @@ fn test_fetch_constant_and_domain_for_format_specifier() {
// Test Case 5: String and tracked domain. // Test Case 5: String and tracked domain.
setup setup
.pi_state_before_symbol_call .pi_state_before_symbol_call
.set_register(&Variable::mock("r6", 4), pointer); .set_register(&variable!("r6:4"), pointer);
let expected_domain = CharacterInclusionDomain::Value(( let expected_domain = CharacterInclusionDomain::Value((
CharacterSet::Value(BTreeSet::new()), CharacterSet::Value(BTreeSet::new()),
...@@ -557,7 +545,7 @@ fn test_fetch_subdomains_if_available() { ...@@ -557,7 +545,7 @@ fn test_fetch_subdomains_if_available() {
let stack_id = AbstractIdentifier::new( let stack_id = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(), AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
); );
// Test Case 2: Target value is not of type string pointer. // Test Case 2: Target value is not of type string pointer.
...@@ -600,13 +588,13 @@ fn test_fetch_constant_domain_if_available() { ...@@ -600,13 +588,13 @@ fn test_fetch_constant_domain_if_available() {
pi_results.compute(false); pi_results.compute(false);
let setup: Setup<CharacterInclusionDomain> = Setup::new(&pi_results); let setup: Setup<CharacterInclusionDomain> = Setup::new(&pi_results);
let string_data: DataDomain<IntervalDomain> = DataDomain::from(Bitvector::from_i32(0x7000)); let string_data: DataDomain<IntervalDomain> = DataDomain::from(bitvec!("0x7000:4"));
let string_arg: Arg = Arg::mock_pointer_register("r0", 4); let string_arg: Arg = Arg::mock_pointer_register("r0", 4);
let integer_data: DataDomain<IntervalDomain> = DataDomain::from(Bitvector::from_i32(2)); let integer_data: DataDomain<IntervalDomain> = DataDomain::from(bitvec!("2:4"));
let integer_arg: Arg = Arg::mock_register_with_data_type("r0", 4, Some(Datatype::Integer)); let integer_arg: Arg = Arg::mock_register_with_data_type("r0", 4, Some(Datatype::Integer));
let char_data: DataDomain<IntervalDomain> = DataDomain::from(Bitvector::from_i32(0x61)); let char_data: DataDomain<IntervalDomain> = DataDomain::from(bitvec!("0x61:4"));
let char_arg: Arg = Arg::mock_register_with_data_type("r0", 4, Some(Datatype::Char)); let char_arg: Arg = Arg::mock_register_with_data_type("r0", 4, Some(Datatype::Char));
assert_eq!( assert_eq!(
......
...@@ -107,11 +107,10 @@ mod tests { ...@@ -107,11 +107,10 @@ mod tests {
context::symbol_calls::tests::Setup, context::symbol_calls::tests::Setup,
tests::mock_project_with_intraprocedural_control_flow, tests::mock_project_with_intraprocedural_control_flow,
}, },
intermediate_representation::{ByteSize, Variable}, intermediate_representation::*,
variable,
}; };
use super::*;
#[test] #[test]
fn test_handle_strcat_and_strncat_calls_with_known_second_input() { fn test_handle_strcat_and_strncat_calls_with_known_second_input() {
let strcat_symbol = ExternSymbol::mock_strcat_symbol_arm(); let strcat_symbol = ExternSymbol::mock_strcat_symbol_arm();
...@@ -247,7 +246,7 @@ mod tests { ...@@ -247,7 +246,7 @@ mod tests {
#[test] #[test]
fn test_process_second_input_domain_local_and_global() { fn test_process_second_input_domain_local_and_global() {
let r1_reg = Variable::mock("r1", ByteSize::new(4)); let r1_reg = variable!("r1:4");
let strcat_symbol = ExternSymbol::mock_strcat_symbol_arm(); let strcat_symbol = ExternSymbol::mock_strcat_symbol_arm();
let project = mock_project_with_intraprocedural_control_flow( let project = mock_project_with_intraprocedural_control_flow(
vec![(strcat_symbol.clone(), vec![false])], vec![(strcat_symbol.clone(), vec![false])],
......
...@@ -13,10 +13,10 @@ use crate::analysis::pointer_inference::PointerInference as PointerInferenceComp ...@@ -13,10 +13,10 @@ use crate::analysis::pointer_inference::PointerInference as PointerInferenceComp
use crate::analysis::pointer_inference::State as PiState; use crate::analysis::pointer_inference::State as PiState;
use crate::analysis::string_abstraction::state::State; use crate::analysis::string_abstraction::state::State;
use crate::analysis::string_abstraction::tests::*; use crate::analysis::string_abstraction::tests::*;
use crate::intermediate_representation::{Bitvector, ExternSymbol, Project, Sub}; use crate::intermediate_representation::*;
use crate::variable;
use crate::{ use crate::{
abstract_domain::{AbstractIdentifier, AbstractLocation}, abstract_domain::{AbstractIdentifier, AbstractLocation},
intermediate_representation::{Tid, Variable},
utils::symbol_utils::get_symbol_map, utils::symbol_utils::get_symbol_map,
}; };
...@@ -119,7 +119,7 @@ fn test_handle_generic_symbol_calls() { ...@@ -119,7 +119,7 @@ fn test_handle_generic_symbol_calls() {
let mut setup: Setup<CharacterInclusionDomain> = Setup::new(&pi_results); let mut setup: Setup<CharacterInclusionDomain> = Setup::new(&pi_results);
setup.state_before_call.add_new_variable_to_pointer_entry( setup.state_before_call.add_new_variable_to_pointer_entry(
Variable::mock("r1", 4), variable!("r1:4"),
DataDomain::from(IntervalDomain::from(Bitvector::from_i32(32))), DataDomain::from(IntervalDomain::from(Bitvector::from_i32(32))),
); );
...@@ -143,7 +143,7 @@ fn test_handle_unknown_symbol_calls() { ...@@ -143,7 +143,7 @@ fn test_handle_unknown_symbol_calls() {
let mut setup: Setup<CharacterInclusionDomain> = Setup::new(&pi_results); let mut setup: Setup<CharacterInclusionDomain> = Setup::new(&pi_results);
setup.state_before_call.add_new_variable_to_pointer_entry( setup.state_before_call.add_new_variable_to_pointer_entry(
Variable::mock("r1", 4), variable!("r1:4"),
DataDomain::from(IntervalDomain::from(Bitvector::from_i32(32))), DataDomain::from(IntervalDomain::from(Bitvector::from_i32(32))),
); );
...@@ -174,7 +174,7 @@ fn test_add_new_string_abstract_domain() { ...@@ -174,7 +174,7 @@ fn test_add_new_string_abstract_domain() {
let stack_id = AbstractIdentifier::new( let stack_id = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(), AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
); );
let stack_pointer = DataDomain::from_target( let stack_pointer = DataDomain::from_target(
stack_id.clone(), stack_id.clone(),
...@@ -194,7 +194,7 @@ fn test_add_new_string_abstract_domain() { ...@@ -194,7 +194,7 @@ fn test_add_new_string_abstract_domain() {
let heap_id = AbstractIdentifier::new( let heap_id = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("r5", 4)).unwrap(), AbstractLocation::from_var(&variable!("r5:4")).unwrap(),
); );
let heap_pointer = DataDomain::from_target( let heap_pointer = DataDomain::from_target(
...@@ -227,12 +227,12 @@ fn test_merge_domains_from_multiple_pointer_targets() { ...@@ -227,12 +227,12 @@ fn test_merge_domains_from_multiple_pointer_targets() {
let stack_id = AbstractIdentifier::new( let stack_id = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(), AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
); );
let heap_id = AbstractIdentifier::new( let heap_id = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("r5", 4)).unwrap(), AbstractLocation::from_var(&variable!("r5:4")).unwrap(),
); );
let mut domain_pointer: DataDomain<IntervalDomain> = let mut domain_pointer: DataDomain<IntervalDomain> =
...@@ -424,7 +424,7 @@ fn test_insert_constant_string_into_format_string() { ...@@ -424,7 +424,7 @@ fn test_insert_constant_string_into_format_string() {
fn test_handle_free() { fn test_handle_free() {
let free_symbol = ExternSymbol::mock_free_symbol_arm(); let free_symbol = ExternSymbol::mock_free_symbol_arm();
let malloc_symbol = ExternSymbol::mock_malloc_symbol_arm(); let malloc_symbol = ExternSymbol::mock_malloc_symbol_arm();
let r0_reg = Variable::mock("r0", 4); let r0_reg = variable!("r0:4");
let project = mock_project_with_intraprocedural_control_flow( let project = mock_project_with_intraprocedural_control_flow(
vec![ vec![
(malloc_symbol.clone(), vec![]), (malloc_symbol.clone(), vec![]),
......
...@@ -28,8 +28,8 @@ fn test_update_def() { ...@@ -28,8 +28,8 @@ fn test_update_def() {
setup.context.block_first_def_set = HashSet::new(); setup.context.block_first_def_set = HashSet::new();
let assign_def = def!["assign_def: r1:4 = 0x7000:4"]; let assign_def = def!["assign_def: r1:4 = 0x7000:4"];
let load_def = load_var_content_from_temp_var("load_def", "r5", "r2"); let load_def = Def::load_var_content_from_temp_var("load_def", "r5", "r2");
let store_def = store_var_content_at_temp_var("store_def", "r0", "r5"); let store_def = Def::store_var_content_at_temp_var("store_def", "r0", "r5");
let new_state = setup let new_state = setup
.context .context
......
...@@ -5,13 +5,16 @@ use crate::{ ...@@ -5,13 +5,16 @@ use crate::{
pointer_inference::State as PiState, pointer_inference::State as PiState,
string_abstraction::tests::mock_project_with_intraprocedural_control_flow, string_abstraction::tests::mock_project_with_intraprocedural_control_flow,
}, },
expr,
intermediate_representation::*,
variable,
}; };
use std::collections::BTreeSet; use std::collections::BTreeSet;
impl<T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>> State<T> { impl<T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>> State<T> {
pub fn mock_with_default_pi_state(current_sub: Term<Sub>) -> Self { pub fn mock_with_default_pi_state(current_sub: Term<Sub>) -> Self {
let pi_state = PointerInferenceState::new( let pi_state = PointerInferenceState::new(
&Variable::mock("sp", 4 as u64), &variable!("sp:4"),
current_sub.tid.clone(), current_sub.tid.clone(),
BTreeSet::new(), BTreeSet::new(),
); );
...@@ -50,7 +53,7 @@ fn test_delete_string_map_entries_if_no_pointer_targets_are_tracked() { ...@@ -50,7 +53,7 @@ fn test_delete_string_map_entries_if_no_pointer_targets_are_tracked() {
let stack_id = AbstractIdentifier::new( let stack_id = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(), AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
); );
let stack_pointer: DataDomain<IntervalDomain> = DataDomain::from_target( let stack_pointer: DataDomain<IntervalDomain> = DataDomain::from_target(
stack_id.clone(), stack_id.clone(),
...@@ -59,7 +62,7 @@ fn test_delete_string_map_entries_if_no_pointer_targets_are_tracked() { ...@@ -59,7 +62,7 @@ fn test_delete_string_map_entries_if_no_pointer_targets_are_tracked() {
let heap_id_1 = AbstractIdentifier::new( let heap_id_1 = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("r5", 4)).unwrap(), AbstractLocation::from_var(&variable!("r5:4")).unwrap(),
); );
let heap_pointer_1: DataDomain<IntervalDomain> = DataDomain::from_target( let heap_pointer_1: DataDomain<IntervalDomain> = DataDomain::from_target(
...@@ -69,7 +72,7 @@ fn test_delete_string_map_entries_if_no_pointer_targets_are_tracked() { ...@@ -69,7 +72,7 @@ fn test_delete_string_map_entries_if_no_pointer_targets_are_tracked() {
let heap_id_2 = AbstractIdentifier::new( let heap_id_2 = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("r6", 4)).unwrap(), AbstractLocation::from_var(&variable!("r6:4")).unwrap(),
); );
let heap_pointer_2: DataDomain<IntervalDomain> = DataDomain::from_target( let heap_pointer_2: DataDomain<IntervalDomain> = DataDomain::from_target(
...@@ -79,12 +82,12 @@ fn test_delete_string_map_entries_if_no_pointer_targets_are_tracked() { ...@@ -79,12 +82,12 @@ fn test_delete_string_map_entries_if_no_pointer_targets_are_tracked() {
let heap_id_3 = AbstractIdentifier::new( let heap_id_3 = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("r7", 4)).unwrap(), AbstractLocation::from_var(&variable!("r7:4")).unwrap(),
); );
state state
.variable_to_pointer_map .variable_to_pointer_map
.insert(Variable::mock("r0", 4), stack_pointer); .insert(variable!("r0:4"), stack_pointer);
state.stack_offset_to_pointer_map.insert(-8, heap_pointer_1); state.stack_offset_to_pointer_map.insert(-8, heap_pointer_1);
state.unassigned_return_pointer.insert(heap_pointer_2); state.unassigned_return_pointer.insert(heap_pointer_2);
...@@ -153,10 +156,10 @@ fn test_handle_assign_and_load() { ...@@ -153,10 +156,10 @@ fn test_handle_assign_and_load() {
let sub = Sub::mock("func"); let sub = Sub::mock("func");
let mut state: State<CharacterInclusionDomain> = State::mock_with_default_pi_state(sub.clone()); let mut state: State<CharacterInclusionDomain> = State::mock_with_default_pi_state(sub.clone());
let runtime_memory_image = RuntimeMemoryImage::mock(); let runtime_memory_image = RuntimeMemoryImage::mock();
let output = Variable::mock("r1", 4); let output = variable!("r1:4");
let constant_input = Expression::Const(Bitvector::from_str_radix(16, "7000").unwrap()); let constant_input = Expression::Const(Bitvector::from_str_radix(16, "7000").unwrap());
let return_address_input = Expression::Const(Bitvector::from_str_radix(16, "14718").unwrap()); let return_address_input = Expression::Const(Bitvector::from_str_radix(16, "14718").unwrap());
let other_input = Expression::var("r6", 4); let other_input = expr!("r6:4");
let mut block_first_def_set: HashSet<(Tid, Tid)> = HashSet::new(); let mut block_first_def_set: HashSet<(Tid, Tid)> = HashSet::new();
let mut return_tid = Tid::new("14718"); let mut return_tid = Tid::new("14718");
...@@ -211,7 +214,7 @@ fn test_handle_assign_and_load() { ...@@ -211,7 +214,7 @@ fn test_handle_assign_and_load() {
// Test Case 2: Assign Def with other input // Test Case 2: Assign Def with other input
let heap_id = AbstractIdentifier::new( let heap_id = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("r5", 4)).unwrap(), AbstractLocation::from_var(&variable!("r5:4")).unwrap(),
); );
let heap_pointer: DataDomain<IntervalDomain> = DataDomain::from_target( let heap_pointer: DataDomain<IntervalDomain> = DataDomain::from_target(
...@@ -263,19 +266,19 @@ fn test_handle_assign_and_load() { ...@@ -263,19 +266,19 @@ fn test_handle_assign_and_load() {
#[test] #[test]
fn test_add_pointer_to_variable_maps_if_tracked() { fn test_add_pointer_to_variable_maps_if_tracked() {
let output_var = Variable::mock("r2", 4); let output_var = variable!("r2:4");
let origin_var = Variable::mock("r5", 4); let origin_var = variable!("r5:4");
let mut mock_state = let mut mock_state =
State::<CharacterInclusionDomain>::mock_with_default_pi_state(Sub::mock("func")); State::<CharacterInclusionDomain>::mock_with_default_pi_state(Sub::mock("func"));
let pi_state = mock_state.get_pointer_inference_state().unwrap().clone(); let pi_state = mock_state.get_pointer_inference_state().unwrap().clone();
let heap_id = AbstractIdentifier::new( let heap_id = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("r5", 4)).unwrap(), AbstractLocation::from_var(&variable!("r5:4")).unwrap(),
); );
let stack_id = AbstractIdentifier::new( let stack_id = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(), AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
); );
let mut source_pointer: DataDomain<IntervalDomain> = let mut source_pointer: DataDomain<IntervalDomain> =
...@@ -339,7 +342,7 @@ fn test_pointer_targets_partially_tracked() { ...@@ -339,7 +342,7 @@ fn test_pointer_targets_partially_tracked() {
let heap_id = AbstractIdentifier::new( let heap_id = AbstractIdentifier::new(
Tid::new("heap"), Tid::new("heap"),
AbstractLocation::from_var(&Variable::mock("r0", 4)).unwrap(), AbstractLocation::from_var(&variable!("r0:4")).unwrap(),
); );
let stack_id = pi_state.stack_id.clone(); let stack_id = pi_state.stack_id.clone();
...@@ -367,8 +370,8 @@ fn test_pointer_targets_partially_tracked() { ...@@ -367,8 +370,8 @@ fn test_pointer_targets_partially_tracked() {
#[test] #[test]
fn test_pointer_is_in_pointer_maps() { fn test_pointer_is_in_pointer_maps() {
let r2_reg = Variable::mock("r2", 4); let r2_reg = variable!("r2:4");
let sp_reg = Variable::mock("sp", 4); let sp_reg = variable!("sp:4");
let mut mock_state = let mut mock_state =
State::<CharacterInclusionDomain>::mock_with_default_pi_state(Sub::mock("func")); State::<CharacterInclusionDomain>::mock_with_default_pi_state(Sub::mock("func"));
...@@ -401,10 +404,10 @@ fn test_pointer_is_in_pointer_maps() { ...@@ -401,10 +404,10 @@ fn test_pointer_is_in_pointer_maps() {
#[test] #[test]
fn test_handle_store() { fn test_handle_store() {
let block_first_def_set: HashSet<(Tid, Tid)> = HashSet::new(); let block_first_def_set: HashSet<(Tid, Tid)> = HashSet::new();
let target_var = Variable::mock("r2", 4); let target_var = variable!("r2:4");
let value_var = Variable::mock("r3", 4); let value_var = variable!("r3:4");
let value_location = Expression::Var(value_var.clone()); let value_location = Expression::Var(value_var.clone());
let sp_reg = Variable::mock("sp", 4); let sp_reg = variable!("sp:4");
let target_location = Expression::Var(target_var.clone()); let target_location = Expression::Var(target_var.clone());
let runtime_memory_image = RuntimeMemoryImage::mock(); let runtime_memory_image = RuntimeMemoryImage::mock();
let mut mock_state = let mut mock_state =
...@@ -465,7 +468,7 @@ fn test_handle_store() { ...@@ -465,7 +468,7 @@ fn test_handle_store() {
mock_state.set_all_maps_empty(); mock_state.set_all_maps_empty();
mock_state mock_state
.variable_to_pointer_map .variable_to_pointer_map
.insert(Variable::mock("r0", 4), string_pointer.clone()); .insert(variable!("r0:4"), string_pointer.clone());
mock_state.handle_store( mock_state.handle_store(
&target_location, &target_location,
...@@ -483,15 +486,15 @@ fn test_handle_store() { ...@@ -483,15 +486,15 @@ fn test_handle_store() {
mock_state.set_all_maps_empty(); mock_state.set_all_maps_empty();
mock_state mock_state
.variable_to_pointer_map .variable_to_pointer_map
.insert(Variable::mock("r0", 4), string_pointer.clone()); .insert(variable!("r0:4"), string_pointer.clone());
// Test Case 5: Global address pointer as constant. // Test Case 5: Global address pointer as constant.
// Test Case 6: Global address pointer in variable. // Test Case 6: Global address pointer in variable.
} }
#[test] #[test]
fn test_add_pointer_to_stack_map() { fn test_add_pointer_to_stack_map() {
let r2_reg = Variable::mock("r2", 4); let r2_reg = variable!("r2:4");
let sp_reg = Variable::mock("sp", 4); let sp_reg = variable!("sp:4");
let target = Expression::Var(r2_reg.clone()); let target = Expression::Var(r2_reg.clone());
let mut mock_state = let mut mock_state =
State::<CharacterInclusionDomain>::mock_with_default_pi_state(Sub::mock("func")); State::<CharacterInclusionDomain>::mock_with_default_pi_state(Sub::mock("func"));
...@@ -532,18 +535,18 @@ fn test_remove_non_callee_saved_pointer_entries_for_external_symbol() { ...@@ -532,18 +535,18 @@ fn test_remove_non_callee_saved_pointer_entries_for_external_symbol() {
mock_state mock_state
.variable_to_pointer_map .variable_to_pointer_map
.insert(Variable::mock("r0", 4), top_domain.clone()); .insert(variable!("r0:4"), top_domain.clone());
mock_state mock_state
.variable_to_pointer_map .variable_to_pointer_map
.insert(Variable::mock("r11", 4), top_domain); .insert(variable!("r11:4"), top_domain);
mock_state mock_state
.remove_non_callee_saved_pointer_entries_for_external_symbol(&project, &sprintf_symbol); .remove_non_callee_saved_pointer_entries_for_external_symbol(&project, &sprintf_symbol);
assert!(!mock_state assert!(!mock_state
.variable_to_pointer_map .variable_to_pointer_map
.contains_key(&Variable::mock("r0", 4))); .contains_key(&variable!("r0:4")));
assert!(mock_state assert!(mock_state
.variable_to_pointer_map .variable_to_pointer_map
.contains_key(&Variable::mock("r11", 4))); .contains_key(&variable!("r11:4")));
} }
use crate::{def, defs, expr, intermediate_representation::*, variable}; use crate::{def, defs, intermediate_representation::*};
// FIXME: Move this function to the intermediate_representation module
pub fn pointer_plus_offset_to_temp_var(
tid: &str,
tmp_name: &str,
pointer: &str,
offset: i64,
) -> Term<Def> {
Def::assign(
tid,
Variable {
name: String::from(tmp_name),
size: ByteSize::new(4),
is_temp: true,
},
expr!(format!("{}:4 + {}:4", pointer, offset)),
)
}
// FIXME: Move this function to the intermediate_representation module
pub fn store_var_content_at_temp_var(tid: &str, tmp_name: &str, var: &str) -> Term<Def> {
Def::store(
tid,
Expression::Var(Variable {
name: String::from(tmp_name),
size: ByteSize::new(4),
is_temp: true,
}),
expr!(format!("{}:4", var)),
)
}
// FIXME: Move this function to the intermediate_representation module
pub fn load_var_content_from_temp_var(tid: &str, var: &str, tmp_name: &str) -> Term<Def> {
Def::load(
tid,
variable!(format!("{}:4", var)),
Expression::Var(Variable {
name: String::from(tmp_name),
size: ByteSize::new(4),
is_temp: true,
}),
)
}
fn mock_defs_for_sprintf(format_known: bool, blk_num: usize) -> Vec<Term<Def>> { fn mock_defs_for_sprintf(format_known: bool, blk_num: usize) -> Vec<Term<Def>> {
/* /*
...@@ -81,13 +37,13 @@ fn mock_defs_for_sprintf(format_known: bool, blk_num: usize) -> Vec<Term<Def>> { ...@@ -81,13 +37,13 @@ fn mock_defs_for_sprintf(format_known: bool, blk_num: usize) -> Vec<Term<Def>> {
.as_mut(), .as_mut(),
); );
defs.push(pointer_plus_offset_to_temp_var( defs.push(Def::pointer_plus_offset_to_temp_var(
&format!("def_6_blk_{}", blk_num), &format!("def_6_blk_{}", blk_num),
"$U1050", "$U1050",
"sp", "sp",
0, 0,
)); ));
defs.push(store_var_content_at_temp_var( defs.push(Def::store_var_content_at_temp_var(
&format!("def_7_blk_{}", blk_num), &format!("def_7_blk_{}", blk_num),
"$U1050", "$U1050",
"r12", "r12",
...@@ -98,13 +54,13 @@ fn mock_defs_for_sprintf(format_known: bool, blk_num: usize) -> Vec<Term<Def>> { ...@@ -98,13 +54,13 @@ fn mock_defs_for_sprintf(format_known: bool, blk_num: usize) -> Vec<Term<Def>> {
blk_num blk_num
)]); )]);
defs.push(pointer_plus_offset_to_temp_var( defs.push(Def::pointer_plus_offset_to_temp_var(
&format!("def_9_blk_{}", blk_num), &format!("def_9_blk_{}", blk_num),
"$U1050", "$U1050",
"sp", "sp",
4, 4,
)); ));
defs.push(store_var_content_at_temp_var( defs.push(Def::store_var_content_at_temp_var(
&format!("def_10_blk_{}", blk_num), &format!("def_10_blk_{}", blk_num),
"$U1050", "$U1050",
"r12", "r12",
...@@ -132,14 +88,14 @@ fn mock_defs_for_scanf(format_known: bool, blk_num: usize) -> Vec<Term<Def>> { ...@@ -132,14 +88,14 @@ fn mock_defs_for_scanf(format_known: bool, blk_num: usize) -> Vec<Term<Def>> {
format!("def_1_blk_{}: r0:4 = r11:4 - 0x3c:4", blk_num) format!("def_1_blk_{}: r0:4 = r11:4 - 0x3c:4", blk_num)
]; ];
defs.push(pointer_plus_offset_to_temp_var( defs.push(Def::pointer_plus_offset_to_temp_var(
&format!("def_2_blk_{}", blk_num), &format!("def_2_blk_{}", blk_num),
"$U1050", "$U1050",
"sp", "sp",
0, 0,
)); ));
defs.push(store_var_content_at_temp_var( defs.push(Def::store_var_content_at_temp_var(
&format!("def_3_blk_{}", blk_num), &format!("def_3_blk_{}", blk_num),
"$U1050", "$U1050",
"r0", "r0",
...@@ -196,14 +152,14 @@ fn mock_defs_for_sscanf(source_known: bool, format_known: bool, blk_num: usize) ...@@ -196,14 +152,14 @@ fn mock_defs_for_sscanf(source_known: bool, format_known: bool, blk_num: usize)
format!("def_1_blk_{}: r3:4 = r11:4 - 0x96:4", blk_num) format!("def_1_blk_{}: r3:4 = r11:4 - 0x96:4", blk_num)
]; ];
defs.push(pointer_plus_offset_to_temp_var( defs.push(Def::pointer_plus_offset_to_temp_var(
&format!("def_2_blk_{}", blk_num), &format!("def_2_blk_{}", blk_num),
"$U1050", "$U1050",
"sp", "sp",
0, 0,
)); ));
defs.push(store_var_content_at_temp_var( defs.push(Def::store_var_content_at_temp_var(
&format!("def_3_blk_{}", blk_num), &format!("def_3_blk_{}", blk_num),
"$U1050", "$U1050",
"r3", "r3",
...@@ -214,13 +170,13 @@ fn mock_defs_for_sscanf(source_known: bool, format_known: bool, blk_num: usize) ...@@ -214,13 +170,13 @@ fn mock_defs_for_sscanf(source_known: bool, format_known: bool, blk_num: usize)
blk_num blk_num
)]); )]);
defs.push(pointer_plus_offset_to_temp_var( defs.push(Def::pointer_plus_offset_to_temp_var(
&format!("def_5_blk_{}", blk_num), &format!("def_5_blk_{}", blk_num),
"$U1050", "$U1050",
"sp", "sp",
4, 4,
)); ));
defs.push(store_var_content_at_temp_var( defs.push(Def::store_var_content_at_temp_var(
&format!("def_6_blk_{}", blk_num), &format!("def_6_blk_{}", blk_num),
"$U1050", "$U1050",
"r3", "r3",
......
...@@ -269,7 +269,7 @@ fn collect_tids_for_cwe_warning( ...@@ -269,7 +269,7 @@ fn collect_tids_for_cwe_warning(
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use super::*; use super::*;
use crate::intermediate_representation::Variable; use crate::{intermediate_representation::*, variable};
#[test] #[test]
fn test_new() { fn test_new() {
...@@ -279,7 +279,7 @@ pub mod tests { ...@@ -279,7 +279,7 @@ pub mod tests {
&FunctionSignature::mock_x64(), &FunctionSignature::mock_x64(),
context.project, context.project,
); );
let stack_id = AbstractIdentifier::from_var(Tid::new("func"), &Variable::mock("RSP", 8)); let stack_id = AbstractIdentifier::from_var(Tid::new("func"), &variable!("RSP:8"));
assert_eq!(state.stack_id, stack_id); assert_eq!(state.stack_id, stack_id);
assert_eq!(state.object_lower_bounds.len(), 1); assert_eq!(state.object_lower_bounds.len(), 1);
...@@ -302,7 +302,7 @@ pub mod tests { ...@@ -302,7 +302,7 @@ pub mod tests {
&FunctionSignature::mock_x64(), &FunctionSignature::mock_x64(),
context.project, context.project,
); );
let stack_id = AbstractIdentifier::from_var(Tid::new("func"), &Variable::mock("RSP", 8)); let stack_id = AbstractIdentifier::from_var(Tid::new("func"), &variable!("RSP:8"));
// access in bounds // access in bounds
let address = Data::from_target(stack_id.clone(), Bitvector::from_i64(-12).into()); let address = Data::from_target(stack_id.clone(), Bitvector::from_i64(-12).into());
assert!(state assert!(state
......
use super::*; use super::*;
use crate::utils::log::LogMessage; use crate::utils::log::LogMessage;
use std::{ use std::{collections::HashSet, fmt};
collections::{HashMap, HashSet},
fmt,
};
/// A basic block is a sequence of `Def` instructions followed by up to two `Jmp` instructions. /// A basic block is a sequence of `Def` instructions followed by up to two `Jmp` instructions.
/// ///
...@@ -70,131 +67,6 @@ impl Term<Blk> { ...@@ -70,131 +67,6 @@ impl Term<Blk> {
Err(logs) Err(logs)
} }
} }
/// Wherever possible, substitute input variables of expressions
/// with the input expression that defines the input variable.
///
/// Note that substitution is only possible
/// if the input variables of the input expression itself did not change since the definition of said variable.
///
/// The expression propagation is used in [`expression_propagation` normalization pass](crate::analysis::expression_propagation)
/// to further simplify the generated expressions using a fixpoint algorithm
/// and allows more dead stores to be removed during [dead variable elimination](crate::analysis::dead_variable_elimination).
pub fn propagate_input_expressions(
&mut self,
apriori_insertable_expressions: Option<HashMap<Variable, Expression>>,
) {
let mut insertable_expressions = HashMap::new();
if let Some(insertables) = apriori_insertable_expressions {
insertable_expressions = insertables;
}
for def in self.term.defs.iter_mut() {
match &mut def.term {
Def::Assign {
var,
value: expression,
} => {
// insert known input expressions
for (input_var, input_expr) in insertable_expressions.iter() {
expression.substitute_input_var(input_var, input_expr);
}
// expressions dependent on the assigned variable are no longer insertable
insertable_expressions.retain(|input_var, input_expr| {
input_var != var && !input_expr.input_vars().into_iter().any(|x| x == var)
});
// If the value of the assigned variable does not depend on the former value of the variable,
// then it is insertable for future expressions.
if !expression.input_vars().into_iter().any(|x| x == var) {
insertable_expressions.insert(var.clone(), expression.clone());
}
}
Def::Load {
var,
address: expression,
} => {
// insert known input expressions
for (input_var, input_expr) in insertable_expressions.iter() {
expression.substitute_input_var(input_var, input_expr);
}
// expressions dependent on the assigned variable are no longer insertable
insertable_expressions.retain(|input_var, input_expr| {
input_var != var && !input_expr.input_vars().into_iter().any(|x| x == var)
});
}
Def::Store { address, value } => {
// insert known input expressions
for (input_var, input_expr) in insertable_expressions.iter() {
address.substitute_input_var(input_var, input_expr);
value.substitute_input_var(input_var, input_expr);
}
}
}
}
for jump in self.term.jmps.iter_mut() {
match &mut jump.term {
Jmp::Branch(_) | Jmp::Call { .. } | Jmp::CallOther { .. } => (),
Jmp::BranchInd(expr)
| Jmp::CBranch {
condition: expr, ..
}
| Jmp::CallInd { target: expr, .. }
| Jmp::Return(expr) => {
// insert known input expressions
for (input_var, input_expr) in insertable_expressions.iter() {
expr.substitute_input_var(input_var, input_expr);
}
}
}
}
}
/// Merge subsequent assignments to the same variable to a single assignment to that variable.
///
/// The value expressions of merged assignments can often be simplified later on
/// in the corresponding [`Project` normalization pass](Project::normalize).
pub fn merge_def_assignments_to_same_var(&mut self) {
let mut new_defs = Vec::new();
let mut last_def_opt = None;
for def in self.term.defs.iter() {
if let Def::Assign {
var: current_var, ..
} = &def.term
{
if let Some(Term {
term:
Def::Assign {
var: last_var,
value: last_value,
},
..
}) = &last_def_opt
{
if current_var == last_var {
let mut substituted_def = def.clone();
substituted_def.substitute_input_var(last_var, last_value);
last_def_opt = Some(substituted_def);
} else {
new_defs.push(last_def_opt.unwrap());
last_def_opt = Some(def.clone());
}
} else if last_def_opt.is_some() {
panic!(); // Only assign-defs should be saved in last_def.
} else {
last_def_opt = Some(def.clone());
}
} else {
if let Some(last_def) = last_def_opt {
new_defs.push(last_def);
}
new_defs.push(def.clone());
last_def_opt = None;
}
}
if let Some(last_def) = last_def_opt {
new_defs.push(last_def);
}
self.term.defs = new_defs;
}
} }
impl fmt::Display for Blk { impl fmt::Display for Blk {
...@@ -208,101 +80,3 @@ impl fmt::Display for Blk { ...@@ -208,101 +80,3 @@ impl fmt::Display for Blk {
Ok(()) Ok(())
} }
} }
#[cfg(test)]
mod tests {
use super::*;
use crate::intermediate_representation::{Def, Expression, Variable};
impl Blk {
/// Creates empty block with tid "block".
pub fn mock() -> Term<Blk> {
Term {
tid: Tid::new("block"),
term: Blk {
defs: Vec::new(),
jmps: Vec::new(),
indirect_jmp_targets: Vec::new(),
},
}
}
pub fn mock_with_tid(tid: &str) -> Term<Blk> {
Term {
tid: Tid::new(tid),
term: Blk {
defs: Vec::new(),
jmps: Vec::new(),
indirect_jmp_targets: Vec::new(),
},
}
}
}
#[test]
fn expression_propagation() {
use crate::intermediate_representation::UnOpType;
let defs = vec![
Def::assign(
"tid_1",
Variable::mock("X", 8),
Expression::var("Y", 8).un_op(UnOpType::IntNegate),
),
Def::assign(
"tid_2",
Variable::mock("Y", 8),
Expression::var("X", 8).plus(Expression::var("Y", 8)),
),
Def::assign(
"tid_3",
Variable::mock("X", 8),
Expression::var("X", 8).un_op(UnOpType::IntNegate),
),
Def::assign(
"tid_4",
Variable::mock("Y", 8),
Expression::var("Y", 8).un_op(UnOpType::IntNegate),
),
Def::assign(
"tid_5",
Variable::mock("Y", 8),
Expression::var("X", 8).plus(Expression::var("Y", 8)),
),
];
let mut block = Term {
tid: Tid::new("block"),
term: Blk {
defs,
jmps: Vec::new(),
indirect_jmp_targets: Vec::new(),
},
};
block.merge_def_assignments_to_same_var();
block.propagate_input_expressions(None);
let result_defs = vec![
Def::assign(
"tid_1",
Variable::mock("X", 8),
Expression::var("Y", 8).un_op(UnOpType::IntNegate),
),
Def::assign(
"tid_2",
Variable::mock("Y", 8),
Expression::var("Y", 8)
.un_op(UnOpType::IntNegate)
.plus(Expression::var("Y", 8)),
),
Def::assign(
"tid_3",
Variable::mock("X", 8),
Expression::var("X", 8).un_op(UnOpType::IntNegate),
),
Def::assign(
"tid_5",
Variable::mock("Y", 8),
Expression::var("X", 8).plus(Expression::var("Y", 8).un_op(UnOpType::IntNegate)),
),
];
assert_eq!(block.term.defs, result_defs);
}
}
...@@ -101,41 +101,18 @@ impl fmt::Display for Def { ...@@ -101,41 +101,18 @@ impl fmt::Display for Def {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::intermediate_representation::BinOpType; use crate::{expr, intermediate_representation::*, variable};
#[test] #[test]
fn zero_extension_check() { fn zero_extension_check() {
let eax_variable = Expression::Var(Variable {
name: String::from("EAX"),
size: ByteSize::new(4),
is_temp: false,
});
let int_sub_expr = Expression::BinOp {
op: BinOpType::IntSub,
lhs: Box::new(Expression::Var(Variable {
name: String::from("EAX"),
size: ByteSize::new(4),
is_temp: false,
})),
rhs: Box::new(Expression::Var(Variable {
name: String::from("ECX"),
size: ByteSize::new(4),
is_temp: false,
})),
};
let zero_extend_def = Term { let zero_extend_def = Term {
tid: Tid::new("zero_tid"), tid: Tid::new("zero_tid"),
term: Def::Assign { term: Def::Assign {
var: Variable { var: variable!("RAX:8"),
name: String::from("RAX"),
size: ByteSize::new(8),
is_temp: false,
},
value: Expression::Cast { value: Expression::Cast {
op: CastOpType::IntZExt, op: CastOpType::IntZExt,
size: ByteSize::new(8), size: ByteSize::new(8),
arg: Box::new(eax_variable.clone()), arg: Box::new(expr!("EAX:8")),
}, },
}, },
}; };
...@@ -143,15 +120,11 @@ mod tests { ...@@ -143,15 +120,11 @@ mod tests {
let zero_extend_but_no_var_def = Term { let zero_extend_but_no_var_def = Term {
tid: Tid::new("zero_tid"), tid: Tid::new("zero_tid"),
term: Def::Assign { term: Def::Assign {
var: Variable { var: variable!("RAX:8"),
name: String::from("RAX"),
size: ByteSize::new(8),
is_temp: false,
},
value: Expression::Cast { value: Expression::Cast {
op: CastOpType::IntZExt, op: CastOpType::IntZExt,
size: ByteSize::new(8), size: ByteSize::new(8),
arg: Box::new(int_sub_expr.clone()), arg: Box::new(expr!("EAX:8 - ECX:8")),
}, },
}, },
}; };
...@@ -159,15 +132,11 @@ mod tests { ...@@ -159,15 +132,11 @@ mod tests {
let non_zero_extend_def = Term { let non_zero_extend_def = Term {
tid: Tid::new("zero_tid"), tid: Tid::new("zero_tid"),
term: Def::Assign { term: Def::Assign {
var: Variable { var: variable!("RAX:8"),
name: String::from("RAX"),
size: ByteSize::new(8),
is_temp: false,
},
value: Expression::Cast { value: Expression::Cast {
op: CastOpType::IntSExt, op: CastOpType::IntSExt,
size: ByteSize::new(8), size: ByteSize::new(8),
arg: Box::new(eax_variable.clone()), arg: Box::new(expr!("EAX:8")),
}, },
}, },
}; };
......
#[cfg(test)]
use apint::ApInt;
#[cfg(test)]
use super::{CastOpType, Variable};
use super::*; use super::*;
/// ## Helper functions for building expressions /// ## Helper functions for building expressions
impl Expression { impl Expression {
/// Shortcut for creating a constant expression from an i64 value
#[cfg(test)]
pub fn const_from_i64(value: i64) -> Expression {
Expression::Const(Bitvector::from_i64(value))
}
/// Shortcut for creating a constant expression from an i32 value
#[cfg(test)]
pub fn const_from_i32(value: i32) -> Expression {
Expression::Const(Bitvector::from_i32(value))
}
/// Shortcut for creating a constant expression from an apint value (e.g. copy of global address)
#[cfg(test)]
pub fn const_from_apint(value: ApInt) -> Expression {
Expression::Const(value)
}
/// Shortcut for creating a variable expression
#[cfg(test)]
pub fn var(name: impl ToString, size_in_bytes: impl Into<ByteSize>) -> Expression {
Expression::Var(Variable {
name: name.to_string(),
size: size_in_bytes.into(),
is_temp: false,
})
}
/// Shortcut for creating a cast expression
#[cfg(test)]
pub fn cast(self, op: CastOpType) -> Expression {
Expression::Cast {
op,
size: ByteSize::new(8),
arg: Box::new(self),
}
}
/// Shortcut for creating a subpiece expression
#[cfg(test)]
pub fn subpiece(self, low_byte: ByteSize, size: ByteSize) -> Expression {
Expression::Subpiece {
low_byte,
size,
arg: Box::new(self),
}
}
/// Shortcut for creating unary operation expressions.
#[cfg(test)]
pub fn un_op(self, op: UnOpType) -> Expression {
Expression::UnOp {
op,
arg: Box::new(self),
}
}
/// Shortcut for creating an `IntAdd`-expression /// Shortcut for creating an `IntAdd`-expression
pub fn plus(self, rhs: Expression) -> Expression { pub fn plus(self, rhs: Expression) -> Expression {
Expression::BinOp { Expression::BinOp {
...@@ -74,16 +11,6 @@ impl Expression { ...@@ -74,16 +11,6 @@ impl Expression {
} }
} }
/// Shortcut for creating an `IntSub`-expression
#[cfg(test)]
pub fn minus(self, rhs: Expression) -> Expression {
Expression::BinOp {
lhs: Box::new(self),
op: BinOpType::IntSub,
rhs: Box::new(rhs),
}
}
/// Construct an expression that adds a constant value to the given expression. /// Construct an expression that adds a constant value to the given expression.
/// ///
/// The bytesize of the value is automatically adjusted to the bytesize of the given expression. /// The bytesize of the value is automatically adjusted to the bytesize of the given expression.
...@@ -100,19 +27,4 @@ impl Expression { ...@@ -100,19 +27,4 @@ impl Expression {
} }
self.plus(Expression::Const(value)) self.plus(Expression::Const(value))
} }
/// Construct an expression that subtracts a constant value from the given expression.
///
/// The bytesize of the value is automatically adjusted to the bytesize of the given expression.
#[cfg(test)]
pub fn minus_const(self, value: i64) -> Expression {
let bytesize = self.bytesize();
let mut value = Bitvector::from_i64(value);
match u64::from(bytesize) {
size if size > 8 => value.sign_extend(bytesize).unwrap(),
size if size < 8 => value.truncate(bytesize).unwrap(),
_ => (),
}
self.minus(Expression::Const(value))
}
} }
use super::*; use super::*;
use crate::{expr, intermediate_representation::*};
#[test] #[test]
fn trivial_expression_substitution() { fn trivial_expression_substitution() {
let rax_variable = Expression::Var(Variable::mock("RAX", 8)); let rax_variable = expr!("RAX:8");
let rcx_variable = Expression::Var(Variable::mock("RCX", 8)); let rcx_variable = expr!("RCX:8");
let mut expr = Expression::BinOp { let mut expr = Expression::BinOp {
op: BinOpType::IntXOr, op: BinOpType::IntXOr,
lhs: Box::new(rax_variable.clone()), lhs: Box::new(rax_variable.clone()),
rhs: Box::new(rax_variable.clone()), rhs: Box::new(rax_variable.clone()),
}; };
expr.substitute_trivial_operations(); expr.substitute_trivial_operations();
assert_eq!( assert_eq!(expr, expr!("0:8"));
expr,
Expression::Const(Bitvector::zero(ByteSize::new(8).into()))
);
let mut expr = Expression::BinOp { let mut expr = Expression::BinOp {
op: BinOpType::IntOr, op: BinOpType::IntOr,
lhs: Box::new(rax_variable.clone()), lhs: Box::new(rax_variable.clone()),
rhs: Box::new(Expression::Const(Bitvector::zero(ByteSize::new(8).into()))), rhs: Box::new(expr!("0:8")),
}; };
expr.substitute_trivial_operations(); expr.substitute_trivial_operations();
assert_eq!(expr, rax_variable); assert_eq!(expr, rax_variable);
let sub_expr = Expression::BinOp { let sub_expr = expr!("RAX:8 - RCX:8");
lhs: Box::new(rax_variable.clone()),
op: BinOpType::IntSub,
rhs: Box::new(rcx_variable.clone()),
};
let mut expr = Expression::BinOp { let mut expr = Expression::BinOp {
op: BinOpType::IntEqual, op: BinOpType::IntEqual,
lhs: Box::new(Expression::Const(Bitvector::zero(ByteSize::new(1).into()))), lhs: Box::new(expr!("0:8")),
rhs: Box::new(sub_expr.clone()), rhs: Box::new(sub_expr.clone()),
}; };
expr.substitute_trivial_operations(); expr.substitute_trivial_operations();
...@@ -44,7 +38,7 @@ fn trivial_expression_substitution() { ...@@ -44,7 +38,7 @@ fn trivial_expression_substitution() {
let mut expr = Expression::BinOp { let mut expr = Expression::BinOp {
op: BinOpType::IntNotEqual, op: BinOpType::IntNotEqual,
lhs: Box::new(sub_expr.clone()), lhs: Box::new(sub_expr.clone()),
rhs: Box::new(Expression::Const(Bitvector::zero(ByteSize::new(1).into()))), rhs: Box::new(expr!("0:8")),
}; };
expr.substitute_trivial_operations(); expr.substitute_trivial_operations();
assert_eq!( assert_eq!(
...@@ -108,29 +102,29 @@ fn trivial_expression_substitution() { ...@@ -108,29 +102,29 @@ fn trivial_expression_substitution() {
arg: Box::new(Expression::Cast { arg: Box::new(Expression::Cast {
op: CastOpType::IntSExt, op: CastOpType::IntSExt,
size: ByteSize::new(8), size: ByteSize::new(8),
arg: Box::new(Expression::Var(Variable::mock("EAX", 4))), arg: Box::new(expr!("EAX:4")),
}), }),
}; };
expr.substitute_trivial_operations(); expr.substitute_trivial_operations();
assert_eq!(expr, Expression::Var(Variable::mock("EAX", 4))); assert_eq!(expr, expr!("EAX:4"));
let mut expr = Expression::Subpiece { let mut expr = Expression::Subpiece {
low_byte: ByteSize::new(4), low_byte: ByteSize::new(4),
size: ByteSize::new(4), size: ByteSize::new(4),
arg: Box::new(Expression::BinOp { arg: Box::new(Expression::BinOp {
op: BinOpType::Piece, op: BinOpType::Piece,
lhs: Box::new(Expression::Var(Variable::mock("EAX", 4))), lhs: Box::new(expr!("EAX:4")),
rhs: Box::new(Expression::Var(Variable::mock("EBX", 4))), rhs: Box::new(expr!("EBX:4")),
}), }),
}; };
expr.substitute_trivial_operations(); expr.substitute_trivial_operations();
assert_eq!(expr, Expression::Var(Variable::mock("EAX", 4))); assert_eq!(expr, expr!("EAX:4"));
let mut expr = Expression::Subpiece { let mut expr = Expression::Subpiece {
low_byte: ByteSize::new(0), low_byte: ByteSize::new(0),
size: ByteSize::new(4), size: ByteSize::new(4),
arg: Box::new(Expression::Subpiece { arg: Box::new(Expression::Subpiece {
low_byte: ByteSize::new(2), low_byte: ByteSize::new(2),
size: ByteSize::new(6), size: ByteSize::new(6),
arg: Box::new(Expression::Var(Variable::mock("RAX", 8))), arg: Box::new(expr!("RAX:8")),
}), }),
}; };
expr.substitute_trivial_operations(); expr.substitute_trivial_operations();
...@@ -139,7 +133,7 @@ fn trivial_expression_substitution() { ...@@ -139,7 +133,7 @@ fn trivial_expression_substitution() {
Expression::Subpiece { Expression::Subpiece {
low_byte: ByteSize::new(2), low_byte: ByteSize::new(2),
size: ByteSize::new(4), size: ByteSize::new(4),
arg: Box::new(Expression::Var(Variable::mock("RAX", 8))), arg: Box::new(expr!("RAX:8")),
} }
); );
...@@ -165,10 +159,10 @@ fn trivial_expression_substitution() { ...@@ -165,10 +159,10 @@ fn trivial_expression_substitution() {
lhs: Box::new(Expression::BinOp { lhs: Box::new(Expression::BinOp {
lhs: Box::new(rax_variable.clone()), lhs: Box::new(rax_variable.clone()),
op: BinOpType::IntSub, op: BinOpType::IntSub,
rhs: Box::new(Expression::Const(Bitvector::from_i64(3))), rhs: Box::new(expr!("3:8")),
}), }),
op: BinOpType::IntSub, op: BinOpType::IntSub,
rhs: Box::new(Expression::Const(Bitvector::from_i64(4))), rhs: Box::new(expr!("4:8")),
}; };
expr.substitute_trivial_operations(); expr.substitute_trivial_operations();
assert_eq!( assert_eq!(
...@@ -176,7 +170,7 @@ fn trivial_expression_substitution() { ...@@ -176,7 +170,7 @@ fn trivial_expression_substitution() {
Expression::BinOp { Expression::BinOp {
lhs: Box::new(rax_variable.clone()), lhs: Box::new(rax_variable.clone()),
op: BinOpType::IntSub, op: BinOpType::IntSub,
rhs: Box::new(Expression::Const(Bitvector::from_i64(7))) rhs: Box::new(expr!("7:8"))
} }
); );
} }
...@@ -187,17 +181,13 @@ fn test_complicated_a_less_than_b_substitution() { ...@@ -187,17 +181,13 @@ fn test_complicated_a_less_than_b_substitution() {
use Expression::*; use Expression::*;
let sborrow_expr = BinOp { let sborrow_expr = BinOp {
op: IntSBorrow, op: IntSBorrow,
lhs: Box::new(Var(Variable::mock("RAX", 8))), lhs: Box::new(expr!("RAX:8")),
rhs: Box::new(Var(Variable::mock("RBX", 8))), rhs: Box::new(expr!("RBX:8")),
}; };
let a_minus_b_less_zero_expr = BinOp { let a_minus_b_less_zero_expr = BinOp {
op: IntSLess, op: IntSLess,
lhs: Box::new(BinOp { lhs: Box::new(expr!("RAX:8 - RBX:8")),
op: IntSub, rhs: Box::new(expr!("0:8")),
lhs: Box::new(Var(Variable::mock("RAX", 8))),
rhs: Box::new(Var(Variable::mock("RBX", 8))),
}),
rhs: Box::new(Const(Bitvector::from_u64(0))),
}; };
let mut expr = BinOp { let mut expr = BinOp {
op: IntNotEqual, op: IntNotEqual,
...@@ -207,19 +197,19 @@ fn test_complicated_a_less_than_b_substitution() { ...@@ -207,19 +197,19 @@ fn test_complicated_a_less_than_b_substitution() {
expr.substitute_trivial_operations(); expr.substitute_trivial_operations();
let expected_expr = BinOp { let expected_expr = BinOp {
op: IntSLess, op: IntSLess,
lhs: Box::new(Var(Variable::mock("RAX", 8))), lhs: Box::new(expr!("RAX:8")),
rhs: Box::new(Var(Variable::mock("RBX", 8))), rhs: Box::new(expr!("RBX:8")),
}; };
assert_eq!(expr, expected_expr); assert_eq!(expr, expected_expr);
} }
#[test] #[test]
fn display() { fn display() {
let expr = Expression::const_from_i32(2); let expr = expr!("2:4");
let mul = Expression::BinOp { let mul = Expression::BinOp {
op: BinOpType::IntMult, op: BinOpType::IntMult,
lhs: Box::new(Expression::Var(Variable::mock("RAX", 8))), lhs: Box::new(expr!("RAX:8")),
rhs: Box::new(Expression::Var(Variable::mock("RBP", 8))), rhs: Box::new(expr!("RBP:8")),
}; };
let expr = expr.plus(mul); let expr = expr.plus(mul);
let expr = Expression::UnOp { let expr = Expression::UnOp {
......
...@@ -92,23 +92,3 @@ impl fmt::Display for Jmp { ...@@ -92,23 +92,3 @@ impl fmt::Display for Jmp {
} }
} }
} }
#[cfg(test)]
pub mod tests {
use super::*;
impl Jmp {
/// Create a mock call to a TID with the given `target` and `return_`
/// as the names of the target and return TIDs.
pub fn mock_call(target: &str, return_: Option<&str>) -> Term<Jmp> {
let call = Jmp::Call {
target: Tid::new(target.to_string()),
return_: return_.map(|tid_name| Tid::new(tid_name)),
};
Term {
tid: Tid::new(format!("call_{}", target.to_string())),
term: call,
}
}
}
}
...@@ -189,7 +189,7 @@ pub mod parsing { ...@@ -189,7 +189,7 @@ pub mod parsing {
#[allow(dead_code)] #[allow(dead_code)]
pub fn parse_expr<S: AsRef<str>>(str: S) -> Expression { pub fn parse_expr<S: AsRef<str>>(str: S) -> Expression {
let set = RegexSet::new([ let set = RegexSet::new([
r"^[[:alnum:]&&[^0-9]]{1}[[:alnum:]&&[^x]]?[[:alnum:]]*:[0-9]{1,2}$", // Variable r"^[[:alnum:]&&[^0-9]]{1}[[:alnum:]&&[^x]]?[[:alnum:]_]*:[0-9]{1,2}$", // Variable
r"^((0x(-)?[[:alnum:]]+)|^(-)?([0-9])+)+:[0-9]+$", // Constant r"^((0x(-)?[[:alnum:]]+)|^(-)?([0-9])+)+:[0-9]+$", // Constant
r"^[^\+]*\+{1}[^\+]*$", // BinOp (IntAdd) r"^[^\+]*\+{1}[^\+]*$", // BinOp (IntAdd)
r"^[[:ascii:]]+ \-{1} [[:ascii:]]+$", // BinOp (IntSub) r"^[[:ascii:]]+ \-{1} [[:ascii:]]+$", // BinOp (IntSub)
......
...@@ -194,50 +194,6 @@ mod tests { ...@@ -194,50 +194,6 @@ mod tests {
use super::*; use super::*;
use apint::BitWidth; use apint::BitWidth;
impl DatatypeProperties {
pub fn mock() -> DatatypeProperties {
DatatypeProperties {
char_size: ByteSize::new(1),
double_size: ByteSize::new(8),
float_size: ByteSize::new(4),
integer_size: ByteSize::new(4),
long_double_size: ByteSize::new(8),
long_long_size: ByteSize::new(8),
long_size: ByteSize::new(4),
pointer_size: ByteSize::new(8),
short_size: ByteSize::new(2),
}
}
/// Datatype sizes according to System V ABI
pub fn mock_x64() -> DatatypeProperties {
DatatypeProperties {
char_size: ByteSize::new(1),
double_size: ByteSize::new(8),
float_size: ByteSize::new(4),
integer_size: ByteSize::new(4),
long_double_size: ByteSize::new(16),
long_long_size: ByteSize::new(8),
long_size: ByteSize::new(8),
pointer_size: ByteSize::new(8),
short_size: ByteSize::new(2),
}
}
pub fn mock_arm32() -> DatatypeProperties {
DatatypeProperties {
char_size: ByteSize::new(1),
double_size: ByteSize::new(8),
float_size: ByteSize::new(4),
integer_size: ByteSize::new(4),
long_double_size: ByteSize::new(8),
long_long_size: ByteSize::new(8),
long_size: ByteSize::new(4),
pointer_size: ByteSize::new(4),
short_size: ByteSize::new(2),
}
}
}
#[test] #[test]
fn check_bit_to_byte_conversion() { fn check_bit_to_byte_conversion() {
let bits: BitWidth = BitWidth::new(8).unwrap(); let bits: BitWidth = BitWidth::new(8).unwrap();
......
...@@ -49,75 +49,3 @@ impl Program { ...@@ -49,75 +49,3 @@ impl Program {
None None
} }
} }
#[cfg(test)]
mod tests {
use crate::intermediate_representation::{CallingConvention, Datatype};
use super::*;
impl Program {
fn add_extern_symbols_to_program(a: Vec<(Tid, ExternSymbol)>) -> Program {
Program {
subs: BTreeMap::new(),
extern_symbols: BTreeMap::from_iter(a),
entry_points: BTreeSet::new(),
address_base_offset: 0x1000u64,
}
}
/// Returns Program with malloc, free and other_function
pub fn mock_x64() -> Program {
let malloc = ExternSymbol::create_extern_symbol(
"malloc",
CallingConvention::mock_x64(),
Some(Datatype::Integer),
Some(Datatype::Pointer),
);
let free = ExternSymbol::create_extern_symbol(
"free",
CallingConvention::mock_x64(),
Some(Datatype::Pointer),
None,
);
let other_function = ExternSymbol::create_extern_symbol(
"other_function",
CallingConvention::mock_x64(),
None,
None,
);
Program::add_extern_symbols_to_program(vec![
(malloc.tid.clone(), malloc),
(free.tid.clone(), free),
(other_function.tid.clone(), other_function),
])
}
/// Returns Program with malloc, free and other_function
pub fn mock_arm32() -> Program {
let malloc = ExternSymbol::create_extern_symbol(
"malloc",
CallingConvention::mock_arm32(),
Some(Datatype::Integer),
Some(Datatype::Pointer),
);
let free = ExternSymbol::create_extern_symbol(
"free",
CallingConvention::mock_arm32(),
Some(Datatype::Pointer),
None,
);
let other_function = ExternSymbol::create_extern_symbol(
"other_function",
CallingConvention::mock_arm32(),
None,
None,
);
Program::add_extern_symbols_to_program(vec![
(malloc.tid.clone(), malloc),
(free.tid.clone(), free),
(other_function.tid.clone(), other_function),
])
}
}
}
...@@ -291,75 +291,6 @@ impl Term<Jmp> { ...@@ -291,75 +291,6 @@ impl Term<Jmp> {
mod tests { mod tests {
use super::*; use super::*;
impl Project {
/// Returns project with x64 calling convention and mocked program.
pub fn mock_x64() -> Project {
let mut none_cconv_register: Vec<Variable> = vec![
"RAX", "RBX", "RSP", "RBP", "R10", "R11", "R12", "R13", "R14", "R15",
]
.into_iter()
.map(|name| Variable::mock(name, ByteSize::new(8)))
.collect();
let mut integer_register = CallingConvention::mock_x64().integer_parameter_register;
integer_register.append(&mut none_cconv_register);
let calling_conventions: BTreeMap<String, CallingConvention> =
BTreeMap::from([("__stdcall".to_string(), CallingConvention::mock_x64())]);
Project {
program: Term {
tid: Tid::new("program_tid"),
term: Program::mock_x64(),
},
cpu_architecture: "x86_64".to_string(),
stack_pointer_register: Variable::mock("RSP", 8u64),
calling_conventions,
register_set: integer_register.iter().cloned().collect(),
datatype_properties: DatatypeProperties::mock_x64(),
runtime_memory_image: RuntimeMemoryImage::mock(),
}
}
pub fn mock_arm32() -> Project {
let none_cconv_4byte_register: Vec<Variable> = vec!["r12", "r14", "r15"]
.into_iter()
.map(|name| Variable::mock(name, ByteSize::new(4)))
.collect();
let none_cconv_16byte_register: Vec<Variable> = vec![
"q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15",
]
.into_iter()
.map(|name| Variable::mock(name, ByteSize::new(16)))
.collect();
let callee_saved_register = CallingConvention::mock_arm32().callee_saved_register;
let integer_register = CallingConvention::mock_arm32()
.integer_parameter_register
.into_iter()
.chain(none_cconv_4byte_register)
.chain(none_cconv_16byte_register)
.chain(callee_saved_register);
Project {
program: Term {
tid: Tid::new("program_tid"),
term: Program::mock_arm32(),
},
cpu_architecture: "arm32".to_string(),
stack_pointer_register: Variable::mock("sp", 4u64),
calling_conventions: BTreeMap::from([(
"__stdcall".to_string(),
CallingConvention::mock_arm32(),
)]),
register_set: integer_register.collect(),
datatype_properties: DatatypeProperties::mock_arm32(),
runtime_memory_image: RuntimeMemoryImage::mock(),
}
}
}
#[test] #[test]
fn retarget_nonexisting_jumps() { fn retarget_nonexisting_jumps() {
let mut jmp_term = Term { let mut jmp_term = Term {
......
...@@ -245,12 +245,13 @@ fn negate_condition(expr: Expression) -> Expression { ...@@ -245,12 +245,13 @@ fn negate_condition(expr: Expression) -> Expression {
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use super::*; use super::*;
use crate::{def, expr};
use std::collections::BTreeMap; use std::collections::BTreeMap;
fn mock_condition_block(name: &str, if_target: &str, else_target: &str) -> Term<Blk> { fn mock_condition_block(name: &str, if_target: &str, else_target: &str) -> Term<Blk> {
let if_jmp = Jmp::CBranch { let if_jmp = Jmp::CBranch {
target: Tid::new(if_target), target: Tid::new(if_target),
condition: Expression::Var(Variable::mock("zero_flag", ByteSize::new(1))), condition: expr!("zero_flag:1"),
}; };
let if_jmp = Term { let if_jmp = Term {
tid: Tid::new(name.to_string() + "_jmp_if"), tid: Tid::new(name.to_string() + "_jmp_if"),
...@@ -273,14 +274,8 @@ pub mod tests { ...@@ -273,14 +274,8 @@ pub mod tests {
} }
fn mock_block_with_defs(name: &str, return_target: &str) -> Term<Blk> { fn mock_block_with_defs(name: &str, return_target: &str) -> Term<Blk> {
let def = Def::Assign { let def = def![format!("{name}_def: r0:4 = r1:4")];
var: Variable::mock("r0", ByteSize::new(4)),
value: Expression::Var(Variable::mock("r1", ByteSize::new(4))),
};
let def = Term {
tid: Tid::new(name.to_string() + "_def"),
term: def,
};
let jmp = Jmp::Branch(Tid::new(return_target)); let jmp = Jmp::Branch(Tid::new(return_target));
let jmp = Term { let jmp = Term {
tid: Tid::new(name.to_string() + "_jmp"), tid: Tid::new(name.to_string() + "_jmp"),
......
...@@ -5,8 +5,10 @@ use goblin::{elf, Object}; ...@@ -5,8 +5,10 @@ use goblin::{elf, Object};
/// A representation of the runtime image of a binary after being loaded into memory by the loader. /// A representation of the runtime image of a binary after being loaded into memory by the loader.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct RuntimeMemoryImage { pub struct RuntimeMemoryImage {
memory_segments: Vec<MemorySegment>, /// Sequence of memory segments.
is_little_endian: bool, pub memory_segments: Vec<MemorySegment>,
/// Endianness
pub is_little_endian: bool,
} }
impl RuntimeMemoryImage { impl RuntimeMemoryImage {
...@@ -276,108 +278,29 @@ impl RuntimeMemoryImage { ...@@ -276,108 +278,29 @@ impl RuntimeMemoryImage {
} }
} }
impl RuntimeMemoryImage {
/// Creates a mock runtime memory image with: byte series, strings and format strings.
pub fn mock() -> RuntimeMemoryImage {
RuntimeMemoryImage {
memory_segments: vec![
MemorySegment {
bytes: [0xb0u8, 0xb1, 0xb2, 0xb3, 0xb4].to_vec(),
base_address: 0x1000,
read_flag: true,
write_flag: false,
execute_flag: false,
},
MemorySegment {
bytes: [0u8; 8].to_vec(),
base_address: 0x2000,
read_flag: true,
write_flag: true,
execute_flag: false,
},
// Contains the Hello World string at byte 3002.
MemorySegment {
bytes: [
0x01, 0x02, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c,
0x64, 0x00,
]
.to_vec(),
base_address: 0x3000,
read_flag: true,
write_flag: false,
execute_flag: false,
},
MemorySegment {
bytes: [0x02, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00].to_vec(),
base_address: 0x4000,
read_flag: true,
write_flag: false,
execute_flag: false,
},
// Contains strings: '/dev/sd%c%d' and 'cat %s'
MemorySegment {
bytes: [
0x2f, 0x64, 0x65, 0x76, 0x2f, 0x73, 0x64, 0x25, 0x63, 0x25, 0x64, 0x00,
0x63, 0x61, 0x74, 0x20, 0x25, 0x73, 0x00,
]
.to_vec(),
base_address: 0x5000,
read_flag: true,
write_flag: false,
execute_flag: false,
},
// Contains string: 'cat %s %s %s %s' starting at the first byte.
MemorySegment {
bytes: [
0x63, 0x61, 0x74, 0x20, 0x25, 0x73, 0x20, 0x25, 0x73, 0x20, 0x25, 0x73,
0x20, 0x25, 0x73, 0x00,
]
.to_vec(),
base_address: 0x6000,
read_flag: true,
write_flag: false,
execute_flag: false,
},
// Contains string: 'str1 str2 str3 str4'
MemorySegment {
bytes: [
0x73, 0x74, 0x72, 0x31, 0x20, 0x73, 0x74, 0x72, 0x32, 0x20, 0x73, 0x74,
0x72, 0x33, 0x20, 0x73, 0x74, 0x72, 0x34, 0x00,
]
.to_vec(),
base_address: 0x7000,
read_flag: true,
write_flag: false,
execute_flag: false,
},
],
is_little_endian: true,
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::intermediate_representation::{Bitvector, ByteSize, RuntimeMemoryImage}; use crate::{bitvec, intermediate_representation::*};
#[test] #[test]
fn read_endianness() { fn read_endianness() {
let mut mem_image = RuntimeMemoryImage::mock(); let mut mem_image = RuntimeMemoryImage::mock();
let address = Bitvector::from_u32(0x1001); let address = bitvec!("0x1001:4");
assert_eq!( assert_eq!(
mem_image.read(&address, ByteSize::new(4)).unwrap(), mem_image.read(&address, ByteSize::new(4)).unwrap(),
Bitvector::from_u32(0xb4b3b2b1).into() bitvec!("0xb4b3b2b1:4").into()
); );
mem_image.is_little_endian = false; mem_image.is_little_endian = false;
assert_eq!( assert_eq!(
mem_image.read(&address, ByteSize::new(4)).unwrap(), mem_image.read(&address, ByteSize::new(4)).unwrap(),
Bitvector::from_u32(0xb1b2b3b4).into() bitvec!("0xb1b2b3b4:4").into()
); );
} }
#[test] #[test]
fn ro_data_pointer() { fn ro_data_pointer() {
let mem_image = RuntimeMemoryImage::mock(); let mem_image = RuntimeMemoryImage::mock();
let address = Bitvector::from_u32(0x1002); let address = bitvec!("0x1002:4");
let (slice, index) = mem_image.get_ro_data_pointer_at_address(&address).unwrap(); let (slice, index) = mem_image.get_ro_data_pointer_at_address(&address).unwrap();
assert_eq!(index, 2); assert_eq!(index, 2);
assert_eq!(&slice[index..], &[0xb2u8, 0xb3, 0xb4]); assert_eq!(&slice[index..], &[0xb2u8, 0xb3, 0xb4]);
...@@ -389,7 +312,7 @@ mod tests { ...@@ -389,7 +312,7 @@ mod tests {
// the byte array contains "Hello World". // the byte array contains "Hello World".
let expected_string: &str = let expected_string: &str =
std::str::from_utf8(b"\x48\x65\x6c\x6c\x6f\x20\x57\x6f\x72\x6c\x64").unwrap(); std::str::from_utf8(b"\x48\x65\x6c\x6c\x6f\x20\x57\x6f\x72\x6c\x64").unwrap();
let address = Bitvector::from_u32(0x3002); let address = bitvec!("0x3002:4");
assert_eq!( assert_eq!(
expected_string, expected_string,
mem_image mem_image
......
...@@ -195,232 +195,3 @@ impl CallingConvention { ...@@ -195,232 +195,3 @@ impl CallingConvention {
register_list register_list
} }
} }
#[cfg(test)]
mod tests {
use super::*;
impl Sub {
pub fn mock(name: impl ToString) -> Term<Sub> {
Term {
tid: Tid::new(name.to_string()),
term: Sub {
name: name.to_string(),
blocks: Vec::new(),
calling_convention: None,
},
}
}
}
/// Wrapper for subpiece to model float register for argument passing
fn create_float_register_subpiece(
name: &str,
reg_size: u64,
low_byte: u64,
size: u64,
) -> Expression {
Expression::subpiece(
Expression::Var(Variable::mock(name, reg_size)),
ByteSize::new(low_byte),
ByteSize::new(size),
)
}
impl CallingConvention {
/// Creates System V Calling Convention with Advanced Vector Extensions 512
pub fn mock_x64() -> CallingConvention {
CallingConvention {
name: "__stdcall".to_string(), // so that the mock is useable as standard calling convention in tests
integer_parameter_register: vec![
Variable::mock("RDI", 8),
Variable::mock("RSI", 8),
Variable::mock("RDX", 8),
Variable::mock("RCX", 8),
Variable::mock("R8", 8),
Variable::mock("R9", 8),
],
// ABI: first 8 Bytes of ZMM0-ZMM7 for float parameter
// Ghidra: first 8 Bytes of YMM0-YMM7 for float parameter
float_parameter_register: vec![
create_float_register_subpiece("ZMM0", 64, 0, 8),
create_float_register_subpiece("ZMM1", 64, 0, 8),
create_float_register_subpiece("ZMM2", 64, 0, 8),
create_float_register_subpiece("ZMM3", 64, 0, 8),
create_float_register_subpiece("ZMM4", 64, 0, 8),
create_float_register_subpiece("ZMM5", 64, 0, 8),
create_float_register_subpiece("ZMM6", 64, 0, 8),
create_float_register_subpiece("ZMM7", 64, 0, 8),
],
integer_return_register: vec![Variable::mock("RAX", 8), Variable::mock("RDX", 8)],
// ABI: XMM0-XMM1 float return register
// Ghidra: uses XMM0 only
float_return_register: vec![create_float_register_subpiece("ZMM0", 64, 0, 8)],
callee_saved_register: vec![
Variable::mock("RBP", 8),
Variable::mock("RBX", 8),
Variable::mock("RSP", 8),
Variable::mock("R12", 8),
Variable::mock("R13", 8),
Variable::mock("R14", 8),
Variable::mock("R15", 8),
],
}
}
/// Following ARM32 ABI with MVE Extention
pub fn mock_arm32() -> CallingConvention {
CallingConvention {
name: "__stdcall".to_string(), // so that the mock is useable as standard calling convention in tests
integer_parameter_register: vec![
Variable::mock("r0", 4),
Variable::mock("r1", 4),
Variable::mock("r2", 4),
Variable::mock("r3", 4),
],
// ABI: q0-q3 used for argument passing
// Ghidra: uses q0-q1 only
float_parameter_register: vec![
create_float_register_subpiece("q0", 16, 0, 4),
create_float_register_subpiece("q0", 16, 4, 4),
create_float_register_subpiece("q0", 16, 8, 4),
create_float_register_subpiece("q0", 16, 12, 4),
create_float_register_subpiece("q1", 16, 0, 4),
create_float_register_subpiece("q1", 16, 4, 4),
create_float_register_subpiece("q1", 16, 8, 4),
create_float_register_subpiece("q1", 16, 12, 4),
],
// ABI: r0-r1 used as integer return register
// Ghidra uses r0 only
integer_return_register: vec![
Variable::mock("r0", 4),
Variable::mock("r1", 4),
Variable::mock("r2", 4),
Variable::mock("r3", 4),
],
// ABI: whole q0 used as float return
// Ghidra: uses first 8 Bytes of q0 only
float_return_register: vec![create_float_register_subpiece("q0", 16, 0, 4)],
callee_saved_register: vec![
Variable::mock("r4", 4),
Variable::mock("r5", 4),
Variable::mock("r6", 4),
Variable::mock("r7", 4),
Variable::mock("r8", 4),
Variable::mock("r9", 4),
Variable::mock("r10", 4),
Variable::mock("r11", 4),
Variable::mock("r13", 4),
Variable::mock("q4", 16),
Variable::mock("q5", 16),
Variable::mock("q6", 16),
Variable::mock("q7", 16),
],
}
}
}
impl Arg {
pub fn mock_register(name: impl ToString, size_in_bytes: impl Into<ByteSize>) -> Arg {
Arg::Register {
expr: Expression::Var(Variable::mock(name.to_string(), size_in_bytes)),
data_type: None,
}
}
pub fn mock_register_with_data_type(
name: impl ToString,
size_in_bytes: impl Into<ByteSize>,
data_type: Option<Datatype>,
) -> Arg {
Arg::Register {
expr: Expression::Var(Variable::mock(name.to_string(), size_in_bytes)),
data_type,
}
}
pub fn mock_pointer_register(
name: impl ToString,
size_in_bytes: impl Into<ByteSize>,
) -> Arg {
Arg::Register {
expr: Expression::Var(Variable::mock(name.to_string(), size_in_bytes)),
data_type: Some(Datatype::Pointer),
}
}
}
impl ExternSymbol {
pub fn mock_x64(name: impl ToString) -> ExternSymbol {
ExternSymbol {
tid: Tid::new(name.to_string()),
addresses: vec!["UNKNOWN".to_string()],
name: name.to_string(),
calling_convention: Some("__stdcall".to_string()),
parameters: vec![Arg::mock_register("RDI", 8)],
return_values: vec![Arg::mock_register("RAX", 8)],
no_return: false,
has_var_args: false,
}
}
pub fn mock_arm32(name: impl ToString) -> ExternSymbol {
ExternSymbol {
tid: Tid::new(name.to_string()),
addresses: vec!["UNKNOWN".to_string()],
name: name.to_string(),
calling_convention: Some("__stdcall".to_string()),
parameters: vec![Arg::mock_register("r0", 4)],
return_values: vec![Arg::mock_register("r0", 4)],
no_return: false,
has_var_args: false,
}
}
pub fn mock_sprintf_x64() -> Self {
ExternSymbol {
tid: Tid::new("sprintf"),
addresses: vec!["UNKNOWN".to_string()],
name: "sprintf".to_string(),
calling_convention: Some("__stdcall".to_string()),
parameters: vec![Arg::mock_register("RDI", 8), Arg::mock_register("RSI", 8)],
return_values: vec![Arg::mock_register("RAX", 8)],
no_return: false,
has_var_args: true,
}
}
/// Returns extern symbol with argument/return register according to calling convention
pub fn create_extern_symbol(
name: &str,
cconv: CallingConvention,
arg_type: Option<Datatype>,
return_type: Option<Datatype>,
) -> ExternSymbol {
ExternSymbol {
tid: Tid::new(name),
addresses: vec![],
name: name.to_string(),
calling_convention: Some(cconv.name),
parameters: match arg_type {
Some(data_type) => {
vec![Arg::from_var(
cconv.integer_parameter_register[0].clone(),
Some(data_type),
)]
}
None => vec![],
},
return_values: match return_type {
Some(data_type) => {
vec![Arg::from_var(
cconv.integer_return_register[0].clone(),
Some(data_type),
)]
}
None => vec![],
},
no_return: false,
has_var_args: false,
}
}
}
}
use crate::prelude::*; use crate::prelude::*;
mod builder; mod builder_high_lvl;
mod builder_low_lvl;
/// A term identifier consisting of an ID string (which is required to be unique) /// A term identifier consisting of an ID string (which is required to be unique)
/// and an address to indicate where the term is located. /// and an address to indicate where the term is located.
......
//! This module contains the implementations of various builder functions
//! for different terms.
#[cfg(test)]
use crate::intermediate_representation::{Def, Expression, Jmp, Variable};
#[cfg(test)]
use super::{Term, Tid};
/// ## Helper functions for building defs
#[cfg(test)]
impl Def {
/// Shortcut for creating a assign def
pub fn assign(tid: &str, var: Variable, value: Expression) -> Term<Def> {
Term {
tid: Tid::new(tid),
term: Def::Assign { var, value },
}
}
/// Shortcut for creating a load def
pub fn load(tid: &str, var: Variable, address: Expression) -> Term<Def> {
Term {
tid: Tid::new(tid),
term: Def::Load { var, address },
}
}
/// Shortcut for creating a store def
pub fn store(tid: &str, address: Expression, value: Expression) -> Term<Def> {
Term {
tid: Tid::new(tid),
term: Def::Store { address, value },
}
}
}
/// ## Helper functions for building jmps
#[cfg(test)]
impl Jmp {
/// Shortcut for creating a call
pub fn call(tid: &str, target_tid: &str, return_tid: Option<&str>) -> Term<Jmp> {
let return_tid = return_tid.map(|tid_name| Tid::new(tid_name));
Term {
tid: Tid::new(tid),
term: Jmp::Call {
target: Tid::new(target_tid),
return_: return_tid,
},
}
}
/// Shortcut for creating a branch
pub fn branch(tid: &str, target_tid: &str) -> Term<Jmp> {
Term {
tid: Tid::new(tid),
term: Jmp::Branch(Tid::new(target_tid)),
}
}
}
//! This module contains the implementations of various builder functions for
//! the higher intermediate representation [Project](crate::intermediate_representation::Expression),
//! [Program](crate::intermediate_representation::Expression)
//! and [RuntimeMemoryImage](crate::intermediate_representation::Expression).
#[cfg(test)]
use crate::utils::binary::MemorySegment;
#[cfg(test)]
use crate::{intermediate_representation::*, variable};
#[cfg(test)]
use std::collections::{BTreeMap, BTreeSet};
#[cfg(test)]
impl RuntimeMemoryImage {
/// Creates a mock runtime memory image with: byte series, strings and format strings.
pub fn mock() -> RuntimeMemoryImage {
RuntimeMemoryImage {
memory_segments: vec![
MemorySegment {
bytes: [0xb0u8, 0xb1, 0xb2, 0xb3, 0xb4].to_vec(),
base_address: 0x1000,
read_flag: true,
write_flag: false,
execute_flag: false,
},
MemorySegment {
bytes: [0u8; 8].to_vec(),
base_address: 0x2000,
read_flag: true,
write_flag: true,
execute_flag: false,
},
// Contains the Hello World string at byte 3002.
MemorySegment {
bytes: [
0x01, 0x02, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c,
0x64, 0x00,
]
.to_vec(),
base_address: 0x3000,
read_flag: true,
write_flag: false,
execute_flag: false,
},
MemorySegment {
bytes: [0x02, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00].to_vec(),
base_address: 0x4000,
read_flag: true,
write_flag: false,
execute_flag: false,
},
// Contains strings: '/dev/sd%c%d' and 'cat %s'
MemorySegment {
bytes: [
0x2f, 0x64, 0x65, 0x76, 0x2f, 0x73, 0x64, 0x25, 0x63, 0x25, 0x64, 0x00,
0x63, 0x61, 0x74, 0x20, 0x25, 0x73, 0x00,
]
.to_vec(),
base_address: 0x5000,
read_flag: true,
write_flag: false,
execute_flag: false,
},
// Contains string: 'cat %s %s %s %s' starting at the first byte.
MemorySegment {
bytes: [
0x63, 0x61, 0x74, 0x20, 0x25, 0x73, 0x20, 0x25, 0x73, 0x20, 0x25, 0x73,
0x20, 0x25, 0x73, 0x00,
]
.to_vec(),
base_address: 0x6000,
read_flag: true,
write_flag: false,
execute_flag: false,
},
// Contains string: 'str1 str2 str3 str4'
MemorySegment {
bytes: [
0x73, 0x74, 0x72, 0x31, 0x20, 0x73, 0x74, 0x72, 0x32, 0x20, 0x73, 0x74,
0x72, 0x33, 0x20, 0x73, 0x74, 0x72, 0x34, 0x00,
]
.to_vec(),
base_address: 0x7000,
read_flag: true,
write_flag: false,
execute_flag: false,
},
],
is_little_endian: true,
}
}
}
#[cfg(test)]
impl Program {
fn add_extern_symbols_to_program(a: Vec<(Tid, ExternSymbol)>) -> Program {
Program {
subs: BTreeMap::new(),
extern_symbols: BTreeMap::from_iter(a),
entry_points: BTreeSet::new(),
address_base_offset: 0x1000u64,
}
}
/// Returns Program with malloc, free and other_function
pub fn mock_x64() -> Program {
let malloc = ExternSymbol::create_extern_symbol(
"malloc",
CallingConvention::mock_x64(),
Some(Datatype::Integer),
Some(Datatype::Pointer),
);
let free = ExternSymbol::create_extern_symbol(
"free",
CallingConvention::mock_x64(),
Some(Datatype::Pointer),
None,
);
let other_function = ExternSymbol::create_extern_symbol(
"other_function",
CallingConvention::mock_x64(),
None,
None,
);
Program::add_extern_symbols_to_program(vec![
(malloc.tid.clone(), malloc),
(free.tid.clone(), free),
(other_function.tid.clone(), other_function),
])
}
/// Returns Program with malloc, free and other_function
pub fn mock_arm32() -> Program {
let malloc = ExternSymbol::create_extern_symbol(
"malloc",
CallingConvention::mock_arm32(),
Some(Datatype::Integer),
Some(Datatype::Pointer),
);
let free = ExternSymbol::create_extern_symbol(
"free",
CallingConvention::mock_arm32(),
Some(Datatype::Pointer),
None,
);
let other_function = ExternSymbol::create_extern_symbol(
"other_function",
CallingConvention::mock_arm32(),
None,
None,
);
Program::add_extern_symbols_to_program(vec![
(malloc.tid.clone(), malloc),
(free.tid.clone(), free),
(other_function.tid.clone(), other_function),
])
}
}
#[cfg(test)]
impl Project {
/// Returns project with x64 calling convention and mocked program.
pub fn mock_x64() -> Project {
let mut none_cconv_register: Vec<Variable> = vec![
"RAX", "RBX", "RSP", "RBP", "R10", "R11", "R12", "R13", "R14", "R15",
]
.into_iter()
.map(|name| variable!(format!("{name}:8")))
.collect();
let mut integer_register = CallingConvention::mock_x64().integer_parameter_register;
integer_register.append(&mut none_cconv_register);
let calling_conventions: BTreeMap<String, CallingConvention> =
BTreeMap::from([("__stdcall".to_string(), CallingConvention::mock_x64())]);
Project {
program: Term {
tid: Tid::new("program_tid"),
term: Program::mock_x64(),
},
cpu_architecture: "x86_64".to_string(),
stack_pointer_register: variable!("RSP:8"),
calling_conventions,
register_set: integer_register.iter().cloned().collect(),
datatype_properties: DatatypeProperties::mock_x64(),
runtime_memory_image: RuntimeMemoryImage::mock(),
}
}
pub fn mock_arm32() -> Project {
let none_cconv_4byte_register: Vec<Variable> = vec!["r12", "r14", "r15"]
.into_iter()
.map(|name| variable!(format!("{name}:4")))
.collect();
let none_cconv_16byte_register: Vec<Variable> = vec![
"q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15",
]
.into_iter()
.map(|name| variable!(format!("{name}:16")))
.collect();
let callee_saved_register = CallingConvention::mock_arm32().callee_saved_register;
let integer_register = CallingConvention::mock_arm32()
.integer_parameter_register
.into_iter()
.chain(none_cconv_4byte_register)
.chain(none_cconv_16byte_register)
.chain(callee_saved_register);
Project {
program: Term {
tid: Tid::new("program_tid"),
term: Program::mock_arm32(),
},
cpu_architecture: "arm32".to_string(),
stack_pointer_register: variable!("sp:4"),
calling_conventions: BTreeMap::from([(
"__stdcall".to_string(),
CallingConvention::mock_arm32(),
)]),
register_set: integer_register.collect(),
datatype_properties: DatatypeProperties::mock_arm32(),
runtime_memory_image: RuntimeMemoryImage::mock(),
}
}
}
//! This module contains the implementations of various builder functions
//! for lower intermediate representation terms [Expression](crate::intermediate_representation::Expression),
//! [Jmp](crate::intermediate_representation::Expression), [Def](crate::intermediate_representation::Expression),
//! [DatatypeProperties](crate::intermediate_representation::Expression),
//! [Blk](crate::intermediate_representation::Expression),
//! [Sub](crate::intermediate_representation::Expression),
//! [CallingConvention](crate::intermediate_representation::Expression),
//! [Arg](crate::intermediate_representation::Expression) and
//! [ExternSymbol](crate::intermediate_representation::Expression)
//!
#[cfg(test)]
use crate::{expr, intermediate_representation::*, variable};
#[cfg(test)]
impl Expression {
/// Shortcut for creating a cast expression
#[cfg(test)]
pub fn cast(self, op: CastOpType) -> Expression {
Expression::Cast {
op,
size: ByteSize::new(8),
arg: Box::new(self),
}
}
/// Shortcut for creating a subpiece expression
#[cfg(test)]
pub fn subpiece(self, low_byte: ByteSize, size: ByteSize) -> Expression {
Expression::Subpiece {
low_byte,
size,
arg: Box::new(self),
}
}
/// Shortcut for creating unary operation expressions.
#[cfg(test)]
pub fn un_op(self, op: UnOpType) -> Expression {
Expression::UnOp {
op,
arg: Box::new(self),
}
}
}
/// ## Helper functions for building defs
#[cfg(test)]
impl Def {
/// Shortcut for creating a assign def
pub fn assign(tid: &str, var: Variable, value: Expression) -> Term<Def> {
Term {
tid: Tid::new(tid),
term: Def::Assign { var, value },
}
}
/// Shortcut for assign def of temp variable. Note: bytesize is 4.
pub fn pointer_plus_offset_to_temp_var(
tid: &str,
tmp_name: &str,
pointer: &str,
offset: i64,
) -> Term<Def> {
Def::assign(
tid,
Variable {
name: String::from(tmp_name),
size: ByteSize::new(4),
is_temp: true,
},
expr!(format!("{}:4 + {}:4", pointer, offset)),
)
}
/// Shortcut for store def from temp variable. Note: bytesize is 4.
pub fn store_var_content_at_temp_var(tid: &str, tmp_name: &str, var: &str) -> Term<Def> {
Term {
tid: Tid::new(tid),
term: Def::Store {
address: Expression::Var(Variable {
name: String::from(tmp_name),
size: ByteSize::new(4),
is_temp: true,
}),
value: expr!(format!("{}:4", var)),
},
}
}
/// Shortcut fir load def from temp variable. Note, bytesize is 4
pub fn load_var_content_from_temp_var(tid: &str, var: &str, tmp_name: &str) -> Term<Def> {
Term {
tid: Tid::new(tid),
term: Def::Load {
var: variable!(format!("{}:4", var)),
address: Expression::Var(Variable {
name: String::from(tmp_name),
size: ByteSize::new(4),
is_temp: true,
}),
},
}
}
}
/// ## Helper functions for building jmps
#[cfg(test)]
impl Jmp {
/// Shortcut for creating a call
pub fn call(tid: &str, target_tid: &str, return_tid: Option<&str>) -> Term<Jmp> {
let return_tid = return_tid.map(|tid_name| Tid::new(tid_name));
Term {
tid: Tid::new(tid),
term: Jmp::Call {
target: Tid::new(target_tid),
return_: return_tid,
},
}
}
/// Shortcut for creating a branch
pub fn branch(tid: &str, target_tid: &str) -> Term<Jmp> {
Term {
tid: Tid::new(tid),
term: Jmp::Branch(Tid::new(target_tid)),
}
}
}
/// ## Helper functions for datatype properties
#[cfg(test)]
impl DatatypeProperties {
pub fn mock() -> DatatypeProperties {
DatatypeProperties {
char_size: ByteSize::new(1),
double_size: ByteSize::new(8),
float_size: ByteSize::new(4),
integer_size: ByteSize::new(4),
long_double_size: ByteSize::new(8),
long_long_size: ByteSize::new(8),
long_size: ByteSize::new(4),
pointer_size: ByteSize::new(8),
short_size: ByteSize::new(2),
}
}
/// Datatype sizes according to System V ABI
pub fn mock_x64() -> DatatypeProperties {
DatatypeProperties {
char_size: ByteSize::new(1),
double_size: ByteSize::new(8),
float_size: ByteSize::new(4),
integer_size: ByteSize::new(4),
long_double_size: ByteSize::new(16),
long_long_size: ByteSize::new(8),
long_size: ByteSize::new(8),
pointer_size: ByteSize::new(8),
short_size: ByteSize::new(2),
}
}
pub fn mock_arm32() -> DatatypeProperties {
DatatypeProperties {
char_size: ByteSize::new(1),
double_size: ByteSize::new(8),
float_size: ByteSize::new(4),
integer_size: ByteSize::new(4),
long_double_size: ByteSize::new(8),
long_long_size: ByteSize::new(8),
long_size: ByteSize::new(4),
pointer_size: ByteSize::new(4),
short_size: ByteSize::new(2),
}
}
}
#[cfg(test)]
impl Blk {
/// Creates empty block with given tid.
pub fn mock_with_tid(tid: &str) -> Term<Blk> {
Term {
tid: Tid::new(tid),
term: Blk {
defs: Vec::new(),
jmps: Vec::new(),
indirect_jmp_targets: Vec::new(),
},
}
}
/// Creates empty block with tid "block".
pub fn mock() -> Term<Blk> {
Self::mock_with_tid("block")
}
}
#[cfg(test)]
impl Sub {
pub fn mock(name: impl ToString) -> Term<Sub> {
Term {
tid: Tid::new(name.to_string()),
term: Sub {
name: name.to_string(),
blocks: Vec::new(),
calling_convention: None,
},
}
}
}
/// Wrapper for subpiece to model float register for argument passing
#[cfg(test)]
fn create_float_register_subpiece(
name: &str,
reg_size: u64,
low_byte: u64,
size: u64,
) -> Expression {
Expression::subpiece(
expr!(format!("{name}:{reg_size}")),
ByteSize::new(low_byte),
ByteSize::new(size),
)
}
#[cfg(test)]
impl CallingConvention {
/// Creates System V Calling Convention with Advanced Vector Extensions 512
pub fn mock_x64() -> CallingConvention {
CallingConvention {
name: "__stdcall".to_string(), // so that the mock is useable as standard calling convention in tests
integer_parameter_register: vec![
variable!("RDI:8"),
variable!("RSI:8"),
variable!("RDX:8"),
variable!("RCX:8"),
variable!("R8:8"),
variable!("R9:8"),
],
// ABI: first 8 Bytes of ZMM0-ZMM7 for float parameter
// Ghidra: first 8 Bytes of YMM0-YMM7 for float parameter
float_parameter_register: vec![
create_float_register_subpiece("ZMM0", 64, 0, 8),
create_float_register_subpiece("ZMM1", 64, 0, 8),
create_float_register_subpiece("ZMM2", 64, 0, 8),
create_float_register_subpiece("ZMM3", 64, 0, 8),
create_float_register_subpiece("ZMM4", 64, 0, 8),
create_float_register_subpiece("ZMM5", 64, 0, 8),
create_float_register_subpiece("ZMM6", 64, 0, 8),
create_float_register_subpiece("ZMM7", 64, 0, 8),
],
integer_return_register: vec![variable!("RAX:8"), variable!("RDX:8")],
// ABI: XMM0-XMM1 float return register
// Ghidra: uses XMM0 only
float_return_register: vec![create_float_register_subpiece("ZMM0", 64, 0, 8)],
callee_saved_register: vec![
variable!("RBP:8"),
variable!("RBX:8"),
variable!("RSP:8"),
variable!("R12:8"),
variable!("R13:8"),
variable!("R14:8"),
variable!("R15:8"),
],
}
}
/// Following ARM32 ABI with MVE Extention
pub fn mock_arm32() -> CallingConvention {
CallingConvention {
name: "__stdcall".to_string(), // so that the mock is useable as standard calling convention in tests
integer_parameter_register: vec![
variable!("r0:4"),
variable!("r1:4"),
variable!("r2:4"),
variable!("r3:4"),
],
// ABI: q0-q3 used for argument passing
// Ghidra: uses q0-q1 only
float_parameter_register: vec![
create_float_register_subpiece("q0", 16, 0, 4),
create_float_register_subpiece("q0", 16, 4, 4),
create_float_register_subpiece("q0", 16, 8, 4),
create_float_register_subpiece("q0", 16, 12, 4),
create_float_register_subpiece("q1", 16, 0, 4),
create_float_register_subpiece("q1", 16, 4, 4),
create_float_register_subpiece("q1", 16, 8, 4),
create_float_register_subpiece("q1", 16, 12, 4),
],
// ABI: r0-r1 used as integer return register
// Ghidra uses r0 only
integer_return_register: vec![
variable!("r0:4"),
variable!("r1:4"),
variable!("r2:4"),
variable!("r3:4"),
],
// ABI: whole q0 used as float return
// Ghidra: uses first 8 Bytes of q0 only
float_return_register: vec![create_float_register_subpiece("q0", 16, 0, 4)],
callee_saved_register: vec![
variable!("r4:4"),
variable!("r5:4"),
variable!("r6:4"),
variable!("r7:4"),
variable!("r8:4"),
variable!("r9:4"),
variable!("r10:4"),
variable!("r11:4"),
variable!("r13:4"),
variable!("q4:16"),
variable!("q5:16"),
variable!("q6:16"),
variable!("q7:16"),
],
}
}
}
#[cfg(test)]
impl Arg {
pub fn mock_register(name: impl ToString, size_in_bytes: impl Into<ByteSize>) -> Arg {
Arg::Register {
expr: expr!(format!("{}:{}", name.to_string(), size_in_bytes.into())),
data_type: None,
}
}
pub fn mock_register_with_data_type(
name: impl ToString,
size_in_bytes: impl Into<ByteSize>,
data_type: Option<Datatype>,
) -> Arg {
Arg::Register {
expr: expr!(format!("{}:{}", name.to_string(), size_in_bytes.into())),
data_type,
}
}
pub fn mock_pointer_register(name: impl ToString, size_in_bytes: impl Into<ByteSize>) -> Arg {
Arg::Register {
expr: expr!(format!("{}:{}", name.to_string(), size_in_bytes.into())),
data_type: Some(Datatype::Pointer),
}
}
}
#[cfg(test)]
impl ExternSymbol {
pub fn mock_x64(name: impl ToString) -> ExternSymbol {
ExternSymbol {
tid: Tid::new(name.to_string()),
addresses: vec!["UNKNOWN".to_string()],
name: name.to_string(),
calling_convention: Some("__stdcall".to_string()),
parameters: vec![Arg::mock_register("RDI", 8)],
return_values: vec![Arg::mock_register("RAX", 8)],
no_return: false,
has_var_args: false,
}
}
pub fn mock_arm32(name: impl ToString) -> ExternSymbol {
ExternSymbol {
tid: Tid::new(name.to_string()),
addresses: vec!["UNKNOWN".to_string()],
name: name.to_string(),
calling_convention: Some("__stdcall".to_string()),
parameters: vec![Arg::mock_register("r0", 4)],
return_values: vec![Arg::mock_register("r0", 4)],
no_return: false,
has_var_args: false,
}
}
pub fn mock_sprintf_x64() -> Self {
ExternSymbol {
tid: Tid::new("sprintf"),
addresses: vec!["UNKNOWN".to_string()],
name: "sprintf".to_string(),
calling_convention: Some("__stdcall".to_string()),
parameters: vec![Arg::mock_register("RDI", 8), Arg::mock_register("RSI", 8)],
return_values: vec![Arg::mock_register("RAX", 8)],
no_return: false,
has_var_args: true,
}
}
/// Returns extern symbol with argument/return register according to calling convention
pub fn create_extern_symbol(
name: &str,
cconv: CallingConvention,
arg_type: Option<Datatype>,
return_type: Option<Datatype>,
) -> ExternSymbol {
ExternSymbol {
tid: Tid::new(name),
addresses: vec![],
name: name.to_string(),
calling_convention: Some(cconv.name),
parameters: match arg_type {
Some(data_type) => {
vec![Arg::from_var(
cconv.integer_parameter_register[0].clone(),
Some(data_type),
)]
}
None => vec![],
},
return_values: match return_type {
Some(data_type) => {
vec![Arg::from_var(
cconv.integer_return_register[0].clone(),
Some(data_type),
)]
}
None => vec![],
},
no_return: false,
has_var_args: false,
}
}
}
...@@ -29,18 +29,3 @@ impl Display for Variable { ...@@ -29,18 +29,3 @@ impl Display for Variable {
Ok(()) Ok(())
} }
} }
#[cfg(test)]
mod tests {
use super::*;
impl Variable {
pub fn mock(name: impl ToString, size_in_bytes: impl Into<ByteSize>) -> Variable {
Variable {
name: name.to_string(),
size: size_in_bytes.into(),
is_temp: false,
}
}
}
}
use crate::intermediate_representation::CastOpType;
use super::*; use super::*;
use crate::{def, expr, intermediate_representation::*, variable};
struct Setup<'a> { struct Setup<'a> {
register_map: HashMap<&'a String, &'a RegisterProperties>, register_map: HashMap<&'a String, &'a RegisterProperties>,
...@@ -61,24 +60,24 @@ impl<'a> Setup<'a> { ...@@ -61,24 +60,24 @@ impl<'a> Setup<'a> {
}, },
int_sub_expr: Expression::BinOp { int_sub_expr: Expression::BinOp {
op: BinOpType::IntSub, op: BinOpType::IntSub,
lhs: Box::new(Expression::Var(Variable::mock("EAX", 4))), lhs: Box::new(expr!("EAX:4")),
rhs: Box::new(Expression::Var(Variable::mock("ECX", 4))), rhs: Box::new(expr!("ECX:4")),
}, },
int_sub_subpiece_expr: Expression::BinOp { int_sub_subpiece_expr: Expression::BinOp {
op: BinOpType::IntSub, op: BinOpType::IntSub,
lhs: Box::new(Expression::Subpiece { lhs: Box::new(Expression::Subpiece {
low_byte: ByteSize::new(0), low_byte: ByteSize::new(0),
size: ByteSize::new(4), size: ByteSize::new(4),
arg: Box::new(Expression::Var(Variable::mock("RAX", 8))), arg: Box::new(expr!("RAX:8")),
}), }),
rhs: Box::new(Expression::Subpiece { rhs: Box::new(Expression::Subpiece {
low_byte: ByteSize::new(0), low_byte: ByteSize::new(0),
size: ByteSize::new(4), size: ByteSize::new(4),
arg: Box::new(Expression::Var(Variable::mock("RCX", 8))), arg: Box::new(expr!("RCX:8")),
}), }),
}, },
eax_variable: Expression::Var(Variable::mock("EAX", 4)), eax_variable: expr!("EAX:4"),
rax_variable: Expression::Var(Variable::mock("RAX", 8)), rax_variable: expr!("RAX:8"),
} }
} }
} }
...@@ -244,58 +243,34 @@ fn piecing_or_zero_extending() { ...@@ -244,58 +243,34 @@ fn piecing_or_zero_extending() {
register_map.insert(&setup.rcx_name, &setup.rcx_register); register_map.insert(&setup.rcx_name, &setup.rcx_register);
register_map.insert(&setup.ah_name, &setup.ah_register); register_map.insert(&setup.ah_name, &setup.ah_register);
let eax_assign = Term { let eax_assign = def!["eax_assign: EAX:4 = 0:4"];
tid: Tid::new("eax_assign"), let load_to_eax = def!["load_to_eax: EAX:4 := Load from 0:8"];
term: Def::Assign { let ah_assign = def!["ah_assign: AH:1 = 0:1"];
var: Variable::mock("EAX", 4),
value: Expression::const_from_i32(0),
},
};
let load_to_eax = Term {
tid: Tid::new("load_to_eax"),
term: Def::Load {
var: Variable::mock("EAX", 4),
address: Expression::const_from_i64(0),
},
};
let ah_assign = Term {
tid: Tid::new("ah_assign"),
term: Def::Assign {
var: Variable::mock("AH", 1),
value: Expression::Const(Bitvector::from_i8(0)),
},
};
let zext_eax_to_rax = Term { let zext_eax_to_rax = Term {
tid: Tid::new("zext_eax_to_rax"), tid: Tid::new("zext_eax_to_rax"),
term: Def::Assign { term: Def::Assign {
var: Variable::mock("RAX", 8), var: variable!("RAX:8"),
value: Expression::cast(setup.eax_variable.clone(), CastOpType::IntZExt), value: Expression::cast(setup.eax_variable.clone(), CastOpType::IntZExt),
}, },
}; };
let zext_ah_to_eax = Term { let zext_ah_to_eax = Term {
tid: Tid::new("zext_ah_to_eax"), tid: Tid::new("zext_ah_to_eax"),
term: Def::Assign { term: Def::Assign {
var: Variable::mock("EAX", 4), var: variable!("EAX:4"),
value: Expression::cast( value: Expression::cast(expr!("AH:1"), CastOpType::IntZExt),
Expression::Var(Variable::mock("AH", 1)),
CastOpType::IntZExt,
),
}, },
}; };
let zext_ah_to_rax = Term { let zext_ah_to_rax = Term {
tid: Tid::new("zext_ah_to_rax"), tid: Tid::new("zext_ah_to_rax"),
term: Def::Assign { term: Def::Assign {
var: Variable::mock("RAX", 8), var: variable!("RAX:8"),
value: Expression::cast( value: Expression::cast(expr!("AH:1"), CastOpType::IntZExt),
Expression::Var(Variable::mock("AH", 1)),
CastOpType::IntZExt,
),
}, },
}; };
let zext_eax_to_rcx = Term { let zext_eax_to_rcx = Term {
tid: Tid::new("zext_eax_to_rcx"), tid: Tid::new("zext_eax_to_rcx"),
term: Def::Assign { term: Def::Assign {
var: Variable::mock("RCX", 8), var: variable!("RCX:8"),
value: Expression::cast(setup.eax_variable.clone(), CastOpType::IntZExt), value: Expression::cast(setup.eax_variable.clone(), CastOpType::IntZExt),
}, },
}; };
......
use std::collections::BTreeSet; use std::collections::BTreeSet;
use crate::{ use crate::{abstract_domain::IntervalDomain, expr, intermediate_representation::*, variable};
abstract_domain::IntervalDomain,
intermediate_representation::{Bitvector, Tid},
};
use super::*; use super::*;
fn mock_pi_state() -> PointerInferenceState { fn mock_pi_state() -> PointerInferenceState {
PointerInferenceState::new( PointerInferenceState::new(&variable!("RSP:8"), Tid::new("func"), BTreeSet::new())
&Variable::mock("RSP", 8 as u64),
Tid::new("func"),
BTreeSet::new(),
)
} }
#[test] #[test]
...@@ -24,20 +17,14 @@ fn test_get_variable_parameters() { ...@@ -24,20 +17,14 @@ fn test_get_variable_parameters() {
format_string_index_map.insert("sprintf".to_string(), 1); format_string_index_map.insert("sprintf".to_string(), 1);
let global_address = Bitvector::from_str_radix(16, "5000").unwrap(); let global_address = Bitvector::from_str_radix(16, "5000").unwrap();
pi_state.set_register( pi_state.set_register(
&Variable::mock("RSI", 8 as u64), &variable!("RSI:8"),
IntervalDomain::new(global_address.clone(), global_address).into(), IntervalDomain::new(global_address.clone(), global_address).into(),
); );
let project = Project::mock_x64(); let project = Project::mock_x64();
let mut output: Vec<Arg> = Vec::new(); let mut output: Vec<Arg> = Vec::new();
output.push(Arg::from_var( output.push(Arg::from_var(variable!("RDX:8"), Some(Datatype::Char)));
Variable::mock("RDX", 8), output.push(Arg::from_var(variable!("RCX:8"), Some(Datatype::Integer)));
Some(Datatype::Char),
));
output.push(Arg::from_var(
Variable::mock("RCX", 8),
Some(Datatype::Integer),
));
assert_eq!( assert_eq!(
output, output,
...@@ -50,14 +37,11 @@ fn test_get_variable_parameters() { ...@@ -50,14 +37,11 @@ fn test_get_variable_parameters() {
.unwrap() .unwrap()
); );
output = vec![Arg::from_var( output = vec![Arg::from_var(variable!("RDX:8"), Some(Datatype::Pointer))];
Variable::mock("RDX", 8),
Some(Datatype::Pointer),
)];
let global_address = Bitvector::from_str_radix(16, "500c").unwrap(); let global_address = Bitvector::from_str_radix(16, "500c").unwrap();
pi_state.set_register( pi_state.set_register(
&Variable::mock("RSI", 8 as u64), &variable!("RSI:8"),
IntervalDomain::new(global_address.clone(), global_address).into(), IntervalDomain::new(global_address.clone(), global_address).into(),
); );
...@@ -81,7 +65,7 @@ fn test_get_input_format_string() { ...@@ -81,7 +65,7 @@ fn test_get_input_format_string() {
let global_address = Bitvector::from_str_radix(16, "3002").unwrap(); let global_address = Bitvector::from_str_radix(16, "3002").unwrap();
pi_state.set_register( pi_state.set_register(
&Variable::mock("RSI", 8 as u64), &variable!("RSI:8"),
IntervalDomain::new(global_address.clone(), global_address).into(), IntervalDomain::new(global_address.clone(), global_address).into(),
); );
...@@ -199,19 +183,15 @@ fn test_calculate_parameter_locations() { ...@@ -199,19 +183,15 @@ fn test_calculate_parameter_locations() {
let mut expected_args = vec![ let mut expected_args = vec![
Arg::Register { Arg::Register {
expr: Expression::Var(Variable::mock("RDX", ByteSize::new(8))), expr: expr!("RDX:8"),
data_type: Some(Datatype::Integer), data_type: Some(Datatype::Integer),
}, },
Arg::Register { Arg::Register {
expr: Expression::subpiece( expr: Expression::subpiece(expr!("ZMM0:64"), ByteSize::new(0), ByteSize::new(8)),
Expression::Var(Variable::mock("ZMM0", 64)),
ByteSize::new(0),
ByteSize::new(8),
),
data_type: Some(Datatype::Double), data_type: Some(Datatype::Double),
}, },
Arg::Register { Arg::Register {
expr: Expression::Var(Variable::mock("RCX", ByteSize::new(8))), expr: expr!("RCX:8"),
data_type: Some(Datatype::Pointer), data_type: Some(Datatype::Pointer),
}, },
]; ];
...@@ -227,15 +207,15 @@ fn test_calculate_parameter_locations() { ...@@ -227,15 +207,15 @@ fn test_calculate_parameter_locations() {
parameters.push(("s".to_string().into(), ByteSize::new(8))); parameters.push(("s".to_string().into(), ByteSize::new(8)));
expected_args.push(Arg::Register { expected_args.push(Arg::Register {
expr: Expression::Var(Variable::mock("R8", ByteSize::new(8))), expr: expr!("R8:8"),
data_type: Some(Datatype::Pointer), data_type: Some(Datatype::Pointer),
}); });
expected_args.push(Arg::Register { expected_args.push(Arg::Register {
expr: Expression::Var(Variable::mock("R9", ByteSize::new(8))), expr: expr!("R9:8"),
data_type: Some(Datatype::Pointer), data_type: Some(Datatype::Pointer),
}); });
expected_args.push(Arg::Stack { expected_args.push(Arg::Stack {
address: Expression::Var(Variable::mock("RSP", 8)).plus_const(8), address: expr!("RSP:8 + 8:8"),
size: ByteSize::new(8), size: ByteSize::new(8),
data_type: Some(Datatype::Pointer), data_type: Some(Datatype::Pointer),
}); });
...@@ -251,15 +231,10 @@ fn test_calculate_parameter_locations() { ...@@ -251,15 +231,10 @@ fn test_calculate_parameter_locations() {
fn test_create_stack_arg() { fn test_create_stack_arg() {
assert_eq!( assert_eq!(
Arg::Stack { Arg::Stack {
address: Expression::Var(Variable::mock("RSP", 8)).plus_const(8), address: expr!("RSP:8 + 8:8"),
size: ByteSize::new(8), size: ByteSize::new(8),
data_type: Some(Datatype::Pointer), data_type: Some(Datatype::Pointer),
}, },
create_stack_arg( create_stack_arg(ByteSize::new(8), 8, Datatype::Pointer, &variable!("RSP:8")),
ByteSize::new(8),
8,
Datatype::Pointer,
&Variable::mock("RSP", 8)
),
) )
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment