use std::ops::Range;
use std::fmt;
use smallvec::SmallVec;
use quickcheck::{Arbitrary, Gen};
use ron_uuid::UUID;
use {
StrRef, Str,
Variable, Value, Constant, Segment,
Result, Constraint
};
#[derive(Clone,PartialEq,Eq)]
pub struct Area {
pub start: u64,
pub end: u64,
pub offset_start: usize,
pub offset_end: usize,
}
impl From<Range<u64>> for Area {
fn from(r: Range<u64>) -> Self {
Area{
start: r.start,
end: r.end,
offset_start: 0,
offset_end: if r.end == r.start { 1 } else { 0 },
}
}
}
impl fmt::Debug for Area {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.offset_start != 0 {
write!(f, "{}.{}..", self.start, self.offset_start)?;
} else {
write!(f, "{}..", self.start)?;
}
if self.offset_end != 0 {
write!(f, "{}.{}", self.end, self.offset_end)
} else {
write!(f, "{}", self.end)
}
}
}
#[derive(Debug,Clone,Copy,PartialEq,Eq)]
pub enum Endianess {
Little,
Big,
}
impl Arbitrary for Endianess {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
match g.gen_range(0, 1) {
0 => Endianess::Little,
1 => Endianess::Big,
_ => unreachable!(),
}
}
}
#[derive(Clone,PartialEq,Eq,Debug)]
pub enum Operation {
Add(Value, Value),
Subtract(Value, Value),
Multiply(Value, Value),
DivideUnsigned(Value, Value),
DivideSigned(Value, Value),
ShiftLeft(Value, Value),
ShiftRightUnsigned(Value, Value),
ShiftRightSigned(Value, Value),
Modulo(Value, Value),
And(Value, Value),
InclusiveOr(Value, Value),
ExclusiveOr(Value, Value),
Equal(Value, Value),
LessOrEqualUnsigned(Value, Value),
LessOrEqualSigned(Value, Value),
LessUnsigned(Value, Value),
LessSigned(Value, Value),
ZeroExtend(usize, Value),
SignExtend(usize, Value),
Move(Value),
Initialize(StrRef,usize),
Select(usize, usize, Value),
Assume(Constraint,Value),
Load(Segment,Endianess,usize,Value),
Phi(Value,Value,Value),
}
impl Operation {
pub fn reads<'x>(&'x self) -> SmallVec<[&'x Value; 3]> {
use Operation::*;
let mut ret = SmallVec::new();
match self {
&Add(ref a, ref b) => { ret.push(a); ret.push(b); }
&Subtract(ref a, ref b) => { ret.push(a); ret.push(b); }
&Multiply(ref a, ref b) => { ret.push(a); ret.push(b); }
&DivideUnsigned(ref a, ref b) => { ret.push(a); ret.push(b); }
&DivideSigned(ref a, ref b) => { ret.push(a); ret.push(b); }
&ShiftLeft(ref a, ref b) => { ret.push(a); ret.push(b); }
&ShiftRightUnsigned(ref a, ref b) => { ret.push(a); ret.push(b); }
&ShiftRightSigned(ref a, ref b) => { ret.push(a); ret.push(b); }
&Modulo(ref a, ref b) => { ret.push(a); ret.push(b); }
&And(ref a, ref b) => { ret.push(a); ret.push(b); }
&InclusiveOr(ref a, ref b) => { ret.push(a); ret.push(b); }
&ExclusiveOr(ref a, ref b) => { ret.push(a); ret.push(b); }
&Equal(ref a, ref b) => { ret.push(a); ret.push(b); }
&LessOrEqualUnsigned(ref a, ref b) => { ret.push(a); ret.push(b); }
&LessOrEqualSigned(ref a, ref b) => { ret.push(a); ret.push(b); }
&LessUnsigned(ref a, ref b) => { ret.push(a); ret.push(b); }
&LessSigned(ref a, ref b) => { ret.push(a); ret.push(b); }
&ZeroExtend(_, ref a) => { ret.push(a); }
&SignExtend(_, ref a) => { ret.push(a); }
&Move(ref a) => { ret.push(a); }
&Initialize(_,_) => {}
&Select(_, _, ref a) => { ret.push(a); }
&Assume(_, ref a) => { ret.push(a); }
&Load(_, _, _, ref a) => { ret.push(a); }
&Phi(ref a, ref b, ref c) => {
ret.push(a); ret.push(b); ret.push(c);
}
}
ret
}
pub fn reads_mut<'x>(&'x mut self) -> SmallVec<[&'x mut Value; 3]> {
use Operation::*;
let mut ret = SmallVec::new();
match self {
&mut Add(ref mut a, ref mut b) => { ret.push(a); ret.push(b); }
&mut Subtract(ref mut a, ref mut b) => { ret.push(a); ret.push(b); }
&mut Multiply(ref mut a, ref mut b) => { ret.push(a); ret.push(b); }
&mut DivideUnsigned(ref mut a, ref mut b) => { ret.push(a); ret.push(b); }
&mut DivideSigned(ref mut a, ref mut b) => { ret.push(a); ret.push(b); }
&mut ShiftLeft(ref mut a, ref mut b) => { ret.push(a); ret.push(b); }
&mut ShiftRightUnsigned(ref mut a, ref mut b) => { ret.push(a); ret.push(b); }
&mut ShiftRightSigned(ref mut a, ref mut b) => { ret.push(a); ret.push(b); }
&mut Modulo(ref mut a, ref mut b) => { ret.push(a); ret.push(b); }
&mut And(ref mut a, ref mut b) => { ret.push(a); ret.push(b); }
&mut InclusiveOr(ref mut a, ref mut b) => { ret.push(a); ret.push(b); }
&mut ExclusiveOr(ref mut a, ref mut b) => { ret.push(a); ret.push(b); }
&mut Equal(ref mut a, ref mut b) => { ret.push(a); ret.push(b); }
&mut LessOrEqualUnsigned(ref mut a, ref mut b) => { ret.push(a); ret.push(b); }
&mut LessOrEqualSigned(ref mut a, ref mut b) => { ret.push(a); ret.push(b); }
&mut LessUnsigned(ref mut a, ref mut b) => { ret.push(a); ret.push(b); }
&mut LessSigned(ref mut a, ref mut b) => { ret.push(a); ret.push(b); }
&mut ZeroExtend(_, ref mut a) => { ret.push(a); }
&mut SignExtend(_, ref mut a) => { ret.push(a); }
&mut Move(ref mut a) => { ret.push(a); }
&mut Initialize(_,_) => {}
&mut Select(_, _, ref mut a) => { ret.push(a); }
&mut Assume(_, ref mut a) => { ret.push(a); }
&mut Load(_, _, _, ref mut a) => { ret.push(a); }
&mut Phi(ref mut a, ref mut b, ref mut c) => {
ret.push(a); ret.push(b); ret.push(c);
}
}
ret
}
pub fn execute(&self) -> Result<Value> {
use std::num::Wrapping;
use Operation::*;
use std::u64;
match self.clone() {
Add(Value::Constant(a), Value::Constant(b)) => {
if a.bits != b.bits {
return Err(format!("tried to add value of {} and {} bits",a.bits,b.bits).into());
}
let mask = a.mask();
let bits = a.bits;
let a: Wrapping<_> = a.into();
let b: Wrapping<_> = b.into();
Value::val(((a + b) & mask).0, bits)
}
Add(Value::Constant(Constant { value: 0, .. }), b) => Ok(b),
Add(a, Value::Constant(Constant { value: 0, .. })) => Ok(a),
Add(_, _) => Ok(Value::Undefined),
Subtract(Value::Constant(a), Value::Constant(b)) => {
if a.bits != b.bits {
return Err(format!("tried to subtract value of {} and {} bits",a.bits,b.bits).into());
}
let mask = a.mask();
let bits = a.bits;
let a: Wrapping<_> = a.into();
let b: Wrapping<_> = b.into();
Value::val(((a - b) & mask).0, bits)
}
Subtract(a, Value::Constant(Constant { value: 0, .. })) => Ok(a),
Subtract(_, _) => Ok(Value::Undefined),
Multiply(Value::Constant(a), Value::Constant(b)) => {
if a.bits != b.bits {
return Err(format!("tried to multiply value of {} and {} bits",a.bits,b.bits).into());
}
let mask = a.mask();
let bits = a.bits;
let a: Wrapping<_> = a.into();
let b: Wrapping<_> = b.into();
Value::val(((a * b) & mask).0, bits)
}
Multiply(Value::Constant(Constant { value: 0, bits: s }), _) => Ok(Value::Constant(Constant { value: 0, bits: s })),
Multiply(_, Value::Constant(Constant { value: 0, bits: s })) => Ok(Value::Constant(Constant { value: 0, bits: s })),
Multiply(Value::Constant(Constant { value: 1, .. }), b) => Ok(b),
Multiply(a, Value::Constant(Constant { value: 1, .. })) => Ok(a),
Multiply(_, _) => Ok(Value::Undefined),
DivideUnsigned(Value::Constant(a), Value::Constant(b)) => {
if a.bits != b.bits {
return Err(format!("tried to divide value of {} and {} bits",a.bits,b.bits).into());
}
let mask = a.mask();
let bits = a.bits;
let a: Wrapping<_> = a.into();
let b: Wrapping<_> = b.into();
if b == Wrapping(0) {
Ok(Value::Undefined)
} else {
Value::val(((a / b) & mask).0, bits)
}
}
DivideUnsigned(a, Value::Constant(Constant { value: 1, .. })) => Ok(a),
DivideUnsigned(Value::Constant(Constant { value: 0, bits: s }), _) => Ok(Value::Constant(Constant { value: 0, bits: s })),
DivideUnsigned(_, _) => Ok(Value::Undefined),
DivideSigned(Value::Constant(a), Value::Constant(b)) => {
if a.bits != b.bits {
return Err(format!("tried to divide value of {} and {} bits",a.bits,b.bits).into());
}
let bits = a.bits;
let mut a = Wrapping(a.value as i64);
let mut b = Wrapping(b.value as i64);
if bits < 64 {
let sign_bit = Wrapping(1 << (bits - 1));
let m = Wrapping(1 << bits);
if sign_bit & a != Wrapping(0) {
a = a - m;
}
if sign_bit & b != Wrapping(0) {
b = b - m;
}
a = a % m;
b = b % m;
}
if b == Wrapping(0) {
Ok(Value::Undefined)
} else {
if bits < 64 {
let m = 1 << bits;
Value::val((a / b).0 as u64 % m, bits)
} else {
Value::val((a / b).0 as u64, bits)
}
}
}
DivideSigned(a, Value::Constant(Constant { value: 1, .. })) => Ok(a),
DivideSigned(Value::Constant(Constant { value: 0, bits: s }), _) => Ok(Value::Constant(Constant { value: 0, bits: s })),
DivideSigned(_, _) => Ok(Value::Undefined),
Modulo(_, Value::Constant(Constant { value: 0, .. })) => Ok(Value::Undefined),
Modulo(Value::Constant(a), Value::Constant(b)) => {
if a.bits != b.bits {
return Err(format!("tried to mod value of {} and {} bits",a.bits,b.bits).into());
}
let mask = a.mask();
let bits = a.bits;
let a: Wrapping<_> = a.into();
let b: Wrapping<_> = b.into();
if b == Wrapping(0) {
Ok(Value::Undefined)
} else {
Value::val(((a % b) & mask).0, bits)
}
}
Modulo(Value::Constant(Constant { value: 0, bits: s }), _) => Ok(Value::Constant(Constant { value: 0, bits: s })),
Modulo(_, Value::Constant(Constant { value: 1, bits: s })) => Ok(Value::Constant(Constant { value: 0, bits: s })),
Modulo(_, _) => Ok(Value::Undefined),
ShiftLeft(Value::Constant(a), Value::Constant(b)) => {
let mask = a.mask();
let bits = a.bits;
let a: Wrapping<_> = a.into();
let b: Wrapping<_> = b.into();
Value::val(((a << (b.0 as usize)) & mask).0, bits)
}
ShiftLeft(Value::Constant(Constant { value: 0, bits: s }), _) => Ok(Value::Constant(Constant { value: 0, bits: s })),
ShiftLeft(a, Value::Constant(Constant { value: 0, .. })) => Ok(a),
ShiftLeft(_, _) => Ok(Value::Undefined),
ShiftRightUnsigned(Value::Constant(a), Value::Constant(b)) => {
use std::cmp;
if a.bits != b.bits {
return Err(format!("tried to mod value of {} and {} bits",a.bits,b.bits).into());
}
let mask = a.mask();
let bits = a.bits;
let a: Wrapping<_> = a.into();
let b: Wrapping<_> = b.into();
if b.0 >= bits as u64 {
Value::val(0, bits)
} else {
Value::val(((a >> cmp::min(cmp::min(64, bits), b.0 as usize)) & mask).0,bits)
}
}
ShiftRightUnsigned(Value::Constant(Constant { value: 0, bits: s }), _) => Ok(Value::Constant(Constant { value: 0, bits: s })),
ShiftRightUnsigned(a, Value::Constant(Constant { value: 0, .. })) => Ok(a),
ShiftRightUnsigned(_, _) => Ok(Value::Undefined),
ShiftRightSigned(Value::Constant(a), Value::Constant(b)) => {
if a.bits != b.bits {
return Err(format!("tried to mod value of {} and {} bits",a.bits,b.bits).into());
}
let mask = a.mask();
let bits = a.bits;
let mut a = Wrapping(a.value as i64);
let b = Wrapping(b.value as i64);
if bits < 64 {
let sign_bit = Wrapping(1 << (bits - 1));
if sign_bit & a != Wrapping(0) {
a = a | Wrapping(-1i64 << bits);
}
}
if b.0 as u64 >= bits as u64 {
let ret = if a < Wrapping(0) {
if bits < 64 {
Value::Constant(Constant { value: (1 << bits) - 1, bits: bits })
} else {
Value::Constant(Constant { value: u64::MAX, bits: bits })
}
} else {
Value::Constant(Constant { value: 0, bits: bits })
};
Ok(ret)
} else {
if bits < 64 {
Value::val((a >> (b.0 as usize)).0 as u64 & mask.0, bits)
} else {
Value::val((a >> (b.0 as usize)).0 as u64, bits)
}
}
}
ShiftRightSigned(Value::Constant(Constant { value: 0, bits: s }), _) => Ok(Value::Constant(Constant { value: 0, bits: s })),
ShiftRightSigned(a, Value::Constant(Constant { value: 0, .. })) => Ok(a),
ShiftRightSigned(_, _) => Ok(Value::Undefined),
And(Value::Constant(a), Value::Constant(b)) => {
if a.bits != b.bits {
return Err(format!("tried to and value of {} and {} bits",a.bits,b.bits).into());
}
let bits = a.bits;
let a: Wrapping<_> = a.into();
let b: Wrapping<_> = b.into();
Value::val((a & b).0 as u64, bits)
}
And(_, Value::Constant(Constant { value: 0, bits: s })) => Ok(Value::Constant(Constant { value: 0, bits: s })),
And(Value::Constant(Constant { value: 0, bits: s }), _) => Ok(Value::Constant(Constant { value: 0, bits: s })),
And(_, _) => Ok(Value::Undefined),
InclusiveOr(Value::Constant(a), Value::Constant(b)) => {
if a.bits != b.bits {
return Err(format!("tried to or value of {} and {} bits",a.bits,b.bits).into());
}
let bits = a.bits;
let a: Wrapping<_> = a.into();
let b: Wrapping<_> = b.into();
Value::val((a | b).0 as u64, bits)
}
InclusiveOr(a, Value::Constant(Constant { value: 0, .. })) => Ok(a),
InclusiveOr(Value::Constant(Constant { value: 0, .. }), b) => Ok(b),
InclusiveOr(_, _) => Ok(Value::Undefined),
ExclusiveOr(Value::Constant(a), Value::Constant(b)) => {
if a.bits != b.bits {
return Err(format!("tried to xor value of {} and {} bits",a.bits,b.bits).into());
}
let bits = a.bits;
let a: Wrapping<_> = a.into();
let b: Wrapping<_> = b.into();
Value::val((a ^ b).0 as u64, bits)
}
ExclusiveOr(_, _) => Ok(Value::Undefined),
Equal(Value::Constant(a), Value::Constant(b)) => {
if a.bits != b.bits {
return Err(format!("tried to compare value of {} and {} bits",a.bits,b.bits).into());
}
let a: Wrapping<_> = a.into();
let b: Wrapping<_> = b.into();
if a == b {
Value::val(1, 1)
} else {
Value::val(0, 1)
}
}
Equal(_, _) => Ok(Value::Undefined),
LessOrEqualUnsigned(Value::Constant(a), Value::Constant(b)) => {
if a.bits != b.bits {
return Err(format!("tried to compare value of {} and {} bits",a.bits,b.bits).into());
}
let a: Wrapping<_> = a.into();
let b: Wrapping<_> = b.into();
if a <= b {
Value::val(1, 1)
} else {
Value::val(0, 1)
}
}
LessOrEqualUnsigned(Value::Constant(Constant { value: 0, .. }), _) => Ok(Value::Constant(Constant { value: 1, bits: 1 })),
LessOrEqualUnsigned(_, _) => Ok(Value::Undefined),
LessOrEqualSigned(Value::Constant(a), Value::Constant(b)) => {
if a.bits != b.bits {
return Err(format!("tried to compare value of {} and {} bits",a.bits,b.bits).into());
}
let bits = a.bits;
let a: Wrapping<_> = a.into();
let b: Wrapping<_> = b.into();
let mask = Wrapping(if bits < 64 {
(1u64 << (bits - 1)) - 1
} else {
u64::MAX
});
let sign_mask = Wrapping(if bits < 64 { 1u64 << (bits - 1) } else { 0 });
if (a & sign_mask) ^ (b & sign_mask) != Wrapping(0) {
Value::val(if a & sign_mask != Wrapping(0) { 1 } else { 0 },1)
} else {
Value::val(if (a & mask) <= (b & mask) { 1 } else { 0 },1)
}
}
LessOrEqualSigned(_, _) => Ok(Value::Undefined),
LessUnsigned(Value::Constant(a), Value::Constant(b)) => {
if a.bits != b.bits {
return Err(format!("tried to compare value of {} and {} bits",a.bits,b.bits).into());
}
let a: Wrapping<_> = a.into();
let b: Wrapping<_> = b.into();
if a < b {
Value::val(1, 1)
} else {
Value::val(0, 1)
}
}
LessUnsigned(_, _) => Ok(Value::Undefined),
LessSigned(Value::Constant(a), Value::Constant(b)) => {
if a.bits != b.bits {
return Err(format!("tried to compare value of {} and {} bits",a.bits,b.bits).into());
}
let bits = a.bits;
let mut a: Wrapping<_> = a.into();
let mut b: Wrapping<_> = b.into();
if bits < 64 {
let sign_bit = Wrapping(1 << (bits - 1));
let m = Wrapping(1 << bits);
if sign_bit & a != Wrapping(0) {
a = a - m;
}
if sign_bit & b != Wrapping(0) {
b = b - m;
}
a = a % m;
b = b % m;
}
if a < b {
Value::val(1,1)
} else {
Value::val(0,1)
}
}
LessSigned(_, _) => Ok(Value::Undefined),
ZeroExtend(s1, Value::Constant(Constant { value: v, bits: s0 })) => {
let mask1 = if s1 < 64 { (1u64 << s1) - 1 } else { u64::MAX };
let mask0 = if s0 < 64 { (1u64 << s0) - 1 } else { u64::MAX };
Value::val((v & mask0) & mask1,s1)
}
ZeroExtend(s, Value::Variable(Variable { name, .. })) => {
Value::var(name,s)
}
ZeroExtend(_, Value::Undefined) => Ok(Value::Undefined),
SignExtend(t, Value::Constant(Constant { value: v, bits: s })) => {
let mask0 = if s < 64 { (1u64 << s) - 1 } else { u64::MAX };
let mask1 = if t < 64 { (1u64 << t) - 1 } else { u64::MAX };
let sign = if s < 64 { 1u64 << (s - 1) } else { 0 };
if v & sign == 0 {
Value::val((v & mask0) & mask1, t)
} else {
let mask = mask1 & !mask0;
Value::val((v & mask0) | mask, t)
}
}
SignExtend(s, Value::Variable(Variable { name, .. })) => {
Value::var(name,s)
}
SignExtend(_, Value::Undefined) => Ok(Value::Undefined),
Move(a) => Ok(a),
Initialize(_, _) => Ok(Value::Undefined),
Select(off, sz, Value::Constant(a)) => {
let Constant{ value: a_value, bits: a_bits } = a;
if off + sz < 64 && a_bits <= off + sz {
let val = a_value >> off;
let mask = (1 << sz) - 1;
Value::val(val & mask, sz)
} else {
Ok(Value::Undefined)
}
}
Select(_, _, _) => Ok(Value::Undefined),
Assume(_, _) => Ok(Value::Undefined),
Load(_, _, _, _) => Ok(Value::Undefined),
Phi(ref a@Value::Variable(_),Value::Undefined,Value::Undefined) => Ok(a.clone()),
Phi(ref a@Value::Variable(_),ref b@Value::Variable(_),Value::Undefined) if *a == *b => Ok(a.clone()),
Phi(ref a@Value::Variable(_),ref b@Value::Variable(_),ref c@Value::Variable(_)) if *a == *b && *b == *c => Ok(a.clone()),
Phi(_,_,_) => Ok(Value::Undefined),
}
}
}
impl Arbitrary for Operation {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
loop {
let op = match g.gen_range(0, 26) {
0 => Operation::Add(Value::arbitrary(g), Value::arbitrary(g)),
1 => Operation::Subtract(Value::arbitrary(g), Value::arbitrary(g)),
2 => Operation::Multiply(Value::arbitrary(g), Value::arbitrary(g)),
3 => Operation::DivideUnsigned(Value::arbitrary(g), Value::arbitrary(g)),
4 => Operation::DivideSigned(Value::arbitrary(g), Value::arbitrary(g)),
5 => Operation::ShiftLeft(Value::arbitrary(g), Value::arbitrary(g)),
6 => Operation::ShiftRightUnsigned(Value::arbitrary(g), Value::arbitrary(g)),
7 => Operation::ShiftRightSigned(Value::arbitrary(g), Value::arbitrary(g)),
8 => Operation::Modulo(Value::arbitrary(g), Value::arbitrary(g)),
9 => Operation::And(Value::arbitrary(g), Value::arbitrary(g)),
10 => Operation::InclusiveOr(Value::arbitrary(g), Value::arbitrary(g)),
11 => Operation::ExclusiveOr(Value::arbitrary(g), Value::arbitrary(g)),
12 => Operation::Equal(Value::arbitrary(g), Value::arbitrary(g)),
13 => Operation::LessOrEqualUnsigned(Value::arbitrary(g), Value::arbitrary(g)),
14 => Operation::LessOrEqualSigned(Value::arbitrary(g), Value::arbitrary(g)),
15 => Operation::LessUnsigned(Value::arbitrary(g), Value::arbitrary(g)),
16 => Operation::LessSigned(Value::arbitrary(g), Value::arbitrary(g)),
17 => Operation::ZeroExtend(g.gen(), Value::arbitrary(g)),
18 => Operation::SignExtend(g.gen(), Value::arbitrary(g)),
19 => Operation::Move(Value::arbitrary(g)),
20 => Operation::Initialize(StrRef::arbitrary(g),g.gen()),
21 => {
let v = Value::arbitrary(g);
let off = g.gen_range(0, v.bits().unwrap_or(0) + 1);
let sz = g.gen_range(0, v.bits().unwrap_or(0) + 1 - off);
Operation::Select(off, sz, v)
}
22 => Operation::Assume(Constraint::arbitrary(g), Value::arbitrary(g)),
23 => Operation::Load(Segment::arbitrary(g), Endianess::arbitrary(g), g.gen(), Value::arbitrary(g)),
24 => {
let a = Value::arbitrary(g);
let b = Value::arbitrary(g);
Operation::Phi(a,b,Value::undef())
}
25 => {
let a = Value::arbitrary(g);
let b = Value::arbitrary(g);
let c = Value::arbitrary(g);
Operation::Phi(a,b,c)
}
_ => unreachable!(),
};
match op {
Operation::Add(Value::Undefined, Value::Undefined) => {}
Operation::Subtract(Value::Undefined, Value::Undefined) => {}
Operation::Multiply(Value::Undefined, Value::Undefined) => {}
Operation::DivideUnsigned(Value::Undefined, Value::Undefined) => {}
Operation::DivideSigned(Value::Undefined, Value::Undefined) => {}
Operation::Modulo(Value::Undefined, Value::Undefined) => {}
Operation::ShiftLeft(Value::Undefined, Value::Undefined) => {}
Operation::ShiftRightUnsigned(Value::Undefined, Value::Undefined) => {}
Operation::ShiftRightSigned(Value::Undefined, Value::Undefined) => {}
Operation::And(Value::Undefined, Value::Undefined) => {}
Operation::InclusiveOr(Value::Undefined, Value::Undefined) => {}
Operation::ExclusiveOr(Value::Undefined, Value::Undefined) => {}
Operation::Equal(Value::Undefined, Value::Undefined) => {}
Operation::LessOrEqualUnsigned(Value::Undefined, Value::Undefined) => {}
Operation::LessOrEqualSigned(Value::Undefined, Value::Undefined) => {}
Operation::LessUnsigned(Value::Undefined, Value::Undefined) => {}
Operation::LessSigned(Value::Undefined, Value::Undefined) => {}
Operation::ZeroExtend(_, Value::Undefined) => {}
Operation::SignExtend(_, Value::Undefined) => {}
Operation::Select(_, _, Value::Undefined) => {}
Operation::Phi(Value::Undefined, _, _) => {}
Operation::Phi(_, Value::Undefined, _) => {}
Operation::Phi(_, Value::Constant(_), _) => {}
Operation::Phi(_, _, Value::Constant(_)) => {}
_ => { return op; }
}
}
}
}
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
pub enum CallTarget {
Function(UUID),
External(Str),
}
#[derive(Clone,PartialEq,Eq,Debug)]
pub enum MemoryOperation {
Store{
segment: Segment,
endianess: Endianess,
bytes: usize,
address: Value,
value: Value,
},
MemoryPhi(Option<Segment>,Option<Segment>,Option<Segment>),
Allocate{
base: StrRef,
}
}
#[derive(Clone,PartialEq,Eq,Debug)]
pub enum FlowOperation {
Call{
function: UUID,
},
ExternalCall{
external: StrRef,
},
IndirectCall{
target: Variable,
},
Return,
}
#[derive(Clone,PartialEq,Eq,Debug)]
pub enum Statement {
Expression{
result: Variable,
op: Operation,
},
Flow{
op: FlowOperation,
},
Memory{
op: MemoryOperation,
result: Segment,
},
}
impl Statement {
pub fn sanity_check(&self) -> Result<()> {
use std::cmp;
use errors::ResultExt;
let typecheck_binop = |a: &Value, b: &Value, result: &Variable| -> Result<()> {
if !(a.bits() == None || b.bits() == None || a.bits() == b.bits()) {
return Err(format!("Argument sizes mismatch: {:?} vs. {:?}", a, b).into());
}
if cmp::max(a.bits().unwrap_or(0), b.bits().unwrap_or(0)) != result.bits {
return Err(format!("Operation result and result sizes mismatch ({:?})",self).into());
}
Ok(())
};
let typecheck_cmpop = |a: &Value, b: &Value, result: &Variable| -> Result<()> {
if !(a.bits() == None || b.bits() == None || a.bits() == b.bits()) {
return Err("Argument sizes mismatch".into());
}
if result.bits != 1 {
return Err("Compare operation result not a flag".into());
}
Ok(())
};
let typecheck_unop = |a: &Value, sz: Option<usize>, result: &Variable| -> Result<()> {
if sz.is_none() {
if !(a.bits() == None || Some(result.bits) <= a.bits()) {
return Err("Operation result and result sizes mismatch".into());
}
} else {
if !(a.bits() == None || Some(result.bits) == sz) {
return Err("Operation result and result sizes mismatch".into());
}
}
Ok(())
};
match self {
&Statement::Expression { op: Operation::Add(ref a, ref b), ref result } => typecheck_binop(a, b, result),
&Statement::Expression { op: Operation::Subtract(ref a, ref b), ref result } => typecheck_binop(a, b, result),
&Statement::Expression { op: Operation::Multiply(ref a, ref b), ref result } => typecheck_binop(a, b, result),
&Statement::Expression { op: Operation::DivideUnsigned(ref a, ref b), ref result } => typecheck_binop(a, b, result),
&Statement::Expression { op: Operation::DivideSigned(ref a, ref b), ref result } => typecheck_binop(a, b, result),
&Statement::Expression { op: Operation::ShiftLeft(ref a, ref b), ref result } => typecheck_binop(a, b, result),
&Statement::Expression {
op: Operation::ShiftRightUnsigned(ref a, ref b),
ref result,
} => typecheck_binop(a, b, result),
&Statement::Expression { op: Operation::ShiftRightSigned(ref a, ref b), ref result } => typecheck_binop(a, b, result),
&Statement::Expression { op: Operation::Modulo(ref a, ref b), ref result } => typecheck_binop(a, b, result),
&Statement::Expression { op: Operation::And(ref a, ref b), ref result } => typecheck_binop(a, b, result),
&Statement::Expression { op: Operation::ExclusiveOr(ref a, ref b), ref result } => typecheck_binop(a, b, result),
&Statement::Expression { op: Operation::InclusiveOr(ref a, ref b), ref result } => typecheck_binop(a, b, result),
&Statement::Expression { op: Operation::Equal(ref a, ref b), ref result } => typecheck_cmpop(a, b, result),
&Statement::Expression {
op: Operation::LessOrEqualUnsigned(ref a, ref b),
ref result,
} => typecheck_cmpop(a, b, result),
&Statement::Expression { op: Operation::LessOrEqualSigned(ref a, ref b), ref result } => typecheck_cmpop(a, b, result),
&Statement::Expression { op: Operation::LessUnsigned(ref a, ref b), ref result } => typecheck_cmpop(a, b, result),
&Statement::Expression { op: Operation::LessSigned(ref a, ref b), ref result } => typecheck_cmpop(a, b, result),
&Statement::Expression { op: Operation::SignExtend(ref a, ref b), ref result } => typecheck_unop(b, Some(*a), result),
&Statement::Expression { op: Operation::ZeroExtend(ref a, ref b), ref result } => typecheck_unop(b, Some(*a), result),
&Statement::Expression { op: Operation::Move(ref a), ref result } => typecheck_unop(a, None, result),
&Statement::Expression { op: Operation::Select(off, sz, ref a), ref result } => {
if !(result.bits == sz && off + sz <= a.bits().unwrap_or(off + sz)) {
Err("Ill-sized Select operation".into())
} else {
Ok(())
}
}
&Statement::Expression{ op: Operation::Initialize(_,ref sz), ref result } => {
if result.bits != *sz {
Err("Operation result and result sizes mismatch".into())
} else {
Ok(())
}
}
_ => { Ok(()) }
}.chain_err(|| format!("Failed sanity check for {:?}", self))?;
Ok(())
}
}
impl Arbitrary for Statement {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
loop {
let stmt = match g.gen_range(0, 7) {
0 => Statement::Expression{
result: Variable::arbitrary(g),
op: Operation::arbitrary(g),
},
1 => Statement::Flow{ op: FlowOperation::Call{ function: UUID::arbitrary(g) }, },
2 => Statement::Flow{ op: FlowOperation::ExternalCall{ external: StrRef::arbitrary(g) }, },
3 => Statement::Flow{ op: FlowOperation::IndirectCall{ target: Variable::arbitrary(g) }, },
4 => Statement::Flow{ op: FlowOperation::Return },
5 => {
let mut addr = Value::arbitrary(g);
let mut val = Value::arbitrary(g);
while addr == Value::Undefined && val == Value::Undefined {
addr = Value::arbitrary(g);
val = Value::arbitrary(g);
}
Statement::Memory{
op: MemoryOperation::Store{
segment: Segment::arbitrary(g),
endianess: Endianess::arbitrary(g),
bytes: g.gen_range(1,11),
address: addr,
value: val,
},
result: Segment::arbitrary(g),
}
}
6 => {
let op = match g.gen_range(0,4) {
0 => MemoryOperation::MemoryPhi(
None,
None,
None),
1 => MemoryOperation::MemoryPhi(
Some(Segment::arbitrary(g)),
None,
None),
2 => MemoryOperation::MemoryPhi(
Some(Segment::arbitrary(g)),
Some(Segment::arbitrary(g)),
None),
3 => MemoryOperation::MemoryPhi(
Some(Segment::arbitrary(g)),
Some(Segment::arbitrary(g)),
Some(Segment::arbitrary(g))),
_ => unreachable!(),
};
Statement::Memory{
op: op,
result: Segment::arbitrary(g),
}
}
_ => unreachable!(),
};
if stmt.sanity_check().is_ok() {
return stmt;
}
}
}
}
#[macro_export]
macro_rules! rreil2 {
( ( $names:tt , $segs:tt ) { } ) => {Ok(vec![])};
( ( $names:tt , $segs:tt ) { add $($cdr:tt)* } ) => { rreil2_binop!($names # $segs # Add # $($cdr)*) };
( ( $names:tt , $segs:tt ) { sub $($cdr:tt)* } ) => { rreil2_binop!($names # $segs # Subtract # $($cdr)*) };
( ( $names:tt , $segs:tt ) { mul $($cdr:tt)* } ) => { rreil2_binop!($names # $segs # Multiply # $($cdr)*) };
( ( $names:tt , $segs:tt ) { div $($cdr:tt)* } ) => { rreil2_binop!($names # $segs # DivideUnsigned # $($cdr)*) };
( ( $names:tt , $segs:tt ) { divs $($cdr:tt)* } ) => { rreil2_binop!($names # $segs # DivideSigned # $($cdr)*) };
( ( $names:tt , $segs:tt ) { shl $($cdr:tt)* } ) => { rreil2_binop!($names # $segs # ShiftLeft # $($cdr)*) };
( ( $names:tt , $segs:tt ) { shr $($cdr:tt)* } ) => { rreil2_binop!($names # $segs # ShiftRightUnsigned # $($cdr)*) };
( ( $names:tt , $segs:tt ) { shrs $($cdr:tt)* } ) => { rreil2_binop!($names # $segs # ShiftRightSigned # $($cdr)*) };
( ( $names:tt , $segs:tt ) { mod $($cdr:tt)* } ) => { rreil2_binop!($names # $segs # Modulo # $($cdr)*) };
( ( $names:tt , $segs:tt ) { and $($cdr:tt)* } ) => { rreil2_binop!($names # $segs # And # $($cdr)*) };
( ( $names:tt , $segs:tt ) { xor $($cdr:tt)* } ) => { rreil2_binop!($names # $segs # ExclusiveOr # $($cdr)*) };
( ( $names:tt , $segs:tt ) { or $($cdr:tt)* } ) => { rreil2_binop!($names # $segs # InclusiveOr # $($cdr)*) };
( ( $names:tt , $segs:tt ) { cmpeq $($cdr:tt)* } ) => { rreil2_binop!($names # $segs # Equal # $($cdr)*) };
( ( $names:tt , $segs:tt ) { cmpleu $($cdr:tt)* } ) => { rreil2_binop!($names # $segs # LessOrEqualUnsigned # $($cdr)*) };
( ( $names:tt , $segs:tt ) { cmples $($cdr:tt)* } ) => { rreil2_binop!($names # $segs # LessOrEqualSigned # $($cdr)*) };
( ( $names:tt , $segs:tt ) { cmpltu $($cdr:tt)* } ) => { rreil2_binop!($names # $segs # LessUnsigned # $($cdr)*) };
( ( $names:tt , $segs:tt ) { cmplts $($cdr:tt)* } ) => { rreil2_binop!($names # $segs # LessSigned # $($cdr)*) };
( ( $names:tt , $segs:tt ) { sel / $off:tt / $sz:tt $($cdr:tt)* } ) => { rreil2_selop!($names # $segs # Select # $off # $sz # $($cdr)*) };
( ( $names:tt , $segs:tt ) { sext / $sz:tt $($cdr:tt)* } ) => { rreil2_extop!($names # $segs # SignExtend # $sz # $($cdr)*) };
( ( $names:tt , $segs:tt ) { zext / $sz:tt $($cdr:tt)* } ) => { rreil2_extop!($names # $segs # ZeroExtend # $sz # $($cdr)*) };
( ( $names:tt , $segs:tt ) { mov $($cdr:tt)* } ) => { rreil2_unop!($names # $segs # Move # $($cdr)*) };
( ( $names:tt , $segs:tt ) { call $($cdr:tt)* } ) => { rreil2_callop!($names # $segs # $($cdr)*) };
( ( $names:tt , $segs:tt ) { ret $($cdr:tt)* } ) => { rreil2_retop!($names # $segs # $($cdr)*) };
( ( $names:tt , $segs:tt ) { load / $r:ident / $en:ident / $sz:tt $($cdr:tt)* } ) => { rreil_load!($names # $segs # $r # $en # $sz # $($cdr)*) };
( ( $names:tt , $segs:tt ) { store / $r:ident / $en:ident / $sz:tt $($cdr:tt)* } ) => { rreil_store!($names # $segs # $r # $en # $sz # $($cdr)*) };
}
include!(concat!(env!("OUT_DIR"), "/rreil.rs"));
#[macro_export]
macro_rules! rreil_var {
( $names:tt # ( $a:expr ) ) =>
{ ($a).clone().into() };
( $names:tt # $a:ident : $a_w:tt ) => {
$crate::Variable{
name: $names.insert(&$crate::Name::new(stringify!($a).into(),None)),
bits: rreil_imm!($a_w)
}
};
}
#[macro_export]
macro_rules! rreil_val {
( $names:tt # ? ) => { $crate::Value::Undefined };
( $names:tt # ( $a:expr ) ) => { ($a).clone().into() };
( $names:tt # [ $a:tt ] : $a_w:tt ) => {
$crate::Value::Constant($crate::Constant{
value: rreil_imm!($a) as u64,
bits: rreil_imm!($a_w)
})
};
( $names:tt # $a:ident : $a_w:tt ) => {
$crate::Value::Variable($crate::Variable{
name: $names.insert(&$crate::Name::new(stringify!($a).into(),None)),
bits: rreil_imm!($a_w)
})
};
}
#[macro_export]
macro_rules! rreil_imm {
($x:expr) => ($x as usize);
}
#[cfg(test)]
mod tests {
use super::*;
use {Value};
#[test]
fn rreil_macro() {
use {Name,Names,Segments};
let mut names = Names::default();
let mut segs = Segments::default();
let eax = Value::var(names.insert(&Name::new("eax".into(),None)),12).unwrap();
let t0 = Variable::new(names.insert(&Name::new("eax".into(),None)),12).unwrap();
let val = Value::val(1337,12).unwrap();
let _ = rreil2!{
(names,segs) {
add (t0), (val), (eax);
and t0:32, [2147483648]:32, eax:32;
and t1:32, [2147483648]:32, ebx:32;
sub t2:32, ebx : 32 , eax:32;
and t3:32, [2147483648]:32, t2:32;
shr SF:8, [31]:8, t3:8;
xor t4:32, t1:32, t0:32;
xor t5:32, t3:32, t0:32;
and t6:32, t5:32, t4:32;
shr OF:8, [31]:8, t6:8;
and t7:64, [4294967296]:64, t2:64;
shr CF:8, [32]:8, t7:8;
and t8:32, [4294967295]:32, t2:32;
xor t9:8, OF:8, SF:8;
sel/0/32 ebx:32, rax:64;
}
}.unwrap();
let _ = rreil2!{
(names,segs) {
sub t0:32, eax:32, ebx:32;
cmpltu CF:1, eax:32, ebx:32;
cmpleu CForZF:1, eax:32, ebx:32;
cmplts SFxorOF:1, eax:32, ebx:32;
cmples SFxorOForZF:1, eax:32, ebx:32;
cmpeq ZF:1, eax:32, ebx:32;
cmplts SF:1, t0:32, [0]:32;
xor OF:1, SFxorOF:1, SF:1;
}
}.unwrap();
let _ = rreil2!{
(names,segs) {
sub rax:32, rax:32, [1]:32;
mov rax:32, [0]:32;
}
}.unwrap();
let _ = rreil2!{
(names,segs) {
store/ram/le/32 rax:32, [0]:32;
load/ram/le/32 rax:32, [0]:32;
}
}.unwrap();
let _ = rreil2!{
(names,segs) {
sext/32 rax:32, ax:16;
zext/32 rax:32, ax:16;
mov rax:32, tbx:32;
}
};
}
}