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