Add data section names and conversion to object file format

* Data section names are encoded in the object file format
* Objects can be converted into their file format layouts
* Add writing/reading test to make sure that an object converted to
  bytes and then back from bytes is equal to hopefully catch major
  object translations

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2020-02-25 16:39:32 -05:00
parent 700ea6c54f
commit 86d46b2a50
3 changed files with 124 additions and 13 deletions

View File

@@ -143,6 +143,7 @@ impl Assemble for DataSection {
); );
asm.names.pop(); asm.names.pop();
Ok(obj::DataSection { Ok(obj::DataSection {
name: self.name.clone(),
start, start,
len: section_len, len: section_len,
contents, contents,

View File

@@ -1,23 +1,34 @@
use crate::vm::obj::error::{ParseError, Result}; use crate::vm::obj::error::{ParseError, Result};
use byteorder::{ReadBytesExt, LE}; use byteorder::{ReadBytesExt, WriteBytesExt, LE};
use std::{ use std::{
collections::HashMap, collections::HashMap,
convert::{TryFrom, TryInto}, convert::{TryFrom, TryInto},
fmt::Debug, fmt::Debug,
io::{Cursor, Read}, io::{Cursor, Read, Write},
}; };
pub const MAGIC: u64 = 0xDEAD_BEA7_BA5E_BA11; pub const MAGIC: u64 = 0xDEAD_BEA7_BA5E_BA11;
pub const OBJ_VERSION: u32 = 0; pub const OBJ_VERSION: u32 = 0;
const OBJECT_HEADER_LEN: usize = 16; // 8 + 4 + 4 const OBJECT_HEADER_LEN: usize = 16; // 8 + 4 + 4
#[derive(Debug)] #[derive(Debug, Clone, PartialEq)]
pub struct Object { pub struct Object {
pub version: u32, pub version: u32,
pub sections: Vec<Section>, pub sections: Vec<Section>,
} }
impl Object { impl Object {
pub fn to_bytes(&self) -> Vec<u8> {
let mut cursor = Cursor::new(Vec::new());
cursor.write_u64::<LE>(MAGIC).unwrap();
cursor.write_u32::<LE>(OBJ_VERSION).unwrap();
cursor.write_u32::<LE>(self.sections.len() as u32).unwrap();
for section in self.sections.iter() {
cursor.write(&section.to_bytes()).unwrap();
}
cursor.into_inner()
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self> { pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
let mut cursor = Cursor::new(bytes); let mut cursor = Cursor::new(bytes);
let magic = cursor.read_u64::<LE>()?; let magic = cursor.read_u64::<LE>()?;
@@ -29,18 +40,23 @@ impl Object {
let mut sections = Vec::new(); let mut sections = Vec::new();
for _ in 0..section_count { for _ in 0..section_count {
let section = Section::from_bytes(&mut cursor)?; let start = cursor.position() as usize;
let section = Section::from_bytes(&bytes[start..])?;
cursor.set_position((start + section.len()) as u64);
sections.push(section); sections.push(section);
} }
Ok(Object { version, sections }) Ok(Object { version, sections })
} }
pub fn virtual_len(&self) -> usize { pub fn virtual_len(&self) -> usize {
self.sections.iter() self.sections
.iter()
.map(|s| match s { .map(|s| match s {
Section::Data(DataSection { start, len, .. }) => { (start + len) as usize } Section::Data(DataSection { start, len, .. }) => (start + len) as usize,
Section::Meta { .. } => { 0 } Section::Meta { .. } => 0,
}).max().unwrap_or(0) })
.max()
.unwrap_or(0)
} }
} }
@@ -69,6 +85,16 @@ macro_rules! section_kind {
} }
} }
} }
impl From<SectionKind> for u8 {
fn from(other: SectionKind) -> Self {
match other {
$(
$enum_name::$name => $value,
)*
}
}
}
}; };
} }
@@ -79,20 +105,45 @@ section_kind! {
} }
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub enum Section { pub enum Section {
Data(DataSection), Data(DataSection),
Meta(MetaSection), Meta(MetaSection),
} }
impl Section { impl Section {
fn from_bytes(cursor: &mut Cursor<&[u8]>) -> Result<Self> { pub fn len(&self) -> usize {
match self {
Section::Data(s) => { 1 + 8 + s.len() },
Section::Meta(s) => { 1 + 8 + s.len() },
}
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut cursor = Cursor::new(Vec::new());
let bytes = match self {
Section::Data(s) => {
cursor.write_u8(SectionKind::Data.into()).unwrap();
s.to_bytes()
}
Section::Meta(s) => {
cursor.write_u8(SectionKind::Meta.into()).unwrap();
s.to_bytes()
},
};
cursor.write_u64::<LE>(bytes.len() as u64).unwrap();
cursor.write(&bytes).unwrap();
cursor.into_inner()
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
let mut cursor = Cursor::new(bytes);
let kind: SectionKind = cursor.read_u8()?.try_into()?;
let len = cursor.read_u64::<LE>()?; let len = cursor.read_u64::<LE>()?;
let start = cursor.position() as usize; let start = cursor.position() as usize;
let end = start + len as usize; let end = start + len as usize;
let bytes = &cursor.get_ref()[start..end]; let bytes = &cursor.get_ref()[start..end];
let kind: SectionKind = cursor.read_u8()?.try_into()?;
match kind { match kind {
SectionKind::Data => Ok(Section::Data(DataSection::from_bytes(bytes)?)), SectionKind::Data => Ok(Section::Data(DataSection::from_bytes(bytes)?)),
SectionKind::Meta => Ok(Section::Meta(MetaSection::from_bytes(bytes)?)), SectionKind::Meta => Ok(Section::Meta(MetaSection::from_bytes(bytes)?)),
@@ -100,20 +151,46 @@ impl Section {
} }
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub struct DataSection { pub struct DataSection {
pub name: String,
pub start: u64, pub start: u64,
pub len: u64, pub len: u64,
pub contents: Vec<u8>, pub contents: Vec<u8>,
} }
impl DataSection { impl DataSection {
pub fn len(&self) -> usize {
2 + self.name.as_bytes().len() + 8 + 8 + self.contents.len()
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut cursor = Cursor::new(Vec::new());
assert!(self.name.len() < u16::max_value() as usize);
cursor.write_u16::<LE>(self.name.len() as u16).unwrap();
cursor.write(self.name.as_bytes()).unwrap();
cursor.write_u64::<LE>(self.start).unwrap();
cursor.write_u64::<LE>(self.len).unwrap();
cursor.write(&self.contents).unwrap();
cursor.into_inner()
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self> { pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
let mut cursor = Cursor::new(bytes); let mut cursor = Cursor::new(bytes);
let name_len = cursor.read_u16::<LE>()? as usize;
let name_start = cursor.position() as usize;
let name_end = name_start + name_len;
let name_bytes = &bytes[name_start .. name_end];
let name_string = String::from_utf8(name_bytes.to_vec())?;
cursor.set_position(name_end as u64);
let start = cursor.read_u64::<LE>()?; let start = cursor.read_u64::<LE>()?;
let len = cursor.read_u64::<LE>()?; let len = cursor.read_u64::<LE>()?;
let contents = &bytes[cursor.position() as usize..]; let contents = &bytes[cursor.position() as usize..];
Ok(DataSection { Ok(DataSection {
name: name_string,
start, start,
len, len,
contents: From::from(contents), contents: From::from(contents),
@@ -121,12 +198,20 @@ impl DataSection {
} }
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub struct MetaSection { pub struct MetaSection {
pub entries: HashMap<String, u64>, pub entries: HashMap<String, u64>,
} }
impl MetaSection { impl MetaSection {
pub fn len(&self) -> usize {
todo!() // 8 + ...
}
pub fn to_bytes(&self) -> Vec<u8> {
todo!()
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self> { pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
let mut cursor = Cursor::new(bytes); let mut cursor = Cursor::new(bytes);
let entry_count = cursor.read_u64::<LE>()?; let entry_count = cursor.read_u64::<LE>()?;
@@ -145,3 +230,27 @@ impl MetaSection {
Ok(MetaSection { entries }) Ok(MetaSection { entries })
} }
} }
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_to_from_bytes() {
let obj = Object {
version: OBJ_VERSION,
sections: vec![
Section::Data(DataSection {
name: "data".to_string(),
start: 0,
len: 16,
contents: vec!(0u8; 16),
}),
],
};
let obj_bytes = obj.to_bytes();
let converted = Object::from_bytes(&obj_bytes).unwrap();
assert_eq!(obj, converted);
}
}

View File

@@ -26,6 +26,7 @@ impl State {
for section in object.sections { for section in object.sections {
match section { match section {
Section::Data(DataSection { Section::Data(DataSection {
name: _,
start, start,
len, len,
contents, contents,