1 //! "Nlist" style symbols in this binary - beware, like most symbol tables in most binary formats, they are strippable, and should not be relied upon, see the imports and exports modules for something more permanent.
2 //!
3 //! Symbols are essentially a type, offset, and the symbol name
4 
5 use scroll::ctx;
6 use scroll::ctx::SizeWith;
7 use scroll::{Pread, Pwrite, SizeWith, IOread, IOwrite};
8 use crate::error;
9 use crate::container::{self, Container};
10 use crate::mach::load_command;
11 use core::fmt::{self, Debug};
12 
13 // The n_type field really contains four fields which are used via the following masks.
14 /// if any of these bits set, a symbolic debugging entry
15 pub const N_STAB: u8 = 0xe0;
16 /// private external symbol bit
17 pub const N_PEXT: u8 = 0x10;
18 /// mask for the type bits
19 pub const N_TYPE: u8 = 0x0e;
20 /// external symbol bit, set for external symbols
21 pub const N_EXT: u8 = 0x01;
22 
23 // If the type is N_SECT then the n_sect field contains an ordinal of the
24 // section the symbol is defined in.  The sections are numbered from 1 and
25 // refer to sections in order they appear in the load commands for the file
26 // they are in.  This means the same ordinal may very well refer to different
27 // sections in different files.
28 
29 // The n_value field for all symbol table entries (including N_STAB's) gets
30 // updated by the link editor based on the value of it's n_sect field and where
31 // the section n_sect references gets relocated.  If the value of the n_sect
32 // field is NO_SECT then it's n_value field is not changed by the link editor.
33 /// symbol is not in any section
34 pub const NO_SECT: u8 = 0;
35 /// 1 thru 255 inclusive
36 pub const MAX_SECT: u8 = 255;
37 
38 /// undefined, n_sect == NO_SECT
39 pub const N_UNDF: u8 = 0x0;
40 /// absolute, n_sect == NO_SECT
41 pub const N_ABS:  u8 = 0x2;
42 /// defined in section number n_sect
43 pub const N_SECT: u8 = 0xe;
44 /// prebound undefined (defined in a dylib)
45 pub const N_PBUD: u8 = 0xc;
46 /// indirect
47 pub const N_INDR: u8 = 0xa;
48 
49 // n_types when N_STAB
50 pub const N_GSYM:    u8 = 0x20;
51 pub const N_FNAME:   u8 = 0x22;
52 pub const N_FUN:     u8 = 0x24;
53 pub const N_STSYM:   u8 = 0x26;
54 pub const N_LCSYM:   u8 = 0x28;
55 pub const N_BNSYM:   u8 = 0x2e;
56 pub const N_PC:      u8 = 0x30;
57 pub const N_AST:     u8 = 0x32;
58 pub const N_OPT:     u8 = 0x3c;
59 pub const N_RSYM:    u8 = 0x40;
60 pub const N_SLINE:   u8 = 0x44;
61 pub const N_ENSYM:   u8 = 0x4e;
62 pub const N_SSYM:    u8 = 0x60;
63 pub const N_SO:      u8 = 0x64;
64 pub const N_OSO:     u8 = 0x66;
65 pub const N_LSYM:    u8 = 0x80;
66 pub const N_BINCL:   u8 = 0x82;
67 pub const N_SOL:     u8 = 0x84;
68 pub const N_PARAMS:  u8 = 0x86;
69 pub const N_VERSION: u8 = 0x88;
70 pub const N_OLEVEL:  u8 = 0x8a;
71 pub const N_PSYM:    u8 = 0xa0;
72 pub const N_EINCL:   u8 = 0xa2;
73 pub const N_ENTRY:   u8 = 0xa4;
74 pub const N_LBRAC:   u8 = 0xc0;
75 pub const N_EXCL:    u8 = 0xc2;
76 pub const N_RBRAC:   u8 = 0xe0;
77 pub const N_BCOMM:   u8 = 0xe2;
78 pub const N_ECOMM:   u8 = 0xe4;
79 pub const N_ECOML:   u8 = 0xe8;
80 pub const N_LENG:    u8 = 0xfe;
81 
82 pub const NLIST_TYPE_MASK: u8 = 0xe;
83 pub const NLIST_TYPE_GLOBAL: u8 = 0x1;
84 pub const NLIST_TYPE_LOCAL: u8 = 0x0;
85 
86 /// Mask for reference flags of `n_desc` field.
87 pub const REFERENCE_TYPE: u16 = 0xf;
88 /// This symbol is a reference to an external non-lazy (data) symbol.
89 pub const REFERENCE_FLAG_UNDEFINED_NON_LAZY: u16 = 0x0;
90 /// This symbol is a reference to an external lazy symbol—that is, to a function call.
91 pub const REFERENCE_FLAG_UNDEFINED_LAZY: u16 = 0x1;
92 /// This symbol is defined in this module.
93 pub const REFERENCE_FLAG_DEFINED: u16 = 0x2;
94 /// This symbol is defined in this module and is visible only to modules within this
95 /// shared library.
96 pub const REFERENCE_FLAG_PRIVATE_DEFINED: u16 = 0x3;
97 /// This symbol is defined in another module in this file, is a non-lazy (data) symbol,
98 /// and is visible only to modules within this shared library.
99 pub const REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY: u16 = 0x4;
100 /// This symbol is defined in another module in this file, is a lazy (function) symbol,
101 /// and is visible only to modules within this shared library.
102 pub const REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY: u16 = 0x5;
103 
104 // Additional flags of n_desc field.
105 
106 /// Must be set for any defined symbol that is referenced by dynamic-loader APIs
107 /// (such as dlsym and NSLookupSymbolInImage) and not ordinary undefined symbol
108 /// references. The `strip` tool uses this bit to avoid removing symbols that must
109 /// exist: If the symbol has this bit set, `strip` does not strip it.
110 pub const REFERENCED_DYNAMICALLY: u16 = 0x10;
111 /// Sometimes used by the dynamic linker at runtime in a fully linked image. Do not
112 /// set this bit in a fully linked image.
113 pub const N_DESC_DISCARDED: u16 = 0x20;
114 /// When set in a relocatable object file (file type MH_OBJECT) on a defined symbol,
115 /// indicates to the static linker to never dead-strip the symbol.
116 // (Note that the same bit (0x20) is used for two nonoverlapping purposes.)
117 pub const N_NO_DEAD_STRIP: u16 = 0x20;
118 /// Indicates that this undefined symbol is a weak reference. If the dynamic linker
119 /// cannot find a definition for this symbol, it sets the address of this symbol to 0.
120 /// The static linker sets this symbol given the appropriate weak-linking flags.
121 pub const N_WEAK_REF: u16 = 0x40;
122 /// Indicates that this symbol is a weak definition. If the static linker or the
123 /// dynamic linker finds another (non-weak) definition for this symbol, the weak
124 /// definition is ignored. Only symbols in a coalesced section can be marked as a
125 /// weak definition.
126 pub const N_WEAK_DEF: u16 = 0x80;
127 
n_type_to_str(n_type: u8) -> &'static str128 pub 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)]
141 pub struct Nlist32 {
142     /// index into the string table
143     pub n_strx: u32,
144     /// type flag, see below
145     pub n_type: u8,
146     /// section number or NO_SECT
147     pub n_sect: u8,
148     /// see <mach-o/stab.h>
149     pub n_desc: u16,
150     /// value of this symbol (or stab offset)
151     pub n_value: u32,
152 }
153 
154 pub const SIZEOF_NLIST_32: usize = 12;
155 
156 impl Debug for Nlist32 {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result157     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)]
170 pub struct Nlist64 {
171     /// index into the string table
172     pub n_strx: u32,
173     /// type flag, see below
174     pub n_type: u8,
175     /// section number or NO_SECT
176     pub n_sect: u8,
177     /// see <mach-o/stab.h>
178     pub n_desc: u16,
179     /// value of this symbol (or stab offset)
180     pub n_value: u64,
181 }
182 
183 pub const SIZEOF_NLIST_64: usize = 16;
184 
185 impl Debug for Nlist64 {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result186     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,)]
198 pub struct Nlist {
199     /// index into the string table
200     pub n_strx: usize,
201     /// type flag, see below
202     pub n_type: u8,
203     /// section number or NO_SECT
204     pub n_sect: usize,
205     /// see <mach-o/stab.h>
206     pub n_desc: u16,
207     /// value of this symbol (or stab offset)
208     pub n_value: u64,
209 }
210 
211 impl Nlist {
212     /// Gets this symbol's type in bits 0xe
get_type(&self) -> u8213     pub fn get_type(&self) -> u8 {
214         self.n_type & N_TYPE
215     }
216     /// Gets the str representation of the type of this symbol
type_str(&self) -> &'static str217     pub fn type_str(&self) -> &'static str {
218         n_type_to_str(self.get_type())
219     }
220     /// Whether this symbol is global or not
is_global(&self) -> bool221     pub fn is_global(&self) -> bool {
222         self.n_type & N_EXT != 0
223     }
224     /// Whether this symbol is weak or not
is_weak(&self) -> bool225     pub fn is_weak(&self) -> bool {
226         self.n_desc & (N_WEAK_REF | N_WEAK_DEF) != 0
227     }
228     /// Whether this symbol is undefined or not
is_undefined(&self) -> bool229     pub fn is_undefined(&self) -> bool {
230         self.n_sect == 0 && self.n_type & N_TYPE == N_UNDF
231     }
232     /// Whether this symbol is a symbolic debugging entry
is_stab(&self) -> bool233     pub fn is_stab(&self) -> bool {
234         self.n_type & N_STAB != 0
235     }
236 }
237 
238 impl ctx::SizeWith<container::Ctx> for Nlist {
239     type Units = usize;
size_with(ctx: &container::Ctx) -> usize240     fn size_with(ctx: &container::Ctx) -> usize {
241         match ctx.container {
242             Container::Little => {
243                 SIZEOF_NLIST_32
244             },
245             Container::Big => {
246                 SIZEOF_NLIST_64
247             },
248         }
249     }
250 }
251 
252 impl From<Nlist32> for Nlist {
from(nlist: Nlist32) -> Self253     fn from(nlist: Nlist32) -> Self {
254         Nlist {
255             n_strx: nlist.n_strx as usize,
256             n_type: nlist.n_type,
257             n_sect: nlist.n_sect as usize,
258             n_desc: nlist.n_desc,
259             n_value: u64::from(nlist.n_value),
260         }
261     }
262 }
263 
264 impl From<Nlist64> for Nlist {
from(nlist: Nlist64) -> Self265     fn from(nlist: Nlist64) -> Self {
266         Nlist {
267             n_strx: nlist.n_strx as usize,
268             n_type: nlist.n_type,
269             n_sect: nlist.n_sect as usize,
270             n_desc: nlist.n_desc,
271             n_value: nlist.n_value,
272         }
273     }
274 }
275 
276 impl From<Nlist> for Nlist32 {
from(nlist: Nlist) -> Self277     fn from(nlist: Nlist) -> Self {
278         Nlist32 {
279             n_strx: nlist.n_strx as u32,
280             n_type: nlist.n_type,
281             n_sect: nlist.n_sect as u8,
282             n_desc: nlist.n_desc,
283             n_value: nlist.n_value as u32,
284         }
285     }
286 }
287 
288 impl From<Nlist> for Nlist64 {
from(nlist: Nlist) -> Self289     fn from(nlist: Nlist) -> Self {
290         Nlist64 {
291             n_strx: nlist.n_strx as u32,
292             n_type: nlist.n_type,
293             n_sect: nlist.n_sect as u8,
294             n_desc: nlist.n_desc,
295             n_value: nlist.n_value,
296         }
297     }
298 }
299 
300 impl<'a> ctx::TryFromCtx<'a, container::Ctx> for Nlist {
301     type Error = crate::error::Error;
302     type Size = usize;
303     fn try_from_ctx(bytes: &'a [u8], container::Ctx { container, le }: container::Ctx) -> crate::error::Result<(Self, Self::Size)> {
304         let nlist = match container {
305             Container::Little => {
306                 (bytes.pread_with::<Nlist32>(0, le)?.into(), SIZEOF_NLIST_32)
307             },
308             Container::Big => {
309                 (bytes.pread_with::<Nlist64>(0, le)?.into(), SIZEOF_NLIST_64)
310             },
311         };
312         Ok(nlist)
313     }
314 }
315 
316 impl ctx::TryIntoCtx<container::Ctx> for Nlist {
317     type Error = crate::error::Error;
318     type Size = usize;
319 
320     fn try_into_ctx(self, bytes: &mut [u8], container::Ctx { container, le }: container::Ctx) -> Result<Self::Size, Self::Error> {
321         let size = match container {
322             Container::Little => {
323                 (bytes.pwrite_with::<Nlist32>(self.into(), 0, le)?)
324             },
325             Container::Big => {
326                 (bytes.pwrite_with::<Nlist64>(self.into(), 0, le)?)
327             },
328         };
329         Ok(size)
330     }
331 }
332 
333 impl ctx::IntoCtx<container::Ctx> for Nlist {
into_ctx(self, bytes: &mut [u8], ctx: container::Ctx)334     fn into_ctx(self, bytes: &mut [u8], ctx: container::Ctx) {
335         bytes.pwrite_with(self, 0, ctx).unwrap();
336     }
337 }
338 
339 #[derive(Debug, Clone, Copy, Default)]
340 pub struct SymbolsCtx {
341     pub nsyms: usize,
342     pub strtab: usize,
343     pub ctx: container::Ctx,
344 }
345 
346 impl<'a, T: ?Sized> ctx::TryFromCtx<'a, SymbolsCtx, T> for Symbols<'a> where T: AsRef<[u8]> {
347     type Error = crate::error::Error;
348     type Size = usize;
349     fn try_from_ctx(bytes: &'a T, SymbolsCtx {
350         nsyms, strtab, ctx
351     }: SymbolsCtx) -> crate::error::Result<(Self, Self::Size)> {
352         let data = bytes.as_ref();
353         Ok ((Symbols {
354             data,
355             start: 0,
356             nsyms,
357             strtab,
358             ctx,
359         }, data.len()))
360     }
361 }
362 
363 #[derive(Default)]
364 pub struct SymbolIterator<'a> {
365     data: &'a [u8],
366     nsyms: usize,
367     offset: usize,
368     count: usize,
369     ctx: container::Ctx,
370     strtab: usize,
371 }
372 
373 impl<'a> Iterator for SymbolIterator<'a> {
374     type Item = error::Result<(&'a str, Nlist)>;
next(&mut self) -> Option<Self::Item>375     fn next(&mut self) -> Option<Self::Item> {
376         if self.count >= self.nsyms {
377             None
378         } else {
379             self.count += 1;
380             match self.data.gread_with::<Nlist>(&mut self.offset, self.ctx) {
381                 Ok(symbol) => {
382                     match self.data.pread(self.strtab + symbol.n_strx) {
383                         Ok(name) => {
384                             Some(Ok((name, symbol)))
385                         },
386                         Err(e) => Some(Err(e.into()))
387                     }
388                 },
389                 Err(e) => Some(Err(e))
390             }
391         }
392     }
393 }
394 
395 /// A zero-copy "nlist" style symbol table ("stab"), including the string table
396 pub struct Symbols<'a> {
397     data: &'a [u8],
398     start: usize,
399     nsyms: usize,
400     // TODO: we can use an actual strtab here and tie it to symbols lifetime
401     strtab: usize,
402     ctx: container::Ctx,
403 }
404 
405 impl<'a, 'b> IntoIterator for &'b Symbols<'a> {
406     type Item = <SymbolIterator<'a> as Iterator>::Item;
407     type IntoIter = SymbolIterator<'a>;
into_iter(self) -> Self::IntoIter408     fn into_iter(self) -> Self::IntoIter {
409         self.iter()
410     }
411 }
412 
413 impl<'a> Symbols<'a> {
414     /// Creates a new symbol table with `count` elements, from the `start` offset, using the string table at `strtab`, with a _default_ ctx.
415     ////
416     /// **Beware**, this will provide incorrect results if you construct this on a 32-bit mach binary, using a 64-bit machine; use `parse` instead if you want 32/64 bit support
new(bytes: &'a [u8], start: usize, count: usize, strtab: usize) -> error::Result<Symbols<'a>>417     pub fn new(bytes: &'a [u8], start: usize, count: usize, strtab: usize) -> error::Result<Symbols<'a>> {
418         let nsyms = count;
419         Ok (Symbols {
420             data: bytes,
421             start,
422             nsyms,
423             strtab,
424             ctx: container::Ctx::default(),
425         })
426     }
parse(bytes: &'a [u8], symtab: &load_command::SymtabCommand, ctx: container::Ctx) -> error::Result<Symbols<'a>>427     pub fn parse(bytes: &'a [u8], symtab: &load_command::SymtabCommand, ctx: container::Ctx) -> error::Result<Symbols<'a>> {
428         // we need to normalize the strtab offset before we receive the truncated bytes in pread_with
429         let strtab = symtab.stroff - symtab.symoff;
430         Ok(bytes.pread_with(symtab.symoff as usize, SymbolsCtx { nsyms: symtab.nsyms as usize, strtab: strtab as usize, ctx })?)
431     }
432 
iter(&self) -> SymbolIterator<'a>433     pub fn iter(&self) -> SymbolIterator<'a> {
434         SymbolIterator {
435             offset: self.start as usize,
436             nsyms: self.nsyms as usize,
437             count: 0,
438             data: self.data,
439             ctx: self.ctx,
440             strtab: self.strtab,
441         }
442     }
443 
444     /// Parses a single Nlist symbol from the binary, with its accompanying name
get(&self, index: usize) -> crate::error::Result<(&'a str, Nlist)>445     pub fn get(&self, index: usize) -> crate::error::Result<(&'a str, Nlist)> {
446         let sym: Nlist = self.data.pread_with(self.start + (index * Nlist::size_with(&self.ctx)), self.ctx)?;
447         let name = self.data.pread(self.strtab + sym.n_strx)?;
448         Ok((name, sym))
449     }
450 }
451 
452 impl<'a> Debug for Symbols<'a> {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result453     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
454         fmt.debug_struct("Symbols")
455             .field("data", &self.data.len())
456             .field("start", &format_args!("{:#?}", self.start))
457             .field("nsyms", &self.nsyms)
458             .field("strtab", &format_args!("{:#x}", self.strtab))
459             .finish()?;
460 
461         writeln!(fmt, "Symbol List {{")?;
462         for (i, res) in self.iter().enumerate() {
463             match res {
464                 Ok((name, nlist)) => writeln!(
465                     fmt,
466                     "{: >10x} {} sect: {:#x} type: {:#02x} desc: {:#03x}",
467                     nlist.n_value, name, nlist.n_sect, nlist.n_type, nlist.n_desc
468                 )?,
469                 Err(error) => writeln!(fmt, "  Bad symbol, index: {}, sym: {:?}", i, error)?,
470             }
471         }
472         writeln!(fmt, "}}")
473     }
474 }
475