1use crate::container::{self, Container};
6use crate::error;
7use crate::mach::load_command;
8use core::fmt::{self, Debug};
9use scroll::ctx;
10use scroll::ctx::SizeWith;
11use scroll::{IOread, IOwrite, Pread, Pwrite, SizeWith};
12
13pub const N_STAB: u8 = 0xe0;
16pub const N_PEXT: u8 = 0x10;
18pub const N_TYPE: u8 = 0x0e;
20pub const N_EXT: u8 = 0x01;
22
23pub const NO_SECT: u8 = 0;
35pub const MAX_SECT: u8 = 255;
37
38pub const N_UNDF: u8 = 0x0;
40pub const N_ABS: u8 = 0x2;
42pub const N_SECT: u8 = 0xe;
44pub const N_PBUD: u8 = 0xc;
46pub const N_INDR: u8 = 0xa;
48
49pub const N_GSYM: u8 = 0x20;
51pub const N_FNAME: u8 = 0x22;
52pub const N_FUN: u8 = 0x24;
53pub const N_STSYM: u8 = 0x26;
54pub const N_LCSYM: u8 = 0x28;
55pub const N_BNSYM: u8 = 0x2e;
56pub const N_PC: u8 = 0x30;
57pub const N_AST: u8 = 0x32;
58pub const N_OPT: u8 = 0x3c;
59pub const N_RSYM: u8 = 0x40;
60pub const N_SLINE: u8 = 0x44;
61pub const N_ENSYM: u8 = 0x4e;
62pub const N_SSYM: u8 = 0x60;
63pub const N_SO: u8 = 0x64;
64pub const N_OSO: u8 = 0x66;
65pub const N_LSYM: u8 = 0x80;
66pub const N_BINCL: u8 = 0x82;
67pub const N_SOL: u8 = 0x84;
68pub const N_PARAMS: u8 = 0x86;
69pub const N_VERSION: u8 = 0x88;
70pub const N_OLEVEL: u8 = 0x8a;
71pub const N_PSYM: u8 = 0xa0;
72pub const N_EINCL: u8 = 0xa2;
73pub const N_ENTRY: u8 = 0xa4;
74pub const N_LBRAC: u8 = 0xc0;
75pub const N_EXCL: u8 = 0xc2;
76pub const N_RBRAC: u8 = 0xe0;
77pub const N_BCOMM: u8 = 0xe2;
78pub const N_ECOMM: u8 = 0xe4;
79pub const N_ECOML: u8 = 0xe8;
80pub const N_LENG: u8 = 0xfe;
81
82pub const NLIST_TYPE_MASK: u8 = 0xe;
83pub const NLIST_TYPE_GLOBAL: u8 = 0x1;
84pub const NLIST_TYPE_LOCAL: u8 = 0x0;
85
86pub const REFERENCE_TYPE: u16 = 0xf;
88pub const REFERENCE_FLAG_UNDEFINED_NON_LAZY: u16 = 0x0;
90pub const REFERENCE_FLAG_UNDEFINED_LAZY: u16 = 0x1;
92pub const REFERENCE_FLAG_DEFINED: u16 = 0x2;
94pub const REFERENCE_FLAG_PRIVATE_DEFINED: u16 = 0x3;
97pub const REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY: u16 = 0x4;
100pub const REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY: u16 = 0x5;
103
104pub const REFERENCED_DYNAMICALLY: u16 = 0x10;
111pub const N_DESC_DISCARDED: u16 = 0x20;
114pub const N_NO_DEAD_STRIP: u16 = 0x20;
118pub const N_WEAK_REF: u16 = 0x40;
122pub const N_WEAK_DEF: u16 = 0x80;
127
128pub fn n_type_to_str(n_type: u8) -> &'static str {
129 match n_type {
130 N_UNDF => "N_UNDF",
131 N_ABS => "N_ABS",
132 N_SECT => "N_SECT",
133 N_PBUD => "N_PBUD",
134 N_INDR => "N_INDR",
135 _ => "UNKNOWN_N_TYPE",
136 }
137}
138
139#[repr(C)]
140#[derive(Clone, Copy, Pread, Pwrite, SizeWith, IOread, IOwrite)]
141pub struct Nlist32 {
142 pub n_strx: u32,
144 pub n_type: u8,
146 pub n_sect: u8,
148 pub n_desc: u16,
150 pub n_value: u32,
152}
153
154pub const SIZEOF_NLIST_32: usize = 12;
155
156impl Debug for Nlist32 {
157 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
158 fmt.debug_struct("Nlist32")
159 .field("n_strx", &format_args!("{:04}", self.n_strx))
160 .field("n_type", &format_args!("{:#02x}", self.n_type))
161 .field("n_sect", &format_args!("{:#x}", self.n_sect))
162 .field("n_desc", &format_args!("{:#03x}", self.n_desc))
163 .field("n_value", &format_args!("{:#x}", self.n_value))
164 .finish()
165 }
166}
167
168#[repr(C)]
169#[derive(Clone, Copy, Pread, Pwrite, SizeWith, IOread, IOwrite)]
170pub struct Nlist64 {
171 pub n_strx: u32,
173 pub n_type: u8,
175 pub n_sect: u8,
177 pub n_desc: u16,
179 pub n_value: u64,
181}
182
183pub const SIZEOF_NLIST_64: usize = 16;
184
185impl Debug for Nlist64 {
186 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
187 fmt.debug_struct("Nlist64")
188 .field("n_strx", &format_args!("{:04}", self.n_strx))
189 .field("n_type", &format_args!("{:#02x}", self.n_type))
190 .field("n_sect", &format_args!("{:#x}", self.n_sect))
191 .field("n_desc", &format_args!("{:#03x}", self.n_desc))
192 .field("n_value", &format_args!("{:#x}", self.n_value))
193 .finish()
194 }
195}
196
197#[derive(Debug, Clone)]
198pub struct Nlist {
199 pub n_strx: usize,
201 pub n_type: u8,
203 pub n_sect: usize,
205 pub n_desc: u16,
207 pub n_value: u64,
209}
210
211impl Nlist {
212 pub fn get_type(&self) -> u8 {
214 self.n_type & N_TYPE
215 }
216 pub fn type_str(&self) -> &'static str {
218 n_type_to_str(self.get_type())
219 }
220 pub fn is_global(&self) -> bool {
222 self.n_type & N_EXT != 0
223 }
224 pub fn is_weak(&self) -> bool {
226 self.n_desc & (N_WEAK_REF | N_WEAK_DEF) != 0
227 }
228 pub fn is_undefined(&self) -> bool {
230 self.n_sect == 0 && self.n_type & N_TYPE == N_UNDF
231 }
232 pub fn is_stab(&self) -> bool {
234 self.n_type & N_STAB != 0
235 }
236}
237
238impl ctx::SizeWith<container::Ctx> for Nlist {
239 fn size_with(ctx: &container::Ctx) -> usize {
240 match ctx.container {
241 Container::Little => SIZEOF_NLIST_32,
242 Container::Big => SIZEOF_NLIST_64,
243 }
244 }
245}
246
247impl From<Nlist32> for Nlist {
248 fn from(nlist: Nlist32) -> Self {
249 Nlist {
250 n_strx: nlist.n_strx as usize,
251 n_type: nlist.n_type,
252 n_sect: nlist.n_sect as usize,
253 n_desc: nlist.n_desc,
254 n_value: u64::from(nlist.n_value),
255 }
256 }
257}
258
259impl From<Nlist64> for Nlist {
260 fn from(nlist: Nlist64) -> Self {
261 Nlist {
262 n_strx: nlist.n_strx as usize,
263 n_type: nlist.n_type,
264 n_sect: nlist.n_sect as usize,
265 n_desc: nlist.n_desc,
266 n_value: nlist.n_value,
267 }
268 }
269}
270
271impl From<Nlist> for Nlist32 {
272 fn from(nlist: Nlist) -> Self {
273 Nlist32 {
274 n_strx: nlist.n_strx as u32,
275 n_type: nlist.n_type,
276 n_sect: nlist.n_sect as u8,
277 n_desc: nlist.n_desc,
278 n_value: nlist.n_value as u32,
279 }
280 }
281}
282
283impl From<Nlist> for Nlist64 {
284 fn from(nlist: Nlist) -> Self {
285 Nlist64 {
286 n_strx: nlist.n_strx as u32,
287 n_type: nlist.n_type,
288 n_sect: nlist.n_sect as u8,
289 n_desc: nlist.n_desc,
290 n_value: nlist.n_value,
291 }
292 }
293}
294
295impl<'a> ctx::TryFromCtx<'a, container::Ctx> for Nlist {
296 type Error = crate::error::Error;
297 fn try_from_ctx(
298 bytes: &'a [u8],
299 container::Ctx { container, le }: container::Ctx,
300 ) -> crate::error::Result<(Self, usize)> {
301 let nlist = match container {
302 Container::Little => (bytes.pread_with::<Nlist32>(0, le)?.into(), SIZEOF_NLIST_32),
303 Container::Big => (bytes.pread_with::<Nlist64>(0, le)?.into(), SIZEOF_NLIST_64),
304 };
305 Ok(nlist)
306 }
307}
308
309impl ctx::TryIntoCtx<container::Ctx> for Nlist {
310 type Error = crate::error::Error;
311 fn try_into_ctx(
312 self,
313 bytes: &mut [u8],
314 container::Ctx { container, le }: container::Ctx,
315 ) -> Result<usize, Self::Error> {
316 let size = match container {
317 Container::Little => bytes.pwrite_with::<Nlist32>(self.into(), 0, le)?,
318 Container::Big => bytes.pwrite_with::<Nlist64>(self.into(), 0, le)?,
319 };
320 Ok(size)
321 }
322}
323
324impl ctx::IntoCtx<container::Ctx> for Nlist {
325 fn into_ctx(self, bytes: &mut [u8], ctx: container::Ctx) {
326 bytes.pwrite_with(self, 0, ctx).unwrap();
327 }
328}
329
330#[derive(Debug, Clone, Copy, Default)]
331pub struct SymbolsCtx {
332 pub nsyms: usize,
333 pub strtab: usize,
334 pub ctx: container::Ctx,
335}
336
337impl<'a, T: ?Sized> ctx::TryFromCtx<'a, SymbolsCtx, T> for Symbols<'a>
338where
339 T: AsRef<[u8]>,
340{
341 type Error = crate::error::Error;
342 fn try_from_ctx(
343 bytes: &'a T,
344 SymbolsCtx { nsyms, strtab, ctx }: SymbolsCtx,
345 ) -> crate::error::Result<(Self, usize)> {
346 let data = bytes.as_ref();
347 Ok((
348 Symbols {
349 data,
350 start: 0,
351 nsyms,
352 strtab,
353 ctx,
354 },
355 data.len(),
356 ))
357 }
358}
359
360#[derive(Default)]
361pub struct SymbolIterator<'a> {
362 data: &'a [u8],
363 nsyms: usize,
364 offset: usize,
365 count: usize,
366 ctx: container::Ctx,
367 strtab: usize,
368}
369
370impl<'a> Iterator for SymbolIterator<'a> {
371 type Item = error::Result<(&'a str, Nlist)>;
372 fn next(&mut self) -> Option<Self::Item> {
373 if self.count >= self.nsyms {
374 None
375 } else {
376 self.count += 1;
377 match self.data.gread_with::<Nlist>(&mut self.offset, self.ctx) {
378 Ok(symbol) => match self.data.pread(self.strtab + symbol.n_strx) {
379 Ok(name) => Some(Ok((name, symbol))),
380 Err(e) => Some(Err(e.into())),
381 },
382 Err(e) => Some(Err(e)),
383 }
384 }
385 }
386}
387
388pub struct Symbols<'a> {
390 data: &'a [u8],
391 start: usize,
392 nsyms: usize,
393 strtab: usize,
395 ctx: container::Ctx,
396}
397
398impl<'a, 'b> IntoIterator for &'b Symbols<'a> {
399 type Item = <SymbolIterator<'a> as Iterator>::Item;
400 type IntoIter = SymbolIterator<'a>;
401 fn into_iter(self) -> Self::IntoIter {
402 self.iter()
403 }
404}
405
406impl<'a> Symbols<'a> {
407 pub fn new(
411 bytes: &'a [u8],
412 start: usize,
413 count: usize,
414 strtab: usize,
415 ) -> error::Result<Symbols<'a>> {
416 let nsyms = count;
417 Ok(Symbols {
418 data: bytes,
419 start,
420 nsyms,
421 strtab,
422 ctx: container::Ctx::default(),
423 })
424 }
425 pub fn parse(
426 bytes: &'a [u8],
427 symtab: &load_command::SymtabCommand,
428 ctx: container::Ctx,
429 ) -> error::Result<Symbols<'a>> {
430 let strtab = symtab
432 .stroff
433 .checked_sub(symtab.symoff)
434 .ok_or_else(|| error::Error::Malformed("invalid symbol table offset".into()))?;
435 bytes.pread_with(
436 symtab.symoff as usize,
437 SymbolsCtx {
438 nsyms: symtab.nsyms as usize,
439 strtab: strtab as usize,
440 ctx,
441 },
442 )
443 }
444
445 pub fn iter(&self) -> SymbolIterator<'a> {
446 SymbolIterator {
447 offset: self.start as usize,
448 nsyms: self.nsyms as usize,
449 count: 0,
450 data: self.data,
451 ctx: self.ctx,
452 strtab: self.strtab,
453 }
454 }
455
456 pub fn get(&self, index: usize) -> crate::error::Result<(&'a str, Nlist)> {
458 let sym: Nlist = self
459 .data
460 .pread_with(self.start + (index * Nlist::size_with(&self.ctx)), self.ctx)?;
461 let name = self.data.pread(self.strtab + sym.n_strx)?;
462 Ok((name, sym))
463 }
464}
465
466impl<'a> Debug for Symbols<'a> {
467 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
468 fmt.debug_struct("Symbols")
469 .field("data", &self.data.len())
470 .field("start", &format_args!("{:#?}", self.start))
471 .field("nsyms", &self.nsyms)
472 .field("strtab", &format_args!("{:#x}", self.strtab))
473 .finish()?;
474
475 writeln!(fmt, "Symbol List {{")?;
476 for (i, res) in self.iter().enumerate() {
477 match res {
478 Ok((name, nlist)) => writeln!(
479 fmt,
480 "{: >10x} {} sect: {:#x} type: {:#02x} desc: {:#03x}",
481 nlist.n_value, name, nlist.n_sect, nlist.n_type, nlist.n_desc
482 )?,
483 Err(error) => writeln!(fmt, " Bad symbol, index: {}, sym: {:?}", i, error)?,
484 }
485 }
486 writeln!(fmt, "}}")
487 }
488}