use crate::il::*;
use std::fmt;
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct RefProgramLocation<'p> {
function: &'p Function,
function_location: RefFunctionLocation<'p>,
}
impl<'p> RefProgramLocation<'p> {
pub fn new(
function: &'p Function,
function_location: RefFunctionLocation<'p>,
) -> RefProgramLocation<'p> {
RefProgramLocation {
function,
function_location,
}
}
pub fn from_address(program: &'p Program, address: u64) -> Option<RefProgramLocation<'p>> {
let mut function = None;
for f in program.functions() {
if f.address() > address {
continue;
}
if function == None {
function = Some(f);
continue;
}
let ff = function.unwrap();
if f.address() > ff.address() {
function = Some(f);
}
}
if let Some(function) = function {
for block in function.blocks() {
for instruction in block.instructions() {
if instruction.address().map(|a| a == address).unwrap_or(false) {
return Some(RefProgramLocation::new(
function,
RefFunctionLocation::Instruction(block, instruction),
));
}
}
}
}
for function in program.functions() {
for block in function.blocks() {
for instruction in block.instructions() {
if let Some(iaddress) = instruction.address() {
if iaddress == address {
return Some(RefProgramLocation::new(
function,
RefFunctionLocation::Instruction(block, instruction),
));
}
}
}
}
}
None
}
pub fn from_function(function: &Function) -> Option<Result<RefProgramLocation>> {
function.control_flow_graph().entry().map(|entry| {
function.block(entry).map(|block| {
RefProgramLocation::new(
function,
block
.instructions()
.first()
.map(|instruction| RefFunctionLocation::Instruction(block, instruction))
.unwrap_or(RefFunctionLocation::EmptyBlock(block)),
)
})
})
}
pub fn function(&self) -> &Function {
self.function
}
pub fn function_location(&self) -> &RefFunctionLocation {
&self.function_location
}
pub fn block(&self) -> Option<&Block> {
self.function_location.block()
}
pub fn instruction(&self) -> Option<&Instruction> {
self.function_location.instruction()
}
pub fn edge(&self) -> Option<&Edge> {
self.function_location.edge()
}
pub fn address(&self) -> Option<u64> {
if let Some(instruction) = self.function_location.instruction() {
return instruction.address();
}
None
}
pub fn migrate<'m>(&self, program: &'m Program) -> Result<RefProgramLocation<'m>> {
let function = program
.function(self.function().index().unwrap())
.ok_or_else(|| {
ErrorKind::ProgramLocationMigration(format!(
"Could not find function {}",
self.function.index().unwrap()
))
})?;
let function_location = match self.function_location {
RefFunctionLocation::Instruction(block, instruction) => {
let block = function.block(block.index())?;
let instruction = block.instruction(instruction.index()).ok_or_else(|| {
ErrorKind::ProgramLocationMigration(format!(
"Could not find instruction {}",
instruction.index()
))
})?;
RefFunctionLocation::Instruction(block, instruction)
}
RefFunctionLocation::Edge(edge) => {
let edge = function.edge(edge.head(), edge.tail())?;
RefFunctionLocation::Edge(edge)
}
RefFunctionLocation::EmptyBlock(block) => {
let block = function.block(block.index())?;
RefFunctionLocation::EmptyBlock(block)
}
};
Ok(RefProgramLocation {
function,
function_location,
})
}
fn instruction_backward(
&self,
block: &'p Block,
instruction: &Instruction,
) -> Result<Vec<RefProgramLocation<'p>>> {
let instructions = block.instructions();
for i in (0..instructions.len()).rev() {
if instructions[i].index() == instruction.index() {
if i > 0 {
let instruction = &instructions[i - 1];
return Ok(vec![RefProgramLocation::new(
self.function,
RefFunctionLocation::Instruction(block, instruction),
)]);
}
let edges = self.function.control_flow_graph().edges_in(block.index())?;
let mut locations = Vec::new();
for edge in edges {
locations.push(RefProgramLocation::new(
self.function,
RefFunctionLocation::Edge(edge),
));
}
return Ok(locations);
}
}
Err(format!(
"Could not find instruction {} in block {} in function {:?}",
instruction.index(),
block.index(),
self.function.index()
)
.into())
}
fn edge_backward(&self, edge: &'p Edge) -> Result<Vec<RefProgramLocation<'p>>> {
let block = self.function.block(edge.head())?;
let instructions = block.instructions();
if instructions.is_empty() {
Ok(vec![RefProgramLocation::new(
self.function,
RefFunctionLocation::EmptyBlock(block),
)])
} else {
Ok(vec![RefProgramLocation::new(
self.function,
RefFunctionLocation::Instruction(block, instructions.last().unwrap()),
)])
}
}
fn empty_block_backward(&self, block: &'p Block) -> Result<Vec<RefProgramLocation<'p>>> {
let edges = self.function.control_flow_graph().edges_in(block.index())?;
let mut locations = Vec::new();
for edge in edges {
locations.push(RefProgramLocation::new(
self.function,
RefFunctionLocation::Edge(edge),
));
}
Ok(locations)
}
fn instruction_forward(
&self,
block: &'p Block,
instruction: &Instruction,
) -> Result<Vec<RefProgramLocation<'p>>> {
let instructions = block.instructions();
for i in 0..instructions.len() {
if instructions[i].index() == instruction.index() {
if i + 1 < instructions.len() {
let instruction = &instructions[i + 1];
return Ok(vec![RefProgramLocation::new(
self.function,
RefFunctionLocation::Instruction(block, instruction),
)]);
}
let edges = self
.function
.control_flow_graph()
.edges_out(block.index())?;
let mut locations = Vec::new();
for edge in edges {
locations.push(RefProgramLocation::new(
self.function,
RefFunctionLocation::Edge(edge),
));
}
return Ok(locations);
}
}
Err(format!(
"Could not find instruction {} in block {} in function {:?}",
instruction.index(),
block.index(),
self.function.index()
)
.into())
}
fn edge_forward(&self, edge: &'p Edge) -> Result<Vec<RefProgramLocation<'p>>> {
let block = self.function.block(edge.tail())?;
let instructions = block.instructions();
if instructions.is_empty() {
Ok(vec![RefProgramLocation::new(
self.function,
RefFunctionLocation::EmptyBlock(block),
)])
} else {
Ok(vec![RefProgramLocation::new(
self.function,
RefFunctionLocation::Instruction(block, &instructions[0]),
)])
}
}
fn empty_block_forward(&self, block: &'p Block) -> Result<Vec<RefProgramLocation<'p>>> {
let edges = self
.function
.control_flow_graph()
.edges_out(block.index())?;
let mut locations = Vec::new();
for edge in edges {
locations.push(RefProgramLocation::new(
self.function,
RefFunctionLocation::Edge(edge),
));
}
Ok(locations)
}
pub fn forward(&self) -> Result<Vec<RefProgramLocation<'p>>> {
match self.function_location {
RefFunctionLocation::Instruction(block, instruction) => {
self.instruction_forward(block, instruction)
}
RefFunctionLocation::Edge(edge) => self.edge_forward(edge),
RefFunctionLocation::EmptyBlock(block) => self.empty_block_forward(block),
}
}
pub fn backward(&self) -> Result<Vec<RefProgramLocation<'p>>> {
match self.function_location {
RefFunctionLocation::Instruction(block, instruction) => {
self.instruction_backward(block, instruction)
}
RefFunctionLocation::Edge(edge) => self.edge_backward(edge),
RefFunctionLocation::EmptyBlock(block) => self.empty_block_backward(block),
}
}
}
impl<'f> fmt::Display for RefProgramLocation<'f> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.function.index() {
Some(index) => write!(f, "0x{:x}:{}", index, self.function_location),
None => write!(f, "{}", self.function_location),
}
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum RefFunctionLocation<'f> {
Instruction(&'f Block, &'f Instruction),
Edge(&'f Edge),
EmptyBlock(&'f Block),
}
impl<'f> RefFunctionLocation<'f> {
pub fn block(&self) -> Option<&Block> {
match *self {
RefFunctionLocation::Instruction(block, _) => Some(block),
RefFunctionLocation::EmptyBlock(block) => Some(block),
_ => None,
}
}
pub fn instruction(&self) -> Option<&Instruction> {
match *self {
RefFunctionLocation::Instruction(_, instruction) => Some(instruction),
_ => None,
}
}
pub fn edge(&self) -> Option<&Edge> {
match *self {
RefFunctionLocation::Edge(edge) => Some(edge),
_ => None,
}
}
pub fn program_location(self, function: &'f Function) -> RefProgramLocation<'f> {
RefProgramLocation::new(function, self)
}
}
impl<'f> fmt::Display for RefFunctionLocation<'f> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
RefFunctionLocation::Instruction(block, ref instruction) => {
write!(f, "0x{:x}:{}", block.index(), instruction)
}
RefFunctionLocation::Edge(ref edge) => edge.fmt(f),
RefFunctionLocation::EmptyBlock(ref empty_block) => empty_block.fmt(f),
}
}
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub struct ProgramLocation {
function_index: Option<usize>,
function_location: FunctionLocation,
}
impl ProgramLocation {
pub fn new(
function_index: Option<usize>,
function_location: FunctionLocation,
) -> ProgramLocation {
ProgramLocation {
function_index,
function_location,
}
}
pub fn apply<'p>(&self, program: &'p Program) -> Result<RefProgramLocation<'p>> {
if self.function_index.is_none() {
return Err(ErrorKind::ProgramLocationApplication.into());
}
let function_index = self
.function_index
.ok_or(ErrorKind::ProgramLocationApplication)?;
let function = program
.function(function_index)
.ok_or(ErrorKind::ProgramLocationApplication)?;
let function_location = self.function_location.apply(function)?;
Ok(RefProgramLocation::new(function, function_location))
}
pub fn function_location(&self) -> &FunctionLocation {
&self.function_location
}
pub fn block_index(&self) -> Option<usize> {
self.function_location.block_index()
}
pub fn instruction_index(&self) -> Option<usize> {
self.function_location.instruction_index()
}
}
impl<'p> From<RefProgramLocation<'p>> for ProgramLocation {
fn from(program_location: RefProgramLocation) -> ProgramLocation {
ProgramLocation {
function_index: program_location.function().index(),
function_location: program_location.function_location.into(),
}
}
}
impl fmt::Display for ProgramLocation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.function_index {
Some(function_index) => write!(f, "0x{:x}:{}", function_index, self.function_location),
None => write!(f, "{}", self.function_location),
}
}
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub enum FunctionLocation {
Instruction(usize, usize),
Edge(usize, usize),
EmptyBlock(usize),
}
impl FunctionLocation {
pub fn apply<'f>(&self, function: &'f Function) -> Result<RefFunctionLocation<'f>> {
match *self {
FunctionLocation::Instruction(block_index, instruction_index) => {
let block = function
.block(block_index)
.map_err(|_| ErrorKind::FunctionLocationApplication)?;
let instruction = block
.instruction(instruction_index)
.ok_or(ErrorKind::FunctionLocationApplication)?;
Ok(RefFunctionLocation::Instruction(block, instruction))
}
FunctionLocation::Edge(head, tail) => {
let edge = function
.edge(head, tail)
.map_err(|_| ErrorKind::FunctionLocationApplication)?;
Ok(RefFunctionLocation::Edge(edge))
}
FunctionLocation::EmptyBlock(block_index) => {
let block = function
.block(block_index)
.map_err(|_| ErrorKind::FunctionLocationApplication)?;
Ok(RefFunctionLocation::EmptyBlock(block))
}
}
}
pub fn block_index(&self) -> Option<usize> {
match *self {
FunctionLocation::Instruction(block_index, _) => Some(block_index),
FunctionLocation::EmptyBlock(block_index) => Some(block_index),
_ => None,
}
}
pub fn instruction_index(&self) -> Option<usize> {
match *self {
FunctionLocation::Instruction(_, instruction_index) => Some(instruction_index),
_ => None,
}
}
}
impl<'f> From<RefFunctionLocation<'f>> for FunctionLocation {
fn from(function_location: RefFunctionLocation) -> FunctionLocation {
match function_location {
RefFunctionLocation::Instruction(block, instruction) => {
FunctionLocation::Instruction(block.index(), instruction.index())
}
RefFunctionLocation::Edge(edge) => FunctionLocation::Edge(edge.head(), edge.tail()),
RefFunctionLocation::EmptyBlock(block) => FunctionLocation::EmptyBlock(block.index()),
}
}
}
impl fmt::Display for FunctionLocation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
FunctionLocation::Instruction(block_index, instruction_index) => {
write!(f, "0x{:X}:{:02X}", block_index, instruction_index)
}
FunctionLocation::Edge(head_index, tail_index) => {
write!(f, "(0x{:X}->0x{:X})", head_index, tail_index)
}
FunctionLocation::EmptyBlock(block_index) => write!(f, "0x{:X}", block_index),
}
}
}