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