-
Notifications
You must be signed in to change notification settings - Fork 288
Expand file tree
/
Copy pathname_stubs.rs
More file actions
99 lines (87 loc) · 3.16 KB
/
Copy pathname_stubs.rs
File metadata and controls
99 lines (87 loc) · 3.16 KB
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 binaryninja::{
low_level_il::instruction::{InstructionHandler as _, LowLevelILInstructionKind},
symbol::{Symbol, SymbolType},
variable::PossibleValueSet,
workflow::AnalysisContext,
};
use crate::{
activities::objc_msg_send_calls::{call_target_type, selector_from_call, MessageSendType},
error::ILLevel,
metadata::GlobalState,
Error,
};
// Reconstruct names for Objective-C selector stubs, e.g. `_objc_msgSend$length`.
// The compiler emits one of these stubs per selector. Each one does nothing but load the selector and tail-call
// `objc_msgSend`.
pub fn process(ac: &AnalysisContext) -> Result<(), Error> {
let view = ac.view();
if GlobalState::should_ignore_view(&view) {
return Ok(());
}
let func = ac.function();
let func_start = func.start();
// Don't override a name the user has assigned to this function.
if let Some(symbol) = view.symbol_by_address(func_start) {
if !symbol.auto_defined() {
return Ok(());
}
}
// Bail out early for functions that do not look like a stub: a single basic block of a few instructions.
const MAX_STUB_SIZE: u64 = 32;
if func.highest_address().saturating_sub(func_start) > MAX_STUB_SIZE
|| func.basic_blocks().len() != 1
{
return Ok(());
}
let Some(llil) = (unsafe { ac.llil_function() }) else {
return Err(Error::MissingIL {
level: ILLevel::Low,
func_start,
});
};
let Some(ssa) = llil.ssa_form() else {
return Err(Error::MissingSsaForm {
level: ILLevel::Low,
func_start,
});
};
// The tail call terminates the stub's single basic block, so it can only be the last instruction.
let blocks = ssa.basic_blocks();
let Some(block) = blocks.iter().next() else {
return Ok(());
};
let Some(insn) = block.iter().last() else {
return Ok(());
};
let LowLevelILInstructionKind::TailCallSsa(call_op) = insn.kind() else {
return Ok(());
};
// A selector stub does nothing besides load a selector and tail-call objc_msgSend.
// Reject any function that performs a regular call or memory write before the tail call.
if block.iter().any(|insn| {
matches!(
insn.kind(),
LowLevelILInstructionKind::CallSsa(_)
| LowLevelILInstructionKind::Store(_)
| LowLevelILInstructionKind::StoreSsa(_)
)
}) {
return Ok(());
}
let call_target = match call_op.target().possible_values() {
PossibleValueSet::ConstantValue { value }
| PossibleValueSet::ConstantPointerValue { value }
| PossibleValueSet::ImportedAddressValue { value } => value as u64,
_ => return Ok(()),
};
if call_target_type(&view, call_target) != Some(MessageSendType::Normal) {
return Ok(());
}
let Some(selector) = selector_from_call(&view, &ssa, &call_op) else {
return Ok(());
};
let name = format!("_objc_msgSend${}", selector.name);
let symbol = Symbol::builder(SymbolType::Function, &name, func_start).create();
view.define_auto_symbol(&symbol);
Ok(())
}