Skip to main content

object/read/
wasm.rs

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