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<std::sync::Arc<str>>,
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) -> std::sync::Arc<str>) -> 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 ins in insns.iter() {
68 let next = ins.pc + ins.size;
69 if ins.is_branch() && next <= code_units {
70 leaders.push(next);
71 }
72 for t in insn_targets(ins) {
73 leaders.push(t);
74 }
75 match &ins.kind {
86 InsnKind::PackedSwitch { payload_pc, .. }
87 | InsnKind::SparseSwitch { payload_pc, .. } => {
88 for t in switch_targets(code.payloads.get(payload_pc), ins.pc) {
89 leaders.push(t);
90 }
91 }
92 _ => {}
93 }
94 }
95 let mut ranges: Vec<ExcRange> = Vec::new();
97 for t in &code.tries {
98 let end = t.start_addr.saturating_add(t.insn_count);
99 if t.start_addr >= end || t.start_addr >= code_units {
100 continue;
101 }
102 leaders.push(t.start_addr);
103 if end < code_units {
104 leaders.push(end);
105 }
106 if let Some(h) = code.handler_of(t) {
107 for (ty, addr) in &h.catches {
108 if *addr < code_units {
109 leaders.push(*addr);
110 ranges.push(ExcRange {
111 start: t.start_addr,
112 end,
113 handler: *addr,
114 catch_type: Some(type_name(*ty)),
115 });
116 }
117 }
118 if let Some(addr) = h.catch_all {
119 if addr < code_units {
120 leaders.push(addr);
121 ranges.push(ExcRange {
122 start: t.start_addr,
123 end,
124 handler: addr,
125 catch_type: None,
126 });
127 }
128 }
129 }
130 }
131 leaders.retain(|&l| l < code_units || (l == 0 && code_units == 0));
132 leaders.sort_unstable();
133 leaders.dedup();
134 if leaders.is_empty() {
135 leaders.push(0);
136 }
137
138 let block_of_pc = |p: u32| -> Option<usize> {
140 let i = leaders.partition_point(|&l| l <= p);
141 if i == 0 {
142 None
143 } else {
144 Some(i - 1)
145 }
146 };
147
148 let mut blocks: Vec<Block> = Vec::with_capacity(leaders.len());
152 for (id, &start) in leaders.iter().enumerate() {
153 let end = leaders
154 .get(id + 1)
155 .copied()
156 .unwrap_or(code_units)
157 .max(start);
158 let lo = insns.partition_point(|i| i.pc < start);
159 let hi = insns.partition_point(|i| i.pc < end);
160 blocks.push(Block {
161 id,
162 start,
163 end,
164 ins_lo: lo,
165 ins_hi: hi,
166 succ: Vec::new(),
167 pred: Vec::new(),
168 handlers: Vec::new(),
169 });
170 }
171
172 for b in blocks.iter_mut() {
174 if b.ins_hi <= b.ins_lo {
175 continue;
176 }
177 let last = &insns[b.ins_hi - 1];
178 match &last.kind {
179 InsnKind::Goto { target } => {
180 if let Some(tb) = block_of_pc(*target) {
181 b.succ.push(tb);
182 }
183 }
184 InsnKind::If { target, .. } => {
185 let next_pc = last.pc + last.size;
186 if let Some(f) = block_of_pc(next_pc) {
187 b.succ.push(f);
188 }
189 if let Some(tb) = block_of_pc(*target) {
190 b.succ.push(tb);
191 }
192 }
193 InsnKind::PackedSwitch { payload_pc, .. }
194 | InsnKind::SparseSwitch { payload_pc, .. } => {
195 let next_pc = last.pc + last.size;
196 if let Some(f) = block_of_pc(next_pc) {
197 b.succ.push(f); }
199 for t in switch_targets(code.payloads.get(payload_pc), last.pc) {
200 if let Some(tb) = block_of_pc(t) {
201 if !b.succ.contains(&tb) {
202 b.succ.push(tb);
203 }
204 }
205 }
206 }
207 InsnKind::ReturnVoid | InsnKind::Return { .. } | InsnKind::Throw { .. } => {}
208 _ => {
209 let next_pc = last.pc + last.size;
210 if next_pc < code_units {
211 if let Some(f) = block_of_pc(next_pc) {
212 b.succ.push(f);
213 }
214 }
215 }
216 }
217 }
218 for b in blocks.iter_mut() {
219 let mut seen = Vec::new();
220 b.succ.retain(|s| {
221 if seen.contains(s) {
222 false
223 } else {
224 seen.push(*s);
225 true
226 }
227 });
228 }
229
230 for b in 0..blocks.len() {
233 let succ = blocks[b].succ.clone();
234 for s in succ {
235 if s < blocks.len() {
236 blocks[s].pred.push(b);
237 }
238 }
239 }
240 for (ri, r) in ranges.iter().enumerate() {
241 if let Some(h) = blocks.iter().position(|b| b.start == r.handler) {
242 blocks[h].handlers.push(ri);
243 }
244 }
245 for b in blocks.iter_mut() {
246 b.pred.sort_unstable();
247 b.pred.dedup();
248 b.handlers.sort_unstable();
249 b.handlers.dedup();
250 }
251
252 let mut starts = leaders;
253 starts.sort_unstable();
254 DexCfg {
255 blocks,
256 entry: 0,
257 exc_ranges: ranges,
258 insns,
259 code_units,
260 starts,
261 }
262 }
263
264 pub fn rpo_order(&self) -> Vec<usize> {
273 let n = self.blocks.len();
274 let mut seen = vec![false; n];
276 let mut post: Vec<usize> = Vec::new();
277 if n > 0 {
278 let mut stack: Vec<(usize, usize)> = vec![(0, 0)];
279 seen[0] = true;
280 while let Some(top) = stack.last_mut() {
281 let (b, i) = *top;
282 if i < self.blocks[b].succ.len() {
283 top.1 += 1;
284 let s = self.blocks[b].succ[i];
285 if s < n && !seen[s] {
286 seen[s] = true;
287 stack.push((s, 0));
288 }
289 } else {
290 post.push(b);
291 stack.pop();
292 }
293 }
294 }
295 let mut order: Vec<usize> = post.into_iter().rev().collect();
296 let mut rest: Vec<usize> = (0..n).filter(|&b| !seen[b]).collect();
298 rest.sort_unstable_by_key(|&b| self.blocks[b].start);
299 order.extend(rest);
300 order
301 }
302 pub fn block_ins(&self, b: &Block) -> &[Insn] {
303 &self.insns[b.ins_lo..b.ins_hi]
304 }
305
306 pub fn block_last(&self, b: &Block) -> Option<&Insn> {
308 if b.ins_hi > b.ins_lo {
309 self.insns.get(b.ins_hi - 1)
310 } else {
311 None
312 }
313 }
314
315 pub fn block_at(&self, p: u32) -> Option<usize> {
316 let i = self.starts.partition_point(|&l| l <= p);
317 if i == 0 {
318 None
319 } else {
320 Some(i - 1)
321 }
322 }
323
324 pub fn exc_edges(&self) -> Vec<(usize, usize, usize)> {
327 let mut out = Vec::new();
328 for (ri, r) in self.exc_ranges.iter().enumerate() {
329 if let Some(h) = self.blocks.iter().position(|b| b.start == r.handler) {
330 for b in &self.blocks {
331 if b.start >= r.start && b.start < r.end && b.ins_hi > b.ins_lo {
332 out.push((ri, b.id, h));
333 }
334 }
335 }
336 }
337 out
338 }
339
340 pub fn to_core(&self) -> jdc_core::cfg::Cfg {
342 let blocks = self
343 .blocks
344 .iter()
345 .map(|b| jdc_core::cfg::Block {
346 id: b.id,
347 start: b.start,
348 end: b.end,
349 ins_len: (b.ins_hi - b.ins_lo) as u32,
350 succ: b.succ.clone(),
351 pred: b.pred.clone(),
352 handlers: b.handlers.iter().map(|&h| h as u32).collect(),
353 })
354 .collect();
355 let ranges = self
356 .exc_ranges
357 .iter()
358 .map(|r| jdc_core::cfg::ExcRange {
359 start: r.start,
360 end: r.end,
361 handler: r.handler,
362 catch_type: r.catch_type.clone(),
363 })
364 .collect();
365 let edges = self
366 .exc_edges()
367 .into_iter()
368 .map(|(range, from, to)| jdc_core::cfg::ExcEdge { range, from, to })
369 .collect();
370 jdc_core::cfg::Cfg::from_parts(blocks, self.entry, ranges, edges)
371 }
372}
373
374fn insn_targets(ins: &Insn) -> Vec<u32> {
376 match &ins.kind {
377 InsnKind::Goto { target } => vec![*target],
378 InsnKind::If { target, .. } => vec![*target],
379 _ => Vec::new(),
381 }
382}
383
384pub fn switch_targets(payload: Option<&Payload>, switch_pc: u32) -> Vec<u32> {
386 match payload {
387 Some(Payload::Packed { targets, .. }) => targets
388 .iter()
389 .map(|t| (switch_pc as i64 + *t as i64) as u32)
390 .collect(),
391 Some(Payload::Sparse { pairs }) => pairs
392 .iter()
393 .map(|(_, t)| (switch_pc as i64 + *t as i64) as u32)
394 .collect(),
395 _ => Vec::new(),
396 }
397}
398
399pub fn switch_keys(payload: Option<&Payload>) -> SwitchKeys {
401 match payload {
402 Some(Payload::Packed { first_key, targets }) => SwitchKeys::Table {
403 low: *first_key,
404 targets: targets.clone(),
405 },
406 Some(Payload::Sparse { pairs }) => SwitchKeys::Lookup {
407 pairs: pairs.clone(),
408 },
409 _ => SwitchKeys::None,
410 }
411}
412
413pub enum SwitchKeys {
414 Table { low: i32, targets: Vec<i32> },
415 Lookup { pairs: Vec<(i32, i32)> },
416 None,
417}