binparse 0.1.0

A colorful, user-friendly alternative to readelf for analyzing ELF binaries
//! ELF header parsing and display.
//!
//! Handles the main ELF header structure including e_ident,
//! core fields, and address/offset information.

use goblin::elf::Elf;

use crate::display::{
    print_address, print_field, print_field_with_raw, print_section, print_title,
};

/// Display the complete ELF header
pub fn display_header(elf: &Elf) {
    print_title("ELF Header");

    display_e_ident(elf);
    display_core_fields(elf);
    display_addresses(elf);
}

/// Display e_ident fields (magic, class, data, etc.)
fn display_e_ident(elf: &Elf) {
    print_section("e_ident");

    // Magic bytes
    let magic = format!(
        "{:02X} {:02X} {:02X} {:02X}",
        elf.header.e_ident[0], elf.header.e_ident[1], elf.header.e_ident[2], elf.header.e_ident[3]
    );
    print_field_with_raw("Magic", "(ELF)", &magic);

    // Class (32/64 bit)
    let class = elf.header.e_ident[4];
    let class_str = match class {
        1 => "32-bit",
        2 => "64-bit",
        _ => "Unknown",
    };
    print_field_with_raw("Class (EI_CLASS)", class_str, &format!("{}", class));

    // Data encoding (endianness)
    let data = elf.header.e_ident[5];
    let data_str = match data {
        1 => "little endian",
        2 => "big endian",
        _ => "Unknown",
    };
    print_field_with_raw("Data (EI_DATA)", data_str, &format!("{}", data));

    // ELF version
    let version = elf.header.e_ident[6];
    let version_str = match version {
        1 => "current",
        _ => "unknown",
    };
    print_field_with_raw("Version (EI_VERSION)", version_str, &format!("{}", version));

    // OS/ABI
    let osabi = elf.header.e_ident[7];
    print_field_with_raw(
        "OS/ABI (EI_OSABI)",
        osabi_to_string(osabi),
        &format!("{}", osabi),
    );

    // ABI version
    let abi_version = elf.header.e_ident[8];
    print_field("ABI Version (EI_ABIVERSION)", &format!("{}", abi_version));
}

/// Display core header fields (type, machine, etc.)
fn display_core_fields(elf: &Elf) {
    print_section("Core");

    // File type
    let e_type = elf.header.e_type;
    let type_str = match e_type {
        0 => "No file type - ET_NONE",
        1 => "Relocatable file - ET_REL",
        2 => "Executable file - ET_EXEC",
        3 => "Shared object file - ET_DYN",
        4 => "Core file - ET_CORE",
        _ => "Unknown",
    };
    print_field_with_raw("Type (e_type)", type_str, &format!("0x{:04X}", e_type));

    // Machine architecture
    let e_machine = elf.header.e_machine;
    let machine_str = machine_to_string(e_machine);
    print_field_with_raw(
        "Machine (e_machine)",
        machine_str,
        &format!("0x{:04X}", e_machine),
    );

    // ELF version (from header)
    print_field_with_raw(
        "Version (e_version)",
        if elf.header.e_version == 1 {
            "1 (current)"
        } else {
            "0"
        },
        &format!("0x{:08X}", elf.header.e_version),
    );

    // Flags
    print_field_with_raw(
        "Flags (e_flags)",
        &format_flags(elf.header.e_flags, e_machine),
        &format!("0x{:08X}", elf.header.e_flags),
    );

    // Header size
    print_field(
        "ELF Header Size (e_ehsize)",
        &format!("{} bytes", elf.header.e_ehsize),
    );
}

/// Display address and offset fields
fn display_addresses(elf: &Elf) {
    print_section("Addresses / Offsets");

    // Entry point
    print_address("Entry Point (e_entry)", elf.header.e_entry);

    // Program header offset
    print_address("Program Headers Offset (e_phoff)", elf.header.e_phoff);

    // Section header offset
    print_address("Section Headers Offset (e_shoff)", elf.header.e_shoff);

    // Program header info
    print_field(
        "Program Header Size (e_phentsize)",
        &format!("{} bytes", elf.header.e_phentsize),
    );
    print_field(
        "Program Header Count (e_phnum)",
        &format!("{}", elf.header.e_phnum),
    );

    // Section header info
    print_field(
        "Section Header Size (e_shentsize)",
        &format!("{} bytes", elf.header.e_shentsize),
    );
    print_field(
        "Section Header Count (e_shnum)",
        &format!("{}", elf.header.e_shnum),
    );

    // Section name string table index
    print_field(
        "Section Name String Table Index (e_shstrndx)",
        &format!("{}", elf.header.e_shstrndx),
    );
}

