#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "std")]
extern crate core;
#[cfg(feature = "alloc")]
#[macro_use]
extern crate alloc;
#[allow(unused)]
macro_rules! if_std {
($($i:item)*) => ($(
#[cfg(feature = "std")]
$i
)*)
}
#[allow(unused)]
macro_rules! if_alloc {
($($i:item)*) => ($(
#[cfg(feature = "alloc")]
$i
)*)
}
#[cfg(feature = "alloc")]
pub mod error;
pub mod strtab;
pub mod container {
pub use scroll::Endian;
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Container {
Little,
Big,
}
impl Container {
pub fn is_big(self) -> bool {
self == Container::Big
}
}
#[cfg(not(target_pointer_width = "64"))]
pub const CONTAINER: Container = Container::Little;
#[cfg(target_pointer_width = "64")]
pub const CONTAINER: Container = Container::Big;
impl Default for Container {
#[inline]
fn default() -> Self {
CONTAINER
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Ctx {
pub container: Container,
pub le: scroll::Endian,
}
impl Ctx {
pub fn is_big(self) -> bool {
self.container.is_big()
}
pub fn is_little_endian(self) -> bool {
self.le.is_little()
}
pub fn new(container: Container, le: scroll::Endian) -> Self {
Ctx { container, le }
}
pub fn size(self) -> usize {
match self.container {
Container::Little => 4,
Container::Big => 8,
}
}
}
impl From<Container> for Ctx {
fn from(container: Container) -> Self {
Ctx {
container,
le: scroll::Endian::default(),
}
}
}
impl From<scroll::Endian> for Ctx {
fn from(le: scroll::Endian) -> Self {
Ctx {
container: CONTAINER,
le,
}
}
}
impl Default for Ctx {
#[inline]
fn default() -> Self {
Ctx {
container: Container::default(),
le: scroll::Endian::default(),
}
}
}
}
macro_rules! if_everything {
($($i:item)*) => ($(
#[cfg(all(feature = "endian_fd", feature = "elf64", feature = "elf32", feature = "pe64", feature = "pe32", feature = "mach64", feature = "mach32", feature = "archive"))]
$i
)*)
}
if_everything! {
#[derive(Debug, Default)]
pub struct HintData {
pub is_lsb: bool,
pub is_64: Option<bool>,
}
#[derive(Debug)]
pub enum Hint {
Elf(HintData),
Mach(HintData),
MachFat(usize),
PE,
Archive,
Unknown(u64),
}
pub fn peek_bytes(bytes: &[u8; 16]) -> error::Result<Hint> {
use scroll::{Pread, LE, BE};
use crate::mach::{fat, header};
if &bytes[0..elf::header::SELFMAG] == elf::header::ELFMAG {
let class = bytes[elf::header::EI_CLASS];
let is_lsb = bytes[elf::header::EI_DATA] == elf::header::ELFDATA2LSB;
let is_64 =
if class == elf::header::ELFCLASS64 {
Some (true)
} else if class == elf::header::ELFCLASS32 {
Some (false)
} else { None };
Ok(Hint::Elf(HintData { is_lsb, is_64 }))
} else if &bytes[0..archive::SIZEOF_MAGIC] == archive::MAGIC {
Ok(Hint::Archive)
} else if (&bytes[0..2]).pread_with::<u16>(0, LE)? == pe::header::DOS_MAGIC {
Ok(Hint::PE)
} else {
let (magic, maybe_ctx) = mach::parse_magic_and_ctx(bytes, 0)?;
match magic {
fat::FAT_MAGIC => {
let narchitectures = bytes.pread_with::<u32>(4, BE)? as usize;
Ok(Hint::MachFat(narchitectures))
},
header::MH_CIGAM_64 | header::MH_CIGAM | header::MH_MAGIC_64 | header::MH_MAGIC => {
if let Some(ctx) = maybe_ctx {
Ok(Hint::Mach(HintData { is_lsb: ctx.le.is_little(), is_64: Some(ctx.container.is_big()) }))
} else {
Err(error::Error::Malformed(format!("Correct mach magic {:#x} does not have a matching parsing context!", magic)))
}
},
_ => Ok(Hint::Unknown(bytes.pread::<u64>(0)?))
}
}
}
#[cfg(feature = "std")]
pub fn peek<R: ::std::io::Read + ::std::io::Seek>(fd: &mut R) -> error::Result<Hint> {
use std::io::SeekFrom;
let mut bytes = [0u8; 16];
fd.seek(SeekFrom::Start(0))?;
fd.read_exact(&mut bytes)?;
fd.seek(SeekFrom::Start(0))?;
peek_bytes(&bytes)
}
fn take_hint_bytes(bytes: &[u8]) -> Option<&[u8; 16]> {
use core::convert::TryInto;
bytes.get(0..16)
.and_then(|hint_bytes_slice| {
hint_bytes_slice.try_into().ok()
})
}
#[derive(Debug)]
#[allow(clippy::large_enum_variant)]
pub enum Object<'a> {
Elf(elf::Elf<'a>),
PE(pe::PE<'a>),
Mach(mach::Mach<'a>),
Archive(archive::Archive<'a>),
Unknown(u64),
}
impl<'a> Object<'a> {
pub fn parse(bytes: &[u8]) -> error::Result<Object> {
if let Some(hint_bytes) = take_hint_bytes(bytes) {
match peek_bytes(hint_bytes)? {
Hint::Elf(_) => Ok(Object::Elf(elf::Elf::parse(bytes)?)),
Hint::Mach(_) | Hint::MachFat(_) => Ok(Object::Mach(mach::Mach::parse(bytes)?)),
Hint::Archive => Ok(Object::Archive(archive::Archive::parse(bytes)?)),
Hint::PE => Ok(Object::PE(pe::PE::parse(bytes)?)),
Hint::Unknown(magic) => Ok(Object::Unknown(magic))
}
} else {
Err(error::Error::Malformed(format!("Object is too small.")))
}
}
}
}
#[cfg(any(feature = "elf64", feature = "elf32"))]
#[macro_use]
pub mod elf;
#[cfg(feature = "elf32")]
pub mod elf32 {
pub use crate::elf::dynamic::dyn32 as dynamic;
pub use crate::elf::header::header32 as header;
pub use crate::elf::note::Nhdr32 as Note;
pub use crate::elf::program_header::program_header32 as program_header;
pub use crate::elf::reloc::reloc32 as reloc;
pub use crate::elf::section_header::section_header32 as section_header;
pub use crate::elf::sym::sym32 as sym;
pub mod gnu_hash {
pub use crate::elf::gnu_hash::hash;
elf_gnu_hash_impl!(u32);
}
}
#[cfg(feature = "elf64")]
pub mod elf64 {
pub use crate::elf::dynamic::dyn64 as dynamic;
pub use crate::elf::header::header64 as header;
pub use crate::elf::note::Nhdr64 as Note;
pub use crate::elf::program_header::program_header64 as program_header;
pub use crate::elf::reloc::reloc64 as reloc;
pub use crate::elf::section_header::section_header64 as section_header;
pub use crate::elf::sym::sym64 as sym;
pub mod gnu_hash {
pub use crate::elf::gnu_hash::hash;
elf_gnu_hash_impl!(u64);
}
}
#[cfg(any(feature = "mach32", feature = "mach64"))]
pub mod mach;
#[cfg(any(feature = "pe32", feature = "pe64"))]
pub mod pe;
#[cfg(feature = "archive")]
pub mod archive;
#[cfg(test)]
mod tests {
use super::*;
if_everything! {
#[test]
fn take_hint_bytes_long_enough() {
let bytes_array = [1; 32];
let bytes = &bytes_array[..];
assert!(take_hint_bytes(bytes).is_some())
}
#[test]
fn take_hint_bytes_not_long_enough() {
let bytes_array = [1; 8];
let bytes = &bytes_array[..];
assert!(take_hint_bytes(bytes).is_none())
}
}
}