Commit c11a21e9 by Enkelmann Committed by Enkelmann

implement conversion functions for jumps and defs.

parent 263c1498
use crate::intermediate_representation::BinOpType as IrBinOpType;
use crate::intermediate_representation::ByteSize;
use crate::intermediate_representation::CastOpType as IrCastOpType;
use crate::intermediate_representation::Expression as IrExpression;
use crate::intermediate_representation::UnOpType as IrUnOpType;
use apint::Width;
use serde::{Deserialize, Serialize};
pub mod variable;
......@@ -7,6 +13,13 @@ pub type Bitvector = apint::ApInt;
pub type BitSize = u16;
impl From<BitSize> for ByteSize {
/// Convert to `ByteSize`, while always rounding up to the nearest full byte.
fn from(bitsize: BitSize) -> ByteSize {
((bitsize as u64 + 7) / 8).into()
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub enum Expression {
Var(Variable),
......@@ -189,6 +202,100 @@ impl Expression {
}
}
}
pub fn bitsize(&self) -> BitSize {
use Expression::*;
match self {
Var(var) => var.bitsize().unwrap(),
Const(bitvector) => bitvector.width().to_usize() as u16,
Load { size, .. } => *size,
Store { .. } => 0,
BinOp { op, lhs, rhs: _ } => {
use BinOpType::*;
match op {
EQ | NEQ | LT | LE | SLT | SLE => 1,
_ => lhs.bitsize(),
}
}
UnOp { arg, .. } => arg.bitsize(),
Cast { width, .. } => *width,
Let { .. } => panic!(),
Unknown {
description: _,
type_,
} => type_.bitsize().unwrap(),
IfThenElse { true_exp, .. } => true_exp.bitsize(),
Extract {
low_bit, high_bit, ..
} => high_bit - low_bit,
Concat { left, right } => left.bitsize() + right.bitsize(),
}
}
}
impl From<Expression> for IrExpression {
fn from(expr: Expression) -> IrExpression {
use Expression::*;
match expr {
Var(var) => IrExpression::Var(var.into()),
Const(bitvector) => IrExpression::Const(bitvector),
Load { .. } | Store { .. } | Let { .. } | Unknown { .. } | IfThenElse { .. } => {
panic!()
}
BinOp { op, lhs, rhs } => IrExpression::BinOp {
op: op.into(),
lhs: Box::new(IrExpression::from(*lhs)),
rhs: Box::new(IrExpression::from(*rhs)),
},
UnOp { op, arg } => IrExpression::UnOp {
op: op.into(),
arg: Box::new(IrExpression::from(*arg)),
},
Cast { kind, width, arg } => {
use CastType::*;
match kind {
UNSIGNED => IrExpression::Cast {
arg: Box::new(IrExpression::from(*arg)),
op: IrCastOpType::IntZExt,
size: width.into(),
},
SIGNED => IrExpression::Cast {
arg: Box::new(IrExpression::from(*arg)),
op: IrCastOpType::IntSExt,
size: width.into(),
},
HIGH => {
assert!(width % 8 == 0);
let low_byte = (arg.bitsize() - BitSize::from(width)).into();
IrExpression::Subpiece {
arg: Box::new(IrExpression::from(*arg)),
low_byte,
size: width.into(),
}
}
LOW => IrExpression::Subpiece {
arg: Box::new(IrExpression::from(*arg)),
low_byte: (0 as u64).into(),
size: width.into(),
},
}
}
Extract {
low_bit,
high_bit,
arg,
} => IrExpression::Subpiece {
size: (high_bit - low_bit + 1).into(),
low_byte: low_bit.into(),
arg: Box::new(IrExpression::from(*arg)),
},
Concat { left, right } => IrExpression::BinOp {
op: IrBinOpType::Piece,
lhs: Box::new(IrExpression::from(*left)),
rhs: Box::new(IrExpression::from(*right)),
},
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone, Copy)]
......@@ -222,12 +329,50 @@ pub enum BinOpType {
SLE,
}
impl From<BinOpType> for IrBinOpType {
fn from(op: BinOpType) -> IrBinOpType {
use BinOpType::*;
use IrBinOpType::*;
match op {
PLUS => IntAdd,
MINUS => IntSub,
TIMES => IntMult,
DIVIDE => IntDiv,
SDIVIDE => IntSDiv,
MOD => IntRem,
SMOD => IntSRem,
LSHIFT => IntLeft,
RSHIFT => IntRight,
ARSHIFT => IntSRight,
AND => IntAnd,
OR => IntOr,
XOR => IntXOr,
EQ => IntEqual,
NEQ => IntNotEqual,
LT => IntLess,
LE => IntLessEqual,
SLT => IntSLess,
SLE => IntSLessEqual,
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum UnOpType {
NEG,
NOT,
}
impl From<UnOpType> for IrUnOpType {
fn from(op: UnOpType) -> IrUnOpType {
use UnOpType::*;
match op {
NEG => IrUnOpType::Int2Comp,
NOT => IrUnOpType::IntNegate,
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum Endianness {
LittleEndian,
......
use super::BitSize;
use crate::intermediate_representation::Variable as IrVariable;
use crate::prelude::*;
use serde::{Deserialize, Serialize};
......@@ -19,12 +20,33 @@ pub enum Type {
Unknown,
}
impl Type {
pub fn bitsize(&self) -> Result<BitSize, Error> {
if let Type::Immediate(bitsize) = self {
Ok(*bitsize)
} else {
Err(anyhow!("Not a register type"))
}
}
}
impl Variable {
pub fn bitsize(&self) -> Result<BitSize, Error> {
if let Type::Immediate(bitsize) = self.type_ {
Ok(bitsize)
self.type_.bitsize()
}
}
impl From<Variable> for IrVariable {
fn from(var: Variable) -> IrVariable {
let size = if let Type::Immediate(bitsize) = var.type_ {
bitsize.into()
} else {
Err(anyhow!("Not a register variable"))
panic!()
};
IrVariable {
name: var.name,
size,
is_temp: var.is_temp,
}
}
}
......
......@@ -38,6 +38,8 @@ pub enum BinOpType {
IntNotEqual,
IntLess,
IntSLess,
IntLessEqual,
IntSLessEqual,
IntAdd,
IntSub,
IntCarry,
......@@ -74,7 +76,6 @@ pub enum CastOpType {
Int2Float,
Float2Float,
Trunc,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone, Copy)]
......
use super::{ByteSize, Expression, Variable};
use crate::prelude::*;
use crate::term::{Term, Tid};
use super::{Variable, Expression, ByteSize};
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub enum Def {
......@@ -19,3 +17,32 @@ pub enum Def {
value: Expression,
},
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub enum Jmp {
Branch(Tid),
BranchInd(Expression),
CBranch {
target: Tid,
condition: Expression,
},
Call {
target: Tid,
return_: Option<Tid>,
},
CallInd {
target: Expression,
return_: Option<Tid>,
},
Return(Expression),
CallOther {
description: String,
return_: Option<Tid>,
},
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct Blk {
pub defs: Vec<Term<Def>>,
pub jmps: Vec<Term<Jmp>>,
}
use crate::prelude::*;
use super::ByteSize;
use crate::prelude::*;
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct Variable {
......
......@@ -11,10 +11,10 @@ pub mod abstract_domain;
pub mod analysis;
pub mod bil;
pub mod ffi;
pub mod intermediate_representation;
pub mod pcode;
pub mod term;
pub mod utils;
pub mod pcode;
pub mod intermediate_representation;
mod prelude {
pub use apint::Width;
......
use crate::prelude::*;
use crate::term::{Term, Tid};
use crate::intermediate_representation::BinOpType as IrBinOpType;
use crate::intermediate_representation::ByteSize;
use crate::intermediate_representation::Variable as IrVariable;
use crate::intermediate_representation::CastOpType as IrCastOpType;
use crate::intermediate_representation::Expression as IrExpression;
use crate::intermediate_representation::BinOpType as IrBinOpType;
use crate::intermediate_representation::UnOpType as IrUnOpType;
use crate::intermediate_representation::CastOpType as IrCastOpType;
use crate::intermediate_representation::Variable as IrVariable;
use crate::prelude::*;
use crate::term::{Term, Tid};
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct Variable {
......@@ -31,17 +31,19 @@ impl From<Variable> for IrExpression {
(Some(_name), None) => IrExpression::Var(pcode_var.into()),
(None, Some(hex_value)) => {
// TODO: Implement parsing for large hex values.
if pcode_var.size > 8.into() {
panic!("Parsing of immediates greater than 8 bytes not yet implemented: {}", hex_value);
if u64::from(pcode_var.size) > 8 {
panic!(
"Parsing of immediates greater than 8 bytes not yet implemented: {}",
hex_value
);
}
let val: u64 = u64::from_str_radix(&hex_value, 16).unwrap();
let mut bitvector: Bitvector = Bitvector::from_u64(val);
bitvector.truncate(pcode_var.size).unwrap();
IrExpression::Const(bitvector)
},
}
_ => panic!(),
}
}
}
......@@ -50,12 +52,15 @@ impl From<Variable> for ByteSize {
match (&pcode_var.name, &pcode_var.value) {
(None, Some(hex_value)) => {
// TODO: Implement parsing for large hex values.
if pcode_var.size > 8.into() {
panic!("Parsing of immediates greater than 8 bytes not yet implemented: {}", hex_value);
if u64::from(pcode_var.size) > 8 {
panic!(
"Parsing of immediates greater than 8 bytes not yet implemented: {}",
hex_value
);
}
let val: u64 = u64::from_str_radix(&hex_value, 16).unwrap();
val.into()
},
}
_ => panic!(),
}
}
......@@ -69,14 +74,6 @@ pub struct Expression {
pub input2: Option<Variable>,
}
impl From<Expression> for IrExpression {
fn from(expr: Expression) -> IrExpression {
match expr.mnemonic {
_ => todo!(),
}
}
}
#[allow(non_camel_case_types)]
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum ExpressionType {
......@@ -90,12 +87,15 @@ pub enum ExpressionType {
INT_NOTEQUAL,
INT_LESS,
INT_SLESS,
INT_LESSEQUAL,
INT_SLESSEQUAL,
INT_ADD,
INT_SUB,
INT_CARRY,
INT_SCARRY,
INT_SBORROW,
INT_XOR,
INT_AND,
INT_OR,
......@@ -103,16 +103,17 @@ pub enum ExpressionType {
INT_LEFT,
INT_RIGHT,
INT_SRIGHT,
INT_MULT,
INT_DIV,
INT_REM,
INT_SDIV,
INT_SREM,
BOOL_XOR,
BOOL_AND,
BOOL_OR,
FLOAT_EQUAL,
FLOAT_NOTEQUAL,
FLOAT_LESS,
......@@ -126,6 +127,7 @@ pub enum ExpressionType {
INT_NEGATE,
INT_2COMP,
BOOL_NEGATE,
FLOAT_NEGATE,
FLOAT_ABS,
FLOAT_SQRT,
......@@ -144,12 +146,16 @@ pub enum ExpressionType {
impl From<ExpressionType> for IrBinOpType {
fn from(expr_type: ExpressionType) -> IrBinOpType {
use ExpressionType::*;
use IrBinOpType::*;
match expr_type {
PIECE => IrBinOpType::Piece,
INT_EQUAL => IrBinOpType::IntEqual,
INT_NOTEQUAL => IrBinOpType::IntNotEqual,
INT_LESS => IrBinOpType::IntLess,
INT_SLESS => IrBinOpType::IntSLess,
INT_LESSEQUAL => IntLessEqual,
INT_SLESSEQUAL => IntSLessEqual,
INT_ADD => IrBinOpType::IntAdd,
INT_SUB => IrBinOpType::IntSub,
......
......@@ -7,4 +7,3 @@ mod expressions;
pub use expressions::*;
mod term;
pub use term::*;
use super::{Expression, Variable};
use crate::intermediate_representation::Def as IrDef;
use crate::intermediate_representation::Expression as IrExpression;
use crate::intermediate_representation::Jmp as IrJmp;
use crate::prelude::*;
use crate::term::{Term, Tid};
// TODO: Handle the case where an indirect tail call is represented by CALLIND plus RETURN
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct Call {
pub target: Label,
......@@ -15,9 +18,10 @@ pub struct Jmp {
pub mnemonic: JmpType,
pub goto: Option<Label>,
pub call: Option<Call>,
pub condition: Option<Expression>,
pub condition: Option<Variable>,
}
// TODO: CALLOTHER is still missing!
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum JmpType {
BRANCH,
......@@ -28,6 +32,49 @@ pub enum JmpType {
RETURN,
}
impl From<Jmp> for IrJmp {
fn from(jmp: Jmp) -> IrJmp {
use JmpType::*;
let unwrap_label_direct = |label| {
if let Label::Direct(tid) = label {
tid
} else {
panic!()
}
};
let unwrap_label_indirect = |label| {
if let Label::Indirect(expr) = label {
expr
} else {
panic!()
}
};
match jmp.mnemonic {
BRANCH => IrJmp::Branch(unwrap_label_direct(jmp.goto.unwrap())),
CBRANCH => IrJmp::CBranch {
target: unwrap_label_direct(jmp.goto.unwrap()),
condition: jmp.condition.unwrap().into(),
},
BRANCHIND => IrJmp::BranchInd(unwrap_label_indirect(jmp.goto.unwrap()).into()),
CALL => {
let call = jmp.call.unwrap();
IrJmp::Call {
target: unwrap_label_direct(call.target),
return_: call.return_.map(unwrap_label_direct),
}
}
CALLIND => {
let call = jmp.call.unwrap();
IrJmp::CallInd {
target: unwrap_label_indirect(call.target).into(),
return_: call.return_.map(unwrap_label_direct),
}
}
RETURN => IrJmp::Return(unwrap_label_indirect(jmp.goto.unwrap()).into()),
}
}
}
// TODO: Remove since code duplication?
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub enum Label {
......@@ -47,7 +94,7 @@ impl From<Def> for IrDef {
match def.rhs.mnemonic {
COPY => IrDef::Assign {
var: def.lhs.into(),
value: IrExpression::from(def.rhs),
value: def.rhs.input0.unwrap().into(),
},
LOAD => IrDef::Load {
var: def.lhs.into(),
......@@ -57,10 +104,11 @@ impl From<Def> for IrDef {
address: def.rhs.input1.unwrap().into(),
value: def.rhs.input2.unwrap().into(),
},
PIECE | INT_EQUAL | INT_NOTEQUAL | INT_LESS | INT_SLESS | INT_ADD | INT_SUB | INT_CARRY
| INT_SCARRY | INT_SBORROW | INT_XOR | INT_AND | INT_OR | INT_LEFT | INT_RIGHT
| INT_SRIGHT | INT_MULT | INT_DIV | INT_REM | INT_SDIV | INT_SREM | BOOL_XOR | BOOL_AND
| BOOL_OR | FLOAT_EQUAL | FLOAT_NOTEQUAL | FLOAT_LESS | FLOAT_LESSEQUAL | FLOAT_ADD
PIECE | INT_EQUAL | INT_NOTEQUAL | INT_LESS | INT_SLESS | INT_LESSEQUAL
| INT_SLESSEQUAL | INT_ADD | INT_SUB | INT_CARRY | INT_SCARRY | INT_SBORROW
| INT_XOR | INT_AND | INT_OR | INT_LEFT | INT_RIGHT | INT_SRIGHT | INT_MULT
| INT_DIV | INT_REM | INT_SDIV | INT_SREM | BOOL_XOR | BOOL_AND | BOOL_OR
| FLOAT_EQUAL | FLOAT_NOTEQUAL | FLOAT_LESS | FLOAT_LESSEQUAL | FLOAT_ADD
| FLOAT_SUB | FLOAT_MULT | FLOAT_DIV => IrDef::Assign {
var: def.lhs.into(),
value: IrExpression::BinOp {
......@@ -77,8 +125,8 @@ impl From<Def> for IrDef {
arg: Box::new(def.rhs.input0.unwrap().into()),
},
},
INT_NEGATE | INT_2COMP | BOOL_NEGATE | FLOAT_NEGATE | FLOAT_ABS | FLOAT_SQRT | FLOAT_CEIL
| FLOAT_FLOOR | FLOAT_ROUND | FLOAT_NAN => IrDef::Assign {
INT_NEGATE | INT_2COMP | BOOL_NEGATE | FLOAT_NEGATE | FLOAT_ABS | FLOAT_SQRT
| FLOAT_CEIL | FLOAT_FLOOR | FLOAT_ROUND | FLOAT_NAN => IrDef::Assign {
var: def.lhs.into(),
value: IrExpression::UnOp {
op: def.rhs.mnemonic.into(),
......@@ -171,7 +219,7 @@ mod tests {
"#,
)
.unwrap();
let _ : IrDef = def.into();
let _: IrDef = def.into();
}
#[test]
......
use crate::bil::*;
use crate::intermediate_representation::Def as IrDef;
use crate::intermediate_representation::Expression as IrExpression;
use crate::intermediate_representation::Jmp as IrJmp;
use serde::{Deserialize, Serialize};
pub mod symbol;
......@@ -37,6 +40,80 @@ pub struct Def {
pub rhs: Expression,
}
impl Def {
fn to_ir_defs(self) -> Vec<IrDef> {
match self.rhs {
Expression::Load { address, .. } => {
let (defs, cleaned_address, _) = extract_loads_from_expression(*address, 0);
let mut ir_defs: Vec<IrDef> =
defs.into_iter().map(|def| def.to_ir_assignment()).collect();
ir_defs.push(IrDef::Load {
address: cleaned_address.into(),
var: self.lhs.into(),
});
ir_defs
}
Expression::Store { address, value, .. } => {
let (mut defs, cleaned_address, counter) =
extract_loads_from_expression(*address, 0);
let (mut more_defs, cleaned_value, _) =
extract_loads_from_expression(*value, counter);
defs.append(&mut more_defs);
let mut ir_defs: Vec<IrDef> =
defs.into_iter().map(|def| def.to_ir_assignment()).collect();
ir_defs.push(IrDef::Store {
address: cleaned_address.into(),
value: cleaned_value.into(),
});
ir_defs
}
Expression::IfThenElse {
condition,
true_exp,
false_exp,
} => {
// We only match for conditional stores.
// Other usages of the `IfThenElse`-expression will result in panics.
let (address, value) = match (*true_exp, *false_exp) {
(Expression::Store { address, value, .. }, Expression::Var(var))
| (Expression::Var(var), Expression::Store { address, value, .. })
if var == self.lhs =>
{
(address, value)
}
_ => panic!(),
};
let (mut defs, _cleaned_condition, counter) =
extract_loads_from_expression(*condition, 0);
let (mut more_defs, cleaned_adress, counter) =
extract_loads_from_expression(*address, counter);
let (mut even_more_defs, cleaned_value, _) =
extract_loads_from_expression(*value, counter);
defs.append(&mut more_defs);
defs.append(&mut even_more_defs);
let mut ir_defs: Vec<IrDef> =
defs.into_iter().map(|def| def.to_ir_assignment()).collect();
ir_defs.push(IrDef::Store {
address: cleaned_adress.into(),
value: IrExpression::Unknown {
description: "BAP conditional store".into(),
size: cleaned_value.bitsize().into(),
},
});
ir_defs
}
_ => vec![self.to_ir_assignment()],
}
}
fn to_ir_assignment(self) -> IrDef {
IrDef::Assign {
var: self.lhs.into(),
value: self.rhs.into(),
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct Jmp {
pub condition: Option<Expression>,
......@@ -51,6 +128,38 @@ pub enum JmpKind {
Interrupt { value: isize, return_addr: Tid },
}
impl From<Jmp> for IrJmp {
fn from(jmp: Jmp) -> IrJmp {
match jmp.kind {
JmpKind::Goto(Label::Direct(tid)) => IrJmp::Branch(tid),
JmpKind::Goto(Label::Indirect(expr)) => IrJmp::BranchInd(expr.into()),
JmpKind::Return(Label::Indirect(expr)) => IrJmp::Return(expr.into()),
JmpKind::Return(Label::Direct(_)) => panic!(),
JmpKind::Call(call) => {
let return_ = match call.return_ {
Some(Label::Direct(tid)) => Some(tid),
None => None,
_ => panic!(),
};
match call.target {
Label::Direct(tid) => IrJmp::Call {
target: tid,
return_,
},
Label::Indirect(expr) => IrJmp::CallInd {
target: expr.into(),
return_,
},
}
}
JmpKind::Interrupt { value, return_addr } => IrJmp::CallOther {
description: format!("Interrupt {}", value),
return_: Some(return_addr),
},
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct Call {
pub target: Label,
......@@ -168,6 +277,106 @@ impl ArgIntent {
}
}
fn extract_loads_from_expression(expr: Expression, counter: u64) -> (Vec<Def>, Expression, u64) {
use Expression::*;
match expr {
Load {
memory,
address,
endian,
size,
} => {
let (mut defs, cleaned_address, mut counter) =
extract_loads_from_expression(*address, counter);
counter += 1;
let temp_var = Variable {
name: format!("temp_{}", counter),
type_: Type::Immediate(size),
is_temp: true,
};
defs.push(Def {
lhs: temp_var.clone(),
rhs: Load {
memory,
address: Box::new(cleaned_address),
endian,
size,
},
});
(defs, Var(temp_var), counter)
}
Var(_) | Const(_) | Unknown { .. } => (Vec::new(), expr, counter),
Store { .. } | Let { .. } | IfThenElse { .. } => panic!(),
BinOp { op, lhs, rhs } => {
let (mut defs, cleaned_lhs, counter) = extract_loads_from_expression(*lhs, counter);
let (mut defs_rhs, cleaned_rhs, counter) = extract_loads_from_expression(*rhs, counter);
defs.append(&mut defs_rhs);
(
defs,
BinOp {
op,
lhs: Box::new(cleaned_lhs),
rhs: Box::new(cleaned_rhs),
},
counter,
)
}
UnOp { op, arg } => {
let (defs, cleaned_arg, counter) = extract_loads_from_expression(*arg, counter);
(
defs,
UnOp {
op,
arg: Box::new(cleaned_arg),
},
counter,
)
}
Cast { kind, width, arg } => {
let (defs, cleaned_arg, counter) = extract_loads_from_expression(*arg, counter);
(
defs,
Cast {
kind,
width,
arg: Box::new(cleaned_arg),
},
counter,
)
}
Extract {
low_bit,
high_bit,
arg,
} => {
let (defs, cleaned_arg, counter) = extract_loads_from_expression(*arg, counter);
(
defs,
Extract {
low_bit,
high_bit,
arg: Box::new(cleaned_arg),
},
counter,
)
}
Concat { left, right } => {
let (mut defs, cleaned_left, counter) = extract_loads_from_expression(*left, counter);
let (mut defs_right, cleaned_right, counter) =
extract_loads_from_expression(*right, counter);
defs.append(&mut defs_right);
(
defs,
Concat {
left: Box::new(cleaned_left),
right: Box::new(cleaned_right),
},
counter,
)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
......
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