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();
Ok(obj::DataSection {
name: self.name.clone(),
start,
len: section_len,
contents,

View File

@@ -1,23 +1,34 @@
use crate::vm::obj::error::{ParseError, Result};
use byteorder::{ReadBytesExt, LE};
use byteorder::{ReadBytesExt, WriteBytesExt, LE};
use std::{
collections::HashMap,
convert::{TryFrom, TryInto},
fmt::Debug,
io::{Cursor, Read},
io::{Cursor, Read, Write},
};
pub const MAGIC: u64 = 0xDEAD_BEA7_BA5E_BA11;
pub const OBJ_VERSION: u32 = 0;
const OBJECT_HEADER_LEN: usize = 16; // 8 + 4 + 4
#[derive(Debug)]
#[derive(Debug, Clone, PartialEq)]
pub struct Object {
pub version: u32,
pub sections: Vec<Section>,
}
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> {
let mut cursor = Cursor::new(bytes);
let magic = cursor.read_u64::<LE>()?;
@@ -29,18 +40,23 @@ impl Object {
let mut sections = Vec::new();
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);
}
Ok(Object { version, sections })
}
pub fn virtual_len(&self) -> usize {
self.sections.iter()
self.sections
.iter()
.map(|s| match s {
Section::Data(DataSection { start, len, .. }) => { (start + len) as usize }
Section::Meta { .. } => { 0 }
}).max().unwrap_or(0)
Section::Data(DataSection { start, len, .. }) => (start + len) as usize,
Section::Meta { .. } => 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 {
Data(DataSection),
Meta(MetaSection),
}
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 start = cursor.position() as usize;
let end = start + len as usize;
let bytes = &cursor.get_ref()[start..end];
let kind: SectionKind = cursor.read_u8()?.try_into()?;
match kind {
SectionKind::Data => Ok(Section::Data(DataSection::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 name: String,
pub start: u64,
pub len: u64,
pub contents: Vec<u8>,
}
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> {
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 len = cursor.read_u64::<LE>()?;
let contents = &bytes[cursor.position() as usize..];
Ok(DataSection {
name: name_string,
start,
len,
contents: From::from(contents),
@@ -121,12 +198,20 @@ impl DataSection {
}
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub struct MetaSection {
pub entries: HashMap<String, u64>,
}
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> {
let mut cursor = Cursor::new(bytes);
let entry_count = cursor.read_u64::<LE>()?;
@@ -145,3 +230,27 @@ impl MetaSection {
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 {
match section {
Section::Data(DataSection {
name: _,
start,
len,
contents,