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

Add dyld shared cache parsing #308

Merged
merged 8 commits into from
May 27, 2021
Merged
Show file tree
Hide file tree
Changes from 5 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
51 changes: 51 additions & 0 deletions examples/dyldcachedump.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use object::read::macho::DyldCache;
use object::Endianness;
use std::{env, fs, process};

fn main() {
let arg_len = env::args().len();
if arg_len <= 1 {
eprintln!("Usage: {} <file> ...", env::args().next().unwrap());
process::exit(1);
}

for file_path in env::args().skip(1) {
if arg_len > 2 {
println!();
println!("{}:", file_path);
}

let file = match fs::File::open(&file_path) {
Ok(file) => file,
Err(err) => {
println!("Failed to open file '{}': {}", file_path, err,);
continue;
}
};
let file = match unsafe { memmap2::Mmap::map(&file) } {
Ok(mmap) => mmap,
Err(err) => {
println!("Failed to map file '{}': {}", file_path, err,);
continue;
}
};
let cache = match DyldCache::<Endianness>::parse(&*file) {
Ok(cache) => cache,
Err(err) => {
println!(
"Failed to parse Dyld shared cache file '{}': {}",
file_path, err,
);
continue;
}
};

// Print the list of image paths in this file.
let mut images = cache.iter_images();
while let Ok(Some(image)) = images.next() {
if let Ok(path) = image.path() {
println!("{}", path);
}
}
}
}
131 changes: 86 additions & 45 deletions examples/objdump.rs
Original file line number Diff line number Diff line change
@@ -1,71 +1,108 @@
use object::read::archive::ArchiveFile;
use object::read::macho::{FatArch, FatHeader};
use object::{Object, ObjectComdat, ObjectSection, ObjectSymbol};
use object::read::macho::{DyldCache, FatArch, FatHeader};
use object::{Endianness, Object, ObjectComdat, ObjectSection, ObjectSymbol};
use std::{env, fs, process};

