1use crate::VtableIndex;
10use crate::{
11 ArgumentTypeHint, CppBodyAnalysis, CppBodyKind, CppConfidence, CppEvidence, CppEvidenceKind,
12 CppReturnChannel,
13};
14use macho_core::model::addr::Va;
15use macho_core::model::macho_file::MachoFile;
16use macho_core::model::symbol::{Symbol, SymbolTable};
17use macho_insn::{Arch, BranchTarget, Insn, InsnKind, Operand, RegClass};
18use std::collections::{BTreeMap, BTreeSet};
19
20const MAX_BODY_BYTES: usize = 16384;
23
24const MAX_INSNS: usize = 4000;
27
28const PROLOGUE_WINDOW: usize = 25;
30
31const EPILOGUE_WINDOW: usize = 10;
33
34const ARM64_MAX_GPR_ARGS: u32 = 8; const ARM64_MAX_FP_ARGS: u32 = 8; const X86_64_MAX_GPR_ARGS: u32 = 6; const X86_64_MAX_FP_ARGS: u32 = 8; pub fn analyze_symbol_body(
42 macho: &MachoFile<'_>,
43 symtab: &SymbolTable<'_>,
44 symbol: &Symbol<'_>,
45 vtable_index: Option<&VtableIndex>,
46) -> Option<CppBodyAnalysis> {
47 if !symbol.is_defined() || symbol.value == 0 {
48 return None;
49 }
50
51 let bytes = symbol_bytes(macho, symtab, symbol, MAX_BODY_BYTES)?;
52 let arch_name = macho.header().cpu_type().name().to_string();
53
54 let (
55 kind,
56 return_channel,
57 likely_wrapper,
58 this_adjustment,
59 param_counts,
60 cfg,
61 mut evidence_detail,
62 ) = if arch_name.starts_with("arm64") {
63 let arch = if arch_name == "arm64e" {
64 Arch::Arm64e
65 } else {
66 Arch::Arm64
67 };
68 analyze_arm64(bytes, symbol.value, arch)
69 } else if arch_name == "x86_64" {
70 analyze_x86_64(bytes, symbol.value)
71 } else {
72 (
73 CppBodyKind::Unknown,
74 CppReturnChannel::Unknown,
75 false,
76 None,
77 None,
78 None,
79 "unsupported architecture".to_string(),
80 )
81 };
82
83 let param_count = param_counts.map(|pc| pc.total());
84
85 let argument_hints = if let (Some(cfg), Some(counts)) = (&cfg, param_counts) {
87 let is_arm64 = arch_name.starts_with("arm64");
88 infer_argument_types(cfg, counts, is_arm64, macho, symtab, vtable_index)
89 } else {
90 Vec::new()
91 };
92
93 let mut confidence = match kind {
94 CppBodyKind::Thunk | CppBodyKind::Stub => CppConfidence::High,
95 CppBodyKind::Standard => {
96 if return_channel != CppReturnChannel::Unknown {
97 CppConfidence::Medium
98 } else {
99 CppConfidence::Low
100 }
101 }
102 CppBodyKind::Unknown => CppConfidence::Low,
103 };
104 if let Some(cfg) = &cfg {
105 if !cfg.decode_gaps.is_empty() {
106 confidence = CppConfidence::Low;
107 evidence_detail.push_str(&format!(
108 "; lossy decoding skipped {} invalid region(s)",
109 cfg.decode_gaps.len()
110 ));
111 }
112 }
113
114 Some(CppBodyAnalysis {
115 arch: arch_name,
116 kind,
117 return_channel,
118 this_adjustment,
119 likely_wrapper,
120 param_count,
121 argument_hints,
122 evidence: vec![CppEvidence {
123 kind: CppEvidenceKind::BodyAnalysis,
124 confidence,
125 detail: evidence_detail,
126 }],
127 })
128}
129
130fn symbol_bytes<'a>(
131 macho: &'a MachoFile<'_>,
132 symtab: &SymbolTable<'_>,
133 symbol: &Symbol<'_>,
134 max_len: usize,
135) -> Option<&'a [u8]> {
136 let next_va = symtab
137 .defined()
138 .filter(|candidate| candidate.value > symbol.value)
139 .map(|candidate| candidate.value)
140 .min()
141 .unwrap_or(symbol.value + max_len as u64);
142 let len = (next_va - symbol.value).min(max_len as u64) as usize;
143 macho.read_bytes_at_va(Va(symbol.value), len.max(1)).ok()
144}
145
146include!("cfg.rs");
147include!("arm64.rs");
148include!("x86_64.rs");
149include!("arguments.rs");
150include!("tests.rs");