parity-wasm 0.28.0

WebAssembly binary format serialization/deserialization/interpreter
Documentation
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, PartialEq)]
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<String>,

	/// Relocation entries.
	entries: Vec<RelocationEntry>,
}

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<String> {
		&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<RelocationEntry> {
		&mut self.entries
	}
}

impl RelocSection {
	/// Deserialize a reloc section.
	pub fn deserialize<R: Read>(
		name: String,
		rdr: &mut R,
	) -> Result<Self, Error> {
		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<W: Write>(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<R: Read>(rdr: &mut R) -> Result<Self, Self::Error> {
		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<W: Write>(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");
	}
}