feat: add object-demo
This commit is contained in:
82
__bin/object-demo/Cargo.lock
generated
Normal file
82
__bin/object-demo/Cargo.lock
generated
Normal file
@@ -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",
|
||||
]
|
||||
10
__bin/object-demo/Cargo.toml
Normal file
10
__bin/object-demo/Cargo.toml
Normal file
@@ -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"
|
||||
323
__bin/object-demo/src/main.rs
Normal file
323
__bin/object-demo/src/main.rs
Normal file
@@ -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: {} <file> [<member>...]", 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<fs::File> {
|
||||
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: Write, E: Write>(
|
||||
w: &mut W,
|
||||
e: &mut E,
|
||||
file: &[u8],
|
||||
extra_files: &[&[u8]],
|
||||
member_names: Vec<String>,
|
||||
) -> 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::<Endianness>::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: Write, E: Write>(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: Write, E: Write>(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("<invalid name>")
|
||||
)?;
|
||||
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(())
|
||||
}
|
||||
Reference in New Issue
Block a user