LCOV - code coverage report
Current view: top level - src/macho - binary.rs (source / functions) Coverage Total Hit
Test: lief.lcov Lines: 70.9 % 220 156
Test Date: 2025-08-10:00:00:00 Functions: 72.7 % 66 48

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

Generated by: LCOV version 2.1-1