1 //! Helper functions to gather information for each of the non-function sections of a
2 //! WebAssembly module.
3 //!
4 //! The code of these helper functions is straightforward since they only read metadata
5 //! about linear memories, tables, globals, etc. and store them for later use.
6 //!
7 //! The special case of the initialize expressions for table elements offsets or global variables
8 //! is handled, according to the semantics of WebAssembly, to only specific expressions that are
9 //! interpreted on the fly.
10 use crate::environ::{ModuleEnvironment, WasmError, WasmResult};
11 use crate::state::ModuleTranslationState;
12 use crate::translation_utils::{
13     tabletype_to_type, type_to_type, DataIndex, ElemIndex, FuncIndex, Global, GlobalIndex,
14     GlobalInit, Memory, MemoryIndex, SignatureIndex, Table, TableElementType, TableIndex,
15 };
16 use crate::{wasm_unsupported, HashMap};
17 use core::convert::TryFrom;
18 use cranelift_codegen::ir::immediates::V128Imm;
19 use cranelift_codegen::ir::{self, AbiParam, Signature};
20 use cranelift_entity::packed_option::ReservedValue;
21 use cranelift_entity::EntityRef;
22 use std::boxed::Box;
23 use std::vec::Vec;
24 use wasmparser::{
25     self, CodeSectionReader, Data, DataKind, DataSectionReader, Element, ElementItem, ElementItems,
26     ElementKind, ElementSectionReader, Export, ExportSectionReader, ExternalKind,
27     FunctionSectionReader, GlobalSectionReader, GlobalType, ImportSectionEntryType,
28     ImportSectionReader, MemorySectionReader, MemoryType, NameSectionReader, Naming, NamingReader,
29     Operator, TableSectionReader, Type, TypeSectionReader,
30 };
31 
32 /// Parses the Type section of the wasm module.
parse_type_section( types: TypeSectionReader, module_translation_state: &mut ModuleTranslationState, environ: &mut dyn ModuleEnvironment, ) -> WasmResult<()>33 pub fn parse_type_section(
34     types: TypeSectionReader,
35     module_translation_state: &mut ModuleTranslationState,
36     environ: &mut dyn ModuleEnvironment,
37 ) -> WasmResult<()> {
38     let count = types.get_count();
39     module_translation_state.wasm_types.reserve(count as usize);
40     environ.reserve_signatures(count)?;
41 
42     for entry in types {
43         let wasm_func_ty = entry?;
44         let mut sig = Signature::new(ModuleEnvironment::target_config(environ).default_call_conv);
45         sig.params.extend(wasm_func_ty.params.iter().map(|ty| {
46             let cret_arg: ir::Type = type_to_type(*ty, environ)
47                 .expect("only numeric types are supported in function signatures");
48             AbiParam::new(cret_arg)
49         }));
50         sig.returns.extend(wasm_func_ty.returns.iter().map(|ty| {
51             let cret_arg: ir::Type = type_to_type(*ty, environ)
52                 .expect("only numeric types are supported in function signatures");
53             AbiParam::new(cret_arg)
54         }));
55         environ.declare_signature(&wasm_func_ty, sig)?;
56         module_translation_state
57             .wasm_types
58             .push((wasm_func_ty.params, wasm_func_ty.returns));
59     }
60     Ok(())
61 }
62 
63 /// Parses the Import section of the wasm module.
parse_import_section<'data>( imports: ImportSectionReader<'data>, environ: &mut dyn ModuleEnvironment<'data>, ) -> WasmResult<()>64 pub fn parse_import_section<'data>(
65     imports: ImportSectionReader<'data>,
66     environ: &mut dyn ModuleEnvironment<'data>,
67 ) -> WasmResult<()> {
68     environ.reserve_imports(imports.get_count())?;
69 
70     for entry in imports {
71         let import = entry?;
72         let module_name = import.module;
73         let field_name = import.field;
74 
75         match import.ty {
76             ImportSectionEntryType::Function(sig) => {
77                 environ.declare_func_import(
78                     SignatureIndex::from_u32(sig),
79                     module_name,
80                     field_name,
81                 )?;
82             }
83             ImportSectionEntryType::Memory(MemoryType {
84                 limits: ref memlimits,
85                 shared,
86             }) => {
87                 environ.declare_memory_import(
88                     Memory {
89                         minimum: memlimits.initial,
90                         maximum: memlimits.maximum,
91                         shared,
92                     },
93                     module_name,
94                     field_name,
95                 )?;
96             }
97             ImportSectionEntryType::Global(ref ty) => {
98                 environ.declare_global_import(
99                     Global {
100                         ty: type_to_type(ty.content_type, environ).unwrap(),
101                         mutability: ty.mutable,
102                         initializer: GlobalInit::Import,
103                     },
104                     module_name,
105                     field_name,
106                 )?;
107             }
108             ImportSectionEntryType::Table(ref tab) => {
109                 environ.declare_table_import(
110                     Table {
111                         ty: match tabletype_to_type(tab.element_type, environ)? {
112                             Some(t) => TableElementType::Val(t),
113                             None => TableElementType::Func,
114                         },
115                         minimum: tab.limits.initial,
116                         maximum: tab.limits.maximum,
117                     },
118                     module_name,
119                     field_name,
120                 )?;
121             }
122         }
123     }
124 
125     environ.finish_imports()?;
126     Ok(())
127 }
128 
129 /// Parses the Function section of the wasm module.
parse_function_section( functions: FunctionSectionReader, environ: &mut dyn ModuleEnvironment, ) -> WasmResult<()>130 pub fn parse_function_section(
131     functions: FunctionSectionReader,
132     environ: &mut dyn ModuleEnvironment,
133 ) -> WasmResult<()> {
134     let num_functions = functions.get_count();
135     if num_functions == std::u32::MAX {
136         // We reserve `u32::MAX` for our own use in cranelift-entity.
137         return Err(WasmError::ImplLimitExceeded);
138     }
139 
140     environ.reserve_func_types(num_functions)?;
141 
142     for entry in functions {
143         let sigindex = entry?;
144         environ.declare_func_type(SignatureIndex::from_u32(sigindex))?;
145     }
146 
147     Ok(())
148 }
149 
150 /// Parses the Table section of the wasm module.
parse_table_section( tables: TableSectionReader, environ: &mut dyn ModuleEnvironment, ) -> WasmResult<()>151 pub fn parse_table_section(
152     tables: TableSectionReader,
153     environ: &mut dyn ModuleEnvironment,
154 ) -> WasmResult<()> {
155     environ.reserve_tables(tables.get_count())?;
156 
157     for entry in tables {
158         let table = entry?;
159         environ.declare_table(Table {
160             ty: match tabletype_to_type(table.element_type, environ)? {
161                 Some(t) => TableElementType::Val(t),
162                 None => TableElementType::Func,
163             },
164             minimum: table.limits.initial,
165             maximum: table.limits.maximum,
166         })?;
167     }
168 
169     Ok(())
170 }
171 
172 /// Parses the Memory section of the wasm module.
parse_memory_section( memories: MemorySectionReader, environ: &mut dyn ModuleEnvironment, ) -> WasmResult<()>173 pub fn parse_memory_section(
174     memories: MemorySectionReader,
175     environ: &mut dyn ModuleEnvironment,
176 ) -> WasmResult<()> {
177     environ.reserve_memories(memories.get_count())?;
178 
179     for entry in memories {
180         let memory = entry?;
181         environ.declare_memory(Memory {
182             minimum: memory.limits.initial,
183             maximum: memory.limits.maximum,
184             shared: memory.shared,
185         })?;
186     }
187 
188     Ok(())
189 }
190 
191 /// Parses the Global section of the wasm module.
parse_global_section( globals: GlobalSectionReader, environ: &mut dyn ModuleEnvironment, ) -> WasmResult<()>192 pub fn parse_global_section(
193     globals: GlobalSectionReader,
194     environ: &mut dyn ModuleEnvironment,
195 ) -> WasmResult<()> {
196     environ.reserve_globals(globals.get_count())?;
197 
198     for entry in globals {
199         let wasmparser::Global {
200             ty: GlobalType {
201                 content_type,
202                 mutable,
203             },
204             init_expr,
205         } = entry?;
206         let mut init_expr_reader = init_expr.get_binary_reader();
207         let initializer = match init_expr_reader.read_operator()? {
208             Operator::I32Const { value } => GlobalInit::I32Const(value),
209             Operator::I64Const { value } => GlobalInit::I64Const(value),
210             Operator::F32Const { value } => GlobalInit::F32Const(value.bits()),
211             Operator::F64Const { value } => GlobalInit::F64Const(value.bits()),
212             Operator::V128Const { value } => {
213                 GlobalInit::V128Const(V128Imm::from(value.bytes().to_vec().as_slice()))
214             }
215             Operator::RefNull { ty: _ } => GlobalInit::RefNullConst,
216             Operator::RefFunc { function_index } => {
217                 GlobalInit::RefFunc(FuncIndex::from_u32(function_index))
218             }
219             Operator::GlobalGet { global_index } => {
220                 GlobalInit::GetGlobal(GlobalIndex::from_u32(global_index))
221             }
222             ref s => {
223                 return Err(wasm_unsupported!(
224                     "unsupported init expr in global section: {:?}",
225                     s
226                 ));
227             }
228         };
229         let global = Global {
230             ty: type_to_type(content_type, environ).unwrap(),
231             mutability: mutable,
232             initializer,
233         };
234         environ.declare_global(global)?;
235     }
236 
237     Ok(())
238 }
239 
240 /// Parses the Export section of the wasm module.
parse_export_section<'data>( exports: ExportSectionReader<'data>, environ: &mut dyn ModuleEnvironment<'data>, ) -> WasmResult<()>241 pub fn parse_export_section<'data>(
242     exports: ExportSectionReader<'data>,
243     environ: &mut dyn ModuleEnvironment<'data>,
244 ) -> WasmResult<()> {
245     environ.reserve_exports(exports.get_count())?;
246 
247     for entry in exports {
248         let Export {
249             field,
250             ref kind,
251             index,
252         } = entry?;
253 
254         // The input has already been validated, so we should be able to
255         // assume valid UTF-8 and use `from_utf8_unchecked` if performance
256         // becomes a concern here.
257         let index = index as usize;
258         match *kind {
259             ExternalKind::Function => environ.declare_func_export(FuncIndex::new(index), field)?,
260             ExternalKind::Table => environ.declare_table_export(TableIndex::new(index), field)?,
261             ExternalKind::Memory => {
262                 environ.declare_memory_export(MemoryIndex::new(index), field)?
263             }
264             ExternalKind::Global => {
265                 environ.declare_global_export(GlobalIndex::new(index), field)?
266             }
267         }
268     }
269 
270     environ.finish_exports()?;
271     Ok(())
272 }
273 
274 /// Parses the Start section of the wasm module.
parse_start_section(index: u32, environ: &mut dyn ModuleEnvironment) -> WasmResult<()>275 pub fn parse_start_section(index: u32, environ: &mut dyn ModuleEnvironment) -> WasmResult<()> {
276     environ.declare_start_func(FuncIndex::from_u32(index))?;
277     Ok(())
278 }
279 
read_elems(items: &ElementItems) -> WasmResult<Box<[FuncIndex]>>280 fn read_elems(items: &ElementItems) -> WasmResult<Box<[FuncIndex]>> {
281     let items_reader = items.get_items_reader()?;
282     let mut elems = Vec::with_capacity(usize::try_from(items_reader.get_count()).unwrap());
283     for item in items_reader {
284         let elem = match item? {
285             ElementItem::Null(_ty) => FuncIndex::reserved_value(),
286             ElementItem::Func(index) => FuncIndex::from_u32(index),
287         };
288         elems.push(elem);
289     }
290     Ok(elems.into_boxed_slice())
291 }
292 
293 /// Parses the Element section of the wasm module.
parse_element_section<'data>( elements: ElementSectionReader<'data>, environ: &mut dyn ModuleEnvironment, ) -> WasmResult<()>294 pub fn parse_element_section<'data>(
295     elements: ElementSectionReader<'data>,
296     environ: &mut dyn ModuleEnvironment,
297 ) -> WasmResult<()> {
298     environ.reserve_table_elements(elements.get_count())?;
299 
300     for (index, entry) in elements.into_iter().enumerate() {
301         let Element { kind, items, ty } = entry?;
302         if ty != Type::FuncRef {
303             return Err(wasm_unsupported!(
304                 "unsupported table element type: {:?}",
305                 ty
306             ));
307         }
308         let segments = read_elems(&items)?;
309         match kind {
310             ElementKind::Active {
311                 table_index,
312                 init_expr,
313             } => {
314                 let mut init_expr_reader = init_expr.get_binary_reader();
315                 let (base, offset) = match init_expr_reader.read_operator()? {
316                     Operator::I32Const { value } => (None, value as u32 as usize),
317                     Operator::GlobalGet { global_index } => {
318                         (Some(GlobalIndex::from_u32(global_index)), 0)
319                     }
320                     ref s => {
321                         return Err(wasm_unsupported!(
322                             "unsupported init expr in element section: {:?}",
323                             s
324                         ));
325                     }
326                 };
327                 environ.declare_table_elements(
328                     TableIndex::from_u32(table_index),
329                     base,
330                     offset,
331                     segments,
332                 )?
333             }
334             ElementKind::Passive => {
335                 let index = ElemIndex::from_u32(index as u32);
336                 environ.declare_passive_element(index, segments)?;
337             }
338             ElementKind::Declared => return Err(wasm_unsupported!("element kind declared")),
339         }
340     }
341     Ok(())
342 }
343 
344 /// Parses the Code section of the wasm module.
parse_code_section<'data>( code: CodeSectionReader<'data>, module_translation_state: &ModuleTranslationState, environ: &mut dyn ModuleEnvironment<'data>, ) -> WasmResult<()>345 pub fn parse_code_section<'data>(
346     code: CodeSectionReader<'data>,
347     module_translation_state: &ModuleTranslationState,
348     environ: &mut dyn ModuleEnvironment<'data>,
349 ) -> WasmResult<()> {
350     for body in code {
351         let mut reader = body?.get_binary_reader();
352         let size = reader.bytes_remaining();
353         let offset = reader.original_position();
354         environ.define_function_body(module_translation_state, reader.read_bytes(size)?, offset)?;
355     }
356     Ok(())
357 }
358 
359 /// Parses the Data section of the wasm module.
parse_data_section<'data>( data: DataSectionReader<'data>, environ: &mut dyn ModuleEnvironment<'data>, ) -> WasmResult<()>360 pub fn parse_data_section<'data>(
361     data: DataSectionReader<'data>,
362     environ: &mut dyn ModuleEnvironment<'data>,
363 ) -> WasmResult<()> {
364     environ.reserve_data_initializers(data.get_count())?;
365 
366     for (index, entry) in data.into_iter().enumerate() {
367         let Data { kind, data } = entry?;
368         match kind {
369             DataKind::Active {
370                 memory_index,
371                 init_expr,
372             } => {
373                 let mut init_expr_reader = init_expr.get_binary_reader();
374                 let (base, offset) = match init_expr_reader.read_operator()? {
375                     Operator::I32Const { value } => (None, value as u32 as usize),
376                     Operator::GlobalGet { global_index } => {
377                         (Some(GlobalIndex::from_u32(global_index)), 0)
378                     }
379                     ref s => {
380                         return Err(wasm_unsupported!(
381                             "unsupported init expr in data section: {:?}",
382                             s
383                         ))
384                     }
385                 };
386                 environ.declare_data_initialization(
387                     MemoryIndex::from_u32(memory_index),
388                     base,
389                     offset,
390                     data,
391                 )?;
392             }
393             DataKind::Passive => {
394                 let index = DataIndex::from_u32(index as u32);
395                 environ.declare_passive_data(index, data)?;
396             }
397         }
398     }
399 
400     Ok(())
401 }
402 
403 /// Parses the Name section of the wasm module.
parse_name_section<'data>( mut names: NameSectionReader<'data>, environ: &mut dyn ModuleEnvironment<'data>, ) -> WasmResult<()>404 pub fn parse_name_section<'data>(
405     mut names: NameSectionReader<'data>,
406     environ: &mut dyn ModuleEnvironment<'data>,
407 ) -> WasmResult<()> {
408     while let Ok(subsection) = names.read() {
409         match subsection {
410             wasmparser::Name::Function(function_subsection) => {
411                 if let Some(function_names) = function_subsection
412                     .get_map()
413                     .ok()
414                     .and_then(parse_function_name_subsection)
415                 {
416                     for (index, name) in function_names {
417                         environ.declare_func_name(index, name)?;
418                     }
419                 }
420             }
421             wasmparser::Name::Module(module) => {
422                 if let Ok(name) = module.get_name() {
423                     environ.declare_module_name(name)?;
424                 }
425             }
426             wasmparser::Name::Local(_) => {}
427         };
428     }
429     Ok(())
430 }
431 
parse_function_name_subsection( mut naming_reader: NamingReader<'_>, ) -> Option<HashMap<FuncIndex, &str>>432 fn parse_function_name_subsection(
433     mut naming_reader: NamingReader<'_>,
434 ) -> Option<HashMap<FuncIndex, &str>> {
435     let mut function_names = HashMap::new();
436     for _ in 0..naming_reader.get_count() {
437         let Naming { index, name } = naming_reader.read().ok()?;
438         if index == std::u32::MAX {
439             // We reserve `u32::MAX` for our own use in cranelift-entity.
440             return None;
441         }
442 
443         if function_names
444             .insert(FuncIndex::from_u32(index), name)
445             .is_some()
446         {
447             // If the function index has been previously seen, then we
448             // break out of the loop and early return `None`, because these
449             // should be unique.
450             return None;
451         }
452     }
453     Some(function_names)
454 }
455