xref: /wasmtime-44.0.1/crates/wizer/src/parse.rs (revision 4a453ef7)
1 use crate::info::{
2     types_interner::{EntityType, TypeId},
3     Module, ModuleContext,
4 };
5 use crate::stack_ext::StackExt;
6 use anyhow::{Context, Result};
7 use std::convert::TryFrom;
8 use wasm_encoder::SectionId;
9 
10 struct StackEntry {
11     parser: wasmparser::Parser,
12     module: Module,
13 }
14 
15 /// Parse the given Wasm bytes into a `ModuleInfo` tree.
16 pub(crate) fn parse<'a>(full_wasm: &'a [u8]) -> anyhow::Result<ModuleContext<'a>> {
17     log::debug!("Parsing the input Wasm");
18 
19     let mut cx = ModuleContext::new();
20 
21     // The wasm we are currently parsing. This is advanced as the parser
22     // consumes input.
23     let mut wasm = full_wasm;
24 
25     let mut stack = vec![StackEntry {
26         parser: wasmparser::Parser::new(0),
27         module: cx.root(),
28     }];
29 
30     loop {
31         let (payload, consumed) = match stack
32             .top_mut()
33             .parser
34             .parse(wasm, true)
35             .context("failed to parse Wasm")?
36         {
37             wasmparser::Chunk::NeedMoreData(_) => unreachable!(),
38             wasmparser::Chunk::Parsed { payload, consumed } => (payload, consumed),
39         };
40         wasm = &wasm[consumed..];
41 
42         use wasmparser::Payload::*;
43         match payload {
44             Version { .. } => {}
45             TypeSection(types) => type_section(&mut cx, &mut stack, full_wasm, types)?,
46             ImportSection(imports) => import_section(&mut cx, &mut stack, full_wasm, imports)?,
47             FunctionSection(funcs) => function_section(&mut cx, &mut stack, full_wasm, funcs)?,
48             TableSection(tables) => table_section(&mut cx, &mut stack, full_wasm, tables)?,
49             MemorySection(mems) => memory_section(&mut cx, &mut stack, full_wasm, mems)?,
50             GlobalSection(globals) => global_section(&mut cx, &mut stack, full_wasm, globals)?,
51             ExportSection(exports) => export_section(&mut cx, &mut stack, full_wasm, exports)?,
52             StartSection { func: _, range } => {
53                 stack
54                     .top_mut()
55                     .module
56                     .add_raw_section(&mut cx, SectionId::Start, range, full_wasm)
57             }
58             ElementSection(elems) => stack.top_mut().module.add_raw_section(
59                 &mut cx,
60                 SectionId::Element,
61                 elems.range(),
62                 full_wasm,
63             ),
64             DataCountSection { range, .. } => stack.top_mut().module.add_raw_section(
65                 &mut cx,
66                 SectionId::DataCount,
67                 range,
68                 full_wasm,
69             ),
70             DataSection(data) => stack.top_mut().module.add_raw_section(
71                 &mut cx,
72                 SectionId::Data,
73                 data.range(),
74                 full_wasm,
75             ),
76             CustomSection(c) => stack.top_mut().module.add_raw_section(
77                 &mut cx,
78                 SectionId::Custom,
79                 c.range(),
80                 full_wasm,
81             ),
82             CodeSectionStart {
83                 range,
84                 count: _,
85                 size,
86             } => {
87                 wasm = &wasm[usize::try_from(size).unwrap()..];
88                 let entry = stack.top_mut();
89                 entry.parser.skip_section();
90                 entry
91                     .module
92                     .add_raw_section(&mut cx, SectionId::Code, range, full_wasm)
93             }
94             CodeSectionEntry(_) => unreachable!(),
95             UnknownSection { .. } => anyhow::bail!("unknown section"),
96             TagSection(_) => anyhow::bail!("exceptions are not supported yet"),
97             End(_) => {
98                 let entry = stack.pop().unwrap();
99 
100                 // If we finished parsing the root Wasm module, then we're done.
101                 assert!(entry.module.is_root());
102                 assert!(stack.is_empty());
103                 return Ok(cx);
104             }
105 
106             ComponentTypeSection(_)
107             | ComponentImportSection(_)
108             | ComponentExportSection(_)
109             | ComponentStartSection { .. }
110             | ComponentAliasSection(_)
111             | CoreTypeSection(_)
112             | InstanceSection(_)
113             | ComponentInstanceSection(_)
114             | ComponentCanonicalSection(_)
115             | ModuleSection { .. }
116             | ComponentSection { .. } => {
117                 unreachable!()
118             }
119         }
120     }
121 }
122 
123 fn type_section<'a>(
124     cx: &mut ModuleContext<'a>,
125     stack: &mut Vec<StackEntry>,
126     full_wasm: &'a [u8],
127     types: wasmparser::TypeSectionReader<'a>,
128 ) -> anyhow::Result<()> {
129     let module = stack.top().module;
130     module.add_raw_section(cx, SectionId::Type, types.range(), full_wasm);
131 
132     // Parse out types, as we will need them later when processing
133     // instance imports.
134     for ty in types {
135         match ty? {
136             ty @ wasmparser::Type::Func(_) => {
137                 module.push_type(cx, ty);
138             }
139         }
140     }
141 
142     Ok(())
143 }
144 
145 fn import_section<'a>(
146     cx: &mut ModuleContext<'a>,
147     stack: &mut Vec<StackEntry>,
148     full_wasm: &'a [u8],
149     imports: wasmparser::ImportSectionReader<'a>,
150 ) -> anyhow::Result<()> {
151     let module = stack.top().module;
152     stack
153         .top_mut()
154         .module
155         .add_raw_section(cx, SectionId::Import, imports.range(), full_wasm);
156 
157     // Check that we can properly handle all imports.
158     for imp in imports {
159         let imp = imp?;
160 
161         if imp.module.starts_with("__wizer_") || imp.name.starts_with("__wizer_") {
162             anyhow::bail!(
163                 "input Wasm module already imports entities named with the `__wizer_*` prefix"
164             );
165         }
166 
167         check_import_type(
168             cx,
169             stack.top().module.types(cx),
170             stack.top().module.is_root(),
171             &module.entity_type(cx, imp.ty),
172         )?;
173         module.push_import(cx, imp);
174     }
175     Ok(())
176 }
177 
178 fn check_import_type(
179     _cx: &ModuleContext,
180     _types: &[TypeId],
181     is_root: bool,
182     ty: &EntityType,
183 ) -> Result<()> {
184     match ty {
185         EntityType::Function(_) => Ok(()),
186         EntityType::Memory(mem_ty) => {
187             anyhow::ensure!(
188                 !mem_ty.shared,
189                 "shared memories are not supported by Wizer yet"
190             );
191             anyhow::ensure!(
192                 !mem_ty.memory64,
193                 "the memory64 proposal is not supported by Wizer yet"
194             );
195             anyhow::ensure!(
196                 !is_root,
197                 "memory imports are not allowed in the root Wasm module"
198             );
199             Ok(())
200         }
201         EntityType::Table(_) | EntityType::Global(_) => {
202             anyhow::ensure!(
203                 !is_root,
204                 "table and global imports are not allowed in the root Wasm module"
205             );
206             Ok(())
207         }
208     }
209 }
210 
211 fn function_section<'a>(
212     cx: &mut ModuleContext<'a>,
213     stack: &mut Vec<StackEntry>,
214     full_wasm: &'a [u8],
215     funcs: wasmparser::FunctionSectionReader<'a>,
216 ) -> anyhow::Result<()> {
217     let module = stack.top().module;
218     module.add_raw_section(cx, SectionId::Function, funcs.range(), full_wasm);
219 
220     for ty_idx in funcs {
221         let ty = module.type_id_at(cx, ty_idx?);
222         module.push_function(cx, ty);
223     }
224     Ok(())
225 }
226 
227 fn table_section<'a>(
228     cx: &mut ModuleContext<'a>,
229     stack: &mut Vec<StackEntry>,
230     full_wasm: &'a [u8],
231     tables: wasmparser::TableSectionReader<'a>,
232 ) -> anyhow::Result<()> {
233     let module = stack.top().module;
234     module.add_raw_section(cx, SectionId::Table, tables.range(), full_wasm);
235 
236     for table in tables {
237         module.push_table(cx, table?.ty);
238     }
239     Ok(())
240 }
241 
242 fn memory_section<'a>(
243     cx: &mut ModuleContext<'a>,
244     stack: &mut Vec<StackEntry>,
245     full_wasm: &'a [u8],
246     mems: wasmparser::MemorySectionReader<'a>,
247 ) -> anyhow::Result<()> {
248     let module = stack.top().module;
249     module.add_raw_section(cx, SectionId::Memory, mems.range(), full_wasm);
250 
251     for m in mems {
252         module.push_defined_memory(cx, m?);
253     }
254     Ok(())
255 }
256 
257 fn global_section<'a>(
258     cx: &mut ModuleContext<'a>,
259     stack: &mut Vec<StackEntry>,
260     full_wasm: &'a [u8],
261     globals: wasmparser::GlobalSectionReader<'a>,
262 ) -> anyhow::Result<()> {
263     let module = stack.top().module;
264     module.add_raw_section(cx, SectionId::Global, globals.range(), full_wasm);
265 
266     for g in globals {
267         module.push_defined_global(cx, g?.ty);
268     }
269     Ok(())
270 }
271 
272 fn export_section<'a>(
273     cx: &mut ModuleContext<'a>,
274     stack: &mut Vec<StackEntry>,
275     full_wasm: &'a [u8],
276     exports: wasmparser::ExportSectionReader<'a>,
277 ) -> anyhow::Result<()> {
278     let module = stack.top().module;
279     module.add_raw_section(cx, SectionId::Export, exports.range(), full_wasm);
280 
281     for export in exports {
282         let export = export?;
283 
284         if export.name.starts_with("__wizer_") {
285             anyhow::bail!(
286                 "input Wasm module already exports entities named with the `__wizer_*` prefix"
287             );
288         }
289 
290         match export.kind {
291             wasmparser::ExternalKind::Tag => {
292                 unreachable!("checked in validation")
293             }
294             wasmparser::ExternalKind::Func
295             | wasmparser::ExternalKind::Table
296             | wasmparser::ExternalKind::Memory
297             | wasmparser::ExternalKind::Global => {
298                 module.push_export(cx, export);
299             }
300         }
301     }
302     Ok(())
303 }
304