use std::mem;
const AR_MAGIC: &[u8] = b"!<arch>\n";
const ELF_MAGIC: &[u8] = &[0x7f, 0x45, 0x4c, 0x46]; const MACHO_MAGIC: &[u8] = &[0xcf, 0xfa, 0xed, 0xfe];
const MACHO_CIGAM: &[u8] = &[0xfe, 0xed, 0xfa, 0xcf];
const MACHO_FAT_MAGIC: &[u8] = &[0xca, 0xfe, 0xba, 0xbe];
const MACHO_FAT_CIGAM: &[u8] = &[0xbe, 0xba, 0xfe, 0xca];
const PE_OFFSET: usize = 0x3c;
type PeOffset = u32;
const PE_OFFSET_SIZE: usize = mem::size_of::<PeOffset>();
const PE_MAGIC: &[u8] = &[0x50, 0x45, 0x00, 0x00];
pub(crate) enum BinaryFormat {
Ar,
Elf,
MachO,
Pe,
}
impl BinaryFormat {
pub(crate) fn name(&self) -> &'static str {
match *self {
BinaryFormat::Ar => "AR",
BinaryFormat::Elf => "ELF",
BinaryFormat::MachO => "Mach-O",
BinaryFormat::Pe => "PE",
}
}
#[allow(clippy::match_like_matches_macro)]
pub(crate) fn is_executable(&self) -> bool {
match *self {
BinaryFormat::Ar => false,
_ => true,
}
}
}
fn detect_pe(file: &[u8]) -> bool {
if file.len() < PE_OFFSET + PE_OFFSET_SIZE {
return false;
}
let mut offset = [0; PE_OFFSET_SIZE];
offset.copy_from_slice(&file[PE_OFFSET..PE_OFFSET + PE_OFFSET_SIZE]);
let offset_le = PeOffset::from_le_bytes(offset);
let offset_be = offset_le.swap_bytes();
let offset_le_sz = offset_le as usize;
let offset_be_sz = offset_be as usize;
let magic_at_offset = |offset| offset < file.len() && file[offset..].starts_with(PE_MAGIC);
magic_at_offset(offset_le_sz) || magic_at_offset(offset_be_sz)
}
pub(crate) fn detect_binary_format<C>(content: C) -> Option<BinaryFormat>
where
C: AsRef<[u8]>,
{
detect_binary_format_impl(content.as_ref())
}
fn detect_binary_format_impl(content: &[u8]) -> Option<BinaryFormat> {
if content.starts_with(ELF_MAGIC) {
Some(BinaryFormat::Elf)
} else if content.starts_with(AR_MAGIC) {
Some(BinaryFormat::Ar)
} else if content.starts_with(MACHO_MAGIC)
|| content.starts_with(MACHO_CIGAM)
|| content.starts_with(MACHO_FAT_MAGIC)
|| content.starts_with(MACHO_FAT_CIGAM)
{
Some(BinaryFormat::MachO)
} else if detect_pe(content) {
Some(BinaryFormat::Pe)
} else {
None
}
}