Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: define the Error type #70

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions src/dynamic.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use core::fmt;
use {P32, P64};
use zero::Pod;
use crate::Error;

#[derive(Debug)]
#[repr(C)]
Expand Down Expand Up @@ -58,34 +59,34 @@ pub enum Tag<P> {
macro_rules! impls {
($p: ident) => {
impl Dynamic<$p> {
pub fn get_tag(&self) -> Result<Tag<$p>, &'static str> {
pub fn get_tag(&self) -> Result<Tag<$p>, Error> {
self.tag.as_tag()
}

pub fn get_val(&self) -> Result<$p, &'static str> {
pub fn get_val(&self) -> Result<$p, Error> {
match self.get_tag()? {
Tag::Needed | Tag::PltRelSize | Tag::RelaSize | Tag::RelaEnt | Tag::StrSize |
Tag::SymEnt | Tag::SoName | Tag::RPath | Tag::RelSize | Tag::RelEnt | Tag::PltRel |
Tag::InitArraySize | Tag::FiniArraySize | Tag::RunPath | Tag::Flags |
Tag::PreInitArraySize | Tag::Flags1 | Tag::OsSpecific(_) |
Tag::ProcessorSpecific(_) => Ok(self.un),
_ => Err("Invalid value"),
_ => Err(Error::ValueNotContained),
}
}

pub fn get_ptr(&self) -> Result<$p, &'static str> {
pub fn get_ptr(&self) -> Result<$p, Error> {
match self.get_tag()? {
Tag::Pltgot | Tag::Hash | Tag::StrTab | Tag::SymTab | Tag::Rela | Tag::Init | Tag::Fini |
Tag::Rel | Tag::Debug | Tag::JmpRel | Tag::InitArray | Tag::FiniArray |
Tag::PreInitArray | Tag::SymTabShIndex | Tag::OsSpecific(_) | Tag::ProcessorSpecific(_)
=> Ok(self.un),
_ => Err("Invalid ptr"),
_ => Err(Error::PointerNotContained),
}
}
}

impl Tag_<$p> {
fn as_tag(self) -> Result<Tag<$p>, &'static str> {
fn as_tag(self) -> Result<Tag<$p>, Error> {
match self.0 {
0 => Ok(Tag::Null),
1 => Ok(Tag::Needed),
Expand Down Expand Up @@ -124,7 +125,7 @@ macro_rules! impls {
0x6ffffffb => Ok(Tag::Flags1),
t if (0x6000000D..0x70000000).contains(&t) => Ok(Tag::OsSpecific(t)),
t if (0x70000000..0x80000000).contains(&t) => Ok(Tag::ProcessorSpecific(t)),
_ => Err("Invalid tag value"),
_ => Err(Error::InvalidTag),
}
}
}
Expand Down
100 changes: 100 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use core::fmt;

/// Errors returned by the methods and functions of this crate.
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Error {
/// The magic number of the given ELF file is invalid.
InvalidMagic,
/// The class of the given ELF file is invalid.
InvalidClass,
/// The section type is invalid.
InvalidSectionType,
/// The segment type is invalid.
InvalidSegmentType,
/// The version of the given ELF file is invalid.
InvalidVersion,
/// The data format of the given ELF file is invalid.
InvalidDataFormat,
/// The symbol's binding is invalid.
InvalidSymbolBinding,
/// The symbol's type is invalid.
InvalidSymbolType,
/// The compression type is invalid.
InvalidCompressionType,
/// The tag of the dynamic link information is invalid.
InvalidTag,
/// The length of the given ELF file is too short.
FileTooShort,
/// The length of the section is too short.
SectionTooShort,
/// Program header is not found.
ProgramHeaderNotFound,
/// The `.symtab_shndx` section is not found.
SymtabShndxNotFound,
/// The `.strtab` section is not found.
StrtabNotFound,
/// The `.dynstr` section is not found.
DynstrNotFound,
/// The section type is `NULL`.
NullSection,
/// The section header index is one of the followings:
/// - `SHN_UNDEF`
/// - `SHN_ABS`
/// - `SHN_COMMON`
ReservedSectionHeaderIndex,
/// The size of each program header recorded in the file header is different from the actual
/// size.
ProgramHeaderSizeMismatch,
/// The class specified in the file header is different from the actual class.
ClassMismatch,
/// The segment whose type is `PT_SHLIB` should not be used.
UseOfShLib,
/// The alignments of the virtual address, offset, and align recorded in the program header are
/// the invalid combination.
MisalignedAddressAndOffset,
/// Failed to decompress the section.
DecompressionError,
/// The dynamic link information does not contain a value, but a pointer.
ValueNotContained,
/// The dynamic link information does not contain a pointer, but a value.
PointerNotContained,
/// The 32-bit binaries are not supported.
Binary32BitNotSupported,
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::InvalidMagic => "The magic number of the given ELF file is invalid.",
Self::InvalidClass => "The class of the given ELF file is invalid.",
Self::InvalidSectionType => "The section type is invalid.",
Self::InvalidSegmentType => "The segment type is invalid.",
Self::InvalidVersion => "The version of the given ELF file is invalid.",
Self::InvalidDataFormat => "The data format of the given ELF file is invalid.",
Self::InvalidSymbolBinding => "The symbol's binding is invalid.",
Self::InvalidSymbolType => "The symbol's type is invalid.",
Self::InvalidCompressionType => "The compression type is invalid.",
Self::InvalidTag => "The tag of the dynamic link information is invalid.",
Self::FileTooShort => "The length of the given ELF file is too short.",
Self::SectionTooShort => "The length of the section is too short.",
Self::ProgramHeaderNotFound => "The program header is not found.",
Self::SymtabShndxNotFound => "The `.symtab_shndx` section is not found.",
Self::StrtabNotFound => "The `.strtab` section is not found.",
Self::DynstrNotFound => "The `.dynstr` section is not found.",
Self::NullSection => "The section type is `NULL`.",
Self::ReservedSectionHeaderIndex => "The section header index is reserved.",
Self::ProgramHeaderSizeMismatch => "The size of each program header recorded in the file header is different from the actual size.",
Self::ClassMismatch => "The class specified in the file header is different from the actual class.",
Self::UseOfShLib => "The segment whose type is `PT_SHLIB` should not be used.",
Self::MisalignedAddressAndOffset => "The alignments of the virtual address, offset, and align recorded in the program header are the invalid combination.",
Self::DecompressionError => "Failed to decompress the section.",
Self::ValueNotContained => "The dynamic link information does not contain a value, but a pointer.",
Self::PointerNotContained => "The dynamic link information does not contain a pointer, but a value.",
Self::Binary32BitNotSupported => "The 32-bit binaries are not supported.",
}
)
}
}
36 changes: 19 additions & 17 deletions src/header.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
use core::fmt;
use core::mem;

use crate::Error;

use {P32, P64, ElfFile};
use zero::{read, Pod};


pub fn parse_header<'a>(input: &'a [u8]) -> Result<Header<'a>, &'static str> {
pub fn parse_header<'a>(input: &'a [u8]) -> Result<Header<'a>, Error> {
let size_pt1 = mem::size_of::<HeaderPt1>();
if input.len() < size_pt1 {
return Err("File is shorter than the first ELF header part");
return Err(Error::FileTooShort);
}

