1use std::collections::HashMap;
4
5use crate::annotations::{self, EncodedValue};
6use crate::code::CodeItem;
7use crate::reader::Cursor;
8
9pub const NO_INDEX: u32 = 0xffff_ffff;
11
12#[derive(Debug, Clone)]
13pub struct ProtoId {
14 pub shorty_idx: u32,
16 pub return_type_idx: u32,
18 pub parameters_off: u32,
20}
21
22#[derive(Debug, Clone)]
23pub struct FieldId {
24 pub class_idx: u32,
25 pub type_idx: u32,
26 pub name_idx: u32,
27}
28
29#[derive(Debug, Clone)]
30pub struct MethodId {
31 pub class_idx: u32,
32 pub proto_idx: u32,
33 pub name_idx: u32,
34}
35
36#[derive(Debug, Clone)]
38pub struct ClassDef {
39 pub class_idx: u32,
41 pub access_flags: u32,
42 pub superclass_idx: u32,
43 pub interfaces_off: u32,
44 pub source_file_idx: u32,
45 pub annotations_off: u32,
46 pub class_data_off: u32,
47 pub static_values_off: u32,
48}
49
50#[derive(Debug, Clone)]
52pub struct EncodedField {
53 pub field_idx: u32,
54 pub access_flags: u32,
55}
56
57#[derive(Debug, Clone)]
59pub struct EncodedMethod {
60 pub method_idx: u32,
61 pub access_flags: u32,
62 pub code_off: u32,
63}
64
65#[derive(Debug, Clone, Default)]
67pub struct ClassData {
68 pub static_fields: Vec<EncodedField>,
69 pub instance_fields: Vec<EncodedField>,
70 pub direct_methods: Vec<EncodedMethod>,
71 pub virtual_methods: Vec<EncodedMethod>,
72}
73
74pub struct DexFile {
80 data: std::sync::RwLock<Option<Vec<u8>>>,
86 released: std::sync::atomic::AtomicBool,
89 strings: Vec<std::sync::Arc<str>>,
94 types: Vec<u32>,
96 protos: Vec<ProtoId>,
97 proto_tys: Vec<Box<[u32]>>,
102 proto_descs: Vec<std::sync::Arc<str>>,
107 fields: Vec<FieldId>,
108 methods: Vec<MethodId>,
109 pub class_defs: Vec<ClassDef>,
110 pub version: String,
111 by_type: HashMap<u32, usize>,
113 method_handles: Vec<MethodHandleItem>,
115 call_sites: Vec<CallSiteInfo>,
117}
118
119#[derive(Debug, Clone)]
121pub struct MethodHandleItem {
122 pub kind: u16,
126 pub target_id: u32,
128 pub is_field: bool,
129}
130
131#[derive(Debug, Clone)]
133pub struct CallSiteInfo {
134 pub bootstrap_handle: u32,
136 pub name_idx: u32,
138 pub proto_idx: u32,
140 pub linker_args: Vec<crate::annotations::EncodedValue>,
142}
143
144impl Drop for DexFile {
145 fn drop(&mut self) {
146 *self.data.write().unwrap() = None;
147 }
148}
149
150impl DexFile {
151 pub fn parse(data: Vec<u8>) -> Result<DexFile, String> {
152 if data.len() < crate::DEX_HEADER_SIZE {
153 return Err("file too small for DEX header".into());
154 }
155 let magic = &data[..8];
156 if &magic[..4] != b"dex\n" || magic[7] != 0 {
157 return Err(format!("not a DEX file: magic {:02x?}", magic));
158 }
159 let version = String::from_utf8_lossy(&magic[4..7]).into_owned();
160 if !matches!(
162 version.as_str(),
163 "035" | "037" | "038" | "039" | "040" | "041"
164 ) {
165 return Err(format!("unsupported DEX version {}", version));
166 }
167 let endian = u32::from_le_bytes(data[40..44].try_into().unwrap());
168 if endian != 0x1234_5678 {
169 return Err(format!("unexpected endian tag {endian:#x}"));
170 }
171
172 let u32at = |off: usize| -> u32 {
173 if off + 4 > data.len() {
174 return 0;
175 }
176 u32::from_le_bytes(data[off..off + 4].try_into().unwrap())
177 };
178 let string_ids_size = u32at(0x38) as usize;
179 let string_ids_off = u32at(0x3c) as usize;
180 let type_ids_size = u32at(0x40) as usize;
181 let type_ids_off = u32at(0x44) as usize;
182 let proto_ids_size = u32at(0x48) as usize;
183 let proto_ids_off = u32at(0x4c) as usize;
184 let field_ids_size = u32at(0x50) as usize;
185 let field_ids_off = u32at(0x54) as usize;
186 let method_ids_size = u32at(0x58) as usize;
187 let method_ids_off = u32at(0x5c) as usize;
188 let class_defs_size = u32at(0x60) as usize;
189 let class_defs_off = u32at(0x64) as usize;
190
191 let mut strings = Vec::with_capacity(string_ids_size);
193 if string_ids_off + 4 * string_ids_size <= data.len() {
194 for i in 0..string_ids_size {
195 let so = u32at(string_ids_off + 4 * i) as usize;
196 let mut c = Cursor::at(&data, so);
197 let s = c
198 .read_uleb128()
199 .and_then(|len| c.read_mutf8(c.pos, len))
200 .unwrap_or_default();
201 strings.push(std::sync::Arc::from(s));
202 }
203 }
204
205 let mut types = Vec::with_capacity(type_ids_size);
207 if type_ids_off + 4 * type_ids_size <= data.len() {
208 for i in 0..type_ids_size {
209 types.push(u32at(type_ids_off + 4 * i));
210 }
211 }
212
213 let mut protos = Vec::with_capacity(proto_ids_size);
215 if proto_ids_off + 12 * proto_ids_size <= data.len() {
216 for i in 0..proto_ids_size {
217 let base = proto_ids_off + 12 * i;
218 protos.push(ProtoId {
219 shorty_idx: u32at(base),
220 return_type_idx: {
221 let v = u16::from_le_bytes(data[base + 4..base + 6].try_into().unwrap());
222 v as u32
223 },
224 parameters_off: u32at(base + 8),
225 });
226 }
227 }
228 let proto_tys: Vec<Box<[u32]>> = protos
231 .iter()
232 .map(|p| read_type_list_at(&data, p.parameters_off as usize))
233 .collect();
234 let ty_name = |t: u32| -> &str {
236 match types.get(t as usize) {
237 Some(&si) => strings.get(si as usize).map(|s| &**s).unwrap_or(""),
238 None => "",
239 }
240 };
241 let proto_descs: Vec<std::sync::Arc<str>> = protos
242 .iter()
243 .zip(&proto_tys)
244 .map(|(p, tys)| {
245 let ret = ty_name(p.return_type_idx);
246 let mut d = String::with_capacity(2 + ret.len() + tys.len() * 12);
247 d.push('(');
248 for &t in tys.iter() {
249 d.push_str(ty_name(t));
250 }
251 d.push(')');
252 d.push_str(ret);
253 std::sync::Arc::from(d)
254 })
255 .collect();
256
257 let mut fields = Vec::with_capacity(field_ids_size);
259 if field_ids_off + 8 * field_ids_size <= data.len() {
260 for i in 0..field_ids_size {
261 let base = field_ids_off + 8 * i;
262 fields.push(FieldId {
263 class_idx: u16::from_le_bytes(data[base..base + 2].try_into().unwrap()) as u32,
264 type_idx: u16::from_le_bytes(data[base + 2..base + 4].try_into().unwrap())
265 as u32,
266 name_idx: u32at(base + 4),
267 });
268 }
269 }
270
271 let mut methods = Vec::with_capacity(method_ids_size);
273 if method_ids_off + 8 * method_ids_size <= data.len() {
274 for i in 0..method_ids_size {
275 let base = method_ids_off + 8 * i;
276 methods.push(MethodId {
277 class_idx: u16::from_le_bytes(data[base..base + 2].try_into().unwrap()) as u32,
278 proto_idx: u16::from_le_bytes(data[base + 2..base + 4].try_into().unwrap())
279 as u32,
280 name_idx: u32at(base + 4),
281 });
282 }
283 }
284
285 let mut class_defs = Vec::with_capacity(class_defs_size);
287 if class_defs_off + 32 * class_defs_size <= data.len() {
288 for i in 0..class_defs_size {
289 let base = class_defs_off + 32 * i;
290 class_defs.push(ClassDef {
291 class_idx: u32at(base),
292 access_flags: u32at(base + 4),
293 superclass_idx: u32at(base + 8),
294 interfaces_off: u32at(base + 12),
295 source_file_idx: u32at(base + 16),
296 annotations_off: u32at(base + 20),
297 class_data_off: u32at(base + 24),
298 static_values_off: u32at(base + 28),
299 });
300 }
301 }
302
303 let mut by_type = HashMap::with_capacity(class_defs.len());
304 for (i, cd) in class_defs.iter().enumerate() {
305 by_type.insert(cd.class_idx, i);
306 }
307
308 let (method_handles, call_sites) = parse_map_tables(&data);
311
312 Ok(DexFile {
313 data: std::sync::RwLock::new(Some(data)),
314 released: std::sync::atomic::AtomicBool::new(false),
315 strings,
316 types,
317 protos,
318 proto_tys,
319 proto_descs,
320 fields,
321 methods,
322 class_defs,
323 version,
324 by_type,
325 method_handles,
326 call_sites,
327 })
328 }
329
330 pub fn raw(&self) -> &[u8] {
341 let guard = self.data.read().unwrap();
346 match guard.as_ref() {
347 Some(bytes) => unsafe { std::mem::transmute::<&[u8], &[u8]>(bytes.as_slice()) },
348 None => &[],
349 }
350 }
351
352 pub fn release_data(&mut self) {
355 *self.data.write().unwrap() = None;
356 self.released
357 .store(true, std::sync::atomic::Ordering::Release);
358 }
359
360 pub fn mark_released(&self) {
365 self.released
366 .store(true, std::sync::atomic::Ordering::Release);
367 *self.data.write().unwrap() = None;
369 }
370
371 #[inline]
372 #[allow(dead_code)] fn is_released(&self) -> bool {
374 self.released.load(std::sync::atomic::Ordering::Acquire)
375 }
376
377 pub fn string(&self, idx: u32) -> &str {
378 self.strings
379 .get(idx as usize)
380 .map(|s| &**s)
381 .unwrap_or("")
382 }
383
384 pub fn string_count(&self) -> usize {
385 self.strings.len()
386 }
387
388 pub fn class_name(&self, idx: u32) -> String {
391 let d = self.type_name(idx);
392 if d.len() > 2 && d.starts_with('L') && d.ends_with(';') {
393 d[1..d.len() - 1].to_string()
394 } else {
395 d.to_string()
396 }
397 }
398
399 pub fn num_types(&self) -> usize {
402 self.types.len()
403 }
404
405 pub fn type_name(&self, idx: u32) -> &str {
406 match self.types.get(idx as usize) {
407 Some(&si) => self.string(si),
408 None => "",
409 }
410 }
411
412 pub fn type_name_arc(&self, idx: u32) -> std::sync::Arc<str> {
415 match self.types.get(idx as usize) {
416 Some(&si) => self.string_arc(si),
417 None => std::sync::Arc::from(""),
418 }
419 }
420
421 pub fn type_count(&self) -> usize {
422 self.types.len()
423 }
424
425 pub fn proto_count(&self) -> usize {
426 self.protos.len()
427 }
428
429 pub fn proto(&self, idx: u32) -> &ProtoId {
430 static EMPTY: std::sync::OnceLock<ProtoId> = std::sync::OnceLock::new();
431 self.protos.get(idx as usize).unwrap_or_else(|| {
432 EMPTY.get_or_init(|| ProtoId {
433 shorty_idx: 0,
434 return_type_idx: 0,
435 parameters_off: 0,
436 })
437 })
438 }
439
440 pub fn string_arc(&self, idx: u32) -> std::sync::Arc<str> {
443 self.strings
444 .get(idx as usize)
445 .cloned()
446 .unwrap_or_else(|| std::sync::Arc::from(""))
447 }
448
449 pub fn proto_desc(&self, idx: u32) -> std::sync::Arc<str> {
451 self.proto_descs
452 .get(idx as usize)
453 .cloned()
454 .unwrap_or_else(|| std::sync::Arc::from("()V"))
455 }
456
457 pub fn proto_params(&self, idx: u32) -> &[u32] {
460 self.proto_tys
461 .get(idx as usize)
462 .map(|b| &**b)
463 .unwrap_or(&[])
464 }
465
466 fn read_type_list(&self, off: u32) -> Option<Vec<u32>> {
469 let raw = self.raw();
470 Some(read_type_list_at(raw, off as usize).into())
471 }
472
473 pub fn field(&self, idx: u32) -> &FieldId {
474 static EMPTY: std::sync::OnceLock<FieldId> = std::sync::OnceLock::new();
475 self.fields.get(idx as usize).unwrap_or_else(|| {
476 EMPTY.get_or_init(|| FieldId {
477 class_idx: 0,
478 type_idx: 0,
479 name_idx: 0,
480 })
481 })
482 }
483
484 pub fn field_count(&self) -> usize {
485 self.fields.len()
486 }
487
488 pub fn method(&self, idx: u32) -> &MethodId {
489 static EMPTY: std::sync::OnceLock<MethodId> = std::sync::OnceLock::new();
490 self.methods.get(idx as usize).unwrap_or_else(|| {
491 EMPTY.get_or_init(|| MethodId {
492 class_idx: 0,
493 proto_idx: 0,
494 name_idx: 0,
495 })
496 })
497 }
498
499 pub fn method_count(&self) -> usize {
500 self.methods.len()
501 }
502
503 pub fn class_def_of(&self, type_idx: u32) -> Option<usize> {
505 self.by_type.get(&type_idx).copied()
506 }
507
508 pub fn interfaces_of(&self, cd: &ClassDef) -> Vec<u32> {
510 if cd.interfaces_off == 0 {
511 return Vec::new();
512 }
513 self.read_type_list(cd.interfaces_off).unwrap_or_default()
514 }
515
516 pub fn class_data(&self, cd: &ClassDef) -> ClassData {
517 if cd.class_data_off == 0 {
518 return ClassData::default();
519 }
520 self.read_class_data(cd.class_data_off).unwrap_or_default()
521 }
522
523 fn read_class_data(&self, off: u32) -> Option<ClassData> {
524 let mut c = Cursor::at(self.raw(), off as usize);
525 let sf = c.read_uleb128()? as usize;
526 let inf = c.read_uleb128()? as usize;
527 let dm = c.read_uleb128()? as usize;
528 let vm = c.read_uleb128()? as usize;
529
530 let read_fields = |n: usize, cur: &mut Cursor| -> Option<Vec<EncodedField>> {
531 let mut out = Vec::with_capacity(n);
532 let mut idx: u64 = 0;
533 for _ in 0..n {
534 idx += cur.read_uleb128()?;
535 out.push(EncodedField {
536 field_idx: idx as u32,
537 access_flags: cur.read_uleb128()? as u32,
538 });
539 }
540 Some(out)
541 };
542 let static_fields = read_fields(sf, &mut c)?;
543 let instance_fields = read_fields(inf, &mut c)?;
544
545 let read_methods = |n: usize, cur: &mut Cursor| -> Option<Vec<EncodedMethod>> {
546 let mut out = Vec::with_capacity(n);
547 let mut idx: u64 = 0;
548 for _ in 0..n {
549 idx += cur.read_uleb128()?;
550 out.push(EncodedMethod {
551 method_idx: idx as u32,
552 access_flags: cur.read_uleb128()? as u32,
553 code_off: cur.read_uleb128()? as u32,
554 });
555 }
556 Some(out)
557 };
558 let direct_methods = read_methods(dm, &mut c)?;
559 let virtual_methods = read_methods(vm, &mut c)?;
560
561 Some(ClassData {
562 static_fields,
563 instance_fields,
564 direct_methods,
565 virtual_methods,
566 })
567 }
568
569 pub fn code_stats(&self, off: u32) -> Option<(u16, u32)> {
572 if off == 0 {
573 return None;
574 }
575 let o = off as usize;
576 let d = self.raw();
579 if o + 16 > d.len() {
580 return None;
581 }
582 let regs = u16::from_le_bytes([d[o], d[o + 1]]);
583 let insns = u32::from_le_bytes([d[o + 12], d[o + 13], d[o + 14], d[o + 15]]);
584 Some((regs, insns))
585 }
586
587 pub fn debug_info_off_at(&self, off: u32) -> Option<u32> {
589 if off == 0 {
590 return None;
591 }
592 let o = off as usize;
593 let d = self.raw();
594 if o + 12 > d.len() {
595 return None;
596 }
597 Some(u32::from_le_bytes([d[o + 8], d[o + 9], d[o + 10], d[o + 11]]))
598 }
599
600 pub fn code_at(&self, off: u32) -> Option<CodeItem> {
601 if off == 0 {
602 return None;
603 }
604 CodeItem::parse(self.raw(), off as usize)
605 }
606
607 pub fn class_names_from_image(image: &[u8]) -> Vec<String> {
612 if image.len() < 0x70 || &image[..4] != b"dex\n" {
613 return Vec::new();
614 }
615 let u4 = |o: usize| -> u32 {
616 u32::from_le_bytes([image[o], image[o + 1], image[o + 2], image[o + 3]])
617 };
618 let strings_size = u4(0x38) as usize;
619 let strings_off = u4(0x3c) as usize;
620 let types_size = u4(0x40) as usize;
621 let types_off = u4(0x44) as usize;
622 let classes_size = u4(0x60) as usize;
623 let classes_off = u4(0x64) as usize;
624 if strings_off + 4 * strings_size > image.len()
625 || types_off + 4 * types_size > image.len()
626 || classes_off + 32 * classes_size > image.len()
627 {
628 return Vec::new();
629 }
630 let u4at = |o: usize| -> u32 {
631 u32::from_le_bytes([image[o], image[o + 1], image[o + 2], image[o + 3]])
632 };
633 let string_at = |idx: u32| -> Option<String> {
634 let so = u4at(strings_off + 4 * idx as usize) as usize;
635 if so >= image.len() {
636 return None;
637 }
638 let mut p = so;
640 let mut len = 0u64;
642 let mut shift = 0;
643 loop {
644 if p >= image.len() {
645 return None;
646 }
647 let b = image[p];
648 p += 1;
649 len |= ((b & 0x7f) as u64) << shift;
650 shift += 7;
651 if b & 0x80 == 0 {
652 break;
653 }
654 }
655 let end = image[p..].iter().position(|&b| b == 0)? + p;
656 crate::reader::mutf8_decode(&image[p..end], len)
657 };
658 let mut out = Vec::with_capacity(classes_size);
659 for i in 0..classes_size {
660 let cd = classes_off + 32 * i;
661 let class_idx = u4at(cd) as usize;
662 if class_idx >= types_size {
663 continue;
664 }
665 let string_idx = u4at(types_off + 4 * class_idx);
666 if let Some(name) = string_at(string_idx) {
667 let plain = name
670 .strip_prefix('L')
671 .and_then(|s| s.strip_suffix(';'))
672 .map(str::to_string)
673 .unwrap_or(name);
674 out.push(plain);
675 }
676 }
677 out
678 }
679
680 pub fn code_insns_bytes_at(&self, off: u32) -> Option<&[u8]> {
683 if off == 0 {
684 return None;
685 }
686 let off = off as usize;
687 let d = self.raw();
688 if off + 16 > d.len() {
689 return None;
690 }
691 let insns_size =
693 u32::from_le_bytes([d[off + 12], d[off + 13], d[off + 14], d[off + 15]]) as usize;
694 let start = off + 16;
695 let end = start + 2 * insns_size;
696 if end > d.len() {
697 return None;
698 }
699 Some(&d[start..end])
700 }
701
702 pub fn static_values(&self, off: u32) -> Vec<EncodedValue> {
704 if off == 0 {
705 return Vec::new();
706 }
707 annotations::read_encoded_array(self.raw(), off as usize).unwrap_or_default()
708 }
709
710 pub fn method_handle(&self, idx: u32) -> Option<&MethodHandleItem> {
712 self.method_handles.get(idx as usize)
713 }
714
715 pub fn method_handle_count(&self) -> usize {
716 self.method_handles.len()
717 }
718
719 pub fn call_site(&self, idx: u32) -> Option<&CallSiteInfo> {
721 self.call_sites.get(idx as usize)
722 }
723
724 pub fn call_site_count(&self) -> usize {
725 self.call_sites.len()
726 }
727
728 pub fn debug_locals(&self, debug_info_off: u32) -> Vec<DebugLocal> {
736 if debug_info_off == 0 {
737 return Vec::new();
738 }
739 let raw = self.raw();
740 let mut c = Cursor::at(raw, debug_info_off as usize);
741 if c.read_uleb128().is_none() {
742 return Vec::new();
743 }
744 let n = match c.read_uleb128() {
745 Some(v) => v as usize,
746 None => return Vec::new(),
747 };
748 for _ in 0..n {
750 if c.read_uleb128p1().is_none() {
751 return Vec::new();
752 }
753 }
754 let mut out: Vec<DebugLocal> = Vec::new();
755 type OpenLocal = (u16, u32, std::sync::Arc<str>, Option<std::sync::Arc<str>>);
760 let mut open: Vec<OpenLocal> = Vec::new();
761 let mut last: Vec<(u16, std::sync::Arc<str>, Option<std::sync::Arc<str>>)> = Vec::new();
762 let mut addr: u32 = 0;
763 for _ in 0..2_000_000u32 {
766 let Some(op) = c.u1() else { break };
767 match op {
768 0x00 => break, 0x01 => {
770 let Some(d) = c.read_uleb128() else { break };
772 addr = addr.saturating_add(d as u32);
773 }
774 0x02 => {
775 if c.read_sleb128().is_none() {
777 break;
778 }
779 }
780 0x03 | 0x04 => {
781 let (Some(reg), Some(ni)) = (c.read_uleb128(), c.read_uleb128p1()) else {
783 break;
784 };
785 let ti = match c.read_uleb128p1() {
786 Some(v) => v,
787 None => break,
788 };
789 if op == 0x04 && c.read_uleb128p1().is_none() {
790 break; }
792 if ni >= 0 {
796 let reg = reg as u16;
797 if let Some(pos) = open.iter().position(|(r, ..)| *r == reg) {
798 let (_, start, name, ty) = open.remove(pos);
799 out.push(DebugLocal { reg, start, end: addr, name, ty });
800 }
801 let name = self.string_arc(ni as u32);
802 let ty = if ti >= 0 {
803 Some(std::sync::Arc::from(self.type_name(ti as u32)))
804 } else {
805 None
806 };
807 if let Some(pos) = last.iter().position(|(r, ..)| *r == reg) {
808 last[pos] = (reg, name.clone(), ty.clone());
809 } else {
810 last.push((reg, name.clone(), ty.clone()));
811 }
812 open.push((reg, addr, name, ty));
813 }
814 }
815 0x05 => {
816 let Some(reg) = c.read_uleb128() else { break };
818 let reg = reg as u16;
819 if let Some(pos) = open.iter().position(|(r, ..)| *r == reg) {
820 let (_, start, name, ty) = open.remove(pos);
821 out.push(DebugLocal { reg, start, end: addr, name, ty });
822 }
823 }
824 0x06 => {
825 let Some(reg) = c.read_uleb128() else { break };
827 let reg = reg as u16;
828 if let Some((_, name, ty)) = last.iter().find(|(r, ..)| *r == reg) {
829 let name = name.clone();
830 let ty = ty.clone();
831 if !open.iter().any(|(r, ..)| *r == reg) {
832 open.push((reg, addr, name, ty));
833 }
834 }
835 }
836 0x07 | 0x08 => {} 0x09 => {
838 if c.read_uleb128p1().is_none() {
840 break;
841 }
842 }
843 c => {
851 let adjusted = (c - 0x0a) as u32;
852 addr = addr.saturating_add(adjusted / 15);
853 }
854 }
855 }
856 for (reg, start, name, ty) in open {
857 out.push(DebugLocal {
858 reg,
859 start,
860 end: u32::MAX,
861 name,
862 ty,
863 });
864 }
865 out
866 }
867
868 pub fn parameter_names(&self, debug_info_off: u32) -> Vec<Option<String>> {
869 if debug_info_off == 0 {
870 return Vec::new();
871 }
872 let mut c = Cursor::at(self.raw(), debug_info_off as usize);
873 let _line_start = match c.read_uleb128() {
874 Some(v) => v,
875 None => return Vec::new(),
876 };
877 let n = match c.read_uleb128() {
878 Some(v) => v as usize,
879 None => return Vec::new(),
880 };
881 let mut out = Vec::with_capacity(n);
882 for _ in 0..n {
883 let idx = c.read_uleb128p1().unwrap_or(-1);
884 if idx < 0 {
885 out.push(None);
886 } else {
887 out.push(Some(self.string(idx as u32).to_string()));
888 }
889 }
890 out
891 }
892}
893
894impl std::fmt::Debug for DexFile {
895 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
896 f.debug_struct("DexFile")
897 .field("version", &self.version)
898 .field("strings", &self.strings.len())
899 .field("types", &self.types.len())
900 .field("protos", &self.protos.len())
901 .field("fields", &self.fields.len())
902 .field("methods", &self.methods.len())
903 .field("class_defs", &self.class_defs.len())
904 .finish()
905 }
906}
907
908fn read_type_list_at(data: &[u8], off: usize) -> Box<[u32]> {
913 if off == 0 || off + 4 > data.len() {
914 return Vec::new().into_boxed_slice();
915 }
916 let size = u32::from_le_bytes(data[off..off + 4].try_into().unwrap()) as usize;
917 let mut p = off + 4;
918 let mut out = Vec::with_capacity(size);
919 for _ in 0..size {
920 if p + 2 > data.len() {
921 break;
922 }
923 out.push(u16::from_le_bytes(data[p..p + 2].try_into().unwrap()) as u32);
924 p += 2;
925 }
926 out.into_boxed_slice()
927}
928
929fn parse_map_tables(data: &[u8]) -> (Vec<MethodHandleItem>, Vec<CallSiteInfo>) {
935 let mut handles = Vec::new();
936 let mut sites = Vec::new();
937 if data.len() < 0x70 {
938 return (handles, sites);
939 }
940 let map_off = u32le(data, 0x34) as usize;
941 if map_off + 4 > data.len() {
942 return (handles, sites);
943 }
944 let n = u32le(data, map_off) as usize;
945 let mut p = map_off + 4;
946 for _ in 0..n {
947 if p + 12 > data.len() {
948 break;
949 }
950 let t = u16le(data, p);
951 let size = u32le(data, p + 4) as usize;
952 let off = u32le(data, p + 8) as usize;
953 p += 12;
954 match t {
955 0x0007 => {
956 for i in 0..size {
958 let q = off + 4 * i;
959 if q + 4 > data.len() {
960 break;
961 }
962 let cs_off = u32le(data, q) as usize;
963 if let Some(info) = parse_call_site(data, cs_off) {
964 sites.push(info);
965 }
966 }
967 }
968 0x0008 => {
969 for i in 0..size {
971 let q = off + 8 * i;
972 if q + 8 > data.len() {
973 break;
974 }
975 let kind = u16le(data, q);
976 let id = u16le(data, q + 4);
977 handles.push(MethodHandleItem {
978 kind,
979 target_id: id as u32,
980 is_field: kind <= 3,
981 });
982 }
983 }
984 _ => {}
985 }
986 }
987 (handles, sites)
988}
989
990fn parse_call_site(data: &[u8], off: usize) -> Option<CallSiteInfo> {
991 let vals = crate::annotations::read_encoded_array(data, off)?;
992 if vals.len() < 3 {
993 return None;
994 }
995 let bootstrap_handle = match &vals[0] {
996 crate::annotations::EncodedValue::MethodHandle(h) => *h,
997 _ => return None,
998 };
999 let name_idx = match &vals[1] {
1000 crate::annotations::EncodedValue::String(s) => *s,
1001 _ => return None,
1002 };
1003 let proto_idx = match &vals[2] {
1004 crate::annotations::EncodedValue::MethodType(p) => *p,
1005 _ => return None,
1006 };
1007 Some(CallSiteInfo {
1008 bootstrap_handle,
1009 name_idx,
1010 proto_idx,
1011 linker_args: vals[3..].to_vec(),
1012 })
1013}
1014
1015fn u16le(data: &[u8], off: usize) -> u16 {
1016 if off + 2 > data.len() {
1017 return 0;
1018 }
1019 u16::from_le_bytes([data[off], data[off + 1]])
1020}
1021
1022fn u32le(data: &[u8], off: usize) -> u32 {
1023 if off + 4 > data.len() {
1024 return 0;
1025 }
1026 u32::from_le_bytes([data[off], data[off + 1], data[off + 2], data[off + 3]])
1027}
1028
1029#[cfg(test)]
1030mod tests {
1031 use super::*;
1032
1033 #[test]
1034 fn rejects_non_dex() {
1035 assert!(DexFile::parse(vec![0u8; 128]).is_err());
1036 }
1037}
1038
1039
1040#[derive(Debug, Clone)]
1043pub struct DebugLocal {
1044 pub reg: u16,
1045 pub start: u32,
1046 pub end: u32,
1047 pub name: std::sync::Arc<str>,
1048 pub ty: Option<std::sync::Arc<str>>,
1053}