first draft implementation using xmas_elf

master
Ales Katona 4 years ago
parent 0d7f5a3206
commit 8f0fadf570
Signed by: almindor
GPG Key ID: 2F773149BF38B48F

2
.gitignore vendored

@ -1,3 +1,5 @@
/target /target
/assets /assets
/docs

Binary file not shown.

Binary file not shown.

@ -1,3 +1,8 @@
use std::{
fmt::{Debug, Display, Formatter},
path::PathBuf,
};
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
IOError(std::io::Error), IOError(std::io::Error),
@ -6,6 +11,21 @@ pub enum Error {
MissingSectionHeader(&'static str), MissingSectionHeader(&'static str),
MissingSectionData(&'static str), MissingSectionData(&'static str),
InvalidSectionData, InvalidSectionData,
LinkingError(Trace),
}
// "backtrace" for error origin
#[derive(Debug)]
pub struct Trace {
origin: PathBuf,
offset: u64, // 0 indicates unknown/invalid
source_info: Option<Box<dyn SourceInfo>>,
}
pub trait SourceInfo: Debug + Display {
fn line(&self) -> u64;
// todo
} }
impl From<std::io::Error> for Error { impl From<std::io::Error> for Error {
@ -13,3 +33,15 @@ impl From<std::io::Error> for Error {
Self::IOError(err) 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!")
}
}

@ -1,63 +1,84 @@
use std::convert::TryFrom; use std::{
fmt::Display,
path::{Path, PathBuf},
};
use crate::error::Error; use crate::linker::LinkState;
use crate::parts::{Lazy, Relocatable, Symbol, Text}; use crate::parts::{Lazy, Relocatable, Storage, Symbol};
use crate::{error::Error, parts::BSI};
use xmas_elf::header::Type as ElfType; use xmas_elf::header::Type as ElfType;
use xmas_elf::sections::*;
use xmas_elf::ElfFile; use xmas_elf::ElfFile;
impl<'a> Lazy<&'a str, ElfFile<'a>> for Symbol<'a> { mod section;
fn value(&self, src: &ElfFile<'a>) -> Result<&'a str, Error> { mod section_iter;
src.get_string(self.index)
.map_err(|e| Error::StringError(e))
}
fn resolved(&self) -> bool { use section_iter::SectionIter;
self.str_ref.is_some()
} pub struct ElfObject<'data> {
origin: PathBuf,
elf: ElfFile<'data>,
} }
impl<'a> TryFrom<SectionData<'a>> for Text<'a> { impl<'data> ElfObject<'data> {
type Error = Error; pub fn new(storage: &'data mut Storage) -> Result<Self, Error> {
let origin = storage.origin()?;
let elf = ElfFile::new(storage.bytes()?).map_err(|_| Error::InvalidObjectType(0))?;
fn try_from(data: SectionData<'a>) -> Result<Self, Error> { is_relocatable(&elf)?;
match data { let result = ElfObject { origin, elf };
SectionData::Undefined(bytes) => Ok(Text { bytes }),
_ => Err(Error::InvalidSectionData), // TODO: more context needed Ok(result)
}
} }
} }
impl<'a> Relocatable<'a> { impl Relocatable for ElfObject<'_> {
pub fn from_elf(origin: &'a str, elf: &'a ElfFile) -> Result<Self, Error> { fn origin(&self) -> &Path {
is_relocatable(elf)?; &self.origin
let mut result = Relocatable::new(origin); }
for header in elf.section_iter() { fn sections(&self) -> BSI<'_> {
// .text sections > 0 only Box::new(SectionIter {
if is_text_section(elf, &header)? { elf: &self.elf,
let data = header iter: self.elf.section_iter(),
.get_data(&elf) })
.map_err(|e| Error::MissingSectionData(e))?; }
}
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(())
} }
}
Ok(result) impl LinkState<'_> {
pub fn new() -> Self {
LinkState {
relocatables: Vec::new(), // it's a trait thing
}
} }
} }
fn is_text_section(elf: &ElfFile, header: &SectionHeader) -> Result<bool, Error> { impl<'data> Lazy<&'data str, ElfFile<'data>> for Symbol<'data> {
let name = header.get_name(&elf).unwrap_or("null"); fn value(&self, src: &ElfFile<'data>) -> Result<&'data str, Error> {
let h_type = header src.get_string(self.index)
.get_type() .map_err(|e| Error::StringError(e))
.map_err(|e| Error::MissingSectionHeader(e))?; }
Ok(h_type == ShType::ProgBits fn resolved(&self) -> bool {
&& header.flags() == (SHF_EXECINSTR | SHF_ALLOC) self.str_ref.is_some()
&& name.starts_with(".text") }
&& header.size() > 0)
} }
fn is_relocatable(elf: &ElfFile) -> Result<(), Error> { fn is_relocatable(elf: &ElfFile) -> Result<(), Error> {

@ -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<Box<dyn Iterator<Item = u8> + '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<Cloned<std::slice::Iter<'file, u8>>, 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
}
}

@ -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<Section<'data>, Error>;
fn next(&mut self) -> Option<Self::Item> {
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)),
}
}
}

