diff --git a/examples/roundtrip.rs b/examples/roundtrip.rs index 4979b5b..b3b9519 100644 --- a/examples/roundtrip.rs +++ b/examples/roundtrip.rs @@ -12,6 +12,7 @@ fn main() { let module = match parity_wasm::deserialize_file(&args[1]) .expect("Failed to load module") .parse_names() + .and_then(|module| module.parse_reloc()) { Ok(m) => m, Err((errors, m)) => { diff --git a/res/cases/v1/relocatable.wasm b/res/cases/v1/relocatable.wasm new file mode 100644 index 0000000..ec986f2 Binary files /dev/null and b/res/cases/v1/relocatable.wasm differ diff --git a/src/elements/mod.rs b/src/elements/mod.rs index 24c77d4..1f8b818 100644 --- a/src/elements/mod.rs +++ b/src/elements/mod.rs @@ -33,6 +33,7 @@ mod func; mod segment; mod index_map; mod name_section; +mod reloc_section; pub use self::module::{Module, peek_size, ImportCountType}; pub use self::section::{ @@ -56,6 +57,9 @@ pub use self::name_section::{ NameMap, NameSection, ModuleNameSection, FunctionNameSection, LocalNameSection, }; +pub use self::reloc_section::{ + RelocSection, RelocationEntry, +}; /// Deserialization from serial i/o pub trait Deserialize : Sized { diff --git a/src/elements/module.rs b/src/elements/module.rs index 672efd1..d63a74a 100644 --- a/src/elements/module.rs +++ b/src/elements/module.rs @@ -7,6 +7,7 @@ use super::section::{ GlobalSection, TableSection, ElementSection, DataSection, MemorySection }; use super::name_section::NameSection; +use super::reloc_section::RelocSection; const WASM_MAGIC_NUMBER: [u8; 4] = [0x00, 0x61, 0x73, 0x6d]; @@ -191,6 +192,47 @@ impl Module { } } + /// Try to parse reloc section in place + /// Corresponding custom section with proper header will convert to reloc sections + /// If some of them will fail to be decoded, Err variant is returned with the list of + /// (index, Error) tuples of failed sections. + pub fn parse_reloc(mut self) -> Result, Self)> { + let mut parse_errors = Vec::new(); + + for (i, section) in self.sections.iter_mut().enumerate() { + if let Some(relocation_section) = { + if let Section::Custom(ref custom) = *section { + if custom.name().starts_with("reloc.") { + let mut rdr = io::Cursor::new(custom.payload()); + let reloc_section = match RelocSection::deserialize(custom.name().to_owned(), &mut rdr) { + Ok(reloc_section) => reloc_section, + Err(e) => { parse_errors.push((i, e)); continue; } + }; + if rdr.position() != custom.payload().len() as u64 { + parse_errors.push((i, io::Error::from(io::ErrorKind::InvalidData).into())); + continue; + } + Some(Section::Reloc(reloc_section)) + } + else { + None + } + } + else { + None + } + } { + *section = relocation_section; + } + } + + if parse_errors.len() > 0 { + Err((parse_errors, self)) + } else { + Ok(self) + } + } + /// Count imports by provided type pub fn import_count(&self, count_type: ImportCountType) -> usize { self.import_section() diff --git a/src/elements/reloc_section.rs b/src/elements/reloc_section.rs new file mode 100644 index 0000000..c3bdf48 --- /dev/null +++ b/src/elements/reloc_section.rs @@ -0,0 +1,346 @@ +use std::io::{Read, Write}; + +use super::{CountedList, CountedListWriter, CountedWriter, Deserialize, Error, Serialize, VarInt32, VarUint32, VarUint7}; + +const FUNCTION_INDEX_LEB: u8 = 0; +const TABLE_INDEX_SLEB: u8 = 1; +const TABLE_INDEX_I32: u8 = 2; +const MEMORY_ADDR_LEB: u8 = 3; +const MEMORY_ADDR_SLEB: u8 = 4; +const MEMORY_ADDR_I32: u8 = 5; +const TYPE_INDEX_LEB: u8 = 6; +const GLOBAL_INDEX_LEB: u8 = 7; + +/// Relocation information. +#[derive(Clone, Debug)] +pub struct RelocSection { + /// Name of this section. + name: String, + + /// ID of the section containing the relocations described in this section. + section_id: u32, + + /// Name of the section containing the relocations described in this section. Only set if section_id is 0. + relocation_section_name: Option, + + /// Relocation entries. + entries: Vec, +} + +impl RelocSection { + /// Name of this section. + pub fn name(&self) -> &str { + &self.name + } + + /// Name of this section (mutable). + pub fn name_mut(&mut self) -> &mut String { + &mut self.name + } + + /// ID of the section containing the relocations described in this section. + pub fn section_id(&self) -> u32 { + self.section_id + } + + /// ID of the section containing the relocations described in this section (mutable). + pub fn section_id_mut(&mut self) -> &mut u32 { + &mut self.section_id + } + + /// Name of the section containing the relocations described in this section. + pub fn relocation_section_name(&self) -> Option<&str> { + self.relocation_section_name.as_ref().map(String::as_str) + } + + /// Name of the section containing the relocations described in this section (mutable). + pub fn relocation_section_name_mut(&mut self) -> &mut Option { + &mut self.relocation_section_name + } + + /// List of relocation entries. + pub fn entries(&self) -> &[RelocationEntry] { + &self.entries + } + + /// List of relocation entries (mutable). + pub fn entries_mut(&mut self) -> &mut Vec { + &mut self.entries + } +} + +impl RelocSection { + /// Deserialize a reloc section. + pub fn deserialize( + name: String, + rdr: &mut R, + ) -> Result { + let section_id = VarUint32::deserialize(rdr)?.into(); + + let relocation_section_name = + if section_id == 0 { + Some(String::deserialize(rdr)?) + } + else { + None + }; + + let entries = CountedList::deserialize(rdr)?.into_inner(); + + Ok(RelocSection { + name, + section_id, + relocation_section_name, + entries, + }) + } +} + +impl Serialize for RelocSection { + type Error = Error; + + fn serialize(self, wtr: &mut W) -> Result<(), Error> { + let mut counted_writer = CountedWriter::new(wtr); + + self.name.serialize(&mut counted_writer)?; + + VarUint32::from(self.section_id).serialize(&mut counted_writer)?; + + if let Some(relocation_section_name) = self.relocation_section_name { + relocation_section_name.serialize(&mut counted_writer)?; + } + + let counted_list = CountedListWriter(self.entries.len(), self.entries.into_iter()); + counted_list.serialize(&mut counted_writer)?; + + counted_writer.done()?; + + Ok(()) + } +} + +/// Relocation entry. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum RelocationEntry { + /// Function index. + FunctionIndexLeb { + /// Offset of the value to rewrite. + offset: u32, + + /// Index of the function symbol in the symbol table. + index: u32, + }, + + /// Function table index. + TableIndexSleb { + /// Offset of the value to rewrite. + offset: u32, + + /// Index of the function symbol in the symbol table. + index: u32, + }, + + /// Function table index. + TableIndexI32 { + /// Offset of the value to rewrite. + offset: u32, + + /// Index of the function symbol in the symbol table. + index: u32, + }, + + /// Linear memory index. + MemoryAddressLeb { + /// Offset of the value to rewrite. + offset: u32, + + /// Index of the data symbol in the symbol table. + index: u32, + + /// Addend to add to the address. + addend: i32, + }, + + /// Linear memory index. + MemoryAddressSleb { + /// Offset of the value to rewrite. + offset: u32, + + /// Index of the data symbol in the symbol table. + index: u32, + + /// Addend to add to the address. + addend: i32, + }, + + /// Linear memory index. + MemoryAddressI32 { + /// Offset of the value to rewrite. + offset: u32, + + /// Index of the data symbol in the symbol table. + index: u32, + + /// Addend to add to the address. + addend: i32, + }, + + /// Type table index. + TypeIndexLeb { + /// Offset of the value to rewrite. + offset: u32, + + /// Index of the type used. + index: u32, + }, + + /// Global index. + GlobalIndexLeb { + /// Offset of the value to rewrite. + offset: u32, + + /// Index of the global symbol in the symbol table. + index: u32, + }, +} + +impl Deserialize for RelocationEntry { + type Error = Error; + + fn deserialize(rdr: &mut R) -> Result { + match VarUint7::deserialize(rdr)?.into() { + FUNCTION_INDEX_LEB => Ok(RelocationEntry::FunctionIndexLeb { + offset: VarUint32::deserialize(rdr)?.into(), + index: VarUint32::deserialize(rdr)?.into(), + }), + + TABLE_INDEX_SLEB => Ok(RelocationEntry::TableIndexSleb { + offset: VarUint32::deserialize(rdr)?.into(), + index: VarUint32::deserialize(rdr)?.into(), + }), + + TABLE_INDEX_I32 => Ok(RelocationEntry::TableIndexI32 { + offset: VarUint32::deserialize(rdr)?.into(), + index: VarUint32::deserialize(rdr)?.into(), + }), + + MEMORY_ADDR_LEB => Ok(RelocationEntry::MemoryAddressLeb { + offset: VarUint32::deserialize(rdr)?.into(), + index: VarUint32::deserialize(rdr)?.into(), + addend: VarInt32::deserialize(rdr)?.into(), + }), + + MEMORY_ADDR_SLEB => Ok(RelocationEntry::MemoryAddressSleb { + offset: VarUint32::deserialize(rdr)?.into(), + index: VarUint32::deserialize(rdr)?.into(), + addend: VarInt32::deserialize(rdr)?.into(), + }), + + MEMORY_ADDR_I32 => Ok(RelocationEntry::MemoryAddressI32 { + offset: VarUint32::deserialize(rdr)?.into(), + index: VarUint32::deserialize(rdr)?.into(), + addend: VarInt32::deserialize(rdr)?.into(), + }), + + TYPE_INDEX_LEB => Ok(RelocationEntry::TypeIndexLeb { + offset: VarUint32::deserialize(rdr)?.into(), + index: VarUint32::deserialize(rdr)?.into(), + }), + + GLOBAL_INDEX_LEB => Ok(RelocationEntry::GlobalIndexLeb { + offset: VarUint32::deserialize(rdr)?.into(), + index: VarUint32::deserialize(rdr)?.into(), + }), + + entry_type => Err(Error::UnknownValueType(entry_type as i8)), + } + } +} + +impl Serialize for RelocationEntry { + type Error = Error; + + fn serialize(self, wtr: &mut W) -> Result<(), Error> { + match self { + RelocationEntry::FunctionIndexLeb { offset, index } => { + VarUint7::from(FUNCTION_INDEX_LEB).serialize(wtr)?; + VarUint32::from(offset).serialize(wtr)?; + VarUint32::from(index).serialize(wtr)?; + }, + + RelocationEntry::TableIndexSleb { offset, index } => { + VarUint7::from(TABLE_INDEX_SLEB).serialize(wtr)?; + VarUint32::from(offset).serialize(wtr)?; + VarUint32::from(index).serialize(wtr)?; + }, + + RelocationEntry::TableIndexI32 { offset, index } => { + VarUint7::from(TABLE_INDEX_I32).serialize(wtr)?; + VarUint32::from(offset).serialize(wtr)?; + VarUint32::from(index).serialize(wtr)?; + }, + + RelocationEntry::MemoryAddressLeb { offset, index, addend } => { + VarUint7::from(MEMORY_ADDR_LEB).serialize(wtr)?; + VarUint32::from(offset).serialize(wtr)?; + VarUint32::from(index).serialize(wtr)?; + VarInt32::from(addend).serialize(wtr)?; + }, + + RelocationEntry::MemoryAddressSleb { offset, index, addend } => { + VarUint7::from(MEMORY_ADDR_SLEB).serialize(wtr)?; + VarUint32::from(offset).serialize(wtr)?; + VarUint32::from(index).serialize(wtr)?; + VarInt32::from(addend).serialize(wtr)?; + }, + + RelocationEntry::MemoryAddressI32 { offset, index, addend } => { + VarUint7::from(MEMORY_ADDR_I32).serialize(wtr)?; + VarUint32::from(offset).serialize(wtr)?; + VarUint32::from(index).serialize(wtr)?; + VarInt32::from(addend).serialize(wtr)?; + }, + + RelocationEntry::TypeIndexLeb { offset, index } => { + VarUint7::from(TYPE_INDEX_LEB).serialize(wtr)?; + VarUint32::from(offset).serialize(wtr)?; + VarUint32::from(index).serialize(wtr)?; + }, + + RelocationEntry::GlobalIndexLeb { offset, index } => { + VarUint7::from(GLOBAL_INDEX_LEB).serialize(wtr)?; + VarUint32::from(offset).serialize(wtr)?; + VarUint32::from(index).serialize(wtr)?; + }, + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::super::{Section, deserialize_file}; + use super::RelocationEntry; + + #[test] + fn reloc_section() { + let module = + deserialize_file("./res/cases/v1/relocatable.wasm").expect("Module should be deserialized") + .parse_reloc().expect("Reloc section should be deserialized"); + let mut found = false; + for section in module.sections() { + match *section { + Section::Reloc(ref reloc_section) => { + assert_eq!(vec![ + RelocationEntry::MemoryAddressSleb { offset: 4, index: 0, addend: 0 }, + RelocationEntry::FunctionIndexLeb { offset: 12, index: 0 }, + ], reloc_section.entries()); + found = true + }, + _ => { } + } + } + assert!(found, "There should be a reloc section in relocatable.wasm"); + } +} diff --git a/src/elements/section.rs b/src/elements/section.rs index 98fa451..4853bec 100644 --- a/src/elements/section.rs +++ b/src/elements/section.rs @@ -23,6 +23,7 @@ use super::{ use super::types::Type; use super::name_section::NameSection; +use super::reloc_section::RelocSection; const ENTRIES_BUFFER_LENGTH: usize = 16384; @@ -64,6 +65,13 @@ pub enum Section { /// /// Note that initially it is not parsed until `parse_names` is called explicitly. Name(NameSection), + /// Relocation section. + /// + /// Note that initially it is not parsed until `parse_reloc` is called explicitly. + /// Also note that currently there are serialization (but not de-serialization) + /// issues with this section + /// (see https://github.com/paritytech/parity-wasm/issues/198) + Reloc(RelocSection), } impl Deserialize for Section { @@ -191,7 +199,11 @@ impl Serialize for Section { payload: serialize(name_section)?, }; custom.serialize(writer)?; - } + }, + Section::Reloc(reloc_section) => { + VarUint7::from(0x00).serialize(writer)?; + reloc_section.serialize(writer)?; + }, } Ok(()) } @@ -214,6 +226,7 @@ impl Section { Section::Code(_) => 0x0a, Section::Data(_) => 0x0b, Section::Name(_) => 0x00, + Section::Reloc(_) => 0x00, } } }