fn main() {
let arg_len = env::args().len();
if arg_len <= 1 {
eprintln!("Usage: {} <file> ...", env::args().next().unwrap());
let mut args = env::args();
let cmd = args.next().unwrap();
if args.len() == 0 {
eprintln!("Usage: {} <file> [<member>...]", cmd);
process::exit(1);
}
let file_path = args.next().unwrap();
let mut member_names: Vec<_> = args.map(|name| (name, false)).collect();

for file_path in env::args().skip(1) {
if arg_len > 2 {
println!();
println!("{}:", file_path);
let file = match fs::File::open(&file_path) {
Ok(file) => file,
Err(err) => {
eprintln!("Failed to open file '{}': {}", file_path, err,);
process::exit(1);
}
};
let file = match unsafe { memmap2::Mmap::map(&file) } {
Ok(mmap) => mmap,
Err(err) => {
eprintln!("Failed to map file '{}': {}", file_path, err,);
process::exit(1);
}
};

let file = match fs::File::open(&file_path) {
Ok(file) => file,
Err(err) => {
println!("Failed to open file '{}': {}", file_path, err,);
continue;
}
};
let file = match unsafe { memmap2::Mmap::map(&file) } {
Ok(mmap) => mmap,
Err(err) => {
println!("Failed to map file '{}': {}", file_path, err,);
continue;
}
};

if let Ok(archive) = ArchiveFile::parse(&*file) {
println!("Format: Archive (kind: {:?})", archive.kind());
for member in archive.members() {
if let Ok(member) = member {
if let Ok(archive) = ArchiveFile::parse(&*file) {
eprintln!("Format: Archive (kind: {:?})", archive.kind());
for member in archive.members() {
if let Ok(member) = member {
if find_member(&mut member_names, member.name()) {
println!();
println!("{}:", String::from_utf8_lossy(member.name()));
if let Ok(data) = member.data(&*file) {
dump_object(data);
}
}
}
} else if let Ok(arches) = FatHeader::parse_arch32(&*file) {
println!("Format: Mach-O Fat 32");
for arch in arches {
println!();
println!("Fat Arch: {:?}", arch.architecture());
if let Ok(data) = arch.data(&*file) {
dump_object(data);
}
}
} else if let Ok(arches) = FatHeader::parse_arch32(&*file) {
println!("Format: Mach-O Fat 32");
for arch in arches {
println!();
println!("Fat Arch: {:?}", arch.architecture());
if let Ok(data) = arch.data(&*file) {
dump_object(data);
}
} else if let Ok(arches) = FatHeader::parse_arch64(&*file) {
println!("Format: Mach-O Fat 64");
for arch in arches {
println!();
println!("Fat Arch: {:?}", arch.architecture());
if let Ok(data) = arch.data(&*file) {
dump_object(data);
}
} else if let Ok(arches) = FatHeader::parse_arch64(&*file) {
println!("Format: Mach-O Fat 64");
for arch in arches {
println!();
println!("Fat Arch: {:?}", arch.architecture());
if let Ok(data) = arch.data(&*file) {
dump_object(data);
}
}
} else if let Ok(cache) = DyldCache::<Endianness>::parse(&*file) {
println!("Format: dyld cache {:?}-endian", cache.endianness());
println!("Architecture: {:?}", cache.architecture());
let mut images = cache.iter_images();
while let Ok(Some(image)) = images.next() {
if let Ok(path) = image.path() {
if find_member(&mut member_names, path.as_bytes()) {
println!();
println!("{}:", path);
let file = match image.parse_object() {
Ok(file) => file,
Err(err) => {
eprintln!("Failed to parse file: {}", err);
continue;
}
};
dump_parsed_object(&file);
}
}
} else {
dump_object(&*file);
}
} else {
dump_object(&*file);
}

for (name, found) in member_names {
if !found {
eprintln!("Failed to find member '{}", name);
}
}
}

fn find_member(member_names: &mut [(String, bool)], name: &[u8]) -> bool {
if member_names.is_empty() {
return true;
}
match member_names.iter().position(|x| x.0.as_bytes() == name) {
Some(i) => {
member_names[i].1 = true;
true
}
None => false,
}
}

fn dump_object(data: &[u8]) {
let file = match object::File::parse(data) {
Ok(file) => file,
Expand All @@ -74,6 +111,10 @@ fn dump_object(data: &[u8]) {
return;
}
};
dump_parsed_object(&file);
}

fn dump_parsed_object(file: &object::File) {
println!(
"Format: {:?} {:?}-endian {}-bit",
file.format(),
Expand Down
59 changes: 59 additions & 0 deletions src/macho.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,62 @@ pub const VM_PROT_WRITE: u32 = 0x02;
/// execute permission
pub const VM_PROT_EXECUTE: u32 = 0x04;

// Definitions from https://opensource.apple.com/source/dyld/dyld-210.2.3/launch-cache/dyld_cache_format.h.auto.html

/// The dyld cache header, containing only the fields which are present
/// in all versions of dyld caches (dyld-95.3 and up).
/// Many more fields exist in later dyld versions, but we currently do
/// not need to parse those.
/// Corresponds to struct dyld_cache_header from dyld_cache_format.h.
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct DyldCacheHeader<E: Endian> {
/// e.g. "dyld_v0 i386"
pub magic: [u8; 16],
/// file offset to first dyld_cache_mapping_info
pub mapping_offset: U32<E>,
/// number of dyld_cache_mapping_info entries
pub mapping_count: U32<E>,
/// file offset to first dyld_cache_image_info
pub images_offset: U32<E>,
/// number of dyld_cache_image_info entries
pub images_count: U32<E>,
/// base address of dyld when cache was built
pub dyld_base_address: U64<E>,
}

/// Corresponds to struct dyld_cache_mapping_info from dyld_cache_format.h.
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct DyldCacheMappingInfo<E: Endian> {
///
pub address: U64<E>,
///
pub size: U64<E>,
///
pub file_offset: U64<E>,
///
pub max_prot: U32<E>,
///
pub init_prot: U32<E>,
}

/// Corresponds to struct dyld_cache_image_info from dyld_cache_format.h.
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct DyldCacheImageInfo<E: Endian> {
///
pub address: U64<E>,
///
pub mod_time: U64<E>,
///
pub inode: U64<E>,
///
pub path_file_offset: U32<E>,
///
pub pad: U32<E>,
}

// Definitions from "/usr/include/mach-o/loader.h".

/*
Expand Down Expand Up @@ -3140,6 +3196,9 @@ pub const X86_64_RELOC_TLV: u8 = 9;

unsafe_impl_pod!(FatHeader, FatArch32, FatArch64,);
unsafe_impl_endian_pod!(
DyldCacheHeader,
DyldCacheMappingInfo,
DyldCacheImageInfo,
MachHeader32,
MachHeader64,
LoadCommand,
Expand Down
18 changes: 18 additions & 0 deletions src/read/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,24 @@ impl<'data, R: ReadRef<'data>> File<'data, R> {
Ok(File { inner })
}

/// Parse the raw file data at an arbitrary offset inside the input data.
///
/// Currently, this is only supported for Mach-O images.
/// This can be used for parsing Mach-O images inside the dyld shared cache,
/// where multiple images, located at different offsets, share the same address
/// space.
pub fn parse_at(data: R, offset: u64) -> Result<Self> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent!

let inner = match FileKind::parse_at(data, offset)? {
#[cfg(feature = "macho")]
FileKind::MachO32 => FileInternal::MachO32(macho::MachOFile32::parse_at(data, offset)?),
#[cfg(feature = "macho")]
FileKind::MachO64 => FileInternal::MachO64(macho::MachOFile64::parse_at(data, offset)?),
#[allow(unreachable_patterns)]
_ => return Err(Error("Unsupported file format")),
};
Ok(File { inner })
}

/// Return the file format.
pub fn format(&self) -> BinaryFormat {
match self.inner {
Expand Down
Loading