1 use crate::alloc::borrow::Cow;
2 use crate::{Relocation, SectionIndex, SectionKind, Symbol, SymbolIndex, SymbolMap};
3 use target_lexicon::{Architecture, Endianness};
4 use uuid::Uuid;
5 
6 /// An object file.
7 pub trait Object<'data, 'file> {
8     /// A segment in the object file.
9     type Segment: ObjectSegment<'data>;
10 
11     /// An iterator over the segments in the object file.
12     type SegmentIterator: Iterator<Item = Self::Segment>;
13 
14     /// A section in the object file.
15     type Section: ObjectSection<'data>;
16 
17     /// An iterator over the sections in the object file.
18     type SectionIterator: Iterator<Item = Self::Section>;
19 
20     /// An iterator over the symbols in the object file.
21     type SymbolIterator: Iterator<Item = (SymbolIndex, Symbol<'data>)>;
22 
23     /// Get the architecture type of the file.
architecture(&self) -> Architecture24     fn architecture(&self) -> Architecture;
25 
26     /// Get the endianness of the file.
27     #[inline]
endianness(&self) -> Endianness28     fn endianness(&self) -> Endianness {
29         if self.is_little_endian() {
30             Endianness::Little
31         } else {
32             Endianness::Big
33         }
34     }
35 
36     /// Return true if the file is little endian, false if it is big endian.
is_little_endian(&self) -> bool37     fn is_little_endian(&self) -> bool;
38 
39     /// Return true if the file can contain 64-bit addresses.
is_64(&self) -> bool40     fn is_64(&self) -> bool;
41 
42     /// Get an iterator over the segments in the file.
segments(&'file self) -> Self::SegmentIterator43     fn segments(&'file self) -> Self::SegmentIterator;
44 
45     /// Get the entry point address of the binary
entry(&'file self) -> u6446     fn entry(&'file self) -> u64;
47 
48     /// Get the section named `section_name`, if such a section exists.
49     ///
50     /// If `section_name` starts with a '.' then it is treated as a system section name,
51     /// and is compared using the conventions specific to the object file format. This
52     /// includes:
53     /// - if ".text" is requested for a Mach-O object file, then the actual
54     /// section name that is searched for is "__text".
55     /// - if ".debug_info" is requested for an ELF object file, then
56     /// ".zdebug_info" may be returned (and similarly for other debug sections).
57     ///
58     /// For some object files, multiple segments may contain sections with the same
59     /// name. In this case, the first matching section will be used.
section_by_name(&'file self, section_name: &str) -> Option<Self::Section>60     fn section_by_name(&'file self, section_name: &str) -> Option<Self::Section>;
61 
62     /// Get the section at the given index.
63     ///
64     /// The meaning of the index depends on the object file.
65     ///
66     /// For some object files, this requires iterating through all sections.
section_by_index(&'file self, index: SectionIndex) -> Option<Self::Section>67     fn section_by_index(&'file self, index: SectionIndex) -> Option<Self::Section>;
68 
69     /// Get the contents of the section named `section_name`, if such
70     /// a section exists.
71     ///
72     /// The `section_name` is interpreted according to `Self::section_by_name`.
73     ///
74     /// This may decompress section data.
section_data_by_name(&'file self, section_name: &str) -> Option<Cow<'data, [u8]>>75     fn section_data_by_name(&'file self, section_name: &str) -> Option<Cow<'data, [u8]>> {
76         self.section_by_name(section_name)
77             .map(|section| section.uncompressed_data())
78     }
79 
80     /// Get an iterator over the sections in the file.
sections(&'file self) -> Self::SectionIterator81     fn sections(&'file self) -> Self::SectionIterator;
82 
83     /// Get the debugging symbol at the given index.
84     ///
85     /// This is similar to `self.symbols().nth(index)`, except that
86     /// the index will take into account malformed or unsupported symbols.
symbol_by_index(&self, index: SymbolIndex) -> Option<Symbol<'data>>87     fn symbol_by_index(&self, index: SymbolIndex) -> Option<Symbol<'data>>;
88 
89     /// Get an iterator over the debugging symbols in the file.
90     ///
91     /// This may skip over symbols that are malformed or unsupported.
symbols(&'file self) -> Self::SymbolIterator92     fn symbols(&'file self) -> Self::SymbolIterator;
93 
94     /// Get the data for the given symbol.
symbol_data(&'file self, symbol: &Symbol<'data>) -> Option<&'data [u8]>95     fn symbol_data(&'file self, symbol: &Symbol<'data>) -> Option<&'data [u8]> {
96         if symbol.is_undefined() {
97             return None;
98         }
99         let address = symbol.address();
100         let size = symbol.size();
101         if let Some(index) = symbol.section_index() {
102             self.section_by_index(index)
103                 .and_then(|section| section.data_range(address, size))
104         } else {
105             self.segments()
106                 .find_map(|segment| segment.data_range(address, size))
107         }
108     }
109 
110     /// Get an iterator over the dynamic linking symbols in the file.
111     ///
112     /// This may skip over symbols that are malformed or unsupported.
dynamic_symbols(&'file self) -> Self::SymbolIterator113     fn dynamic_symbols(&'file self) -> Self::SymbolIterator;
114 
115     /// Construct a map from addresses to symbols.
symbol_map(&self) -> SymbolMap<'data>116     fn symbol_map(&self) -> SymbolMap<'data>;
117 
118     /// Return true if the file contains debug information sections, false if not.
has_debug_symbols(&self) -> bool119     fn has_debug_symbols(&self) -> bool;
120 
121     /// The UUID from a Mach-O `LC_UUID` load command.
122     #[inline]
mach_uuid(&self) -> Option<Uuid>123     fn mach_uuid(&self) -> Option<Uuid> {
124         None
125     }
126 
127     /// The build ID from an ELF `NT_GNU_BUILD_ID` note.
128     #[inline]
build_id(&self) -> Option<&'data [u8]>129     fn build_id(&self) -> Option<&'data [u8]> {
130         None
131     }
132 
133     /// The filename and CRC from a `.gnu_debuglink` section.
134     #[inline]
gnu_debuglink(&self) -> Option<(&'data [u8], u32)>135     fn gnu_debuglink(&self) -> Option<(&'data [u8], u32)> {
136         None
137     }
138 }
139 
140 /// A loadable segment defined in an object file.
141 ///
142 /// For ELF, this is a program header with type `PT_LOAD`.
143 /// For Mach-O, this is a load command with type `LC_SEGMENT` or `LC_SEGMENT_64`.
144 pub trait ObjectSegment<'data> {
145     /// Returns the virtual address of the segment.
address(&self) -> u64146     fn address(&self) -> u64;
147 
148     /// Returns the size of the segment in memory.
size(&self) -> u64149     fn size(&self) -> u64;
150 
151     /// Returns the alignment of the segment in memory.
align(&self) -> u64152     fn align(&self) -> u64;
153 
154     /// Returns the offset and size of the segment in the file.
file_range(&self) -> (u64, u64)155     fn file_range(&self) -> (u64, u64);
156 
157     /// Returns a reference to the file contents of the segment.
158     /// The length of this data may be different from the size of the
159     /// segment in memory.
data(&self) -> &'data [u8]160     fn data(&self) -> &'data [u8];
161 
162     /// Return the segment data in the given range.
data_range(&self, address: u64, size: u64) -> Option<&'data [u8]>163     fn data_range(&self, address: u64, size: u64) -> Option<&'data [u8]>;
164 
165     /// Returns the name of the segment.
name(&self) -> Option<&str>166     fn name(&self) -> Option<&str>;
167 }
168 
169 /// A section defined in an object file.
170 pub trait ObjectSection<'data> {
171     /// An iterator over the relocations for a section.
172     ///
173     /// The first field in the item tuple is the section offset
174     /// that the relocation applies to.
175     type RelocationIterator: Iterator<Item = (u64, Relocation)>;
176 
177     /// Returns the section index.
index(&self) -> SectionIndex178     fn index(&self) -> SectionIndex;
179 
180     /// Returns the address of the section.
address(&self) -> u64181     fn address(&self) -> u64;
182 
183     /// Returns the size of the section in memory.
size(&self) -> u64184     fn size(&self) -> u64;
185 
186     /// Returns the alignment of the section in memory.
align(&self) -> u64187     fn align(&self) -> u64;
188 
189     /// Returns offset and size of on-disk segment (if any)
file_range(&self) -> Option<(u64, u64)>190     fn file_range(&self) -> Option<(u64, u64)>;
191 
192     /// Returns the raw contents of the section.
193     /// The length of this data may be different from the size of the
194     /// section in memory.
195     ///
196     /// This does not do any decompression.
data(&self) -> Cow<'data, [u8]>197     fn data(&self) -> Cow<'data, [u8]>;
198 
199     /// Return the raw contents of the section data in the given range.
200     ///
201     /// This does not do any decompression.
data_range(&self, address: u64, size: u64) -> Option<&'data [u8]>202     fn data_range(&self, address: u64, size: u64) -> Option<&'data [u8]>;
203 
204     /// Returns the uncompressed contents of the section.
205     /// The length of this data may be different from the size of the
206     /// section in memory.
uncompressed_data(&self) -> Cow<'data, [u8]>207     fn uncompressed_data(&self) -> Cow<'data, [u8]>;
208 
209     /// Returns the name of the section.
name(&self) -> Option<&str>210     fn name(&self) -> Option<&str>;
211 
212     /// Returns the name of the segment for this section.
segment_name(&self) -> Option<&str>213     fn segment_name(&self) -> Option<&str>;
214 
215     /// Return the kind of this section.
kind(&self) -> SectionKind216     fn kind(&self) -> SectionKind;
217 
218     /// Get the relocations for this section.
relocations(&self) -> Self::RelocationIterator219     fn relocations(&self) -> Self::RelocationIterator;
220 }
221