object/read/
wasm.rs

1//! Support for reading Wasm files.
2//!
3//! [`WasmFile`] implements the [`Object`] trait for Wasm files.
4use alloc::boxed::Box;
5use alloc::vec::Vec;
6use core::marker::PhantomData;
7use core::ops::Range;
8use core::{slice, str};
9use wasmparser as wp;
10
11use crate::read::{
12    self, Architecture, ComdatKind, CompressedData, CompressedFileRange, Error, Export, FileFlags,
13    Import, NoDynamicRelocationIterator, Object, ObjectComdat, ObjectKind, ObjectSection,
14    ObjectSegment, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, Relocation, RelocationMap,
15    Result, SectionFlags, SectionIndex, SectionKind, SegmentFlags, SymbolFlags, SymbolIndex,
16    SymbolKind, SymbolScope, SymbolSection,
17};
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20#[repr(usize)]
21enum SectionId {
22    Custom = 0,
23    Type = 1,
24    Import = 2,
25    Function = 3,
26    Table = 4,
27    Memory = 5,
28    Global = 6,
29    Export = 7,
30    Start = 8,
31    Element = 9,
32    Code = 10,
33    Data = 11,
34    DataCount = 12,
35    Tag = 13,
36}
37// Update this constant when adding new section id:
38const MAX_SECTION_ID: usize = SectionId::Tag as usize;
39
40/// A WebAssembly object file.
41#[derive(Debug)]
42pub struct WasmFile<'data, R = &'data [u8]> {
43    data: &'data [u8],
44    has_memory64: bool,
45    // All sections, including custom sections.
46    sections: Vec<SectionHeader<'data>>,
47    // Indices into `sections` of sections with a non-zero id.
48    id_sections: Box<[Option<usize>; MAX_SECTION_ID + 1]>,
49    // Whether the file has DWARF information.
50    has_debug_symbols: bool,
51    // Symbols collected from imports, exports, code and name sections.
52    symbols: Vec<WasmSymbolInternal<'data>>,
53    // Address of the function body for the entry point.
54    entry: u64,
55    marker: PhantomData<R>,
56}
57
58#[derive(Debug)]
59struct SectionHeader<'data> {
60    id: SectionId,
61    range: Range<usize>,
62    name: &'data str,
63}
64
65#[derive(Clone)]
66enum LocalFunctionKind {
67    Unknown,
68    Exported,
69}
70
71impl<T> ReadError<T> for wasmparser::Result<T> {
72    fn read_error(self, error: &'static str) -> Result<T> {
73        self.map_err(|_| Error(error))
74    }
75}
76
77impl<'data, R: ReadRef<'data>> WasmFile<'data, R> {
78    /// Parse the raw wasm data.
79    pub fn parse(data: R) -> Result<Self> {
80        let len = data.len().read_error("Unknown Wasm file size")?;
81        let data = data.read_bytes_at(0, len).read_error("Wasm read failed")?;
82        let parser = wp::Parser::new(0).parse_all(data);
83
84        let mut file = WasmFile {
85            data,
86            has_memory64: false,
87            sections: Vec::new(),
88            id_sections: Default::default(),
89            has_debug_symbols: false,
90            symbols: Vec::new(),
91            entry: 0,
92            marker: PhantomData,
93        };
94
95        let mut main_file_symbol = Some(WasmSymbolInternal {
96            name: "",
97            address: 0,
98            size: 0,
99            kind: SymbolKind::File,
100            section: SymbolSection::None,
101            scope: SymbolScope::Compilation,
102            weak: false,
103        });
104
105        let mut local_func_kinds = Vec::new();
106        let mut entry_func_id = None;
107        let mut code_range_start = 0;
108        let mut code_ranges = Vec::new();
109        let mut imports = None;
110        let mut exports = None;
111        let mut names = None;
112        let mut symbols = None;
113        // One-to-one mapping of globals to their value (if the global is a constant integer).
114        let mut global_values = Vec::new();
115
116        for payload in parser {
117            let payload = payload.read_error("Invalid Wasm section header")?;
118
119            match payload {
120                wp::Payload::Version { encoding, .. } => {
121                    if encoding != wp::Encoding::Module {
122                        return Err(Error("Unsupported Wasm encoding"));
123                    }
124                }
125                wp::Payload::TypeSection(section) => {
126                    file.add_section(SectionId::Type, section.range(), "");
127                }
128                wp::Payload::ImportSection(section) => {
129                    file.add_section(SectionId::Import, section.range(), "");
130                    imports = Some(section);
131                }
132                wp::Payload::FunctionSection(section) => {
133                    file.add_section(SectionId::Function, section.range(), "");
134                    local_func_kinds =
135                        vec![LocalFunctionKind::Unknown; section.into_iter().count()];
136                }
137                wp::Payload::TableSection(section) => {
138                    file.add_section(SectionId::Table, section.range(), "");
139                }
140                wp::Payload::MemorySection(section) => {
141                    file.add_section(SectionId::Memory, section.range(), "");
142                    for memory in section {
143                        let memory = memory.read_error("Couldn't read a memory item")?;
144                        file.has_memory64 |= memory.memory64;
145                    }
146                }
147                wp::Payload::GlobalSection(section) => {
148                    file.add_section(SectionId::Global, section.range(), "");
149                    for global in section {
150                        let global = global.read_error("Couldn't read a global item")?;
151                        let mut address = None;
152                        if !global.ty.mutable {
153                            // There should be exactly one instruction.
154                            let init = global.init_expr.get_operators_reader().read();
155                            address = match init.read_error("Couldn't read a global init expr")? {
156                                wp::Operator::I32Const { value } => Some(value as u64),
157                                wp::Operator::I64Const { value } => Some(value as u64),
158                                _ => None,
159                            };
160                        }
161                        global_values.push(address);
162                    }
163                }
164                wp::Payload::ExportSection(section) => {
165                    file.add_section(SectionId::Export, section.range(), "");
166                    exports = Some(section);
167                }
168                wp::Payload::StartSection { func, range, .. } => {
169                    file.add_section(SectionId::Start, range, "");
170                    entry_func_id = Some(func);
171                }
172                wp::Payload::ElementSection(section) => {
173                    file.add_section(SectionId::Element, section.range(), "");
174                }
175                wp::Payload::CodeSectionStart { range, .. } => {
176                    code_range_start = range.start;
177                    file.add_section(SectionId::Code, range, "");
178                }
179                wp::Payload::CodeSectionEntry(body) => {
180                    let range = body.range();
181                    let address = range.start as u64 - code_range_start as u64;
182                    let size = (range.end - range.start) as u64;
183                    code_ranges.push((address, size));
184                }
185                wp::Payload::DataSection(section) => {
186                    file.add_section(SectionId::Data, section.range(), "");
187                }
188                wp::Payload::DataCountSection { range, .. } => {
189                    file.add_section(SectionId::DataCount, range, "");
190                }
191                wp::Payload::TagSection(section) => {
192                    file.add_section(SectionId::Tag, section.range(), "");
193                }
194                wp::Payload::CustomSection(section) => {
195                    let name = section.name();
196                    let size = section.data().len();
197                    let mut range = section.range();
198                    range.start = range.end - size;
199                    file.add_section(SectionId::Custom, range, name);
200                    if name == "name" {
201                        let reader = wp::BinaryReader::new(section.data(), section.data_offset());
202                        names = Some(wp::NameSectionReader::new(reader));
203                    } else if name == "linking" {
204                        // https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md
205                        let reader = wp::BinaryReader::new(section.data(), section.data_offset());
206                        let linking = wp::LinkingSectionReader::new(reader)
207                            .read_error("Invalid Wasm linking section")?;
208                        for subsection in linking {
209                            let subsection =
210                                subsection.read_error("Invalid Wasm linking subsection")?;
211                            if let wp::Linking::SymbolTable(s) = subsection {
212                                symbols = Some(s);
213                            }
214                        }
215                    } else if name.starts_with(".debug_") {
216                        file.has_debug_symbols = true;
217                    }
218                }
219                _ => {}
220            }
221        }
222
223        if let Some(entry_func_id) = entry_func_id {
224            if let Some(range) = code_ranges.get(entry_func_id as usize) {
225                file.entry = range.0;
226            }
227        }
228
229        let mut import_func_names = Vec::new();
230        let mut import_global_names = Vec::new();
231        if let Some(imports) = imports {
232            let mut last_module_name = None;
233
234            for import in imports {
235                let import = import.read_error("Couldn't read an import item")?;
236                let kind = match import.ty {
237                    wp::TypeRef::Func(_) => {
238                        import_func_names.push(import.name);
239                        SymbolKind::Text
240                    }
241                    wp::TypeRef::Memory(memory) => {
242                        file.has_memory64 |= memory.memory64;
243                        SymbolKind::Data
244                    }
245                    wp::TypeRef::Global(_) => {
246                        import_global_names.push(import.name);
247                        SymbolKind::Data
248                    }
249                    wp::TypeRef::Table(_) => SymbolKind::Data,
250                    wp::TypeRef::Tag(_) => SymbolKind::Unknown,
251                };
252
253                if symbols.is_some() {
254                    // We have a symbol table, so we don't need to add symbols for imports.
255                    // TODO: never add symbols for imports. Return them via Object::imports instead.
256                    continue;
257                }
258
259                let module_name = import.module;
260                if last_module_name != Some(module_name) {
261                    file.symbols.push(WasmSymbolInternal {
262                        name: module_name,
263                        address: 0,
264                        size: 0,
265                        kind: SymbolKind::File,
266                        section: SymbolSection::None,
267                        scope: SymbolScope::Dynamic,
268                        weak: false,
269                    });
270                    last_module_name = Some(module_name);
271                }
272
273                file.symbols.push(WasmSymbolInternal {
274                    name: import.name,
275                    address: 0,
276                    size: 0,
277                    kind,
278                    section: SymbolSection::Undefined,
279                    scope: SymbolScope::Dynamic,
280                    weak: false,
281                });
282            }
283        }
284
285        if let Some(symbols) = symbols {
286            // We have a symbol table, so we don't need to add symbols for locals or exports.
287            // These sections shouldn't be present at the same time as a symbol table anyway.
288            // TODO: never add symbols for exports. Return them via Object::exports instead.
289            exports = None;
290            names = None;
291
292            for symbol in symbols {
293                let symbol = symbol.read_error("Invalid Wasm linking symbol")?;
294                let flags = match symbol {
295                    wp::SymbolInfo::Func { flags, .. } => flags,
296                    wp::SymbolInfo::Data { flags, .. } => flags,
297                    wp::SymbolInfo::Global { flags, .. } => flags,
298                    wp::SymbolInfo::Section { flags, .. } => flags,
299                    wp::SymbolInfo::Event { flags, .. } => flags,
300                    wp::SymbolInfo::Table { flags, .. } => flags,
301                };
302                let kind = if flags.contains(wp::SymbolFlags::TLS) {
303                    SymbolKind::Tls
304                } else {
305                    match symbol {
306                        wp::SymbolInfo::Func { .. } => SymbolKind::Text,
307                        wp::SymbolInfo::Data { .. } => SymbolKind::Data,
308                        wp::SymbolInfo::Global { .. } => SymbolKind::Data,
309                        wp::SymbolInfo::Section { .. } => SymbolKind::Section,
310                        wp::SymbolInfo::Event { .. } => SymbolKind::Unknown,
311                        wp::SymbolInfo::Table { .. } => SymbolKind::Data,
312                    }
313                };
314                let section = if flags.contains(wp::SymbolFlags::UNDEFINED) {
315                    SymbolSection::Undefined
316                } else if flags.contains(wp::SymbolFlags::ABSOLUTE) {
317                    SymbolSection::Absolute
318                } else {
319                    match symbol {
320                        wp::SymbolInfo::Func { .. } => {
321                            SymbolSection::Section(SectionIndex(SectionId::Code as usize))
322                        }
323                        _ => {
324                            // TODO: anything that is defined should have a known section.
325                            // Additionally, address and size should be within this section.
326                            SymbolSection::Unknown
327                        }
328                    }
329                };
330                let scope = if flags.contains(wp::SymbolFlags::BINDING_LOCAL) {
331                    SymbolScope::Compilation
332                } else if flags.contains(wp::SymbolFlags::VISIBILITY_HIDDEN) {
333                    SymbolScope::Linkage
334                } else {
335                    SymbolScope::Dynamic
336                };
337                let weak = flags.contains(wp::SymbolFlags::BINDING_WEAK);
338
339                let mut address = 0;
340                let mut size = 0;
341                let name = match symbol {
342                    wp::SymbolInfo::Func {
343                        index, mut name, ..
344                    } => {
345                        if let Some(local_index) = index.checked_sub(import_func_names.len() as u32)
346                        {
347                            if let Some(range) = code_ranges.get(local_index as usize).copied() {
348                                address = range.0;
349                                size = range.1;
350                            }
351                        } else {
352                            if !flags.contains(wp::SymbolFlags::EXPLICIT_NAME) {
353                                name = Some(import_func_names[index as usize]);
354                            }
355                        }
356                        name
357                    }
358                    wp::SymbolInfo::Data { name, symbol, .. } => {
359                        if let Some(symbol) = symbol {
360                            // TODO: this is an offset within a data segment.
361                            // This may need to be changed to be an offset within the data section.
362                            address = symbol.offset.into();
363                            size = symbol.size.into();
364                        }
365                        Some(name)
366                    }
367                    wp::SymbolInfo::Section { .. } => {
368                        // TODO: find the section name
369                        None
370                    }
371                    wp::SymbolInfo::Global { name, index, .. } => {
372                        if !flags.contains(wp::SymbolFlags::EXPLICIT_NAME) {
373                            import_global_names.get(index as usize).copied()
374                        } else {
375                            name
376                        }
377                    }
378                    wp::SymbolInfo::Event { name, .. } | wp::SymbolInfo::Table { name, .. } => name,
379                };
380
381                file.symbols.push(WasmSymbolInternal {
382                    name: name.unwrap_or(""),
383                    address,
384                    size,
385                    kind,
386                    section,
387                    scope,
388                    weak,
389                });
390            }
391        }
392
393        if let Some(exports) = exports {
394            if let Some(main_file_symbol) = main_file_symbol.take() {
395                file.symbols.push(main_file_symbol);
396            }
397
398            for export in exports {
399                let export = export.read_error("Couldn't read an export item")?;
400
401                let (kind, section_idx) = match export.kind {
402                    wp::ExternalKind::Func => {
403                        if let Some(local_func_id) =
404                            export.index.checked_sub(import_func_names.len() as u32)
405                        {
406                            let local_func_kind = local_func_kinds
407                                .get_mut(local_func_id as usize)
408                                .read_error("Invalid Wasm export index")?;
409                            *local_func_kind = LocalFunctionKind::Exported;
410                        }
411                        (SymbolKind::Text, SectionId::Code)
412                    }
413                    wp::ExternalKind::Table
414                    | wp::ExternalKind::Memory
415                    | wp::ExternalKind::Global => (SymbolKind::Data, SectionId::Data),
416                    // TODO
417                    wp::ExternalKind::Tag => continue,
418                };
419
420                // Try to guess the symbol address. Rust and C export a global containing
421                // the address in linear memory of the symbol.
422                let mut address = 0;
423                let mut size = 0;
424                if export.kind == wp::ExternalKind::Global {
425                    if let Some(&Some(x)) = global_values.get(export.index as usize) {
426                        address = x;
427                    }
428                }
429                if export.kind == wp::ExternalKind::Func {
430                    if let Some(local_func_id) =
431                        export.index.checked_sub(import_func_names.len() as u32)
432                    {
433                        if let Some(range) = code_ranges.get(local_func_id as usize) {
434                            address = range.0;
435                            size = range.1
436                        }
437                    }
438                }
439
440                file.symbols.push(WasmSymbolInternal {
441                    name: export.name,
442                    address,
443                    size,
444                    kind,
445                    section: SymbolSection::Section(SectionIndex(section_idx as usize)),
446                    scope: SymbolScope::Dynamic,
447                    weak: false,
448                });
449            }
450        }
451        if let Some(names) = names {
452            if let Some(main_file_symbol) = main_file_symbol.take() {
453                file.symbols.push(main_file_symbol);
454            }
455            for name in names {
456                let name = name.read_error("Invalid wasm name section")?;
457                let wp::Name::Function(name_map) = name else {
458                    continue;
459                };
460                for naming in name_map {
461                    let naming = naming.read_error("Couldn't read a function name")?;
462                    let Some(local_index) =
463                        naming.index.checked_sub(import_func_names.len() as u32)
464                    else {
465                        continue;
466                    };
467                    let Some(LocalFunctionKind::Unknown) =
468                        local_func_kinds.get(local_index as usize)
469                    else {
470                        continue;
471                    };
472                    let Some((address, size)) = code_ranges.get(local_index as usize).copied()
473                    else {
474                        continue;
475                    };
476                    file.symbols.push(WasmSymbolInternal {
477                        name: naming.name,
478                        address,
479                        size,
480                        kind: SymbolKind::Text,
481                        section: SymbolSection::Section(SectionIndex(SectionId::Code as usize)),
482                        scope: SymbolScope::Compilation,
483                        weak: false,
484                    });
485                }
486            }
487        }
488
489        Ok(file)
490    }
491
492    fn add_section(&mut self, id: SectionId, range: Range<usize>, name: &'data str) {
493        let section = SectionHeader { id, range, name };
494        self.id_sections[id as usize] = Some(self.sections.len());
495        self.sections.push(section);
496    }
497}
498
499impl<'data, R> read::private::Sealed for WasmFile<'data, R> {}
500
501impl<'data, R: ReadRef<'data>> Object<'data> for WasmFile<'data, R> {
502    type Segment<'file>
503        = WasmSegment<'data, 'file, R>
504    where
505        Self: 'file,
506        'data: 'file;
507    type SegmentIterator<'file>
508        = WasmSegmentIterator<'data, 'file, R>
509    where
510        Self: 'file,
511        'data: 'file;
512    type Section<'file>
513        = WasmSection<'data, 'file, R>
514    where
515        Self: 'file,
516        'data: 'file;
517    type SectionIterator<'file>
518        = WasmSectionIterator<'data, 'file, R>
519    where
520        Self: 'file,
521        'data: 'file;
522    type Comdat<'file>
523        = WasmComdat<'data, 'file, R>
524    where
525        Self: 'file,
526        'data: 'file;
527    type ComdatIterator<'file>
528        = WasmComdatIterator<'data, 'file, R>
529    where
530        Self: 'file,
531        'data: 'file;
532    type Symbol<'file>
533        = WasmSymbol<'data, 'file>
534    where
535        Self: 'file,
536        'data: 'file;
537    type SymbolIterator<'file>
538        = WasmSymbolIterator<'data, 'file>
539    where
540        Self: 'file,
541        'data: 'file;
542    type SymbolTable<'file>
543        = WasmSymbolTable<'data, 'file>
544    where
545        Self: 'file,
546        'data: 'file;
547    type DynamicRelocationIterator<'file>
548        = NoDynamicRelocationIterator
549    where
550        Self: 'file,
551        'data: 'file;
552
553    #[inline]
554    fn architecture(&self) -> Architecture {
555        if self.has_memory64 {
556            Architecture::Wasm64
557        } else {
558            Architecture::Wasm32
559        }
560    }
561
562    #[inline]
563    fn is_little_endian(&self) -> bool {
564        true
565    }
566
567    #[inline]
568    fn is_64(&self) -> bool {
569        self.has_memory64
570    }
571
572    fn kind(&self) -> ObjectKind {
573        // TODO: check for `linking` custom section
574        ObjectKind::Unknown
575    }
576
577    fn segments(&self) -> Self::SegmentIterator<'_> {
578        WasmSegmentIterator { file: self }
579    }
580
581    fn section_by_name_bytes<'file>(
582        &'file self,
583        section_name: &[u8],
584    ) -> Option<WasmSection<'data, 'file, R>> {
585        self.sections()
586            .find(|section| section.name_bytes() == Ok(section_name))
587    }
588
589    fn section_by_index(&self, index: SectionIndex) -> Result<WasmSection<'data, '_, R>> {
590        // TODO: Missing sections should return an empty section.
591        let id_section = self
592            .id_sections
593            .get(index.0)
594            .and_then(|x| *x)
595            .read_error("Invalid Wasm section index")?;
596        let section = self.sections.get(id_section).unwrap();
597        Ok(WasmSection {
598            file: self,
599            section,
600        })
601    }
602
603    fn sections(&self) -> Self::SectionIterator<'_> {
604        WasmSectionIterator {
605            file: self,
606            sections: self.sections.iter(),
607        }
608    }
609
610    fn comdats(&self) -> Self::ComdatIterator<'_> {
611        WasmComdatIterator { file: self }
612    }
613
614    #[inline]
615    fn symbol_by_index(&self, index: SymbolIndex) -> Result<WasmSymbol<'data, '_>> {
616        let symbol = self
617            .symbols
618            .get(index.0)
619            .read_error("Invalid Wasm symbol index")?;
620        Ok(WasmSymbol { index, symbol })
621    }
622
623    fn symbols(&self) -> Self::SymbolIterator<'_> {
624        WasmSymbolIterator {
625            symbols: self.symbols.iter().enumerate(),
626        }
627    }
628
629    fn symbol_table(&self) -> Option<WasmSymbolTable<'data, '_>> {
630        Some(WasmSymbolTable {
631            symbols: &self.symbols,
632        })
633    }
634
635    fn dynamic_symbols(&self) -> Self::SymbolIterator<'_> {
636        WasmSymbolIterator {
637            symbols: [].iter().enumerate(),
638        }
639    }
640
641    #[inline]
642    fn dynamic_symbol_table(&self) -> Option<WasmSymbolTable<'data, '_>> {
643        None
644    }
645
646    #[inline]
647    fn dynamic_relocations(&self) -> Option<NoDynamicRelocationIterator> {
648        None
649    }
650
651    fn imports(&self) -> Result<Vec<Import<'data>>> {
652        // TODO: return entries in the import section
653        Ok(Vec::new())
654    }
655
656    fn exports(&self) -> Result<Vec<Export<'data>>> {
657        // TODO: return entries in the export section
658        Ok(Vec::new())
659    }
660
661    fn has_debug_symbols(&self) -> bool {
662        self.has_debug_symbols
663    }
664
665    fn relative_address_base(&self) -> u64 {
666        0
667    }
668
669    #[inline]
670    fn entry(&self) -> u64 {
671        self.entry
672    }
673
674    #[inline]
675    fn flags(&self) -> FileFlags {
676        FileFlags::None
677    }
678}
679
680/// An iterator for the segments in a [`WasmFile`].
681///
682/// This is a stub that doesn't implement any functionality.
683#[derive(Debug)]
684pub struct WasmSegmentIterator<'data, 'file, R = &'data [u8]> {
685    #[allow(unused)]
686    file: &'file WasmFile<'data, R>,
687}
688
689impl<'data, 'file, R> Iterator for WasmSegmentIterator<'data, 'file, R> {
690    type Item = WasmSegment<'data, 'file, R>;
691
692    #[inline]
693    fn next(&mut self) -> Option<Self::Item> {
694        None
695    }
696}
697
698/// A segment in a [`WasmFile`].
699///
700/// This is a stub that doesn't implement any functionality.
701#[derive(Debug)]
702pub struct WasmSegment<'data, 'file, R = &'data [u8]> {
703    #[allow(unused)]
704    file: &'file WasmFile<'data, R>,
705}
706
707impl<'data, 'file, R> read::private::Sealed for WasmSegment<'data, 'file, R> {}
708
709impl<'data, 'file, R> ObjectSegment<'data> for WasmSegment<'data, 'file, R> {
710    #[inline]
711    fn address(&self) -> u64 {
712        unreachable!()
713    }
714
715    #[inline]
716    fn size(&self) -> u64 {
717        unreachable!()
718    }
719
720    #[inline]
721    fn align(&self) -> u64 {
722        unreachable!()
723    }
724
725    #[inline]
726    fn file_range(&self) -> (u64, u64) {
727        unreachable!()
728    }
729
730    fn data(&self) -> Result<&'data [u8]> {
731        unreachable!()
732    }
733
734    fn data_range(&self, _address: u64, _size: u64) -> Result<Option<&'data [u8]>> {
735        unreachable!()
736    }
737
738    #[inline]
739    fn name_bytes(&self) -> Result<Option<&[u8]>> {
740        unreachable!()
741    }
742
743    #[inline]
744    fn name(&self) -> Result<Option<&str>> {
745        unreachable!()
746    }
747
748    #[inline]
749    fn flags(&self) -> SegmentFlags {
750        unreachable!()
751    }
752}
753
754/// An iterator for the sections in a [`WasmFile`].
755#[derive(Debug)]
756pub struct WasmSectionIterator<'data, 'file, R = &'data [u8]> {
757    file: &'file WasmFile<'data, R>,
758    sections: slice::Iter<'file, SectionHeader<'data>>,
759}
760
761impl<'data, 'file, R> Iterator for WasmSectionIterator<'data, 'file, R> {
762    type Item = WasmSection<'data, 'file, R>;
763
764    fn next(&mut self) -> Option<Self::Item> {
765        let section = self.sections.next()?;
766        Some(WasmSection {
767            file: self.file,
768            section,
769        })
770    }
771}
772
773/// A section in a [`WasmFile`].
774///
775/// Most functionality is provided by the [`ObjectSection`] trait implementation.
776#[derive(Debug)]
777pub struct WasmSection<'data, 'file, R = &'data [u8]> {
778    file: &'file WasmFile<'data, R>,
779    section: &'file SectionHeader<'data>,
780}
781
782impl<'data, 'file, R> read::private::Sealed for WasmSection<'data, 'file, R> {}
783
784impl<'data, 'file, R: ReadRef<'data>> ObjectSection<'data> for WasmSection<'data, 'file, R> {
785    type RelocationIterator = WasmRelocationIterator<'data, 'file, R>;
786
787    #[inline]
788    fn index(&self) -> SectionIndex {
789        // Note that we treat all custom sections as index 0.
790        // This is ok because they are never looked up by index.
791        SectionIndex(self.section.id as usize)
792    }
793
794    #[inline]
795    fn address(&self) -> u64 {
796        0
797    }
798
799    #[inline]
800    fn size(&self) -> u64 {
801        let range = &self.section.range;
802        (range.end - range.start) as u64
803    }
804
805    #[inline]
806    fn align(&self) -> u64 {
807        1
808    }
809
810    #[inline]
811    fn file_range(&self) -> Option<(u64, u64)> {
812        let range = &self.section.range;
813        Some((range.start as _, range.end as _))
814    }
815
816    #[inline]
817    fn data(&self) -> Result<&'data [u8]> {
818        let range = &self.section.range;
819        self.file
820            .data
821            .read_bytes_at(range.start as u64, range.end as u64 - range.start as u64)
822            .read_error("Invalid Wasm section size or offset")
823    }
824
825    fn data_range(&self, _address: u64, _size: u64) -> Result<Option<&'data [u8]>> {
826        unimplemented!()
827    }
828
829    #[inline]
830    fn compressed_file_range(&self) -> Result<CompressedFileRange> {
831        Ok(CompressedFileRange::none(self.file_range()))
832    }
833
834    #[inline]
835    fn compressed_data(&self) -> Result<CompressedData<'data>> {
836        self.data().map(CompressedData::none)
837    }
838
839    #[inline]
840    fn name_bytes(&self) -> Result<&'data [u8]> {
841        self.name().map(str::as_bytes)
842    }
843
844    #[inline]
845    fn name(&self) -> Result<&'data str> {
846        Ok(match self.section.id {
847            SectionId::Custom => self.section.name,
848            SectionId::Type => "<type>",
849            SectionId::Import => "<import>",
850            SectionId::Function => "<function>",
851            SectionId::Table => "<table>",
852            SectionId::Memory => "<memory>",
853            SectionId::Global => "<global>",
854            SectionId::Export => "<export>",
855            SectionId::Start => "<start>",
856            SectionId::Element => "<element>",
857            SectionId::Code => "<code>",
858            SectionId::Data => "<data>",
859            SectionId::DataCount => "<data_count>",
860            SectionId::Tag => "<tag>",
861        })
862    }
863
864    #[inline]
865    fn segment_name_bytes(&self) -> Result<Option<&[u8]>> {
866        Ok(None)
867    }
868
869    #[inline]
870    fn segment_name(&self) -> Result<Option<&str>> {
871        Ok(None)
872    }
873
874    #[inline]
875    fn kind(&self) -> SectionKind {
876        match self.section.id {
877            SectionId::Custom => match self.section.name {
878                "reloc." | "linking" => SectionKind::Linker,
879                _ => SectionKind::Other,
880            },
881            SectionId::Type => SectionKind::Metadata,
882            SectionId::Import => SectionKind::Linker,
883            SectionId::Function => SectionKind::Metadata,
884            SectionId::Table => SectionKind::UninitializedData,
885            SectionId::Memory => SectionKind::UninitializedData,
886            SectionId::Global => SectionKind::Data,
887            SectionId::Export => SectionKind::Linker,
888            SectionId::Start => SectionKind::Linker,
889            SectionId::Element => SectionKind::Data,
890            SectionId::Code => SectionKind::Text,
891            SectionId::Data => SectionKind::Data,
892            SectionId::DataCount => SectionKind::UninitializedData,
893            SectionId::Tag => SectionKind::Data,
894        }
895    }
896
897    #[inline]
898    fn relocations(&self) -> WasmRelocationIterator<'data, 'file, R> {
899        WasmRelocationIterator(PhantomData)
900    }
901
902    fn relocation_map(&self) -> read::Result<RelocationMap> {
903        RelocationMap::new(self.file, self)
904    }
905
906    #[inline]
907    fn flags(&self) -> SectionFlags {
908        SectionFlags::None
909    }
910}
911
912/// An iterator for the COMDAT section groups in a [`WasmFile`].
913///
914/// This is a stub that doesn't implement any functionality.
915#[derive(Debug)]
916pub struct WasmComdatIterator<'data, 'file, R = &'data [u8]> {
917    #[allow(unused)]
918    file: &'file WasmFile<'data, R>,
919}
920
921impl<'data, 'file, R> Iterator for WasmComdatIterator<'data, 'file, R> {
922    type Item = WasmComdat<'data, 'file, R>;
923
924    #[inline]
925    fn next(&mut self) -> Option<Self::Item> {
926        None
927    }
928}
929
930/// A COMDAT section group in a [`WasmFile`].
931///
932/// This is a stub that doesn't implement any functionality.
933#[derive(Debug)]
934pub struct WasmComdat<'data, 'file, R = &'data [u8]> {
935    #[allow(unused)]
936    file: &'file WasmFile<'data, R>,
937}
938
939impl<'data, 'file, R> read::private::Sealed for WasmComdat<'data, 'file, R> {}
940
941impl<'data, 'file, R> ObjectComdat<'data> for WasmComdat<'data, 'file, R> {
942    type SectionIterator = WasmComdatSectionIterator<'data, 'file, R>;
943
944    #[inline]
945    fn kind(&self) -> ComdatKind {
946        unreachable!();
947    }
948
949    #[inline]
950    fn symbol(&self) -> SymbolIndex {
951        unreachable!();
952    }
953
954    #[inline]
955    fn name_bytes(&self) -> Result<&'data [u8]> {
956        unreachable!();
957    }
958
959    #[inline]
960    fn name(&self) -> Result<&'data str> {
961        unreachable!();
962    }
963
964    #[inline]
965    fn sections(&self) -> Self::SectionIterator {
966        unreachable!();
967    }
968}
969
970/// An iterator for the sections in a COMDAT section group in a [`WasmFile`].
971///
972/// This is a stub that doesn't implement any functionality.
973#[derive(Debug)]
974pub struct WasmComdatSectionIterator<'data, 'file, R = &'data [u8]> {
975    #[allow(unused)]
976    file: &'file WasmFile<'data, R>,
977}
978
979impl<'data, 'file, R> Iterator for WasmComdatSectionIterator<'data, 'file, R> {
980    type Item = SectionIndex;
981
982    fn next(&mut self) -> Option<Self::Item> {
983        None
984    }
985}
986
987/// A symbol table in a [`WasmFile`].
988#[derive(Debug)]
989pub struct WasmSymbolTable<'data, 'file> {
990    symbols: &'file [WasmSymbolInternal<'data>],
991}
992
993impl<'data, 'file> read::private::Sealed for WasmSymbolTable<'data, 'file> {}
994
995impl<'data, 'file> ObjectSymbolTable<'data> for WasmSymbolTable<'data, 'file> {
996    type Symbol = WasmSymbol<'data, 'file>;
997    type SymbolIterator = WasmSymbolIterator<'data, 'file>;
998
999    fn symbols(&self) -> Self::SymbolIterator {
1000        WasmSymbolIterator {
1001            symbols: self.symbols.iter().enumerate(),
1002        }
1003    }
1004
1005    fn symbol_by_index(&self, index: SymbolIndex) -> Result<Self::Symbol> {
1006        let symbol = self
1007            .symbols
1008            .get(index.0)
1009            .read_error("Invalid Wasm symbol index")?;
1010        Ok(WasmSymbol { index, symbol })
1011    }
1012}
1013
1014/// An iterator for the symbols in a [`WasmFile`].
1015#[derive(Debug)]
1016pub struct WasmSymbolIterator<'data, 'file> {
1017    symbols: core::iter::Enumerate<slice::Iter<'file, WasmSymbolInternal<'data>>>,
1018}
1019
1020impl<'data, 'file> Iterator for WasmSymbolIterator<'data, 'file> {
1021    type Item = WasmSymbol<'data, 'file>;
1022
1023    fn next(&mut self) -> Option<Self::Item> {
1024        let (index, symbol) = self.symbols.next()?;
1025        Some(WasmSymbol {
1026            index: SymbolIndex(index),
1027            symbol,
1028        })
1029    }
1030}
1031
1032/// A symbol in a [`WasmFile`].
1033///
1034/// Most functionality is provided by the [`ObjectSymbol`] trait implementation.
1035#[derive(Clone, Copy, Debug)]
1036pub struct WasmSymbol<'data, 'file> {
1037    index: SymbolIndex,
1038    symbol: &'file WasmSymbolInternal<'data>,
1039}
1040
1041#[derive(Clone, Debug)]
1042struct WasmSymbolInternal<'data> {
1043    name: &'data str,
1044    address: u64,
1045    size: u64,
1046    kind: SymbolKind,
1047    section: SymbolSection,
1048    scope: SymbolScope,
1049    weak: bool,
1050}
1051
1052impl<'data, 'file> read::private::Sealed for WasmSymbol<'data, 'file> {}
1053
1054impl<'data, 'file> ObjectSymbol<'data> for WasmSymbol<'data, 'file> {
1055    #[inline]
1056    fn index(&self) -> SymbolIndex {
1057        self.index
1058    }
1059
1060    #[inline]
1061    fn name_bytes(&self) -> read::Result<&'data [u8]> {
1062        Ok(self.symbol.name.as_bytes())
1063    }
1064
1065    #[inline]
1066    fn name(&self) -> read::Result<&'data str> {
1067        Ok(self.symbol.name)
1068    }
1069
1070    #[inline]
1071    fn address(&self) -> u64 {
1072        self.symbol.address
1073    }
1074
1075    #[inline]
1076    fn size(&self) -> u64 {
1077        self.symbol.size
1078    }
1079
1080    #[inline]
1081    fn kind(&self) -> SymbolKind {
1082        self.symbol.kind
1083    }
1084
1085    #[inline]
1086    fn section(&self) -> SymbolSection {
1087        self.symbol.section
1088    }
1089
1090    #[inline]
1091    fn is_undefined(&self) -> bool {
1092        self.symbol.section == SymbolSection::Undefined
1093    }
1094
1095    #[inline]
1096    fn is_definition(&self) -> bool {
1097        (self.symbol.kind == SymbolKind::Text || self.symbol.kind == SymbolKind::Data)
1098            && self.symbol.section != SymbolSection::Undefined
1099    }
1100
1101    #[inline]
1102    fn is_common(&self) -> bool {
1103        self.symbol.section == SymbolSection::Common
1104    }
1105
1106    #[inline]
1107    fn is_weak(&self) -> bool {
1108        self.symbol.weak
1109    }
1110
1111    #[inline]
1112    fn scope(&self) -> SymbolScope {
1113        self.symbol.scope
1114    }
1115
1116    #[inline]
1117    fn is_global(&self) -> bool {
1118        self.symbol.scope != SymbolScope::Compilation
1119    }
1120
1121    #[inline]
1122    fn is_local(&self) -> bool {
1123        self.symbol.scope == SymbolScope::Compilation
1124    }
1125
1126    #[inline]
1127    fn flags(&self) -> SymbolFlags<SectionIndex, SymbolIndex> {
1128        SymbolFlags::None
1129    }
1130}
1131
1132/// An iterator for the relocations for a [`WasmSection`].
1133///
1134/// This is a stub that doesn't implement any functionality.
1135#[derive(Debug)]
1136pub struct WasmRelocationIterator<'data, 'file, R = &'data [u8]>(
1137    PhantomData<(&'data (), &'file (), R)>,
1138);
1139
1140impl<'data, 'file, R> Iterator for WasmRelocationIterator<'data, 'file, R> {
1141    type Item = (u64, Relocation);
1142
1143    #[inline]
1144    fn next(&mut self) -> Option<Self::Item> {
1145        None
1146    }
1147}