LCOV - code coverage report
Current view: top level - src/elf - binary.rs (source / functions) Coverage Total Hit
Test: lief.lcov Lines: 89.4 % 227 203
Test Date: 2025-02-23:00:00:00 Functions: 68.4 % 76 52

            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 : );
        

Generated by: LCOV version 2.1-1