1use crate::error::Result;
2use crate::format::io::pod::{self, RawClassRoT64};
3use crate::model::addr::Va;
4use crate::objc::ivar::parse_ivar_list;
5use crate::objc::method::parse_method_list;
6use crate::objc::property::{parse_property_list, parse_property_list_with_kind};
7use crate::objc::protocol::parse_protocol_name_list;
8use crate::objc::resolve::ObjCResolver;
9use crate::objc::types::{ObjCClass, ObjCMethod, ObjCProperty, RO_META};
10
11pub fn parse_class(resolver: &ObjCResolver<'_>, class_va: Va) -> Result<ObjCClass> {
13 let class_offset = resolver.va_to_offset(class_va)?;
14 let data = resolver.macho().bytes();
15 let endian = resolver.endian();
16
17 let data_ptr_offset = class_offset.as_usize() as u64 + 32; let raw_data_ptr = match resolver.read_pointer_at_offset(data_ptr_offset)? {
20 Some(va) => va,
21 None => {
22 return Err(crate::error::Error::format("null class_ro_t pointer"));
23 }
24 };
25 let is_swift = raw_data_ptr.0 & 1 != 0;
26 let data_va = Va(raw_data_ptr.0 & crate::types::CLASS_DATA_POINTER_MASK);
27
28 let ro_offset = resolver.va_to_offset(data_va)?;
30 let ro: RawClassRoT64 = pod::read_pod(data, ro_offset.as_usize())?;
31 let flags = endian.interpret_u32(ro.flags);
32 let instance_size = endian.interpret_u32(ro.instance_size);
33 let is_meta = flags & RO_META != 0;
34
35 let name_ptr_offset = ro_offset.as_usize() as u64 + 24; let name = match resolver.read_pointer_at_offset(name_ptr_offset)? {
38 Some(va) => resolver.read_cstring(va).unwrap_or("<invalid>").to_string(),
39 None => "<null>".to_string(),
40 };
41
42 let superclass_offset = class_offset.as_usize() as u64 + 8;
44 let superclass_name = resolve_class_ref_name(resolver, superclass_offset);
45
46 let methods_ptr_offset = ro_offset.as_usize() as u64 + 32;
48 let instance_methods = match resolver.read_pointer_at_offset(methods_ptr_offset)? {
49 Some(va) if va.0 != 0 => parse_method_list(resolver, va).unwrap_or_default(),
50 _ => Vec::new(),
51 };
52
53 let protos_ptr_offset = ro_offset.as_usize() as u64 + 40;
55 let protocols = match resolver.read_pointer_at_offset(protos_ptr_offset)? {
56 Some(va) if va.0 != 0 => parse_protocol_name_list(resolver, va).unwrap_or_default(),
57 _ => Vec::new(),
58 };
59
60 let ivars_ptr_offset = ro_offset.as_usize() as u64 + 48;
62 let ivars = match resolver.read_pointer_at_offset(ivars_ptr_offset)? {
63 Some(va) if va.0 != 0 => parse_ivar_list(resolver, va).unwrap_or_default(),
64 _ => Vec::new(),
65 };
66
67 let props_ptr_offset = ro_offset.as_usize() as u64 + 64;
69 let properties = match resolver.read_pointer_at_offset(props_ptr_offset)? {
70 Some(va) if va.0 != 0 => parse_property_list(resolver, va).unwrap_or_default(),
71 _ => Vec::new(),
72 };
73
74 let meta_offset = class_offset.as_usize() as u64; let (class_methods, class_properties) = match resolver.read_pointer_at_offset(meta_offset)? {
77 Some(meta_va) => parse_metaclass_surface(resolver, meta_va).unwrap_or_default(),
78 None => (Vec::new(), Vec::new()),
79 };
80
81 let mut properties = properties;
82 properties.extend(class_properties);
83
84 Ok(ObjCClass {
85 name,
86 superclass_name,
87 instance_methods,
88 class_methods,
89 ivars,
90 properties,
91 protocols,
92 instance_size,
93 is_meta,
94 is_swift,
95 })
96}
97
98pub(crate) fn resolve_class_ref_name(
99 resolver: &ObjCResolver<'_>,
100 class_ref_offset: u64,
101) -> Option<String> {
102 if let Some(bind_name) = resolver.bind_name_at_offset(class_ref_offset) {
103 return Some(
104 bind_name
105 .strip_prefix("_OBJC_CLASS_$_")
106 .unwrap_or(bind_name)
107 .to_string(),
108 );
109 }
110
111 let class_va = resolver.read_pointer_at_offset(class_ref_offset).ok()??;
112 resolve_class_name_from_va(resolver, class_va)
113}
114
115pub(crate) fn resolve_class_name_from_va(
116 resolver: &ObjCResolver<'_>,
117 class_va: Va,
118) -> Option<String> {
119 let class_offset = resolver.va_to_offset(class_va).ok()?;
120
121 let data_ptr_offset = class_offset.as_usize() as u64 + 32;
123 let data_va = resolver.read_pointer_at_offset(data_ptr_offset).ok()??;
124 let data_va = Va(data_va.0 & crate::types::CLASS_DATA_POINTER_MASK);
125 let ro_offset = resolver.va_to_offset(data_va).ok()?;
126
127 let name_ptr_offset = ro_offset.as_usize() as u64 + 24;
129 let name_va = resolver.read_pointer_at_offset(name_ptr_offset).ok()??;
130 resolver.read_cstring(name_va).ok().map(|s| s.to_string())
131}
132
133fn parse_metaclass_surface(
134 resolver: &ObjCResolver<'_>,
135 meta_va: Va,
136) -> Result<(Vec<ObjCMethod>, Vec<ObjCProperty>)> {
137 let meta_offset = resolver.va_to_offset(meta_va)?;
138
139 let data_ptr_offset = meta_offset.as_usize() as u64 + 32;
140 let data_va = match resolver.read_pointer_at_offset(data_ptr_offset)? {
141 Some(va) => Va(va.0 & crate::types::CLASS_DATA_POINTER_MASK),
142 None => return Ok((Vec::new(), Vec::new())),
143 };
144
145 let ro_offset = resolver.va_to_offset(data_va)?;
146
147 let methods_ptr_offset = ro_offset.as_usize() as u64 + 32;
148 let methods = match resolver.read_pointer_at_offset(methods_ptr_offset)? {
149 Some(va) if va.0 != 0 => parse_method_list(resolver, va),
150 _ => Ok(Vec::new()),
151 }?;
152
153 let props_ptr_offset = ro_offset.as_usize() as u64 + 64;
154 let properties = match resolver.read_pointer_at_offset(props_ptr_offset)? {
155 Some(va) if va.0 != 0 => parse_property_list_with_kind(resolver, va, true),
156 _ => Ok(Vec::new()),
157 }?;
158
159 Ok((methods, properties))
160}