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 100 101 102 103 104 105 106 107
use crate::prelude::*;
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.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
pub struct Tid {
/// The unique ID of the term.
id: String,
/// The address where the term is located.
pub address: String,
}
impl Tid {
/// Prefix for IDs of artificial sinks in the control flow graph.
///
/// Dummy blocks with such TIDs are added for different purposes, e.g., as
/// targets for jumps to non-existing targets or return targets for calls to
/// non-returning functions.
const ARTIFICIAL_SINK_BLOCK_ID_PREFIX: &'static str = "Artificial Sink Block";
/// The ID of the artificial sink sub.
///
/// This is used as the target for calls to non-existing functions.
const ARTIFICIAL_SINK_SUB_ID: &'static str = "Artificial Sink Sub";
/// Address for use in IDs of terms that do not have an address.
const UNKNOWN_ADDRESS: &'static str = "UNKNOWN";
/// Generate a new term identifier with the given ID string
/// and with unknown address.
pub fn new<T: ToString>(val: T) -> Tid {
Tid {
id: val.to_string(),
address: Self::UNKNOWN_ADDRESS.to_string(),
}
}
/// Add a suffix to the ID string and return the new `Tid`
pub fn with_id_suffix(self, suffix: &str) -> Self {
Tid {
id: self.id + suffix,
address: self.address,
}
}
/// Returns true if the ID string ends with the provided suffix.
pub fn has_id_suffix(&self, suffix: &str) -> bool {
self.id.ends_with(suffix)
}
/// Generate the ID of a block starting at the given address.
///
/// Note that the block may not actually exist.
/// For cases where one assembly instruction generates more than one block,
/// the returned block ID is the one that would be executed first if a jump to the given address happened.
pub fn blk_id_at_address(address: &str) -> Tid {
Tid {
id: format!("blk_{address}"),
address: address.to_string(),
}
}
/// Returns a new ID for an artificial sink block with the given suffix.
pub fn artificial_sink_block(suffix: &str) -> Self {
Self {
id: format!("{}{}", Self::ARTIFICIAL_SINK_BLOCK_ID_PREFIX, suffix),
address: Self::UNKNOWN_ADDRESS.to_string(),
}
}
/// Returns a new ID for the artificial sink sub.
pub fn artificial_sink_sub() -> Self {
Self {
id: Self::ARTIFICIAL_SINK_SUB_ID.to_string(),
address: Self::UNKNOWN_ADDRESS.to_string(),
}
}
/// Returns true iff the ID is for the artificial sink block with the given
/// suffix.
pub fn is_artificial_sink_block(&self, suffix: &str) -> bool {
self.id.starts_with(Self::ARTIFICIAL_SINK_BLOCK_ID_PREFIX)
&& self.has_id_suffix(suffix)
&& self.address == Self::UNKNOWN_ADDRESS
}
/// Returns true iff the ID is for the artificial sink sub.
pub fn is_artificial_sink_sub(&self) -> bool {
self.id == Self::ARTIFICIAL_SINK_SUB_ID && self.address == Self::UNKNOWN_ADDRESS
}
}
impl std::fmt::Display for Tid {
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "{}", self.id)
}
}
/// A term is an object inside a binary with an address and an unique ID (both contained in the `tid`).
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct Term<T> {
/// The term identifier, which also contains the address of the term
pub tid: Tid,
/// The object
pub term: T,
}