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 #305

Closed
wants to merge 2 commits into from
Closed
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
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);
}
}
}
2 changes: 1 addition & 1 deletion examples/readobj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3248,7 +3248,7 @@ mod macho {
if let Ok(endian) = header.endian() {
let mut state = MachState::default();
print_mach_header(p, endian, header);
if let Ok(mut commands) = header.load_commands(endian, data) {
if let Ok(mut commands) = header.load_commands(endian, data, 0) {
while let Ok(Some(command)) = commands.next() {
print_load_command(p, endian, data, header, command, &mut state);
}
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
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