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