1 use crate::bindings;
2 use lucet_runtime::{self, MmapRegion, Module as LucetModule, Region, UntypedRetVal, Val};
3 use lucetc::{Compiler, CpuFeatures, Error as LucetcError};
4 use std::io;
5 use std::process::Command;
6 use std::sync::Arc;
7 use thiserror::Error;
8
9 #[derive(Debug, Error)]
10 pub enum ScriptError {
11 #[error("Validation error")]
12 ValidationError(#[source] LucetcError),
13 #[error("Program error")]
14 ProgramError(#[source] LucetcError),
15 #[error("Compilation error")]
16 CompileError(#[source] LucetcError),
17 #[error("Codegen error")]
18 CodegenError(#[source] LucetcError),
19 #[error("Load error")]
20 LoadError(#[source] lucet_runtime::Error),
21 #[error("Instantiation error")]
22 InstantiateError(#[source] lucet_runtime::Error),
23 #[error("Runtime error")]
24 RuntimeError(#[source] lucet_runtime::Error),
25 #[error("Malformed script: {0}")]
26 MalformedScript(String),
27 #[error("IO error")]
28 IoError(#[from] io::Error),
29 #[error("run_ld error: {0}")]
30 LdError(String),
31 }
32
33 impl ScriptError {
unsupported(&self) -> bool34 pub fn unsupported(&self) -> bool {
35 match self {
36 ScriptError::ProgramError(ref lucetc_err)
37 | ScriptError::ValidationError(ref lucetc_err)
38 | ScriptError::CompileError(ref lucetc_err) => match lucetc_err {
39 &LucetcError::Unsupported(_) => true,
40 _ => false,
41 },
42 _ => false,
43 }
44 }
45 }
46
47 pub struct ScriptEnv {
48 instances: Vec<(Option<String>, lucet_runtime::InstanceHandle)>,
49 }
50
program_error(e: LucetcError) -> ScriptError51 fn program_error(e: LucetcError) -> ScriptError {
52 match e {
53 LucetcError::WasmValidation(_) => ScriptError::ValidationError(e),
54 _ => ScriptError::ProgramError(e),
55 }
56 }
57
58 impl ScriptEnv {
new() -> Self59 pub fn new() -> Self {
60 Self {
61 instances: Vec::new(),
62 }
63 }
instantiate(&mut self, module: &[u8], name: &Option<String>) -> Result<(), ScriptError>64 pub fn instantiate(&mut self, module: &[u8], name: &Option<String>) -> Result<(), ScriptError> {
65 let bindings = bindings::spec_test_bindings();
66 let builder = Compiler::builder()
67 .with_cpu_features(CpuFeatures::baseline())
68 .with_count_instructions(true);
69 let compiler = builder.create(module, &bindings).map_err(program_error)?;
70
71 let dir = tempfile::Builder::new().prefix("codegen").tempdir()?;
72 let objfile_path = dir.path().join("a.o");
73 let sofile_path = dir.path().join("a.so");
74
75 compiler
76 .object_file()
77 .map_err(ScriptError::CompileError)?
78 .write(&objfile_path)
79 .map_err(ScriptError::CodegenError)?;
80
81 let mut cmd_ld = Command::new("ld");
82 cmd_ld.arg(objfile_path.clone());
83 cmd_ld.arg("-shared");
84 cmd_ld.arg("-o");
85 cmd_ld.arg(sofile_path.clone());
86 let run_ld = cmd_ld.output()?;
87 if !run_ld.status.success() {
88 let message = format!(
89 "ld {:?}: {}",
90 objfile_path,
91 String::from_utf8_lossy(&run_ld.stderr)
92 );
93
94 return Err(ScriptError::LdError(message));
95 }
96
97 let lucet_module: Arc<dyn LucetModule> =
98 lucet_runtime::DlModule::load(sofile_path).map_err(ScriptError::LoadError)?;
99
100 let lucet_region = MmapRegion::create(
101 1,
102 &lucet_runtime::Limits {
103 heap_memory_size: 4 * 1024 * 1024 * 1024,
104 ..lucet_runtime::Limits::default()
105 },
106 )
107 .expect("valid region");
108
109 let lucet_instance = lucet_region
110 .new_instance(lucet_module.clone())
111 .map_err(ScriptError::InstantiateError)?;
112
113 self.instances.push((name.clone(), lucet_instance));
114 Ok(())
115 }
116
instance_named_mut( &mut self, name: &Option<String>, ) -> Result<&mut (Option<String>, lucet_runtime::InstanceHandle), ScriptError>117 fn instance_named_mut(
118 &mut self,
119 name: &Option<String>,
120 ) -> Result<&mut (Option<String>, lucet_runtime::InstanceHandle), ScriptError> {
121 Ok(match name {
122 // None means the last defined module should be used
123 None => self
124 .instances
125 .last_mut()
126 .ok_or_else(|| ScriptError::MalformedScript("no defined instances".to_owned()))?,
127 Some(ref n) => self
128 .instances
129 .iter_mut()
130 .find(|(iname, _)| *iname == *name)
131 .ok_or_else(|| ScriptError::MalformedScript(format!("no instance named {}", n)))?,
132 })
133 }
134
instance_named( &self, name: &Option<String>, ) -> Result<&lucet_runtime::InstanceHandle, ScriptError>135 pub fn instance_named(
136 &self,
137 name: &Option<String>,
138 ) -> Result<&lucet_runtime::InstanceHandle, ScriptError> {
139 Ok(match name {
140 // None means the last defined module should be used
141 None => self
142 .instances
143 .last()
144 .map(|(_fst, snd)| snd)
145 .ok_or_else(|| ScriptError::MalformedScript("no defined instances".to_owned()))?,
146 Some(ref n) => self
147 .instances
148 .iter()
149 .find(|(iname, _)| *iname == *name)
150 .map(|(_fst, snd)| snd)
151 .ok_or_else(|| ScriptError::MalformedScript(format!("no instance named {}", n)))?,
152 })
153 }
154
run( &mut self, name: &Option<String>, field: &str, args: Vec<Val>, ) -> Result<UntypedRetVal, ScriptError>155 pub fn run(
156 &mut self,
157 name: &Option<String>,
158 field: &str,
159 args: Vec<Val>,
160 ) -> Result<UntypedRetVal, ScriptError> {
161 let (_, ref mut inst) = self.instance_named_mut(name)?;
162 inst.run(field, &args)
163 .and_then(|rr| rr.returned())
164 .map_err(ScriptError::RuntimeError)
165 }
166
register(&mut self, name: &Option<String>, as_name: &str) -> Result<(), ScriptError>167 pub fn register(&mut self, name: &Option<String>, as_name: &str) -> Result<(), ScriptError> {
168 let (ref mut oldname, _) = self.instance_named_mut(name)?;
169 *oldname = Some(as_name.to_owned());
170 Ok(())
171 }
172
delete_last(&mut self)173 pub fn delete_last(&mut self) {
174 let last_index = self.instances.len() - 1;
175 self.instances.remove(last_index);
176 }
177 }
178