diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 000000000..210ce8679 Binary files /dev/null and b/src/.DS_Store differ diff --git a/src/mach/exports.rs b/src/mach/exports.rs index 4342bc245..2ce4f89e0 100644 --- a/src/mach/exports.rs +++ b/src/mach/exports.rs @@ -265,22 +265,11 @@ impl<'a> ExportTrie<'a> { } } - /// Walk the export trie for symbols exported by this binary, using the provided `libs` to resolve re-exports - pub fn exports(&self, libs: &[&'a str]) -> error::Result>> { - let offset = self.location.start; - let current_symbol = String::new(); - let mut exports = Vec::new(); - self.walk_trie(libs, current_symbol, offset, &mut exports)?; - Ok(exports) - } - - /// Create a new, lazy, zero-copy export trie from the `DyldInfo` `command` - pub fn new(bytes: &'a [u8], command: &load_command::DyldInfoCommand) -> Self { - let start = command.export_off as usize; + fn new_impl(bytes: &'a [u8], start: usize, size: usize) -> Self { // FIXME: Ideally, this should validate `command`, but the best we can // do for now is return an empty `Range`. let location = match start - .checked_add(command.export_size as usize) + .checked_add(size) .and_then(|end| bytes.get(start..end).map(|_| start..end)) { Some(location) => location, @@ -294,6 +283,32 @@ impl<'a> ExportTrie<'a> { location, } } + + /// Walk the export trie for symbols exported by this binary, using the provided `libs` to resolve re-exports + pub fn exports(&self, libs: &[&'a str]) -> error::Result>> { + let offset = self.location.start; + let current_symbol = String::new(); + let mut exports = Vec::new(); + self.walk_trie(libs, current_symbol, offset, &mut exports)?; + Ok(exports) + } + + /// Create a new, lazy, zero-copy export trie from the `DyldInfo` `command` + pub fn new(bytes: &'a [u8], command: &load_command::DyldInfoCommand) -> Self { + Self::new_impl( + bytes, + command.export_off as usize, + command.export_size as usize, + ) + } + + /// Create a new, lazy, zero-copy export trie from the `LinkeditDataCommand` `command` + pub fn new_from_linkedit_data_command( + bytes: &'a [u8], + command: &load_command::LinkeditDataCommand, + ) -> Self { + Self::new_impl(bytes, command.dataoff as usize, command.datasize as usize) + } } impl<'a> Debug for ExportTrie<'a> { @@ -331,6 +346,28 @@ mod tests { assert_eq!(exports.len() as usize, 3usize) } + #[test] + fn export_trie_linkedit_data() { + const EXPORTS: [u8; 64] = [ + 0x00, 0x01, 0x5f, 0x00, 0x05, 0x00, 0x02, 0x5f, 0x6d, 0x68, 0x5f, 0x65, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x00, 0x1f, 0x6d, + 0x61, 0x00, 0x23, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x78, 0x69, 0x6d, 0x75, 0x6d, + 0x00, 0x30, 0x69, 0x6e, 0x00, 0x35, 0x03, 0x00, 0xc0, 0x1e, 0x00, 0x03, 0x00, 0xd0, + 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + let exports = &EXPORTS[..]; + let libs = vec!["/usr/lib/libderp.so", "/usr/lib/libthuglife.so"]; + let command = load_command::LinkeditDataCommand { + datasize: exports.len() as u32, + ..Default::default() + }; + let trie = ExportTrie::new_from_linkedit_data_command(exports, &command); + println!("trie: {:#?}", &trie); + let exports = trie.exports(&libs).unwrap(); + println!("len: {} exports: {:#?}", exports.len(), &exports); + assert_eq!(exports.len() as usize, 3usize); + } + #[test] fn invalid_range() { let mut command = load_command::DyldInfoCommand::default(); diff --git a/src/mach/load_command.rs b/src/mach/load_command.rs index 063c2c19c..f549cc0b0 100644 --- a/src/mach/load_command.rs +++ b/src/mach/load_command.rs @@ -947,7 +947,7 @@ pub const SIZEOF_RPATH_COMMAND: usize = 12; /// The linkedit_data_command contains the offsets and sizes of a blob /// of data in the __LINKEDIT segment. #[repr(C)] -#[derive(Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)] +#[derive(Default, Debug, Clone, Copy, Pread, Pwrite, IOread, IOwrite, SizeWith)] pub struct LinkeditDataCommand { /// LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, /// LC_DYLIB_CODE_SIGN_DRS, LC_LINKER_OPTIMIZATION_HINT, LC_DYLD_EXPORTS_TRIE, or LC_DYLD_CHAINED_FIXUPS. diff --git a/src/mach/mod.rs b/src/mach/mod.rs index f1984cbd9..48830e023 100644 --- a/src/mach/mod.rs +++ b/src/mach/mod.rs @@ -210,6 +210,11 @@ impl<'a> MachO<'a> { export_trie = Some(exports::ExportTrie::new(bytes, &command)); bind_interpreter = Some(imports::BindInterpreter::new(bytes, &command)); } + load_command::CommandVariant::DyldExportsTrie(command) => { + export_trie = Some(exports::ExportTrie::new_from_linkedit_data_command( + bytes, &command, + )); + } load_command::CommandVariant::Unixthread(command) => { // dyld cares only about the first LC_UNIXTHREAD if unixthread_entry_address.is_none() {