@ -1,27 +1,40 @@
use std::fmt::Display; use std::fmt::Display;
use crate::error::Error; use crate::{error::Error, parts::Relocatable};
use crate::parts::Relocatable;
#[derive(Default)] pub struct LinkState<'data> {
pub struct LinkState<'a> { pub relocatables: Vec<Box<dyn Relocatable + 'data>>,
relocatables: Vec<Relocatable<'a>>,
} }
impl<'a> LinkState<'a> { impl<'data> LinkState<'data> {
pub fn add_relocatable(&mut self, relocatable: Relocatable<'a>) -> Result<(), Error> { pub fn add_relocatable(
&mut self,
relocatable: Box<dyn Relocatable + 'data>,
) -> Result<(), Error> {
self.relocatables.push(relocatable); self.relocatables.push(relocatable);
Ok(()) Ok(())
} }
pub fn total_size(&self) -> Result<u64, Error> {
let mut result = 0u64;
for o in self.relocatables.iter() {
for s in o.sections() {
result += s?.size()?;
}
}
Ok(result)
}
} }
impl Display for LinkState<'_> { impl Display for LinkState<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "===Relocatables===")?; writeln!(f, "===Relocatables===")?;
for r in self.relocatables.iter() { for r in &self.relocatables {
writeln!(f, "{}", r)?; write!(f, "{}", r)?;
} }
Ok(()) Ok(())

@ -1,13 +1,11 @@
use std::fs;
use xmas_elf::ElfFile;
mod error; mod error;
mod formats; mod formats;
mod linker; mod linker;
mod parts; mod parts;
use formats::ElfObject;
use linker::LinkState; use linker::LinkState;
use parts::Relocatable; use parts::Storage;
fn main() { fn main() {
let mut args = std::env::args().peekable(); let mut args = std::env::args().peekable();
@ -15,25 +13,22 @@ fn main() {
// can this miss? // can this miss?
args.next().unwrap(); args.next().unwrap();
let mut contents: Vec<(String, Vec<u8>)> = Vec::new(); let mut contents: Vec<Storage> = Vec::new();
let mut parsed: Vec<(&str, ElfFile)> = Vec::new(); let mut linker = LinkState::new();
let mut linker = LinkState::default();
while args.peek().is_some() { while args.peek().is_some() {
let fpath = args.next().expect("Unexpected peek-a-boo"); let fpath = args.next().expect("Unexpected peek-a-boo");
let raw = fs::read(&fpath).expect("Error reading file"); let stored = Storage::new(fpath.into());
contents.push((fpath, raw)); contents.push(stored);
} }
for data in contents.iter() { for storage in contents.iter_mut() {
let elf = ElfFile::new(&data.1).expect("Error parsing ELF file"); let relocatable = ElfObject::new(storage).unwrap();
parsed.push((&data.0, elf)); linker.add_relocatable(Box::new(relocatable)).unwrap();
} }
for elf in parsed.iter() { let total_size = linker.total_size().expect("Size to be known");
let relocatable = Relocatable::from_elf(elf.0, &elf.1).unwrap(); println!("to allocate: {}", total_size);
linker.add_relocatable(relocatable).unwrap();
}
println!("{}", linker); println!("all: \n{}", linker);
} }

@ -1,9 +1,11 @@
mod lazy; mod lazy;
mod relocatable; mod relocatable;
mod section;
mod storage;
mod symbol; mod symbol;
mod segments;
pub use lazy::*; pub use lazy::*;
pub use relocatable::*; pub use relocatable::*;
pub use section::*;
pub use storage::*;
pub use symbol::*; pub use symbol::*;
pub use segments::*;

@ -1,5 +1,4 @@
use crate::error::Error; use crate::error::Error;
pub trait Lazy<VALUE, SOURCE> { pub trait Lazy<VALUE, SOURCE> {
/// Resolve VALUE, blocking load from the SOURCE /// Resolve VALUE, blocking load from the SOURCE
fn value(&self, src: &SOURCE) -> Result<VALUE, Error>; fn value(&self, src: &SOURCE) -> Result<VALUE, Error>;

@ -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 use crate::error::Error;
/// mushed and relocated executable from an object file
pub struct Relocatable<'a> {
pub origin: &'a str,
pub symbols: Vec<Symbol<'a>>,
pub texts: Vec<Text<'a>>,
pub data: Vec<Data<'a>>,
pub bss: Vec<Bss<'a>>,
}
impl<'a> Relocatable<'a> { use super::Section;
pub fn new(origin: &'a str) -> Self {
Self { pub type BSI<'data> = Box<dyn Iterator<Item = Result<Section<'data>, Error>> + 'data>;
origin, /// Contains all the needed getters to construct a final
symbols: Vec::new(), /// mushed and relocated executable from an object file
texts: Vec::new(), pub trait Relocatable: Display {
data: Vec::new(), fn origin(&self) -> &Path; // not same as section's path since this one's supposed to be cannonical
bss: Vec::new(),
}
}
}
impl std::fmt::Display for Relocatable<'_> { fn sections(&self) -> BSI<'_>;
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"==={}===\nSymbols:\n{:?}\nTexts:\n??",
self.origin, self.symbols //, self.texts
)
}
} }

@ -0,0 +1,55 @@
use crate::error::Error;
use std::fmt::{Display, Formatter};
pub type SectionDataIterator<'data> = Box<dyn Iterator<Item = u8> + 'data>;
pub struct SectionInfo<'data> {
pub size: u64,
pub data: Option<SectionDataIterator<'data>>,
pub offset: u64,
}
pub enum Section<'data> {
Text(SectionInfo<'data>),
Data(SectionInfo<'data>),
Bss(SectionInfo<'data>),
}
impl Section<'_> {
pub fn size(&self) -> Result<u64, Error> {
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,
}

@ -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],
}

@ -0,0 +1,33 @@
use std::path::PathBuf;
use crate::error::Error;
pub struct Storage {
origin: PathBuf,
bytes: Vec<u8>, // for storing the total if needed
}
impl Storage {
pub fn new(origin: PathBuf) -> Self {
Self {
origin,
bytes: Vec::new(),
}
}
pub fn origin(&self) -> Result<PathBuf, Error> {
std::fs::canonicalize(&self.origin).map_err(|e| e.into())
}
pub fn iter(&mut self) -> Result<impl Iterator<Item = &u8>, 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)
}
}
Loading…
Cancel
Save