diff --git a/crates/examples/src/readobj/macho.rs b/crates/examples/src/readobj/macho.rs index 34e4d50..e241a24 100644 --- a/crates/examples/src/readobj/macho.rs +++ b/crates/examples/src/readobj/macho.rs @@ -112,15 +112,23 @@ pub(super) fn print_dyld_cache_images(p: &mut Printer<'_>, cache: &DyldCache) { }); } if let Some((data, offset)) = image.image_data_and_offset().print_err(p) { - if p.options.file { - p.blank(); - } - print_object_at(p, data, offset); + print_dyld_cache_image(p, data, offset, cache); p.blank(); } } } +fn print_dyld_cache_image(p: &mut Printer<'_>, data: &[u8], offset: u64, cache: &DyldCache) { + let Some(kind) = object::FileKind::parse_at(data, offset).print_err(p) else { + return; + }; + match kind { + object::FileKind::MachO32 => macho::print_macho32(p, data, offset, Some(cache)), + object::FileKind::MachO64 => macho::print_macho64(p, data, offset, Some(cache)), + _ => writeln!(p.w(), "Format: {:?}", kind).unwrap(), + } +} + pub(super) fn print_macho_fat32(p: &mut Printer<'_>, data: &[u8]) { if let Some(fat) = MachOFatFile32::parse(data).print_err(p) { writeln!(p.w(), "Format: Mach-O Fat 32-bit").unwrap(); @@ -175,23 +183,34 @@ pub(super) fn print_fat_arch(p: &mut Printer<'_>, arch: &Arch) { }); } -pub(super) fn print_macho32(p: &mut Printer<'_>, data: &[u8], offset: u64) { +pub(super) fn print_macho32( + p: &mut Printer<'_>, + data: &[u8], + offset: u64, + cache: Option<&DyldCache>, +) { if let Some(header) = MachHeader32::parse(data, offset).print_err(p) { writeln!(p.w(), "Format: Mach-O 32-bit").unwrap(); - print_macho(p, header, data, offset); + print_macho(p, header, data, offset, cache); } } -pub(super) fn print_macho64(p: &mut Printer<'_>, data: &[u8], offset: u64) { +pub(super) fn print_macho64( + p: &mut Printer<'_>, + data: &[u8], + offset: u64, + cache: Option<&DyldCache>, +) { if let Some(header) = MachHeader64::parse(data, offset).print_err(p) { writeln!(p.w(), "Format: Mach-O 64-bit").unwrap(); - print_macho(p, header, data, offset); + print_macho(p, header, data, offset, cache); } } #[derive(Default)] struct MachState<'a> { cputype: u32, + linkedit_data: &'a [u8], symbols: Vec>, sections: Vec>, section_index: usize, @@ -202,16 +221,33 @@ fn print_macho>( header: &Mach, data: &[u8], offset: u64, + cache: Option<&DyldCache>, ) { if let Some(endian) = header.endian().print_err(p) { let mut state = MachState { cputype: header.cputype(endian), + linkedit_data: data, + // Dummy first entry because section index starts at 1. sections: vec![vec![]], ..MachState::default() }; - if let Ok(mut commands) = header.load_commands(endian, data, 0) { + // Scan the load commands for info that we need to reference during parsing. + if let Ok(mut commands) = header.load_commands(endian, data, offset) { + let mut symtab_command = None; while let Ok(Some(command)) = commands.next() { if let Ok(Some((segment, section_data))) = Mach::Segment::from_command(command) { + if let Some(cache) = cache { + // The symbol table will be in the linkedit segment, but that may be in a + // different subcache, so we need to remember the data for that subcache. + // TODO: this logic should be in the object crate somehow. It already + // exists there for MachOFile but we're not using that here. + if segment.name() == macho::SEG_LINKEDIT.as_bytes() { + let addr = segment.vmaddr(endian).into(); + if let Some((data, _offset)) = cache.data_and_offset_for_address(addr) { + state.linkedit_data = data; + } + } + } if let Ok(segment_sections) = segment.sections(endian, section_data) { state .sections @@ -224,13 +260,16 @@ fn print_macho>( })); } } else if let Ok(Some(command)) = command.symtab() { - if let Ok(symtab) = command.symbols::(endian, data) { - state.symbols.extend( - symtab - .iter() - .map(|symbol| symbol.name(endian, symtab.strings()).ok()), - ); - } + symtab_command = Some(command); + } + } + if let Some(symtab_command) = symtab_command { + if let Ok(symtab) = symtab_command.symbols::(endian, state.linkedit_data) { + state.symbols.extend( + symtab + .iter() + .map(|symbol| symbol.name(endian, symtab.strings()).ok()), + ); } } } @@ -286,7 +325,7 @@ fn print_load_command( print_segment(p, endian, data, segment, section_data, state); } LoadCommandVariant::Symtab(symtab) => { - print_symtab::(p, endian, data, symtab, state); + print_symtab::(p, endian, state.linkedit_data, symtab, state); } _ => {} } diff --git a/crates/examples/src/readobj/mod.rs b/crates/examples/src/readobj/mod.rs index ab8238b..5b94c31 100644 --- a/crates/examples/src/readobj/mod.rs +++ b/crates/examples/src/readobj/mod.rs @@ -290,8 +290,8 @@ fn print_object(p: &mut Printer<'_>, data: &[u8], extra_files: &[&[u8]]) { object::FileKind::DyldCache => macho::print_dyld_cache(p, data, extra_files), object::FileKind::Elf32 => elf::print_elf32(p, data), object::FileKind::Elf64 => elf::print_elf64(p, data), - object::FileKind::MachO32 => macho::print_macho32(p, data, 0), - object::FileKind::MachO64 => macho::print_macho64(p, data, 0), + object::FileKind::MachO32 => macho::print_macho32(p, data, 0, None), + object::FileKind::MachO64 => macho::print_macho64(p, data, 0, None), object::FileKind::MachOFat32 => macho::print_macho_fat32(p, data), object::FileKind::MachOFat64 => macho::print_macho_fat64(p, data), object::FileKind::Pe32 => pe::print_pe32(p, data), @@ -303,22 +303,6 @@ fn print_object(p: &mut Printer<'_>, data: &[u8], extra_files: &[&[u8]]) { } } -fn print_object_at(p: &mut Printer<'_>, data: &[u8], offset: u64) { - let kind = match object::FileKind::parse_at(data, offset) { - Ok(file) => file, - Err(err) => { - println!("Failed to parse file: {}", err); - return; - } - }; - match kind { - object::FileKind::MachO32 => macho::print_macho32(p, data, offset), - object::FileKind::MachO64 => macho::print_macho64(p, data, offset), - // TODO - _ => {} - } -} - fn print_archive(p: &mut Printer<'_>, data: &[u8]) { if let Some(archive) = ArchiveFile::parse(data).print_err(p) { write!(p.w(), "Format: Archive ({:?})", archive.kind()).unwrap(); diff --git a/src/read/macho/load_command.rs b/src/read/macho/load_command.rs index 1429e1d..be690db 100644 --- a/src/read/macho/load_command.rs +++ b/src/read/macho/load_command.rs @@ -195,8 +195,6 @@ impl<'data, E: Endian> LoadCommandData<'data, E> { } /// Try to parse this command as a [`macho::SymtabCommand`]. - /// - /// Returns the segment command and the data containing the sections. pub fn symtab(self) -> Result>> { if self.cmd == macho::LC_SYMTAB { Some(self.data()).transpose()