near_vm_compiler/translator/
sections.rs

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