1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
use std::collections::BTreeSet;
use std::fmt;

use crate::dwarf::{DwarfData, DwarfSection};
use crate::object::{Object, ObjectTarget};
use crate::symbols::SymbolTable;

fn has_dwarf_unwind_info(object: &Object) -> bool {
    object.get_dwarf_section(DwarfSection::EhFrame).is_some()
        || object.get_dwarf_section(DwarfSection::DebugFrame).is_some()
}

fn has_breakpad_record(object: &Object, record: &[u8]) -> bool {
    for line in object.as_bytes().split(|b| *b == b'\n') {
        if line.starts_with(record) {
            return true;
        }
    }

    false
}

/// A debug feature of an `Object` file.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum ObjectFeature {
    /// This object contains a symbol table.
    ///
    /// It can be used as a fallback for memory address symbolication if `DebugInfo` is not
    /// available. Symbol tables are usually contained in both executables and debug files.
    SymbolTable,

    /// This object contains debug information.
    ///
    /// It can be used to resolve native memory addresses to stack frames. Examples are Dwarf's
    /// .debug_info and related sections, or the Debug Info (DBI) stream in PDBs.
    DebugInfo,

    /// This object contains unwind information.
    ///
    /// It can be used to improve stack walking on stack memory. Examples are Call Frame Information
    /// (CFI) Dwarf or FPO-Info in PDBs.
    UnwindInfo,

    /// This object contains source name mapping information.
    ///
    /// It can be used to map obfuscated or shortened names to their original representations.
    /// Examples are JavaScript source maps or Proguard mapping files.
    Mapping,
}

impl fmt::Display for ObjectFeature {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ObjectFeature::SymbolTable => write!(f, "symtab"),
            ObjectFeature::DebugInfo => write!(f, "debug"),
            ObjectFeature::UnwindInfo => write!(f, "unwind"),
            ObjectFeature::Mapping => write!(f, "mapping"),
        }
    }
}

/// Inspects features of `Object` files.
pub trait DebugFeatures {
    /// Checks whether this file contains a symbol table.
    fn has_symbol_table(&self) -> bool;

    /// Checks whether this object file contains processable debug information.
    fn has_debug_info(&self) -> bool;

    /// Checks whether this object contains processable unwind information (CFI).
    fn has_unwind_info(&self) -> bool;

    /// Checks whether this object contains processable name mapping info.
    fn has_mapping(&self) -> bool;

    /// Checks whether this object has a given feature.
    fn has_feature(&self, tag: ObjectFeature) -> bool;

    /// Returns all features of this object.
    fn features(&self) -> BTreeSet<ObjectFeature>;
}

impl<'a> DebugFeatures for Object<'a> {
    fn has_symbol_table(&self) -> bool {
        self.has_symbols()
    }

    fn has_debug_info(&self) -> bool {
        match self.target {
            ObjectTarget::Elf(..) => self.has_dwarf_data(),
            ObjectTarget::MachOSingle(..) => self.has_dwarf_data(),
            ObjectTarget::MachOFat(..) => self.has_dwarf_data(),
            ObjectTarget::Breakpad(..) => has_breakpad_record(self, b"FUNC"),
        }
    }

    fn has_unwind_info(&self) -> bool {
        match self.target {
            ObjectTarget::Elf(..) => has_dwarf_unwind_info(self),
            ObjectTarget::MachOSingle(..) => has_dwarf_unwind_info(self),
            ObjectTarget::MachOFat(..) => has_dwarf_unwind_info(self),
            ObjectTarget::Breakpad(..) => has_breakpad_record(self, b"STACK"),
        }
    }

    fn has_mapping(&self) -> bool {
        // Added for future proofing
        false
    }

    fn has_feature(&self, feature: ObjectFeature) -> bool {
        match feature {
            ObjectFeature::SymbolTable => self.has_symbol_table(),
            ObjectFeature::DebugInfo => self.has_debug_info(),
            ObjectFeature::UnwindInfo => self.has_unwind_info(),
            ObjectFeature::Mapping => self.has_mapping(),
        }
    }

    fn features(&self) -> BTreeSet<ObjectFeature> {
        let mut features = BTreeSet::new();

        if self.has_feature(ObjectFeature::SymbolTable) {
            features.insert(ObjectFeature::SymbolTable);
        }

        if self.has_feature(ObjectFeature::DebugInfo) {
            features.insert(ObjectFeature::DebugInfo);
        }

        if self.has_feature(ObjectFeature::UnwindInfo) {
            features.insert(ObjectFeature::UnwindInfo);
        }

        if self.has_feature(ObjectFeature::Mapping) {
            features.insert(ObjectFeature::Mapping);
        }

        features
    }
}