1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#![forbid(unsafe_code)]

//! Extremely minimal parser for ELF/PE/Mach-o/ar.
//!
//! This crate is used mostly for sharing code between `cargo-bloat` and `auditable-extract` crates.
//! It implements just enough features for those tools to work.
//! If you're looking for a fully-featured parser, see [`goblin`](https://crates.io/crates/goblin).

// Style lints on which pre-existing code disagrees with Clippy
#![allow(clippy::single_match)]
#![allow(clippy::while_let_loop)]
#![allow(clippy::single_char_pattern)]
#![allow(clippy::many_single_char_names)]

// I find this more readable
#![allow(clippy::skip_while_next)]

pub mod ar;
pub mod demangle;
pub mod elf32;
pub mod elf64;
pub mod macho;
pub mod pe;
mod parser;
mod error;

pub use crate::error::ParseError;

#[derive(Clone, Copy, PartialEq, Debug)]
pub enum ByteOrder {
    LittleEndian,
    BigEndian,
}

pub enum Format {
    Elf32 {byte_order: ByteOrder},
    Elf64 {byte_order: ByteOrder},
    Macho,
    PE,
    Unknown,
}

pub fn detect_format(data: &[u8]) -> Format {
    if data.len() < 8 {return Format::Unknown};
    let macho_signatures = [
        b"\xCA\xFE\xBA\xBE", // multi-architecture macOS
        b"\xFE\xED\xFA\xCE", // 32-bit macOS
        b"\xFE\xED\xFA\xCF", // 64-bit macOS
        b"\xCE\xFA\xED\xFE", // and now the same in reverse order
        b"\xCF\xFA\xED\xFE", // because they could
    ];
    if data.starts_with(b"\x7FELF") {
        let byte_order = match data[5] {
            1 => ByteOrder::LittleEndian,
            2 => ByteOrder::BigEndian,
            _ => return Format::Unknown,
        };
        return match data[4] {
            1 => Format::Elf32{byte_order},
            2 => Format::Elf64{byte_order},
            _ => Format::Unknown,
        };
    } else if data.starts_with(b"MZ") {
        return Format::PE;
    } else {
        for signature in &macho_signatures {
            if data.starts_with(*signature) {
                return Format::Macho;
            }
        }
    }
    Format::Unknown
}