Unverified Commit decc1254 by van den Bosch Committed by GitHub

Test refactoring collection (#386)

parent dcbdac1e
......@@ -186,12 +186,12 @@ fn extract_results<'a>(
///
/// This uses the expression propagation of basic blocks, thus performs intra-basic-block insertion of expressions.
fn insert_expressions(
inseratables: HashMap<Tid, HashMap<Variable, Expression>>,
insertables: HashMap<Tid, HashMap<Variable, Expression>>,
program: &mut Program,
) {
for sub in program.subs.values_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(
fn merge_same_var_assignments(project: &mut Project) {
for sub in project.program.term.subs.values_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.
///
/// 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> {
}
#[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.
fn inter_block_propagation() {
let mut project = mock_project();
......
......@@ -333,6 +333,7 @@ mod tests {
create_computation_with_top_down_worklist_order,
},
},
expr,
intermediate_representation::*,
};
use std::collections::{BTreeMap, HashMap, HashSet};
......@@ -353,7 +354,7 @@ mod tests {
let mut callee_block = new_block("callee block");
callee_block.term.jmps.push(Term {
tid: Tid::new("ret"),
term: Jmp::Return(Expression::const_from_i32(42)),
term: Jmp::Return(expr!("42:4")),
});
let called_function = Term {
......
......@@ -235,14 +235,14 @@ pub mod tests {
// main -> callee1 -> callee2
let mut func = Sub::mock("main");
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);
func.term.blocks.push(call_blk);
project.program.term.subs.insert(Tid::new("main"), func);
let mut func = Sub::mock("callee1");
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);
func.term.blocks.push(call_blk);
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> {
#[cfg(test)]
mod tests {
use crate::expr;
use super::*;
use std::collections::{BTreeMap, BTreeSet};
use std::iter::FromIterator;
......@@ -526,7 +528,7 @@ mod tests {
};
let return_term = Term {
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_term = Term {
......@@ -559,7 +561,7 @@ mod tests {
};
let cond_jump = Jmp::CBranch {
target: Tid::new("sub1_blk1"),
condition: Expression::Const(Bitvector::from_u8(0)),
condition: expr!("0:1"),
};
let cond_jump_term = Term {
tid: Tid::new("cond_jump"),
......@@ -618,7 +620,7 @@ mod tests {
fn add_indirect_jumps() {
let indirect_jmp_term = Term {
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");
blk_tid.address = "00001000".to_string();
......
use super::*;
use crate::intermediate_representation::Variable;
use crate::{intermediate_representation::*, variable};
use std::collections::BTreeMap;
fn new_abstract_object() -> AbstractObject {
......@@ -23,7 +23,7 @@ fn bv(number: i64) -> ValueDomain {
fn new_id(tid: &str, reg_name: &str) -> AbstractIdentifier {
AbstractIdentifier::new(
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::*;
......@@ -10,7 +11,7 @@ fn bv(value: i64) -> ValueDomain {
fn new_id(name: &str) -> AbstractIdentifier {
AbstractIdentifier::new(
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() {
.add_signed_greater_equal_bound(&Bitvector::from_i64(6))
.unwrap();
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]
......@@ -363,11 +363,11 @@ fn from_fn_sig() {
Data::from_target(new_id("func", "RSP"), bv(0).into())
);
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())
);
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())
);
}
......
......@@ -167,11 +167,11 @@ fn specialize_by_binop() {
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&Variable::mock("FLAG1", 1u64)),
state.get_register(&variable!("FLAG1:1")),
bitvec!("1:1").into()
);
assert_eq!(
state.get_register(&Variable::mock("FLAG2", 1u64)),
state.get_register(&variable!("FLAG2:1")),
bitvec!("1:1").into()
);
// Expr = (FLAG bool_and 1 = Const)
......
......@@ -285,9 +285,7 @@ fn supports_commutative_and() {
Expression::BinOp {
op: BinOpType::IntAnd,
lhs: Box::new(expr!("RSP:8")),
rhs: Box::new(Expression::const_from_apint(ApInt::from_u64(
0xFFFFFFFF_FFFFFFFF << 4, // 16 Byte alignment
))),
rhs: Box::new(expr!(format!("{}:8", 0xFFFFFFFF_FFFFFFFF_u64 << 4))),
},
);
let bitmask_and_var = Def::assign(
......@@ -330,9 +328,7 @@ fn skips_empty_blocks() {
lhs: Box::new(Expression::Var(
Project::mock_x64().stack_pointer_register.clone(),
)),
rhs: Box::new(Expression::const_from_apint(ApInt::from_u64(
0xFFFFFFFF_FFFFFFFF << 4, // 16 Byte alignment
))),
rhs: Box::new(expr!(format!("{}:8", 0xFFFFFFFF_FFFFFFFF_u64 << 4))),
},
);
// get project with empty block
......
......@@ -7,7 +7,7 @@ use crate::{
AbstractDomain, DataDomain, DomainInsertion, HasTop, IntervalDomain, TryToBitvec,
},
analysis::string_abstraction::{context::Context, state::State},
intermediate_representation::ExternSymbol,
intermediate_representation::*,
};
use std::collections::BTreeMap;
......@@ -206,7 +206,8 @@ mod tests {
context::symbol_calls::tests::Setup,
tests::mock_project_with_intraprocedural_control_flow,
},
intermediate_representation::{Bitvector, Tid, Variable},
intermediate_representation::{Bitvector, Tid},
variable,
};
use std::collections::{BTreeMap, BTreeSet};
......@@ -226,12 +227,12 @@ mod tests {
let stack_id = AbstractIdentifier::new(
Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(),
AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
);
let heap_id = AbstractIdentifier::new(
Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("r5", 4)).unwrap(),
AbstractLocation::from_var(&variable!("r5:4")).unwrap(),
);
let mut parameter_pointer: DataDomain<IntervalDomain> =
......@@ -250,7 +251,7 @@ mod tests {
setup
.pi_state_before_symbol_call
.set_register(&Variable::mock("r1", 4), parameter_pointer);
.set_register(&variable!("r1:4"), parameter_pointer);
setup
.state_before_call
......@@ -314,7 +315,7 @@ mod tests {
let stack_id = AbstractIdentifier::new(
Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(),
AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
);
let expected_data: DataDomain<IntervalDomain> =
......@@ -365,12 +366,12 @@ mod tests {
let return_targets = setup
.pi_state_before_symbol_call
.get_register(&Variable::mock("r0", 4));
.get_register(&variable!("r0:4"));
let input_target: DataDomain<IntervalDomain> = DataDomain::from(
setup
.pi_state_before_symbol_call
.get_register(&Variable::mock("r1", 4))
.get_register(&variable!("r1:4"))
.get_absolute_value()
.unwrap()
.clone(),
......@@ -407,14 +408,14 @@ mod tests {
let return_targets = setup
.pi_state_before_symbol_call
.get_register(&Variable::mock("r0", 4))
.get_register(&variable!("r0:4"))
.get_relative_values()
.clone();
let input_target: DataDomain<IntervalDomain> = DataDomain::from(
setup
.pi_state_before_symbol_call
.get_register(&Variable::mock("r1", 4))
.get_register(&variable!("r1:4"))
.get_absolute_value()
.unwrap()
.clone(),
......@@ -462,11 +463,11 @@ mod tests {
fn test_has_multiple_targets() {
let stack_id = AbstractIdentifier::new(
Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(),
AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
);
let heap_id = AbstractIdentifier::new(
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.
let mut data: DataDomain<IntervalDomain> = DataDomain::mock_from_target_map(
......
......@@ -199,7 +199,7 @@ mod tests {
use crate::abstract_domain::{AbstractIdentifier, AbstractLocation, CharacterInclusionDomain};
use crate::analysis::pointer_inference::PointerInference as PointerInferenceComputation;
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]
fn test_handle_scanf_calls() {
......@@ -222,7 +222,7 @@ mod tests {
let stack_id = AbstractIdentifier::new(
Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(),
AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
);
assert!(new_state
......@@ -285,7 +285,7 @@ mod tests {
#[test]
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 project = mock_project_with_intraprocedural_control_flow(
......@@ -299,7 +299,7 @@ mod tests {
let stack_id = AbstractIdentifier::new(
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();
......@@ -309,7 +309,7 @@ mod tests {
data_type: Some(Datatype::Pointer),
};
let stack_arg = Arg::Stack {
address: Expression::Var(Variable::mock("sp", 4)),
address: expr!("sp:4"),
size: ByteSize::new(4),
data_type: Some(Datatype::Pointer),
};
......@@ -361,7 +361,7 @@ mod tests {
#[test]
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 project = mock_project_with_intraprocedural_control_flow(
......@@ -375,7 +375,7 @@ mod tests {
let stack_id = AbstractIdentifier::new(
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();
......@@ -384,7 +384,7 @@ mod tests {
data_type: Some(Datatype::Pointer),
};
let stack_arg = Arg::Stack {
address: Expression::Var(Variable::mock("sp", 4)),
address: expr!("sp:4"),
size: ByteSize::new(4),
data_type: Some(Datatype::Pointer),
};
......@@ -448,7 +448,7 @@ mod tests {
let stack_id = AbstractIdentifier::new(
Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(),
AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
);
let return_target: DataDomain<IntervalDomain> =
......@@ -532,7 +532,7 @@ mod tests {
let stack_id = AbstractIdentifier::new(
Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(),
AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
);
let new_state = setup
......@@ -585,7 +585,7 @@ mod tests {
let stack_id = AbstractIdentifier::new(
Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(),
AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
);
let new_state = setup
......@@ -734,7 +734,7 @@ mod tests {
),
(
Arg::Stack {
address: Expression::Var(Variable::mock("sp", 4)),
address: expr!("sp:4"),
size: ByteSize::new(4),
data_type: Some(Datatype::Pointer),
},
......@@ -742,7 +742,7 @@ mod tests {
),
(
Arg::Stack {
address: Expression::Var(Variable::mock("sp", 4)).plus_const(4),
address: expr!("sp:4 + 4:4"),
size: ByteSize::new(4),
data_type: Some(Datatype::Pointer),
},
......
......@@ -3,13 +3,13 @@ use std::collections::BTreeSet;
use super::*;
use crate::abstract_domain::{AbstractIdentifier, AbstractLocation};
use crate::analysis::pointer_inference::PointerInference as PointerInferenceComputation;
use crate::intermediate_representation::{Bitvector, Expression, Tid, Variable};
use crate::{
abstract_domain::{CharacterInclusionDomain, CharacterSet},
analysis::string_abstraction::{
context::symbol_calls::tests::Setup, tests::mock_project_with_intraprocedural_control_flow,
},
};
use crate::{bitvec, expr, intermediate_representation::*, variable};
#[test]
fn test_handle_sprintf_and_snprintf_calls() {
......@@ -29,10 +29,10 @@ fn test_handle_sprintf_and_snprintf_calls() {
let stack_id = AbstractIdentifier::new(
Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(),
AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
);
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!(
return_pointer,
......@@ -68,10 +68,10 @@ fn test_parse_format_string_and_add_new_string_domain() {
let format_string_index: usize = 1;
let stack_id = AbstractIdentifier::new(
Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(),
AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
);
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(
vec![(sprintf_symbol.clone(), vec![true])],
......@@ -174,15 +174,15 @@ fn test_create_string_domain_using_data_type_approximations() {
fn test_create_string_domain_using_constants_and_sub_domains() {
let sprintf_symbol = ExternSymbol::mock_sprintf_symbol_arm();
let string_arg = Arg::Register {
expr: Expression::Var(Variable::mock("r6", 4)),
expr: Expression::Var(variable!("r6:4")),
data_type: Some(Datatype::Pointer),
};
let integer_arg = Arg::Register {
expr: Expression::Var(Variable::mock("r7", 4)),
expr: Expression::Var(variable!("r7:4")),
data_type: Some(Datatype::Integer),
};
let char_arg = Arg::Register {
expr: Expression::Var(Variable::mock("r8", 4)),
expr: Expression::Var(variable!("r8:4")),
data_type: Some(Datatype::Char),
};
......@@ -199,27 +199,21 @@ fn test_create_string_domain_using_constants_and_sub_domains() {
let mut setup: Setup<CharacterInclusionDomain> = Setup::new(&pi_results);
setup.pi_state_before_symbol_call.set_register(
&Variable::mock("r6", 4),
&variable!("r6:4"),
DataDomain::from(IntervalDomain::new(
Bitvector::from_u64(0x3002),
Bitvector::from_u64(0x3002),
bitvec!("0x3002:8"),
bitvec!("0x3002:8"),
)),
);
setup.pi_state_before_symbol_call.set_register(
&Variable::mock("r7", 4),
DataDomain::from(IntervalDomain::new(
Bitvector::from_u64(2),
Bitvector::from_u64(2),
)),
&variable!("r7:4"),
DataDomain::from(IntervalDomain::new(bitvec!("2:8"), bitvec!("2:8"))),
);
setup.pi_state_before_symbol_call.set_register(
&Variable::mock("r8", 4),
DataDomain::from(IntervalDomain::new(
Bitvector::from_u64(0x42),
Bitvector::from_u64(0x42),
)),
&variable!("r8:4"),
DataDomain::from(IntervalDomain::new(bitvec!("0x42:8"), bitvec!("0x42:8"))),
);
let result_domain = setup
......@@ -341,15 +335,15 @@ fn test_no_specifiers() {
fn test_fetch_constant_and_domain_for_format_specifier() {
let sprintf_symbol = ExternSymbol::mock_sprintf_symbol_arm();
let string_arg = Arg::Register {
expr: Expression::Var(Variable::mock("r6", 4)),
expr: expr!("r6:4"),
data_type: Some(Datatype::Pointer),
};
let integer_arg = Arg::Register {
expr: Expression::Var(Variable::mock("r7", 4)),
expr: expr!("r7:4"),
data_type: Some(Datatype::Integer),
};
let char_arg = Arg::Register {
expr: Expression::Var(Variable::mock("r8", 4)),
expr: expr!("r8:4"),
data_type: Some(Datatype::Char),
};
......@@ -408,11 +402,8 @@ fn test_fetch_constant_and_domain_for_format_specifier() {
// Test Case 4: Integer and tracked constant.
setup.pi_state_before_symbol_call.set_register(
&Variable::mock("r7", 4),
DataDomain::from(IntervalDomain::new(
Bitvector::from_u64(2),
Bitvector::from_u64(2),
)),
&variable!("r7:4"),
DataDomain::from(IntervalDomain::new(bitvec!("2:8"), bitvec!("2:8"))),
);
assert_eq!(
......@@ -429,11 +420,8 @@ fn test_fetch_constant_and_domain_for_format_specifier() {
// Test Case 5: Char and tracked constant.
setup.pi_state_before_symbol_call.set_register(
&Variable::mock("r8", 4),
DataDomain::from(IntervalDomain::new(
Bitvector::from_u32(0x42),
Bitvector::from_u32(0x42),
)),
&variable!("r8:4"),
DataDomain::from(IntervalDomain::new(bitvec!("0x42:4"), bitvec!("0x42:4"))),
);
assert_eq!(
......@@ -450,10 +438,10 @@ fn test_fetch_constant_and_domain_for_format_specifier() {
// Test Case 6: String and tracked constant.
setup.pi_state_before_symbol_call.set_register(
&Variable::mock("r6", 4),
&variable!("r6:4"),
DataDomain::from(IntervalDomain::new(
Bitvector::from_u32(0x3002),
Bitvector::from_u32(0x3002),
bitvec!("0x3002:4"),
bitvec!("0x3002:4"),
)),
);
......@@ -471,22 +459,22 @@ fn test_fetch_constant_and_domain_for_format_specifier() {
let stack_id = AbstractIdentifier::new(
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(
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(
Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("r9", 4)).unwrap(),
AbstractLocation::from_var(&variable!("r9:4")).unwrap(),
);
pointer.insert_relative_value(
heap_id.clone(),
IntervalDomain::new(Bitvector::from_i32(0), Bitvector::from_i32(0)),
IntervalDomain::new(bitvec!("0:4"), bitvec!("0:4")),
);
setup
......@@ -499,7 +487,7 @@ fn test_fetch_constant_and_domain_for_format_specifier() {
// Test Case 5: String and tracked domain.
setup
.pi_state_before_symbol_call
.set_register(&Variable::mock("r6", 4), pointer);
.set_register(&variable!("r6:4"), pointer);
let expected_domain = CharacterInclusionDomain::Value((
CharacterSet::Value(BTreeSet::new()),
......@@ -557,7 +545,7 @@ fn test_fetch_subdomains_if_available() {
let stack_id = AbstractIdentifier::new(
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.
......@@ -600,13 +588,13 @@ fn test_fetch_constant_domain_if_available() {
pi_results.compute(false);
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 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 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));
assert_eq!(
......
......@@ -107,11 +107,10 @@ mod tests {
context::symbol_calls::tests::Setup,
tests::mock_project_with_intraprocedural_control_flow,
},
intermediate_representation::{ByteSize, Variable},
intermediate_representation::*,
variable,
};
use super::*;
#[test]
fn test_handle_strcat_and_strncat_calls_with_known_second_input() {
let strcat_symbol = ExternSymbol::mock_strcat_symbol_arm();
......@@ -247,7 +246,7 @@ mod tests {
#[test]
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 project = mock_project_with_intraprocedural_control_flow(
vec![(strcat_symbol.clone(), vec![false])],
......
......@@ -13,10 +13,10 @@ use crate::analysis::pointer_inference::PointerInference as PointerInferenceComp
use crate::analysis::pointer_inference::State as PiState;
use crate::analysis::string_abstraction::state::State;
use crate::analysis::string_abstraction::tests::*;
use crate::intermediate_representation::{Bitvector, ExternSymbol, Project, Sub};
use crate::intermediate_representation::*;
use crate::variable;
use crate::{
abstract_domain::{AbstractIdentifier, AbstractLocation},
intermediate_representation::{Tid, Variable},
utils::symbol_utils::get_symbol_map,
};
......@@ -119,7 +119,7 @@ fn test_handle_generic_symbol_calls() {
let mut setup: Setup<CharacterInclusionDomain> = Setup::new(&pi_results);
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))),
);
......@@ -143,7 +143,7 @@ fn test_handle_unknown_symbol_calls() {
let mut setup: Setup<CharacterInclusionDomain> = Setup::new(&pi_results);
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))),
);
......@@ -174,7 +174,7 @@ fn test_add_new_string_abstract_domain() {
let stack_id = AbstractIdentifier::new(
Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(),
AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
);
let stack_pointer = DataDomain::from_target(
stack_id.clone(),
......@@ -194,7 +194,7 @@ fn test_add_new_string_abstract_domain() {
let heap_id = AbstractIdentifier::new(
Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("r5", 4)).unwrap(),
AbstractLocation::from_var(&variable!("r5:4")).unwrap(),
);
let heap_pointer = DataDomain::from_target(
......@@ -227,12 +227,12 @@ fn test_merge_domains_from_multiple_pointer_targets() {
let stack_id = AbstractIdentifier::new(
Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(),
AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
);
let heap_id = AbstractIdentifier::new(
Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("r5", 4)).unwrap(),
AbstractLocation::from_var(&variable!("r5:4")).unwrap(),
);
let mut domain_pointer: DataDomain<IntervalDomain> =
......@@ -424,7 +424,7 @@ fn test_insert_constant_string_into_format_string() {
fn test_handle_free() {
let free_symbol = ExternSymbol::mock_free_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(
vec![
(malloc_symbol.clone(), vec![]),
......
......@@ -28,8 +28,8 @@ fn test_update_def() {
setup.context.block_first_def_set = HashSet::new();
let assign_def = def!["assign_def: r1:4 = 0x7000:4"];
let load_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 load_def = Def::load_var_content_from_temp_var("load_def", "r5", "r2");
let store_def = Def::store_var_content_at_temp_var("store_def", "r0", "r5");
let new_state = setup
.context
......
......@@ -5,13 +5,16 @@ use crate::{
pointer_inference::State as PiState,
string_abstraction::tests::mock_project_with_intraprocedural_control_flow,
},
expr,
intermediate_representation::*,
variable,
};
use std::collections::BTreeSet;
impl<T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>> State<T> {
pub fn mock_with_default_pi_state(current_sub: Term<Sub>) -> Self {
let pi_state = PointerInferenceState::new(
&Variable::mock("sp", 4 as u64),
&variable!("sp:4"),
current_sub.tid.clone(),
BTreeSet::new(),
);
......@@ -50,7 +53,7 @@ fn test_delete_string_map_entries_if_no_pointer_targets_are_tracked() {
let stack_id = AbstractIdentifier::new(
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(
stack_id.clone(),
......@@ -59,7 +62,7 @@ fn test_delete_string_map_entries_if_no_pointer_targets_are_tracked() {
let heap_id_1 = AbstractIdentifier::new(
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(
......@@ -69,7 +72,7 @@ fn test_delete_string_map_entries_if_no_pointer_targets_are_tracked() {
let heap_id_2 = AbstractIdentifier::new(
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(
......@@ -79,12 +82,12 @@ fn test_delete_string_map_entries_if_no_pointer_targets_are_tracked() {
let heap_id_3 = AbstractIdentifier::new(
Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("r7", 4)).unwrap(),
AbstractLocation::from_var(&variable!("r7:4")).unwrap(),
);
state
.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.unassigned_return_pointer.insert(heap_pointer_2);
......@@ -153,10 +156,10 @@ fn test_handle_assign_and_load() {
let sub = Sub::mock("func");
let mut state: State<CharacterInclusionDomain> = State::mock_with_default_pi_state(sub.clone());
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 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 return_tid = Tid::new("14718");
......@@ -211,7 +214,7 @@ fn test_handle_assign_and_load() {
// Test Case 2: Assign Def with other input
let heap_id = AbstractIdentifier::new(
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(
......@@ -263,19 +266,19 @@ fn test_handle_assign_and_load() {
#[test]
fn test_add_pointer_to_variable_maps_if_tracked() {
let output_var = Variable::mock("r2", 4);
let origin_var = Variable::mock("r5", 4);
let output_var = variable!("r2:4");
let origin_var = variable!("r5:4");
let mut mock_state =
State::<CharacterInclusionDomain>::mock_with_default_pi_state(Sub::mock("func"));
let pi_state = mock_state.get_pointer_inference_state().unwrap().clone();
let heap_id = AbstractIdentifier::new(
Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("r5", 4)).unwrap(),
AbstractLocation::from_var(&variable!("r5:4")).unwrap(),
);
let stack_id = AbstractIdentifier::new(
Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(),
AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
);
let mut source_pointer: DataDomain<IntervalDomain> =
......@@ -339,7 +342,7 @@ fn test_pointer_targets_partially_tracked() {
let heap_id = AbstractIdentifier::new(
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();
......@@ -367,8 +370,8 @@ fn test_pointer_targets_partially_tracked() {
#[test]
fn test_pointer_is_in_pointer_maps() {
let r2_reg = Variable::mock("r2", 4);
let sp_reg = Variable::mock("sp", 4);
let r2_reg = variable!("r2:4");
let sp_reg = variable!("sp:4");
let mut mock_state =
State::<CharacterInclusionDomain>::mock_with_default_pi_state(Sub::mock("func"));
......@@ -401,10 +404,10 @@ fn test_pointer_is_in_pointer_maps() {
#[test]
fn test_handle_store() {
let block_first_def_set: HashSet<(Tid, Tid)> = HashSet::new();
let target_var = Variable::mock("r2", 4);
let value_var = Variable::mock("r3", 4);
let target_var = variable!("r2:4");
let value_var = variable!("r3:4");
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 runtime_memory_image = RuntimeMemoryImage::mock();
let mut mock_state =
......@@ -465,7 +468,7 @@ fn test_handle_store() {
mock_state.set_all_maps_empty();
mock_state
.variable_to_pointer_map
.insert(Variable::mock("r0", 4), string_pointer.clone());
.insert(variable!("r0:4"), string_pointer.clone());
mock_state.handle_store(
&target_location,
......@@ -483,15 +486,15 @@ fn test_handle_store() {
mock_state.set_all_maps_empty();
mock_state
.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 6: Global address pointer in variable.
}
#[test]
fn test_add_pointer_to_stack_map() {
let r2_reg = Variable::mock("r2", 4);
let sp_reg = Variable::mock("sp", 4);
let r2_reg = variable!("r2:4");
let sp_reg = variable!("sp:4");
let target = Expression::Var(r2_reg.clone());
let mut mock_state =
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() {
mock_state
.variable_to_pointer_map
.insert(Variable::mock("r0", 4), top_domain.clone());
.insert(variable!("r0:4"), top_domain.clone());
mock_state
.variable_to_pointer_map
.insert(Variable::mock("r11", 4), top_domain);
.insert(variable!("r11:4"), top_domain);
mock_state
.remove_non_callee_saved_pointer_entries_for_external_symbol(&project, &sprintf_symbol);
assert!(!mock_state
.variable_to_pointer_map
.contains_key(&Variable::mock("r0", 4)));
.contains_key(&variable!("r0:4")));
assert!(mock_state
.variable_to_pointer_map
.contains_key(&Variable::mock("r11", 4)));
.contains_key(&variable!("r11:4")));
}
use crate::{def, defs, expr, intermediate_representation::*, variable};
// 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,
}),
)
}
use crate::{def, defs, intermediate_representation::*};
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(),
);
defs.push(pointer_plus_offset_to_temp_var(
defs.push(Def::pointer_plus_offset_to_temp_var(
&format!("def_6_blk_{}", blk_num),
"$U1050",
"sp",
0,
));
defs.push(store_var_content_at_temp_var(
defs.push(Def::store_var_content_at_temp_var(
&format!("def_7_blk_{}", blk_num),
"$U1050",
"r12",
......@@ -98,13 +54,13 @@ fn mock_defs_for_sprintf(format_known: bool, blk_num: usize) -> Vec<Term<Def>> {
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),
"$U1050",
"sp",
4,
));
defs.push(store_var_content_at_temp_var(
defs.push(Def::store_var_content_at_temp_var(
&format!("def_10_blk_{}", blk_num),
"$U1050",
"r12",
......@@ -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)
];
defs.push(pointer_plus_offset_to_temp_var(
defs.push(Def::pointer_plus_offset_to_temp_var(
&format!("def_2_blk_{}", blk_num),
"$U1050",
"sp",
0,
));
defs.push(store_var_content_at_temp_var(
defs.push(Def::store_var_content_at_temp_var(
&format!("def_3_blk_{}", blk_num),
"$U1050",
"r0",
......@@ -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)
];
defs.push(pointer_plus_offset_to_temp_var(
defs.push(Def::pointer_plus_offset_to_temp_var(
&format!("def_2_blk_{}", blk_num),
"$U1050",
"sp",
0,
));
defs.push(store_var_content_at_temp_var(
defs.push(Def::store_var_content_at_temp_var(
&format!("def_3_blk_{}", blk_num),
"$U1050",
"r3",
......@@ -214,13 +170,13 @@ fn mock_defs_for_sscanf(source_known: bool, format_known: bool, blk_num: usize)
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),
"$U1050",
"sp",
4,
));
defs.push(store_var_content_at_temp_var(
defs.push(Def::store_var_content_at_temp_var(
&format!("def_6_blk_{}", blk_num),
"$U1050",
"r3",
......
......@@ -269,7 +269,7 @@ fn collect_tids_for_cwe_warning(
#[cfg(test)]
pub mod tests {
use super::*;
use crate::intermediate_representation::Variable;
use crate::{intermediate_representation::*, variable};
#[test]
fn test_new() {
......@@ -279,7 +279,7 @@ pub mod tests {
&FunctionSignature::mock_x64(),
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.object_lower_bounds.len(), 1);
......@@ -302,7 +302,7 @@ pub mod tests {
&FunctionSignature::mock_x64(),
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
let address = Data::from_target(stack_id.clone(), Bitvector::from_i64(-12).into());
assert!(state
......
use super::*;
use crate::utils::log::LogMessage;
use std::{
collections::{HashMap, HashSet},
fmt,
};
use std::{collections::HashSet, fmt};
/// A basic block is a sequence of `Def` instructions followed by up to two `Jmp` instructions.
///
......@@ -70,131 +67,6 @@ impl Term<Blk> {
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 {
......@@ -208,101 +80,3 @@ impl fmt::Display for Blk {
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 {
#[cfg(test)]
mod tests {
use super::*;
use crate::intermediate_representation::BinOpType;
use crate::{expr, intermediate_representation::*, variable};
#[test]
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 {
tid: Tid::new("zero_tid"),
term: Def::Assign {
var: Variable {
name: String::from("RAX"),
size: ByteSize::new(8),
is_temp: false,
},
var: variable!("RAX:8"),
value: Expression::Cast {
op: CastOpType::IntZExt,
size: ByteSize::new(8),
arg: Box::new(eax_variable.clone()),
arg: Box::new(expr!("EAX:8")),
},
},
};
......@@ -143,15 +120,11 @@ mod tests {
let zero_extend_but_no_var_def = Term {
tid: Tid::new("zero_tid"),
term: Def::Assign {
var: Variable {
name: String::from("RAX"),
size: ByteSize::new(8),
is_temp: false,
},
var: variable!("RAX:8"),
value: Expression::Cast {
op: CastOpType::IntZExt,
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 {
let non_zero_extend_def = Term {
tid: Tid::new("zero_tid"),
term: Def::Assign {
var: Variable {
name: String::from("RAX"),
size: ByteSize::new(8),
is_temp: false,
},
var: variable!("RAX:8"),
value: Expression::Cast {
op: CastOpType::IntSExt,
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::*;
/// ## Helper functions for building expressions
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
pub fn plus(self, rhs: Expression) -> Expression {
Expression::BinOp {
......@@ -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.
///
/// The bytesize of the value is automatically adjusted to the bytesize of the given expression.
......@@ -100,19 +27,4 @@ impl Expression {
}
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 crate::{expr, intermediate_representation::*};
#[test]
fn trivial_expression_substitution() {
let rax_variable = Expression::Var(Variable::mock("RAX", 8));
let rcx_variable = Expression::Var(Variable::mock("RCX", 8));
let rax_variable = expr!("RAX:8");
let rcx_variable = expr!("RCX:8");
let mut expr = Expression::BinOp {
op: BinOpType::IntXOr,
lhs: Box::new(rax_variable.clone()),
rhs: Box::new(rax_variable.clone()),
};
expr.substitute_trivial_operations();
assert_eq!(
expr,
Expression::Const(Bitvector::zero(ByteSize::new(8).into()))
);
assert_eq!(expr, expr!("0:8"));
let mut expr = Expression::BinOp {
op: BinOpType::IntOr,
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();
assert_eq!(expr, rax_variable);
let sub_expr = Expression::BinOp {
lhs: Box::new(rax_variable.clone()),
op: BinOpType::IntSub,
rhs: Box::new(rcx_variable.clone()),
};
let sub_expr = expr!("RAX:8 - RCX:8");
let mut expr = Expression::BinOp {
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()),
};
expr.substitute_trivial_operations();
......@@ -44,7 +38,7 @@ fn trivial_expression_substitution() {
let mut expr = Expression::BinOp {
op: BinOpType::IntNotEqual,
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();
assert_eq!(
......@@ -108,29 +102,29 @@ fn trivial_expression_substitution() {
arg: Box::new(Expression::Cast {
op: CastOpType::IntSExt,
size: ByteSize::new(8),
arg: Box::new(Expression::Var(Variable::mock("EAX", 4))),
arg: Box::new(expr!("EAX:4")),
}),
};
expr.substitute_trivial_operations();
assert_eq!(expr, Expression::Var(Variable::mock("EAX", 4)));
assert_eq!(expr, expr!("EAX:4"));
let mut expr = Expression::Subpiece {
low_byte: ByteSize::new(4),
size: ByteSize::new(4),
arg: Box::new(Expression::BinOp {
op: BinOpType::Piece,
lhs: Box::new(Expression::Var(Variable::mock("EAX", 4))),
rhs: Box::new(Expression::Var(Variable::mock("EBX", 4))),
lhs: Box::new(expr!("EAX:4")),
rhs: Box::new(expr!("EBX:4")),
}),
};
expr.substitute_trivial_operations();
assert_eq!(expr, Expression::Var(Variable::mock("EAX", 4)));
assert_eq!(expr, expr!("EAX:4"));
let mut expr = Expression::Subpiece {
low_byte: ByteSize::new(0),
size: ByteSize::new(4),
arg: Box::new(Expression::Subpiece {
low_byte: ByteSize::new(2),
size: ByteSize::new(6),
arg: Box::new(Expression::Var(Variable::mock("RAX", 8))),
arg: Box::new(expr!("RAX:8")),
}),
};
expr.substitute_trivial_operations();
......@@ -139,7 +133,7 @@ fn trivial_expression_substitution() {
Expression::Subpiece {
low_byte: ByteSize::new(2),
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() {
lhs: Box::new(Expression::BinOp {
lhs: Box::new(rax_variable.clone()),
op: BinOpType::IntSub,
rhs: Box::new(Expression::Const(Bitvector::from_i64(3))),
rhs: Box::new(expr!("3:8")),
}),
op: BinOpType::IntSub,
rhs: Box::new(Expression::Const(Bitvector::from_i64(4))),
rhs: Box::new(expr!("4:8")),
};
expr.substitute_trivial_operations();
assert_eq!(
......@@ -176,7 +170,7 @@ fn trivial_expression_substitution() {
Expression::BinOp {
lhs: Box::new(rax_variable.clone()),
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() {
use Expression::*;
let sborrow_expr = BinOp {
op: IntSBorrow,
lhs: Box::new(Var(Variable::mock("RAX", 8))),
rhs: Box::new(Var(Variable::mock("RBX", 8))),
lhs: Box::new(expr!("RAX:8")),
rhs: Box::new(expr!("RBX:8")),
};
let a_minus_b_less_zero_expr = BinOp {
op: IntSLess,
lhs: Box::new(BinOp {
op: IntSub,
lhs: Box::new(Var(Variable::mock("RAX", 8))),
rhs: Box::new(Var(Variable::mock("RBX", 8))),
}),
rhs: Box::new(Const(Bitvector::from_u64(0))),
lhs: Box::new(expr!("RAX:8 - RBX:8")),
rhs: Box::new(expr!("0:8")),
};
let mut expr = BinOp {
op: IntNotEqual,
......@@ -207,19 +197,19 @@ fn test_complicated_a_less_than_b_substitution() {
expr.substitute_trivial_operations();
let expected_expr = BinOp {
op: IntSLess,
lhs: Box::new(Var(Variable::mock("RAX", 8))),
rhs: Box::new(Var(Variable::mock("RBX", 8))),
lhs: Box::new(expr!("RAX:8")),
rhs: Box::new(expr!("RBX:8")),
};
assert_eq!(expr, expected_expr);
}
#[test]
fn display() {
let expr = Expression::const_from_i32(2);
let expr = expr!("2:4");
let mul = Expression::BinOp {
op: BinOpType::IntMult,
lhs: Box::new(Expression::Var(Variable::mock("RAX", 8))),
rhs: Box::new(Expression::Var(Variable::mock("RBP", 8))),
lhs: Box::new(expr!("RAX:8")),
rhs: Box::new(expr!("RBP:8")),
};
let expr = expr.plus(mul);
let expr = Expression::UnOp {
......
......@@ -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,10 +189,10 @@ pub mod parsing {
#[allow(dead_code)]
pub fn parse_expr<S: AsRef<str>>(str: S) -> Expression {
let set = RegexSet::new([
r"^[[:alnum:]&&[^0-9]]{1}[[:alnum:]&&[^x]]?[[:alnum:]]*:[0-9]{1,2}$", // Variable
r"^((0x(-)?[[:alnum:]]+)|^(-)?([0-9])+)+:[0-9]+$", // Constant
r"^[^\+]*\+{1}[^\+]*$", // BinOp (IntAdd)
r"^[[:ascii:]]+ \-{1} [[:ascii:]]+$", // BinOp (IntSub)
r"^[[:alnum:]&&[^0-9]]{1}[[:alnum:]&&[^x]]?[[:alnum:]_]*:[0-9]{1,2}$", // Variable
r"^((0x(-)?[[:alnum:]]+)|^(-)?([0-9])+)+:[0-9]+$", // Constant
r"^[^\+]*\+{1}[^\+]*$", // BinOp (IntAdd)
r"^[[:ascii:]]+ \-{1} [[:ascii:]]+$", // BinOp (IntSub)
r"^-\([[:ascii:]]*\)$", // UnOp (IntNegate)
r"^¬\([[:ascii:]]*\)$", // UnOp (BoolNegate)
])
......
......@@ -194,50 +194,6 @@ mod tests {
use super::*;
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]
fn check_bit_to_byte_conversion() {
let bits: BitWidth = BitWidth::new(8).unwrap();
......
......@@ -49,75 +49,3 @@ impl Program {
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> {
mod tests {
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]
fn retarget_nonexisting_jumps() {
let mut jmp_term = Term {
......
......@@ -245,12 +245,13 @@ fn negate_condition(expr: Expression) -> Expression {
#[cfg(test)]
pub mod tests {
use super::*;
use crate::{def, expr};
use std::collections::BTreeMap;
fn mock_condition_block(name: &str, if_target: &str, else_target: &str) -> Term<Blk> {
let if_jmp = Jmp::CBranch {
target: Tid::new(if_target),
condition: Expression::Var(Variable::mock("zero_flag", ByteSize::new(1))),
condition: expr!("zero_flag:1"),
};
let if_jmp = Term {
tid: Tid::new(name.to_string() + "_jmp_if"),
......@@ -273,14 +274,8 @@ pub mod tests {
}
fn mock_block_with_defs(name: &str, return_target: &str) -> Term<Blk> {
let def = Def::Assign {
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 def = def![format!("{name}_def: r0:4 = r1:4")];
let jmp = Jmp::Branch(Tid::new(return_target));
let jmp = Term {
tid: Tid::new(name.to_string() + "_jmp"),
......
......@@ -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.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct RuntimeMemoryImage {
memory_segments: Vec<MemorySegment>,
is_little_endian: bool,
/// Sequence of memory segments.
pub memory_segments: Vec<MemorySegment>,
/// Endianness
pub is_little_endian: bool,
}
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)]
mod tests {
use crate::intermediate_representation::{Bitvector, ByteSize, RuntimeMemoryImage};
use crate::{bitvec, intermediate_representation::*};
#[test]
fn read_endianness() {
let mut mem_image = RuntimeMemoryImage::mock();
let address = Bitvector::from_u32(0x1001);
let address = bitvec!("0x1001:4");
assert_eq!(
mem_image.read(&address, ByteSize::new(4)).unwrap(),
Bitvector::from_u32(0xb4b3b2b1).into()
bitvec!("0xb4b3b2b1:4").into()
);
mem_image.is_little_endian = false;
assert_eq!(
mem_image.read(&address, ByteSize::new(4)).unwrap(),
Bitvector::from_u32(0xb1b2b3b4).into()
bitvec!("0xb1b2b3b4:4").into()
);
}
#[test]
fn ro_data_pointer() {
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();
assert_eq!(index, 2);
assert_eq!(&slice[index..], &[0xb2u8, 0xb3, 0xb4]);
......@@ -389,7 +312,7 @@ mod tests {
// the byte array contains "Hello World".
let expected_string: &str =
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!(
expected_string,
mem_image
......
......@@ -195,232 +195,3 @@ impl CallingConvention {
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::*;
mod builder;
mod builder_high_lvl;
mod builder_low_lvl;
/// A term identifier consisting of an ID string (which is required to be unique)
/// 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(),
}
}
}
......@@ -29,18 +29,3 @@ impl Display for Variable {
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 crate::{def, expr, intermediate_representation::*, variable};
struct Setup<'a> {
register_map: HashMap<&'a String, &'a RegisterProperties>,
......@@ -61,24 +60,24 @@ impl<'a> Setup<'a> {
},
int_sub_expr: Expression::BinOp {
op: BinOpType::IntSub,
lhs: Box::new(Expression::Var(Variable::mock("EAX", 4))),
rhs: Box::new(Expression::Var(Variable::mock("ECX", 4))),
lhs: Box::new(expr!("EAX:4")),
rhs: Box::new(expr!("ECX:4")),
},
int_sub_subpiece_expr: Expression::BinOp {
op: BinOpType::IntSub,
lhs: Box::new(Expression::Subpiece {
low_byte: ByteSize::new(0),
size: ByteSize::new(4),
arg: Box::new(Expression::Var(Variable::mock("RAX", 8))),
arg: Box::new(expr!("RAX:8")),
}),
rhs: Box::new(Expression::Subpiece {
low_byte: ByteSize::new(0),
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)),
rax_variable: Expression::Var(Variable::mock("RAX", 8)),
eax_variable: expr!("EAX:4"),
rax_variable: expr!("RAX:8"),
}
}
}
......@@ -244,58 +243,34 @@ fn piecing_or_zero_extending() {
register_map.insert(&setup.rcx_name, &setup.rcx_register);
register_map.insert(&setup.ah_name, &setup.ah_register);
let eax_assign = Term {
tid: Tid::new("eax_assign"),
term: Def::Assign {
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 eax_assign = def!["eax_assign: EAX:4 = 0:4"];
let load_to_eax = def!["load_to_eax: EAX:4 := Load from 0:8"];
let ah_assign = def!["ah_assign: AH:1 = 0:1"];
let zext_eax_to_rax = Term {
tid: Tid::new("zext_eax_to_rax"),
term: Def::Assign {
var: Variable::mock("RAX", 8),
var: variable!("RAX:8"),
value: Expression::cast(setup.eax_variable.clone(), CastOpType::IntZExt),
},
};
let zext_ah_to_eax = Term {
tid: Tid::new("zext_ah_to_eax"),
term: Def::Assign {
var: Variable::mock("EAX", 4),
value: Expression::cast(
Expression::Var(Variable::mock("AH", 1)),
CastOpType::IntZExt,
),
var: variable!("EAX:4"),
value: Expression::cast(expr!("AH:1"), CastOpType::IntZExt),
},
};
let zext_ah_to_rax = Term {
tid: Tid::new("zext_ah_to_rax"),
term: Def::Assign {
var: Variable::mock("RAX", 8),
value: Expression::cast(
Expression::Var(Variable::mock("AH", 1)),
CastOpType::IntZExt,
),
var: variable!("RAX:8"),
value: Expression::cast(expr!("AH:1"), CastOpType::IntZExt),
},
};
let zext_eax_to_rcx = Term {
tid: Tid::new("zext_eax_to_rcx"),
term: Def::Assign {
var: Variable::mock("RCX", 8),
var: variable!("RCX:8"),
value: Expression::cast(setup.eax_variable.clone(), CastOpType::IntZExt),
},
};
......
use std::collections::BTreeSet;
use crate::{
abstract_domain::IntervalDomain,
intermediate_representation::{Bitvector, Tid},
};
use crate::{abstract_domain::IntervalDomain, expr, intermediate_representation::*, variable};
use super::*;
fn mock_pi_state() -> PointerInferenceState {
PointerInferenceState::new(
&Variable::mock("RSP", 8 as u64),
Tid::new("func"),
BTreeSet::new(),
)
PointerInferenceState::new(&variable!("RSP:8"), Tid::new("func"), BTreeSet::new())
}
#[test]
......@@ -24,20 +17,14 @@ fn test_get_variable_parameters() {
format_string_index_map.insert("sprintf".to_string(), 1);
let global_address = Bitvector::from_str_radix(16, "5000").unwrap();
pi_state.set_register(
&Variable::mock("RSI", 8 as u64),
&variable!("RSI:8"),
IntervalDomain::new(global_address.clone(), global_address).into(),
);
let project = Project::mock_x64();
let mut output: Vec<Arg> = Vec::new();
output.push(Arg::from_var(
Variable::mock("RDX", 8),
Some(Datatype::Char),
));
output.push(Arg::from_var(
Variable::mock("RCX", 8),
Some(Datatype::Integer),
));
output.push(Arg::from_var(variable!("RDX:8"), Some(Datatype::Char)));
output.push(Arg::from_var(variable!("RCX:8"), Some(Datatype::Integer)));
assert_eq!(
output,
......@@ -50,14 +37,11 @@ fn test_get_variable_parameters() {
.unwrap()
);
output = vec![Arg::from_var(
Variable::mock("RDX", 8),
Some(Datatype::Pointer),
)];
output = vec![Arg::from_var(variable!("RDX:8"), Some(Datatype::Pointer))];
let global_address = Bitvector::from_str_radix(16, "500c").unwrap();
pi_state.set_register(
&Variable::mock("RSI", 8 as u64),
&variable!("RSI:8"),
IntervalDomain::new(global_address.clone(), global_address).into(),
);
......@@ -81,7 +65,7 @@ fn test_get_input_format_string() {
let global_address = Bitvector::from_str_radix(16, "3002").unwrap();
pi_state.set_register(
&Variable::mock("RSI", 8 as u64),
&variable!("RSI:8"),
IntervalDomain::new(global_address.clone(), global_address).into(),
);
......@@ -199,19 +183,15 @@ fn test_calculate_parameter_locations() {
let mut expected_args = vec![
Arg::Register {
expr: Expression::Var(Variable::mock("RDX", ByteSize::new(8))),
expr: expr!("RDX:8"),
data_type: Some(Datatype::Integer),
},
Arg::Register {
expr: Expression::subpiece(
Expression::Var(Variable::mock("ZMM0", 64)),
ByteSize::new(0),
ByteSize::new(8),
),
expr: Expression::subpiece(expr!("ZMM0:64"), ByteSize::new(0), ByteSize::new(8)),
data_type: Some(Datatype::Double),
},
Arg::Register {
expr: Expression::Var(Variable::mock("RCX", ByteSize::new(8))),
expr: expr!("RCX:8"),
data_type: Some(Datatype::Pointer),
},
];
......@@ -227,15 +207,15 @@ fn test_calculate_parameter_locations() {
parameters.push(("s".to_string().into(), ByteSize::new(8)));
expected_args.push(Arg::Register {
expr: Expression::Var(Variable::mock("R8", ByteSize::new(8))),
expr: expr!("R8:8"),
data_type: Some(Datatype::Pointer),
});
expected_args.push(Arg::Register {
expr: Expression::Var(Variable::mock("R9", ByteSize::new(8))),
expr: expr!("R9:8"),
data_type: Some(Datatype::Pointer),
});
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),
data_type: Some(Datatype::Pointer),
});
......@@ -251,15 +231,10 @@ fn test_calculate_parameter_locations() {
fn test_create_stack_arg() {
assert_eq!(
Arg::Stack {
address: Expression::Var(Variable::mock("RSP", 8)).plus_const(8),
address: expr!("RSP:8 + 8:8"),
size: ByteSize::new(8),
data_type: Some(Datatype::Pointer),
},
create_stack_arg(
ByteSize::new(8),
8,
Datatype::Pointer,
&Variable::mock("RSP", 8)
),
create_stack_arg(ByteSize::new(8), 8, Datatype::Pointer, &variable!("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