use alloc::boxed::Box;
use alloc::vec::Vec;
use core::marker::PhantomData;
use core::ops::Range;
use core::{slice, str};
use wasmparser as wp;
use crate::read::{
self, Architecture, ComdatKind, CompressedData, CompressedFileRange, Error, Export, FileFlags,
Import, NoDynamicRelocationIterator, Object, ObjectComdat, ObjectKind, ObjectSection,
ObjectSegment, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, Relocation, RelocationMap,
Result, SectionFlags, SectionIndex, SectionKind, SegmentFlags, SymbolFlags, SymbolIndex,
SymbolKind, SymbolScope, SymbolSection,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(usize)]
enum SectionId {
Custom = 0,
Type = 1,
Import = 2,
Function = 3,
Table = 4,
Memory = 5,
Global = 6,
Export = 7,
Start = 8,
Element = 9,
Code = 10,
Data = 11,
DataCount = 12,
Tag = 13,
}
const MAX_SECTION_ID: usize = SectionId::Tag as usize;
#[derive(Debug)]
pub struct WasmFile<'data, R = &'data [u8]> {
data: &'data [u8],
has_memory64: bool,
sections: Vec<SectionHeader<'data>>,
id_sections: Box<[Option<usize>; MAX_SECTION_ID + 1]>,
has_debug_symbols: bool,
symbols: Vec<WasmSymbolInternal<'data>>,
entry: u64,
marker: PhantomData<R>,
}
#[derive(Debug)]
struct SectionHeader<'data> {
id: SectionId,
range: Range<usize>,
name: &'data str,
}
#[derive(Clone)]
enum LocalFunctionKind {
Unknown,
Exported { symbol_ids: Vec<u32> },
Local { symbol_id: u32 },
}
impl<T> ReadError<T> for wasmparser::Result<T> {
fn read_error(self, error: &'static str) -> Result<T> {
self.map_err(|_| Error(error))
}
}
impl<'data, R: ReadRef<'data>> WasmFile<'data, R> {
pub fn parse(data: R) -> Result<Self> {
let len = data.len().read_error("Unknown Wasm file size")?;
let data = data.read_bytes_at(0, len).read_error("Wasm read failed")?;
let parser = wp::Parser::new(0).parse_all(data);
let mut file = WasmFile {
data,
has_memory64: false,
sections: Vec::new(),
id_sections: Default::default(),
has_debug_symbols: false,
symbols: Vec::new(),
entry: 0,
marker: PhantomData,
};
let mut main_file_symbol = Some(WasmSymbolInternal {
name: "",
address: 0,
size: 0,
kind: SymbolKind::File,
section: SymbolSection::None,
scope: SymbolScope::Compilation,
});
let mut imported_funcs_count = 0;
let mut local_func_kinds = Vec::new();
let mut entry_func_id = None;
let mut code_range_start = 0;
let mut code_func_index = 0;
let mut global_values = Vec::new();
for payload in parser {
let payload = payload.read_error("Invalid Wasm section header")?;
match payload {
wp::Payload::Version { encoding, .. } => {
if encoding != wp::Encoding::Module {
return Err(Error("Unsupported Wasm encoding"));
}
}
wp::Payload::TypeSection(section) => {
file.add_section(SectionId::Type, section.range(), "");
}
wp::Payload::ImportSection(section) => {
file.add_section(SectionId::Import, section.range(), "");
let mut last_module_name = None;
for import in section {
let import = import.read_error("Couldn't read an import item")?;
let module_name = import.module;
if last_module_name != Some(module_name) {
file.symbols.push(WasmSymbolInternal {
name: module_name,
address: 0,
size: 0,
kind: SymbolKind::File,
section: SymbolSection::None,
scope: SymbolScope::Dynamic,
});
last_module_name = Some(module_name);
}
let kind = match import.ty {
wp::TypeRef::Func(_) => {
imported_funcs_count += 1;
SymbolKind::Text
}
wp::TypeRef::Memory(memory) => {
file.has_memory64 |= memory.memory64;
SymbolKind::Data
}
wp::TypeRef::Table(_) | wp::TypeRef::Global(_) => SymbolKind::Data,
wp::TypeRef::Tag(_) => SymbolKind::Unknown,
};
file.symbols.push(WasmSymbolInternal {
name: import.name,
address: 0,
size: 0,
kind,
section: SymbolSection::Undefined,
scope: SymbolScope::Dynamic,
});
}
}
wp::Payload::FunctionSection(section) => {
file.add_section(SectionId::Function, section.range(), "");
local_func_kinds =
vec![LocalFunctionKind::Unknown; section.into_iter().count()];
}
wp::Payload::TableSection(section) => {
file.add_section(SectionId::Table, section.range(), "");
}
wp::Payload::MemorySection(section) => {
file.add_section(SectionId::Memory, section.range(), "");
for memory in section {
let memory = memory.read_error("Couldn't read a memory item")?;
file.has_memory64 |= memory.memory64;
}
}
wp::Payload::GlobalSection(section) => {
file.add_section(SectionId::Global, section.range(), "");
for global in section {
let global = global.read_error("Couldn't read a global item")?;
let mut address = None;
if !global.ty.mutable {
let init = global.init_expr.get_operators_reader().read();
address = match init.read_error("Couldn't read a global init expr")? {
wp::Operator::I32Const { value } => Some(value as u64),
wp::Operator::I64Const { value } => Some(value as u64),
_ => None,
};
}
global_values.push(address);
}
}
wp::Payload::ExportSection(section) => {
file.add_section(SectionId::Export, section.range(), "");
if let Some(main_file_symbol) = main_file_symbol.take() {
file.symbols.push(main_file_symbol);
}
for export in section {
let export = export.read_error("Couldn't read an export item")?;
let (kind, section_idx) = match export.kind {
wp::ExternalKind::Func => {
if let Some(local_func_id) =
export.index.checked_sub(imported_funcs_count)
{
let local_func_kind = local_func_kinds
.get_mut(local_func_id as usize)
.read_error("Invalid Wasm export index")?;
if let LocalFunctionKind::Unknown = local_func_kind {
*local_func_kind = LocalFunctionKind::Exported {
symbol_ids: Vec::new(),
};
}
let symbol_ids = match local_func_kind {
LocalFunctionKind::Exported { symbol_ids } => symbol_ids,
_ => unreachable!(),
};
symbol_ids.push(file.symbols.len() as u32);
}
(SymbolKind::Text, SectionId::Code)
}
wp::ExternalKind::Table
| wp::ExternalKind::Memory
| wp::ExternalKind::Global => (SymbolKind::Data, SectionId::Data),
wp::ExternalKind::Tag => continue,
};
let mut address = 0;
if export.kind == wp::ExternalKind::Global {
if let Some(&Some(x)) = global_values.get(export.index as usize) {
address = x;
}
}
file.symbols.push(WasmSymbolInternal {
name: export.name,
address,
size: 0,
kind,
section: SymbolSection::Section(SectionIndex(section_idx as usize)),
scope: SymbolScope::Dynamic,
});
}
}
wp::Payload::StartSection { func, range, .. } => {
file.add_section(SectionId::Start, range, "");
entry_func_id = Some(func);
}
wp::Payload::ElementSection(section) => {
file.add_section(SectionId::Element, section.range(), "");
}
wp::Payload::CodeSectionStart { range, .. } => {
code_range_start = range.start;
file.add_section(SectionId::Code, range, "");
if let Some(main_file_symbol) = main_file_symbol.take() {
file.symbols.push(main_file_symbol);
}
}
wp::Payload::CodeSectionEntry(body) => {
let i = code_func_index;
code_func_index += 1;
let range = body.range();
let address = range.start as u64 - code_range_start as u64;
let size = (range.end - range.start) as u64;
if entry_func_id == Some(i as u32) {
file.entry = address;
}
let local_func_kind = local_func_kinds
.get_mut(i)
.read_error("Invalid Wasm code section index")?;
match local_func_kind {
LocalFunctionKind::Unknown => {
*local_func_kind = LocalFunctionKind::Local {
symbol_id: file.symbols.len() as u32,
};
file.symbols.push(WasmSymbolInternal {
name: "",
address,
size,
kind: SymbolKind::Text,
section: SymbolSection::Section(SectionIndex(
SectionId::Code as usize,
)),
scope: SymbolScope::Compilation,
});
}
LocalFunctionKind::Exported { symbol_ids } => {
for symbol_id in core::mem::take(symbol_ids) {
let export_symbol = &mut file.symbols[symbol_id as usize];
export_symbol.address = address;
export_symbol.size = size;
}
}
_ => unreachable!(),
}
}
wp::Payload::DataSection(section) => {
file.add_section(SectionId::Data, section.range(), "");
}
wp::Payload::DataCountSection { range, .. } => {
file.add_section(SectionId::DataCount, range, "");
}
wp::Payload::TagSection(section) => {
file.add_section(SectionId::Tag, section.range(), "");
}
wp::Payload::CustomSection(section) => {
let name = section.name();
let size = section.data().len();
let mut range = section.range();
range.start = range.end - size;
file.add_section(SectionId::Custom, range, name);
if name == "name" {
let reader = wp::BinaryReader::new(section.data(), section.data_offset());
for name in wp::NameSectionReader::new(reader) {
if let Ok(wp::Name::Function(name_map)) = name {
for naming in name_map {
let naming =
naming.read_error("Couldn't read a function name")?;
if let Some(local_index) =
naming.index.checked_sub(imported_funcs_count)
{
if let LocalFunctionKind::Local { symbol_id } =
local_func_kinds[local_index as usize]
{
file.symbols[symbol_id as usize].name = naming.name;
}
}
}
}
}
} else if name.starts_with(".debug_") {
file.has_debug_symbols = true;
}
}
_ => {}
}
}
Ok(file)
}
fn add_section(&mut self, id: SectionId, range: Range<usize>, name: &'data str) {
let section = SectionHeader { id, range, name };
self.id_sections[id as usize] = Some(self.sections.len());
self.sections.push(section);
}
}
impl<'data, R> read::private::Sealed for WasmFile<'data, R> {}
impl<'data, R: ReadRef<'data>> Object<'data> for WasmFile<'data, R> {
type Segment<'file> = WasmSegment<'data, 'file, R> where Self: 'file, 'data: 'file;
type SegmentIterator<'file> = WasmSegmentIterator<'data, 'file, R> where Self: 'file, 'data: 'file;
type Section<'file> = WasmSection<'data, 'file, R> where Self: 'file, 'data: 'file;
type SectionIterator<'file> = WasmSectionIterator<'data, 'file, R> where Self: 'file, 'data: 'file;
type Comdat<'file> = WasmComdat<'data, 'file, R> where Self: 'file, 'data: 'file;
type ComdatIterator<'file> = WasmComdatIterator<'data, 'file, R> where Self: 'file, 'data: 'file;
type Symbol<'file> = WasmSymbol<'data, 'file> where Self: 'file, 'data: 'file;
type SymbolIterator<'file> = WasmSymbolIterator<'data, 'file> where Self: 'file, 'data: 'file;
type SymbolTable<'file> = WasmSymbolTable<'data, 'file> where Self: 'file, 'data: 'file;
type DynamicRelocationIterator<'file> = NoDynamicRelocationIterator where Self: 'file, 'data: 'file;
#[inline]
fn architecture(&self) -> Architecture {
if self.has_memory64 {
Architecture::Wasm64
} else {
Architecture::Wasm32
}
}
#[inline]
fn is_little_endian(&self) -> bool {
true
}
#[inline]
fn is_64(&self) -> bool {
self.has_memory64
}
fn kind(&self) -> ObjectKind {
ObjectKind::Unknown
}
fn segments(&self) -> Self::SegmentIterator<'_> {
WasmSegmentIterator { file: self }
}
fn section_by_name_bytes<'file>(
&'file self,
section_name: &[u8],
) -> Option<WasmSection<'data, 'file, R>> {
self.sections()
.find(|section| section.name_bytes() == Ok(section_name))
}
fn section_by_index(&self, index: SectionIndex) -> Result<WasmSection<'data, '_, R>> {
let id_section = self
.id_sections
.get(index.0)
.and_then(|x| *x)
.read_error("Invalid Wasm section index")?;
let section = self.sections.get(id_section).unwrap();
Ok(WasmSection {
file: self,
section,
})
}
fn sections(&self) -> Self::SectionIterator<'_> {
WasmSectionIterator {
file: self,
sections: self.sections.iter(),
}
}
fn comdats(&self) -> Self::ComdatIterator<'_> {
WasmComdatIterator { file: self }
}
#[inline]
fn symbol_by_index(&self, index: SymbolIndex) -> Result<WasmSymbol<'data, '_>> {
let symbol = self
.symbols
.get(index.0)
.read_error("Invalid Wasm symbol index")?;
Ok(WasmSymbol { index, symbol })
}
fn symbols(&self) -> Self::SymbolIterator<'_> {
WasmSymbolIterator {
symbols: self.symbols.iter().enumerate(),
}
}
fn symbol_table(&self) -> Option<WasmSymbolTable<'data, '_>> {
Some(WasmSymbolTable {
symbols: &self.symbols,
})
}
fn dynamic_symbols(&self) -> Self::SymbolIterator<'_> {
WasmSymbolIterator {
symbols: [].iter().enumerate(),
}
}
#[inline]
fn dynamic_symbol_table(&self) -> Option<WasmSymbolTable<'data, '_>> {
None
}
#[inline]
fn dynamic_relocations(&self) -> Option<NoDynamicRelocationIterator> {
None
}
fn imports(&self) -> Result<Vec<Import<'data>>> {
Ok(Vec::new())
}
fn exports(&self) -> Result<Vec<Export<'data>>> {
Ok(Vec::new())
}
fn has_debug_symbols(&self) -> bool {
self.has_debug_symbols
}
fn relative_address_base(&self) -> u64 {
0
}
#[inline]
fn entry(&self) -> u64 {
self.entry
}
#[inline]
fn flags(&self) -> FileFlags {
FileFlags::None
}
}
#[derive(Debug)]
pub struct WasmSegmentIterator<'data, 'file, R = &'data [u8]> {
#[allow(unused)]
file: &'file WasmFile<'data, R>,
}
impl<'data, 'file, R> Iterator for WasmSegmentIterator<'data, 'file, R> {
type Item = WasmSegment<'data, 'file, R>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
None
}
}
#[derive(Debug)]
pub struct WasmSegment<'data, 'file, R = &'data [u8]> {
#[allow(unused)]
file: &'file WasmFile<'data, R>,
}
impl<'data, 'file, R> read::private::Sealed for WasmSegment<'data, 'file, R> {}
impl<'data, 'file, R> ObjectSegment<'data> for WasmSegment<'data, 'file, R> {
#[inline]
fn address(&self) -> u64 {
unreachable!()
}
#[inline]
fn size(&self) -> u64 {
unreachable!()
}
#[inline]
fn align(&self) -> u64 {
unreachable!()
}
#[inline]
fn file_range(&self) -> (u64, u64) {
unreachable!()
}
fn data(&self) -> Result<&'data [u8]> {
unreachable!()
}
fn data_range(&self, _address: u64, _size: u64) -> Result<Option<&'data [u8]>> {
unreachable!()
}
#[inline]
fn name_bytes(&self) -> Result<Option<&[u8]>> {
unreachable!()
}
#[inline]
fn name(&self) -> Result<Option<&str>> {
unreachable!()
}
#[inline]
fn flags(&self) -> SegmentFlags {
unreachable!()
}
}
#[derive(Debug)]
pub struct WasmSectionIterator<'data, 'file, R = &'data [u8]> {
file: &'file WasmFile<'data, R>,
sections: slice::Iter<'file, SectionHeader<'data>>,
}
impl<'data, 'file, R> Iterator for WasmSectionIterator<'data, 'file, R> {
type Item = WasmSection<'data, 'file, R>;
fn next(&mut self) -> Option<Self::Item> {
let section = self.sections.next()?;
Some(WasmSection {
file: self.file,
section,
})
}
}
#[derive(Debug)]
pub struct WasmSection<'data, 'file, R = &'data [u8]> {
file: &'file WasmFile<'data, R>,
section: &'file SectionHeader<'data>,
}
impl<'data, 'file, R> read::private::Sealed for WasmSection<'data, 'file, R> {}
impl<'data, 'file, R: ReadRef<'data>> ObjectSection<'data> for WasmSection<'data, 'file, R> {
type RelocationIterator = WasmRelocationIterator<'data, 'file, R>;
#[inline]
fn index(&self) -> SectionIndex {
SectionIndex(self.section.id as usize)
}
#[inline]
fn address(&self) -> u64 {
0
}
#[inline]
fn size(&self) -> u64 {
let range = &self.section.range;
(range.end - range.start) as u64
}
#[inline]
fn align(&self) -> u64 {
1
}
#[inline]
fn file_range(&self) -> Option<(u64, u64)> {
let range = &self.section.range;
Some((range.start as _, range.end as _))
}
#[inline]
fn data(&self) -> Result<&'data [u8]> {
let range = &self.section.range;
self.file
.data
.read_bytes_at(range.start as u64, range.end as u64 - range.start as u64)
.read_error("Invalid Wasm section size or offset")
}
fn data_range(&self, _address: u64, _size: u64) -> Result<Option<&'data [u8]>> {
unimplemented!()
}
#[inline]
fn compressed_file_range(&self) -> Result<CompressedFileRange> {
Ok(CompressedFileRange::none(self.file_range()))
}
#[inline]
fn compressed_data(&self) -> Result<CompressedData<'data>> {
self.data().map(CompressedData::none)
}
#[inline]
fn name_bytes(&self) -> Result<&'data [u8]> {
self.name().map(str::as_bytes)
}
#[inline]
fn name(&self) -> Result<&'data str> {
Ok(match self.section.id {
SectionId::Custom => self.section.name,
SectionId::Type => "<type>",
SectionId::Import => "<import>",
SectionId::Function => "<function>",
SectionId::Table => "<table>",
SectionId::Memory => "<memory>",
SectionId::Global => "<global>",
SectionId::Export => "<export>",
SectionId::Start => "<start>",
SectionId::Element => "<element>",
SectionId::Code => "<code>",
SectionId::Data => "<data>",
SectionId::DataCount => "<data_count>",
SectionId::Tag => "<tag>",
})
}
#[inline]
fn segment_name_bytes(&self) -> Result<Option<&[u8]>> {
Ok(None)
}
#[inline]
fn segment_name(&self) -> Result<Option<&str>> {
Ok(None)
}
#[inline]
fn kind(&self) -> SectionKind {
match self.section.id {
SectionId::Custom => match self.section.name {
"reloc." | "linking" => SectionKind::Linker,
_ => SectionKind::Other,
},
SectionId::Type => SectionKind::Metadata,
SectionId::Import => SectionKind::Linker,
SectionId::Function => SectionKind::Metadata,
SectionId::Table => SectionKind::UninitializedData,
SectionId::Memory => SectionKind::UninitializedData,
SectionId::Global => SectionKind::Data,
SectionId::Export => SectionKind::Linker,
SectionId::Start => SectionKind::Linker,
SectionId::Element => SectionKind::Data,
SectionId::Code => SectionKind::Text,
SectionId::Data => SectionKind::Data,
SectionId::DataCount => SectionKind::UninitializedData,
SectionId::Tag => SectionKind::Data,
}
}
#[inline]
fn relocations(&self) -> WasmRelocationIterator<'data, 'file, R> {
WasmRelocationIterator(PhantomData)
}
fn relocation_map(&self) -> read::Result<RelocationMap> {
RelocationMap::new(self.file, self)
}
#[inline]
fn flags(&self) -> SectionFlags {
SectionFlags::None
}
}
#[derive(Debug)]
pub struct WasmComdatIterator<'data, 'file, R = &'data [u8]> {
#[allow(unused)]
file: &'file WasmFile<'data, R>,
}
impl<'data, 'file, R> Iterator for WasmComdatIterator<'data, 'file, R> {
type Item = WasmComdat<'data, 'file, R>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
None
}
}
#[derive(Debug)]
pub struct WasmComdat<'data, 'file, R = &'data [u8]> {
#[allow(unused)]
file: &'file WasmFile<'data, R>,
}
impl<'data, 'file, R> read::private::Sealed for WasmComdat<'data, 'file, R> {}
impl<'data, 'file, R> ObjectComdat<'data> for WasmComdat<'data, 'file, R> {
type SectionIterator = WasmComdatSectionIterator<'data, 'file, R>;
#[inline]
fn kind(&self) -> ComdatKind {
unreachable!();
}
#[inline]
fn symbol(&self) -> SymbolIndex {
unreachable!();
}
#[inline]
fn name_bytes(&self) -> Result<&'data [u8]> {
unreachable!();
}
#[inline]
fn name(&self) -> Result<&'data str> {
unreachable!();
}
#[inline]
fn sections(&self) -> Self::SectionIterator {
unreachable!();
}
}
#[derive(Debug)]
pub struct WasmComdatSectionIterator<'data, 'file, R = &'data [u8]> {
#[allow(unused)]
file: &'file WasmFile<'data, R>,
}
impl<'data, 'file, R> Iterator for WasmComdatSectionIterator<'data, 'file, R> {
type Item = SectionIndex;
fn next(&mut self) -> Option<Self::Item> {
None
}
}
#[derive(Debug)]
pub struct WasmSymbolTable<'data, 'file> {
symbols: &'file [WasmSymbolInternal<'data>],
}
impl<'data, 'file> read::private::Sealed for WasmSymbolTable<'data, 'file> {}
impl<'data, 'file> ObjectSymbolTable<'data> for WasmSymbolTable<'data, 'file> {
type Symbol = WasmSymbol<'data, 'file>;
type SymbolIterator = WasmSymbolIterator<'data, 'file>;
fn symbols(&self) -> Self::SymbolIterator {
WasmSymbolIterator {
symbols: self.symbols.iter().enumerate(),
}
}
fn symbol_by_index(&self, index: SymbolIndex) -> Result<Self::Symbol> {
let symbol = self
.symbols
.get(index.0)
.read_error("Invalid Wasm symbol index")?;
Ok(WasmSymbol { index, symbol })
}
}
#[derive(Debug)]
pub struct WasmSymbolIterator<'data, 'file> {
symbols: core::iter::Enumerate<slice::Iter<'file, WasmSymbolInternal<'data>>>,
}
impl<'data, 'file> Iterator for WasmSymbolIterator<'data, 'file> {
type Item = WasmSymbol<'data, 'file>;
fn next(&mut self) -> Option<Self::Item> {
let (index, symbol) = self.symbols.next()?;
Some(WasmSymbol {
index: SymbolIndex(index),
symbol,
})
}
}
#[derive(Clone, Copy, Debug)]
pub struct WasmSymbol<'data, 'file> {
index: SymbolIndex,
symbol: &'file WasmSymbolInternal<'data>,
}
#[derive(Clone, Debug)]
struct WasmSymbolInternal<'data> {
name: &'data str,
address: u64,
size: u64,
kind: SymbolKind,
section: SymbolSection,
scope: SymbolScope,
}
impl<'data, 'file> read::private::Sealed for WasmSymbol<'data, 'file> {}
impl<'data, 'file> ObjectSymbol<'data> for WasmSymbol<'data, 'file> {
#[inline]
fn index(&self) -> SymbolIndex {
self.index
}
#[inline]
fn name_bytes(&self) -> read::Result<&'data [u8]> {
Ok(self.symbol.name.as_bytes())
}
#[inline]
fn name(&self) -> read::Result<&'data str> {
Ok(self.symbol.name)
}
#[inline]
fn address(&self) -> u64 {
self.symbol.address
}
#[inline]
fn size(&self) -> u64 {
self.symbol.size
}
#[inline]
fn kind(&self) -> SymbolKind {
self.symbol.kind
}
#[inline]
fn section(&self) -> SymbolSection {
self.symbol.section
}
#[inline]
fn is_undefined(&self) -> bool {
self.symbol.section == SymbolSection::Undefined
}
#[inline]
fn is_definition(&self) -> bool {
(self.symbol.kind == SymbolKind::Text || self.symbol.kind == SymbolKind::Data)
&& self.symbol.section != SymbolSection::Undefined
}
#[inline]
fn is_common(&self) -> bool {
self.symbol.section == SymbolSection::Common
}
#[inline]
fn is_weak(&self) -> bool {
false
}
#[inline]
fn scope(&self) -> SymbolScope {
self.symbol.scope
}
#[inline]
fn is_global(&self) -> bool {
self.symbol.scope != SymbolScope::Compilation
}
#[inline]
fn is_local(&self) -> bool {
self.symbol.scope == SymbolScope::Compilation
}
#[inline]
fn flags(&self) -> SymbolFlags<SectionIndex, SymbolIndex> {
SymbolFlags::None
}
}
#[derive(Debug)]
pub struct WasmRelocationIterator<'data, 'file, R = &'data [u8]>(
PhantomData<(&'data (), &'file (), R)>,
);
impl<'data, 'file, R> Iterator for WasmRelocationIterator<'data, 'file, R> {
type Item = (u64, Relocation);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
None
}
}