LCOV - code coverage report
Current view: top level - src/macho - binary.rs (source / functions) Coverage Total Hit
Test: lief.lcov Lines: 72.9 % 236 172
Test Date: 2026-02-28:00:00:00 Functions: 74.3 % 70 52

            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          372 :     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
      62          372 :         f.debug_struct("Binary").finish()
      63          372 :     }
      64              : }
      65              : 
      66              : impl FromFFI<ffi::MachO_Binary> for Binary {
      67         3996 :     fn from_ffi(ptr: cxx::UniquePtr<ffi::MachO_Binary>) -> Self {
      68         3996 :         Binary { ptr }
      69         3996 :     }
      70              : }
      71              : 
      72              : impl Binary {
      73              :     /// Return the main Mach-O header
      74         1116 :     pub fn header(&self) -> Header<'_> {
      75         1116 :         Header::from_ffi(self.ptr.header())
      76         1116 :     }
      77              : 
      78              :     /// Return an iterator over the different [`crate::macho::Commands`] used by the
      79              :     /// Mach-O binary
      80          372 :     pub fn commands(&self) -> CommandsIter<'_> {
      81          372 :         CommandsIter::new(self.ptr.commands())
      82          372 :     }
      83              : 
      84              :     /// Return an iterator over the different [`crate::macho::Section`] of the binary
      85          372 :     pub fn sections(&self) -> Sections<'_> {
      86          372 :         Sections::new(self.ptr.sections())
      87          372 :     }
      88              : 
      89              :     /// Return an iterator over the different [`crate::macho::commands::Segment`] (`LC_SEGMENT/LC_SIGNATURE`)
      90              :     /// of the binary.
      91          372 :     pub fn segments(&self) -> Segments<'_> {
      92          372 :         Segments::new(self.ptr.segments())
      93          372 :     }
      94              : 
      95              :     /// Return an iterator over the [`crate::macho::commands::Dylib`] used by this binary
      96          372 :     pub fn libraries(&self) -> Libraries<'_> {
      97          372 :         Libraries::new(self.ptr.libraries())
      98          372 :     }
      99              : 
     100              :     /// Return an iterator over the different [`crate::macho::Relocation`] of this binary
     101          372 :     pub fn relocations(&self) -> Relocations<'_> {
     102          372 :         Relocations::new(self.ptr.relocations())
     103          372 :     }
     104              : 
     105              :     /// Return an iterator over the different [`crate::macho::Symbol`] of this binary
     106          372 :     pub fn symbols(&self) -> Symbols<'_> {
     107          372 :         Symbols::new(self.ptr.symbols())
     108          372 :     }
     109              : 
     110              :     /// Return the `LC_DYLD_INFO/LC_DYLD_INFO_ONLY` command if present
     111          372 :     pub fn dyld_info(&self) -> Option<DyldInfo<'_>> {
     112          372 :         into_optional(self.ptr.dyld_info())
     113          372 :     }
     114              : 
     115              :     /// Return the `LC_UUID` command if present
     116          372 :     pub fn uuid(&self) -> Option<UUID<'_>> {
     117          372 :         into_optional(self.ptr.uuid())
     118          372 :     }
     119              : 
     120              :     /// Return the `LC_MAIN` command if present
     121          372 :     pub fn main_command(&self) -> Option<Main<'_>> {
     122          372 :         into_optional(self.ptr.main_command())
     123          372 :     }
     124              : 
     125              :     /// Return the `LC_LOAD_DYLINKER/LC_ID_DYLINKER` command if present
     126          372 :     pub fn dylinker(&self) -> Option<Dylinker<'_>> {
     127          372 :         into_optional(self.ptr.dylinker())
     128          372 :     }
     129              : 
     130              :     /// Return the `LC_FUNCTION_STARTS` command if present
     131          372 :     pub fn function_starts(&self) -> Option<FunctionStarts<'_>> {
     132          372 :         into_optional(self.ptr.function_starts())
     133          372 :     }
     134              : 
     135              :     /// Return the `LC_SOURCE_VERSION` command if present
     136          372 :     pub fn source_version(&self) -> Option<SourceVersion<'_>> {
     137          372 :         into_optional(self.ptr.source_version())
     138          372 :     }
     139              : 
     140              :     /// Return the `LC_THREAD/LC_UNIXTHREAD` command if present
     141          372 :     pub fn thread_command(&self) -> Option<ThreadCommand<'_>> {
     142          372 :         into_optional(self.ptr.thread_command())
     143          372 :     }
     144              : 
     145              :     /// Return the `LC_RPATH` command if present
     146          372 :     pub fn rpath(&self) -> Option<RPath<'_>> {
     147          372 :         into_optional(self.ptr.rpath())
     148          372 :     }
     149              : 
     150              :     /// Return the `LC_ROUTINE/LC_ROUTINE64` command if present
     151          372 :     pub fn routine(&self) -> Option<Routine<'_>> {
     152          372 :         into_optional(self.ptr.routine_command())
     153          372 :     }
     154              : 
     155              :     /// Return the `LC_SYMTAB` command if present
     156          372 :     pub fn symbol_command(&self) -> Option<SymbolCommand<'_>> {
     157          372 :         into_optional(self.ptr.symbol_command())
     158          372 :     }
     159              : 
     160              :     /// Return the `LC_DYSYMTAB` command if present
     161          372 :     pub fn dynamic_symbol(&self) -> Option<DynamicSymbolCommand<'_>> {
     162          372 :         into_optional(self.ptr.dynamic_symbol_command())
     163          372 :     }
     164              : 
     165              :     /// Return the `LC_CODE_SIGNATURE` command if present
     166          372 :     pub fn code_signature(&self) -> Option<CodeSignature<'_>> {
     167          372 :         into_optional(self.ptr.code_signature())
     168          372 :     }
     169              : 
     170              :     /// Return the `LC_DYLIB_CODE_SIGN_DRS` command if present
     171          372 :     pub fn code_signature_dir(&self) -> Option<CodeSignatureDir<'_>> {
     172          372 :         into_optional(self.ptr.code_signature_dir())
     173          372 :     }
     174              : 
     175              :     /// Return the `LC_DATA_IN_CODE` command if present
     176          372 :     pub fn data_in_code(&self) -> Option<DataInCode<'_>> {
     177          372 :         into_optional(self.ptr.data_in_code())
     178          372 :     }
     179              : 
     180              :     /// Return the `LC_SEGMENT_SPLIT_INFO` command if present
     181          372 :     pub fn segment_split_info(&self) -> Option<SegmentSplitInfo<'_>> {
     182          372 :         into_optional(self.ptr.segment_split_info())
     183          372 :     }
     184              : 
     185              :     /// Return the `LC_ENCRYPTION_INFO/LC_ENCRYPTION_INFO_64` command if present
     186          372 :     pub fn encryption_info(&self) -> Option<EncryptionInfo<'_>> {
     187          372 :         into_optional(self.ptr.encryption_info())
     188          372 :     }
     189              : 
     190              :     /// Return the `LC_SUB_FRAMEWORK` command if present
     191          372 :     pub fn sub_framework(&self) -> Option<SubFramework<'_>> {
     192          372 :         into_optional(self.ptr.sub_framework())
     193          372 :     }
     194              : 
     195              :     /// Return the `LC_SUBCLIENT` command if present
     196          372 :     pub fn subclients(&self) -> SubClients<'_> {
     197          372 :         SubClients::new(self.ptr.subclients())
     198          372 :     }
     199              : 
     200              :     /// Return the `LC_DYLD_ENVIRONMENT` command if present
     201          372 :     pub fn dyld_environment(&self) -> Option<DyldEnvironment<'_>> {
     202          372 :         into_optional(self.ptr.dyld_environment())
     203          372 :     }
     204              : 
     205              :     /// Return the `LC_BUILD_VERSION` command if present
     206          372 :     pub fn build_version(&self) -> Option<BuildVersion<'_>> {
     207          372 :         into_optional(self.ptr.build_version())
     208          372 :     }
     209              : 
     210              :     /// Return the `LC_DYLD_CHAINED_FIXUPS` command if present
     211          372 :     pub fn dyld_chained_fixups(&self) -> Option<DyldChainedFixups<'_>> {
     212          372 :         into_optional(self.ptr.dyld_chained_fixups())
     213          372 :     }
     214              : 
     215              :     /// Return the `LC_DYLD_EXPORTS_TRIE` command if present
     216          372 :     pub fn dyld_exports_trie(&self) -> Option<DyldExportsTrie<'_>> {
     217          372 :         into_optional(self.ptr.dyld_exports_trie())
     218          372 :     }
     219              : 
     220              :     /// Return the `LC_TWOLEVEL_HINTS` command if present
     221          372 :     pub fn two_level_hints(&self) -> Option<TwoLevelHints<'_>> {
     222          372 :         into_optional(self.ptr.two_level_hints())
     223          372 :     }
     224              : 
     225              :     /// Return the `LC_LINKER_OPTIMIZATION_HINT` command if present
     226          372 :     pub fn linker_opt_hint(&self) -> Option<LinkerOptHint<'_>> {
     227          372 :         into_optional(self.ptr.linker_opt_hint())
     228          372 :     }
     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          372 :     pub fn version_min(&self) -> Option<VersionMin<'_>> {
     247          372 :         into_optional(self.ptr.version_min())
     248          372 :     }
     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          372 :     pub fn bindings(&self) -> BindingsInfo<'_> {
     257          372 :         BindingsInfo::new(self.ptr.bindings())
     258          372 :     }
     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          372 :     pub fn symbol_stubs(&self) -> Stubs<'_> {
     267          372 :         Stubs::new(self.ptr.symbol_stubs())
     268          372 :     }
     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          372 :     pub fn platform(&self) -> Platform {
     277          372 :         Platform::from(self.ptr.platform())
     278          372 :     }
     279              : 
     280              :     /// True if this binary targets iOS
     281          372 :     pub fn is_ios(&self) -> bool {
     282          372 :         self.ptr.is_ios()
     283          372 :     }
     284              : 
     285              :     /// True if this binary targets macOS
     286          372 :     pub fn is_macos(&self) -> bool {
     287          372 :         self.ptr.is_macos()
     288          372 :     }
     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          372 :     pub fn functions(&self) -> generic::Functions<'_> {
     355          372 :         generic::Functions::new(self.ptr.functions())
     356          372 :     }
     357              : 
     358              :     /// Return an iterator over the `LC_NOTE` commands
     359          372 :     pub fn notes(&self) -> Notes<'_> {
     360          372 :         Notes::new(self.ptr.notes())
     361          372 :     }
     362              : 
     363              :     /// Name associated with the `LC_FILESET_ENTRY` for this MachO.
     364              :     /// For instance: `com.apple.kec.corecrypto`
     365         3516 :     pub fn fileset_name(&self) -> String {
     366         3516 :         self.ptr.fileset_name().to_string()
     367         3516 :     }
     368              : 
     369              :     /// Original address associated with the `LC_FILESET_ENTRY` for this MachO.
     370         3516 :     pub fn fileset_addr(&self) -> u64 {
     371         3516 :         self.ptr.fileset_addr()
     372         3516 :     }
     373              : 
     374              : 
     375              :     /// Return an iterator over the [`Binary`] associated with the `LC_FILESET_ENTRY` commands
     376          372 :     pub fn filesets(&self) -> FilesetBinaries<'_> {
     377          372 :         FilesetBinaries::new(self.ptr.filesets())
     378          372 :     }
     379              : }
     380              : 
     381              : impl generic::Binary for Binary {
     382          372 :     fn as_generic(&self) -> &ffi::AbstractBinary {
     383          372 :         self.ptr.as_ref().unwrap().as_ref()
     384          372 :     }
     385              : 
     386            0 :     fn as_pin_mut_generic(&mut self) -> Pin<&mut ffi::AbstractBinary> {
     387            0 :         unsafe {
     388            0 :             Pin::new_unchecked({
     389            0 :                 (self.ptr.as_ref().unwrap().as_ref()
     390            0 :                     as *const ffi::AbstractBinary
     391            0 :                     as *mut ffi::AbstractBinary).as_mut().unwrap()
     392            0 :             })
     393            0 :         }
     394            0 :     }
     395              : }
     396              : 
     397        78840 : declare_fwd_iterator!(
     398        78840 :     BindingsInfo,
     399        78840 :     BindingInfo<'a>,
     400        78840 :     ffi::MachO_BindingInfo,
     401        78840 :     ffi::MachO_Binary,
     402        78840 :     ffi::MachO_Binary_it_bindings_info
     403        78840 : );
     404              : 
     405        18252 : declare_iterator!(
     406        18252 :     Stubs,
     407        18252 :     Stub<'a>,
     408        18252 :     ffi::MachO_Stub,
     409        18252 :     ffi::MachO_Binary,
     410        18252 :     ffi::MachO_Binary_it_stubs
     411        18252 : );
     412              : 
     413         1440 : declare_iterator!(
     414         1440 :     Notes,
     415         1440 :     Note<'a>,
     416         1440 :     ffi::MachO_NoteCommand,
     417         1440 :     ffi::MachO_Binary,
     418         1440 :     ffi::MachO_Binary_it_notes
     419         1440 : );
     420              : 
     421         3516 : declare_iterator!(
     422         3516 :     FilesetBinaries,
     423         3516 :     Binary,
     424         3516 :     ffi::MachO_Binary,
     425         3516 :     ffi::MachO_Binary,
     426         3516 :     ffi::MachO_Binary_it_fileset_binaries
     427         3516 : );
        

Generated by: LCOV version 2.1-1