symbolic_debuginfo/
base.rs

1use std::borrow::Cow;
2use std::fmt;
3use std::ops::{Bound, Deref, RangeBounds};
4use std::str::FromStr;
5
6use symbolic_common::{clean_path, join_path, Arch, CodeId, DebugId, Name};
7
8use crate::sourcebundle::SourceFileDescriptor;
9
10pub(crate) trait Parse<'data>: Sized {
11    type Error;
12
13    fn parse(data: &'data [u8]) -> Result<Self, Self::Error>;
14
15    fn test(data: &'data [u8]) -> bool {
16        Self::parse(data).is_ok()
17    }
18}
19
20/// An error returned for unknown or invalid `ObjectKinds`.
21#[derive(Debug)]
22pub struct UnknownObjectKindError;
23
24impl fmt::Display for UnknownObjectKindError {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        write!(f, "unknown object class")
27    }
28}
29
30impl std::error::Error for UnknownObjectKindError {}
31
32/// Represents the designated use of the object file and hints at its contents.
33#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone)]
34pub enum ObjectKind {
35    /// There is no object class specified for this object file.
36    None,
37
38    /// The Relocatable file type is the format used for intermediate object
39    /// files. It is a very compact format containing all its sections in one
40    /// segment. The compiler and assembler usually create one Relocatable file
41    /// for each source code file. By convention, the file name extension for
42    /// this format is .o.
43    Relocatable,
44
45    /// The Executable file type is the format used by standard executable
46    /// programs.
47    Executable,
48
49    /// The Library file type is for dynamic shared libraries. It contains
50    /// some additional tables to support multiple modules. By convention, the
51    /// file name extension for this format is .dylib, except for the main
52    /// shared library of a framework, which does not usually have a file name
53    /// extension.
54    Library,
55
56    /// The Dump file type is used to store core files, which are
57    /// traditionally created when a program crashes. Core files store the
58    /// entire address space of a process at the time it crashed. You can
59    /// later run gdb on the core file to figure out why the crash occurred.
60    Dump,
61
62    /// The Debug file type designates files that store symbol information
63    /// for a corresponding binary file.
64    Debug,
65
66    /// A container that just stores source code files, but no other debug
67    /// information corresponding to the original object file.
68    Sources,
69
70    /// The Other type represents any valid object class that does not fit any
71    /// of the other classes. These are mostly CPU or OS dependent, or unique
72    /// to a single kind of object.
73    Other,
74}
75
76impl ObjectKind {
77    /// Returns the name of the object kind.
78    pub fn name(self) -> &'static str {
79        match self {
80            ObjectKind::None => "none",
81            ObjectKind::Relocatable => "rel",
82            ObjectKind::Executable => "exe",
83            ObjectKind::Library => "lib",
84            ObjectKind::Dump => "dump",
85            ObjectKind::Debug => "dbg",
86            ObjectKind::Sources => "src",
87            ObjectKind::Other => "other",
88        }
89    }
90
91    /// Returns a human readable name of the object kind.
92    ///
93    /// This is also used in alternate formatting:
94    ///
95    /// ```rust
96    /// # use symbolic_debuginfo::ObjectKind;
97    /// assert_eq!(format!("{:#}", ObjectKind::Executable), ObjectKind::Executable.human_name());
98    /// ```
99    pub fn human_name(self) -> &'static str {
100        match self {
101            ObjectKind::None => "file",
102            ObjectKind::Relocatable => "object",
103            ObjectKind::Executable => "executable",
104            ObjectKind::Library => "library",
105            ObjectKind::Dump => "memory dump",
106            ObjectKind::Debug => "debug companion",
107            ObjectKind::Sources => "sources",
108            ObjectKind::Other => "file",
109        }
110    }
111}
112
113impl fmt::Display for ObjectKind {
114    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115        if f.alternate() {
116            f.write_str(self.human_name())
117        } else {
118            f.write_str(self.name())
119        }
120    }
121}
122
123impl FromStr for ObjectKind {
124    type Err = UnknownObjectKindError;
125
126    fn from_str(string: &str) -> Result<ObjectKind, UnknownObjectKindError> {
127        Ok(match string {
128            "none" => ObjectKind::None,
129            "rel" => ObjectKind::Relocatable,
130            "exe" => ObjectKind::Executable,
131            "lib" => ObjectKind::Library,
132            "dump" => ObjectKind::Dump,
133            "dbg" => ObjectKind::Debug,
134            "src" => ObjectKind::Sources,
135            "other" => ObjectKind::Other,
136            _ => return Err(UnknownObjectKindError),
137        })
138    }
139}
140
141/// An error returned for unknown or invalid [`FileFormats`](enum.FileFormat.html).
142#[derive(Debug)]
143pub struct UnknownFileFormatError;
144
145impl fmt::Display for UnknownFileFormatError {
146    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147        write!(f, "unknown file format")
148    }
149}
150
151impl std::error::Error for UnknownFileFormatError {}
152
153/// Represents the physical object file format.
154#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone)]
155pub enum FileFormat {
156    /// An unknown file format.
157    Unknown,
158    /// Breakpad ASCII symbol.
159    Breakpad,
160    /// Executable and Linkable Format, used on Linux.
161    Elf,
162    /// Mach Objects, used on macOS and iOS derivatives.
163    MachO,
164    /// Program Database, the debug companion format on Windows.
165    Pdb,
166    /// Portable Executable, an extension of COFF used on Windows.
167    Pe,
168    /// Source code bundle ZIP.
169    SourceBundle,
170    /// WASM container.
171    Wasm,
172    /// Portable PDB
173    PortablePdb,
174}
175
176impl FileFormat {
177    /// Returns the name of the file format.
178    pub fn name(self) -> &'static str {
179        match self {
180            FileFormat::Unknown => "unknown",
181            FileFormat::Breakpad => "breakpad",
182            FileFormat::Elf => "elf",
183            FileFormat::MachO => "macho",
184            FileFormat::Pdb => "pdb",
185            FileFormat::Pe => "pe",
186            FileFormat::SourceBundle => "sourcebundle",
187            FileFormat::Wasm => "wasm",
188            FileFormat::PortablePdb => "portablepdb",
189        }
190    }
191}
192
193impl fmt::Display for FileFormat {
194    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195        f.write_str(self.name())
196    }
197}
198
199impl FromStr for FileFormat {
200    type Err = UnknownFileFormatError;
201
202    fn from_str(string: &str) -> Result<FileFormat, UnknownFileFormatError> {
203        Ok(match string {
204            "breakpad" => FileFormat::Breakpad,
205            "elf" => FileFormat::Elf,
206            "macho" => FileFormat::MachO,
207            "pdb" => FileFormat::Pdb,
208            "pe" => FileFormat::Pe,
209            "sourcebundle" => FileFormat::SourceBundle,
210            "wasm" => FileFormat::Wasm,
211            "portablepdb" => FileFormat::PortablePdb,
212            _ => return Err(UnknownFileFormatError),
213        })
214    }
215}
216
217/// A symbol from a symbol table.
218#[derive(Clone, Default, Eq, PartialEq)]
219pub struct Symbol<'data> {
220    /// The name of the symbol.
221    ///
222    /// This name is generally mangled. It can be demangled by constructing a `Name` instance and
223    /// calling demangle on it. Certain object files might only store demangled symbol names.
224    pub name: Option<Cow<'data, str>>,
225
226    /// The relative address of this symbol.
227    pub address: u64,
228
229    /// The size of this symbol, if known.
230    ///
231    /// When loading symbols from an object file, the size will generally not be known. Instead,
232    /// construct a [`SymbolMap`] from the object, which also fills in sizes.
233    ///
234    /// [`SymbolMap`]: struct.SymbolMap.html
235    pub size: u64,
236}
237
238impl Symbol<'_> {
239    /// Returns the name of this symbol as string.
240    pub fn name(&self) -> Option<&str> {
241        self.name.as_ref().map(Cow::as_ref)
242    }
243
244    /// Determines whether the given address is covered by this symbol.
245    ///
246    /// If the symbol size has not been computed, the address is assumed to be covered if it is
247    /// greated than the symbol address. Otherwise, the address must be in the half-open interval
248    /// `[address, address + size)`.
249    pub fn contains(&self, address: u64) -> bool {
250        address >= self.address && (self.size == 0 || address < self.address + self.size)
251    }
252}
253
254impl fmt::Debug for Symbol<'_> {
255    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
256        f.debug_struct("Symbol")
257            .field("name", &self.name().unwrap_or("<unknown>"))
258            .field("address", &format_args!("{:#x}", self.address))
259            .field("size", &format_args!("{:#x}", self.size))
260            .finish()
261    }
262}
263
264/// IntoIterator type for [`SymbolMap`](struct.SymbolMap.html).
265pub type SymbolMapIter<'data> = std::vec::IntoIter<Symbol<'data>>;
266
267/// A sorted list of symbols, suitable for quick lookups.
268///
269/// This type can either be computed from a list or iterator of symbols, or preferrably directly
270/// by calling [`ObjectLike::symbol_map`] on any object. Symbols in the symbol map are guaranteed to
271/// have a `size` set, except for the last symbol, which is computed by taking the offset to the
272/// subsequent symbol.
273///
274/// `SymbolMap` also exposes a read-only view on the sorted slice of symbols. It can be converted to
275/// and from lists of symbols.
276///
277/// ## Example
278///
279/// ```rust
280/// # use symbolic_debuginfo::{Symbol, SymbolMap};
281/// let map = SymbolMap::from(vec![
282///     Symbol { name: Some("A".into()), address: 0x4400, size: 0 },
283///     Symbol { name: Some("B".into()), address: 0x4200, size: 0 },
284///     Symbol { name: Some("C".into()), address: 0x4000, size: 0 },
285/// ]);
286///
287/// assert_eq!(map[0], Symbol {
288///     name: Some("C".into()),
289///     address: 0x4000,
290///     size: 0x200,
291/// });
292/// ```
293///
294/// [`ObjectLike::symbol_map`]: trait.ObjectLike.html#tymethod.symbol_map
295#[derive(Clone, Debug, Default)]
296pub struct SymbolMap<'data> {
297    symbols: Vec<Symbol<'data>>,
298}
299
300impl<'data> SymbolMap<'data> {
301    /// Creates a new, empty symbol map.
302    pub fn new() -> Self {
303        SymbolMap {
304            symbols: Vec::new(),
305        }
306    }
307
308    /// Looks up the symbol covering the given address.
309    pub fn lookup(&self, address: u64) -> Option<&Symbol<'data>> {
310        match self.symbols.binary_search_by_key(&address, Self::key) {
311            Ok(index) => Some(&self.symbols[index]),
312            Err(0) => None,
313            Err(next_index) => {
314                let symbol = &self.symbols[next_index - 1];
315                if symbol.contains(address) {
316                    Some(symbol)
317                } else {
318                    None
319                }
320            }
321        }
322    }
323
324    /// Looks up a symbol by its start address.
325    pub fn lookup_exact(&self, address: u64) -> Option<&Symbol<'data>> {
326        let idx = self
327            .symbols
328            .binary_search_by_key(&address, Self::key)
329            .ok()?;
330        self.symbols.get(idx)
331    }
332
333    /// Looks up a symbol covering an entire range.
334    ///
335    /// This is similar to [`lookup`], but it only returns the symbol result if it _also_ covers the
336    /// inclusive end address of the range.
337    ///
338    /// [`lookup`]: struct.SymbolMap.html#method.lookup
339    pub fn lookup_range<R>(&self, range: R) -> Option<&Symbol<'data>>
340    where
341        R: RangeBounds<u64>,
342    {
343        let start = match range.start_bound() {
344            Bound::Included(start) => *start,
345            Bound::Excluded(start) => *start + 1,
346            Bound::Unbounded => 0,
347        };
348
349        let symbol = self.lookup(start)?;
350
351        let end = match range.end_bound() {
352            Bound::Included(end) => *end,
353            Bound::Excluded(end) => *end - 1,
354            Bound::Unbounded => u64::MAX,
355        };
356
357        if end <= start || symbol.contains(end) {
358            Some(symbol)
359        } else {
360            None
361        }
362    }
363
364    /// Returns the lookup key for a symbol, which is the symbol's address.
365    #[inline(always)]
366    fn key(symbol: &Symbol<'data>) -> u64 {
367        symbol.address
368    }
369}
370
371impl<'d> Deref for SymbolMap<'d> {
372    type Target = [Symbol<'d>];
373
374    fn deref(&self) -> &Self::Target {
375        &self.symbols
376    }
377}
378
379impl<'data> IntoIterator for SymbolMap<'data> {
380    type Item = Symbol<'data>;
381    type IntoIter = SymbolMapIter<'data>;
382
383    fn into_iter(self) -> Self::IntoIter {
384        self.symbols.into_iter()
385    }
386}
387
388impl<'data, 'a> IntoIterator for &'a SymbolMap<'data> {
389    type Item = &'a Symbol<'data>;
390    type IntoIter = std::slice::Iter<'a, Symbol<'data>>;
391
392    fn into_iter(self) -> Self::IntoIter {
393        self.symbols.iter()
394    }
395}
396
397impl<'d> AsRef<[Symbol<'d>]> for SymbolMap<'d> {
398    fn as_ref(&self) -> &[Symbol<'d>] {
399        &self.symbols
400    }
401}
402
403impl<'d> From<Vec<Symbol<'d>>> for SymbolMap<'d> {
404    fn from(mut symbols: Vec<Symbol<'d>>) -> Self {
405        if !symbols.is_empty() {
406            // NB: This might require stable sorting to ensure determinism if multiple symbols point
407            // at the same location. However, this only seems to happen for equivalent variants of
408            // the same function.
409            //
410            // An example would be destructors where D2 (base object destructor) and D1 (complete
411            // object destructor) might share the same code. Since those always demangle to the same
412            // name, we do not care which function to keep in this case.
413            //
414            // Inlined functions will generally not appear in this list, unless they _also_ have an
415            // explicit function body, in which case they will have a unique address, again.
416            symbols.sort_by_key(Self::key);
417
418            // Compute sizes of consecutive symbols if the size has not been provided by the symbol
419            // iterator. In the same go, drop all but the first symbols at any given address. We do
420            // not rely on the size of symbols in this case, since the ranges might still be
421            // overlapping.
422            symbols.dedup_by(|next, symbol| {
423                if symbol.size == 0 {
424                    symbol.size = next.address - symbol.address;
425                }
426                symbol.address == next.address
427            })
428        }
429
430        SymbolMap { symbols }
431    }
432}
433
434impl<'d> FromIterator<Symbol<'d>> for SymbolMap<'d> {
435    fn from_iter<I>(iter: I) -> Self
436    where
437        I: IntoIterator<Item = Symbol<'d>>,
438    {
439        Vec::from_iter(iter).into()
440    }
441}
442
443/// File information referred by [`LineInfo`](struct.LineInfo.html) comprising a directory and name.
444///
445/// The file path is usually relative to a compilation directory. It might contain parent directory
446/// segments (`../`).
447#[derive(Clone, Default, Eq, PartialEq)]
448pub struct FileInfo<'data> {
449    /// The file's basename.
450    name: Cow<'data, [u8]>,
451    /// Path to the file.
452    dir: Cow<'data, [u8]>,
453}
454
455impl<'data> FileInfo<'data> {
456    /// Creates a `FileInfo` with a given directory and the file name.
457    #[cfg(feature = "dwarf")]
458    pub fn new(dir: Cow<'data, [u8]>, name: Cow<'data, [u8]>) -> Self {
459        FileInfo { name, dir }
460    }
461
462    /// Creates a `FileInfo` from a joined path by trying to split it.
463    #[cfg(any(feature = "breakpad", feature = "ms", feature = "sourcebundle"))]
464    pub fn from_path(path: &'data [u8]) -> Self {
465        let (dir, name) = symbolic_common::split_path_bytes(path);
466
467        FileInfo {
468            name: Cow::Borrowed(name),
469            dir: match dir {
470                Some(dir) => Cow::Borrowed(dir),
471                None => Cow::default(),
472            },
473        }
474    }
475
476    /// Creates a `FileInfo` from a joined path by trying to split it.
477    /// Unlike from_path(), copies the given data instead of referencing it.
478    #[cfg(feature = "ppdb")]
479    pub(crate) fn from_path_owned(path: &[u8]) -> Self {
480        let (dir, name) = symbolic_common::split_path_bytes(path);
481
482        FileInfo {
483            name: Cow::Owned(name.to_vec()),
484            dir: match dir {
485                Some(dir) => Cow::Owned(dir.to_vec()),
486                None => Cow::default(),
487            },
488        }
489    }
490
491    /// Creates a `FileInfo` with the file name.
492    pub fn from_filename(name: &'data [u8]) -> Self {
493        FileInfo {
494            name: Cow::Borrowed(name),
495            dir: Cow::default(),
496        }
497    }
498
499    /// The file name as UTF-8 string.
500    pub fn name_str(&self) -> Cow<'data, str> {
501        from_utf8_cow_lossy(&self.name)
502    }
503
504    /// Path to the file relative to the compilation directory.
505    pub fn dir_str(&self) -> Cow<'data, str> {
506        from_utf8_cow_lossy(&self.dir)
507    }
508
509    /// The full path to the file, relative to the compilation directory.
510    pub fn path_str(&self) -> String {
511        let joined = join_path(&self.dir_str(), &self.name_str());
512        clean_path(&joined).into_owned()
513    }
514}
515
516#[allow(clippy::ptr_arg)] // false positive https://github.com/rust-lang/rust-clippy/issues/9218
517pub(crate) fn from_utf8_cow_lossy<'data>(input: &Cow<'data, [u8]>) -> Cow<'data, str> {
518    // See https://github.com/rust-lang/rust/issues/32669
519    match input {
520        Cow::Borrowed(bytes) => String::from_utf8_lossy(bytes),
521        Cow::Owned(bytes) => match String::from_utf8_lossy(bytes) {
522            Cow::Borrowed(_) => unsafe { String::from_utf8_unchecked(bytes.to_vec()) }.into(),
523            Cow::Owned(s) => s.into(),
524        },
525    }
526}
527
528impl fmt::Debug for FileInfo<'_> {
529    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
530        f.debug_struct("FileInfo")
531            .field("name", &self.name_str())
532            .field("dir", &self.dir_str())
533            .finish()
534    }
535}
536
537/// File information comprising a compilation directory, relative path and name.
538pub struct FileEntry<'data> {
539    /// Path to the compilation directory. File paths are relative to this.
540    compilation_dir: Cow<'data, [u8]>,
541    /// File name and path.
542    pub info: FileInfo<'data>,
543}
544
545impl<'data> FileEntry<'data> {
546    /// Path to the compilation directory.
547    pub fn new(compilation_dir: Cow<'data, [u8]>, info: FileInfo<'data>) -> Self {
548        FileEntry {
549            compilation_dir,
550            info,
551        }
552    }
553
554    /// Path to the compilation directory.
555    pub fn compilation_dir_str(&self) -> Cow<'data, str> {
556        from_utf8_cow_lossy(&self.compilation_dir)
557    }
558
559    /// Absolute path to the file, including the compilation directory.
560    pub fn abs_path_str(&self) -> String {
561        let joined_path = join_path(&self.dir_str(), &self.name_str());
562        let joined = join_path(&self.compilation_dir_str(), &joined_path);
563        clean_path(&joined).into_owned()
564    }
565}
566
567impl fmt::Debug for FileEntry<'_> {
568    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
569        f.debug_struct("FileInfo")
570            .field("compilation_dir", &self.compilation_dir_str())
571            .field("name", &self.name_str())
572            .field("dir", &self.dir_str())
573            .finish()
574    }
575}
576
577impl<'data> Deref for FileEntry<'data> {
578    type Target = FileInfo<'data>;
579
580    fn deref(&self) -> &Self::Target {
581        &self.info
582    }
583}
584
585/// File and line number mapping for an instruction address.
586#[derive(Clone, Eq, PartialEq)]
587pub struct LineInfo<'data> {
588    /// The instruction address relative to the image base (load address).
589    pub address: u64,
590    /// Total code size covered by this line record.
591    pub size: Option<u64>,
592    /// File name and path.
593    pub file: FileInfo<'data>,
594    /// Absolute line number starting at 1. Zero means no line number.
595    pub line: u64,
596}
597
598#[cfg(test)]
599impl LineInfo<'static> {
600    pub(crate) fn new(address: u64, size: u64, file: &[u8], line: u64) -> LineInfo<'_> {
601        LineInfo {
602            address,
603            size: Some(size),
604            file: FileInfo::from_filename(file),
605            line,
606        }
607    }
608}
609
610impl fmt::Debug for LineInfo<'_> {
611    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
612        let mut s = f.debug_struct("LineInfo");
613        s.field("address", &format_args!("{:#x}", self.address));
614
615        match self.size {
616            Some(size) => s.field("size", &format_args!("{size:#x}")),
617            None => s.field("size", &self.size),
618        };
619
620        s.field("file", &self.file)
621            .field("line", &self.line)
622            .finish()
623    }
624}
625
626/// Debug information for a function.
627#[derive(Clone)]
628pub struct Function<'data> {
629    /// Relative instruction address of the start of the function.
630    pub address: u64,
631    /// Total code size covered by the function body, including inlined functions.
632    pub size: u64,
633    /// The name and language of the function symbol.
634    pub name: Name<'data>,
635    /// Path to the compilation directory. File paths are relative to this.
636    pub compilation_dir: &'data [u8],
637    /// Lines covered by this function, including inlined children.
638    pub lines: Vec<LineInfo<'data>>,
639    /// Functions that have been inlined into this function's body.
640    pub inlinees: Vec<Function<'data>>,
641    /// Specifies whether this function is inlined.
642    pub inline: bool,
643}
644
645impl Function<'_> {
646    /// End address of the entire function body, including inlined functions.
647    ///
648    /// This address points at the first instruction after the function body.
649    pub fn end_address(&self) -> u64 {
650        self.address.saturating_add(self.size)
651    }
652}
653
654impl fmt::Debug for Function<'_> {
655    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
656        f.debug_struct("Function")
657            .field("address", &format_args!("{:#x}", self.address))
658            .field("size", &format_args!("{:#x}", self.size))
659            .field("name", &self.name)
660            .field(
661                "compilation_dir",
662                &String::from_utf8_lossy(self.compilation_dir),
663            )
664            .field("lines", &self.lines)
665            .field("inlinees", &self.inlinees)
666            .field("inline", &self.inline)
667            .finish()
668    }
669}
670
671/// A dynamically dispatched iterator over items with the given lifetime.
672pub type DynIterator<'a, T> = Box<dyn Iterator<Item = T> + 'a>;
673
674/// A stateful session for interfacing with debug information.
675///
676/// Debug sessions can be obtained via [`ObjectLike::debug_session`]. Since computing a session may
677/// be a costly operation, try to reuse the session as much as possible.
678///
679/// ## Implementing DebugSession
680///
681/// Reading debug information from object files usually requires loading multiple sections into
682/// memory and computing maps for quick random access to certain information. Since this can be a
683/// quite costly process, this is encapsulated into a `DebugSession`. The session may hold whatever
684/// data and caches may be necessary for efficiently interfacing with the debug info.
685///
686/// All trait methods on a `DebugSession` receive `&mut self`, to allow mutation of internal cache
687/// structures. Lifetimes of returned types are tied to this session's lifetime, which allows to
688/// borrow data from the session.
689///
690/// Examples for things to compute when building a debug session are:
691///
692///  - Decompress debug information if it is stored with compression.
693///  - Build a symbol map for random access to public symbols.
694///  - Map string tables and other lookup tables.
695///  - Read headers of compilation units (compilands) to resolve cross-unit references.
696///
697/// [`ObjectLike::debug_session`]: trait.ObjectLike.html#tymethod.debug_session
698pub trait DebugSession<'session> {
699    /// The error returned when reading debug information fails.
700    type Error;
701
702    /// An iterator over all functions in this debug file.
703    type FunctionIterator: Iterator<Item = Result<Function<'session>, Self::Error>>;
704
705    /// An iterator over all source files referenced by this debug file.
706    type FileIterator: Iterator<Item = Result<FileEntry<'session>, Self::Error>>;
707
708    /// Returns an iterator over all functions in this debug file.
709    ///
710    /// Functions are iterated in the order they are declared in their compilation units. The
711    /// functions yielded by this iterator include all inlinees and line records resolved.
712    ///
713    /// Note that the iterator holds a mutable borrow on the debug session, which allows it to use
714    /// caches and optimize resources while resolving function and line information.
715    fn functions(&'session self) -> Self::FunctionIterator;
716
717    /// Returns an iterator over all source files referenced by this debug file.
718    fn files(&'session self) -> Self::FileIterator;
719
720    /// Looks up a file's source by its full canonicalized path.
721    ///
722    /// Returns a descriptor that has all the information available of the source.  It can
723    /// either contain the source contents directly, if it was embedded, or a source link.
724    fn source_by_path(&self, path: &str) -> Result<Option<SourceFileDescriptor<'_>>, Self::Error>;
725}
726
727/// An object containing debug information.
728pub trait ObjectLike<'data, 'object> {
729    /// Errors thrown when reading information from this object.
730    type Error;
731
732    /// A session that allows optimized access to debugging information.
733    type Session: for<'session> DebugSession<'session, Error = Self::Error>;
734
735    /// The iterator over the symbols in the public symbol table.
736    type SymbolIterator: Iterator<Item = Symbol<'data>>;
737
738    /// The container format of this file.
739    fn file_format(&self) -> FileFormat;
740
741    /// The code identifier of this object.
742    ///
743    /// The identifier can be `None` if it cannot be determined from the object file, for instance,
744    /// because the identifier was stripped in the build process.
745    fn code_id(&self) -> Option<CodeId>;
746
747    /// The debug information identifier of this object.
748    fn debug_id(&self) -> DebugId;
749
750    /// The CPU architecture of this object.
751    fn arch(&self) -> Arch;
752
753    /// The kind of this object.
754    fn kind(&self) -> ObjectKind;
755
756    /// The address at which the image prefers to be loaded into memory.
757    fn load_address(&self) -> u64;
758
759    /// Determines whether this object exposes a public symbol table.
760    fn has_symbols(&self) -> bool;
761
762    /// Returns an iterator over symbols in the public symbol table.
763    fn symbols(&'object self) -> Self::SymbolIterator;
764
765    /// Returns an ordered map of symbols in the symbol table.
766    fn symbol_map(&self) -> SymbolMap<'data>;
767
768    /// Determines whether this object contains debug information.
769    fn has_debug_info(&self) -> bool;
770
771    /// Constructs a debugging session.
772    ///
773    /// A debugging session loads certain information from the object file and creates caches for
774    /// efficient access to various records in the debug information. Since this can be quite a
775    /// costly process, try to reuse the debugging session as long as possible.
776    ///
777    /// Constructing this session will also work if the object does not contain debugging
778    /// information, in which case the session will be a no-op. This can be checked via
779    /// [`has_debug_info`](trait.ObjectLike.html#tymethod.has_debug_info).
780    fn debug_session(&'object self) -> Result<Self::Session, Self::Error>;
781
782    /// Determines whether this object contains stack unwinding information.
783    fn has_unwind_info(&self) -> bool;
784
785    /// Determines whether this object contains embedded sources.
786    fn has_sources(&self) -> bool;
787
788    /// Determines whether this object is malformed and was only partially parsed
789    fn is_malformed(&self) -> bool;
790}
791
792mod derive_serde {
793    /// Helper macro to implement string based serialization and deserialization.
794    ///
795    /// If a type implements `FromStr` and `Display` then this automatically
796    /// implements a serializer/deserializer for that type that dispatches
797    /// appropriately.
798    macro_rules! impl_str_serde {
799        ($type:ty) => {
800            impl ::serde::ser::Serialize for $type {
801                fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
802                where
803                    S: ::serde::ser::Serializer,
804                {
805                    serializer.serialize_str(self.name())
806                }
807            }
808
809            impl<'de> ::serde::de::Deserialize<'de> for $type {
810                fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
811                where
812                    D: ::serde::de::Deserializer<'de>,
813                {
814                    <::std::borrow::Cow<'_, str>>::deserialize(deserializer)?
815                        .parse()
816                        .map_err(::serde::de::Error::custom)
817                }
818            }
819        };
820    }
821
822    impl_str_serde!(super::ObjectKind);
823    impl_str_serde!(super::FileFormat);
824}
825
826#[cfg(test)]
827mod tests {
828    use super::*;
829    use similar_asserts::assert_eq;
830
831    fn file_info<'a>(dir: &'a str, name: &'a str) -> FileInfo<'a> {
832        FileInfo::new(
833            Cow::Borrowed(dir.as_bytes()),
834            Cow::Borrowed(name.as_bytes()),
835        )
836    }
837
838    fn file_entry<'a>(compilation_dir: &'a str, dir: &'a str, name: &'a str) -> FileEntry<'a> {
839        FileEntry::new(
840            Cow::Borrowed(compilation_dir.as_bytes()),
841            file_info(dir, name),
842        )
843    }
844
845    #[test]
846    fn test_file_info() {
847        assert_eq!(file_info("", "foo.h").path_str(), "foo.h");
848        assert_eq!(
849            file_info("C:\\Windows", "foo.h").path_str(),
850            "C:\\Windows\\foo.h"
851        );
852        assert_eq!(
853            file_info("/usr/local", "foo.h").path_str(),
854            "/usr/local/foo.h"
855        );
856        assert_eq!(file_info("/usr/local", "../foo.h").path_str(), "/usr/foo.h");
857        assert_eq!(file_info("/usr/local", "/foo.h").path_str(), "/foo.h");
858    }
859
860    #[test]
861    fn test_file_entry() {
862        assert_eq!(file_entry("", "", "foo.h").abs_path_str(), "foo.h");
863        assert_eq!(
864            file_entry("C:\\Windows", "src", "foo.h").abs_path_str(),
865            "C:\\Windows\\src\\foo.h"
866        );
867        assert_eq!(
868            file_entry("/usr", "local", "foo.h").abs_path_str(),
869            "/usr/local/foo.h"
870        );
871        assert_eq!(
872            file_entry("/usr/local", "..", "foo.h").abs_path_str(),
873            "/usr/foo.h"
874        );
875        assert_eq!(
876            file_entry("/usr", "/src", "foo.h").abs_path_str(),
877            "/src/foo.h"
878        );
879    }
880}