mod display;
mod elf;
mod error;
use std::fs;
use std::path::PathBuf;
use std::process::ExitCode;
use clap::Parser;
use colored::Colorize;
use crate::elf::ElfFile;
use crate::error::{BinparseError, Result};
#[derive(Parser, Debug)]
#[command(
name = "binparse",
author,
version,
about = "A colorful, user-friendly alternative to readelf",
long_about = "binparse is a modern tool for analyzing ELF binaries.\n\n\
It provides beautiful colored output with human-readable descriptions\n\
of all ELF structures including headers, segments, sections, and symbols."
)]
struct Args {
#[arg(value_name = "FILE")]
file: PathBuf,
#[arg(short = 'H', long = "header", help = "Display ELF header")]
header: bool,
#[arg(
short = 'p',
long = "segments",
alias = "program-headers",
help = "Display program headers (segments)"
)]
segments: bool,
#[arg(
short = 's',
long = "sections",
alias = "section-headers",
help = "Display section headers"
)]
sections: bool,
#[arg(long = "symbols", alias = "syms", help = "Display symbol tables")]
symbols: bool,
#[arg(short = 'a', long = "all", help = "Display all information")]
all: bool,
}
impl Args {
fn any_specified(&self) -> bool {
self.header || self.segments || self.sections || self.symbols || self.all
}
fn effective_options(&self) -> DisplayOptions {
if self.all {
DisplayOptions {
header: true,
segments: true,
sections: true,
symbols: true,
}
} else if self.any_specified() {
DisplayOptions {
header: self.header,
segments: self.segments,
sections: self.sections,
symbols: self.symbols,
}
} else {
DisplayOptions {
header: true,
segments: false,
sections: false,
symbols: false,
}
}
}
}
struct DisplayOptions {
header: bool,
segments: bool,
sections: bool,
symbols: bool,
}
fn main() -> ExitCode {
let args = Args::parse();
match run(&args) {
Ok(()) => ExitCode::SUCCESS,
Err(e) => {
eprintln!("{}: {}", "Error".red().bold(), e);
ExitCode::FAILURE
}
}
}
fn run(args: &Args) -> Result<()> {
let data = fs::read(&args.file).map_err(|e| BinparseError::FileRead {
path: args.file.clone(),
source: e,
})?;
analyze_file(&data, &args.file, args.effective_options())
}
fn analyze_file(data: &[u8], path: &PathBuf, options: DisplayOptions) -> Result<()> {
if data.len() < 16 {
return Err(BinparseError::FileTooSmall { size: data.len() });
}
match detect_format(data) {
Format::Elf => analyze_elf(data, path, options),
Format::Unknown => Err(BinparseError::UnsupportedFormat(
"Unknown binary format. Only ELF is currently supported.".to_string(),
)),
}
}
fn analyze_elf(data: &[u8], path: &PathBuf, options: DisplayOptions) -> Result<()> {
let elf = ElfFile::parse(data, path)?;
elf.print_file_info();
if options.header {
elf.display_header();
}
if options.segments {
elf.display_program_headers();
}
if options.sections {
elf.display_section_headers();
}
if options.symbols {
elf.display_symbols();
}
println!();
Ok(())
}
enum Format {
Elf,
Unknown,
}
fn detect_format(data: &[u8]) -> Format {
if data.len() >= 4 && &data[0..4] == b"\x7FELF" {
Format::Elf
} else {
Format::Unknown
}
}