Skip to content

Commit

Permalink
Add DyldCache.
Browse files Browse the repository at this point in the history
This implements just enough to get the path and header offset of each contained image.
It also adds a function to get an "any" File object for the image, so that the caller
doesn't need to write code twice for 32 and 64 bit images and can instead benefit from
the enum-based dynamic dispatch.
This commit also adds two "examples", for printing the list of images in the cache and
for dumping an object from inside the cache.
  • Loading branch information
mstange committed May 18, 2021
1 parent c6581fb commit 7b94b96
Show file tree
Hide file tree
Showing 6 changed files with 505 additions and 0 deletions.
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);
}
}
}
}
162 changes: 162 additions & 0 deletions examples/dyldcacheobjdump.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
use object::read::macho::{DyldCache, DyldCacheImage};
use object::{Endianness, File, Object, ObjectComdat, ObjectSection, ObjectSymbol, ReadRef};
use std::{env, fs, process};

fn main() {
let arg_len = env::args().len();
if arg_len < 3 {
// E.g. dyldcacheobjdump /System/Library/dyld/dyld_shared_cache_x86_64 /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit
eprintln!(
"Usage: {} <cache_path> <dylib_path> ...",
env::args().next().unwrap()
);
process::exit(1);
}

let mut path_iter = env::args().skip(1);
let cache_path = path_iter.next().unwrap();

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

for dylib_path in path_iter {
if arg_len > 3 {
println!();
println!("{}:", dylib_path);
}

let image = match find_image(&cache, &dylib_path) {
Some(image) => image,
None => {
println!(
"Could not find dylib path in shared cache file '{}': {}",
cache_path, dylib_path,
);
continue;
}
};

let file = match image.parse_object() {
Ok(file) => file,
Err(err) => {
println!("Failed to parse Mach-O image '{}': {}", dylib_path, err);
process::exit(1);
}
};
dump_object(&file);
}
}

fn find_image<'data>(cache: &DyldCache<'data>, path: &str) -> Option<DyldCacheImage<'data>> {
let mut images = cache.iter_images();
while let Ok(Some(image)) = images.next() {
if image.path() == Ok(path) {
return Some(image);
}
}
None
}

fn dump_object<'data, R>(file: &File<'data, R>)
where
R: ReadRef<'data>,
{
println!(
"Format: Mach-O {:?}-endian {}-bit",
file.endianness(),
if file.is_64() { "64" } else { "32" }
);
println!("Architecture: {:?}", file.architecture());
println!("Flags: {:x?}", file.flags());

match file.mach_uuid() {
Ok(Some(uuid)) => println!("Mach UUID: {:x?}", uuid),
Ok(None) => {}
Err(e) => println!("Failed to parse Mach UUID: {}", e),
}
for segment in file.segments() {
println!("{:x?}", segment);
}

for section in file.sections() {
println!("{}: {:x?}", section.index().0, section);
}

for comdat in file.comdats() {
print!("{:?} Sections:", comdat);
for section in comdat.sections() {
print!(" {}", section.0);
}
println!();
}

println!();
println!("Symbols");
for symbol in file.symbols() {
println!("{}: {:x?}", symbol.index().0, symbol);
}

for section in file.sections() {
if section.relocations().next().is_some() {
println!(
"\n{} relocations",
section.name().unwrap_or("<invalid name>")
);
for relocation in section.relocations() {
println!("{:x?}", relocation);
}
}
}

println!();
println!("Dynamic symbols");
for symbol in file.dynamic_symbols() {
println!("{}: {:x?}", symbol.index().0, symbol);
}

if let Some(relocations) = file.dynamic_relocations() {
println!();
println!("Dynamic relocations");
for relocation in relocations {
println!("{:x?}", relocation);
}
}

let imports = file.imports().unwrap();
if !imports.is_empty() {
println!();
for import in imports {
println!("{:?}", import);
}
}

let exports = file.exports().unwrap();
if !exports.is_empty() {
println!();
for export in exports {
println!("{:x?}", export);
}
}
}
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
16 changes: 16 additions & 0 deletions src/read/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,22 @@ impl<'data, R: ReadRef<'data>> File<'data, R> {
FileInternal::Wasm(_) => BinaryFormat::Wasm,
}
}

/// Initialize with an existing Mach-O 32 file.
#[cfg(feature = "macho")]
pub(crate) fn from_macho_32(inner: macho::MachOFile32<'data, Endianness, R>) -> Self {
File {
inner: FileInternal::MachO32(inner),
}
}

/// Initialize with an existing Mach-O 64 file.
#[cfg(feature = "macho")]
pub(crate) fn from_macho_64(inner: macho::MachOFile64<'data, Endianness, R>) -> Self {
File {
inner: FileInternal::MachO64(inner),
}
}
}

impl<'data, R: ReadRef<'data>> read::private::Sealed for File<'data, R> {}
Expand Down
Loading

0 comments on commit 7b94b96

Please sign in to comment.