wasmer_compiler/translator/
module.rs

1// This file contains code from external sources.
2// Attributions: https://github.com/wasmerio/wasmer/blob/main/docs/ATTRIBUTIONS.md
3
4//! Translation skeleton that traverses the whole WebAssembly module and call helper functions
5//! to deal with each part of it.
6use std::sync::atomic::{AtomicBool, Ordering};
7
8use crate::{FunctionBinaryReader, MiddlewareBinaryReader};
9
10use super::environ::ModuleEnvironment;
11use super::error::from_binaryreadererror_wasmerror;
12use super::sections::{
13    parse_data_section, parse_element_section, parse_export_section, parse_function_section,
14    parse_global_section, parse_import_section, parse_memory_section, parse_name_section,
15    parse_start_section, parse_table_section, parse_tag_section, parse_type_section,
16};
17use super::state::ModuleTranslationState;
18use itertools::Itertools;
19use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
20use wasmer_types::entity::PrimaryMap;
21use wasmer_types::{
22    InitExpr, InitExprOp, LocalFunctionIndex, ModuleInfo, TableIndex, WasmError, WasmResult,
23};
24use wasmparser::{BinaryReader, NameSectionReader, Parser, Payload};
25
26fn analyze_function_readonly_table(
27    function_body: &super::environ::FunctionBodyData<'_>,
28    table_index: TableIndex,
29) -> WasmResult<bool> {
30    let mut reader =
31        MiddlewareBinaryReader::new_with_offset(function_body.data, function_body.module_offset);
32
33    let local_count = reader.read_local_count()?;
34    for _ in 0..local_count {
35        reader.read_local_decl()?;
36    }
37
38    while !reader.eof() {
39        match reader.read_operator()? {
40            wasmparser::Operator::TableSet { table }
41            | wasmparser::Operator::TableFill { table }
42            | wasmparser::Operator::TableGrow { table }
43                if TableIndex::from_u32(table) == table_index =>
44            {
45                return Ok(false);
46            }
47            wasmparser::Operator::TableCopy { dst_table, .. }
48                if TableIndex::from_u32(dst_table) == table_index =>
49            {
50                return Ok(false);
51            }
52            wasmparser::Operator::TableInit { table, .. }
53                if TableIndex::from_u32(table) == table_index =>
54            {
55                return Ok(false);
56            }
57            wasmparser::Operator::ElemDrop { .. } => return Ok(false),
58            _ => {}
59        }
60    }
61
62    Ok(true)
63}
64
65pub(crate) fn analyze_readonly_funcref_table(
66    module: &ModuleInfo,
67    function_body_inputs: &PrimaryMap<LocalFunctionIndex, super::environ::FunctionBodyData<'_>>,
68) -> WasmResult<Option<TableIndex>> {
69    let Ok(table_index) = module
70        .tables
71        .iter()
72        .filter_map(|(index, table)| {
73            if module.local_table_index(index).is_some() && table.is_fixed_funcref_table() {
74                Some(index)
75            } else {
76                None
77            }
78        })
79        .exactly_one()
80    else {
81        return Ok(None);
82    };
83
84    let table = module.tables[table_index];
85    let Ok(table_initializer) = module
86        .table_initializers
87        .iter()
88        .filter(|initializer| initializer.table_index == table_index)
89        .exactly_one()
90    else {
91        return Ok(None);
92    };
93
94    // We're expecting all table elements are initialized except the first one (null pointer).
95    if table_initializer.offset_expr != InitExpr::new([InitExprOp::I32Const(1)])
96        || table_initializer.elements.len() as u32 != table.minimum.saturating_sub(1)
97    {
98        return Ok(None);
99    }
100
101    // Imported functions carry their own host environment / callee VMContext.
102    if table_initializer
103        .elements
104        .iter()
105        .any(|func_index| module.is_imported_function(*func_index))
106    {
107        return Ok(None);
108    }
109
110    let readonly = AtomicBool::new(true);
111    function_body_inputs
112        .iter()
113        .collect_vec()
114        .par_iter()
115        .map(|(_local_func_index, function_body)| {
116            if !readonly.load(Ordering::Relaxed) {
117                return Ok(());
118            }
119
120            if !analyze_function_readonly_table(function_body, table_index)? {
121                readonly.store(false, Ordering::Relaxed);
122            }
123
124            Ok(())
125        })
126        .collect::<WasmResult<Vec<_>>>()?;
127
128    if !readonly.load(Ordering::Relaxed) {
129        return Ok(None);
130    }
131
132    Ok(Some(table_index))
133}
134
135/// Translate a sequence of bytes forming a valid Wasm binary into a
136/// parsed ModuleInfo `ModuleTranslationState`.
137pub fn translate_module<'data>(
138    data: &'data [u8],
139    environ: &mut ModuleEnvironment<'data>,
140) -> WasmResult<ModuleTranslationState> {
141    let mut module_translation_state = ModuleTranslationState::new();
142
143    for payload in Parser::new(0).parse_all(data) {
144        match payload.map_err(from_binaryreadererror_wasmerror)? {
145            Payload::Version { .. } | Payload::End { .. } => {}
146
147            Payload::TypeSection(types) => {
148                parse_type_section(types, &mut module_translation_state, environ)?;
149            }
150
151            Payload::ImportSection(imports) => {
152                parse_import_section(imports, environ)?;
153            }
154
155            Payload::FunctionSection(functions) => {
156                parse_function_section(functions, environ)?;
157            }
158
159            Payload::TableSection(tables) => {
160                parse_table_section(tables, environ)?;
161            }
162
163            Payload::MemorySection(memories) => {
164                parse_memory_section(memories, environ)?;
165            }
166
167            Payload::GlobalSection(globals) => {
168                parse_global_section(globals, environ)?;
169            }
170
171            Payload::ExportSection(exports) => {
172                parse_export_section(exports, environ)?;
173            }
174
175            Payload::StartSection { func, .. } => {
176                parse_start_section(func, environ)?;
177            }
178
179            Payload::ElementSection(elements) => {
180                parse_element_section(elements, environ)?;
181            }
182
183            Payload::CodeSectionStart { .. } => {}
184            Payload::CodeSectionEntry(code) => {
185                let mut code = code.get_binary_reader();
186                let size = code.bytes_remaining();
187                let offset = code.original_position();
188                environ.define_function_body(
189                    &module_translation_state,
190                    code.read_bytes(size)
191                        .map_err(from_binaryreadererror_wasmerror)?,
192                    offset,
193                )?;
194            }
195
196            Payload::DataSection(data) => {
197                parse_data_section(data, environ)?;
198            }
199
200            Payload::DataCountSection { count, .. } => {
201                environ.reserve_passive_data(count)?;
202            }
203
204            Payload::TagSection(t) => parse_tag_section(t, environ)?,
205
206            Payload::CustomSection(sectionreader) => {
207                // We still add the custom section data, but also read it as name section reader
208                let name = sectionreader.name();
209                environ.custom_section(name, sectionreader.data())?;
210                if name == "name" {
211                    parse_name_section(
212                        NameSectionReader::new(BinaryReader::new(
213                            sectionreader.data(),
214                            sectionreader.data_offset(),
215                        )),
216                        environ,
217                    )?;
218                }
219            }
220
221            Payload::UnknownSection { .. } => unreachable!(),
222            k => {
223                return Err(WasmError::Unsupported(format!(
224                    "Unsupported paylod kind: {k:?}"
225                )));
226            }
227        }
228    }
229
230    Ok(module_translation_state)
231}