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