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