/// Convert OS/ABI byte to human-readable string
pub fn osabi_to_string(osabi: u8) -> &'static str {
    match osabi {
        0 => "System V",
        1 => "HP-UX",
        2 => "NetBSD",
        3 => "Linux",
        4 => "GNU Hurd",
        6 => "Solaris",
        7 => "AIX",
        8 => "IRIX",
        9 => "FreeBSD",
        10 => "Tru64",
        11 => "Novell Modesto",
        12 => "OpenBSD",
        13 => "OpenVMS",
        14 => "NonStop Kernel",
        15 => "AROS",
        16 => "FenixOS",
        17 => "Nuxi CloudABI",
        18 => "OpenVOS",
        97 => "ARM EABI",
        255 => "Standalone (embedded)",
        _ => "Unknown",
    }
}

/// Convert machine type to human-readable string
pub fn machine_to_string(machine: u16) -> &'static str {
    match machine {
        0 => "No machine",
        1 => "AT&T WE 32100",
        2 => "SPARC",
        3 => "Intel 80386",
        4 => "Motorola 68000",
        5 => "Motorola 88000",
        6 => "Intel MCU",
        7 => "Intel 80860",
        8 => "MIPS I",
        9 => "IBM System/370",
        10 => "MIPS RS3000 LE",
        15 => "HP PA-RISC",
        17 => "Fujitsu VPP500",
        18 => "Enhanced SPARC",
        19 => "Intel 80960",
        20 => "PowerPC",
        21 => "PowerPC 64-bit",
        22 => "IBM S/390",
        23 => "IBM SPU/SPC",
        36 => "NEC V800",
        37 => "Fujitsu FR20",
        38 => "TRW RH-32",
        39 => "Motorola RCE",
        40 => "ARM (up to ARMv7)",
        41 => "DEC Alpha",
        42 => "SuperH",
        43 => "SPARC V9",
        44 => "Siemens TriCore",
        45 => "Argonaut RISC Core",
        46 => "Hitachi H8/300",
        47 => "Hitachi H8/300H",
        48 => "Hitachi H8S",
        49 => "Hitachi H8/500",
        50 => "Intel Itanium",
        51 => "Stanford MIPS-X",
        52 => "Motorola ColdFire",
        53 => "Motorola M68HC12",
        54 => "Fujitsu MMA",
        55 => "Siemens PCP",
        56 => "Sony nCPU",
        57 => "Denso NDR1",
        58 => "Motorola Star*Core",
        59 => "Toyota ME16",
        60 => "STMicro ST100",
        61 => "TinyJ",
        62 => "AMD x86-64",
        63 => "Sony DSP",
        64 => "DEC PDP-10",
        65 => "DEC PDP-11",
        66 => "Siemens FX66",
        67 => "STMicro ST9+",
        68 => "STMicro ST7",
        69 => "Motorola MC68HC16",
        70 => "Motorola MC68HC11",
        71 => "Motorola MC68HC08",
        72 => "Motorola MC68HC05",
        73 => "Silicon Graphics SVx",
        74 => "STMicro ST19",
        75 => "Digital VAX",
        76 => "Axis CRIS",
        77 => "Infineon JAVELIN",
        78 => "Element 14 Firepath",
        79 => "LSI ZSP",
        80 => "MMIX",
        81 => "Harvard HUANY",
        82 => "SiTera Prism",
        83 => "Atmel AVR",
        84 => "Fujitsu FR30",
        85 => "Mitsubishi D10V",
        86 => "Mitsubishi D30V",
        87 => "NEC v850",
        88 => "Mitsubishi M32R",
        89 => "Matsushita MN10300",
        90 => "Matsushita MN10200",
        91 => "picoJava",
        92 => "OpenRISC",
        93 => "ARC Tangent-A5",
        94 => "Tensilica Xtensa",
        95 => "Alphamosaic VideoCore",
        96 => "Thompson Multimedia GPP",
        97 => "National Semiconductor 32000",
        98 => "Tenor Network TPC",
        99 => "Trebia SNP 1000",
        100 => "STMicro ST200",
        164 => "Tilera TILEPro",
        183 => "ARM AArch64",
        188 => "Tilera TILE-Gx",
        243 => "RISC-V",
        247 => "Linux BPF",
        252 => "C-SKY",
        258 => "LoongArch",
        _ => "Unknown",
    }
}

/// Format processor-specific flags
fn format_flags(flags: u32, machine: u16) -> String {
    if flags == 0 {
        return String::from("none");
    }

    match machine {
        40 | 183 => format_arm_flags(flags), // ARM
        62 => format_x86_64_flags(flags),    // x86-64
        _ => format!("0x{:08X}", flags),
    }
}

fn format_arm_flags(flags: u32) -> String {
    let mut parts = Vec::new();

    // ARM EABI version
    let eabi = (flags >> 24) & 0xFF;
    if eabi > 0 {
        parts.push(format!("EABI{}", eabi));
    }

    // Common ARM flags
    if flags & 0x00000200 != 0 {
        parts.push("soft-float".to_string());
    }
    if flags & 0x00000400 != 0 {
        parts.push("hard-float".to_string());
    }

    if parts.is_empty() {
        format!("0x{:08X}", flags)
    } else {
        parts.join(", ")
    }
}

fn format_x86_64_flags(_flags: u32) -> String {
    // x86-64 typically has no processor-specific flags
    String::from("none")
}