1use ddc_dex::code::CodeItem;
8use ddc_dex::insn::{Insn, InsnKind, Payload};
9
10#[derive(Debug, Clone)]
11pub struct Block {
12 pub id: usize,
13 pub start: u32,
15 pub end: u32,
17 pub ins_lo: usize,
21 pub ins_hi: usize,
22 pub succ: Vec<usize>,
25 pub pred: Vec<usize>,
26 pub handlers: Vec<usize>,
28}
29
30#[derive(Debug, Clone)]
31pub struct ExcRange {
32 pub start: u32,
34 pub end: u32,
35 pub handler: u32,
37 pub catch_type: Option<String>,
39}
40
41pub struct DexCfg {
42 pub blocks: Vec<Block>,
43 pub entry: usize,
44 pub exc_ranges: Vec<ExcRange>,
45 pub insns: Vec<Insn>,
48 pub code_units: u32,
50 starts: Vec<u32>,
52}
53
54impl DexCfg {
55 pub fn build(code: &mut CodeItem, type_name: &dyn Fn(u32) -> String) -> DexCfg {
62 let insns = std::mem::take(&mut code.insns);
63 let code_units: u32 = insns.last().map(|last| last.pc + last.size).unwrap_or(0);
64
65 let mut leaders: Vec<u32> = vec![0];
67 for i in 0..insns.len() {
68 let ins = &insns[i];
69 let next = ins.pc + ins.size;
70 if ins.is_branch() && next <= code_units {
71 leaders.push(next);
72 }
73 for t in insn_targets(ins) {
74 leaders.push(t);
75 }
76 }
77 let mut ranges: Vec<ExcRange> = Vec::new();
79 for t in &code.tries {
80 let end = t.start_addr.saturating_add(t.insn_count);
81 if t.start_addr >= end || t.start_addr >= code_units {
82 continue;
83 }
84 leaders.push(t.start_addr);
85 if end < code_units {
86 leaders.push(end);
87 }
88 if let Some(h) = code.handler_of(t) {
89 for (ty, addr) in &h.catches {
90 if *addr < code_units {
91 leaders.push(*addr);
92 ranges.push(ExcRange {
93 start: t.start_addr,
94 end,
95 handler: *addr,
96 catch_type: Some(type_name(*ty)),
97 });
98 }
99 }
100 if let Some(addr) = h.catch_all {
101 if addr < code_units {
102 leaders.push(addr);
103 ranges.push(ExcRange {
104 start: t.start_addr,
105 end,
106 handler: addr,
107 catch_type: None,
108 });
109 }
110 }
111 }
112 }
113 leaders.retain(|&l| l < code_units || (l == 0 && code_units == 0));
114 leaders.sort_unstable();
115 leaders.dedup();
116 if leaders.is_empty() {
117 leaders.push(0);
118 }
119
120 let block_of_pc = |p: u32| -> Option<usize> {
122 let i = leaders.partition_point(|&l| l <= p);
123 if i == 0 {
124 None
125 } else {
126 Some(i - 1)
127 }
128 };
129
130 let mut blocks: Vec<Block> = Vec::with_capacity(leaders.len());
134 for (id, &start) in leaders.iter().enumerate() {
135 let end = leaders
136 .get(id + 1)
137 .copied()
138 .unwrap_or(code_units)
139 .max(start);
140 let lo = insns.partition_point(|i| i.pc < start);
141 let hi = insns.partition_point(|i| i.pc < end);
142 blocks.push(Block {
143 id,
144 start,
145 end,
146 ins_lo: lo,
147 ins_hi: hi,
148 succ: Vec::new(),
149 pred: Vec::new(),
150 handlers: Vec::new(),
151 });
152 }
153
154 for b in blocks.iter_mut() {
156 if b.ins_hi <= b.ins_lo {
157 continue;
158 }
159 let last = &insns[b.ins_hi - 1];
160 match &last.kind {
161 InsnKind::Goto { target } => {
162 if let Some(tb) = block_of_pc(*target) {
163 b.succ.push(tb);
164 }
165 }
166 InsnKind::If { target, .. } => {
167 let next_pc = last.pc + last.size;
168 if let Some(f) = block_of_pc(next_pc) {
169 b.succ.push(f);
170 }
171 if let Some(tb) = block_of_pc(*target) {
172 b.succ.push(tb);
173 }
174 }
175 InsnKind::PackedSwitch { payload_pc, .. }
176 | InsnKind::SparseSwitch { payload_pc, .. } => {
177 let next_pc = last.pc + last.size;
178 if let Some(f) = block_of_pc(next_pc) {
179 b.succ.push(f); }
181 for t in switch_targets(code.payloads.get(payload_pc), last.pc) {
182 if let Some(tb) = block_of_pc(t) {
183 if !b.succ.contains(&tb) {
184 b.succ.push(tb);
185 }
186 }
187 }
188 }
189 InsnKind::ReturnVoid | InsnKind::Return { .. } | InsnKind::Throw { .. } => {}
190 _ => {
191 let next_pc = last.pc + last.size;
192 if next_pc < code_units {
193 if let Some(f) = block_of_pc(next_pc) {
194 b.succ.push(f);
195 }
196 }
197 }
198 }
199 }
200 for b in blocks.iter_mut() {
201 let mut seen = Vec::new();
202 b.succ.retain(|s| {
203 if seen.contains(s) {
204 false
205 } else {
206 seen.push(*s);
207 true
208 }
209 });
210 }
211
212 for b in 0..blocks.len() {
215 let succ = blocks[b].succ.clone();
216 for s in succ {
217 if s < blocks.len() {
218 blocks[s].pred.push(b);
219 }
220 }
221 }
222 for (ri, r) in ranges.iter().enumerate() {
223 if let Some(h) = blocks.iter().position(|b| b.start == r.handler) {
224 blocks[h].handlers.push(ri);
225 }
226 }
227 for b in blocks.iter_mut() {
228 b.pred.sort_unstable();
229 b.pred.dedup();
230 b.handlers.sort_unstable();
231 b.handlers.dedup();
232 }
233
234 let mut starts = leaders;
235 starts.sort_unstable();
236 DexCfg {
237 blocks,
238 entry: 0,
239 exc_ranges: ranges,
240 insns,
241 code_units,
242 starts,
243 }
244 }
245
246 pub fn block_ins(&self, b: &Block) -> &[Insn] {
248 &self.insns[b.ins_lo..b.ins_hi]
249 }
250
251 pub fn block_last(&self, b: &Block) -> Option<&Insn> {
253 if b.ins_hi > b.ins_lo {
254 self.insns.get(b.ins_hi - 1)
255 } else {
256 None
257 }
258 }
259
260 pub fn block_at(&self, p: u32) -> Option<usize> {
261 let i = self.starts.partition_point(|&l| l <= p);
262 if i == 0 {
263 None
264 } else {
265 Some(i - 1)
266 }
267 }
268
269 pub fn exc_edges(&self) -> Vec<(usize, usize, usize)> {
272 let mut out = Vec::new();
273 for (ri, r) in self.exc_ranges.iter().enumerate() {
274 if let Some(h) = self.blocks.iter().position(|b| b.start == r.handler) {
275 for b in &self.blocks {
276 if b.start >= r.start && b.start < r.end && b.ins_hi > b.ins_lo {
277 out.push((ri, b.id, h));
278 }
279 }
280 }
281 }
282 out
283 }
284
285 pub fn to_core(&self) -> jdc_core::cfg::Cfg {
287 let blocks = self
288 .blocks
289 .iter()
290 .map(|b| jdc_core::cfg::Block {
291 id: b.id,
292 start: b.start,
293 end: b.end,
294 ins_len: (b.ins_hi - b.ins_lo) as u32,
295 succ: b.succ.clone(),
296 pred: b.pred.clone(),
297 handlers: b.handlers.iter().map(|&h| h as u32).collect(),
298 })
299 .collect();
300 let ranges = self
301 .exc_ranges
302 .iter()
303 .map(|r| jdc_core::cfg::ExcRange {
304 start: r.start,
305 end: r.end,
306 handler: r.handler,
307 catch_type: r.catch_type.clone(),
308 })
309 .collect();
310 let edges = self
311 .exc_edges()
312 .into_iter()
313 .map(|(range, from, to)| jdc_core::cfg::ExcEdge { range, from, to })
314 .collect();
315 jdc_core::cfg::Cfg::from_parts(blocks, self.entry, ranges, edges)
316 }
317}
318
319fn insn_targets(ins: &Insn) -> Vec<u32> {
321 match &ins.kind {
322 InsnKind::Goto { target } => vec![*target],
323 InsnKind::If { target, .. } => vec![*target],
324 _ => Vec::new(),
326 }
327}
328
329pub fn switch_targets(payload: Option<&Payload>, switch_pc: u32) -> Vec<u32> {
331 match payload {
332 Some(Payload::Packed { targets, .. }) => targets
333 .iter()
334 .map(|t| (switch_pc as i64 + *t as i64) as u32)
335 .collect(),
336 Some(Payload::Sparse { pairs }) => pairs
337 .iter()
338 .map(|(_, t)| (switch_pc as i64 + *t as i64) as u32)
339 .collect(),
340 _ => Vec::new(),
341 }
342}
343
344pub fn switch_keys(payload: Option<&Payload>) -> SwitchKeys {
346 match payload {
347 Some(Payload::Packed { first_key, targets }) => SwitchKeys::Table {
348 low: *first_key,
349 targets: targets.clone(),
350 },
351 Some(Payload::Sparse { pairs }) => SwitchKeys::Lookup {
352 pairs: pairs.clone(),
353 },
354 _ => SwitchKeys::None,
355 }
356}
357
358pub enum SwitchKeys {
359 Table { low: i32, targets: Vec<i32> },
360 Lookup { pairs: Vec<(i32, i32)> },
361 None,
362}