From 1defe5958f552cc58210db62f0ddb4ca3c8524d1 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Fri, 17 Mar 2023 00:36:18 +0800 Subject: [PATCH] feat: add object-demo --- README.md | 5 +- __bin/object-demo/Cargo.lock | 82 +++++++++ __bin/object-demo/Cargo.toml | 10 ++ __bin/object-demo/src/main.rs | 323 ++++++++++++++++++++++++++++++++++ 4 files changed, 418 insertions(+), 2 deletions(-) create mode 100644 __bin/object-demo/Cargo.lock create mode 100644 __bin/object-demo/Cargo.toml create mode 100644 __bin/object-demo/src/main.rs diff --git a/README.md b/README.md index e4be1fe..abc4140 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,8 @@ Project or files: . ├── README_2.md ├── __bin -│   └── goblin +│   ├── goblin +│   └── object-demo ├── __compress │   └── zstd-demo ├── __concurrent @@ -271,6 +272,6 @@ Project or files: ├── vec.rs └── while.rs -240 directories, 39 files +241 directories, 39 files ``` diff --git a/__bin/object-demo/Cargo.lock b/__bin/object-demo/Cargo.lock new file mode 100644 index 0000000..a07aed4 --- /dev/null +++ b/__bin/object-demo/Cargo.lock @@ -0,0 +1,82 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "flate2" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "libc" +version = "0.2.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "object" +version = "0.30.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +dependencies = [ + "flate2", + "memchr", +] + +[[package]] +name = "object-demo" +version = "0.1.0" +dependencies = [ + "memmap2", + "object", +] diff --git a/__bin/object-demo/Cargo.toml b/__bin/object-demo/Cargo.toml new file mode 100644 index 0000000..8978809 --- /dev/null +++ b/__bin/object-demo/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "object-demo" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +memmap2 = "0.5.10" +object = "0.30.3" diff --git a/__bin/object-demo/src/main.rs b/__bin/object-demo/src/main.rs new file mode 100644 index 0000000..a934e17 --- /dev/null +++ b/__bin/object-demo/src/main.rs @@ -0,0 +1,323 @@ +use std::io::{Result, Write}; + +use object::{Endianness, Object, ObjectComdat, ObjectSection, ObjectSymbol}; +use object::read::archive::ArchiveFile; +use object::read::macho::{DyldCache, FatArch, FatHeader}; + +use std::{env, fs, io, process}; + +fn main() { + let mut args = env::args(); + let cmd = args.next().unwrap(); + if args.len() == 0 { + eprintln!("Usage: {} [...]", cmd); + process::exit(1); + } + let file_path = args.next().unwrap(); + let member_names: Vec<_> = args.collect(); + + 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 extra_files = open_subcaches_if_exist(&file_path); + 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 extra_files: Vec<_> = extra_files + .into_iter() + .map( + |subcache_file| match unsafe { memmap2::Mmap::map(&subcache_file) } { + Ok(mmap) => mmap, + Err(err) => { + eprintln!("Failed to map file '{}': {}", file_path, err,); + process::exit(1); + } + }, + ) + .collect(); + let extra_file_data: Vec<&[u8]> = extra_files.iter().map(|f| &**f).collect(); + + let stdout = io::stdout(); + let stderr = io::stderr(); + print( + &mut stdout.lock(), + &mut stderr.lock(), + &*file, + &extra_file_data, + member_names, + ) + .unwrap(); +} + +// 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 etc. +// Read those files now, if they exist, even if we don't know that +// we're dealing with a dyld shared cache. By the time we know what +// we're dealing with, it's too late to read more files. +fn open_subcaches_if_exist(path: &str) -> Vec { + let mut files = Vec::new(); + for i in 1.. { + let subcache_path = format!("{}.{}", path, i); + 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); + }; + files +} + +pub fn print( + w: &mut W, + e: &mut E, + file: &[u8], + extra_files: &[&[u8]], + member_names: Vec, +) -> Result<()> { + let mut member_names: Vec<_> = member_names.into_iter().map(|name| (name, false)).collect(); + + if let Ok(archive) = ArchiveFile::parse(file) { + writeln!(w, "Format: Archive (kind: {:?})", archive.kind())?; + for member in archive.members() { + match member { + Ok(member) => { + if find_member(&mut member_names, member.name()) { + writeln!(w)?; + writeln!(w, "{}:", String::from_utf8_lossy(member.name()))?; + if let Ok(data) = member.data(file) { + dump_object(w, e, data)?; + } + } + } + Err(err) => writeln!(e, "Failed to parse archive member: {}", err)?, + } + } + } else if let Ok(arches) = FatHeader::parse_arch32(file) { + writeln!(w, "Format: Mach-O Fat 32")?; + for arch in arches { + writeln!(w)?; + writeln!(w, "Fat Arch: {:?}", arch.architecture())?; + match arch.data(file) { + Ok(data) => dump_object(w, e, data)?, + Err(err) => writeln!(e, "Failed to parse Fat 32 data: {}", err)?, + } + } + } else if let Ok(arches) = FatHeader::parse_arch64(file) { + writeln!(w, "Format: Mach-O Fat 64")?; + for arch in arches { + writeln!(w)?; + writeln!(w, "Fat Arch: {:?}", arch.architecture())?; + match arch.data(file) { + Ok(data) => dump_object(w, e, data)?, + Err(err) => writeln!(e, "Failed to parse Fat 64 data: {}", err)?, + } + } + } else if let Ok(cache) = DyldCache::::parse(file, extra_files) { + writeln!(w, "Format: dyld cache {:?}-endian", cache.endianness())?; + writeln!(w, "Architecture: {:?}", cache.architecture())?; + for image in cache.images() { + let path = match image.path() { + Ok(path) => path, + Err(err) => { + writeln!(e, "Failed to parse dydld image name: {}", err)?; + continue; + } + }; + if !find_member(&mut member_names, path.as_bytes()) { + continue; + } + writeln!(w)?; + writeln!(w, "{}:", path)?; + let file = match image.parse_object() { + Ok(file) => file, + Err(err) => { + writeln!(e, "Failed to parse file: {}", err)?; + continue; + } + }; + dump_parsed_object(w, e, &file)?; + } + } else { + dump_object(w, e, file)?; + } + + for (name, found) in member_names { + if !found { + writeln!(e, "Failed to find member '{}", name)?; + } + } + Ok(()) +} + +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(w: &mut W, e: &mut E, data: &[u8]) -> Result<()> { + match object::File::parse(data) { + Ok(file) => { + dump_parsed_object(w, e, &file)?; + } + Err(err) => { + writeln!(e, "Failed to parse file: {}", err)?; + } + } + Ok(()) +} + +fn dump_parsed_object(w: &mut W, e: &mut E, file: &object::File) -> Result<()> { + writeln!( + w, + "Format: {:?} {:?}-endian {}-bit", + file.format(), + file.endianness(), + if file.is_64() { "64" } else { "32" } + )?; + writeln!(w, "Kind: {:?}", file.kind())?; + writeln!(w, "Architecture: {:?}", file.architecture())?; + writeln!(w, "Flags: {:x?}", file.flags())?; + writeln!( + w, + "Relative Address Base: {:x?}", + file.relative_address_base() + )?; + writeln!(w, "Entry Address: {:x?}", file.entry())?; + + match file.mach_uuid() { + Ok(Some(uuid)) => writeln!(w, "Mach UUID: {:x?}", uuid)?, + Ok(None) => {} + Err(err) => writeln!(e, "Failed to parse Mach UUID: {}", err)?, + } + match file.build_id() { + Ok(Some(build_id)) => writeln!(w, "Build ID: {:x?}", build_id)?, + Ok(None) => {} + Err(err) => writeln!(e, "Failed to parse build ID: {}", err)?, + } + match file.gnu_debuglink() { + Ok(Some((filename, crc))) => writeln!( + w, + "GNU debug link: {} CRC: {:08x}", + String::from_utf8_lossy(filename), + crc, + )?, + Ok(None) => {} + Err(err) => writeln!(e, "Failed to parse GNU debug link: {}", err)?, + } + match file.gnu_debugaltlink() { + Ok(Some((filename, build_id))) => writeln!( + w, + "GNU debug alt link: {}, build ID: {:x?}", + String::from_utf8_lossy(filename), + build_id, + )?, + Ok(None) => {} + Err(err) => writeln!(e, "Failed to parse GNU debug alt link: {}", err)?, + } + match file.pdb_info() { + Ok(Some(info)) => writeln!( + w, + "PDB file: {}, GUID: {:x?}, Age: {}", + String::from_utf8_lossy(info.path()), + info.guid(), + info.age() + )?, + Ok(None) => {} + Err(err) => writeln!(e, "Failed to parse PE CodeView info: {}", err)?, + } + + for segment in file.segments() { + writeln!(w, "{:x?}", segment)?; + } + + for section in file.sections() { + writeln!(w, "{}: {:x?}", section.index().0, section)?; + } + + for comdat in file.comdats() { + write!(w, "{:?} Sections:", comdat)?; + for section in comdat.sections() { + write!(w, " {}", section.0)?; + } + writeln!(w)?; + } + + writeln!(w)?; + writeln!(w, "Symbols")?; + for symbol in file.symbols() { + writeln!(w, "{}: {:x?}", symbol.index().0, symbol)?; + } + + for section in file.sections() { + if section.relocations().next().is_some() { + writeln!( + w, + "\n{} relocations", + section.name().unwrap_or("") + )?; + for relocation in section.relocations() { + writeln!(w, "{:x?}", relocation)?; + } + } + } + + writeln!(w)?; + writeln!(w, "Dynamic symbols")?; + for symbol in file.dynamic_symbols() { + writeln!(w, "{}: {:x?}", symbol.index().0, symbol)?; + } + + if let Some(relocations) = file.dynamic_relocations() { + writeln!(w)?; + writeln!(w, "Dynamic relocations")?; + for relocation in relocations { + writeln!(w, "{:x?}", relocation)?; + } + } + + match file.imports() { + Ok(imports) => { + if !imports.is_empty() { + writeln!(w)?; + for import in imports { + writeln!(w, "{:x?}", import)?; + } + } + } + Err(err) => writeln!(e, "Failed to parse imports: {}", err)?, + } + + match file.exports() { + Ok(exports) => { + if !exports.is_empty() { + writeln!(w)?; + for export in exports { + writeln!(w, "{:x?}", export)?; + } + } + } + Err(err) => writeln!(e, "Failed to parse exports: {}", err)?, + } + + Ok(()) +} \ No newline at end of file