Skip to main content

wamex_cli/read/
mod.rs

1use anyhow::{Result, anyhow, bail};
2use vec_map::VecMap;
3use wasm_encoder::CustomSection;
4use wasmparser::{BinaryReader, Payload};
5pub use wasmparser::{Element, Export, FuncType, Global, Import, MemoryType, Table, TagType};
6
7use crate::{
8    index::{DefinedFuncId, FuncTypeId, IdVec, IndexedSection},
9    read::target_features::TargetFeatures,
10};
11
12pub mod code;
13pub mod data;
14pub mod linking;
15pub mod names;
16pub mod relocs;
17mod target_features;
18
19use code::CodeSection;
20use data::DataSection;
21use linking::LinkingInfo;
22use names::Names;
23use relocs::Relocation;
24
25type Ind<T> = IndexedSection<T>;
26
27/// Lossless representation of wasm module, without preprocessing
28/// That can pass round-trip test without any loss.
29/// After round-trip section will have canonical order.
30#[derive(Default)]
31pub struct InputModule<'a> {
32    // parsed sections
33    pub types: IdVec<FuncType>,
34    pub imports: IdVec<Import<'a>>,
35    pub exports: IdVec<Export<'a>>,
36    pub tables: IdVec<Table<'a>>,
37    // elements is just a table initialisation
38    pub elements: IdVec<Element<'a>>,
39    // tags are used for exceptions
40    pub tags: IdVec<TagType>,
41    pub globals: IdVec<Global<'a>>,
42    // Should be only one memory ?
43    pub memories: IdVec<MemoryType>,
44    // code and data is only interested section for relocation application
45    pub code: Ind<CodeSection<'a>>,
46    pub data: Ind<DataSection<'a>>,
47
48    // Custom sections
49    // section "name"
50    pub names: Names<'a>,
51    // section "linking" (only partial)
52    pub linking: LinkingInfo<'a>,
53    // sections "reloc.*"
54    pub relocs: Relocation,
55    // Activated features
56    pub target_features: TargetFeatures,
57    // other sections
58    pub custom_sections: VecMap<Ind<CustomSection<'a>>>,
59}
60
61impl<'a> InputModule<'a> {
62    pub fn parse(wasm: &'a [u8]) -> anyhow::Result<Self> {
63        let mut module = Self {
64            ..Default::default()
65        };
66
67        let mut section_index = 0;
68        let mut end = None;
69
70        let mut function_types: Vec<FuncTypeId> = Vec::new();
71        let mut code_start = None;
72        let mut code_reader_header = None;
73        let mut funcs = Vec::new();
74
75        let mut data_count = None;
76
77        let parser = wasmparser::Parser::new(0);
78        let mut parser = parser.parse_all(wasm);
79        for payload in &mut parser {
80            match payload? {
81                Payload::TypeSection(reader) => {
82                    module.types = reader
83                        .into_iter_err_on_gc_types()
84                        .collect::<Result<IdVec<_>, _>>()?;
85                }
86                Payload::ImportSection(reader) => {
87                    module.imports = reader.into_iter().collect::<Result<IdVec<_>, _>>()?;
88                }
89                Payload::TableSection(reader) => {
90                    module.tables = reader.into_iter().collect::<Result<IdVec<_>, _>>()?;
91                }
92                Payload::MemorySection(reader) => {
93                    module.memories = reader.into_iter().collect::<Result<IdVec<_>, _>>()?;
94                }
95                Payload::TagSection(reader) => {
96                    module.tags = reader.into_iter().collect::<Result<IdVec<_>, _>>()?;
97                }
98                Payload::GlobalSection(reader) => {
99                    module.globals = reader.into_iter().collect::<Result<IdVec<_>, _>>()?;
100                }
101                Payload::ElementSection(reader) => {
102                    module.elements = reader.into_iter().collect::<Result<IdVec<_>, _>>()?;
103                }
104                Payload::FunctionSection(reader) => {
105                    function_types = reader
106                        .into_iter()
107                        .map(|t| t.map(crate::index::Id::from_index))
108                        .collect::<Result<Vec<_>, _>>()?;
109                }
110                Payload::ExportSection(reader) => {
111                    module.exports = reader.into_iter().collect::<Result<IdVec<_>, _>>()?;
112                }
113                Payload::StartSection { func, .. } => {
114                    code_start = Some(crate::index::Id::from_index(func));
115                }
116                Payload::DataCountSection { count, .. } => {
117                    data_count = Some(count as usize);
118                }
119                Payload::DataSection(reader) => {
120                    let starting_offset = reader.range().start;
121
122                    let data = DataSection {
123                        data_segments: reader.into_iter().collect::<Result<IdVec<_>, _>>()?,
124                    };
125                    module.data = Ind {
126                        section_payload: data,
127                        section_index,
128                        starting_offset,
129                    };
130                }
131                // process after loop
132                Payload::CodeSectionStart { range, count, .. } => {
133                    code_reader_header = Some((range.start, section_index, count));
134                }
135                Payload::CustomSection(reader) => {
136                    let name = reader.name();
137                    if name == "name" {
138                        let name_reader = wasmparser::NameSectionReader::new(BinaryReader::new(
139                            reader.data(),
140                            reader.data_offset(),
141                        ));
142                        module.names = Names::read(name_reader)?;
143                    } else if name == "linking" {
144                        let linking_reader = wasmparser::LinkingSectionReader::new(
145                            BinaryReader::new(reader.data(), reader.data_offset()),
146                        )?;
147                        module.linking = LinkingInfo::read(linking_reader)?;
148                    } else if name.starts_with("reloc.") {
149                        let reloc_reader = wasmparser::RelocSectionReader::new(BinaryReader::new(
150                            reader.data(),
151                            reader.data_offset(),
152                        ))?;
153                        module.relocs.push_section(reloc_reader)?;
154                    } else if name == "target_features" {
155                        module.target_features = TargetFeatures::read(BinaryReader::new(
156                            reader.data(),
157                            reader.data_offset(),
158                        ))?;
159                    } else {
160                        let custom_section = CustomSection {
161                            name: reader.name().into(),
162                            data: reader.data().into(),
163                        };
164                        module.custom_sections.insert(
165                            section_index,
166                            Ind {
167                                section_payload: custom_section,
168                                section_index,
169                                starting_offset: reader.range().start,
170                            },
171                        );
172                    }
173                }
174                // process after loop
175                Payload::CodeSectionEntry(body) => {
176                    funcs.push(body);
177                    // (not a full section)
178                    continue;
179                }
180                Payload::Version { .. } => continue,
181                Payload::End(offset) => {
182                    end = Some(offset);
183                    break;
184                }
185                section => {
186                    bail!("Unknown section: {:?}", section);
187                }
188            }
189
190            section_index += 1;
191        }
192        let _end = end.ok_or_else(|| anyhow!("No end section"))?;
193        if parser.next().is_some() {
194            bail!("Unexpected trailing data");
195        }
196        if let Some(data_count) = data_count {
197            if data_count != module.data.section_payload.data_segments.len() {
198                bail!(
199                    "Data count mismatch: {} != {}",
200                    data_count,
201                    module.data.section_payload.data_segments.len()
202                );
203            }
204        }
205
206        // merge fields into code section
207        module.code = CodeSection::new(code_start, funcs, function_types, code_reader_header)?;
208
209        Ok(module)
210    }
211    pub fn defined_func_type_id(&self, id: DefinedFuncId) -> FuncTypeId {
212        self.code.section_payload.defined_funcs[id].type_id
213    }
214}
215
216trait CustomSectionReader<'a> {
217    type Reader;
218
219    fn read(reader: Self::Reader) -> Result<Self>
220    where
221        Self: Sized;
222}