From e109535f889ed5610896a65c5c0cb9964fb75e74 Mon Sep 17 00:00:00 2001 From: Philip Craig Date: Thu, 9 Jan 2025 13:42:33 +1000 Subject: [PATCH] read/macho: add DyldCache::subcache_suffixes --- crates/examples/src/bin/dyldcachedump.rs | 105 ++++++----------------- crates/examples/src/bin/objdump.rs | 92 +++++--------------- src/read/macho/dyld_cache.rs | 45 ++++++++-- 3 files changed, 89 insertions(+), 153 deletions(-) diff --git a/crates/examples/src/bin/dyldcachedump.rs b/crates/examples/src/bin/dyldcachedump.rs index 6d7b9424..674f606d 100644 --- a/crates/examples/src/bin/dyldcachedump.rs +++ b/crates/examples/src/bin/dyldcachedump.rs @@ -1,5 +1,4 @@ -use object::macho::DyldCacheHeader; -use object::read::macho::{DyldCache, DyldSubCacheSlice}; +use object::read::macho::DyldCache; use object::Endianness; use std::{env, fs, process}; @@ -23,16 +22,17 @@ fn main() { continue; } }; - let file = match unsafe { memmap2::Mmap::map(&file) } { + let mmap = match unsafe { memmap2::Mmap::map(&file) } { Ok(mmap) => mmap, Err(err) => { println!("Failed to map file '{}': {}", file_path, err,); continue; } }; + let data = &*mmap; - let subcaches_info = match get_subcache_info(&file) { - Ok(subcaches_info) => subcaches_info, + let subcache_suffixes = match DyldCache::::subcache_suffixes(data) { + Ok(subcaches_suffixes) => subcaches_suffixes, Err(err) => { println!( "Failed to parse Dyld shared cache file '{}': {}", @@ -41,29 +41,33 @@ fn main() { continue; } }; - let subcache_files = subcaches_info - .map(|info| open_subcaches(&file_path, info)) - .unwrap_or_default(); - let subcache_files: Option> = subcache_files + let Ok(subcache_files) = subcache_suffixes .into_iter() - .map( - |subcache_file| match unsafe { memmap2::Mmap::map(&subcache_file) } { - Ok(mmap) => Some(mmap), + .map(|suffix| { + let subcache_path = format!("{}{}", file_path, suffix); + let file = match fs::File::open(&subcache_path) { + Ok(file) => file, Err(err) => { - println!("Failed to map file '{}': {}", file_path, err); - None + println!("Failed to open file '{}': {}", subcache_path, err); + return Err(()); } - }, - ) - .collect(); - let subcache_files: Vec<&[u8]> = match &subcache_files { - Some(subcache_files) => subcache_files - .iter() - .map(|subcache_file| &**subcache_file) - .collect(), - None => continue, + }; + let mmap = match unsafe { memmap2::Mmap::map(&file) } { + Ok(mmap) => mmap, + Err(err) => { + println!("Failed to map file '{}': {}", subcache_path, err); + return Err(()); + } + }; + Ok(mmap) + }) + .collect::, _>>() + else { + continue; }; - let cache = match DyldCache::::parse(&*file, &subcache_files) { + let subcache_data: Vec<&[u8]> = subcache_files.iter().map(|f| &**f).collect(); + + let cache = match DyldCache::::parse(data, &subcache_data) { Ok(cache) => cache, Err(err) => { println!( @@ -82,56 +86,3 @@ fn main() { } } } - -/// Gets the slice of subcache info structs from the header of the main cache. -fn get_subcache_info( - main_cache_data: &[u8], -) -> object::read::Result>> { - let header = DyldCacheHeader::::parse(main_cache_data)?; - let (_arch, endian) = header.parse_magic()?; - let subcaches_info = header.subcaches(endian, main_cache_data)?; - Ok(subcaches_info) -} - -// If the file is a dyld shared cache, and we're on macOS 12 or later, -// then there will be one or more "subcache" files next to this file, -// with the names filename.1, filename.2, ..., filename.symbols -// or filename.01, filename.02, ..., filename.symbols on macOS 13 -fn open_subcaches(path: &str, subcaches_info: DyldSubCacheSlice) -> Vec { - let subcache_suffixes: Vec = match subcaches_info { - DyldSubCacheSlice::V1(subcaches) => { - // macOS 12: Subcaches have the file suffixes .1, .2, .3 etc. - (1..subcaches.len() + 1).map(|i| format!(".{i}")).collect() - } - DyldSubCacheSlice::V2(subcaches) => { - // macOS 13+: The subcache file suffix is written down in the header of the main cache. - subcaches - .iter() - .map(|s| { - // The suffix is a nul-terminated string in a fixed-size byte array. - let suffix = s.file_suffix; - let len = suffix.iter().position(|&c| c == 0).unwrap_or(suffix.len()); - String::from_utf8_lossy(&suffix[..len]).to_string() - }) - .collect() - } - _ => panic!( - "If this case is hit, it means that someone added a variant to the (non-exhaustive) \ - DyldSubCacheSlice enum and forgot to update this example" - ), - }; - let mut files = Vec::new(); - for suffix in subcache_suffixes { - let subcache_path = format!("{path}{suffix}"); - match fs::File::open(subcache_path) { - Ok(subcache_file) => files.push(subcache_file), - Err(_) => break, - }; - } - let symbols_subcache_path = format!("{}.symbols", path); - if let Ok(subcache_file) = fs::File::open(symbols_subcache_path) { - files.push(subcache_file); - }; - println!("Found {} subcache files", files.len()); - files -} diff --git a/crates/examples/src/bin/objdump.rs b/crates/examples/src/bin/objdump.rs index 58fb6c46..30935286 100644 --- a/crates/examples/src/bin/objdump.rs +++ b/crates/examples/src/bin/objdump.rs @@ -1,4 +1,5 @@ -use object::{macho::DyldCacheHeader, read::macho::DyldSubCacheSlice, Endianness}; +use object::read::macho::DyldCache; +use object::Endianness; use object_examples::objdump; use std::{env, fs, io, process}; @@ -19,93 +20,46 @@ fn main() { process::exit(1); } }; - let file = match unsafe { memmap2::Mmap::map(&file) } { + let mmap = match unsafe { memmap2::Mmap::map(&file) } { Ok(mmap) => mmap, Err(err) => { eprintln!("Failed to map file '{}': {}", file_path, err,); process::exit(1); } }; - let subcaches_info = get_subcache_info_if_dyld_cache(&file).ok().flatten(); - let extra_files = subcaches_info - .map(|info| open_subcaches(&file_path, info)) - .unwrap_or_default(); - let extra_files: Vec<_> = extra_files + let data = &*mmap; + + let subcache_suffixes = DyldCache::::subcache_suffixes(data).unwrap_or_default(); + let subcache_files = subcache_suffixes .into_iter() - .map( - |subcache_file| match unsafe { memmap2::Mmap::map(&subcache_file) } { + .map(|suffix| { + let subcache_path = format!("{}{}", file_path, suffix); + let file = match fs::File::open(&subcache_path) { + Ok(file) => file, + Err(err) => { + eprintln!("Failed to open file '{}': {}", subcache_path, err); + process::exit(1); + } + }; + match unsafe { memmap2::Mmap::map(&file) } { Ok(mmap) => mmap, Err(err) => { - eprintln!("Failed to map file '{}': {}", file_path, err,); + eprintln!("Failed to map file '{}': {}", subcache_path, err); process::exit(1); } - }, - ) - .collect(); - let extra_file_data: Vec<&[u8]> = extra_files.iter().map(|f| &**f).collect(); + } + }) + .collect::>(); + let extra_file_data: Vec<&[u8]> = subcache_files.iter().map(|f| &**f).collect(); let stdout = io::stdout(); let stderr = io::stderr(); objdump::print( &mut stdout.lock(), &mut stderr.lock(), - &file, + data, &extra_file_data, member_names, ) .unwrap(); } - -/// Gets the slice of subcache info structs from the header of the main cache, -/// if `main_cache_data` is the data of a Dyld shared cache. -fn get_subcache_info_if_dyld_cache( - main_cache_data: &[u8], -) -> object::read::Result>> { - let header = DyldCacheHeader::::parse(main_cache_data)?; - let (_arch, endian) = header.parse_magic()?; - let subcaches_info = header.subcaches(endian, main_cache_data)?; - Ok(subcaches_info) -} - -// If the file is a dyld shared cache, and we're on macOS 12 or later, -// then there will be one or more "subcache" files next to this file, -// with the names filename.1, filename.2, ..., filename.symbols -// or filename.01, filename.02, ..., filename.symbols on macOS 13 -fn open_subcaches(path: &str, subcaches_info: DyldSubCacheSlice) -> Vec { - let subcache_suffixes: Vec = match subcaches_info { - DyldSubCacheSlice::V1(subcaches) => { - // macOS 12: Subcaches have the file suffixes .1, .2, .3 etc. - (1..subcaches.len() + 1).map(|i| format!(".{i}")).collect() - } - DyldSubCacheSlice::V2(subcaches) => { - // macOS 13+: The subcache file suffix is written down in the header of the main cache. - subcaches - .iter() - .map(|s| { - // The suffix is a nul-terminated string in a fixed-size byte array. - let suffix = s.file_suffix; - let len = suffix.iter().position(|&c| c == 0).unwrap_or(suffix.len()); - String::from_utf8_lossy(&suffix[..len]).to_string() - }) - .collect() - } - _ => panic!( - "If this case is hit, it means that someone added a variant to the (non-exhaustive) \ - DyldSubCacheSlice enum and forgot to update this example" - ), - }; - let mut files = Vec::new(); - for suffix in subcache_suffixes { - let subcache_path = format!("{path}{suffix}"); - match fs::File::open(subcache_path) { - Ok(subcache_file) => files.push(subcache_file), - Err(_) => break, - }; - } - let symbols_subcache_path = format!("{}.symbols", path); - if let Ok(subcache_file) = fs::File::open(symbols_subcache_path) { - files.push(subcache_file); - }; - println!("Found {} subcache files", files.len()); - files -} diff --git a/src/read/macho/dyld_cache.rs b/src/read/macho/dyld_cache.rs index 2bfed080..9190502b 100644 --- a/src/read/macho/dyld_cache.rs +++ b/src/read/macho/dyld_cache.rs @@ -1,3 +1,4 @@ +use alloc::string::{String, ToString}; use alloc::vec::Vec; use core::fmt::{self, Debug}; use core::{mem, slice}; @@ -44,12 +45,10 @@ pub enum DyldSubCacheSlice<'data, E: Endian> { V2(&'data [macho::DyldSubCacheEntryV2]), } -// This is the offset of the end of the images_across_all_subcaches_count field. +// This is the offset of the end of the images_count field. const MIN_HEADER_SIZE_SUBCACHES_V1: u32 = 0x1c8; -// This is the offset of the end of the cacheSubType field. -// This field comes right after the images_across_all_subcaches_count field, -// and we don't currently have it in our definition of the DyldCacheHeader type. +// This is the offset of the end of the cache_sub_type field. const MIN_HEADER_SIZE_SUBCACHES_V2: u32 = 0x1d0; impl<'data, E, R> DyldCache<'data, E, R> @@ -57,12 +56,44 @@ where E: Endian, R: ReadRef<'data>, { + /// Return the suffixes of the subcache files given the data of the main cache file. + /// + /// Each of these should be appended to the path of the main cache file. + pub fn subcache_suffixes(data: R) -> Result> { + let header = macho::DyldCacheHeader::::parse(data)?; + let (_arch, endian) = header.parse_magic()?; + let Some(subcaches_info) = header.subcaches(endian, data)? else { + return Ok(Vec::new()); + }; + let mut subcache_suffixes: Vec = match subcaches_info { + DyldSubCacheSlice::V1(subcaches) => { + // macOS 12: Subcaches have the file suffixes .1, .2, .3 etc. + (1..subcaches.len() + 1).map(|i| format!(".{i}")).collect() + } + DyldSubCacheSlice::V2(subcaches) => { + // macOS 13+: The subcache file suffix is written down in the header of the main cache. + subcaches + .iter() + .map(|s| { + // The suffix is a nul-terminated string in a fixed-size byte array. + let suffix = s.file_suffix; + let len = suffix.iter().position(|&c| c == 0).unwrap_or(suffix.len()); + String::from_utf8_lossy(&suffix[..len]).to_string() + }) + .collect() + } + }; + if header.symbols_subcache_uuid(endian).is_some() { + subcache_suffixes.push(".symbols".to_string()); + } + Ok(subcache_suffixes) + } + /// Parse the raw dyld shared cache data. /// /// For shared caches from macOS 12 / iOS 15 and above, the subcache files need to be - /// supplied as well, in the correct order, with the `.symbols` subcache last (if present). - /// For example, `data` would be the data for `dyld_shared_cache_x86_64`, - /// and `subcache_data` would be the data for `[dyld_shared_cache_x86_64.1, dyld_shared_cache_x86_64.2, ...]`. + /// supplied as well, in the correct order. Use [`Self::subcache_suffixes`] to obtain + /// the suffixes for the path of the files. pub fn parse(data: R, subcache_data: &[R]) -> Result { let header = macho::DyldCacheHeader::parse(data)?; let (arch, endian) = header.parse_magic()?;