LCOV - code coverage report
Current view: top level - src/macho - binary.rs (source / functions) Coverage Total Hit
Test: lief.lcov Lines: 72.6 % 201 146
Test Date: 2025-02-23:00:00:00 Functions: 83.6 % 55 46

            Line data    Source code
       1              : use std::mem::size_of;
       2              : use std::pin::Pin;
       3              : use std::path::Path;
       4              : use num_traits::{Num, cast};
       5              : 
       6              : use crate::Error;
       7              : use super::builder::Config;
       8              : use super::commands::build_version::{BuildVersion, Platform};
       9              : use super::commands::code_signature::CodeSignature;
      10              : use super::commands::code_signature_dir::CodeSignatureDir;
      11              : use super::commands::data_in_code::DataInCode;
      12              : use super::commands::dyld_chained_fixups::DyldChainedFixups;
      13              : use super::commands::dyld_environment::DyldEnvironment;
      14              : use super::commands::dyld_export_trie::DyldExportsTrie;
      15              : use super::commands::dyldinfo::DyldInfo;
      16              : use super::commands::dylib::Libraries;
      17              : use super::commands::dylinker::Dylinker;
      18              : use super::commands::dynamic_symbol_command::DynamicSymbolCommand;
      19              : use super::commands::encryption_info::EncryptionInfo;
      20              : use super::commands::functionstarts::FunctionStarts;
      21              : use super::commands::linker_opt_hint::LinkerOptHint;
      22              : use super::commands::atom_info::AtomInfo;
      23              : use super::commands::main_cmd::Main;
      24              : use super::commands::rpath::RPath;
      25              : use super::commands::routine::Routine;
      26              : use super::commands::segment::Segments;
      27              : use super::commands::segment_split_info::SegmentSplitInfo;
      28              : use super::commands::source_version::SourceVersion;
      29              : use super::commands::sub_framework::SubFramework;
      30              : use super::commands::sub_client::SubClients;
      31              : use super::commands::symbol_command::SymbolCommand;
      32              : use super::commands::thread_command::ThreadCommand;
      33              : use super::commands::two_level_hints::TwoLevelHints;
      34              : use super::commands::uuid::UUID;
      35              : use super::commands::version_min::VersionMin;
      36              : use super::commands::{CommandsIter, Dylib};
      37              : use super::header::Header;
      38              : use super::relocation::Relocations;
      39              : use super::section::Sections;
      40              : use super::symbol::Symbols;
      41              : use super::binding_info::BindingInfo;
      42              : use super::stub::Stub;
      43              : use lief_ffi as ffi;
      44              : 
      45              : use crate::common::{into_optional, FromFFI};
      46              : use crate::{generic, declare_fwd_iterator, declare_iterator, to_conv_result};
      47              : use crate::objc::Metadata;
      48              : 
      49              : /// This is the main interface to read and write Mach-O binary attributes.
      50              : ///
      51              : /// Note that this structure implements the [`generic::Binary`] trait from which other generic
      52              : /// functions are exposed
      53              : pub struct Binary {
      54              :     ptr: cxx::UniquePtr<ffi::MachO_Binary>,
      55              : }
      56              : 
      57              : impl std::fmt::Debug for Binary {
      58          140 :     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
      59          140 :         f.debug_struct("Binary").finish()
      60          140 :     }
      61              : }
      62              : 
      63              : impl FromFFI<ffi::MachO_Binary> for Binary {
      64          170 :     fn from_ffi(ptr: cxx::UniquePtr<ffi::MachO_Binary>) -> Self {
      65          170 :         Binary { ptr }
      66          170 :     }
      67              : }
      68              : 
      69              : impl Binary {
      70              :     /// Return the main Mach-O header
      71          420 :     pub fn header(&self) -> Header {
      72          420 :         Header::from_ffi(self.ptr.header())
      73          420 :     }
      74              : 
      75              :     /// Return an iterator over the different [`crate::macho::Commands`] used by the
      76              :     /// Mach-O binary
      77          140 :     pub fn commands(&self) -> CommandsIter {
      78          140 :         CommandsIter::new(self.ptr.commands())
      79          140 :     }
      80              : 
      81              :     /// Return an iterator over the different [`crate::macho::Section`] of the binary
      82          140 :     pub fn sections(&self) -> Sections {
      83          140 :         Sections::new(self.ptr.sections())
      84          140 :     }
      85              : 
      86              :     /// Return an iterator over the different [`crate::macho::commands::Segment`] (`LC_SEGMENT/LC_SIGNATURE`)
      87              :     /// of the binary.
      88          140 :     pub fn segments(&self) -> Segments {
      89          140 :         Segments::new(self.ptr.segments())
      90          140 :     }
      91              : 
      92              :     /// Return an iterator over the [`crate::macho::commands::Dylib`] used by this binary
      93          140 :     pub fn libraries(&self) -> Libraries {
      94          140 :         Libraries::new(self.ptr.libraries())
      95          140 :     }
      96              : 
      97              :     /// Return an iterator over the different [`crate::macho::Relocation`] of this binary
      98          140 :     pub fn relocations(&self) -> Relocations {
      99          140 :         Relocations::new(self.ptr.relocations())
     100          140 :     }
     101              : 
     102              :     /// Return an iterator over the different [`crate::macho::Symbol`] of this binary
     103          140 :     pub fn symbols(&self) -> Symbols {
     104          140 :         Symbols::new(self.ptr.symbols())
     105          140 :     }
     106              : 
     107              :     /// Return the `LC_DYLD_INFO/LC_DYLD_INFO_ONLY` command if present
     108          140 :     pub fn dyld_info(&self) -> Option<DyldInfo> {
     109          140 :         into_optional(self.ptr.dyld_info())
     110          140 :     }
     111              : 
     112              :     /// Return the `LC_UUID` command if present
     113          140 :     pub fn uuid(&self) -> Option<UUID> {
     114          140 :         into_optional(self.ptr.uuid())
     115          140 :     }
     116              : 
     117              :     /// Return the `LC_MAIN` command if present
     118          140 :     pub fn main_command(&self) -> Option<Main> {
     119          140 :         into_optional(self.ptr.main_command())
     120          140 :     }
     121              : 
     122              :     /// Return the `LC_LOAD_DYLINKER/LC_ID_DYLINKER` command if present
     123          140 :     pub fn dylinker(&self) -> Option<Dylinker> {
     124          140 :         into_optional(self.ptr.dylinker())
     125          140 :     }
     126              : 
     127              :     /// Return the `LC_FUNCTION_STARTS` command if present
     128          140 :     pub fn function_starts(&self) -> Option<FunctionStarts> {
     129          140 :         into_optional(self.ptr.function_starts())
     130          140 :     }
     131              : 
     132              :     /// Return the `LC_SOURCE_VERSION` command if present
     133          140 :     pub fn source_version(&self) -> Option<SourceVersion> {
     134          140 :         into_optional(self.ptr.source_version())
     135          140 :     }
     136              : 
     137              :     /// Return the `LC_THREAD/LC_UNIXTHREAD` command if present
     138          140 :     pub fn thread_command(&self) -> Option<ThreadCommand> {
     139          140 :         into_optional(self.ptr.thread_command())
     140          140 :     }
     141              : 
     142              :     /// Return the `LC_RPATH` command if present
     143          140 :     pub fn rpath(&self) -> Option<RPath> {
     144          140 :         into_optional(self.ptr.rpath())
     145          140 :     }
     146              : 
     147              :     /// Return the `LC_ROUTINE/LC_ROUTINE64` command if present
     148          140 :     pub fn routine(&self) -> Option<Routine> {
     149          140 :         into_optional(self.ptr.routine_command())
     150          140 :     }
     151              : 
     152              :     /// Return the `LC_SYMTAB` command if present
     153          140 :     pub fn symbol_command(&self) -> Option<SymbolCommand> {
     154          140 :         into_optional(self.ptr.symbol_command())
     155          140 :     }
     156              : 
     157              :     /// Return the `LC_DYSYMTAB` command if present
     158          140 :     pub fn dynamic_symbol(&self) -> Option<DynamicSymbolCommand> {
     159          140 :         into_optional(self.ptr.dynamic_symbol_command())
     160          140 :     }
     161              : 
     162              :     /// Return the `LC_CODE_SIGNATURE` command if present
     163          140 :     pub fn code_signature(&self) -> Option<CodeSignature> {
     164          140 :         into_optional(self.ptr.code_signature())
     165          140 :     }
     166              : 
     167              :     /// Return the `LC_DYLIB_CODE_SIGN_DRS` command if present
     168          140 :     pub fn code_signature_dir(&self) -> Option<CodeSignatureDir> {
     169          140 :         into_optional(self.ptr.code_signature_dir())
     170          140 :     }
     171              : 
     172              :     /// Return the `LC_DATA_IN_CODE` command if present
     173          140 :     pub fn data_in_code(&self) -> Option<DataInCode> {
     174          140 :         into_optional(self.ptr.data_in_code())
     175          140 :     }
     176              : 
     177              :     /// Return the `LC_SEGMENT_SPLIT_INFO` command if present
     178          140 :     pub fn segment_split_info(&self) -> Option<SegmentSplitInfo> {
     179          140 :         into_optional(self.ptr.segment_split_info())
     180          140 :     }
     181              : 
     182              :     /// Return the `LC_ENCRYPTION_INFO/LC_ENCRYPTION_INFO_64` command if present
     183          140 :     pub fn encryption_info(&self) -> Option<EncryptionInfo> {
     184          140 :         into_optional(self.ptr.encryption_info())
     185          140 :     }
     186              : 
     187              :     /// Return the `LC_SUB_FRAMEWORK` command if present
     188          140 :     pub fn sub_framework(&self) -> Option<SubFramework> {
     189          140 :         into_optional(self.ptr.sub_framework())
     190          140 :     }
     191              : 
     192              :     /// Return the `LC_SUBCLIENT` command if present
     193          140 :     pub fn subclients(&self) -> SubClients {
     194          140 :         SubClients::new(self.ptr.subclients())
     195          140 :     }
     196              : 
     197              :     /// Return the `LC_DYLD_ENVIRONMENT` command if present
     198          140 :     pub fn dyld_environment(&self) -> Option<DyldEnvironment> {
     199          140 :         into_optional(self.ptr.dyld_environment())
     200          140 :     }
     201              : 
     202              :     /// Return the `LC_BUILD_VERSION` command if present
     203          140 :     pub fn build_version(&self) -> Option<BuildVersion> {
     204          140 :         into_optional(self.ptr.build_version())
     205          140 :     }
     206              : 
     207              :     /// Return the `LC_DYLD_CHAINED_FIXUPS` command if present
     208          140 :     pub fn dyld_chained_fixups(&self) -> Option<DyldChainedFixups> {
     209          140 :         into_optional(self.ptr.dyld_chained_fixups())
     210          140 :     }
     211              : 
     212              :     /// Return the `LC_DYLD_EXPORTS_TRIE` command if present
     213          140 :     pub fn dyld_exports_trie(&self) -> Option<DyldExportsTrie> {
     214          140 :         into_optional(self.ptr.dyld_exports_trie())
     215          140 :     }
     216              : 
     217              :     /// Return the `LC_TWOLEVEL_HINTS` command if present
     218          140 :     pub fn two_level_hints(&self) -> Option<TwoLevelHints> {
     219          140 :         into_optional(self.ptr.two_level_hints())
     220          140 :     }
     221              : 
     222              :     /// Return the `LC_LINKER_OPTIMIZATION_HINT` command if present
     223          140 :     pub fn linker_opt_hint(&self) -> Option<LinkerOptHint> {
     224          140 :         into_optional(self.ptr.linker_opt_hint())
     225          140 :     }
     226              : 
     227              :     /// Return the `LC_ATOM_INFO` command if present
     228            0 :     pub fn atom_info(&self) -> Option<AtomInfo> {
     229            0 :         into_optional(self.ptr.atom_info())
     230            0 :     }
     231              : 
     232              :     /// Return the `LC_VERSION_MIN_MACOSX/VERSION_MIN_IPHONEOS` command if present
     233          140 :     pub fn version_min(&self) -> Option<VersionMin> {
     234          140 :         into_optional(self.ptr.version_min())
     235          140 :     }
     236              : 
     237              :     /// Check if the binary is supporting ARM64 pointer authentication (arm64e)
     238            0 :     pub fn support_arm64_ptr_auth(&self) -> bool {
     239            0 :         self.ptr.support_arm64_ptr_auth()
     240            0 :     }
     241              : 
     242              :     /// Return an iterator over the bindings located in [`DyldInfo`] or [`DyldChainedFixups`]
     243          140 :     pub fn bindings(&self) -> BindingsInfo {
     244          140 :         BindingsInfo::new(self.ptr.bindings())
     245          140 :     }
     246              : 
     247              :     /// Return an iterator over the symbol stubs.
     248              :     ///
     249              :     /// These stubs are involved when calling an **imported** function and are
     250              :     /// similar to the ELF's plt/got mechanism.
     251              :     ///
     252              :     /// There are located in sections like: `__stubs,__auth_stubs,__symbol_stub,__picsymbolstub4`
     253          140 :     pub fn symbol_stubs(&self) -> Stubs {
     254          140 :         Stubs::new(self.ptr.symbol_stubs())
     255          140 :     }
     256              : 
     257              :     /// Return Objective-C metadata if present
     258            0 :     pub fn objc_metadata(&self) -> Option<Metadata> {
     259            0 :         into_optional(self.ptr.objc_metadata())
     260            0 :     }
     261              : 
     262              :     /// Return the platform for which this Mach-O has been compiled for
     263          140 :     pub fn platform(&self) -> Platform {
     264          140 :         Platform::from(self.ptr.platform())
     265          140 :     }
     266              : 
     267              :     /// True if this binary targets iOS
     268          140 :     pub fn is_ios(&self) -> bool {
     269          140 :         self.ptr.is_ios()
     270          140 :     }
     271              : 
     272              :     /// True if this binary targets macOS
     273          140 :     pub fn is_macos(&self) -> bool {
     274          140 :         self.ptr.is_macos()
     275          140 :     }
     276              : 
     277              : 
     278              :     /// Get the integer value at the given virtual address
     279            0 :     pub fn get_int_from_virtual_address<T>(&self, addr: u64) -> Result<T, Error>
     280            0 :         where T: Num + cast::FromPrimitive + cast::ToPrimitive
     281            0 :     {
     282            0 :         // Can't be in the generic trait because of:
     283            0 :         //   > for a trait to be "object safe" it needs to allow building a vtable to allow the call
     284            0 :         //   > to be resolvable dynamically; for more information visit
     285            0 :         //   > https://doc.rust-lang.org/reference/items/traits.html#object-safety
     286            0 :         if size_of::<T>() == size_of::<u8>() {
     287            0 :             to_conv_result!(ffi::AbstractBinary::get_u8,
     288            0 :                 self.ptr.as_ref().unwrap().as_ref(),
     289            0 :                 |value| { T::from_u8(value).expect(format!("Can't cast value: {}", value).as_str()) },
     290            0 :                 addr);
     291            0 :         }
     292            0 : 
     293            0 :         if size_of::<T>() == size_of::<u16>() {
     294            0 :             to_conv_result!(ffi::AbstractBinary::get_u16,
     295            0 :                 self.ptr.as_ref().unwrap().as_ref(),
     296            0 :                 |value| { T::from_u16(value).expect(format!("Can't cast value: {}", value).as_str()) },
     297            0 :                 addr);
     298            0 :         }
     299            0 : 
     300            0 :         if size_of::<T>() == size_of::<u32>() {
     301            0 :             to_conv_result!(ffi::AbstractBinary::get_u32,
     302            0 :                 self.ptr.as_ref().unwrap().as_ref(),
     303            0 :                 |value| { T::from_u32(value).expect(format!("Can't cast value: {}", value).as_str()) },
     304            0 :                 addr);
     305            0 :         }
     306            0 : 
     307            0 :         if size_of::<T>() == size_of::<u64>() {
     308            0 :             to_conv_result!(ffi::AbstractBinary::get_u64,
     309            0 :                 self.ptr.as_ref().unwrap().as_ref(),
     310            0 :                 |value| { T::from_u64(value).expect(format!("Can't cast value: {}", value).as_str()) },
     311            0 :                 addr);
     312            0 :         }
     313            0 : 
     314            0 :         Err(Error::NotSupported)
     315            0 :     }
     316              : 
     317              :     /// Write back the current MachO binary into the file specified in parameter
     318           20 :     pub fn write(&mut self, output: &Path) {
     319           20 :         self.ptr.as_mut().unwrap().write(output.to_str().unwrap());
     320           20 :     }
     321              : 
     322              :     /// Write back the current MachO binary into the file specified in parameter with the
     323              :     /// configuration provided in the second parameter.
     324           10 :     pub fn write_with_config(&mut self, output: &Path, config: Config) {
     325           10 :         self.ptr.as_mut().unwrap().write_with_config(output.to_str().unwrap(), config.to_ffi());
     326           10 :     }
     327              : 
     328              :     /// Insert a new shared library through a `LC_LOAD_DYLIB` command
     329           20 :     pub fn add_library<'a>(&'a mut self, libname: &str) -> Dylib<'a> {
     330           20 :         Dylib::from_ffi(self.ptr.as_mut().unwrap().add_library(libname))
     331           20 :     }
     332              : 
     333          140 :     pub fn functions(&self) -> generic::Functions {
     334          140 :         generic::Functions::new(self.ptr.functions())
     335          140 :     }
     336              : }
     337              : 
     338              : impl generic::Binary for Binary {
     339          140 :     fn as_generic(&self) -> &ffi::AbstractBinary {
     340          140 :         self.ptr.as_ref().unwrap().as_ref()
     341          140 :     }
     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()
     347            0 :                     as *const ffi::AbstractBinary
     348            0 :                     as *mut ffi::AbstractBinary).as_mut().unwrap()
     349            0 :             })
     350            0 :         }
     351            0 :     }
     352              : }
     353              : 
     354              : 
     355        65690 : declare_fwd_iterator!(
     356        65690 :     BindingsInfo,
     357        65690 :     BindingInfo<'a>,
     358        65690 :     ffi::MachO_BindingInfo,
     359        65690 :     ffi::MachO_Binary,
     360        65690 :     ffi::MachO_Binary_it_bindings_info
     361        65690 : );
     362              : 
     363        15200 : declare_iterator!(
     364        15200 :     Stubs,
     365        15200 :     Stub<'a>,
     366        15200 :     ffi::MachO_Stub,
     367        15200 :     ffi::MachO_Binary,
     368        15200 :     ffi::MachO_Binary_it_stubs
     369        15200 : );
        

Generated by: LCOV version 2.1-1