1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
use super::*;
use crate::utils::log::LogMessage;
use std::{collections::HashSet, fmt};
/// A basic block is a sequence of `Def` instructions followed by up to two `Jmp` instructions.
///
/// The `Def` instructions represent side-effectful operations that are executed in order when the block is entered.
/// `Def` instructions do not affect the control flow of a program.
///
/// The `Jmp` instructions represent control flow affecting operations.
/// There can only be zero, one or two `Jmp`s:
/// - Zero `Jmp`s indicate that the next execution to be executed could not be discerned.
/// This should only happen on disassembler errors or on dead ends in the control flow graph that were deliberately inserted by the user.
/// - If there is exactly one `Jmp`, it is required to be an unconditional jump.
/// - For two jumps, the first one has to be a conditional jump,
/// where the second unconditional jump is only taken if the condition of the first jump evaluates to false.
///
/// If one of the `Jmp` instructions is an indirect jump,
/// then the `indirect_jmp_targets` is a list of possible jump target addresses for that jump.
/// The list may not be complete and the entries are not guaranteed to be correct.
///
/// Basic blocks are *single entry, single exit*, i.e. a basic block is only entered at the beginning
/// and is only exited by the jump instructions at the end of the block.
/// If a new control flow edge is discovered that would jump to the middle of a basic block,
/// the block structure needs to be updated accordingly.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct Blk {
/// The `Def` instructions of the basic block in order of execution.
pub defs: Vec<Term<Def>>,
/// The `Jmp` instructions of the basic block
pub jmps: Vec<Term<Jmp>>,
/// If the basic block contains an indirect jump,
/// this field contains possible jump target addresses for the jump.
///
/// Note that possible targets of indirect calls are *not* contained,
/// since the [`Project` normalization passes](Project::normalize) assume
/// that only intraprocedural jump targets are contained in this field.
pub indirect_jmp_targets: Vec<Tid>,
}
impl Term<Blk> {
/// Remove indirect jump target addresses for which no corresponding target
/// block exists.
///
/// Returns an error message for each removed address.
pub fn remove_nonexisting_indirect_jump_targets(
&mut self,
all_jump_targets: &HashSet<Tid>,
) -> Result<(), Vec<LogMessage>> {
let mut logs = Vec::new();
self.term.indirect_jmp_targets = self
.term
.indirect_jmp_targets
.iter()
.filter_map(|target| {
if all_jump_targets.contains(target) {
Some(target.clone())
} else {
let error_msg =
format!("Indirect jump target at {} does not exist", target.address);
logs.push(LogMessage::new_error(error_msg).location(self.tid.clone()));
None
}
})
.collect();
if logs.is_empty() {
Ok(())
} else {
Err(logs)
}
}
/// Returns a new artificial sink block with the given suffix attached to
/// its ID.
pub fn artificial_sink(id_suffix: &str) -> Self {
Self {
tid: Tid::artificial_sink_block(id_suffix),
term: Blk {
defs: Vec::with_capacity(0),
jmps: Vec::with_capacity(0),
indirect_jmp_targets: Vec::with_capacity(0),
},
}
}
}
impl fmt::Display for Blk {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for Term { tid, term: def } in self.defs.iter() {
writeln!(f, "DEF [{}] {}", tid, def)?;
}
for Term { tid, term: jmp } in self.jmps.iter() {
writeln!(f, "JMP [{}] {}", tid, jmp)?;
}
Ok(())
}
}