blob: d4aee7101344c83b6bad3959c27f44a81e7c995f [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use failure::Fail;
use fallible_iterator::FallibleIterator;
use gimli::{Reader, ReaderOffset};
use std::convert::TryInto;
use std::num::NonZeroU8;
#[derive(Debug, Fail)]
pub enum Error {
#[fail(display = "WebAssembly magic mismatch.")]
InvalidMagic,
#[fail(display = "Unsupported WebAssembly version {}.", _0)]
UnsupportedVersion(u32),
#[fail(display = "Missing code section.")]
MissingCodeSection,
#[fail(display = "{}", _0)]
Reader(#[fail(cause)] gimli::Error),
}
impl From<gimli::Error> for Error {
fn from(err: gimli::Error) -> Self {
Error::Reader(err)
}
}
#[derive(Debug)]
pub enum SectionKind {
Custom { name: String },
Standard { id: NonZeroU8 },
}
#[derive(Debug)]
pub struct Section<R> {
pub kind: SectionKind,
pub payload: R,
}
pub fn parse_sections<R: Reader>(
mut reader: R,
) -> Result<impl FallibleIterator<Item = Section<R>, Error = gimli::Error>, Error> {
struct Iterator<R> {
reader: R,
}
impl<R: Reader> FallibleIterator for Iterator<R> {
type Item = Section<R>;
type Error = gimli::Error;
fn next(&mut self) -> Result<Option<Section<R>>, gimli::Error> {
if self.reader.is_empty() {
return Ok(None);
}
let id = self
.reader
.read_uleb128()?
.try_into()
.map_err(|_| gimli::Error::BadUnsignedLeb128)?;
let payload_len = ReaderOffset::from_u64(self.reader.read_uleb128()?)?;
let mut payload_reader = self.reader.split(payload_len)?;
let kind = match NonZeroU8::new(id) {
None => {
let name_len = ReaderOffset::from_u64(payload_reader.read_uleb128()?)?;
let name_reader = payload_reader.split(name_len)?;
SectionKind::Custom {
name: name_reader.to_string()?.into_owned(),
}
}
Some(id) => SectionKind::Standard { id },
};
Ok(Some(Section {
kind,
payload: payload_reader,
}))
}
}
if reader.read_u8_array::<[u8; 4]>()? != *b"\0asm" {
return Err(Error::InvalidMagic);
}
let version = reader.read_u32()?;
if version != 1 {
return Err(Error::UnsupportedVersion(version));
}
Ok(Iterator { reader })
}