use std::borrow::Cow;
use std::fmt;
use thiserror::Error;
use symbolic_common::{Arch, AsSelf, CodeId, DebugId, Uuid};
use crate::base::*;
use crate::dwarf::{Dwarf, DwarfDebugSession, DwarfError, DwarfSection, Endian};
mod parser;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum WasmError {
#[error("invalid wasm file")]
Read(#[from] wasmparser::BinaryReaderError),
#[error("function references unknown type")]
UnknownFunctionType,
}
pub struct WasmObject<'data> {
dwarf_sections: Vec<(&'data str, &'data [u8])>,
funcs: Vec<Symbol<'data>>,
build_id: Option<&'data [u8]>,
data: &'data [u8],
code_offset: u64,
kind: ObjectKind,
}
impl<'data> WasmObject<'data> {
pub fn test(data: &[u8]) -> bool {
data.starts_with(b"\x00asm")
}
pub fn file_format(&self) -> FileFormat {
FileFormat::Wasm
}
#[inline]
pub fn code_id(&self) -> Option<CodeId> {
self.build_id.map(CodeId::from_binary)
}
#[inline]
pub fn debug_id(&self) -> DebugId {
self.build_id
.and_then(|data| {
data.get(..16)
.and_then(|first_16| Uuid::from_slice(first_16).ok())
})
.map(DebugId::from_uuid)
.unwrap_or_else(DebugId::nil)
}
pub fn arch(&self) -> Arch {
Arch::Wasm32
}
#[inline]
pub fn kind(&self) -> ObjectKind {
self.kind
}
pub fn load_address(&self) -> u64 {
0
}
pub fn has_symbols(&self) -> bool {
true
}
pub fn symbols(&self) -> WasmSymbolIterator<'data, '_> {
WasmSymbolIterator {
funcs: self.funcs.clone().into_iter(),
_marker: std::marker::PhantomData,
}
}
pub fn symbol_map(&self) -> SymbolMap<'data> {
self.symbols().collect()
}
#[inline]
pub fn has_debug_info(&self) -> bool {
self.dwarf_sections
.iter()
.any(|(name, _)| *name == ".debug_info")
}
pub fn debug_session(&self) -> Result<DwarfDebugSession<'data>, DwarfError> {
let symbols = self.symbol_map();
DwarfDebugSession::parse(self, symbols, -(self.code_offset() as i64), self.kind())
}
#[inline]
pub fn has_unwind_info(&self) -> bool {
self.dwarf_sections
.iter()
.any(|(name, _)| *name == ".debug_frame")
}
pub fn has_sources(&self) -> bool {
false
}
pub fn is_malformed(&self) -> bool {
false
}
pub fn data(&self) -> &'data [u8] {
self.data
}
pub fn code_offset(&self) -> u64 {
self.code_offset
}
}
impl fmt::Debug for WasmObject<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("WasmObject")
.field("code_id", &self.code_id())
.field("debug_id", &self.debug_id())
.field("arch", &self.arch())
.field("kind", &self.kind())
.field("load_address", &format_args!("{:#x}", self.load_address()))
.field("has_symbols", &self.has_symbols())
.field("has_debug_info", &self.has_debug_info())
.field("has_unwind_info", &self.has_unwind_info())
.field("is_malformed", &self.is_malformed())
.finish()
}
}
impl<'slf, 'd: 'slf> AsSelf<'slf> for WasmObject<'d> {
type Ref = WasmObject<'slf>;
fn as_self(&'slf self) -> &Self::Ref {
self
}
}
impl<'d> Parse<'d> for WasmObject<'d> {
type Error = WasmError;
fn test(data: &[u8]) -> bool {
Self::test(data)
}
fn parse(data: &'d [u8]) -> Result<Self, WasmError> {
Self::parse(data)
}
}
impl<'data: 'object, 'object> ObjectLike<'data, 'object> for WasmObject<'data> {
type Error = DwarfError;
type Session = DwarfDebugSession<'data>;
type SymbolIterator = WasmSymbolIterator<'data, 'object>;
fn file_format(&self) -> FileFormat {
self.file_format()
}
fn code_id(&self) -> Option<CodeId> {
self.code_id()
}
fn debug_id(&self) -> DebugId {
self.debug_id()
}
fn arch(&self) -> Arch {
self.arch()
}
fn kind(&self) -> ObjectKind {
self.kind()
}
fn load_address(&self) -> u64 {
self.load_address()
}
fn has_symbols(&self) -> bool {
self.has_symbols()
}
fn symbols(&'object self) -> Self::SymbolIterator {
self.symbols()
}
fn symbol_map(&self) -> SymbolMap<'data> {
self.symbol_map()
}
fn has_debug_info(&self) -> bool {
self.has_debug_info()
}
fn debug_session(&self) -> Result<Self::Session, Self::Error> {
self.debug_session()
}
fn has_unwind_info(&self) -> bool {
self.has_unwind_info()
}
fn has_sources(&self) -> bool {
self.has_sources()
}
fn is_malformed(&self) -> bool {
self.is_malformed()
}
}
impl<'data> Dwarf<'data> for WasmObject<'data> {
fn endianity(&self) -> Endian {
Endian::Little
}
fn raw_section(&self, section_name: &str) -> Option<DwarfSection<'data>> {
self.dwarf_sections.iter().find_map(|(name, data)| {
if name.strip_prefix('.') == Some(section_name) {
Some(DwarfSection {
data: Cow::Borrowed(data),
address: 0,
offset: 0,
align: 4,
})
} else {
None
}
})
}
}
pub struct WasmSymbolIterator<'data, 'object> {
funcs: std::vec::IntoIter<Symbol<'data>>,
_marker: std::marker::PhantomData<&'object u8>,
}
impl<'data, 'object> Iterator for WasmSymbolIterator<'data, 'object> {
type Item = Symbol<'data>;
fn next(&mut self) -> Option<Self::Item> {
self.funcs.next()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_invalid_header() {
let data = b"\x00asm ";
assert!(WasmObject::parse(data).is_err());
}
}