use crate::arch::x86::{
DecodedInsn, FlowControl, direct_unconditional_branch_target, format_intel,
};
use crate::ast::Stmt;
use super::{Candidate, Pattern, PatternCtx};
pub struct TailJmp;
impl Pattern for TailJmp {
fn name(&self) -> &'static str {
"tail_jmp"
}
fn tentative(
&self,
ctx: &PatternCtx,
insns: &[DecodedInsn],
start: usize,
) -> Option<Candidate> {
let ins = insns.get(start)?;
let fc = ins.iced.flow_control();
let next_addr = ins.iced.ip().wrapping_add(ins.original_bytes.len() as u64);
let is_function_tail = next_addr >= ctx.fn_addr_end;
match fc {
FlowControl::IndirectBranch => {
if !is_function_tail {
return None;
}
let formatted = format_intel(&ins.iced);
let target = formatted
.strip_prefix("jmp ")
.unwrap_or(&formatted)
.trim_start_matches("dword ptr ")
.trim_start_matches("qword ptr ")
.trim_start_matches("near ptr ")
.to_string();
Some(Candidate {
pattern: self.name(),
start,
consumed: 1,
priority: 150,
stmts: vec![Stmt::Call {
name: "tail_call".into(),
args: vec![target],
bytes: ins.original_bytes.clone(),
direct_target: None,
}],
})
}
FlowControl::UnconditionalBranch => {
let target = direct_unconditional_branch_target(&ins.iced)?;
if target >= ctx.fn_addr_start && target < ctx.fn_addr_end {
return None;
}
let _ = is_function_tail;
let (name, args) = if let Some(callee) = ctx.name_at.get(&target) {
(format!("tail_{callee}"), Vec::new())
} else {
("tail_call".into(), vec![format!("0x{target:x}")])
};
Some(Candidate {
pattern: self.name(),
start,
consumed: 1,
priority: 150,
stmts: vec![Stmt::Call {
name,
args,
bytes: ins.original_bytes.clone(),
direct_target: None,
}],
})
}
_ => None,
}
}
}