diff --git a/Cargo.lock b/Cargo.lock index 9fc70ad..4857dcb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,6 +67,7 @@ version = "0.1.0" dependencies = [ "elf-utilities", "page_size", + "xmas-elf", ] [[package]] @@ -147,3 +148,18 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "xmas-elf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e74de9a366f6ab8c405fa6b371d9ac24943921fa14b3d64afcb202065c405f11" +dependencies = [ + "zero", +] + +[[package]] +name = "zero" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f1bc8a6b2005884962297587045002d8cfb8dcec9db332f4ca216ddc5de82c5" diff --git a/Cargo.toml b/Cargo.toml index 1816eee..03350fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,5 +7,6 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +xmas-elf = "0.7.0" elf-utilities = "0.2.8" page_size = "0.4.2" \ No newline at end of file diff --git a/src/common/section.rs b/src/common/section.rs index be1ca33..e362456 100644 --- a/src/common/section.rs +++ b/src/common/section.rs @@ -1,5 +1,8 @@ use crate::error::Error; -use std::fmt::{Display, Formatter}; +use std::{ + convert::TryFrom, + fmt::{Display, Formatter}, +}; pub type SectionDataIterator<'data> = Box + 'data>; @@ -25,6 +28,22 @@ impl Section<'_> { } } +impl<'data> TryFrom<(&str, SectionInfo<'data>)> for Section<'data> { + type Error = Error; + + fn try_from(value: (&str, SectionInfo<'data>)) -> Result { + if value.0.starts_with(".text") { + Ok(Section::Text(value.1)) + } else if value.0.starts_with(".rodata") || value.0.starts_with(".data") { + Ok(Section::Data(value.1)) + } else if value.0.starts_with(".bss") { + Ok(Section::Bss(value.1)) + } else { + Err(Error::InvalidSectionName) + } + } +} + impl Display for SectionInfo<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!( diff --git a/src/error.rs b/src/error.rs index dbb422e..f580d47 100644 --- a/src/error.rs +++ b/src/error.rs @@ -7,6 +7,7 @@ use std::{ pub enum Error { IOError(std::io::Error), InvalidObjectType(u32), + InvalidSectionName, MissingSectionHeader(&'static str), MissingSectionData(&'static str), InvalidSectionData, diff --git a/src/formats.rs b/src/formats.rs index a1deb41..0c36959 100644 --- a/src/formats.rs +++ b/src/formats.rs @@ -1,3 +1,5 @@ mod elf; +mod xelf; pub use elf::*; +pub use xelf::*; diff --git a/src/formats/elf/object.rs b/src/formats/elf/object.rs index fadc500..017274a 100644 --- a/src/formats/elf/object.rs +++ b/src/formats/elf/object.rs @@ -1,4 +1,5 @@ use std::{ + convert::TryFrom, fmt::Display, path::{Path, PathBuf}, }; @@ -45,20 +46,23 @@ impl Relocatable for ElfObject { .iter() .filter_map(|s| match s.header.get_type() { Type::ProgBits => { - if s.header.sh_size > 0 - && (s.name.starts_with(".text") - || s.name.starts_with(".rodata") - || s.name.starts_with(".data")) - { + if s.header.sh_size > 0 { if let Some(di) = match &s.contents { Contents64::Raw(v) => Some(v.iter()), _ => None, } { - Some(Ok(Section::Text(SectionInfo { + let si = SectionInfo { size: s.header.sh_size, data: Some(Box::new(di.copied())), offset: s.header.sh_offset, - }))) + }; + let s_name: &str = &s.name; + + match Section::try_from((s_name, si)) { + Ok(s) => Some(Ok(s)), + Err(Error::InvalidSectionName) => None, // skip + Err(err) => Some(Err(err)), + } } else { Some(Err(Error::InvalidSectionData)) } diff --git a/src/formats/xelf.rs b/src/formats/xelf.rs new file mode 100644 index 0000000..b207754 --- /dev/null +++ b/src/formats/xelf.rs @@ -0,0 +1,26 @@ +use crate::common::{Lazy, Symbol}; +use crate::error::Error; +use crate::linker::Linker; +use xmas_elf::ElfFile; + +mod object; + +pub use object::XElfObject; + +impl Linker<'_> { + // shortcut to avoid turbofish + pub fn xelf() -> Self { + Self::new(Vec::new()) + } +} + +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::MissingSectionData(e)) + } + + fn resolved(&self) -> bool { + self.str_ref.is_some() + } +} diff --git a/src/formats/xelf/object.rs b/src/formats/xelf/object.rs new file mode 100644 index 0000000..5a2bbbe --- /dev/null +++ b/src/formats/xelf/object.rs @@ -0,0 +1,113 @@ +use std::{ + convert::TryFrom, + fmt::Display, + path::{Path, PathBuf}, +}; + +use crate::common::{Relocatable, Section, SectionInfo, Storage}; +use crate::{common::BSI, error::Error}; +use xmas_elf::{ElfFile, sections::{SectionData, SectionHeader}}; +use xmas_elf::{header::Type as ElfType, sections::ShType}; + +pub struct XElfObject<'data> { + origin: PathBuf, + elf: ElfFile<'data>, +} + +impl<'data> XElfObject<'data> { + pub fn new(storage: &'data mut Storage) -> Result { + let origin = storage.origin()?; + let elf = ElfFile::new(storage.bytes()?).map_err(|_| Error::InvalidObjectType(0))?; + + is_relocatable(&elf)?; + let result = XElfObject { origin, elf }; + + Ok(result) + } + + fn data(&self, h: &SectionHeader<'data>) -> Result, Error> { + match h.get_data(&self.elf) { + Ok(SectionData::Undefined(ba)) => Ok(ba.iter()), + Ok(_) => Err(Error::InvalidSectionData), + Err(_) => Err(Error::InvalidSectionData), + } + } +} + +impl Relocatable for XElfObject<'_> { + fn origin(&self) -> &Path { + &self.origin + } + + fn sections(&self) -> BSI<'_> { + let iter = self.elf.section_iter().filter_map(move |h| { + 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(&self.elf).unwrap_or("null"); + if h.size() > 0 { + match self.data(&h) { + Ok(data_iter) => { + let si = SectionInfo { + size: h.size(), + data: Some(Box::new(data_iter.copied())), + offset: h.offset(), + }; + + match Section::try_from((s_name, si)) { + Ok(s) => Some(Ok(s)), + Err(Error::InvalidSectionName) => None, // skip + Err(err) => Some(Err(err)), + } + } + Err(err) => Some(Err(err)), + } + } else { + None + } + } + // bss + ShType::NoBits => Some(Ok(Section::Bss(SectionInfo { + size: h.size(), + data: None, + offset: h.offset(), + }))), + _ => None, + }, + Err(err) => Some(Err(err)), + }; + }); + + Box::new(iter) + } +} + +impl Display for XElfObject<'_> { + 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(()) + } +} + +fn is_relocatable(elf: &ElfFile) -> Result<(), Error> { + let raw_type = elf.header.pt2.type_(); + let elf_type: ElfType = raw_type.as_type(); + if elf_type != ElfType::Relocatable { + return Err(Error::InvalidObjectType(raw_type.0.into())); + } + + Ok(()) +}