]> git.proxmox.com Git - rustc.git/blame - vendor/object-0.31.1/src/read/macho/symbol.rs
New upstream version 1.73.0+dfsg1
[rustc.git] / vendor / object-0.31.1 / src / read / macho / symbol.rs
CommitLineData
9c376795
FG
1use alloc::vec::Vec;
2use core::fmt::Debug;
3use core::{fmt, slice, str};
4
5use crate::endian::{self, Endianness};
6use crate::macho;
7use crate::pod::Pod;
8use crate::read::util::StringTable;
9use crate::read::{
10 self, ObjectMap, ObjectMapEntry, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, Result,
11 SectionIndex, SectionKind, SymbolFlags, SymbolIndex, SymbolKind, SymbolMap, SymbolMapEntry,
12 SymbolScope, SymbolSection,
13};
14
15use super::{MachHeader, MachOFile};
16
17/// A table of symbol entries in a Mach-O file.
18///
19/// Also includes the string table used for the symbol names.
20#[derive(Debug, Clone, Copy)]
21pub struct SymbolTable<'data, Mach: MachHeader, R = &'data [u8]>
22where
23 R: ReadRef<'data>,
24{
25 symbols: &'data [Mach::Nlist],
26 strings: StringTable<'data, R>,
27}
28
29impl<'data, Mach: MachHeader, R: ReadRef<'data>> Default for SymbolTable<'data, Mach, R> {
30 fn default() -> Self {
31 SymbolTable {
32 symbols: &[],
33 strings: Default::default(),
34 }
35 }
36}
37
38impl<'data, Mach: MachHeader, R: ReadRef<'data>> SymbolTable<'data, Mach, R> {
39 #[inline]
40 pub(super) fn new(symbols: &'data [Mach::Nlist], strings: StringTable<'data, R>) -> Self {
41 SymbolTable { symbols, strings }
42 }
43
44 /// Return the string table used for the symbol names.
45 #[inline]
46 pub fn strings(&self) -> StringTable<'data, R> {
47 self.strings
48 }
49
50 /// Iterate over the symbols.
51 #[inline]
52 pub fn iter(&self) -> slice::Iter<'data, Mach::Nlist> {
53 self.symbols.iter()
54 }
55
56 /// Return true if the symbol table is empty.
57 #[inline]
58 pub fn is_empty(&self) -> bool {
59 self.symbols.is_empty()
60 }
61
62 /// The number of symbols.
63 #[inline]
64 pub fn len(&self) -> usize {
65 self.symbols.len()
66 }
67
68 /// Return the symbol at the given index.
69 pub fn symbol(&self, index: usize) -> Result<&'data Mach::Nlist> {
70 self.symbols
71 .get(index)
72 .read_error("Invalid Mach-O symbol index")
73 }
74
75 /// Construct a map from addresses to a user-defined map entry.
76 pub fn map<Entry: SymbolMapEntry, F: Fn(&'data Mach::Nlist) -> Option<Entry>>(
77 &self,
78 f: F,
79 ) -> SymbolMap<Entry> {
80 let mut symbols = Vec::new();
81 for nlist in self.symbols {
82 if !nlist.is_definition() {
83 continue;
84 }
85 if let Some(entry) = f(nlist) {
86 symbols.push(entry);
87 }
88 }
89 SymbolMap::new(symbols)
90 }
91
92 /// Construct a map from addresses to symbol names and object file names.
93 pub fn object_map(&self, endian: Mach::Endian) -> ObjectMap<'data> {
94 let mut symbols = Vec::new();
95 let mut objects = Vec::new();
96 let mut object = None;
97 let mut current_function = None;
98 // Each module starts with one or two N_SO symbols (path, or directory + filename)
99 // and one N_OSO symbol. The module is terminated by an empty N_SO symbol.
100 for nlist in self.symbols {
101 let n_type = nlist.n_type();
102 if n_type & macho::N_STAB == 0 {
103 continue;
104 }
105 // TODO: includes variables too (N_GSYM, N_STSYM). These may need to get their
106 // address from regular symbols though.
107 match n_type {
108 macho::N_SO => {
109 object = None;
110 }
111 macho::N_OSO => {
112 object = None;
113 if let Ok(name) = nlist.name(endian, self.strings) {
114 if !name.is_empty() {
115 object = Some(objects.len());
116 objects.push(name);
117 }
118 }
119 }
120 macho::N_FUN => {
121 if let Ok(name) = nlist.name(endian, self.strings) {
122 if !name.is_empty() {
123 current_function = Some((name, nlist.n_value(endian).into()))
124 } else if let Some((name, address)) = current_function.take() {
125 if let Some(object) = object {
126 symbols.push(ObjectMapEntry {
127 address,
128 size: nlist.n_value(endian).into(),
129 name,
130 object,
131 });
132 }
133 }
134 }
135 }
136 _ => {}
137 }
138 }
139 ObjectMap {
140 symbols: SymbolMap::new(symbols),
141 objects,
142 }
143 }
144}
145
146/// An iterator over the symbols of a `MachOFile32`.
147pub type MachOSymbolTable32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
148 MachOSymbolTable<'data, 'file, macho::MachHeader32<Endian>, R>;
149/// An iterator over the symbols of a `MachOFile64`.
150pub type MachOSymbolTable64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
151 MachOSymbolTable<'data, 'file, macho::MachHeader64<Endian>, R>;
152
153/// A symbol table of a `MachOFile`.
154#[derive(Debug, Clone, Copy)]
155pub struct MachOSymbolTable<'data, 'file, Mach, R = &'data [u8]>
156where
157 Mach: MachHeader,
158 R: ReadRef<'data>,
159{
160 pub(super) file: &'file MachOFile<'data, Mach, R>,
161}
162
163impl<'data, 'file, Mach, R> read::private::Sealed for MachOSymbolTable<'data, 'file, Mach, R>
164where
165 Mach: MachHeader,
166 R: ReadRef<'data>,
167{
168}
169
170impl<'data, 'file, Mach, R> ObjectSymbolTable<'data> for MachOSymbolTable<'data, 'file, Mach, R>
171where
172 Mach: MachHeader,
173 R: ReadRef<'data>,
174{
175 type Symbol = MachOSymbol<'data, 'file, Mach, R>;
176 type SymbolIterator = MachOSymbolIterator<'data, 'file, Mach, R>;
177
178 fn symbols(&self) -> Self::SymbolIterator {
179 MachOSymbolIterator {
180 file: self.file,
181 index: 0,
182 }
183 }
184
185 fn symbol_by_index(&self, index: SymbolIndex) -> Result<Self::Symbol> {
186 let nlist = self.file.symbols.symbol(index.0)?;
187 MachOSymbol::new(self.file, index, nlist).read_error("Unsupported Mach-O symbol index")
188 }
189}
190
191/// An iterator over the symbols of a `MachOFile32`.
192pub type MachOSymbolIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
193 MachOSymbolIterator<'data, 'file, macho::MachHeader32<Endian>, R>;
194/// An iterator over the symbols of a `MachOFile64`.
195pub type MachOSymbolIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
196 MachOSymbolIterator<'data, 'file, macho::MachHeader64<Endian>, R>;
197
198/// An iterator over the symbols of a `MachOFile`.
199pub struct MachOSymbolIterator<'data, 'file, Mach, R = &'data [u8]>
200where
201 Mach: MachHeader,
202 R: ReadRef<'data>,
203{
204 pub(super) file: &'file MachOFile<'data, Mach, R>,
205 pub(super) index: usize,
206}
207
208impl<'data, 'file, Mach, R> fmt::Debug for MachOSymbolIterator<'data, 'file, Mach, R>
209where
210 Mach: MachHeader,
211 R: ReadRef<'data>,
212{
213 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214 f.debug_struct("MachOSymbolIterator").finish()
215 }
216}
217
218impl<'data, 'file, Mach, R> Iterator for MachOSymbolIterator<'data, 'file, Mach, R>
219where
220 Mach: MachHeader,
221 R: ReadRef<'data>,
222{
223 type Item = MachOSymbol<'data, 'file, Mach, R>;
224
225 fn next(&mut self) -> Option<Self::Item> {
226 loop {
227 let index = self.index;
228 let nlist = self.file.symbols.symbols.get(index)?;
229 self.index += 1;
230 if let Some(symbol) = MachOSymbol::new(self.file, SymbolIndex(index), nlist) {
231 return Some(symbol);
232 }
233 }
234 }
235}
236
237/// A symbol of a `MachOFile32`.
238pub type MachOSymbol32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
239 MachOSymbol<'data, 'file, macho::MachHeader32<Endian>, R>;
240/// A symbol of a `MachOFile64`.
241pub type MachOSymbol64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
242 MachOSymbol<'data, 'file, macho::MachHeader64<Endian>, R>;
243
244/// A symbol of a `MachOFile`.
245#[derive(Debug, Clone, Copy)]
246pub struct MachOSymbol<'data, 'file, Mach, R = &'data [u8]>
247where
248 Mach: MachHeader,
249 R: ReadRef<'data>,
250{
251 file: &'file MachOFile<'data, Mach, R>,
252 index: SymbolIndex,
253 nlist: &'data Mach::Nlist,
254}
255
256impl<'data, 'file, Mach, R> MachOSymbol<'data, 'file, Mach, R>
257where
258 Mach: MachHeader,
259 R: ReadRef<'data>,
260{
261 pub(super) fn new(
262 file: &'file MachOFile<'data, Mach, R>,
263 index: SymbolIndex,
264 nlist: &'data Mach::Nlist,
265 ) -> Option<Self> {
266 if nlist.n_type() & macho::N_STAB != 0 {
267 return None;
268 }
269 Some(MachOSymbol { file, index, nlist })
270 }
271}
272
273impl<'data, 'file, Mach, R> read::private::Sealed for MachOSymbol<'data, 'file, Mach, R>
274where
275 Mach: MachHeader,
276 R: ReadRef<'data>,
277{
278}
279
280impl<'data, 'file, Mach, R> ObjectSymbol<'data> for MachOSymbol<'data, 'file, Mach, R>
281where
282 Mach: MachHeader,
283 R: ReadRef<'data>,
284{
285 #[inline]
286 fn index(&self) -> SymbolIndex {
287 self.index
288 }
289
290 fn name_bytes(&self) -> Result<&'data [u8]> {
291 self.nlist.name(self.file.endian, self.file.symbols.strings)
292 }
293
294 fn name(&self) -> Result<&'data str> {
295 let name = self.name_bytes()?;
296 str::from_utf8(name)
297 .ok()
298 .read_error("Non UTF-8 Mach-O symbol name")
299 }
300
301 #[inline]
302 fn address(&self) -> u64 {
303 self.nlist.n_value(self.file.endian).into()
304 }
305
306 #[inline]
307 fn size(&self) -> u64 {
308 0
309 }
310
311 fn kind(&self) -> SymbolKind {
312 self.section()
313 .index()
314 .and_then(|index| self.file.section_internal(index).ok())
315 .map(|section| match section.kind {
316 SectionKind::Text => SymbolKind::Text,
317 SectionKind::Data
318 | SectionKind::ReadOnlyData
319 | SectionKind::ReadOnlyString
320 | SectionKind::UninitializedData
321 | SectionKind::Common => SymbolKind::Data,
322 SectionKind::Tls | SectionKind::UninitializedTls | SectionKind::TlsVariables => {
323 SymbolKind::Tls
324 }
325 _ => SymbolKind::Unknown,
326 })
327 .unwrap_or(SymbolKind::Unknown)
328 }
329
330 fn section(&self) -> SymbolSection {
331 match self.nlist.n_type() & macho::N_TYPE {
332 macho::N_UNDF => SymbolSection::Undefined,
333 macho::N_ABS => SymbolSection::Absolute,
334 macho::N_SECT => {
335 let n_sect = self.nlist.n_sect();
336 if n_sect != 0 {
337 SymbolSection::Section(SectionIndex(n_sect as usize))
338 } else {
339 SymbolSection::Unknown
340 }
341 }
342 _ => SymbolSection::Unknown,
343 }
344 }
345
346 #[inline]
347 fn is_undefined(&self) -> bool {
348 self.nlist.n_type() & macho::N_TYPE == macho::N_UNDF
349 }
350
351 #[inline]
352 fn is_definition(&self) -> bool {
353 self.nlist.is_definition()
354 }
355
356 #[inline]
357 fn is_common(&self) -> bool {
358 // Mach-O common symbols are based on section, not symbol
359 false
360 }
361
362 #[inline]
363 fn is_weak(&self) -> bool {
364 self.nlist.n_desc(self.file.endian) & (macho::N_WEAK_REF | macho::N_WEAK_DEF) != 0
365 }
366
367 fn scope(&self) -> SymbolScope {
368 let n_type = self.nlist.n_type();
369 if n_type & macho::N_TYPE == macho::N_UNDF {
370 SymbolScope::Unknown
371 } else if n_type & macho::N_EXT == 0 {
372 SymbolScope::Compilation
373 } else if n_type & macho::N_PEXT != 0 {
374 SymbolScope::Linkage
375 } else {
376 SymbolScope::Dynamic
377 }
378 }
379
380 #[inline]
381 fn is_global(&self) -> bool {
382 self.scope() != SymbolScope::Compilation
383 }
384
385 #[inline]
386 fn is_local(&self) -> bool {
387 self.scope() == SymbolScope::Compilation
388 }
389
390 #[inline]
fe692bf9 391 fn flags(&self) -> SymbolFlags<SectionIndex, SymbolIndex> {
9c376795
FG
392 let n_desc = self.nlist.n_desc(self.file.endian);
393 SymbolFlags::MachO { n_desc }
394 }
395}
396
397/// A trait for generic access to `Nlist32` and `Nlist64`.
398#[allow(missing_docs)]
399pub trait Nlist: Debug + Pod {
400 type Word: Into<u64>;
401 type Endian: endian::Endian;
402
403 fn n_strx(&self, endian: Self::Endian) -> u32;
404 fn n_type(&self) -> u8;
405 fn n_sect(&self) -> u8;
406 fn n_desc(&self, endian: Self::Endian) -> u16;
407 fn n_value(&self, endian: Self::Endian) -> Self::Word;
408
409 fn name<'data, R: ReadRef<'data>>(
410 &self,
411 endian: Self::Endian,
412 strings: StringTable<'data, R>,
413 ) -> Result<&'data [u8]> {
414 strings
415 .get(self.n_strx(endian))
416 .read_error("Invalid Mach-O symbol name offset")
417 }
418
419 /// Return true if this is a STAB symbol.
420 ///
421 /// This determines the meaning of the `n_type` field.
422 fn is_stab(&self) -> bool {
423 self.n_type() & macho::N_STAB != 0
424 }
425
426 /// Return true if this is an undefined symbol.
427 fn is_undefined(&self) -> bool {
428 let n_type = self.n_type();
429 n_type & macho::N_STAB == 0 && n_type & macho::N_TYPE == macho::N_UNDF
430 }
431
432 /// Return true if the symbol is a definition of a function or data object.
433 fn is_definition(&self) -> bool {
434 let n_type = self.n_type();
435 n_type & macho::N_STAB == 0 && n_type & macho::N_TYPE != macho::N_UNDF
436 }
437
438 /// Return the library ordinal.
439 ///
440 /// This is either a 1-based index into the dylib load commands,
441 /// or a special ordinal.
442 #[inline]
443 fn library_ordinal(&self, endian: Self::Endian) -> u8 {
444 (self.n_desc(endian) >> 8) as u8
445 }
446}
447
448impl<Endian: endian::Endian> Nlist for macho::Nlist32<Endian> {
449 type Word = u32;
450 type Endian = Endian;
451
452 fn n_strx(&self, endian: Self::Endian) -> u32 {
453 self.n_strx.get(endian)
454 }
455 fn n_type(&self) -> u8 {
456 self.n_type
457 }
458 fn n_sect(&self) -> u8 {
459 self.n_sect
460 }
461 fn n_desc(&self, endian: Self::Endian) -> u16 {
462 self.n_desc.get(endian)
463 }
464 fn n_value(&self, endian: Self::Endian) -> Self::Word {
465 self.n_value.get(endian)
466 }
467}
468
469impl<Endian: endian::Endian> Nlist for macho::Nlist64<Endian> {
470 type Word = u64;
471 type Endian = Endian;
472
473 fn n_strx(&self, endian: Self::Endian) -> u32 {
474 self.n_strx.get(endian)
475 }
476 fn n_type(&self) -> u8 {
477 self.n_type
478 }
479 fn n_sect(&self) -> u8 {
480 self.n_sect
481 }
482 fn n_desc(&self, endian: Self::Endian) -> u16 {
483 self.n_desc.get(endian)
484 }
485 fn n_value(&self, endian: Self::Endian) -> Self::Word {
486 self.n_value.get(endian)
487 }
488}