feat: add object-demo

This commit is contained in:
2023-03-17 00:36:18 +08:00
parent e7f03687dc
commit 1defe5958f
4 changed files with 418 additions and 2 deletions

View File

@@ -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
```

82
__bin/object-demo/Cargo.lock generated Normal file
View 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",
]

View 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"

View 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(())
}