macho_core/model/
symbol.rs1use crate::error::{Error, Result};
2use crate::format::constants::*;
3use std::fmt;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum SymbolType {
8 Undefined,
10 Absolute,
12 Section,
14 PreboundUndefined,
16 Indirect,
18 Stab(u8),
23 Unknown(u8),
26}
27
28impl SymbolType {
29 pub fn from_n_type(n_type: u8) -> Self {
31 if n_type & N_STAB != 0 {
32 return Self::Stab(n_type);
33 }
34 match n_type & N_TYPE {
35 N_UNDF => Self::Undefined,
36 N_ABS => Self::Absolute,
37 N_SECT => Self::Section,
38 N_PBUD => Self::PreboundUndefined,
39 N_INDR => Self::Indirect,
40 other => Self::Unknown(other),
41 }
42 }
43
44 pub fn name(&self) -> &'static str {
46 match self {
47 Self::Undefined => "undef",
48 Self::Absolute => "abs",
49 Self::Section => "sect",
50 Self::PreboundUndefined => "pbud",
51 Self::Indirect => "indr",
52 Self::Stab(_) => "stab",
53 Self::Unknown(_) => "unk",
54 }
55 }
56
57 pub fn is_stab(&self) -> bool {
59 matches!(self, Self::Stab(_))
60 }
61}
62
63impl fmt::Display for SymbolType {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 write!(f, "{}", self.name())
66 }
67}
68
69#[derive(Debug, Clone)]
70pub struct Symbol<'data> {
72 pub name: &'data str,
74 pub sym_type: SymbolType,
76 pub external: bool,
78 pub private_external: bool,
80 pub section_index: u8,
82 pub desc: u16,
84 pub value: u64,
86 pub index: usize,
88}
89
90impl Symbol<'_> {
91 pub fn is_stab(&self) -> bool {
93 self.sym_type.is_stab()
94 }
95
96 pub fn is_defined(&self) -> bool {
98 matches!(self.sym_type, SymbolType::Section | SymbolType::Absolute)
99 }
100
101 pub fn is_undefined(&self) -> bool {
103 matches!(self.sym_type, SymbolType::Undefined)
104 }
105
106 pub fn is_weak_def(&self) -> bool {
108 self.desc & N_WEAK_DEF != 0
109 }
110
111 pub fn is_weak_ref(&self) -> bool {
113 self.desc & N_WEAK_REF != 0
114 }
115
116 pub fn is_no_dead_strip(&self) -> bool {
118 self.desc & N_NO_DEAD_STRIP != 0
119 }
120
121 pub fn is_alt_entry(&self) -> bool {
123 self.desc & N_ALT_ENTRY != 0
124 }
125
126 pub fn library_ordinal(&self) -> u8 {
128 ((self.desc >> 8) & 0xFF) as u8
129 }
130}
131
132impl fmt::Display for Symbol<'_> {
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 write!(f, "{:#018x} {} {}", self.value, self.sym_type, self.name)
135 }
136}
137
138#[derive(Clone)]
140pub struct StringTable<'data> {
141 data: &'data [u8],
142}
143
144impl<'data> StringTable<'data> {
145 pub fn new(data: &'data [u8]) -> Self {
147 Self { data }
148 }
149
150 pub fn get(&self, index: u32) -> Result<&'data str> {
152 let start = index as usize;
153 if start >= self.data.len() {
154 return Err(Error::bounds(start as u64, 1, self.data.len() as u64));
155 }
156 let slice = &self.data[start..];
157 let end = slice.iter().position(|&b| b == 0).unwrap_or(slice.len());
158 std::str::from_utf8(&slice[..end]).map_err(|e| {
159 Error::format(format!(
160 "invalid UTF-8 in string table at index {index}: {e}"
161 ))
162 })
163 }
164
165 pub fn bytes(&self) -> &'data [u8] {
167 self.data
168 }
169
170 pub fn len(&self) -> usize {
172 self.data.len()
173 }
174
175 pub fn is_empty(&self) -> bool {
177 self.data.is_empty()
178 }
179}
180
181impl fmt::Debug for StringTable<'_> {
182 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183 f.debug_struct("StringTable")
184 .field("len", &self.data.len())
185 .finish()
186 }
187}
188
189pub struct SymbolTable<'data> {
191 symbols: Vec<Symbol<'data>>,
192 string_table: StringTable<'data>,
193}
194
195impl<'data> SymbolTable<'data> {
196 pub(crate) fn new(symbols: Vec<Symbol<'data>>, string_table: StringTable<'data>) -> Self {
197 Self {
198 symbols,
199 string_table,
200 }
201 }
202
203 pub fn symbols(&self) -> &[Symbol<'data>] {
205 &self.symbols
206 }
207
208 pub fn len(&self) -> usize {
210 self.symbols.len()
211 }
212
213 pub fn is_empty(&self) -> bool {
215 self.symbols.is_empty()
216 }
217
218 pub fn get(&self, index: usize) -> Option<&Symbol<'data>> {
220 self.symbols.get(index)
221 }
222
223 pub fn string_table(&self) -> &StringTable<'data> {
225 &self.string_table
226 }
227
228 pub fn find_by_name(&self, name: &str) -> Option<&Symbol<'data>> {
230 self.symbols.iter().find(|s| s.name == name)
231 }
232
233 pub fn defined(&self) -> impl Iterator<Item = &Symbol<'data>> {
235 self.symbols.iter().filter(|s| s.is_defined())
236 }
237
238 pub fn undefined(&self) -> impl Iterator<Item = &Symbol<'data>> {
240 self.symbols.iter().filter(|s| s.is_undefined())
241 }
242
243 pub fn stabs(&self) -> impl Iterator<Item = &Symbol<'data>> {
245 self.symbols.iter().filter(|s| s.is_stab())
246 }
247
248 pub fn external(&self) -> impl Iterator<Item = &Symbol<'data>> {
250 self.symbols.iter().filter(|s| s.external)
251 }
252}
253
254impl fmt::Debug for SymbolTable<'_> {
255 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
256 f.debug_struct("SymbolTable")
257 .field("num_symbols", &self.symbols.len())
258 .field("string_table", &self.string_table)
259 .finish()
260 }
261}
262
263#[cfg(test)]
264mod tests {
265 use super::*;
266
267 #[test]
268 fn string_table_get() {
269 let data = b"\0hello\0world\0";
270 let st = StringTable::new(data);
271 assert_eq!(st.get(0).unwrap(), "");
272 assert_eq!(st.get(1).unwrap(), "hello");
273 assert_eq!(st.get(7).unwrap(), "world");
274 }
275
276 #[test]
277 fn string_table_out_of_bounds() {
278 let data = b"\0hello\0";
279 let st = StringTable::new(data);
280 assert!(st.get(100).is_err());
281 }
282
283 #[test]
284 fn symbol_type_classification() {
285 assert_eq!(SymbolType::from_n_type(0x0f), SymbolType::Section);
286 assert_eq!(SymbolType::from_n_type(0x01), SymbolType::Undefined);
287 assert_eq!(SymbolType::from_n_type(0x03), SymbolType::Absolute);
288 }
289
290 #[test]
291 fn stab_type_classification() {
292 let st = SymbolType::from_n_type(0x24);
294 assert!(st.is_stab());
295 assert_eq!(st, SymbolType::Stab(0x24));
296 assert_eq!(st.name(), "stab");
297 }
298
299 #[test]
300 fn symbol_helpers() {
301 let sym = Symbol {
302 name: "_test",
303 sym_type: SymbolType::Section,
304 external: true,
305 private_external: false,
306 section_index: 1,
307 desc: N_WEAK_DEF,
308 value: 0x1000,
309 index: 0,
310 };
311 assert!(sym.is_defined());
312 assert!(!sym.is_undefined());
313 assert!(!sym.is_stab());
314 assert!(sym.is_weak_def());
315 assert!(!sym.is_weak_ref());
316 assert_eq!(sym.library_ordinal(), 0);
317 }
318}