let header_1: &'a HeaderPt1 = read(&input[..size_pt1]);
if header_1.magic != MAGIC {
return Err("Did not find ELF magic number");
return Err(Error::InvalidMagic);
}

let header_2 = match header_1.class() {
Class::None | Class::Other(_) => return Err("Invalid ELF class"),
Class::None | Class::Other(_) => return Err(Error::InvalidClass),
Class::ThirtyTwo => {
let size_pt2 = mem::size_of::<HeaderPt2_<P32>>();
if input.len() < size_pt1 + size_pt2 {
return Err("File is shorter than ELF headers");
return Err(Error::FileTooShort);
}
let header_2: &'a HeaderPt2_<P32> =
read(&input[size_pt1..size_pt1 + mem::size_of::<HeaderPt2_<P32>>()]);
Expand All @@ -30,7 +32,7 @@ pub fn parse_header<'a>(input: &'a [u8]) -> Result<Header<'a>, &'static str> {
Class::SixtyFour => {
let size_pt2 = mem::size_of::<HeaderPt2_<P64>>();
if input.len() < size_pt1 + size_pt2 {
return Err("File is shorter than ELF headers");
return Err(Error::FileTooShort);
}
let header_2: &'a HeaderPt2_<P64> =
read(&input[size_pt1..size_pt1 + mem::size_of::<HeaderPt2_<P64>>()]);
Expand Down Expand Up @@ -81,6 +83,8 @@ pub struct HeaderPt1 {
pub padding: [u8; 7],
}

const _CONST_CHECK_HEADER_PT1_SIZE: [(); !(mem::size_of::<HeaderPt1>() == 16) as usize] = [];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved the size check of HeaderPt1 from sanity_check as it can be done in a const expression.


unsafe impl Pod for HeaderPt1 {}

impl HeaderPt1 {
Expand Down Expand Up @@ -425,27 +429,25 @@ pub enum Machine {

// TODO any more constants that need to go in here?

pub fn sanity_check(file: &ElfFile) -> Result<(), &'static str> {
check!(mem::size_of::<HeaderPt1>() == 16);
check!(file.header.pt1.magic == MAGIC, "bad magic number");
pub fn sanity_check(file: &ElfFile) -> Result<(), Error> {
check!(file.header.pt1.magic == MAGIC, Error::InvalidMagic);
let pt2 = &file.header.pt2;
check!(mem::size_of::<HeaderPt1>() + pt2.size() == pt2.header_size() as usize,
"header_size does not match size of header");
check!(mem::size_of::<HeaderPt1>() + pt2.size() == pt2.header_size() as usize, Error::ProgramHeaderSizeMismatch);
match (&file.header.pt1.class(), &file.header.pt2) {
(&Class::None, _) => return Err("No class"),
(&Class::None, _) => return Err(Error::InvalidClass),
(&Class::ThirtyTwo, &HeaderPt2::Header32(_)) |
(&Class::SixtyFour, &HeaderPt2::Header64(_)) => {}
_ => return Err("Mismatch between specified and actual class"),
_ => return Err(Error::ClassMismatch),
}
check!(!file.header.pt1.version.is_none(), "no version");
check!(!file.header.pt1.data.is_none(), "no data format");
check!(!file.header.pt1.version.is_none(), Error::InvalidVersion);
check!(!file.header.pt1.data.is_none(), Error::InvalidDataFormat);

check!(pt2.ph_offset() + (pt2.ph_entry_size() as u64) * (pt2.ph_count() as u64) <=
file.input.len() as u64,
"program header table out of range");
Error::FileTooShort);
check!(pt2.sh_offset() + (pt2.sh_entry_size() as u64) * (pt2.sh_count() as u64) <=
file.input.len() as u64,
"section header table out of range");
Error::FileTooShort);

// TODO check that SectionHeader_ is the same size as sh_entry_size, depending on class

Expand Down
36 changes: 17 additions & 19 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,9 @@

// TODO move to a module
macro_rules! check {
($e:expr) => {
($e:expr, $err: expr) => {
if !$e {
return Err("");
}
};
($e:expr, $msg: expr) => {
if !$e {
return Err($msg);
return Err($err);
}
};
}
Expand All @@ -26,6 +21,7 @@ extern crate zero;

pub mod header;
pub mod sections;
mod error;
pub mod program;
pub mod symbol_table;
pub mod dynamic;
Expand All @@ -36,6 +32,8 @@ use sections::{SectionHeader, SectionIter};
use program::{ProgramHeader, ProgramIter};
use zero::{read, read_str};

pub use error::Error;

pub type P32 = u32;
pub type P64 = u64;

Expand All @@ -46,11 +44,11 @@ pub struct ElfFile<'a> {
}

impl<'a> ElfFile<'a> {
pub fn new(input: &'a [u8]) -> Result<ElfFile<'a>, &'static str> {
pub fn new(input: &'a [u8]) -> Result<ElfFile<'a>, Error> {
header::parse_header(input).map(|header| ElfFile {input, header})
}

pub fn section_header(&self, index: u16) -> Result<SectionHeader<'a>, &'static str> {
pub fn section_header(&self, index: u16) -> Result<SectionHeader<'a>, Error> {
sections::parse_section_header(self.input, self.header, index)
}

Expand All @@ -61,7 +59,7 @@ impl<'a> ElfFile<'a> {
}
}

pub fn program_header(&self, index: u16) -> Result<ProgramHeader<'a>, &'static str> {
pub fn program_header(&self, index: u16) -> Result<ProgramHeader<'a>, Error> {
program::parse_program_header(self.input, self.header, index)
}

Expand All @@ -72,20 +70,20 @@ impl<'a> ElfFile<'a> {
}
}

pub fn get_shstr(&self, index: u32) -> Result<&'a str, &'static str> {
pub fn get_shstr(&self, index: u32) -> Result<&'a str, Error> {
self.get_shstr_table().map(|shstr_table| read_str(&shstr_table[(index as usize)..]))
}

pub fn get_string(&self, index: u32) -> Result<&'a str, &'static str> {
let header = self.find_section_by_name(".strtab").ok_or("no .strtab section")?;
if header.get_type()? != sections::ShType::StrTab {
return Err("expected .strtab to be StrTab");
}
pub fn get_string(&self, index: u32) -> Result<&'a str, Error> {
let header = self.find_section_by_name(".strtab").ok_or(Error::StrtabNotFound)?;

assert_eq!(header.get_type()?, sections::ShType::StrTab, "expected .strtab to be StrTab");

Ok(read_str(&header.raw_data(self)[(index as usize)..]))
}

pub fn get_dyn_string(&self, index: u32) -> Result<&'a str, &'static str> {
let header = self.find_section_by_name(".dynstr").ok_or("no .dynstr section")?;
pub fn get_dyn_string(&self, index: u32) -> Result<&'a str, Error> {
let header = self.find_section_by_name(".dynstr").ok_or(Error::DynstrNotFound)?;
Ok(read_str(&header.raw_data(self)[(index as usize)..]))
}

Expand All @@ -103,7 +101,7 @@ impl<'a> ElfFile<'a> {
None
}

fn get_shstr_table(&self) -> Result<&'a [u8], &'static str> {
fn get_shstr_table(&self) -> Result<&'a [u8], Error> {
// TODO cache this?
let header = self.section_header(self.header.pt2.sh_str_index());
header.map(|h| &self.input[(h.offset() as usize)..])
Expand Down
Loading