Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

readobj: fix handling of dyld subcache images #754

Merged
merged 3 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion clippy.toml

This file was deleted.

105 changes: 28 additions & 77 deletions crates/examples/src/bin/dyldcachedump.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand All @@ -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::<Endianness>::subcache_suffixes(data) {
Ok(subcaches_suffixes) => subcaches_suffixes,
Err(err) => {
println!(
"Failed to parse Dyld shared cache file '{}': {}",
Expand All @@ -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<Vec<_>> = 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::<Result<Vec<_>, _>>()
else {
continue;
};
let cache = match DyldCache::<Endianness>::parse(&*file, &subcache_files) {
let subcache_data: Vec<&[u8]> = subcache_files.iter().map(|f| &**f).collect();

let cache = match DyldCache::<Endianness>::parse(data, &subcache_data) {
Ok(cache) => cache,
Err(err) => {
println!(
Expand All @@ -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<Option<DyldSubCacheSlice<'_, Endianness>>> {
let header = DyldCacheHeader::<Endianness>::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<Endianness>) -> Vec<fs::File> {
let subcache_suffixes: Vec<String> = 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
}
92 changes: 23 additions & 69 deletions crates/examples/src/bin/objdump.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand All @@ -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::<Endianness>::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::<Vec<_>>();
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<Option<DyldSubCacheSlice<'_, Endianness>>> {
let header = DyldCacheHeader::<Endianness>::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<Endianness>) -> Vec<fs::File> {
let subcache_suffixes: Vec<String> = 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
}
41 changes: 39 additions & 2 deletions crates/examples/src/bin/readobj.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Example that uses the lower level read API.

use clap::{Arg, ArgAction, Command};
use object::read::macho::DyldCache;
use object::Endianness;
use object_examples::readobj;
use std::path::PathBuf;
use std::{fs, io};
Expand Down Expand Up @@ -156,16 +158,51 @@ 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.display(), err);
continue;
}
};
let data = &*mmap;

let subcache_suffixes =
DyldCache::<Endianness>::subcache_suffixes(data).unwrap_or_default();
let Ok(subcache_files) = subcache_suffixes
.into_iter()
.map(|suffix| {
let mut subcache_path = file_path.clone();
subcache_path.as_mut_os_string().push(suffix);
let file = match fs::File::open(&subcache_path) {
Ok(file) => file,
Err(err) => {
println!("Failed to open file '{}': {}", subcache_path.display(), err);
return Err(());
}
};
let mmap = match unsafe { memmap2::Mmap::map(&file) } {
Ok(mmap) => mmap,
Err(err) => {
println!("Failed to map file '{}': {}", subcache_path.display(), err);
return Err(());
}
};
Ok(mmap)
})
.collect::<Result<Vec<_>, _>>()
else {
continue;
};
let subcache_data: Vec<&[u8]> = subcache_files.iter().map(|f| &**f).collect();
let stdout = io::stdout();
let stderr = io::stderr();
readobj::print(&mut stdout.lock(), &mut stderr.lock(), &file, &options);
readobj::print(
&mut stdout.lock(),
&mut stderr.lock(),
data,
&subcache_data,
&options,
);
}
}
Loading
Loading