diff --git a/.gitignore b/.gitignore index 979737f..b4770c0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /target /assets + +/docs diff --git a/docs/2-study-and-analysis-of-elf-vulnerabilities-in-linux.pdf b/docs/2-study-and-analysis-of-elf-vulnerabilities-in-linux.pdf deleted file mode 100644 index bca3758..0000000 Binary files a/docs/2-study-and-analysis-of-elf-vulnerabilities-in-linux.pdf and /dev/null differ diff --git a/docs/ELF_Format.pdf b/docs/ELF_Format.pdf deleted file mode 100644 index 34b9e8c..0000000 Binary files a/docs/ELF_Format.pdf and /dev/null differ diff --git a/docs/linkers_and_loaders.pdf b/docs/linkers_and_loaders.pdf deleted file mode 100644 index c82383c..0000000 Binary files a/docs/linkers_and_loaders.pdf and /dev/null differ diff --git a/src/error.rs b/src/error.rs index e7c196a..fc13a63 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,3 +1,8 @@ +use std::{ + fmt::{Debug, Display, Formatter}, + path::PathBuf, +}; + #[derive(Debug)] pub enum Error { IOError(std::io::Error), @@ -6,6 +11,21 @@ pub enum Error { MissingSectionHeader(&'static str), MissingSectionData(&'static str), InvalidSectionData, + LinkingError(Trace), +} + +// "backtrace" for error origin +#[derive(Debug)] +pub struct Trace { + origin: PathBuf, + offset: u64, // 0 indicates unknown/invalid + source_info: Option>, +} + +pub trait SourceInfo: Debug + Display { + fn line(&self) -> u64; + + // todo } impl From for Error { @@ -13,3 +33,15 @@ impl From for Error { Self::IOError(err) } } + +impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "TODO!") + } +} + +impl Display for Trace { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "TODO!") + } +} diff --git a/src/formats/elf.rs b/src/formats/elf.rs index 6c0161a..2129019 100644 --- a/src/formats/elf.rs +++ b/src/formats/elf.rs @@ -1,63 +1,84 @@ -use std::convert::TryFrom; +use std::{ + fmt::Display, + path::{Path, PathBuf}, +}; -use crate::error::Error; -use crate::parts::{Lazy, Relocatable, Symbol, Text}; +use crate::linker::LinkState; +use crate::parts::{Lazy, Relocatable, Storage, Symbol}; +use crate::{error::Error, parts::BSI}; use xmas_elf::header::Type as ElfType; -use xmas_elf::sections::*; use xmas_elf::ElfFile; -impl<'a> Lazy<&'a str, ElfFile<'a>> for Symbol<'a> { - fn value(&self, src: &ElfFile<'a>) -> Result<&'a str, Error> { - src.get_string(self.index) - .map_err(|e| Error::StringError(e)) - } +mod section; +mod section_iter; - fn resolved(&self) -> bool { - self.str_ref.is_some() - } +use section_iter::SectionIter; + +pub struct ElfObject<'data> { + origin: PathBuf, + elf: ElfFile<'data>, } -impl<'a> TryFrom> for Text<'a> { - type Error = Error; +impl<'data> ElfObject<'data> { + pub fn new(storage: &'data mut Storage) -> Result { + let origin = storage.origin()?; + let elf = ElfFile::new(storage.bytes()?).map_err(|_| Error::InvalidObjectType(0))?; - fn try_from(data: SectionData<'a>) -> Result { - match data { - SectionData::Undefined(bytes) => Ok(Text { bytes }), - _ => Err(Error::InvalidSectionData), // TODO: more context needed - } + is_relocatable(&elf)?; + let result = ElfObject { origin, elf }; + + Ok(result) } } -impl<'a> Relocatable<'a> { - pub fn from_elf(origin: &'a str, elf: &'a ElfFile) -> Result { - is_relocatable(elf)?; - let mut result = Relocatable::new(origin); +impl Relocatable for ElfObject<'_> { + fn origin(&self) -> &Path { + &self.origin + } - for header in elf.section_iter() { - // .text sections > 0 only - if is_text_section(elf, &header)? { - let data = header - .get_data(&elf) - .map_err(|e| Error::MissingSectionData(e))?; + fn sections(&self) -> BSI<'_> { + Box::new(SectionIter { + elf: &self.elf, + iter: self.elf.section_iter(), + }) + } +} - result.texts.push(Text::try_from(data)?); - } +impl Display for ElfObject<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "==={:?}===\n>Symbols:\n{}\n>Sections:\n", + self.origin().file_name().unwrap(), + "TODO" + )?; + + for section in self.sections() { + let u = section.unwrap(); + write!(f, "{}", u)?; } - Ok(result) + Ok(()) + } +} + +impl LinkState<'_> { + pub fn new() -> Self { + LinkState { + relocatables: Vec::new(), // it's a trait thing + } } } -fn is_text_section(elf: &ElfFile, header: &SectionHeader) -> Result { - let name = header.get_name(&elf).unwrap_or("null"); - let h_type = header - .get_type() - .map_err(|e| Error::MissingSectionHeader(e))?; +impl<'data> Lazy<&'data str, ElfFile<'data>> for Symbol<'data> { + fn value(&self, src: &ElfFile<'data>) -> Result<&'data str, Error> { + src.get_string(self.index) + .map_err(|e| Error::StringError(e)) + } - Ok(h_type == ShType::ProgBits - && header.flags() == (SHF_EXECINSTR | SHF_ALLOC) - && name.starts_with(".text") - && header.size() > 0) + fn resolved(&self) -> bool { + self.str_ref.is_some() + } } fn is_relocatable(elf: &ElfFile) -> Result<(), Error> { diff --git a/src/formats/elf/section.rs b/src/formats/elf/section.rs new file mode 100644 index 0000000..5e06e42 --- /dev/null +++ b/src/formats/elf/section.rs @@ -0,0 +1,110 @@ +use std::iter::Cloned; + +use crate::{ + error::Error, + parts::{Section, SectionInfo, SectionIterResult}, +}; +use xmas_elf::{ + sections::{SectionData, SectionHeader, SectionIter as ElfIter, ShType}, + ElfFile, +}; + +impl<'data> Section<'data> { + pub fn iterate( + elf: &'data ElfFile<'data>, + iter: &mut ElfIter<'data, 'data>, + ) -> SectionIterResult<'data> { + if let Some(h) = iter.next() { + let h_result = h.get_type().map_err(|e| Error::MissingSectionHeader(e)); + return match h_result { + Ok(h_type) => match h_type { + // text, [ro]data + ShType::ProgBits => { + let s_name = h.get_name(&elf).unwrap_or("null"); + match section_data_to_iterator(elf, h) { + Ok(i) => Section::from_name(s_name, &h, Some(Box::new(i))), + Err(err) => SectionIterResult::Err(err), + } + } + // bss + ShType::NoBits => { + let si = SectionInfo { + size: h.size(), + data: None, + offset: h.offset(), + }; + let s = Section::Bss(si); + SectionIterResult::Ok(s) + } + _ => SectionIterResult::Skip, + }, + Err(err) => SectionIterResult::Err(err), + }; + } + + SectionIterResult::None + } + + fn from_name( + name: &str, + header: &SectionHeader, + data: Option + 'data>>, + ) -> SectionIterResult<'data> { + if name.starts_with(".text") { + if header.size() == 0 { + return SectionIterResult::Skip; // empty texts + } + let iter = match data { + Some(i) => i, + None => return SectionIterResult::Err(Error::MissingSectionData(".text")), + }; + let si = SectionInfo { + size: header.size(), + data: Some(iter), + offset: header.offset(), + }; + return SectionIterResult::Ok(Section::Text(si)); + } else if name.starts_with(".rodata") || name.starts_with(".data") { + if header.size() == 0 { + return SectionIterResult::Skip; // empty data + } + let iter = match data { + Some(i) => i, + None => return SectionIterResult::Err(Error::MissingSectionData(".data")), + }; + let si = SectionInfo { + size: header.size(), + data: Some(iter), + offset: header.offset(), + }; + return SectionIterResult::Ok(Section::Data(si)); + } else if name.starts_with(".bss") { + let si = SectionInfo { + size: header.size(), + data: None, + offset: header.offset(), + }; + return SectionIterResult::Ok(Section::Bss(si)); + } + + SectionIterResult::Skip + } +} + +fn section_data_to_iterator<'file>( + elf: &'file ElfFile, + header: SectionHeader<'file>, +) -> Result>, Error> { + let data = header + .get_data(&elf) + .map_err(|e| Error::MissingSectionData(e))?; + + match data { + SectionData::Empty => Ok(([]).iter().cloned()), + SectionData::Undefined(bytes) => { + let iter = bytes.iter().cloned(); + Ok(iter) + } + _ => Err(Error::InvalidSectionData), // TODO: more context needed + } +} diff --git a/src/formats/elf/section_iter.rs b/src/formats/elf/section_iter.rs new file mode 100644 index 0000000..cccb41a --- /dev/null +++ b/src/formats/elf/section_iter.rs @@ -0,0 +1,24 @@ +use crate::{ + error::Error, + parts::{Section, SectionIterResult}, +}; +use xmas_elf::sections::SectionIter as ElfIter; +use xmas_elf::ElfFile; + +pub struct SectionIter<'data> { + pub elf: &'data ElfFile<'data>, + pub iter: ElfIter<'data, 'data>, +} + +impl<'data> Iterator for SectionIter<'data> { + type Item = Result, Error>; + + fn next(&mut self) -> Option { + match Section::iterate(&self.elf, &mut self.iter) { + SectionIterResult::Err(err) => Some(Err(err)), + SectionIterResult::None => None, + SectionIterResult::Skip => self.next(), + SectionIterResult::Ok(r) => Some(Ok(r)), + } + } +} diff --git a/src/linker.rs b/src/linker.rs index c1b8254..73eb178 100644 --- a/src/linker.rs +++ b/src/linker.rs @@ -1,27 +1,40 @@ use std::fmt::Display; -use crate::error::Error; -use crate::parts::Relocatable; +use crate::{error::Error, parts::Relocatable}; -#[derive(Default)] -pub struct LinkState<'a> { - relocatables: Vec>, +pub struct LinkState<'data> { + pub relocatables: Vec>, } -impl<'a> LinkState<'a> { - pub fn add_relocatable(&mut self, relocatable: Relocatable<'a>) -> Result<(), Error> { +impl<'data> LinkState<'data> { + pub fn add_relocatable( + &mut self, + relocatable: Box, + ) -> Result<(), Error> { self.relocatables.push(relocatable); Ok(()) } + + pub fn total_size(&self) -> Result { + let mut result = 0u64; + + for o in self.relocatables.iter() { + for s in o.sections() { + result += s?.size()?; + } + } + + Ok(result) + } } impl Display for LinkState<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!(f, "===Relocatables===")?; - for r in self.relocatables.iter() { - writeln!(f, "{}", r)?; + for r in &self.relocatables { + write!(f, "{}", r)?; } Ok(()) diff --git a/src/main.rs b/src/main.rs index 63dc852..3b5b345 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,11 @@ -use std::fs; -use xmas_elf::ElfFile; - mod error; mod formats; mod linker; mod parts; +use formats::ElfObject; use linker::LinkState; -use parts::Relocatable; +use parts::Storage; fn main() { let mut args = std::env::args().peekable(); @@ -15,25 +13,22 @@ fn main() { // can this miss? args.next().unwrap(); - let mut contents: Vec<(String, Vec)> = Vec::new(); - let mut parsed: Vec<(&str, ElfFile)> = Vec::new(); - let mut linker = LinkState::default(); + let mut contents: Vec = Vec::new(); + let mut linker = LinkState::new(); while args.peek().is_some() { let fpath = args.next().expect("Unexpected peek-a-boo"); - let raw = fs::read(&fpath).expect("Error reading file"); - contents.push((fpath, raw)); + let stored = Storage::new(fpath.into()); + contents.push(stored); } - for data in contents.iter() { - let elf = ElfFile::new(&data.1).expect("Error parsing ELF file"); - parsed.push((&data.0, elf)); + for storage in contents.iter_mut() { + let relocatable = ElfObject::new(storage).unwrap(); + linker.add_relocatable(Box::new(relocatable)).unwrap(); } - for elf in parsed.iter() { - let relocatable = Relocatable::from_elf(elf.0, &elf.1).unwrap(); - linker.add_relocatable(relocatable).unwrap(); - } + let total_size = linker.total_size().expect("Size to be known"); + println!("to allocate: {}", total_size); - println!("{}", linker); + println!("all: \n{}", linker); } diff --git a/src/parts.rs b/src/parts.rs index d511345..995d3be 100644 --- a/src/parts.rs +++ b/src/parts.rs @@ -1,9 +1,11 @@ mod lazy; mod relocatable; +mod section; +mod storage; mod symbol; -mod segments; pub use lazy::*; pub use relocatable::*; +pub use section::*; +pub use storage::*; pub use symbol::*; -pub use segments::*; diff --git a/src/parts/lazy.rs b/src/parts/lazy.rs index 77849ae..44732e0 100644 --- a/src/parts/lazy.rs +++ b/src/parts/lazy.rs @@ -1,5 +1,4 @@ use crate::error::Error; - pub trait Lazy { /// Resolve VALUE, blocking load from the SOURCE fn value(&self, src: &SOURCE) -> Result; diff --git a/src/parts/relocatable.rs b/src/parts/relocatable.rs index 5feead1..c7bdb9f 100644 --- a/src/parts/relocatable.rs +++ b/src/parts/relocatable.rs @@ -1,33 +1,15 @@ -use crate::parts::{Symbol, Text, Data, Bss}; +use std::fmt::Display; +use std::path::Path; -/// Contains all the needed references to construct a final -/// mushed and relocated executable from an object file -pub struct Relocatable<'a> { - pub origin: &'a str, - pub symbols: Vec>, - pub texts: Vec>, - pub data: Vec>, - pub bss: Vec>, -} +use crate::error::Error; -impl<'a> Relocatable<'a> { - pub fn new(origin: &'a str) -> Self { - Self { - origin, - symbols: Vec::new(), - texts: Vec::new(), - data: Vec::new(), - bss: Vec::new(), - } - } -} +use super::Section; + +pub type BSI<'data> = Box, Error>> + 'data>; +/// Contains all the needed getters to construct a final +/// mushed and relocated executable from an object file +pub trait Relocatable: Display { + fn origin(&self) -> &Path; // not same as section's path since this one's supposed to be cannonical -impl std::fmt::Display for Relocatable<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "==={}===\nSymbols:\n{:?}\nTexts:\n??", - self.origin, self.symbols //, self.texts - ) - } + fn sections(&self) -> BSI<'_>; } diff --git a/src/parts/section.rs b/src/parts/section.rs new file mode 100644 index 0000000..365d1f2 --- /dev/null +++ b/src/parts/section.rs @@ -0,0 +1,55 @@ +use crate::error::Error; +use std::fmt::{Display, Formatter}; + +pub type SectionDataIterator<'data> = Box + 'data>; + +pub struct SectionInfo<'data> { + pub size: u64, + pub data: Option>, + pub offset: u64, +} + +pub enum Section<'data> { + Text(SectionInfo<'data>), + Data(SectionInfo<'data>), + Bss(SectionInfo<'data>), +} + +impl Section<'_> { + pub fn size(&self) -> Result { + match self { + Section::Text(s) => Ok(s.size), + Section::Data(s) => Ok(s.size), + Section::Bss(s) => Ok(s.size), + } + } +} + +impl Display for SectionInfo<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "size: {}, data: {}, offset: {}", + self.size, + self.data.is_some(), + self.offset, + ) + } +} + +impl Display for Section<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Section::Text(s) => writeln!(f, "TEXT {}", s), + Section::Data(s) => writeln!(f, "DATA {}", s), + Section::Bss(s) => writeln!(f, "BSS {}", s), + } + } +} + +pub enum SectionIterResult<'data> { + Ok(Section<'data>), + Err(Error), + Skip, + None, +} diff --git a/src/parts/segments.rs b/src/parts/segments.rs deleted file mode 100644 index 15a5530..0000000 --- a/src/parts/segments.rs +++ /dev/null @@ -1,13 +0,0 @@ - - -pub struct Text<'a> { - pub bytes: &'a [u8], -} - -pub struct Data<'a> { - pub bytes: &'a [u8], -} - -pub struct Bss<'a> { - pub bytes: &'a [u8], -} diff --git a/src/parts/storage.rs b/src/parts/storage.rs new file mode 100644 index 0000000..679800d --- /dev/null +++ b/src/parts/storage.rs @@ -0,0 +1,33 @@ +use std::path::PathBuf; + +use crate::error::Error; + +pub struct Storage { + origin: PathBuf, + bytes: Vec, // for storing the total if needed +} + +impl Storage { + pub fn new(origin: PathBuf) -> Self { + Self { + origin, + bytes: Vec::new(), + } + } + + pub fn origin(&self) -> Result { + std::fs::canonicalize(&self.origin).map_err(|e| e.into()) + } + + pub fn iter(&mut self) -> Result, Error> { + self.bytes = std::fs::read(&self.origin)?; + + Ok(self.bytes.iter()) + } + + pub fn bytes(&mut self) -> Result<&[u8], Error> { + self.bytes = std::fs::read(&self.origin)?; + + Ok(&self.bytes) + } +}