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 block_ins(&self, b: &Block) -> &[Insn] {
266 &self.insns[b.ins_lo..b.ins_hi]
267 }
268
269 pub fn block_last(&self, b: &Block) -> Option<&Insn> {
271 if b.ins_hi > b.ins_lo {
272 self.insns.get(b.ins_hi - 1)
273 } else {
274 None
275 }
276 }
277
278 pub fn block_at(&self, p: u32) -> Option<usize> {
279 let i = self.starts.partition_point(|&l| l <= p);
280 if i == 0 {
281 None
282 } else {
283 Some(i - 1)
284 }
285 }
286
287 pub fn exc_edges(&self) -> Vec<(usize, usize, usize)> {
290 let mut out = Vec::new();
291 for (ri, r) in self.exc_ranges.iter().enumerate() {
292 if let Some(h) = self.blocks.iter().position(|b| b.start == r.handler) {
293 for b in &self.blocks {
294 if b.start >= r.start && b.start < r.end && b.ins_hi > b.ins_lo {
295 out.push((ri, b.id, h));
296 }
297 }
298 }
299 }
300 out
301 }
302
303 pub fn to_core(&self) -> jdc_core::cfg::Cfg {
305 let blocks = self
306 .blocks
307 .iter()
308 .map(|b| jdc_core::cfg::Block {
309 id: b.id,
310 start: b.start,
311 end: b.end,
312 ins_len: (b.ins_hi - b.ins_lo) as u32,
313 succ: b.succ.clone(),
314 pred: b.pred.clone(),
315 handlers: b.handlers.iter().map(|&h| h as u32).collect(),
316 })
317 .collect();
318 let ranges = self
319 .exc_ranges
320 .iter()
321 .map(|r| jdc_core::cfg::ExcRange {
322 start: r.start,
323 end: r.end,
324 handler: r.handler,
325 catch_type: r.catch_type.clone(),
326 })
327 .collect();
328 let edges = self
329 .exc_edges()
330 .into_iter()
331 .map(|(range, from, to)| jdc_core::cfg::ExcEdge { range, from, to })
332 .collect();
333 jdc_core::cfg::Cfg::from_parts(blocks, self.entry, ranges, edges)
334 }
335}
336
337fn insn_targets(ins: &Insn) -> Vec<u32> {
339 match &ins.kind {
340 InsnKind::Goto { target } => vec![*target],
341 InsnKind::If { target, .. } => vec![*target],
342 _ => Vec::new(),
344 }
345}
346
347pub fn switch_targets(payload: Option<&Payload>, switch_pc: u32) -> Vec<u32> {
349 match payload {
350 Some(Payload::Packed { targets, .. }) => targets
351 .iter()
352 .map(|t| (switch_pc as i64 + *t as i64) as u32)
353 .collect(),
354 Some(Payload::Sparse { pairs }) => pairs
355 .iter()
356 .map(|(_, t)| (switch_pc as i64 + *t as i64) as u32)
357 .collect(),
358 _ => Vec::new(),
359 }
360}
361
362pub fn switch_keys(payload: Option<&Payload>) -> SwitchKeys {
364 match payload {
365 Some(Payload::Packed { first_key, targets }) => SwitchKeys::Table {
366 low: *first_key,
367 targets: targets.clone(),
368 },
369 Some(Payload::Sparse { pairs }) => SwitchKeys::Lookup {
370 pairs: pairs.clone(),
371 },
372 _ => SwitchKeys::None,
373 }
374}
375
376pub enum SwitchKeys {
377 Table { low: i32, targets: Vec<i32> },
378 Lookup { pairs: Vec<(i32, i32)> },
379 None,
380}