Line data Source code
1 : use std::mem::size_of;
2 : use std::path::Path;
3 : use std::pin::Pin;
4 :
5 : use num_traits::{cast, Num};
6 :
7 : use lief_ffi as ffi;
8 :
9 : use super::builder::Config;
10 : use super::dynamic::{self, DynamicEntries, Library};
11 : use super::hash::{Gnu, Sysv};
12 : use super::header::Header;
13 : use super::note::ItNotes;
14 : use super::relocation::{
15 : DynamicRelocations, ObjectRelocations, PltGotRelocations, Relocation, Relocations,
16 : };
17 : use super::section::{Section, Sections};
18 : use super::segment::Segments;
19 : use super::symbol::{DynamicSymbols, ExportedSymbols, ImportedSymbols, SymtabSymbols};
20 : use super::symbol_versioning::{SymbolVersion, SymbolVersionDefinition, SymbolVersionRequirement};
21 : use super::{Segment, Symbol};
22 : use crate::Error;
23 :
24 : use crate::common::{into_optional, FromFFI};
25 : use crate::generic;
26 : use crate::{declare_iterator, to_conv_result, to_result, to_slice};
27 :
28 0 : #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
29 : pub enum ElfClass {
30 : Elf32,
31 : Elf64,
32 : Unknown,
33 : }
34 :
35 : impl ElfClass {
36 : const ELF_CLASS32: u32 = 1;
37 : const ELF_CLASS64: u32 = 2;
38 :
39 0 : pub fn from_value(value: u32) -> Self {
40 0 : match value {
41 0 : Self::ELF_CLASS32 => ElfClass::Elf32,
42 0 : Self::ELF_CLASS64 => ElfClass::Elf64,
43 0 : _ => ElfClass::Unknown,
44 : }
45 0 : }
46 : }
47 :
48 : /// This is the main interface to read and write ELF binary attributes.
49 : ///
50 : /// Note that this structure implements the [`generic::Binary`] trait from which other generic
51 : /// functions are exposed
52 : pub struct Binary {
53 : ptr: cxx::UniquePtr<ffi::ELF_Binary>,
54 : }
55 :
56 : impl std::fmt::Debug for Binary {
57 80 : fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 80 : f.debug_struct("Binary")
59 80 : .field("header", &self.header())
60 80 : .finish()
61 80 : }
62 : }
63 :
64 : impl FromFFI<ffi::ELF_Binary> for Binary {
65 180 : fn from_ffi(ptr: cxx::UniquePtr<ffi::ELF_Binary>) -> Self {
66 180 : Self { ptr }
67 180 : }
68 : }
69 :
70 : impl Binary {
71 : /// Create a [`Binary`] from the given file path
72 100 : pub fn parse(path: &str) -> Option<Self> {
73 100 : let bin = ffi::ELF_Binary::parse(path);
74 100 : if bin.is_null() {
75 0 : return None;
76 100 : }
77 100 : Some(Binary::from_ffi(bin))
78 100 : }
79 :
80 : /// Return the main ELF header
81 80 : pub fn header(&self) -> Header {
82 80 : Header::from_ffi(self.ptr.header())
83 80 : }
84 :
85 : /// Return the size taken by the binary when loaded (virtual size)
86 80 : pub fn virtual_size(&self) -> u64 {
87 80 : self.ptr.virtual_size()
88 80 : }
89 :
90 : /// Return the path to the ELF interpreter that is used to process the ELF information
91 : /// once loaded by the kernel
92 80 : pub fn interpreter(&self) -> String {
93 80 : self.ptr.interpreter().to_string()
94 80 : }
95 :
96 : /// Return sysv-hash information (if present)
97 80 : pub fn sysv_hash(&self) -> Option<Sysv> {
98 80 : into_optional(self.ptr.sysv_hash())
99 80 : }
100 :
101 : /// Return GNU Hash info (if present)
102 80 : pub fn gnu_hash(&self) -> Option<Gnu> {
103 80 : into_optional(self.ptr.gnu_hash())
104 80 : }
105 :
106 : /// Return an iterator over the [`crate::elf::Section`] of the binary
107 80 : pub fn sections(&self) -> Sections {
108 80 : Sections::new(self.ptr.sections())
109 80 : }
110 :
111 : /// Return an iterator over the [`crate::elf::Segment`] of the binary
112 80 : pub fn segments(&self) -> Segments {
113 80 : Segments::new(self.ptr.segments())
114 80 : }
115 :
116 : /// Return an iterator over the [`crate::elf::DynamicEntries`] of the binary
117 80 : pub fn dynamic_entries(&self) -> DynamicEntries {
118 80 : DynamicEntries::new(self.ptr.dynamic_entries())
119 80 : }
120 :
121 : /// Return an iterator over the dynamic [`crate::elf::Symbol`] of the binary
122 80 : pub fn dynamic_symbols(&self) -> DynamicSymbols {
123 80 : DynamicSymbols::new(self.ptr.dynamic_symbols())
124 80 : }
125 :
126 : /// Return an iterator over the **exported** [`crate::elf::Symbol`] of the binary
127 80 : pub fn exported_symbols(&self) -> ExportedSymbols {
128 80 : ExportedSymbols::new(self.ptr.exported_symbols())
129 80 : }
130 :
131 : /// Return an iterator over the **imported** [`crate::elf::Symbol`] of the binary
132 80 : pub fn imported_symbols(&self) -> ImportedSymbols {
133 80 : ImportedSymbols::new(self.ptr.imported_symbols())
134 80 : }
135 :
136 : /// Return an iterator over the symtab-debug [`crate::elf::Symbol`] of the binary
137 80 : pub fn symtab_symbols(&self) -> SymtabSymbols {
138 80 : SymtabSymbols::new(self.ptr.symtab_symbols())
139 80 : }
140 :
141 : /// Return an iterator over the [`crate::elf::SymbolVersion`] of the binary
142 160 : pub fn symbols_version(&self) -> SymbolsVersion {
143 160 : SymbolsVersion::new(self.ptr.symbols_version())
144 160 : }
145 :
146 : /// Return an iterator over the [`crate::elf::SymbolVersionRequirement`] of the binary
147 80 : pub fn symbols_version_requirement(&self) -> SymbolsVersionRequirement {
148 80 : SymbolsVersionRequirement::new(self.ptr.symbols_version_requirement())
149 80 : }
150 :
151 : /// Return an iterator over the [`crate::elf::SymbolVersionDefinition`] of the binary
152 80 : pub fn symbols_version_definition(&self) -> SymbolsVersionDefinition {
153 80 : SymbolsVersionDefinition::new(self.ptr.symbols_version_definition())
154 80 : }
155 :
156 : /// Return an iterator over the [`crate::elf::Notes`] of the binary
157 80 : pub fn notes(&self) -> ItNotes {
158 80 : ItNotes::new(self.ptr.notes())
159 80 : }
160 :
161 : /// Return an iterator over the `.plt.got` [`crate::elf::Relocation`] of the binary
162 80 : pub fn pltgot_relocations(&self) -> PltGotRelocations {
163 80 : PltGotRelocations::new(self.ptr.pltgot_relocations())
164 80 : }
165 :
166 : /// Return an iterator over the regular [`crate::elf::Relocation`] of the binary
167 80 : pub fn dynamic_relocations(&self) -> DynamicRelocations {
168 80 : DynamicRelocations::new(self.ptr.dynamic_relocations())
169 80 : }
170 :
171 : /// Return an iterator over the object-file (`.o`) [`crate::elf::Relocation`]
172 80 : pub fn object_relocations(&self) -> ObjectRelocations {
173 80 : ObjectRelocations::new(self.ptr.object_relocations())
174 80 : }
175 :
176 : /// Return an iterator over **all** [`crate::elf::Relocation`] of the binary
177 80 : pub fn relocations(&self) -> Relocations {
178 80 : Relocations::new(self.ptr.relocations())
179 80 : }
180 :
181 : /// Try to find the ELF section with the given name
182 80 : pub fn section_by_name(&self, name: &str) -> Option<Section> {
183 80 : into_optional(self.ptr.section_by_name(name))
184 80 : }
185 :
186 : /// Try to find the ELF relocation that takes place at the given address
187 20 : pub fn relocation_by_addr(&self, address: u64) -> Option<Relocation> {
188 20 : into_optional(self.ptr.relocation_by_addr(address))
189 20 : }
190 :
191 : /// Try to find the `.plt.got` relocation for the given symbol name
192 20 : pub fn relocation_for_symbol(&self, sym_name: &str) -> Option<Relocation> {
193 20 : into_optional(self.ptr.relocation_for_symbol(sym_name))
194 20 : }
195 :
196 : /// Try to find the symbol with the given name in the dynamic `.dynsym` table
197 10 : pub fn dynamic_symbol_by_name(&self, sym_name: &str) -> Option<Symbol> {
198 10 : into_optional(self.ptr.get_dynamic_symbol(sym_name))
199 10 : }
200 :
201 : /// Try to find the symbol with the given name in the debug `.symtab` table
202 10 : pub fn symtab_symbol_by_name(&self, sym_name: &str) -> Option<Symbol> {
203 10 : into_optional(self.ptr.get_symtab_symbol(sym_name))
204 10 : }
205 :
206 : /// Try to find the library (`DT_NEEDED`) with the given name
207 20 : pub fn get_library(&self, name: &str) -> Option<dynamic::Library> {
208 20 : into_optional(self.ptr.get_library(name))
209 20 : }
210 :
211 : /// Try to find the section that encompasses the given offset. `skip_nobits` can be used
212 : /// to include (or not) the `SHT_NOTBIT` sections
213 20 : pub fn section_from_offset(&self, offset: u64, skip_nobits: bool) -> Option<Section> {
214 20 : into_optional(self.ptr.section_from_offset(offset, skip_nobits))
215 20 : }
216 :
217 : /// Try to find the section that encompasses the given virtual address. `skip_nobits` can be used
218 : /// to include (or not) the `SHT_NOTBIT` sections
219 20 : pub fn section_from_virtual_address(&self, address: u64, skip_nobits: bool) -> Option<Section> {
220 20 : into_optional(self.ptr.section_from_virtual_address(address, skip_nobits))
221 20 : }
222 :
223 : /// Try to find the segment that encompasses the given virtual address
224 20 : pub fn segment_from_virtual_address(&self, address: u64) -> Option<Segment> {
225 20 : into_optional(self.ptr.segment_from_virtual_address(address))
226 20 : }
227 :
228 : /// Try to find the segment that encompasses the given offset
229 20 : pub fn segment_from_offset(&self, offset: u64) -> Option<Segment> {
230 20 : into_optional(self.ptr.segment_from_offset(offset))
231 20 : }
232 :
233 : /// Get a slice of the content at the given address.
234 10 : pub fn content_from_virtual_address(&self, address: u64, size: u64) -> &[u8] {
235 10 : to_slice!(self.ptr.get_content_from_virtual_address(address, size));
236 10 : }
237 :
238 : /// Convert the given virtual address into an offset
239 20 : pub fn virtual_address_to_offset(&self, address: u64) -> Result<u64, Error> {
240 20 : to_result!(ffi::ELF_Binary::virtual_address_to_offset, &self, address);
241 20 : }
242 :
243 : /// Return the array defined by the given tag (e.g.
244 : /// [`dynamic::Tag::INIT_ARRAY`]) with relocations applied (if any)
245 240 : pub fn get_relocated_dynamic_array(&self, tag: dynamic::Tag) -> Vec<u64> {
246 240 : Vec::from(
247 240 : self.ptr
248 240 : .get_relocated_dynamic_array(u64::from(tag))
249 240 : .as_slice(),
250 240 : )
251 240 : }
252 :
253 : /// True if the current binary is targeting Android
254 0 : pub fn is_targeting_android(&self) -> bool {
255 0 : self.ptr.is_targeting_android()
256 0 : }
257 :
258 : /// Get the integer value at the given virtual address
259 4 : pub fn get_int_from_virtual_address<T>(&self, addr: u64) -> Result<T, Error>
260 4 : where
261 4 : T: Num + cast::FromPrimitive + cast::ToPrimitive,
262 4 : {
263 4 : // Can't be in the generic trait because of:
264 4 : // > for a trait to be "object safe" it needs to allow building a vtable to allow the call
265 4 : // > to be resolvable dynamically; for more information visit
266 4 : // > https://doc.rust-lang.org/reference/items/traits.html#object-safety
267 4 : if size_of::<T>() == size_of::<u8>() {
268 1 : to_conv_result!(
269 1 : ffi::AbstractBinary::get_u8,
270 1 : self.ptr.as_ref().unwrap().as_ref(),
271 1 : |value| {
272 1 : T::from_u8(value).expect(format!("Can't cast value: {}", value).as_str())
273 1 : },
274 1 : addr
275 : );
276 3 : }
277 3 :
278 3 : if size_of::<T>() == size_of::<u16>() {
279 1 : to_conv_result!(
280 1 : ffi::AbstractBinary::get_u16,
281 1 : self.ptr.as_ref().unwrap().as_ref(),
282 1 : |value| {
283 1 : T::from_u16(value).expect(format!("Can't cast value: {}", value).as_str())
284 1 : },
285 1 : addr
286 : );
287 2 : }
288 2 :
289 2 : if size_of::<T>() == size_of::<u32>() {
290 1 : to_conv_result!(
291 1 : ffi::AbstractBinary::get_u32,
292 1 : self.ptr.as_ref().unwrap().as_ref(),
293 1 : |value| {
294 1 : T::from_u32(value).expect(format!("Can't cast value: {}", value).as_str())
295 1 : },
296 1 : addr
297 : );
298 1 : }
299 1 :
300 1 : if size_of::<T>() == size_of::<u64>() {
301 1 : to_conv_result!(
302 1 : ffi::AbstractBinary::get_u64,
303 1 : self.ptr.as_ref().unwrap().as_ref(),
304 1 : |value| {
305 1 : T::from_u64(value).expect(format!("Can't cast value: {}", value).as_str())
306 1 : },
307 1 : addr
308 : );
309 0 : }
310 0 :
311 0 : Err(Error::NotSupported)
312 4 : }
313 :
314 : /// Write back the current ELF binary into the file specified in parameter
315 10 : pub fn write(&mut self, output: &Path) {
316 10 : self.ptr.as_mut().unwrap().write(output.to_str().unwrap());
317 10 : }
318 :
319 : /// Write back the current ELF binary into the file specified in parameter with the
320 : /// configuration provided in the second parameter.
321 10 : pub fn write_with_config(&mut self, output: &Path, config: Config) {
322 10 : self.ptr
323 10 : .as_mut()
324 10 : .unwrap()
325 10 : .write_with_config(output.to_str().unwrap(), config.to_ffi());
326 10 : }
327 :
328 : /// Add a library as dependency
329 10 : pub fn add_library<'a>(&'a mut self, library: &str) -> Library<'a> {
330 10 : Library::from_ffi(self.ptr.as_mut().unwrap().add_library(library))
331 10 : }
332 :
333 80 : pub fn functions(&self) -> generic::Functions {
334 80 : generic::Functions::new(self.ptr.functions())
335 80 : }
336 : }
337 :
338 : impl generic::Binary for Binary {
339 400 : fn as_generic(&self) -> &ffi::AbstractBinary {
340 400 : self.ptr.as_ref().unwrap().as_ref()
341 400 : }
342 :
343 0 : fn as_pin_mut_generic(&mut self) -> Pin<&mut ffi::AbstractBinary> {
344 0 : unsafe {
345 0 : Pin::new_unchecked({
346 0 : (self.ptr.as_ref().unwrap().as_ref() as *const ffi::AbstractBinary
347 0 : as *mut ffi::AbstractBinary)
348 0 : .as_mut()
349 0 : .unwrap()
350 0 : })
351 0 : }
352 0 : }
353 : }
354 :
355 15540 : declare_iterator!(
356 15540 : SymbolsVersion,
357 15540 : SymbolVersion<'a>,
358 15540 : ffi::ELF_SymbolVersion,
359 15540 : ffi::ELF_Binary,
360 15540 : ffi::ELF_Binary_it_symbols_version
361 15540 : );
362 150 : declare_iterator!(
363 150 : SymbolsVersionRequirement,
364 150 : SymbolVersionRequirement<'a>,
365 150 : ffi::ELF_SymbolVersionRequirement,
366 150 : ffi::ELF_Binary,
367 150 : ffi::ELF_Binary_it_symbols_version_requirement
368 150 : );
369 30 : declare_iterator!(
370 30 : SymbolsVersionDefinition,
371 30 : SymbolVersionDefinition<'a>,
372 30 : ffi::ELF_SymbolVersionDefinition,
373 30 : ffi::ELF_Binary,
374 30 : ffi::ELF_Binary_it_symbols_version_definition
375 30 : );
|