1use crate::executable::{unrkyv, UniversalExecutableRef};
4use crate::{CodeMemory, UniversalArtifact, UniversalExecutable};
5#[cfg(feature = "compiler")]
6use near_vm_compiler::Compiler;
7use near_vm_compiler::{
8 CompileError, CustomSectionProtection, CustomSectionRef, FunctionBodyRef, JumpTable,
9 SectionIndex, Target,
10};
11use near_vm_engine::{Engine, EngineId};
12use near_vm_types::entity::{EntityRef, PrimaryMap};
13use near_vm_types::{
14 DataInitializer, ExportIndex, Features, FunctionIndex, FunctionType, FunctionTypeRef,
15 GlobalInit, GlobalType, ImportCounts, ImportIndex, LocalFunctionIndex, LocalGlobalIndex,
16 MemoryIndex, SignatureIndex, TableIndex,
17};
18use near_vm_vm::{
19 FuncDataRegistry, FunctionBodyPtr, SectionBodyPtr, SignatureRegistry, Tunables,
20 VMCallerCheckedAnyfunc, VMFuncRef, VMFunctionBody, VMImportType, VMLocalFunction, VMOffsets,
21 VMSharedSignatureIndex, VMTrampoline,
22};
23use rkyv::de::deserializers::SharedDeserializeMap;
24use std::collections::BTreeMap;
25use std::convert::TryFrom;
26use std::sync::{Arc, Mutex};
27
28#[derive(Clone)]
30pub struct UniversalEngine {
31 inner: Arc<Mutex<UniversalEngineInner>>,
32 target: Arc<Target>,
34 engine_id: EngineId,
35}
36
37impl UniversalEngine {
38 #[cfg(feature = "compiler")]
40 pub fn new(compiler: Box<dyn Compiler>, target: Target, features: Features) -> Self {
41 Self {
42 inner: Arc::new(Mutex::new(UniversalEngineInner {
43 compiler: Some(compiler),
44 code_memory: vec![],
45 signatures: SignatureRegistry::new(),
46 func_data: Arc::new(FuncDataRegistry::new()),
47 features,
48 })),
49 target: Arc::new(target),
50 engine_id: EngineId::default(),
51 }
52 }
53
54 pub fn headless() -> Self {
68 Self {
69 inner: Arc::new(Mutex::new(UniversalEngineInner {
70 #[cfg(feature = "compiler")]
71 compiler: None,
72 code_memory: vec![],
73 signatures: SignatureRegistry::new(),
74 func_data: Arc::new(FuncDataRegistry::new()),
75 features: Features::default(),
76 })),
77 target: Arc::new(Target::default()),
78 engine_id: EngineId::default(),
79 }
80 }
81
82 pub(crate) fn inner(&self) -> std::sync::MutexGuard<'_, UniversalEngineInner> {
83 self.inner.lock().unwrap()
84 }
85
86 pub(crate) fn inner_mut(&self) -> std::sync::MutexGuard<'_, UniversalEngineInner> {
87 self.inner.lock().unwrap()
88 }
89
90 #[cfg(feature = "compiler")]
92 #[tracing::instrument(skip_all)]
93 pub fn compile_universal(
94 &self,
95 binary: &[u8],
96 tunables: &dyn Tunables,
97 ) -> Result<crate::UniversalExecutable, CompileError> {
98 let instrumentation = finite_wasm::Analysis::new()
100 .with_stack(tunables.stack_limiter_cfg())
101 .with_gas(tunables.gas_cfg())
102 .analyze(binary)
103 .map_err(CompileError::Analyze)?;
104
105 let inner_engine = self.inner_mut();
106 let features = inner_engine.features();
107 let compiler = inner_engine.compiler()?;
108 let environ = near_vm_compiler::ModuleEnvironment::new();
109 let translation = environ.translate(binary).map_err(CompileError::Wasm)?;
110
111 let memory_styles: PrimaryMap<near_vm_types::MemoryIndex, _> = translation
112 .module
113 .memories
114 .values()
115 .map(|memory_type| tunables.memory_style(memory_type))
116 .collect();
117 let table_styles: PrimaryMap<near_vm_types::TableIndex, _> = translation
118 .module
119 .tables
120 .values()
121 .map(|table_type| tunables.table_style(table_type))
122 .collect();
123
124 let compile_info = near_vm_compiler::CompileModuleInfo {
126 module: Arc::new(translation.module),
127 features: features.clone(),
128 memory_styles,
129 table_styles,
130 };
131 let near_vm_compiler::Compilation {
132 functions,
133 custom_sections,
134 function_call_trampolines,
135 dynamic_function_trampolines,
136 debug,
137 trampolines,
138 } = compiler.compile_module(
139 &self.target(),
140 &compile_info,
141 translation.module_translation_state.as_ref().unwrap(),
145 translation.function_body_inputs,
146 tunables,
147 &instrumentation,
148 )?;
149 let data_initializers = translation
150 .data_initializers
151 .iter()
152 .map(near_vm_types::OwnedDataInitializer::new)
153 .collect();
154 let mut function_frame_info = PrimaryMap::with_capacity(functions.len());
155 let mut function_bodies = PrimaryMap::with_capacity(functions.len());
156 let mut function_relocations = PrimaryMap::with_capacity(functions.len());
157 let mut function_jt_offsets = PrimaryMap::with_capacity(functions.len());
158 for (_, func) in functions.into_iter() {
159 function_bodies.push(func.body);
160 function_relocations.push(func.relocations);
161 function_jt_offsets.push(func.jt_offsets);
162 function_frame_info.push(func.frame_info);
163 }
164 let custom_section_relocations = custom_sections
165 .iter()
166 .map(|(_, section)| section.relocations.clone())
167 .collect::<PrimaryMap<SectionIndex, _>>();
168 Ok(crate::UniversalExecutable {
169 function_bodies,
170 function_relocations,
171 function_jt_offsets,
172 function_frame_info,
173 function_call_trampolines,
174 dynamic_function_trampolines,
175 custom_sections,
176 custom_section_relocations,
177 debug,
178 trampolines,
179 compile_info,
180 data_initializers,
181 cpu_features: self.target().cpu_features().as_u64(),
182 })
183 }
184
185 #[tracing::instrument(skip_all)]
187 pub fn load_universal_executable(
188 &self,
189 executable: &UniversalExecutable,
190 ) -> Result<UniversalArtifact, CompileError> {
191 let info = &executable.compile_info;
192 let module = &info.module;
193 let local_memories = (module.import_counts.memories as usize..module.memories.len())
194 .map(|idx| {
195 let idx = MemoryIndex::new(idx);
196 (module.memories[idx], info.memory_styles[idx].clone())
197 })
198 .collect();
199 let local_tables = (module.import_counts.tables as usize..module.tables.len())
200 .map(|idx| {
201 let idx = TableIndex::new(idx);
202 (module.tables[idx], info.table_styles[idx].clone())
203 })
204 .collect();
205 let local_globals: Vec<(GlobalType, GlobalInit)> = module
206 .globals
207 .iter()
208 .skip(module.import_counts.globals as usize)
209 .enumerate()
210 .map(|(idx, (_, t))| {
211 let init = module.global_initializers[LocalGlobalIndex::new(idx)];
212 (*t, init)
213 })
214 .collect();
215 let mut inner_engine = self.inner_mut();
216
217 let local_functions = executable.function_bodies.iter().map(|(_, b)| b.into());
218 let function_call_trampolines = &executable.function_call_trampolines;
219 let dynamic_function_trampolines = &executable.dynamic_function_trampolines;
220 let signatures = module
221 .signatures
222 .iter()
223 .map(|(_, sig)| inner_engine.signatures.register(sig.clone()))
224 .collect::<PrimaryMap<SignatureIndex, _>>()
225 .into_boxed_slice();
226 let (functions, trampolines, dynamic_trampolines, custom_sections) = inner_engine
227 .allocate(
228 local_functions,
229 function_call_trampolines.iter().map(|(_, b)| b.into()),
230 dynamic_function_trampolines.iter().map(|(_, b)| b.into()),
231 executable.custom_sections.iter().map(|(_, s)| s.into()),
232 |idx: LocalFunctionIndex| {
233 let func_idx = module.import_counts.function_index(idx);
234 let sig_idx = module.functions[func_idx];
235 (sig_idx, signatures[sig_idx])
236 },
237 )?;
238 let imports = module
239 .imports
240 .iter()
241 .map(|((module_name, field, idx), entity)| near_vm_vm::VMImport {
242 module: String::from(module_name),
243 field: String::from(field),
244 import_no: *idx,
245 ty: match entity {
246 ImportIndex::Function(i) => {
247 let sig_idx = module.functions[*i];
248 VMImportType::Function {
249 sig: signatures[sig_idx],
250 static_trampoline: trampolines[sig_idx],
251 }
252 }
253 ImportIndex::Table(i) => VMImportType::Table(module.tables[*i]),
254 &ImportIndex::Memory(i) => {
255 let ty = module.memories[i];
256 VMImportType::Memory(ty, info.memory_styles[i].clone())
257 }
258 ImportIndex::Global(i) => VMImportType::Global(module.globals[*i]),
259 },
260 })
261 .collect();
262
263 let function_relocations = executable.function_relocations.iter();
264 let section_relocations = executable.custom_section_relocations.iter();
265 crate::link_module(
266 &functions,
267 |func_idx, jt_idx| executable.function_jt_offsets[func_idx][jt_idx],
268 function_relocations.map(|(i, rs)| (i, rs.iter().cloned())),
269 &custom_sections,
270 section_relocations.map(|(i, rs)| (i, rs.iter().cloned())),
271 &executable.trampolines,
272 );
273
274 inner_engine.publish_compiled_code();
276 if let Some(ref d) = executable.debug {
277 unsafe {
278 inner_engine.publish_eh_frame(std::slice::from_raw_parts(
280 *custom_sections[d.eh_frame],
281 executable.custom_sections[d.eh_frame].bytes.len(),
282 ))?;
283 }
284 }
285 let exports = module
286 .exports
287 .iter()
288 .map(|(s, i)| (s.clone(), i.clone()))
289 .collect::<BTreeMap<String, ExportIndex>>();
290
291 Ok(UniversalArtifact {
292 engine: self.clone(),
293 import_counts: module.import_counts,
294 start_function: module.start_function,
295 vmoffsets: VMOffsets::for_host().with_module_info(&*module),
296 imports,
297 dynamic_function_trampolines: dynamic_trampolines.into_boxed_slice(),
298 functions: functions.into_boxed_slice(),
299 exports,
300 signatures,
301 local_memories,
302 data_segments: executable.data_initializers.clone(),
303 passive_data: module.passive_data.clone(),
304 local_tables,
305 element_segments: module.table_initializers.clone(),
306 passive_elements: module.passive_elements.clone(),
307 local_globals,
308 })
309 }
310
311 pub fn load_universal_executable_ref(
313 &self,
314 executable: &UniversalExecutableRef,
315 ) -> Result<UniversalArtifact, CompileError> {
316 let info = &executable.compile_info;
317 let module = &info.module;
318 let import_counts: ImportCounts = unrkyv(&module.import_counts);
319 let local_memories = (import_counts.memories as usize..module.memories.len())
320 .map(|idx| {
321 let idx = MemoryIndex::new(idx);
322 let mty = &module.memories[&idx];
323 (unrkyv(mty), unrkyv(&info.memory_styles[&idx]))
324 })
325 .collect();
326 let local_tables = (import_counts.tables as usize..module.tables.len())
327 .map(|idx| {
328 let idx = TableIndex::new(idx);
329 let tty = &module.tables[&idx];
330 (unrkyv(tty), unrkyv(&info.table_styles[&idx]))
331 })
332 .collect();
333 let local_globals: Vec<(GlobalType, GlobalInit)> = module
334 .globals
335 .iter()
336 .skip(import_counts.globals as _)
337 .enumerate()
338 .map(|(idx, (_, t))| {
339 let init = unrkyv(&module.global_initializers[&LocalGlobalIndex::new(idx)]);
340 (*t, init)
341 })
342 .collect();
343
344 let passive_data =
345 rkyv::Deserialize::deserialize(&module.passive_data, &mut SharedDeserializeMap::new())
346 .map_err(|_| CompileError::Validate("could not deserialize passive data".into()))?;
347 let data_segments = executable.data_initializers.iter();
348 let data_segments = data_segments.map(|s| DataInitializer::from(s).into()).collect();
349 let element_segments = unrkyv(&module.table_initializers);
350 let passive_elements: BTreeMap<near_vm_types::ElemIndex, Box<[FunctionIndex]>> =
351 unrkyv(&module.passive_elements);
352
353 let import_counts: ImportCounts = unrkyv(&module.import_counts);
354 let mut inner_engine = self.inner_mut();
355
356 let local_functions = executable.function_bodies.iter().map(|(_, b)| b.into());
357 let call_trampolines = executable.function_call_trampolines.iter();
358 let dynamic_trampolines = executable.dynamic_function_trampolines.iter();
359 let signatures = module
360 .signatures
361 .values()
362 .map(|sig| {
363 let sig_ref = FunctionTypeRef::from(sig);
364 inner_engine
365 .signatures
366 .register(FunctionType::new(sig_ref.params(), sig_ref.results()))
367 })
368 .collect::<PrimaryMap<SignatureIndex, _>>()
369 .into_boxed_slice();
370 let (functions, trampolines, dynamic_trampolines, custom_sections) = inner_engine
371 .allocate(
372 local_functions,
373 call_trampolines.map(|(_, b)| b.into()),
374 dynamic_trampolines.map(|(_, b)| b.into()),
375 executable.custom_sections.iter().map(|(_, s)| s.into()),
376 |idx: LocalFunctionIndex| {
377 let func_idx = import_counts.function_index(idx);
378 let sig_idx = module.functions[&func_idx];
379 (sig_idx, signatures[sig_idx])
380 },
381 )?;
382 let imports = {
383 module
384 .imports
385 .iter()
386 .map(|((module_name, field, idx), entity)| near_vm_vm::VMImport {
387 module: String::from(module_name.as_str()),
388 field: String::from(field.as_str()),
389 import_no: *idx,
390 ty: match entity {
391 ImportIndex::Function(i) => {
392 let sig_idx = module.functions[i];
393 VMImportType::Function {
394 sig: signatures[sig_idx],
395 static_trampoline: trampolines[sig_idx],
396 }
397 }
398 ImportIndex::Table(i) => VMImportType::Table(unrkyv(&module.tables[i])),
399 ImportIndex::Memory(i) => {
400 let ty = unrkyv(&module.memories[i]);
401 VMImportType::Memory(ty, unrkyv(&info.memory_styles[i]))
402 }
403 ImportIndex::Global(i) => VMImportType::Global(unrkyv(&module.globals[i])),
404 },
405 })
406 .collect()
407 };
408
409 let function_relocations = executable.function_relocations.iter();
410 let section_relocations = executable.custom_section_relocations.iter();
411 crate::link_module(
412 &functions,
413 |func_idx, jt_idx| {
414 let func_idx = rkyv::Archived::<LocalFunctionIndex>::new(func_idx.index());
415 let jt_idx = rkyv::Archived::<JumpTable>::new(jt_idx.index());
416 executable.function_jt_offsets[&func_idx][&jt_idx]
417 },
418 function_relocations.map(|(i, r)| (i, r.iter().map(unrkyv))),
419 &custom_sections,
420 section_relocations.map(|(i, r)| (i, r.iter().map(unrkyv))),
421 &unrkyv(&executable.trampolines),
422 );
423
424 inner_engine.publish_compiled_code();
426 if let rkyv::option::ArchivedOption::Some(ref d) = executable.debug {
427 unsafe {
428 let s = CustomSectionRef::from(&executable.custom_sections[&d.eh_frame]);
430 inner_engine.publish_eh_frame(std::slice::from_raw_parts(
431 *custom_sections[unrkyv(&d.eh_frame)],
432 s.bytes.len(),
433 ))?;
434 }
435 }
436 let exports = module
437 .exports
438 .iter()
439 .map(|(s, i)| (unrkyv(s), unrkyv(i)))
440 .collect::<BTreeMap<String, ExportIndex>>();
441 Ok(UniversalArtifact {
442 engine: self.clone(),
443 import_counts,
444 start_function: unrkyv(&module.start_function),
445 vmoffsets: VMOffsets::for_host().with_archived_module_info(&*module),
446 imports,
447 dynamic_function_trampolines: dynamic_trampolines.into_boxed_slice(),
448 functions: functions.into_boxed_slice(),
449 exports,
450 signatures,
451 local_memories,
452 data_segments,
453 passive_data,
454 local_tables,
455 element_segments,
456 passive_elements,
457 local_globals,
458 })
459 }
460}
461
462impl Engine for UniversalEngine {
463 fn target(&self) -> &Target {
465 &self.target
466 }
467
468 fn register_signature(&self, func_type: FunctionType) -> VMSharedSignatureIndex {
470 self.inner().signatures.register(func_type)
471 }
472
473 fn register_function_metadata(&self, func_data: VMCallerCheckedAnyfunc) -> VMFuncRef {
474 self.inner().func_data().register(func_data)
475 }
476
477 fn lookup_signature(&self, sig: VMSharedSignatureIndex) -> Option<FunctionType> {
479 self.inner().signatures.lookup(sig).cloned()
480 }
481
482 #[tracing::instrument(skip_all)]
484 fn validate(&self, binary: &[u8]) -> Result<(), CompileError> {
485 self.inner().validate(binary)
486 }
487
488 #[cfg(not(feature = "compiler"))]
489 fn compile(
490 &self,
491 binary: &[u8],
492 tunables: &dyn Tunables,
493 ) -> Result<Box<dyn near_vm_engine::Executable>, CompileError> {
494 return Err(CompileError::Codegen(
495 "The UniversalEngine is operating in headless mode, so it can not compile Modules."
496 .to_string(),
497 ));
498 }
499
500 #[cfg(feature = "compiler")]
502 #[tracing::instrument(skip_all)]
503 fn compile(
504 &self,
505 binary: &[u8],
506 tunables: &dyn Tunables,
507 ) -> Result<Box<dyn near_vm_engine::Executable>, CompileError> {
508 self.compile_universal(binary, tunables).map(|ex| Box::new(ex) as _)
509 }
510
511 #[tracing::instrument(skip_all)]
512 fn load(
513 &self,
514 executable: &(dyn near_vm_engine::Executable),
515 ) -> Result<Arc<dyn near_vm_vm::Artifact>, CompileError> {
516 executable.load(self)
517 }
518
519 fn id(&self) -> &EngineId {
520 &self.engine_id
521 }
522
523 fn cloned(&self) -> Arc<dyn Engine + Send + Sync> {
524 Arc::new(self.clone())
525 }
526}
527
528pub struct UniversalEngineInner {
530 #[cfg(feature = "compiler")]
532 compiler: Option<Box<dyn Compiler>>,
533 features: Features,
535 code_memory: Vec<CodeMemory>,
538 pub(crate) signatures: SignatureRegistry,
541 func_data: Arc<FuncDataRegistry>,
545}
546
547impl UniversalEngineInner {
548 #[cfg(feature = "compiler")]
550 pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> {
551 if self.compiler.is_none() {
552 return Err(CompileError::Codegen("The UniversalEngine is operating in headless mode, so it can only execute already compiled Modules.".to_string()));
553 }
554 Ok(&**self.compiler.as_ref().unwrap())
555 }
556
557 #[cfg(feature = "compiler")]
559 pub fn validate<'data>(&self, data: &'data [u8]) -> Result<(), CompileError> {
560 self.compiler()?.validate_module(self.features(), data)
561 }
562
563 #[cfg(not(feature = "compiler"))]
565 pub fn validate<'data>(&self, _data: &'data [u8]) -> Result<(), CompileError> {
566 Err(CompileError::Validate(
567 "The UniversalEngine is not compiled with compiler support, which is required for validating"
568 .to_string(),
569 ))
570 }
571
572 pub fn features(&self) -> &Features {
574 &self.features
575 }
576
577 #[allow(clippy::type_complexity)]
579 pub(crate) fn allocate<'a>(
580 &mut self,
581 local_functions: impl ExactSizeIterator<Item = FunctionBodyRef<'a>>,
582 call_trampolines: impl ExactSizeIterator<Item = FunctionBodyRef<'a>>,
583 dynamic_trampolines: impl ExactSizeIterator<Item = FunctionBodyRef<'a>>,
584 custom_sections: impl ExactSizeIterator<Item = CustomSectionRef<'a>>,
585 function_signature: impl Fn(LocalFunctionIndex) -> (SignatureIndex, VMSharedSignatureIndex),
586 ) -> Result<
587 (
588 PrimaryMap<LocalFunctionIndex, VMLocalFunction>,
589 PrimaryMap<SignatureIndex, VMTrampoline>,
590 PrimaryMap<FunctionIndex, FunctionBodyPtr>,
591 PrimaryMap<SectionIndex, SectionBodyPtr>,
592 ),
593 CompileError,
594 > {
595 let code_memory = &mut self.code_memory;
596 let function_count = local_functions.len();
597 let call_trampoline_count = call_trampolines.len();
598 let function_bodies =
599 call_trampolines.chain(local_functions).chain(dynamic_trampolines).collect::<Vec<_>>();
600
601 let mut section_types = Vec::with_capacity(custom_sections.len());
603 let mut executable_sections = Vec::new();
604 let mut data_sections = Vec::new();
605 for section in custom_sections {
606 if let CustomSectionProtection::ReadExecute = section.protection {
607 executable_sections.push(section);
608 } else {
609 data_sections.push(section);
610 }
611 section_types.push(section.protection);
612 }
613 code_memory.push(CodeMemory::new());
614 let code_memory = self.code_memory.last_mut().expect("infallible");
615
616 let (mut allocated_functions, allocated_executable_sections, allocated_data_sections) =
617 code_memory
618 .allocate(
619 function_bodies.as_slice(),
620 executable_sections.as_slice(),
621 data_sections.as_slice(),
622 )
623 .map_err(|message| {
624 CompileError::Resource(format!(
625 "failed to allocate memory for functions: {}",
626 message
627 ))
628 })?;
629
630 let mut allocated_function_call_trampolines: PrimaryMap<SignatureIndex, VMTrampoline> =
631 PrimaryMap::new();
632 for ptr in allocated_functions.drain(0..call_trampoline_count).map(|slice| slice.as_ptr()) {
633 let trampoline =
635 unsafe { std::mem::transmute::<*const VMFunctionBody, VMTrampoline>(ptr) };
636 allocated_function_call_trampolines.push(trampoline);
637 }
638
639 let allocated_functions_result = allocated_functions
640 .drain(0..function_count)
641 .enumerate()
642 .map(|(index, slice)| -> Result<_, CompileError> {
643 let index = LocalFunctionIndex::new(index);
644 let (sig_idx, sig) = function_signature(index);
645 Ok(VMLocalFunction {
646 body: FunctionBodyPtr(slice.as_ptr()),
647 length: u32::try_from(slice.len()).map_err(|_| {
648 CompileError::Codegen("function body length exceeds 4GiB".into())
649 })?,
650 signature: sig,
651 trampoline: allocated_function_call_trampolines[sig_idx],
652 })
653 })
654 .collect::<Result<PrimaryMap<LocalFunctionIndex, _>, _>>()?;
655
656 let allocated_dynamic_function_trampolines = allocated_functions
657 .drain(..)
658 .map(|slice| FunctionBodyPtr(slice.as_ptr()))
659 .collect::<PrimaryMap<FunctionIndex, _>>();
660
661 let mut exec_iter = allocated_executable_sections.iter();
662 let mut data_iter = allocated_data_sections.iter();
663 let allocated_custom_sections = section_types
664 .into_iter()
665 .map(|protection| {
666 SectionBodyPtr(
667 if protection == CustomSectionProtection::ReadExecute {
668 exec_iter.next()
669 } else {
670 data_iter.next()
671 }
672 .unwrap()
673 .as_ptr(),
674 )
675 })
676 .collect::<PrimaryMap<SectionIndex, _>>();
677
678 Ok((
679 allocated_functions_result,
680 allocated_function_call_trampolines,
681 allocated_dynamic_function_trampolines,
682 allocated_custom_sections,
683 ))
684 }
685
686 pub(crate) fn publish_compiled_code(&mut self) {
688 self.code_memory.last_mut().unwrap().publish();
689 }
690
691 pub(crate) fn publish_eh_frame(&mut self, eh_frame: &[u8]) -> Result<(), CompileError> {
693 self.code_memory.last_mut().unwrap().unwind_registry_mut().publish(eh_frame).map_err(
694 |e| CompileError::Resource(format!("Error while publishing the unwind code: {}", e)),
695 )?;
696 Ok(())
697 }
698
699 pub(crate) fn func_data(&self) -> &Arc<FuncDataRegistry> {
701 &self.func_data
702 }
703}