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