#![forbid(unsafe_code)]
#![forbid(clippy::arithmetic_side_effects)]
#![forbid(clippy::cast_possible_truncation)]
#![forbid(clippy::cast_possible_wrap)]
#![forbid(clippy::cast_sign_loss)]
use std::{
convert::TryInto,
fmt,
io::{self, ErrorKind, Read, Seek, SeekFrom},
os::fd::AsFd,
};
use bitflags::bitflags;
use memchr::arch::all::{is_equal, is_prefix};
use nix::errno::Errno;
use crate::{lookup::safe_open_file, XPath};
const ELF_MAGIC: &[u8] = b"\x7FELF";
const EI_CLASS: usize = 4;
const EI_DATA: usize = 5;
const EI_VERSION: usize = 6;
const ELFCLASS32: u8 = 1;
const ELFCLASS64: u8 = 2;
const ET_NONE: u16 = 0;
const ET_EXEC: u16 = 2;
const ET_DYN: u16 = 3;
const ET_REL: u16 = 1;
const ET_CORE: u16 = 4;
const ET_LOPROC: u16 = 0xff00;
const ET_HIPROC: u16 = 0xffff;
const PT_DYNAMIC: u32 = 2;
const PT_INTERP: u32 = 3;
const PT_GNU_STACK: u32 = 0x6474e551;
const PT_GNU_RELRO: u32 = 0x6474e552;
const PF_X: u32 = 0x1;
const ELFDATA2LSB: u8 = 1;
const ELFDATA2MSB: u8 = 2;
const EV_CURRENT: u8 = 1;
const PHENTSIZE_ELF32: usize = 32;
const PHENTSIZE_ELF64: usize = 56;
const MAX_PHDR_TABLE_SIZE: usize = 0x10000;
const MAX_DYN_SECTION_SIZE: usize = 0x4000;
const DT_NULL: u64 = 0;
const DT_TEXTREL: u64 = 22;
const DT_BIND_NOW: u64 = 24;
const DT_FLAGS: u64 = 30;
const DT_FLAGS_1: u64 = 0x6fff_fffb;
const DF_TEXTREL: u64 = 0x4;
const DF_BIND_NOW: u64 = 0x8;
const DF_1_NOW: u64 = 0x1;
const DF_1_PIE: u64 = 0x0800_0000;
bitflags! {
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
struct DynFlags: u8 {
const PIE = 1 << 0;
const BIND_NOW = 1 << 1;
const TEXTREL = 1 << 2;
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum ExecutableFile {
Elf {
elf_type: ElfType,
file_type: ElfFileType,
linking_type: Option<LinkingType>,
pie: bool,
xs: bool,
relro: bool,
textrel: bool,
},
Script,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum ElfType {
Elf32,
Elf64,
}
#[cfg(target_pointer_width = "32")]
pub const ELFTYPE_NATIVE: ElfType = ElfType::Elf32;
#[cfg(target_pointer_width = "64")]
pub const ELFTYPE_NATIVE: ElfType = ElfType::Elf64;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum ElfFileType {
None,
Executable,
Library,
Relocatable,
Core,
ProcessorSpecific,
Unknown,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum LinkingType {
Static,
Dynamic,
}
#[derive(Debug)]
pub enum ElfError {
BadMagic,
Malformed,
IoError(io::Error),
}
impl From<u16> for ElfFileType {
fn from(e_type: u16) -> Self {
match e_type {
ET_NONE => Self::None,
ET_EXEC => Self::Executable,
ET_DYN => Self::Library,
ET_REL => Self::Relocatable,
ET_CORE => Self::Core,
ET_LOPROC..=ET_HIPROC => Self::ProcessorSpecific,
_ => Self::Unknown,
}
}
}
impl fmt::Display for ElfError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::BadMagic => write!(f, "Invalid ELF magic number"),
Self::Malformed => write!(f, "Malformed ELF header"),
Self::IoError(e) => write!(f, "I/O error: {e}"),
}
}
}
impl From<io::Error> for ElfError {
fn from(err: io::Error) -> Self {
Self::IoError(err)
}
}
impl From<ElfError> for Errno {
fn from(err: ElfError) -> Self {
match err {
ElfError::BadMagic | ElfError::Malformed => Self::ENOEXEC,
ElfError::IoError(e) => Self::from_raw(e.raw_os_error().unwrap_or(Self::EIO as i32)),
}
}
}
impl From<Errno> for ElfError {
fn from(errno: Errno) -> Self {
Self::IoError(io::Error::from_raw_os_error(errno as i32))
}
}
impl fmt::Display for ExecutableFile {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Elf {
elf_type,
file_type,
linking_type,
pie,
xs,
relro,
textrel,
} => {
let pie = if *pie { "-pie" } else { "" };
let xs = if *xs { "-xs" } else { "" };
let relro = if *relro { "-relro" } else { "" };
let textrel = if *textrel { "-textrel" } else { "" };
if let Some(linking_type) = linking_type {
write!(
f,
"ELF:{file_type}{elf_type}-{linking_type}{pie}{xs}{relro}{textrel}"
)
} else {
write!(f, "ELF:{file_type}{elf_type}{pie}{xs}{relro}{textrel}")
}
}
Self::Script => write!(f, "SCRIPT"),
}
}
}
impl fmt::Display for ElfType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Elf32 => write!(f, "32"),
Self::Elf64 => write!(f, "64"),
}
}
}
impl fmt::Display for ElfFileType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::None => write!(f, "none"),
Self::Executable => write!(f, "exe"),
Self::Library => write!(f, "lib"),
Self::Relocatable => write!(f, "rel"),
Self::Core => write!(f, "core"),
Self::ProcessorSpecific => write!(f, "proc"),
Self::Unknown => write!(f, "reserved"),
}
}
}
impl fmt::Display for LinkingType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Static => write!(f, "static"),
Self::Dynamic => write!(f, "dynamic"),
}
}
}
impl ExecutableFile {
pub fn is_script_file<Fd: AsFd>(fd: Fd, path: &XPath) -> Result<bool, ElfError> {
let (mut file, _) = safe_open_file(fd, path)?;
let mut hashbang = [0u8; 2];
file.read_exact(&mut hashbang)?;
Ok(&hashbang == b"#!")
}
pub fn is_elf_file<R: Read>(mut reader: R) -> Result<bool, ElfError> {
let mut magic = [0u8; 4];
let mut nread = 0;
while nread < 4 {
match reader.read(&mut magic[nread..]) {
Ok(0) => {
return Ok(false);
}
Ok(n) => nread = nread.checked_add(n).ok_or(Errno::EOVERFLOW)?,
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
Err(e) => return Err(e.into()),
}
}
Ok(is_equal(&magic, ELF_MAGIC))
}
pub fn is_valid_elf_file<R: Read>(mut reader: R) -> Result<bool, ElfError> {
let mut ident = [0u8; 16]; let mut nread = 0;
while nread < 16 {
match reader.read(&mut ident[nread..]) {
Ok(0) => {
return Ok(false);
}
Ok(n) => nread = nread.checked_add(n).ok_or(Errno::EOVERFLOW)?,
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
Err(e) => return Err(e.into()),
}
}
if !is_equal(&ident[0..4], ELF_MAGIC) {
return Ok(false);
}
if ident[EI_VERSION] != EV_CURRENT {
return Ok(false);
}
if !matches!(ident[EI_DATA], ELFDATA2LSB | ELFDATA2MSB) {
return Ok(false);
}
if !matches!(ident[EI_CLASS], ELFCLASS64 | ELFCLASS32) {
return Ok(false);
}
Ok(true)
}
#[expect(clippy::cognitive_complexity)]
pub fn parse<R: Read + Seek>(
mut reader: R,
check_linking: bool,
) -> Result<ExecutableFile, ElfError> {
let mut header = [0u8; 64];
let mut bytes_read = 0;
while bytes_read < header.len() {
match reader.read(&mut header[bytes_read..]) {
Ok(0) => break,
Ok(n) => bytes_read = bytes_read.checked_add(n).ok_or(Errno::EOVERFLOW)?,
Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
Err(e) => return Err(ElfError::IoError(e)),
}
}
match bytes_read {
0 => return Err(ElfError::BadMagic),
1 => {
if header[0] == b'#' {
return Ok(ExecutableFile::Script);
} else {
return Err(ElfError::BadMagic);
}
}
2..=3 => {
if is_prefix(&header, b"#!") {
return Ok(ExecutableFile::Script);
} else {
return Err(ElfError::BadMagic);
}
}
4..=63 => {
if is_prefix(&header, b"#!") {
return Ok(ExecutableFile::Script);
} else if !is_equal(&header[0..4], ELF_MAGIC) {
return Err(ElfError::BadMagic);
} else {
return Err(ElfError::Malformed);
}
}
_ => {
if is_prefix(&header, b"#!") {
return Ok(ExecutableFile::Script);
} else if !is_equal(&header[0..4], ELF_MAGIC) {
return Err(ElfError::BadMagic);
}
}
}
let is_be = match header.get(EI_DATA) {
Some(&ELFDATA2LSB) => false,
Some(&ELFDATA2MSB) => true,
_ => return Err(ElfError::Malformed),
};
let elf_type = match header.get(EI_CLASS) {
Some(&ELFCLASS32) => ElfType::Elf32,
Some(&ELFCLASS64) => ElfType::Elf64,
_ => return Err(ElfError::Malformed),
};
let mut file_type = if is_be {
read_u16_be(header.get(16..18).ok_or(ElfError::Malformed)?)
} else {
read_u16_le(header.get(16..18).ok_or(ElfError::Malformed)?)
}
.map(ElfFileType::from)?;
let mut dynamic = false;
let mut pie = false;
let mut relro = false;
let mut textrel = false;
let mut xs = !(cfg!(target_arch = "powerpc64") && elf_type == ElfType::Elf64);
if check_linking && matches!(file_type, ElfFileType::Executable | ElfFileType::Library) {
let is_et_dyn = file_type == ElfFileType::Library;
let (phoff_offset, phnum_offset, phentsize_offset) = if elf_type == ElfType::Elf64 {
(32usize, 56usize, 54usize)
} else {
(28usize, 44usize, 42usize)
};
let phoff = if elf_type == ElfType::Elf64 {
if is_be {
read_u64_be(
header
.get(
phoff_offset
..phoff_offset.checked_add(8).ok_or(Errno::EOVERFLOW)?,
)
.ok_or(ElfError::Malformed)?,
)?
} else {
read_u64_le(
header
.get(
phoff_offset
..phoff_offset.checked_add(8).ok_or(Errno::EOVERFLOW)?,
)
.ok_or(ElfError::Malformed)?,
)?
}
} else {
if is_be {
read_u32_be(
header
.get(
phoff_offset
..phoff_offset.checked_add(4).ok_or(Errno::EOVERFLOW)?,
)
.ok_or(ElfError::Malformed)?,
)?
} else {
read_u32_le(
header
.get(
phoff_offset
..phoff_offset.checked_add(4).ok_or(Errno::EOVERFLOW)?,
)
.ok_or(ElfError::Malformed)?,
)?
}
.into()
};
let phnum = if is_be {
read_u16_be(
header
.get(phnum_offset..phnum_offset.checked_add(2).ok_or(Errno::EOVERFLOW)?)
.ok_or(ElfError::Malformed)?,
)?
} else {
read_u16_le(
header
.get(phnum_offset..phnum_offset.checked_add(2).ok_or(Errno::EOVERFLOW)?)
.ok_or(ElfError::Malformed)?,
)?
} as usize;
let phentsize = if is_be {
read_u16_be(
header
.get(
phentsize_offset
..phentsize_offset.checked_add(2).ok_or(Errno::EOVERFLOW)?,
)
.ok_or(ElfError::Malformed)?,
)?
} else {
read_u16_le(
header
.get(
phentsize_offset
..phentsize_offset.checked_add(2).ok_or(Errno::EOVERFLOW)?,
)
.ok_or(ElfError::Malformed)?,
)?
} as usize;
let want_phentsize = if elf_type == ElfType::Elf64 {
PHENTSIZE_ELF64
} else {
PHENTSIZE_ELF32
};
if phentsize != want_phentsize {
return Err(ElfError::Malformed);
}
let phsize = phnum.checked_mul(phentsize).ok_or(Errno::EOVERFLOW)?;
if phsize == 0 || phsize > MAX_PHDR_TABLE_SIZE {
return Err(ElfError::Malformed);
}
let mut phdr_buf = [0u8; MAX_PHDR_TABLE_SIZE];
let phdrs = &mut phdr_buf[..phsize];
reader.seek(SeekFrom::Start(phoff))?;
reader.read_exact(phdrs)?;
let mut seen_interp = false;
let mut seen_relro = false;
for i in 0..phnum {
let offset = i.checked_mul(phentsize).ok_or(Errno::EOVERFLOW)?;
let end = offset.checked_add(4).ok_or(Errno::EOVERFLOW)?;
if end > phdrs.len() || offset >= phdrs.len() {
break;
}
let p_type = if is_be {
read_u32_be(&phdrs[offset..end])?
} else {
read_u32_le(&phdrs[offset..end])?
};
match p_type {
PT_INTERP if !seen_interp => {
file_type = ElfFileType::Executable;
dynamic = true;
seen_interp = true;
}
PT_GNU_RELRO => seen_relro = true,
PT_GNU_STACK => {
let flags_offset = if elf_type == ElfType::Elf64 {
offset.checked_add(4).ok_or(Errno::EOVERFLOW)?
} else {
offset.checked_add(24).ok_or(Errno::EOVERFLOW)?
};
let flags_end = flags_offset.checked_add(4).ok_or(Errno::EOVERFLOW)?;
if flags_end > phdrs.len() || flags_offset >= phdrs.len() {
break;
}
let p_flags = if is_be {
read_u32_be(&phdrs[flags_offset..flags_end])?
} else {
read_u32_le(&phdrs[flags_offset..flags_end])?
};
xs = p_flags & PF_X != 0;
}
_ => continue,
}
}
let mut dyn_buf = [0u8; MAX_DYN_SECTION_SIZE];
if let Some(dyn_size) = read_dyn(
&mut reader,
phdrs,
&mut dyn_buf,
elf_type,
is_be,
phnum,
phentsize,
)? {
let dyn_flags = scan_dyn(&dyn_buf[..dyn_size], elf_type, is_be)?;
pie = is_et_dyn && dyn_flags.contains(DynFlags::PIE);
if pie {
file_type = ElfFileType::Executable;
}
relro = seen_relro && dyn_flags.contains(DynFlags::BIND_NOW);
textrel = dyn_flags.contains(DynFlags::TEXTREL);
}
}
let linking_type = if file_type == ElfFileType::Executable {
if dynamic {
Some(LinkingType::Dynamic)
} else {
Some(LinkingType::Static)
}
} else {
None
};
Ok(ExecutableFile::Elf {
elf_type,
file_type,
linking_type,
pie,
xs,
relro,
textrel,
})
}
}
fn scan_dyn(buf: &[u8], elf_type: ElfType, is_be: bool) -> Result<DynFlags, ElfError> {
let dyn_size = buf.len();
let ent_size = match elf_type {
ElfType::Elf32 => 8,
ElfType::Elf64 => 16,
};
let mut dt_bind_now: Option<u64> = None;
let mut dt_textrel = false;
let mut df_flags: Option<u64> = None;
let mut df_flags_or: u64 = 0;
let mut df_flags_1: Option<u64> = None;
for i in (0..dyn_size).step_by(ent_size) {
let j = i.checked_add(ent_size / 2).ok_or(Errno::EOVERFLOW)?;
if j > dyn_size || i >= dyn_size {
break;
}
let d_tag = read_dyn_word(&buf[i..j], elf_type, is_be)?;
if d_tag == DT_NULL {
break;
}
if d_tag == DT_TEXTREL {
dt_textrel = true;
continue;
}
if d_tag == DT_BIND_NOW || d_tag == DT_FLAGS || d_tag == DT_FLAGS_1 {
let k = i.checked_add(ent_size).ok_or(Errno::EOVERFLOW)?;
if k > dyn_size || j >= dyn_size {
break;
}
let d_val = read_dyn_word(&buf[j..k], elf_type, is_be)?;
if d_tag == DT_BIND_NOW {
dt_bind_now = Some(d_val);
} else if d_tag == DT_FLAGS {
df_flags = Some(df_flags.map_or(d_val, |acc| acc & d_val));
df_flags_or |= d_val;
} else {
df_flags_1 = Some(df_flags_1.map_or(d_val, |acc| acc & d_val));
}
}
}
let df_flags = df_flags.unwrap_or(0);
let df_flags_1 = df_flags_1.unwrap_or(0);
let mut flags = DynFlags::empty();
flags.set(DynFlags::PIE, df_flags_1 & DF_1_PIE != 0);
flags.set(
DynFlags::BIND_NOW,
dt_bind_now.is_some_and(|v| v != 0)
|| df_flags & DF_BIND_NOW != 0
|| df_flags_1 & DF_1_NOW != 0,
);
flags.set(
DynFlags::TEXTREL,
dt_textrel || df_flags_or & DF_TEXTREL != 0,
);
Ok(flags)
}
fn read_dyn<R: Read + Seek>(
reader: &mut R,
phdrs: &[u8],
buf: &mut [u8],
elf_type: ElfType,
is_be: bool,
phnum: usize,
phentsize: usize,
) -> Result<Option<usize>, ElfError> {
for i in 0..phnum {
let offset = i.checked_mul(phentsize).ok_or(Errno::EOVERFLOW)?;
let end = offset.checked_add(4).ok_or(Errno::EOVERFLOW)?;
if end > phdrs.len() || offset >= phdrs.len() {
break;
}
let p_type = if is_be {
read_u32_be(&phdrs[offset..end])?
} else {
read_u32_le(&phdrs[offset..end])?
};
if p_type == PT_DYNAMIC {
let p_offset = if elf_type == ElfType::Elf64 {
let offset_dyn_min = offset.checked_add(8).ok_or(Errno::EOVERFLOW)?;
let offset_dyn_max = offset.checked_add(16).ok_or(Errno::EOVERFLOW)?;
if offset_dyn_max > phdrs.len() || offset_dyn_min >= phdrs.len() {
break;
}
if is_be {
read_u64_be(&phdrs[offset_dyn_min..offset_dyn_max])?
} else {
read_u64_le(&phdrs[offset_dyn_min..offset_dyn_max])?
}
} else {
let offset_dyn_min = offset.checked_add(4).ok_or(Errno::EOVERFLOW)?;
let offset_dyn_max = offset.checked_add(8).ok_or(Errno::EOVERFLOW)?;
if offset_dyn_max > phdrs.len() || offset_dyn_min >= phdrs.len() {
break;
}
if is_be {
read_u32_be(&phdrs[offset_dyn_min..offset_dyn_max])?.into()
} else {
read_u32_le(&phdrs[offset_dyn_min..offset_dyn_max])?.into()
}
};
let p_filesz = if elf_type == ElfType::Elf64 {
let offset_filesz_min = offset.checked_add(32).ok_or(Errno::EOVERFLOW)?;
let offset_filesz_max = offset.checked_add(40).ok_or(Errno::EOVERFLOW)?;
if offset_filesz_max > phdrs.len() || offset_filesz_min >= phdrs.len() {
break;
}
if is_be {
read_u64_be(&phdrs[offset_filesz_min..offset_filesz_max])?
} else {
read_u64_le(&phdrs[offset_filesz_min..offset_filesz_max])?
}
} else {
let offset_filesz_min = offset.checked_add(16).ok_or(Errno::EOVERFLOW)?;
let offset_filesz_max = offset.checked_add(20).ok_or(Errno::EOVERFLOW)?;
if offset_filesz_max > phdrs.len() || offset_filesz_min >= phdrs.len() {
break;
}
if is_be {
read_u32_be(&phdrs[offset_filesz_min..offset_filesz_max])?.into()
} else {
read_u32_le(&phdrs[offset_filesz_min..offset_filesz_max])?.into()
}
};
if p_filesz == 0 {
return Err(ElfError::Malformed);
}
let buf_off = usize::try_from(p_filesz).or(Err(ElfError::Malformed))?;
if buf_off > buf.len() {
return Err(ElfError::Malformed);
}
let file_size = reader.seek(SeekFrom::End(0))?;
if p_offset > file_size || p_offset.saturating_add(p_filesz) > file_size {
return Err(ElfError::Malformed);
}
reader.seek(SeekFrom::Start(p_offset))?;
reader.read_exact(&mut buf[..buf_off])?;
return Ok(Some(buf_off));
}
}
Ok(None)
}
fn read_dyn_word(buf: &[u8], elf_type: ElfType, is_be: bool) -> Result<u64, ElfError> {
if is_be {
if elf_type == ElfType::Elf64 {
read_u64_be(buf)
} else {
read_u32_be(buf).map(u64::from)
}
} else if elf_type == ElfType::Elf64 {
read_u64_le(buf)
} else {
read_u32_le(buf).map(u64::from)
}
}
fn read_u16_be(bytes: &[u8]) -> Result<u16, ElfError> {
let arr: [u8; 2] = bytes.try_into().or(Err(ElfError::Malformed))?;
Ok(u16::from_be_bytes(arr))
}
fn read_u16_le(bytes: &[u8]) -> Result<u16, ElfError> {
let arr: [u8; 2] = bytes.try_into().or(Err(ElfError::Malformed))?;
Ok(u16::from_le_bytes(arr))
}
fn read_u32_be(bytes: &[u8]) -> Result<u32, ElfError> {
let arr: [u8; 4] = bytes.try_into().or(Err(ElfError::Malformed))?;
Ok(u32::from_be_bytes(arr))
}
fn read_u32_le(bytes: &[u8]) -> Result<u32, ElfError> {
let arr: [u8; 4] = bytes.try_into().or(Err(ElfError::Malformed))?;
Ok(u32::from_le_bytes(arr))
}
fn read_u64_be(bytes: &[u8]) -> Result<u64, ElfError> {
let arr: [u8; 8] = bytes.try_into().or(Err(ElfError::Malformed))?;
Ok(u64::from_be_bytes(arr))
}
fn read_u64_le(bytes: &[u8]) -> Result<u64, ElfError> {
let arr: [u8; 8] = bytes.try_into().or(Err(ElfError::Malformed))?;
Ok(u64::from_le_bytes(arr))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_elf_type_1() {
assert_eq!(ElfType::Elf32.to_string(), "32");
assert_eq!(ElfType::Elf64.to_string(), "64");
}
#[test]
#[cfg(target_pointer_width = "64")]
fn test_elf_type_2() {
assert_eq!(ELFTYPE_NATIVE, ElfType::Elf64);
}
#[test]
#[cfg(target_pointer_width = "32")]
fn test_elf_type_3() {
assert_eq!(ELFTYPE_NATIVE, ElfType::Elf32);
}
#[test]
fn test_elf_file_type_1() {
assert_eq!(ElfFileType::from(0u16), ElfFileType::None);
assert_eq!(ElfFileType::from(1u16), ElfFileType::Relocatable);
assert_eq!(ElfFileType::from(2u16), ElfFileType::Executable);
assert_eq!(ElfFileType::from(3u16), ElfFileType::Library);
assert_eq!(ElfFileType::from(4u16), ElfFileType::Core);
assert_eq!(ElfFileType::from(0xff00u16), ElfFileType::ProcessorSpecific);
assert_eq!(ElfFileType::from(0xffffu16), ElfFileType::ProcessorSpecific);
assert_eq!(ElfFileType::from(0xff80u16), ElfFileType::ProcessorSpecific);
assert_eq!(ElfFileType::from(42u16), ElfFileType::Unknown);
assert_eq!(ElfFileType::from(0xfeffu16), ElfFileType::Unknown);
}
#[test]
fn test_elf_file_type_2() {
assert_eq!(ElfFileType::None.to_string(), "none");
assert_eq!(ElfFileType::Executable.to_string(), "exe");
assert_eq!(ElfFileType::Library.to_string(), "lib");
assert_eq!(ElfFileType::Relocatable.to_string(), "rel");
assert_eq!(ElfFileType::Core.to_string(), "core");
assert_eq!(ElfFileType::ProcessorSpecific.to_string(), "proc");
assert_eq!(ElfFileType::Unknown.to_string(), "reserved");
}
#[test]
fn test_linking_type_1() {
assert_eq!(LinkingType::Static.to_string(), "static");
assert_eq!(LinkingType::Dynamic.to_string(), "dynamic");
}
#[test]
fn test_elf_error_1() {
assert_eq!(ElfError::BadMagic.to_string(), "Invalid ELF magic number");
assert_eq!(ElfError::Malformed.to_string(), "Malformed ELF header");
}
#[test]
fn test_elf_error_2() {
let io_err = io::Error::new(ErrorKind::NotFound, "gone");
let elf_err = ElfError::IoError(io_err);
assert_eq!(elf_err.to_string(), "I/O error: gone");
}
#[test]
fn test_elf_error_3() {
let io_err = io::Error::new(ErrorKind::PermissionDenied, "denied");
let elf_err = ElfError::from(io_err);
assert_io_error::<()>(Err(elf_err), ErrorKind::PermissionDenied);
}
#[test]
fn test_elf_error_4() {
assert_eq!(Errno::from(ElfError::BadMagic), Errno::ENOEXEC);
assert_eq!(Errno::from(ElfError::Malformed), Errno::ENOEXEC);
}
#[test]
fn test_elf_error_5() {
let io_err = io::Error::from_raw_os_error(Errno::ENOENT as i32);
let elf_err = ElfError::IoError(io_err);
assert_eq!(Errno::from(elf_err), Errno::ENOENT);
}
#[test]
fn test_elf_error_6() {
let io_err = io::Error::new(ErrorKind::Other, "custom");
let elf_err = ElfError::IoError(io_err);
assert_eq!(Errno::from(elf_err), Errno::EIO);
}
#[test]
fn test_elf_error_7() {
match ElfError::from(Errno::EIO) {
ElfError::IoError(err) => assert_eq!(err.raw_os_error(), Some(Errno::EIO as i32)),
other => panic!("{other:?}"),
}
assert_eq!(Errno::from(ElfError::from(Errno::EIO)), Errno::EIO);
}
#[test]
fn test_executable_file_1() {
let f = ExecutableFile::Elf {
elf_type: ElfType::Elf64,
file_type: ElfFileType::Executable,
linking_type: Some(LinkingType::Static),
pie: false,
xs: false,
relro: false,
textrel: false,
};
assert_eq!(f.to_string(), "ELF:exe64-static");
}
#[test]
fn test_executable_file_2() {
let f = ExecutableFile::Elf {
elf_type: ElfType::Elf64,
file_type: ElfFileType::Executable,
linking_type: Some(LinkingType::Dynamic),
pie: true,
xs: false,
relro: false,
textrel: false,
};
assert_eq!(f.to_string(), "ELF:exe64-dynamic-pie");
}
#[test]
fn test_executable_file_3() {
let f = ExecutableFile::Elf {
elf_type: ElfType::Elf32,
file_type: ElfFileType::Library,
linking_type: None,
pie: false,
xs: false,
relro: false,
textrel: false,
};
assert_eq!(f.to_string(), "ELF:lib32");
}
#[test]
fn test_executable_file_4() {
let f = ExecutableFile::Elf {
elf_type: ElfType::Elf64,
file_type: ElfFileType::Executable,
linking_type: Some(LinkingType::Static),
pie: false,
xs: true,
relro: false,
textrel: false,
};
assert_eq!(f.to_string(), "ELF:exe64-static-xs");
}
#[test]
fn test_executable_file_5() {
let f = ExecutableFile::Elf {
elf_type: ElfType::Elf64,
file_type: ElfFileType::Executable,
linking_type: Some(LinkingType::Dynamic),
pie: true,
xs: true,
relro: false,
textrel: false,
};
assert_eq!(f.to_string(), "ELF:exe64-dynamic-pie-xs");
}
#[test]
fn test_executable_file_6() {
let f = ExecutableFile::Elf {
elf_type: ElfType::Elf32,
file_type: ElfFileType::Relocatable,
linking_type: None,
pie: true,
xs: true,
relro: false,
textrel: false,
};
assert_eq!(f.to_string(), "ELF:rel32-pie-xs");
}
#[test]
fn test_executable_file_7() {
assert_eq!(ExecutableFile::Script.to_string(), "SCRIPT");
}
#[test]
fn test_executable_file_8() {
let f = ExecutableFile::Elf {
elf_type: ElfType::Elf64,
file_type: ElfFileType::Executable,
linking_type: Some(LinkingType::Dynamic),
pie: true,
xs: false,
relro: true,
textrel: false,
};
assert_eq!(f.to_string(), "ELF:exe64-dynamic-pie-relro");
}
#[test]
fn test_executable_file_9() {
let f = ExecutableFile::Elf {
elf_type: ElfType::Elf32,
file_type: ElfFileType::Library,
linking_type: None,
pie: false,
xs: true,
relro: true,
textrel: false,
};
assert_eq!(f.to_string(), "ELF:lib32-xs-relro");
}
#[test]
fn test_executable_file_10() {
let f = ExecutableFile::Elf {
elf_type: ElfType::Elf64,
file_type: ElfFileType::Library,
linking_type: None,
pie: false,
xs: false,
relro: false,
textrel: true,
};
assert_eq!(f.to_string(), "ELF:lib64-textrel");
}
fn dyn64_le(entries: &[(u64, u64)]) -> Vec<u8> {
let mut buf = Vec::new();
for (d_tag, d_val) in entries {
buf.extend_from_slice(&d_tag.to_le_bytes());
buf.extend_from_slice(&d_val.to_le_bytes());
}
buf
}
fn dyn32_be(entries: &[(u32, u32)]) -> Vec<u8> {
let mut buf = Vec::new();
for (d_tag, d_val) in entries {
buf.extend_from_slice(&d_tag.to_be_bytes());
buf.extend_from_slice(&d_val.to_be_bytes());
}
buf
}
fn dyn64_be(entries: &[(u64, u64)]) -> Vec<u8> {
let mut buf = Vec::new();
for (d_tag, d_val) in entries {
buf.extend_from_slice(&d_tag.to_be_bytes());
buf.extend_from_slice(&d_val.to_be_bytes());
}
buf
}
fn dyn32_le(entries: &[(u32, u32)]) -> Vec<u8> {
let mut buf = Vec::new();
for (d_tag, d_val) in entries {
buf.extend_from_slice(&d_tag.to_le_bytes());
buf.extend_from_slice(&d_val.to_le_bytes());
}
buf
}
fn phdr64_le(p_offset: u64, p_filesz: u64) -> Vec<u8> {
let mut v = vec![0u8; 56];
v[0..4].copy_from_slice(&PT_DYNAMIC.to_le_bytes());
v[8..16].copy_from_slice(&p_offset.to_le_bytes());
v[32..40].copy_from_slice(&p_filesz.to_le_bytes());
v
}
fn phdr64_be(p_offset: u64, p_filesz: u64) -> Vec<u8> {
let mut v = vec![0u8; 56];
v[0..4].copy_from_slice(&PT_DYNAMIC.to_be_bytes());
v[8..16].copy_from_slice(&p_offset.to_be_bytes());
v[32..40].copy_from_slice(&p_filesz.to_be_bytes());
v
}
fn phdr32_le(p_offset: u32, p_filesz: u32) -> Vec<u8> {
let mut v = vec![0u8; 32];
v[0..4].copy_from_slice(&PT_DYNAMIC.to_le_bytes());
v[4..8].copy_from_slice(&p_offset.to_le_bytes());
v[16..20].copy_from_slice(&p_filesz.to_le_bytes());
v
}
fn phdr32_be(p_offset: u32, p_filesz: u32) -> Vec<u8> {
let mut v = vec![0u8; 32];
v[0..4].copy_from_slice(&PT_DYNAMIC.to_be_bytes());
v[4..8].copy_from_slice(&p_offset.to_be_bytes());
v[16..20].copy_from_slice(&p_filesz.to_be_bytes());
v
}
#[test]
fn test_scan_dyn_1() {
let buf = dyn64_le(&[(DT_NULL, 0)]);
let flags = scan_dyn(&buf, ElfType::Elf64, false).unwrap();
assert_eq!(flags, DynFlags::empty());
}
#[test]
fn test_scan_dyn_2() {
let buf = dyn64_le(&[(DT_FLAGS_1, DF_1_PIE), (DT_NULL, 0)]);
let flags = scan_dyn(&buf, ElfType::Elf64, false).unwrap();
assert_eq!(flags, DynFlags::PIE);
}
#[test]
fn test_scan_dyn_3() {
let buf = dyn64_le(&[(DT_BIND_NOW, 1), (DT_NULL, 0)]);
let flags = scan_dyn(&buf, ElfType::Elf64, false).unwrap();
assert_eq!(flags, DynFlags::BIND_NOW);
}
#[test]
fn test_scan_dyn_4() {
let buf = dyn64_le(&[(DT_FLAGS, DF_BIND_NOW), (DT_NULL, 0)]);
let flags = scan_dyn(&buf, ElfType::Elf64, false).unwrap();
assert_eq!(flags, DynFlags::BIND_NOW);
}
#[test]
fn test_scan_dyn_5() {
let buf = dyn64_le(&[(DT_FLAGS_1, DF_1_NOW), (DT_NULL, 0)]);
let flags = scan_dyn(&buf, ElfType::Elf64, false).unwrap();
assert_eq!(flags, DynFlags::BIND_NOW);
}
#[test]
fn test_scan_dyn_6() {
let buf = dyn64_le(&[(DT_FLAGS, 0x1), (DT_NULL, 0)]);
let flags = scan_dyn(&buf, ElfType::Elf64, false).unwrap();
assert_eq!(flags, DynFlags::empty());
}
#[test]
fn test_scan_dyn_7() {
let buf = dyn64_le(&[
(DT_FLAGS, DF_BIND_NOW),
(DT_FLAGS_1, DF_1_PIE),
(DT_NULL, 0),
]);
let flags = scan_dyn(&buf, ElfType::Elf64, false).unwrap();
assert_eq!(flags, DynFlags::PIE | DynFlags::BIND_NOW);
}
#[test]
fn test_scan_dyn_8() {
let buf = dyn64_le(&[(DT_FLAGS, DF_BIND_NOW), (DT_FLAGS, 0), (DT_NULL, 0)]);
let flags = scan_dyn(&buf, ElfType::Elf64, false).unwrap();
assert_eq!(flags, DynFlags::empty());
}
#[test]
fn test_scan_dyn_9() {
let buf = dyn64_le(&[
(DT_FLAGS_1, DF_1_PIE | DF_1_NOW),
(DT_FLAGS_1, 0),
(DT_NULL, 0),
]);
let flags = scan_dyn(&buf, ElfType::Elf64, false).unwrap();
assert_eq!(flags, DynFlags::empty());
}
#[test]
fn test_scan_dyn_10() {
let buf = dyn64_le(&[
(DT_FLAGS_1, 0),
(DT_FLAGS_1, DF_1_PIE | DF_1_NOW),
(DT_NULL, 0),
]);
let flags = scan_dyn(&buf, ElfType::Elf64, false).unwrap();
assert_eq!(flags, DynFlags::empty());
}
#[test]
fn test_scan_dyn_11() {
let buf = dyn64_le(&[(DT_FLAGS_1, DF_1_NOW), (DT_FLAGS_1, DF_1_NOW), (DT_NULL, 0)]);
let flags = scan_dyn(&buf, ElfType::Elf64, false).unwrap();
assert_eq!(flags, DynFlags::BIND_NOW);
}
#[test]
fn test_scan_dyn_12() {
let buf = dyn64_le(&[(DT_BIND_NOW, 0), (DT_FLAGS, 0), (DT_NULL, 0)]);
let flags = scan_dyn(&buf, ElfType::Elf64, false).unwrap();
assert_eq!(flags, DynFlags::empty());
}
#[test]
fn test_scan_dyn_13() {
let buf = dyn64_le(&[(DT_NULL, 0), (DT_BIND_NOW, 1)]);
let flags = scan_dyn(&buf, ElfType::Elf64, false).unwrap();
assert_eq!(flags, DynFlags::empty());
}
#[test]
fn test_scan_dyn_14() {
let buf = dyn32_be(&[(30, 8), (24, 0), (0, 0)]);
let flags = scan_dyn(&buf, ElfType::Elf32, true).unwrap();
assert_eq!(flags, DynFlags::BIND_NOW);
}
#[test]
fn test_scan_dyn_15() {
let buf = dyn64_le(&[(DT_BIND_NOW, 5), (DT_BIND_NOW, 0), (DT_NULL, 0)]);
let flags = scan_dyn(&buf, ElfType::Elf64, false).unwrap();
assert_eq!(flags, DynFlags::empty());
}
#[test]
fn test_scan_dyn_16() {
let buf = dyn64_le(&[(DT_TEXTREL, 0), (DT_NULL, 0)]);
let flags = scan_dyn(&buf, ElfType::Elf64, false).unwrap();
assert_eq!(flags, DynFlags::TEXTREL);
}
#[test]
fn test_scan_dyn_17() {
let buf = dyn64_le(&[(DT_FLAGS, DF_TEXTREL), (DT_NULL, 0)]);
let flags = scan_dyn(&buf, ElfType::Elf64, false).unwrap();
assert_eq!(flags, DynFlags::TEXTREL);
}
#[test]
fn test_scan_dyn_18() {
let buf = dyn64_le(&[(DT_FLAGS, DF_TEXTREL), (DT_FLAGS, 0), (DT_NULL, 0)]);
let flags = scan_dyn(&buf, ElfType::Elf64, false).unwrap();
assert_eq!(flags, DynFlags::TEXTREL);
}
#[test]
fn test_scan_dyn_19() {
let buf = DT_TEXTREL.to_le_bytes().to_vec();
let flags = scan_dyn(&buf, ElfType::Elf64, false).unwrap();
assert_eq!(flags, DynFlags::TEXTREL);
}
#[test]
fn test_scan_dyn_20() {
let buf = dyn64_be(&[(DT_FLAGS_1, DF_1_NOW), (DT_NULL, 0)]);
let flags = scan_dyn(&buf, ElfType::Elf64, true).unwrap();
assert_eq!(flags, DynFlags::BIND_NOW);
}
#[test]
fn test_scan_dyn_21() {
let buf = dyn32_le(&[(30, 8), (24, 0), (0, 0)]);
let flags = scan_dyn(&buf, ElfType::Elf32, false).unwrap();
assert_eq!(flags, DynFlags::BIND_NOW);
}
#[test]
fn test_scan_dyn_22() {
let mut buf = dyn64_le(&[(DT_FLAGS_1, DF_1_NOW), (DT_NULL, 0)]);
buf.truncate(20);
let flags = scan_dyn(&buf, ElfType::Elf64, false).unwrap();
assert_eq!(flags, DynFlags::BIND_NOW);
}
#[test]
fn test_scan_dyn_23() {
let mut buf = dyn64_le(&[(DT_FLAGS_1, DF_1_NOW), (DT_FLAGS, DF_TEXTREL)]);
buf.truncate(24);
let flags = scan_dyn(&buf, ElfType::Elf64, false).unwrap();
assert_eq!(flags, DynFlags::BIND_NOW);
}
#[test]
fn test_scan_dyn_24() {
let mut buf = dyn32_le(&[(30, 8), (0, 0)]);
buf.truncate(10);
let flags = scan_dyn(&buf, ElfType::Elf32, false).unwrap();
assert_eq!(flags, DynFlags::BIND_NOW);
}
#[test]
fn test_scan_dyn_25() {
let mut buf = dyn32_le(&[(30, 8), (30, 4)]);
buf.truncate(12);
let flags = scan_dyn(&buf, ElfType::Elf32, false).unwrap();
assert_eq!(flags, DynFlags::BIND_NOW);
}
#[test]
fn test_read_dyn_1() {
let phdrs = phdr64_le(0, 16);
let mut reader = std::io::Cursor::new(vec![0xAB; 16]);
let mut buf = [0u8; 32];
let size = read_dyn(&mut reader, &phdrs, &mut buf, ElfType::Elf64, false, 1, 56)
.unwrap()
.unwrap();
assert_eq!(size, 16);
assert_eq!(&buf[..16], &[0xAB; 16]);
}
#[test]
fn test_read_dyn_2() {
let phdrs = phdr64_be(0, 16);
let mut reader = std::io::Cursor::new(vec![0xCD; 16]);
let mut buf = [0u8; 32];
let size = read_dyn(&mut reader, &phdrs, &mut buf, ElfType::Elf64, true, 1, 56)
.unwrap()
.unwrap();
assert_eq!(size, 16);
assert_eq!(&buf[..16], &[0xCD; 16]);
}
#[test]
fn test_read_dyn_3() {
let phdrs = phdr32_le(0, 8);
let mut reader = std::io::Cursor::new(vec![0xAB; 8]);
let mut buf = [0u8; 32];
let size = read_dyn(&mut reader, &phdrs, &mut buf, ElfType::Elf32, false, 1, 32)
.unwrap()
.unwrap();
assert_eq!(size, 8);
assert_eq!(&buf[..8], &[0xAB; 8]);
}
#[test]
fn test_read_dyn_4() {
let phdrs = phdr32_be(0, 8);
let mut reader = std::io::Cursor::new(vec![0xCD; 8]);
let mut buf = [0u8; 32];
let size = read_dyn(&mut reader, &phdrs, &mut buf, ElfType::Elf32, true, 1, 32)
.unwrap()
.unwrap();
assert_eq!(size, 8);
assert_eq!(&buf[..8], &[0xCD; 8]);
}
#[test]
fn test_read_dyn_5() {
let phdrs = vec![0u8; 56];
let mut reader = std::io::Cursor::new(vec![0xAB; 16]);
let mut buf = [0u8; 32];
let size = read_dyn(&mut reader, &phdrs, &mut buf, ElfType::Elf64, false, 1, 56).unwrap();
assert_eq!(size, None);
}
#[test]
fn test_read_dyn_6() {
let mut phdrs = vec![0u8; 56];
phdrs.extend_from_slice(&phdr64_le(0, 16));
let mut reader = std::io::Cursor::new(vec![0xAB; 16]);
let mut buf = [0u8; 32];
let size = read_dyn(&mut reader, &phdrs, &mut buf, ElfType::Elf64, false, 2, 56)
.unwrap()
.unwrap();
assert_eq!(size, 16);
}
#[test]
fn test_read_dyn_7() {
let phdrs = phdr64_le(0, 0);
let mut reader = std::io::Cursor::new(vec![0xAB; 16]);
let mut buf = [0u8; 32];
assert!(matches!(
read_dyn(&mut reader, &phdrs, &mut buf, ElfType::Elf64, false, 1, 56),
Err(ElfError::Malformed)
));
}
#[test]
fn test_read_dyn_8() {
let phdrs = phdr64_le(0, 64);
let mut reader = std::io::Cursor::new(vec![0xAB; 64]);
let mut buf = [0u8; 32];
assert!(matches!(
read_dyn(&mut reader, &phdrs, &mut buf, ElfType::Elf64, false, 1, 56),
Err(ElfError::Malformed)
));
}
#[test]
fn test_read_dyn_9() {
let phdrs = phdr64_le(4096, 16);
let mut reader = std::io::Cursor::new(vec![0xAB; 16]);
let mut buf = [0u8; 32];
assert!(matches!(
read_dyn(&mut reader, &phdrs, &mut buf, ElfType::Elf64, false, 1, 56),
Err(ElfError::Malformed)
));
}
#[test]
fn test_read_dyn_10() {
let phdrs = phdr64_le(8, 16);
let mut reader = std::io::Cursor::new(vec![0xAB; 16]);
let mut buf = [0u8; 32];
assert!(matches!(
read_dyn(&mut reader, &phdrs, &mut buf, ElfType::Elf64, false, 1, 56),
Err(ElfError::Malformed)
));
}
#[test]
fn test_read_dyn_11() {
let phdrs = vec![0u8; 2];
let mut reader = std::io::Cursor::new(vec![0xAB; 16]);
let mut buf = [0u8; 32];
let size = read_dyn(&mut reader, &phdrs, &mut buf, ElfType::Elf64, false, 1, 56).unwrap();
assert_eq!(size, None);
}
#[test]
fn test_read_dyn_12() {
let mut phdrs = phdr64_le(0, 16);
phdrs.truncate(12);
let mut reader = std::io::Cursor::new(vec![0xAB; 16]);
let mut buf = [0u8; 32];
let size = read_dyn(&mut reader, &phdrs, &mut buf, ElfType::Elf64, false, 1, 56).unwrap();
assert_eq!(size, None);
}
#[test]
fn test_read_dyn_13() {
let mut phdrs = phdr32_le(0, 8);
phdrs.truncate(6);
let mut reader = std::io::Cursor::new(vec![0xAB; 8]);
let mut buf = [0u8; 32];
let size = read_dyn(&mut reader, &phdrs, &mut buf, ElfType::Elf32, false, 1, 32).unwrap();
assert_eq!(size, None);
}
#[test]
fn test_read_dyn_14() {
let mut phdrs = phdr64_le(0, 16);
phdrs.truncate(20);
let mut reader = std::io::Cursor::new(vec![0xAB; 16]);
let mut buf = [0u8; 32];
let size = read_dyn(&mut reader, &phdrs, &mut buf, ElfType::Elf64, false, 1, 56).unwrap();
assert_eq!(size, None);
}
#[test]
fn test_read_dyn_15() {
let mut phdrs = phdr32_le(0, 8);
phdrs.truncate(12);
let mut reader = std::io::Cursor::new(vec![0xAB; 8]);
let mut buf = [0u8; 32];
let size = read_dyn(&mut reader, &phdrs, &mut buf, ElfType::Elf32, false, 1, 32).unwrap();
assert_eq!(size, None);
}
#[test]
fn test_read_dyn_16() {
let phdrs = phdr64_le(0, 16);
let mut reader = SeekErrorReader(std::io::Cursor::new(vec![0xAB; 16]));
let mut buf = [0u8; 32];
assert_io_error(
read_dyn(&mut reader, &phdrs, &mut buf, ElfType::Elf64, false, 1, 56),
ErrorKind::Other,
);
}
#[test]
fn test_read_dyn_17() {
let phdrs = phdr64_le(0, 16);
let mut reader = BigSizeReader(std::io::Cursor::new(vec![]));
let mut buf = [0u8; 32];
assert_io_error(
read_dyn(&mut reader, &phdrs, &mut buf, ElfType::Elf64, false, 1, 56),
ErrorKind::UnexpectedEof,
);
}
#[test]
fn test_read_dyn_18() {
let phdrs = phdr64_le(0, 16);
let mut reader = StartSeekErrorReader(std::io::Cursor::new(vec![0xAB; 16]));
let mut tmp = [0u8; 4];
reader.read_exact(&mut tmp).unwrap();
assert_eq!(tmp, [0xAB; 4]);
let mut buf = [0u8; 32];
assert_io_error(
read_dyn(&mut reader, &phdrs, &mut buf, ElfType::Elf64, false, 1, 56),
ErrorKind::Other,
);
}
fn build_elf64(relro_seg: bool, dyn_tag: u64, dyn_val: u64) -> Vec<u8> {
let mut v = vec![0u8; 208];
v[0..4].copy_from_slice(ELF_MAGIC);
v[EI_CLASS] = ELFCLASS64;
v[EI_DATA] = ELFDATA2LSB;
v[EI_VERSION] = EV_CURRENT;
v[16..18].copy_from_slice(&3u16.to_le_bytes());
v[32..40].copy_from_slice(&64u64.to_le_bytes());
v[54..56].copy_from_slice(&56u16.to_le_bytes());
v[56..58].copy_from_slice(&2u16.to_le_bytes());
let p1 = if relro_seg { PT_GNU_RELRO } else { 0u32 };
v[64..68].copy_from_slice(&p1.to_le_bytes());
v[120..124].copy_from_slice(&PT_DYNAMIC.to_le_bytes());
v[128..136].copy_from_slice(&176u64.to_le_bytes());
v[152..160].copy_from_slice(&32u64.to_le_bytes());
v[176..184].copy_from_slice(&dyn_tag.to_le_bytes());
v[184..192].copy_from_slice(&dyn_val.to_le_bytes());
v[192..200].copy_from_slice(&DT_NULL.to_le_bytes());
v
}
fn build_elf64_be(relro_seg: bool, dyn_tag: u64, dyn_val: u64) -> Vec<u8> {
let mut v = vec![0u8; 208];
v[0..4].copy_from_slice(ELF_MAGIC);
v[EI_CLASS] = ELFCLASS64;
v[EI_DATA] = ELFDATA2MSB;
v[EI_VERSION] = EV_CURRENT;
v[16..18].copy_from_slice(&3u16.to_be_bytes());
v[32..40].copy_from_slice(&64u64.to_be_bytes());
v[54..56].copy_from_slice(&56u16.to_be_bytes());
v[56..58].copy_from_slice(&2u16.to_be_bytes());
let p1 = if relro_seg { PT_GNU_RELRO } else { 0u32 };
v[64..68].copy_from_slice(&p1.to_be_bytes());
v[120..124].copy_from_slice(&PT_DYNAMIC.to_be_bytes());
v[128..136].copy_from_slice(&176u64.to_be_bytes());
v[152..160].copy_from_slice(&32u64.to_be_bytes());
v[176..184].copy_from_slice(&dyn_tag.to_be_bytes());
v[184..192].copy_from_slice(&dyn_val.to_be_bytes());
v[192..200].copy_from_slice(&DT_NULL.to_be_bytes());
v
}
fn build_elf32(relro_seg: bool, dyn_tag: u64, dyn_val: u64) -> Vec<u8> {
let dyn_tag = dyn_tag as u32;
let dyn_val = dyn_val as u32;
let mut v = vec![0u8; 144];
v[0..4].copy_from_slice(ELF_MAGIC);
v[EI_CLASS] = ELFCLASS32;
v[EI_DATA] = ELFDATA2LSB;
v[EI_VERSION] = EV_CURRENT;
v[16..18].copy_from_slice(&3u16.to_le_bytes());
v[28..32].copy_from_slice(&64u32.to_le_bytes());
v[42..44].copy_from_slice(&32u16.to_le_bytes());
v[44..46].copy_from_slice(&2u16.to_le_bytes());
let p1 = if relro_seg { PT_GNU_RELRO } else { 0u32 };
v[64..68].copy_from_slice(&p1.to_le_bytes());
v[96..100].copy_from_slice(&PT_DYNAMIC.to_le_bytes());
v[100..104].copy_from_slice(&128u32.to_le_bytes());
v[112..116].copy_from_slice(&16u32.to_le_bytes());
v[128..132].copy_from_slice(&dyn_tag.to_le_bytes());
v[132..136].copy_from_slice(&dyn_val.to_le_bytes());
v[136..140].copy_from_slice(&0u32.to_le_bytes());
v
}
fn build_elf32_be(relro_seg: bool, dyn_tag: u64, dyn_val: u64) -> Vec<u8> {
let dyn_tag = dyn_tag as u32;
let dyn_val = dyn_val as u32;
let mut v = vec![0u8; 144];
v[0..4].copy_from_slice(ELF_MAGIC);
v[EI_CLASS] = ELFCLASS32;
v[EI_DATA] = ELFDATA2MSB;
v[EI_VERSION] = EV_CURRENT;
v[16..18].copy_from_slice(&3u16.to_be_bytes());
v[28..32].copy_from_slice(&64u32.to_be_bytes());
v[42..44].copy_from_slice(&32u16.to_be_bytes());
v[44..46].copy_from_slice(&2u16.to_be_bytes());
let p1 = if relro_seg { PT_GNU_RELRO } else { 0u32 };
v[64..68].copy_from_slice(&p1.to_be_bytes());
v[96..100].copy_from_slice(&PT_DYNAMIC.to_be_bytes());
v[100..104].copy_from_slice(&128u32.to_be_bytes());
v[112..116].copy_from_slice(&16u32.to_be_bytes());
v[128..132].copy_from_slice(&dyn_tag.to_be_bytes());
v[132..136].copy_from_slice(&dyn_val.to_be_bytes());
v[136..140].copy_from_slice(&0u32.to_be_bytes());
v
}
struct InterruptReader(std::io::Cursor<Vec<u8>>, bool);
impl Read for InterruptReader {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if !self.1 {
self.1 = true;
return Err(io::Error::from(ErrorKind::Interrupted));
}
self.0.read(buf)
}
}
impl Seek for InterruptReader {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
self.0.seek(pos)
}
}
struct ErrorReader;
impl Read for ErrorReader {
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
Err(io::Error::other("test"))
}
}
impl Seek for ErrorReader {
fn seek(&mut self, _pos: SeekFrom) -> io::Result<u64> {
Ok(0)
}
}
struct SeekErrorReader(std::io::Cursor<Vec<u8>>);
impl Read for SeekErrorReader {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.0.read(buf)
}
}
impl Seek for SeekErrorReader {
fn seek(&mut self, _pos: SeekFrom) -> io::Result<u64> {
Err(io::Error::other("test"))
}
}
struct BigSizeReader(std::io::Cursor<Vec<u8>>);
impl Read for BigSizeReader {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.0.read(buf)
}
}
impl Seek for BigSizeReader {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
match pos {
SeekFrom::End(0) => Ok(1 << 20),
pos => self.0.seek(pos),
}
}
}
struct StartSeekErrorReader(std::io::Cursor<Vec<u8>>);
impl Read for StartSeekErrorReader {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.0.read(buf)
}
}
impl Seek for StartSeekErrorReader {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
match pos {
SeekFrom::Start(_) => Err(io::Error::other("test")),
pos => self.0.seek(pos),
}
}
}
#[track_caller]
fn assert_io_error<T: fmt::Debug>(result: Result<T, ElfError>, kind: ErrorKind) {
match result {
Err(ElfError::IoError(err)) => assert_eq!(err.kind(), kind),
other => panic!("{other:?}"),
}
}
#[test]
fn test_parse_1() {
assert!(matches!(
ExecutableFile::parse(std::io::Cursor::new(vec![]), true),
Err(ElfError::BadMagic)
));
}
#[test]
fn test_parse_2() {
let exe = ExecutableFile::parse(std::io::Cursor::new(b"#".to_vec()), true).unwrap();
assert_eq!(exe, ExecutableFile::Script);
}
#[test]
fn test_parse_3() {
assert!(matches!(
ExecutableFile::parse(std::io::Cursor::new(b"!".to_vec()), true),
Err(ElfError::BadMagic)
));
}
#[test]
fn test_parse_4() {
let exe = ExecutableFile::parse(std::io::Cursor::new(b"#!".to_vec()), true).unwrap();
assert_eq!(exe, ExecutableFile::Script);
}
#[test]
fn test_parse_5() {
assert!(matches!(
ExecutableFile::parse(std::io::Cursor::new(b"ab".to_vec()), true),
Err(ElfError::BadMagic)
));
}
#[test]
fn test_parse_6() {
let exe =
ExecutableFile::parse(std::io::Cursor::new(b"#!/bin/sh\n".to_vec()), true).unwrap();
assert_eq!(exe, ExecutableFile::Script);
}
#[test]
fn test_parse_7() {
let mut v = vec![b' '; 100];
v[0..2].copy_from_slice(b"#!");
let exe = ExecutableFile::parse(std::io::Cursor::new(v), true).unwrap();
assert_eq!(exe, ExecutableFile::Script);
}
#[test]
fn test_parse_8() {
assert!(matches!(
ExecutableFile::parse(std::io::Cursor::new(vec![0xAA; 16]), true),
Err(ElfError::BadMagic)
));
}
#[test]
fn test_parse_9() {
assert!(matches!(
ExecutableFile::parse(std::io::Cursor::new(vec![0xAA; 100]), true),
Err(ElfError::BadMagic)
));
}
#[test]
fn test_parse_10() {
let mut v = vec![0u8; 16];
v[0..4].copy_from_slice(ELF_MAGIC);
assert!(matches!(
ExecutableFile::parse(std::io::Cursor::new(v), true),
Err(ElfError::Malformed)
));
}
#[test]
fn test_parse_11() {
let mut v = build_elf64(false, DT_NULL, 0);
v[EI_DATA] = 9;
assert!(matches!(
ExecutableFile::parse(std::io::Cursor::new(v), true),
Err(ElfError::Malformed)
));
}
#[test]
fn test_parse_12() {
let mut v = build_elf64(false, DT_NULL, 0);
v[EI_CLASS] = 9;
assert!(matches!(
ExecutableFile::parse(std::io::Cursor::new(v), true),
Err(ElfError::Malformed)
));
}
#[test]
fn test_parse_13() {
let mut v = build_elf64(false, DT_NULL, 0);
v[16..18].copy_from_slice(&ET_EXEC.to_le_bytes());
let exe = ExecutableFile::parse(std::io::Cursor::new(v), true).unwrap();
assert!(matches!(
exe,
ExecutableFile::Elf {
file_type: ElfFileType::Executable,
linking_type: Some(LinkingType::Static),
..
}
));
}
#[test]
fn test_parse_14() {
let mut v = build_elf32(false, DT_NULL, 0);
v[16..18].copy_from_slice(&ET_EXEC.to_le_bytes());
let exe = ExecutableFile::parse(std::io::Cursor::new(v), true).unwrap();
assert!(matches!(
exe,
ExecutableFile::Elf {
file_type: ElfFileType::Executable,
linking_type: Some(LinkingType::Static),
..
}
));
}
#[test]
fn test_parse_15() {
let mut v = build_elf64(false, DT_NULL, 0);
v[64..68].copy_from_slice(&PT_INTERP.to_le_bytes());
let exe = ExecutableFile::parse(std::io::Cursor::new(v), true).unwrap();
assert!(matches!(
exe,
ExecutableFile::Elf {
file_type: ElfFileType::Executable,
linking_type: Some(LinkingType::Dynamic),
..
}
));
}
#[test]
fn test_parse_16() {
let mut v = build_elf32(false, DT_NULL, 0);
v[64..68].copy_from_slice(&PT_INTERP.to_le_bytes());
let exe = ExecutableFile::parse(std::io::Cursor::new(v), true).unwrap();
assert!(matches!(
exe,
ExecutableFile::Elf {
file_type: ElfFileType::Executable,
linking_type: Some(LinkingType::Dynamic),
..
}
));
}
#[test]
fn test_parse_17() {
let mut v = build_elf64(false, DT_NULL, 0);
v[64..68].copy_from_slice(&PT_INTERP.to_le_bytes());
v[120..124].copy_from_slice(&PT_INTERP.to_le_bytes());
let exe = ExecutableFile::parse(std::io::Cursor::new(v), true).unwrap();
assert!(matches!(
exe,
ExecutableFile::Elf {
file_type: ElfFileType::Executable,
linking_type: Some(LinkingType::Dynamic),
..
}
));
}
#[test]
fn test_parse_18() {
let mut v = build_elf64(false, DT_NULL, 0);
v[16..18].copy_from_slice(&ET_REL.to_le_bytes());
let exe = ExecutableFile::parse(std::io::Cursor::new(v), true).unwrap();
assert!(matches!(
exe,
ExecutableFile::Elf {
file_type: ElfFileType::Relocatable,
linking_type: None,
..
}
));
}
#[test]
fn test_parse_19() {
let mut v = build_elf64(false, DT_NULL, 0);
v[16..18].copy_from_slice(&ET_CORE.to_le_bytes());
let exe = ExecutableFile::parse(std::io::Cursor::new(v), true).unwrap();
assert!(matches!(
exe,
ExecutableFile::Elf {
file_type: ElfFileType::Core,
linking_type: None,
..
}
));
}
#[test]
fn test_parse_20() {
let exe = ExecutableFile::parse(std::io::Cursor::new(build_elf64(false, DT_NULL, 0)), true)
.unwrap();
assert!(matches!(
exe,
ExecutableFile::Elf {
file_type: ElfFileType::Library,
linking_type: None,
pie: false,
..
}
));
}
#[test]
fn test_parse_21() {
let exe = ExecutableFile::parse(
std::io::Cursor::new(build_elf64(true, DT_FLAGS_1, DF_1_NOW)),
false,
)
.unwrap();
assert!(matches!(
exe,
ExecutableFile::Elf {
file_type: ElfFileType::Library,
relro: false,
..
}
));
}
#[test]
fn test_parse_22() {
let mut v = build_elf64(true, DT_FLAGS_1, DF_1_NOW);
v[120..124].copy_from_slice(&0u32.to_le_bytes());
let exe = ExecutableFile::parse(std::io::Cursor::new(v), true).unwrap();
assert!(matches!(
exe,
ExecutableFile::Elf {
file_type: ElfFileType::Library,
relro: false,
..
}
));
}
#[test]
fn test_parse_23() {
let exe = ExecutableFile::parse(
std::io::Cursor::new(build_elf64(false, DT_FLAGS_1, DF_1_PIE)),
true,
)
.unwrap();
assert!(matches!(
exe,
ExecutableFile::Elf {
file_type: ElfFileType::Executable,
pie: true,
..
}
));
}
#[test]
fn test_parse_24() {
let exe = ExecutableFile::parse(
std::io::Cursor::new(build_elf32(false, DT_FLAGS_1, DF_1_PIE)),
true,
)
.unwrap();
assert!(matches!(
exe,
ExecutableFile::Elf {
file_type: ElfFileType::Executable,
pie: true,
..
}
));
}
#[test]
fn test_parse_25() {
let mut v = build_elf64(false, DT_NULL, 0);
v[64..68].copy_from_slice(&PT_GNU_STACK.to_le_bytes());
v[68..72].copy_from_slice(&PF_X.to_le_bytes());
let exe = ExecutableFile::parse(std::io::Cursor::new(v), true).unwrap();
assert!(matches!(exe, ExecutableFile::Elf { xs: true, .. }));
}
#[test]
fn test_parse_26() {
let mut v = build_elf64(false, DT_NULL, 0);
v[64..68].copy_from_slice(&PT_GNU_STACK.to_le_bytes());
let exe = ExecutableFile::parse(std::io::Cursor::new(v), true).unwrap();
assert!(matches!(exe, ExecutableFile::Elf { xs: false, .. }));
}
#[test]
fn test_parse_27() {
let mut v = build_elf32(false, DT_NULL, 0);
v[64..68].copy_from_slice(&PT_GNU_STACK.to_le_bytes());
v[88..92].copy_from_slice(&PF_X.to_le_bytes());
let exe = ExecutableFile::parse(std::io::Cursor::new(v), true).unwrap();
assert!(matches!(exe, ExecutableFile::Elf { xs: true, .. }));
}
#[test]
fn test_parse_28() {
let mut v = build_elf32(false, DT_NULL, 0);
v[64..68].copy_from_slice(&PT_GNU_STACK.to_le_bytes());
let exe = ExecutableFile::parse(std::io::Cursor::new(v), true).unwrap();
assert!(matches!(exe, ExecutableFile::Elf { xs: false, .. }));
}
#[test]
fn test_parse_29() {
let mut v = build_elf64_be(false, DT_NULL, 0);
v[64..68].copy_from_slice(&PT_GNU_STACK.to_be_bytes());
v[68..72].copy_from_slice(&PF_X.to_be_bytes());
let exe = ExecutableFile::parse(std::io::Cursor::new(v), true).unwrap();
assert!(matches!(exe, ExecutableFile::Elf { xs: true, .. }));
}
#[test]
fn test_parse_30() {
let mut v = build_elf64(false, DT_NULL, 0);
v[64..68].copy_from_slice(&PT_GNU_STACK.to_le_bytes());
v[68..72].copy_from_slice(&PF_X.to_le_bytes());
v[120..124].copy_from_slice(&PT_GNU_STACK.to_le_bytes());
let exe = ExecutableFile::parse(std::io::Cursor::new(v), true).unwrap();
assert!(matches!(exe, ExecutableFile::Elf { xs: false, .. }));
}
#[test]
fn test_parse_31() {
let mut v = build_elf64(false, DT_NULL, 0);
v[54..56].copy_from_slice(&55u16.to_le_bytes());
assert!(matches!(
ExecutableFile::parse(std::io::Cursor::new(v), true),
Err(ElfError::Malformed)
));
}
#[test]
fn test_parse_32() {
let mut v = build_elf64(false, DT_NULL, 0);
v[56..58].copy_from_slice(&0u16.to_le_bytes());
assert!(matches!(
ExecutableFile::parse(std::io::Cursor::new(v), true),
Err(ElfError::Malformed)
));
}
#[test]
fn test_parse_33() {
let mut v = build_elf64(false, DT_NULL, 0);
v[152..160].copy_from_slice(&0u64.to_le_bytes());
assert!(matches!(
ExecutableFile::parse(std::io::Cursor::new(v), true),
Err(ElfError::Malformed)
));
}
#[test]
fn test_parse_34() {
let mut v = build_elf64(false, DT_NULL, 0);
v[152..160].copy_from_slice(&0x4001u64.to_le_bytes());
assert!(matches!(
ExecutableFile::parse(std::io::Cursor::new(v), true),
Err(ElfError::Malformed)
));
}
#[test]
fn test_parse_35() {
let mut v = build_elf64(false, DT_NULL, 0);
v[128..136].copy_from_slice(&4096u64.to_le_bytes());
assert!(matches!(
ExecutableFile::parse(std::io::Cursor::new(v), true),
Err(ElfError::Malformed)
));
}
#[test]
fn test_parse_36() {
let exe = ExecutableFile::parse(
InterruptReader(
std::io::Cursor::new(build_elf64(true, DT_FLAGS_1, DF_1_NOW)),
false,
),
true,
)
.unwrap();
assert!(matches!(exe, ExecutableFile::Elf { relro: true, .. }));
}
#[test]
fn test_parse_37() {
assert_eq!(ErrorReader.seek(SeekFrom::Start(0)).unwrap(), 0);
assert_io_error(ExecutableFile::parse(ErrorReader, true), ErrorKind::Other);
}
#[test]
fn test_parse_38() {
assert_io_error(
ExecutableFile::parse(
SeekErrorReader(std::io::Cursor::new(build_elf64(false, DT_NULL, 0))),
true,
),
ErrorKind::Other,
);
}
#[test]
fn test_parse_39() {
let mut v = build_elf64(false, DT_NULL, 0);
v.truncate(100);
assert_io_error(
ExecutableFile::parse(std::io::Cursor::new(v), true),
ErrorKind::UnexpectedEof,
);
}
#[test]
fn test_parse_40() {
let exe = ExecutableFile::parse(
std::io::Cursor::new(build_elf64(true, DT_FLAGS_1, DF_1_NOW)),
true,
)
.unwrap();
assert!(matches!(exe, ExecutableFile::Elf { relro: true, .. }));
}
#[test]
fn test_parse_41() {
let exe =
ExecutableFile::parse(std::io::Cursor::new(build_elf64(true, DT_FLAGS_1, 0)), true)
.unwrap();
assert!(matches!(exe, ExecutableFile::Elf { relro: false, .. }));
}
#[test]
fn test_parse_42() {
let exe = ExecutableFile::parse(
std::io::Cursor::new(build_elf64(false, DT_FLAGS_1, DF_1_NOW)),
true,
)
.unwrap();
assert!(matches!(exe, ExecutableFile::Elf { relro: false, .. }));
}
#[test]
fn test_parse_43() {
let exe = ExecutableFile::parse(
std::io::Cursor::new(build_elf32(true, DT_FLAGS_1, DF_1_NOW)),
true,
)
.unwrap();
assert!(matches!(
exe,
ExecutableFile::Elf {
elf_type: ElfType::Elf32,
relro: true,
..
}
));
}
#[test]
fn test_parse_44() {
let exe =
ExecutableFile::parse(std::io::Cursor::new(build_elf32(true, DT_FLAGS_1, 0)), true)
.unwrap();
assert!(matches!(exe, ExecutableFile::Elf { relro: false, .. }));
}
#[test]
fn test_parse_45() {
let exe = ExecutableFile::parse(
std::io::Cursor::new(build_elf32(false, DT_FLAGS_1, DF_1_NOW)),
true,
)
.unwrap();
assert!(matches!(exe, ExecutableFile::Elf { relro: false, .. }));
}
#[test]
fn test_parse_46() {
let exe = ExecutableFile::parse(
std::io::Cursor::new(build_elf64_be(true, DT_FLAGS_1, DF_1_NOW)),
true,
)
.unwrap();
assert!(matches!(
exe,
ExecutableFile::Elf {
elf_type: ElfType::Elf64,
relro: true,
..
}
));
}
#[test]
fn test_parse_47() {
let exe = ExecutableFile::parse(
std::io::Cursor::new(build_elf32_be(true, DT_FLAGS_1, DF_1_NOW)),
true,
)
.unwrap();
assert!(matches!(
exe,
ExecutableFile::Elf {
elf_type: ElfType::Elf32,
relro: true,
..
}
));
}
#[test]
fn test_parse_48() {
let exe = ExecutableFile::parse(
std::io::Cursor::new(build_elf64(true, DT_BIND_NOW, 1)),
true,
)
.unwrap();
assert!(matches!(exe, ExecutableFile::Elf { relro: true, .. }));
}
#[test]
fn test_parse_49() {
let exe = ExecutableFile::parse(
std::io::Cursor::new(build_elf64(true, DT_BIND_NOW, 0)),
true,
)
.unwrap();
assert!(matches!(exe, ExecutableFile::Elf { relro: false, .. }));
}
#[test]
fn test_parse_50() {
let exe = ExecutableFile::parse(
std::io::Cursor::new(build_elf64(true, DT_FLAGS, DF_BIND_NOW)),
true,
)
.unwrap();
assert!(matches!(exe, ExecutableFile::Elf { relro: true, .. }));
}
#[test]
fn test_parse_51() {
let exe = ExecutableFile::parse(
std::io::Cursor::new(build_elf32(true, DT_FLAGS, DF_BIND_NOW)),
true,
)
.unwrap();
assert!(matches!(exe, ExecutableFile::Elf { relro: true, .. }));
}
#[test]
fn test_parse_52() {
let mut v = build_elf64(true, DT_FLAGS_1, DF_1_NOW);
v[152..160].copy_from_slice(&20u64.to_le_bytes());
let exe = ExecutableFile::parse(std::io::Cursor::new(v), true).unwrap();
assert!(matches!(exe, ExecutableFile::Elf { relro: true, .. }));
}
#[test]
fn test_parse_53() {
let mut v = build_elf64(true, DT_FLAGS_1, DF_1_NOW);
v[152..160].copy_from_slice(&24u64.to_le_bytes());
v[192..200].copy_from_slice(&DT_FLAGS.to_le_bytes());
let exe = ExecutableFile::parse(std::io::Cursor::new(v), true).unwrap();
assert!(matches!(exe, ExecutableFile::Elf { relro: true, .. }));
}
#[test]
fn test_parse_54() {
let mut v = build_elf64(true, DT_FLAGS_1, DF_1_NOW | DF_1_PIE);
v.extend_from_slice(&[0u8; 16]);
v[152..160].copy_from_slice(&48u64.to_le_bytes());
v[192..200].copy_from_slice(&DT_FLAGS_1.to_le_bytes());
v[200..208].copy_from_slice(&DF_1_NOW.to_le_bytes());
let exe = ExecutableFile::parse(std::io::Cursor::new(v), true).unwrap();
assert!(matches!(
exe,
ExecutableFile::Elf {
relro: true,
pie: false,
..
}
));
}
#[test]
fn test_parse_55() {
let exe = ExecutableFile::parse(
std::io::Cursor::new(build_elf64(false, DT_FLAGS, DF_TEXTREL)),
true,
)
.unwrap();
assert!(matches!(exe, ExecutableFile::Elf { textrel: true, .. }));
}
#[test]
fn test_parse_56() {
let exe = ExecutableFile::parse(
std::io::Cursor::new(build_elf64(false, DT_TEXTREL, 0)),
true,
)
.unwrap();
assert!(matches!(exe, ExecutableFile::Elf { textrel: true, .. }));
}
#[test]
fn test_parse_57() {
let exe = ExecutableFile::parse(
std::io::Cursor::new(build_elf32(false, DT_FLAGS, DF_TEXTREL)),
true,
)
.unwrap();
assert!(matches!(exe, ExecutableFile::Elf { textrel: true, .. }));
}
#[test]
fn test_parse_58() {
let exe = ExecutableFile::parse(
std::io::Cursor::new(build_elf32(false, DT_TEXTREL, 0)),
true,
)
.unwrap();
assert!(matches!(exe, ExecutableFile::Elf { textrel: true, .. }));
}
#[test]
fn test_parse_59() {
let mut v = build_elf64(true, DT_FLAGS, DF_TEXTREL);
v.extend_from_slice(&[0u8; 16]);
v[152..160].copy_from_slice(&48u64.to_le_bytes());
v[192..200].copy_from_slice(&DT_FLAGS.to_le_bytes());
v[200..208].copy_from_slice(&DF_BIND_NOW.to_le_bytes());
let exe = ExecutableFile::parse(std::io::Cursor::new(v), true).unwrap();
assert!(matches!(
exe,
ExecutableFile::Elf {
relro: false,
textrel: true,
..
}
));
}
#[test]
fn test_is_elf_file_1() {
assert!(
ExecutableFile::is_elf_file(std::io::Cursor::new(build_elf64(false, DT_NULL, 0)))
.unwrap()
);
}
#[test]
fn test_is_elf_file_2() {
assert!(!ExecutableFile::is_elf_file(std::io::Cursor::new(b"\x7FEL".to_vec())).unwrap());
}
#[test]
fn test_is_elf_file_3() {
assert!(!ExecutableFile::is_elf_file(std::io::Cursor::new(b"NOPE".to_vec())).unwrap());
}
#[test]
fn test_is_elf_file_4() {
assert!(ExecutableFile::is_elf_file(InterruptReader(
std::io::Cursor::new(build_elf64(false, DT_NULL, 0)),
false,
))
.unwrap());
}
#[test]
fn test_is_elf_file_5() {
assert_io_error(ExecutableFile::is_elf_file(ErrorReader), ErrorKind::Other);
}
#[test]
fn test_is_valid_elf_file_1() {
assert!(
ExecutableFile::is_valid_elf_file(std::io::Cursor::new(build_elf64(false, DT_NULL, 0)))
.unwrap()
);
assert!(
ExecutableFile::is_valid_elf_file(std::io::Cursor::new(build_elf32(false, DT_NULL, 0)))
.unwrap()
);
}
#[test]
fn test_is_valid_elf_file_2() {
assert!(
!ExecutableFile::is_valid_elf_file(std::io::Cursor::new(b"\x7FELF".to_vec())).unwrap()
);
}
#[test]
fn test_is_valid_elf_file_3() {
assert!(!ExecutableFile::is_valid_elf_file(std::io::Cursor::new(vec![0xAA; 16])).unwrap());
}
#[test]
fn test_is_valid_elf_file_4() {
let mut v = build_elf64(false, DT_NULL, 0);
v[EI_VERSION] = 0;
assert!(!ExecutableFile::is_valid_elf_file(std::io::Cursor::new(v)).unwrap());
}
#[test]
fn test_is_valid_elf_file_5() {
let mut v = build_elf64(false, DT_NULL, 0);
v[EI_DATA] = 9;
assert!(!ExecutableFile::is_valid_elf_file(std::io::Cursor::new(v)).unwrap());
}
#[test]
fn test_is_valid_elf_file_6() {
let mut v = build_elf64(false, DT_NULL, 0);
v[EI_CLASS] = 9;
assert!(!ExecutableFile::is_valid_elf_file(std::io::Cursor::new(v)).unwrap());
}
#[test]
fn test_is_valid_elf_file_7() {
assert!(ExecutableFile::is_valid_elf_file(InterruptReader(
std::io::Cursor::new(build_elf64(false, DT_NULL, 0)),
false,
))
.unwrap());
}
#[test]
fn test_is_valid_elf_file_8() {
assert_io_error(
ExecutableFile::is_valid_elf_file(ErrorReader),
ErrorKind::Other,
);
}
fn script_test_dir() -> (std::fs::File, std::path::PathBuf) {
let _ = crate::fd::open_static_proc(nix::fcntl::OFlag::empty());
let dir = std::env::temp_dir();
let fd = std::fs::File::open(&dir).unwrap();
(fd, dir)
}
#[test]
fn test_is_script_file_1() {
let (fd, dir) = script_test_dir();
let name = format!("syd-test-elf-script-{}", std::process::id());
std::fs::write(dir.join(&name), "#!/bin/sh\n").unwrap();
let path = crate::path::XPathBuf::from(name.clone());
assert!(ExecutableFile::is_script_file(&fd, &path).unwrap());
let _ = std::fs::remove_file(dir.join(&name));
}
#[test]
fn test_is_script_file_2() {
let (fd, dir) = script_test_dir();
let name = format!("syd-test-elf-noscript-{}", std::process::id());
std::fs::write(dir.join(&name), "ELF\n").unwrap();
let path = crate::path::XPathBuf::from(name.clone());
assert!(!ExecutableFile::is_script_file(&fd, &path).unwrap());
let _ = std::fs::remove_file(dir.join(&name));
}
#[test]
fn test_is_script_file_3() {
let (fd, _) = script_test_dir();
let path =
crate::path::XPathBuf::from(format!("syd-test-elf-enoent-{}", std::process::id()));
assert_io_error(
ExecutableFile::is_script_file(&fd, &path),
ErrorKind::NotFound,
);
}
#[test]
fn test_read_dyn_word_1() {
let bytes = 0x1122334455667788u64.to_le_bytes();
assert_eq!(
read_dyn_word(&bytes, ElfType::Elf64, false).unwrap(),
0x1122334455667788
);
}
#[test]
fn test_read_dyn_word_2() {
let bytes = 0x1122334455667788u64.to_be_bytes();
assert_eq!(
read_dyn_word(&bytes, ElfType::Elf64, true).unwrap(),
0x1122334455667788
);
}
#[test]
fn test_read_dyn_word_3() {
let bytes = 0x11223344u32.to_le_bytes();
assert_eq!(
read_dyn_word(&bytes, ElfType::Elf32, false).unwrap(),
0x11223344
);
}
#[test]
fn test_read_dyn_word_4() {
let bytes = 0x11223344u32.to_be_bytes();
assert_eq!(
read_dyn_word(&bytes, ElfType::Elf32, true).unwrap(),
0x11223344
);
}
#[test]
fn test_read_dyn_word_5() {
assert!(matches!(
read_dyn_word(&[0x01], ElfType::Elf64, false),
Err(ElfError::Malformed)
));
}
#[test]
fn test_read_u16_1() {
assert_eq!(read_u16_be(&[0x01, 0x02]).unwrap(), 0x0102);
assert_eq!(read_u16_le(&[0x01, 0x02]).unwrap(), 0x0201);
assert!(matches!(read_u16_be(&[0x01]), Err(ElfError::Malformed)));
}
#[test]
fn test_read_u32_1() {
assert_eq!(read_u32_be(&[0x00, 0x00, 0x01, 0x00]).unwrap(), 256);
assert_eq!(read_u32_le(&[0x00, 0x01, 0x00, 0x00]).unwrap(), 256);
assert!(matches!(
read_u32_be(&[0x01, 0x02]),
Err(ElfError::Malformed)
));
}
#[test]
fn test_read_u64_1() {
let bytes = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42];
assert_eq!(read_u64_be(&bytes).unwrap(), 0x42);
}
#[test]
fn test_read_u64_2() {
let bytes = [0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
assert_eq!(read_u64_le(&bytes).unwrap(), 0x42);
}
#[test]
fn test_read_u64_3() {
assert!(matches!(
read_u64_be(&[0x01, 0x02, 0x03]),
Err(ElfError::Malformed)
));
}
}