diff --git a/Cargo.toml b/Cargo.toml index 03350fa..8a55991 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,11 @@ 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 +page_size = "0.4.2" +elf-utilities = { version = "0.2.8", optional = true } +xmas-elf = { version = "0.7.0", optional = true } + +[features] +default = ["elf"] +elf = ["elf-utilities"] +xelf = ["xmas-elf"] \ No newline at end of file diff --git a/src/common/output.rs b/src/common/output.rs index d92c4a9..9e279b9 100644 --- a/src/common/output.rs +++ b/src/common/output.rs @@ -1,24 +1,32 @@ +use std::path::PathBuf; + use super::Section; use crate::error::Error; -pub trait Output { +pub trait Output<'data> { fn allocate(&mut self, size: u64) -> Result; - fn append_section(&mut self, section: &Section) -> Result<(), Error>; + fn append_section(&mut self, section: Section<'data>) -> Result<(), Error>; // fn prepare_symbol(&mut self, symbol: impl Lazy) -> Result<&mut Self, Error>; + + fn finalize(self) -> Result; } pub struct DummyOutput; -impl Output for DummyOutput { +impl<'data> Output<'data> for DummyOutput { fn allocate(&mut self, size: u64) -> Result { eprintln!("Allocating: {}", size); Ok(size) } - fn append_section(&mut self, section: &Section) -> Result<(), Error> { + fn append_section(&mut self, section: Section<'data>) -> Result<(), Error> { eprintln!("Appending section: {}", section); Ok(()) } + + fn finalize(self) -> Result { + todo!(); + } } diff --git a/src/common/storage.rs b/src/common/storage.rs index 679800d..97d4d75 100644 --- a/src/common/storage.rs +++ b/src/common/storage.rs @@ -1,4 +1,5 @@ use std::path::PathBuf; +use std::{fs::canonicalize, io::ErrorKind}; use crate::error::Error; @@ -16,7 +17,18 @@ impl Storage { } pub fn origin(&self) -> Result { - std::fs::canonicalize(&self.origin).map_err(|e| e.into()) + canonicalize(&self.origin).map_err(|e| e.into()) + } + + pub fn destination(&self) -> Result { + let mut dest = canonicalize(&self.origin).map_err(|e| Error::from(e))?; + if !dest.pop() { + let err = std::io::Error::new(ErrorKind::Other, "Destination path invalid"); + Err(Error::IOError(Box::new(err))) + } else { + dest.push("rld.out"); + Ok(dest) + } } pub fn iter(&mut self) -> Result, Error> { diff --git a/src/error.rs b/src/error.rs index f580d47..73a185f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -5,11 +5,9 @@ use std::{ #[derive(Debug)] pub enum Error { - IOError(std::io::Error), + IOError(Box), InvalidObjectType(u32), InvalidSectionName, - MissingSectionHeader(&'static str), - MissingSectionData(&'static str), InvalidSectionData, ParseError(Box), LinkingError(Trace), @@ -31,7 +29,7 @@ pub trait SourceInfo: Debug + Display { impl From for Error { fn from(err: std::io::Error) -> Self { - Self::IOError(err) + Self::IOError(Box::new(err)) } } diff --git a/src/formats.rs b/src/formats.rs index 0c36959..235bf54 100644 --- a/src/formats.rs +++ b/src/formats.rs @@ -1,5 +1,9 @@ +#[cfg(feature = "elf")] mod elf; +#[cfg(feature = "xelf")] mod xelf; +#[cfg(feature = "elf")] pub use elf::*; +#[cfg(feature = "xelf")] pub use xelf::*; diff --git a/src/formats/elf.rs b/src/formats/elf.rs index 2ebf472..4cfdb88 100644 --- a/src/formats/elf.rs +++ b/src/formats/elf.rs @@ -3,9 +3,11 @@ use crate::error::Error; use crate::linker::Linker; mod object; +mod output; use elf_utilities::file::ELF64; -pub use object::ElfObject; +pub use object::*; +pub use output::*; impl Linker<'_> { // shortcut to avoid turbofish diff --git a/src/formats/elf/object.rs b/src/formats/elf/object.rs index 017274a..62b1ba4 100644 --- a/src/formats/elf/object.rs +++ b/src/formats/elf/object.rs @@ -30,6 +30,10 @@ impl ElfObject { Ok(result) } + + pub fn elf(&self) -> &ELF64 { + &self.elf + } } impl Relocatable for ElfObject { diff --git a/src/formats/elf/output.rs b/src/formats/elf/output.rs new file mode 100644 index 0000000..2004c64 --- /dev/null +++ b/src/formats/elf/output.rs @@ -0,0 +1,128 @@ +use std::path::PathBuf; + +use elf_utilities::{ + file::{ELF64Dumper, ELF64}, + section::Section64, + segment::{Segment64, Type}, +}; + +use crate::{ + common::{Output, Section, SectionInfo}, + error::Error, +}; + +use super::ElfObject; + +pub struct ElfOutput<'data> { + destination: PathBuf, + dump: ELF64Dumper, + text: Vec>, + data: Vec>, + bss: Vec>, +} + +impl ElfOutput<'_> { + pub fn new(destination: PathBuf) -> Self { + use elf_utilities::header::{Class, Data, Machine, Type, Version, OSABI}; + + let mut elf = ELF64::default(); + elf.ehdr.set_elf_type(Type::Exec); + elf.ehdr.set_class(Class::Bit64); + elf.ehdr.set_machine(Machine::Intel386); + elf.ehdr.set_object_version(Version::Current); + elf.ehdr.set_file_version(Version::Current); + elf.ehdr.set_osabi(OSABI::Linux); + elf.ehdr.set_data(Data::LSB2); + + Self { + destination, + dump: ELF64Dumper::new(elf), + text: Vec::new(), + data: Vec::new(), + bss: Vec::new(), + } + } + + pub fn from_object(object: &ElfObject, destination: PathBuf) -> Self { + use elf_utilities::header::Type; + + let other = object.elf(); + + let mut elf = ELF64::default(); + elf.ehdr = other.ehdr.clone(); + elf.ehdr.set_elf_type(Type::Exec); + + Self { + destination, + dump: ELF64Dumper::new(elf), + text: Vec::new(), + data: Vec::new(), + bss: Vec::new(), + } + } + + fn populate_section_data(&mut self) -> Result { + use elf_utilities::section::{Contents64, Shdr64, Type}; + + let mut data = Vec::new(); + + for t in self.text.iter_mut() { + if let Some(iter) = &mut t.data { + data.extend(iter.as_mut()); + } + } + + let mut header = Shdr64::default(); + header.set_type(Type::ProgBits); + header.sh_entsize = data.len() as u64; + + let sec64 = Section64 { + name: ".text".into(), + header, + contents: Contents64::Raw(data), + }; + + self.dump.file.add_section(sec64); + + Ok(0) + } +} + +impl<'data> Output<'data> for ElfOutput<'data> { + fn allocate(&mut self, size: u64) -> Result { + Ok(size) + } + + fn append_section(&mut self, section: Section<'data>) -> Result<(), Error> { + match section { + Section::Text(si) => self.text.push(si), + Section::Data(si) => self.data.push(si), + Section::Bss(si) => self.bss.push(si), + } + + Ok(()) + } + + fn finalize(mut self) -> Result { + use std::io::Error as IOError; + use std::io::ErrorKind; + + self.populate_section_data()?; + let mut segment = Segment64::default(); + segment.header.set_type(Type::Load); + self.dump.file.segments.push(segment); + self.dump.file.finalize(); + + let str_path = self.destination.to_str().ok_or_else(|| { + let ioe = IOError::new(ErrorKind::Other, "Path expansion fail"); + let boxed = Box::new(ioe); + Error::IOError(boxed) + })?; + + self.dump + .generate_elf_file(str_path, 0o755) + .map_err(|e| Error::IOError(e))?; + + Ok(self.destination) + } +} diff --git a/src/formats/xelf/object.rs b/src/formats/xelf/object.rs index 5a2bbbe..9f80cdb 100644 --- a/src/formats/xelf/object.rs +++ b/src/formats/xelf/object.rs @@ -6,8 +6,11 @@ use std::{ 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}; +use xmas_elf::{ + sections::{SectionData, SectionHeader}, + ElfFile, +}; pub struct XElfObject<'data> { origin: PathBuf, @@ -55,7 +58,7 @@ impl Relocatable for XElfObject<'_> { 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 diff --git a/src/linker.rs b/src/linker.rs index aa5a5cb..39e8453 100644 --- a/src/linker.rs +++ b/src/linker.rs @@ -18,12 +18,12 @@ impl<'data> Linker<'data> { self.relocatables.push(relocatable); } - pub fn link(&self, output: &mut dyn Output) -> Result { + pub fn link<'l: 'data>(&'l self, output: &mut dyn Output<'data>) -> Result { let allocated = output.allocate(self.total_size()?)?; for r in self.relocatables.iter() { for s in r.sections() { - output.append_section(&s?)?; + output.append_section(s?)?; } } diff --git a/src/main.rs b/src/main.rs index df03c31..f139a16 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,8 +3,8 @@ mod error; mod formats; mod linker; -use common::{DummyOutput, Storage}; -use formats::ElfObject; +use common::{Output, Storage}; +use formats::{ElfObject, ElfOutput}; use linker::Linker; fn main() { @@ -15,7 +15,7 @@ fn main() { let mut contents: Vec = Vec::new(); let mut linker = Linker::elf(); - let mut output = DummyOutput; + let mut maybe_out = None; while args.peek().is_some() { let fpath = args.next().expect("Unexpected peek-a-boo"); @@ -25,8 +25,19 @@ fn main() { for storage in contents.iter_mut() { let relocatable = ElfObject::new(storage).expect("Parsing ELF"); + + if maybe_out.is_none() { + let dest = storage.destination().expect("Path resolution"); + maybe_out = Some(ElfOutput::from_object(&relocatable, dest)); + } linker.add_relocatable(Box::new(relocatable)); } - linker.link(&mut output).expect("Linking"); + if let Some(mut output) = maybe_out { + let linked = linker.link(&mut output).expect("Linking"); + let outpath = output.finalize().expect("Finalization"); + eprintln!("Done {:?}, linked {} bytes", outpath, linked); + } else { + eprintln!("Nothing to do"); + } }