1use crate::error::Result;
2use crate::format::io::pod::{self, RawProtocolT64};
3use crate::model::addr::Va;
4use crate::objc::method::parse_method_list;
5use crate::objc::property::{parse_property_list, parse_property_list_with_kind};
6use crate::objc::resolve::ObjCResolver;
7use crate::objc::types::ObjCProtocol;
8
9pub fn parse_protocol(resolver: &ObjCResolver<'_>, proto_va: Va) -> Result<ObjCProtocol> {
11 let offset = resolver.va_to_offset(proto_va)?;
12 let data = resolver.macho().bytes();
13 let endian = resolver.endian();
14 let raw: RawProtocolT64 = pod::read_pod(data, offset.as_usize())?;
15 let protocol_size = endian.interpret_u32(raw.size) as usize;
16 let name_ptr_offset = offset.as_usize() as u64 + 8;
17 let name = match resolver.read_pointer_at_offset(name_ptr_offset)? {
18 Some(va) => resolver.read_cstring(va).unwrap_or("<invalid>").to_string(),
19 None => "<null>".to_string(),
20 };
21
22 let inst_methods_offset = offset.as_usize() as u64 + 24;
23 let instance_methods = match resolver.read_pointer_at_offset(inst_methods_offset)? {
24 Some(va) if va.0 != 0 => parse_method_list(resolver, va).unwrap_or_default(),
25 _ => Vec::new(),
26 };
27
28 let cls_methods_offset = offset.as_usize() as u64 + 32;
29 let class_methods = match resolver.read_pointer_at_offset(cls_methods_offset)? {
30 Some(va) if va.0 != 0 => parse_method_list(resolver, va).unwrap_or_default(),
31 _ => Vec::new(),
32 };
33
34 let opt_inst_offset = offset.as_usize() as u64 + 40;
35 let optional_instance_methods = match resolver.read_pointer_at_offset(opt_inst_offset)? {
36 Some(va) if va.0 != 0 => parse_method_list(resolver, va).unwrap_or_default(),
37 _ => Vec::new(),
38 };
39
40 let opt_cls_offset = offset.as_usize() as u64 + 48;
41 let optional_class_methods = match resolver.read_pointer_at_offset(opt_cls_offset)? {
42 Some(va) if va.0 != 0 => parse_method_list(resolver, va).unwrap_or_default(),
43 _ => Vec::new(),
44 };
45
46 let props_offset = offset.as_usize() as u64 + 56;
47 let properties = match resolver.read_pointer_at_offset(props_offset)? {
48 Some(va) if va.0 != 0 => parse_property_list(resolver, va).unwrap_or_default(),
49 _ => Vec::new(),
50 };
51
52 let class_properties = if protocol_size >= 96 {
53 let class_props_offset = offset.as_usize() as u64 + 88;
54 match resolver.read_pointer_at_offset(class_props_offset) {
55 Ok(Some(va)) if va.0 != 0 => {
56 parse_property_list_with_kind(resolver, va, true).unwrap_or_default()
57 }
58 _ => Vec::new(),
59 }
60 } else {
61 Vec::new()
62 };
63
64 let protos_offset = offset.as_usize() as u64 + 16;
65 let adopted_protocols = match resolver.read_pointer_at_offset(protos_offset)? {
66 Some(va) if va.0 != 0 => parse_protocol_name_list(resolver, va).unwrap_or_default(),
67 _ => Vec::new(),
68 };
69
70 let mut properties = properties;
71 properties.extend(class_properties);
72
73 Ok(ObjCProtocol {
74 name,
75 instance_methods,
76 class_methods,
77 optional_instance_methods,
78 optional_class_methods,
79 properties,
80 adopted_protocols,
81 })
82}
83
84pub fn parse_protocol_name_list(resolver: &ObjCResolver<'_>, va: Va) -> Result<Vec<String>> {
86 let offset = resolver.va_to_offset(va)?;
87 let data = resolver.macho().bytes();
88 let endian = resolver.endian();
89
90 let count = endian.interpret_u64(pod::read_pod::<u64>(data, offset.as_usize())?) as usize;
92 if count > 10_000 {
93 return Ok(Vec::new());
94 }
95
96 let mut names = Vec::with_capacity(count);
97 for i in 0..count {
98 let ptr_offset = offset.as_usize() as u64 + 8 + i as u64 * 8;
99 if let Ok(Some(proto_va)) = resolver.read_pointer_at_offset(ptr_offset) {
100 if proto_va.0 != 0 {
101 let name_ptr_offset = match resolver.va_to_offset(proto_va) {
103 Ok(off) => off.as_usize() as u64 + 8,
104 Err(_) => continue,
105 };
106 if let Ok(Some(name_va)) = resolver.read_pointer_at_offset(name_ptr_offset) {
107 if let Ok(name) = resolver.read_cstring(name_va) {
108 names.push(name.to_string());
109 }
110 }
111 }
112 }
113 }
114
115 Ok(names)
116}