diff --git a/Makefile b/Makefile
index 905f924a6..33403699d 100644
--- a/Makefile
+++ b/Makefile
@@ -130,6 +130,15 @@ test-capi: test-capi-singlepass test-capi-cranelift test-capi-llvm test-capi-ems
# Packaging #
#############
+package-wapm:
+ mkdir -p "package/bin"
+ifeq ($(OS), Windows_NT)
+ echo ""
+else
+ echo "#!/bin/bash\nwapm execute \"\$$@\"" > package/bin/wax
+ chmod +x package/bin/wax
+endif
+
package-wasmer:
mkdir -p "package/bin"
ifeq ($(OS), Windows_NT)
@@ -171,7 +180,7 @@ package-docs: build-docs build-docs-capi
echo '' > package/docs/index.html
echo '' > package/docs/crates/index.html
-package: package-wasmer package-capi
+package: package-wapm package-wasmer package-capi
cp LICENSE package/LICENSE
cp ATTRIBUTIONS.md package/ATTRIBUTIONS
mkdir -p dist
diff --git a/README.md b/README.md
index 360c6e490..1a04a8440 100644
--- a/README.md
+++ b/README.md
@@ -8,12 +8,12 @@
-
-
-
+
+
+
-[Wasmer](https://wasmer.io/) is a standalone [WebAssembly](https://webassembly.org/) runtime:
+[Wasmer](https://wasmer.io/) is the fastest and most popular [WebAssembly](https://webassembly.org/) runtime:
* **Universal**: Wasmer is available in *Linux, macOS and Windows* (for both Desktop and [ARM](https://medium.com/wasmer/running-webassembly-on-arm-7d365ed0e50c))
* **Fast**: Wasmer aims to run WebAssembly at near-native speed
* **Pluggable**: Wasmer can be used from almost **any programming language**
diff --git a/lib/api/src/import_object.rs b/lib/api/src/import_object.rs
index 8da2bf766..bdb9fd848 100644
--- a/lib/api/src/import_object.rs
+++ b/lib/api/src/import_object.rs
@@ -184,8 +184,8 @@ impl IntoIterator for ImportObject {
///
/// [`ImportObject`]: struct.ImportObject.html
///
+/// # Usage
///
-/// # Usage:
/// ```
/// # use wasmer::{Function, Store};
/// # let store = Store::default();
@@ -194,7 +194,7 @@ impl IntoIterator for ImportObject {
/// let import_object = imports! {
/// "env" => {
/// "foo" => Function::new(&store, foo)
-/// }
+/// },
/// };
///
/// fn foo(n: i32) -> i32 {
@@ -202,43 +202,45 @@ impl IntoIterator for ImportObject {
/// }
/// ```
#[macro_export]
-// TOOD: port of lost fixes of imports macro from wasmer master/imports macro tests
macro_rules! imports {
- ( $( $ns_name:expr => $ns:tt ),* $(,)? ) => {{
- use $crate::ImportObject;
+ ( $( $ns_name:expr => $ns:tt ),* $(,)? ) => {
+ {
+ let mut import_object = $crate::ImportObject::new();
- let mut import_object = ImportObject::new();
+ $({
+ let namespace = $crate::import_namespace!($ns);
- $({
- let ns = $crate::import_namespace!($ns);
+ import_object.register($ns_name, namespace);
+ })*
- import_object.register($ns_name, ns);
- })*
-
- import_object
- }};
+ import_object
+ }
+ };
}
#[macro_export]
#[doc(hidden)]
macro_rules! namespace {
- ($( $imp_name:expr => $import_item:expr ),* $(,)? ) => {
- $crate::import_namespace!({ $( $imp_name => $import_item, )* })
+ ($( $import_name:expr => $import_item:expr ),* $(,)? ) => {
+ $crate::import_namespace!( { $( $import_name => $import_item, )* } )
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! import_namespace {
- ( { $( $imp_name:expr => $import_item:expr ),* $(,)? } ) => {{
- let mut ns = $crate::Exports::new();
+ ( { $( $import_name:expr => $import_item:expr ),* $(,)? } ) => {{
+ let mut namespace = $crate::Exports::new();
+
$(
- ns.insert($imp_name, $import_item);
+ namespace.insert($import_name, $import_item);
)*
- ns
+
+ namespace
}};
- ($ns:ident) => {
- $ns
+
+ ( $namespace:ident ) => {
+ $namespace
};
}
@@ -354,4 +356,56 @@ mod test {
false
});
}
+
+ #[test]
+ fn imports_macro_allows_trailing_comma_and_none() {
+ use crate::Function;
+
+ let store = Default::default();
+
+ fn func(arg: i32) -> i32 {
+ arg + 1
+ }
+
+ let _ = imports! {
+ "env" => {
+ "func" => Function::new(&store, func),
+ },
+ };
+ let _ = imports! {
+ "env" => {
+ "func" => Function::new(&store, func),
+ }
+ };
+ let _ = imports! {
+ "env" => {
+ "func" => Function::new(&store, func),
+ },
+ "abc" => {
+ "def" => Function::new(&store, func),
+ }
+ };
+ let _ = imports! {
+ "env" => {
+ "func" => Function::new(&store, func)
+ },
+ };
+ let _ = imports! {
+ "env" => {
+ "func" => Function::new(&store, func)
+ }
+ };
+ let _ = imports! {
+ "env" => {
+ "func1" => Function::new(&store, func),
+ "func2" => Function::new(&store, func)
+ }
+ };
+ let _ = imports! {
+ "env" => {
+ "func1" => Function::new(&store, func),
+ "func2" => Function::new(&store, func),
+ }
+ };
+ }
}
diff --git a/lib/compiler-llvm/src/config.rs b/lib/compiler-llvm/src/config.rs
index 8d03c44e3..2d147841a 100644
--- a/lib/compiler-llvm/src/config.rs
+++ b/lib/compiler-llvm/src/config.rs
@@ -69,26 +69,6 @@ impl LLVMConfig {
/// Creates a new configuration object with the default configuration
/// specified.
pub fn new(features: Features, target: Target) -> Self {
- let operating_system =
- if target.triple().operating_system == wasmer_compiler::OperatingSystem::Darwin {
- // LLVM detects static relocation + darwin + 64-bit and
- // force-enables PIC because MachO doesn't support that
- // combination. They don't check whether they're targeting
- // MachO, they check whether the OS is set to Darwin.
- //
- // Since both linux and darwin use SysV ABI, this should work.
- wasmer_compiler::OperatingSystem::Linux
- } else {
- target.triple().operating_system
- };
- let triple = Triple {
- architecture: target.triple().architecture,
- vendor: target.triple().vendor.clone(),
- operating_system,
- environment: target.triple().environment,
- binary_format: target_lexicon::BinaryFormat::Elf,
- };
- let target = Target::new(triple, *target.cpu_features());
Self {
enable_nan_canonicalization: true,
enable_verifier: false,
@@ -112,7 +92,27 @@ impl LLVMConfig {
}
pub fn target_triple(&self) -> TargetTriple {
- TargetTriple::create(&self.target().triple().to_string())
+ let target = self.target();
+ let operating_system =
+ if target.triple().operating_system == wasmer_compiler::OperatingSystem::Darwin {
+ // LLVM detects static relocation + darwin + 64-bit and
+ // force-enables PIC because MachO doesn't support that
+ // combination. They don't check whether they're targeting
+ // MachO, they check whether the OS is set to Darwin.
+ //
+ // Since both linux and darwin use SysV ABI, this should work.
+ wasmer_compiler::OperatingSystem::Linux
+ } else {
+ target.triple().operating_system
+ };
+ let triple = Triple {
+ architecture: target.triple().architecture,
+ vendor: target.triple().vendor.clone(),
+ operating_system,
+ environment: target.triple().environment,
+ binary_format: target_lexicon::BinaryFormat::Elf,
+ };
+ TargetTriple::create(&triple.to_string())
}
/// Generates the target machine for the current target
@@ -149,10 +149,11 @@ impl LLVMConfig {
.map(|feature| format!("+{}", feature.to_string()))
.join(",");
- let llvm_target = InkwellTarget::from_triple(&self.target_triple()).unwrap();
+ let target_triple = self.target_triple();
+ let llvm_target = InkwellTarget::from_triple(&target_triple).unwrap();
llvm_target
.create_target_machine(
- &self.target_triple(),
+ &target_triple,
"generic",
&llvm_cpu_features,
self.opt_level,
diff --git a/lib/engine-native/src/artifact.rs b/lib/engine-native/src/artifact.rs
index c4d3c2666..cb82d8669 100644
--- a/lib/engine-native/src/artifact.rs
+++ b/lib/engine-native/src/artifact.rs
@@ -294,38 +294,38 @@ impl NativeArtifact {
let shared_file = NamedTempFile::new().map_err(to_compile_error)?;
let (_file, shared_filepath) = shared_file.keep().map_err(to_compile_error)?;
- let wasmer_symbols = libcalls
- .iter()
- .map(|libcall| {
- match target_triple.binary_format {
- BinaryFormat::Macho => format!("-Wl,-U,_{}", libcall),
- BinaryFormat::Elf => format!("-Wl,--undefined={}", libcall),
- _ => {
- // We should already be filtering only valid binary formats before
- // so this should never happen.
- unreachable!("Incorrect binary format")
- }
- }
- })
- .collect::>();
- let is_cross_compiling = target_triple != Triple::host();
+ let host_target = Triple::host();
+ let is_cross_compiling = target_triple != host_target;
let cross_compiling_args = if is_cross_compiling {
vec![
format!("--target={}", target_triple),
"-fuse-ld=lld".to_string(),
+ "-nodefaultlibs".to_string(),
+ "-nostdlib".to_string(),
]
} else {
vec![]
};
- let output = Command::new("gcc")
+ trace!(
+ "Compiling for target {} from host {}",
+ target_triple.to_string(),
+ host_target.to_string()
+ );
+
+ let linker = if is_cross_compiling {
+ "clang-10"
+ } else {
+ "gcc"
+ };
+
+ let output = Command::new(linker)
.arg(&filepath)
.arg("-nostartfiles")
- .arg("-nodefaultlibs")
- .arg("-nostdlib")
.arg("-o")
.arg(&shared_filepath)
- .args(&wasmer_symbols)
+ .arg("-Wl,-undefined,dynamic_lookup")
+ // .args(&wasmer_symbols)
.arg("-shared")
.args(&cross_compiling_args)
.arg("-v")
diff --git a/lib/runtime/src/libcalls.rs b/lib/runtime/src/libcalls.rs
index 4a5122c44..09ab8167b 100644
--- a/lib/runtime/src/libcalls.rs
+++ b/lib/runtime/src/libcalls.rs
@@ -405,36 +405,36 @@ pub unsafe extern "C" fn wasmer_probestack() {
/// This list is likely to grow over time.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum LibCall {
+ /// ceil.f32
+ CeilF32,
+
+ /// ceil.f64
+ CeilF64,
+
+ /// floor.f32
+ FloorF32,
+
+ /// floor.f64
+ FloorF64,
+
+ /// nearest.f32
+ NearestF32,
+
+ /// nearest.f64
+ NearestF64,
+
/// probe for stack overflow. These are emitted for functions which need
/// when the `enable_probestack` setting is true.
Probestack,
- /// ceil.f32
- CeilF32,
- /// ceil.f64
- CeilF64,
- /// floor.f32
- FloorF32,
- /// floor.f64
- FloorF64,
- /// trunc.f32
- TruncF32,
- /// frunc.f64
- TruncF64,
- /// nearest.f32
- NearestF32,
- /// nearest.f64
- NearestF64,
+
/// A custom trap
RaiseTrap,
- // /// libc.memcpy
- // Memcpy,
- // /// libc.memset
- // Memset,
- // /// libc.memmove
- // Memmove,
- // /// Elf __tls_get_addr
- // ElfTlsGetAddr,
+ /// trunc.f32
+ TruncF32,
+
+ /// frunc.f64
+ TruncF64,
}
impl LibCall {
@@ -442,16 +442,31 @@ impl LibCall {
pub fn function_pointer(self) -> usize {
match self {
Self::CeilF32 => wasmer_f32_ceil as usize,
- Self::FloorF32 => wasmer_f32_floor as usize,
- Self::TruncF32 => wasmer_f32_trunc as usize,
- Self::NearestF32 => wasmer_f32_nearest as usize,
Self::CeilF64 => wasmer_f64_ceil as usize,
+ Self::FloorF32 => wasmer_f32_floor as usize,
Self::FloorF64 => wasmer_f64_floor as usize,
- Self::TruncF64 => wasmer_f64_trunc as usize,
+ Self::NearestF32 => wasmer_f32_nearest as usize,
Self::NearestF64 => wasmer_f64_nearest as usize,
+ Self::Probestack => wasmer_probestack as usize,
Self::RaiseTrap => wasmer_raise_trap as usize,
- Self::Probestack => PROBESTACK as usize,
- // other => panic!("unexpected libcall: {}", other),
+ Self::TruncF32 => wasmer_f32_trunc as usize,
+ Self::TruncF64 => wasmer_f64_trunc as usize,
+ }
+ }
+
+ /// Return the function name associated to the libcall.
+ pub fn to_function_name(&self) -> &str {
+ match self {
+ Self::CeilF32 => "wasmer_f32_ceil",
+ Self::CeilF64 => "wasmer_f64_ceil",
+ Self::FloorF32 => "wasmer_f32_floor",
+ Self::FloorF64 => "wasmer_f64_floor",
+ Self::NearestF32 => "wasmer_f32_nearest",
+ Self::NearestF64 => "wasmer_f64_nearest",
+ Self::Probestack => "wasmer_probestack",
+ Self::RaiseTrap => "wasmer_raise_trap",
+ Self::TruncF32 => "wasmer_f32_trunc",
+ Self::TruncF64 => "wasmer_f64_trunc",
}
}
}
diff --git a/lib/wasi/src/lib.rs b/lib/wasi/src/lib.rs
index 41ea47b82..f3def12ab 100644
--- a/lib/wasi/src/lib.rs
+++ b/lib/wasi/src/lib.rs
@@ -26,7 +26,7 @@ pub use crate::syscalls::types;
pub use crate::utils::{get_wasi_version, is_wasi_module, WasiVersion};
use thiserror::Error;
-use wasmer::{imports, Function, ImportObject, Memory, Store};
+use wasmer::{imports, Function, ImportObject, Memory, Module, Store};
/// This is returned in `RuntimeError`.
/// Use `downcast` or `downcast_ref` to retrieve the `ExitCode`.
@@ -34,6 +34,8 @@ use wasmer::{imports, Function, ImportObject, Memory, Store};
pub enum WasiError {
#[error("WASI exited with code: {0}")]
Exit(syscalls::types::__wasi_exitcode_t),
+ #[error("The WASI version could not be determined")]
+ UnknownWasiVersion,
}
/// The environment provided to the WASI imports.
@@ -51,6 +53,15 @@ impl<'a> WasiEnv<'a> {
}
}
+ pub fn import_object(&mut self, module: &Module) -> Result {
+ let wasi_version = get_wasi_version(module, false).ok_or(WasiError::UnknownWasiVersion)?;
+ Ok(generate_import_object_from_env(
+ module.store(),
+ self,
+ wasi_version,
+ ))
+ }
+
/// Set the state
pub fn set_memory(&mut self, memory: &'a Memory) {
self.memory = Some(memory);
diff --git a/lib/wasi/src/state/builder.rs b/lib/wasi/src/state/builder.rs
index 8a05023ba..d9403ca41 100644
--- a/lib/wasi/src/state/builder.rs
+++ b/lib/wasi/src/state/builder.rs
@@ -2,6 +2,7 @@
use crate::state::{WasiFile, WasiFs, WasiFsError, WasiState};
use crate::syscalls::types::{__WASI_STDERR_FILENO, __WASI_STDIN_FILENO, __WASI_STDOUT_FILENO};
+use crate::WasiEnv;
use std::path::{Path, PathBuf};
use thiserror::Error;
@@ -388,6 +389,14 @@ impl WasiStateBuilder {
envs: self.envs.clone(),
})
}
+
+ /// Consumes the [`WasiStateBuilder`] and produces a [`WasiEnv`]
+ ///
+ /// Returns the error from `WasiFs::new` if there's an error
+ pub fn finalize(&mut self) -> Result {
+ let state = self.build()?;
+ Ok(WasiEnv::new(state))
+ }
}
/// Builder for preopened directories.
diff --git a/lib/wasi/src/state/mod.rs b/lib/wasi/src/state/mod.rs
index 9416f282b..4ae2ef03a 100644
--- a/lib/wasi/src/state/mod.rs
+++ b/lib/wasi/src/state/mod.rs
@@ -1481,8 +1481,8 @@ impl WasiState {
/// Create a [`WasiStateBuilder`] to construct a validated instance of
/// [`WasiState`].
#[allow(clippy::new_ret_no_self)]
- pub fn new(program_name: &str) -> WasiStateBuilder {
- create_wasi_state(program_name)
+ pub fn new(program_name: impl AsRef) -> WasiStateBuilder {
+ create_wasi_state(program_name.as_ref())
}
/// Turn the WasiState into bytes
diff --git a/src/commands/run.rs b/src/commands/run.rs
index ce2a2763e..059002cf8 100644
--- a/src/commands/run.rs
+++ b/src/commands/run.rs
@@ -109,7 +109,7 @@ impl Run {
#[cfg(feature = "wasi")]
{
let wasi_version = Wasi::get_version(&module);
- if let Some(version) = wasi_version {
+ if wasi_version.is_some() {
let program_name = self
.command_name
.clone()
@@ -121,7 +121,7 @@ impl Run {
.unwrap_or_default();
return self
.wasi
- .execute(module, version, program_name, self.args.clone())
+ .execute(module, program_name, self.args.clone())
.with_context(|| "WASI execution failed");
}
}
diff --git a/src/commands/run/wasi.rs b/src/commands/run/wasi.rs
index cea6ddb64..e2449498c 100644
--- a/src/commands/run/wasi.rs
+++ b/src/commands/run/wasi.rs
@@ -1,10 +1,8 @@
+use crate::utils::{parse_envvar, parse_mapdir};
use anyhow::{Context, Result};
use std::path::PathBuf;
-
-use wasmer::{Function, Instance, Memory, Module};
-use wasmer_wasi::{
- generate_import_object_from_env, get_wasi_version, WasiEnv, WasiState, WasiVersion,
-};
+use wasmer::{Instance, Module};
+use wasmer_wasi::{get_wasi_version, WasiState, WasiVersion};
use structopt::StructOpt;
@@ -12,16 +10,16 @@ use structopt::StructOpt;
/// WASI Options
pub struct Wasi {
/// WASI pre-opened directory
- #[structopt(long = "dir", multiple = true, group = "wasi")]
+ #[structopt(long = "dir", name = "DIR", multiple = true, group = "wasi")]
pre_opened_directories: Vec,
/// Map a host directory to a different location for the wasm module
- #[structopt(long = "mapdir", multiple = true)]
- mapped_dirs: Vec,
+ #[structopt(long = "mapdir", name = "GUEST_DIR:HOST_DIR", multiple = true, parse(try_from_str = parse_mapdir))]
+ mapped_dirs: Vec<(String, PathBuf)>,
/// Pass custom environment variables
- #[structopt(long = "env", multiple = true)]
- env_vars: Vec,
+ #[structopt(long = "env", name = "KEY=VALUE", multiple = true, parse(try_from_str = parse_envvar))]
+ env_vars: Vec<(String, String)>,
/// Enable experimental IO devices
#[cfg(feature = "experimental-io-devices")]
@@ -30,44 +28,6 @@ pub struct Wasi {
}
impl Wasi {
- fn get_mapped_dirs(&self) -> Result> {
- let mut md = vec![];
- for entry in self.mapped_dirs.iter() {
- if let [alias, real_dir] = entry.split(':').collect::>()[..] {
- let pb = PathBuf::from(&real_dir);
- if let Ok(pb_metadata) = pb.metadata() {
- if !pb_metadata.is_dir() {
- bail!("\"{}\" exists, but it is not a directory", &real_dir);
- }
- } else {
- bail!("Directory \"{}\" does not exist", &real_dir);
- }
- md.push((alias.to_string(), pb));
- continue;
- }
- bail!(
- "Directory mappings must consist of two paths separate by a colon. Found {}",
- &entry
- );
- }
- Ok(md)
- }
-
- fn get_env_vars(&self) -> Result> {
- let mut env = vec![];
- for entry in self.env_vars.iter() {
- if let [env_var, value] = entry.split('=').collect::>()[..] {
- env.push((env_var, value));
- } else {
- bail!(
- "Env vars must be of the form =. Found {}",
- &entry
- );
- }
- }
- Ok(env)
- }
-
/// Gets the WASI version (if any) for the provided module
pub fn get_version(module: &Module) -> Option {
// Get the wasi version on strict mode, so no other imports are
@@ -76,25 +36,15 @@ impl Wasi {
}
/// Helper function for executing Wasi from the `Run` command.
- pub fn execute(
- &self,
- module: Module,
- wasi_version: WasiVersion,
- program_name: String,
- args: Vec,
- ) -> Result<()> {
- let mut wasi_state_builder = WasiState::new(&program_name);
-
+ pub fn execute(&self, module: Module, program_name: String, args: Vec) -> Result<()> {
let args = args.iter().cloned().map(|arg| arg.into_bytes());
- let env_vars = self.get_env_vars()?;
- let preopened_files = self.pre_opened_directories.clone();
- let mapped_dirs = self.get_mapped_dirs()?;
+ let mut wasi_state_builder = WasiState::new(program_name);
wasi_state_builder
.args(args)
- .envs(env_vars)
- .preopen_dirs(preopened_files)?
- .map_dirs(mapped_dirs)?;
+ .envs(self.env_vars.clone())
+ .preopen_dirs(self.pre_opened_directories.clone())?
+ .map_dirs(self.mapped_dirs.clone())?;
#[cfg(feature = "experimental-io-devices")]
{
@@ -104,18 +54,13 @@ impl Wasi {
}
}
- let wasi_state = wasi_state_builder.build()?;
- let mut wasi_env = WasiEnv::new(wasi_state);
- let import_object =
- generate_import_object_from_env(module.store(), &mut wasi_env, wasi_version);
-
+ let mut wasi_env = wasi_state_builder.finalize()?;
+ let import_object = wasi_env.import_object(&module)?;
let instance = Instance::new(&module, &import_object)?;
- let memory: &Memory = instance.exports.get("memory")?;
- wasi_env.set_memory(memory);
-
- let start: &Function = instance.exports.get("_start")?;
+ wasi_env.set_memory(instance.exports.get_memory("memory")?);
+ let start = instance.exports.get_function("_start")?;
start
.call(&[])
.with_context(|| "failed to run WASI `_start` function")?;
diff --git a/src/utils.rs b/src/utils.rs
index 6a0fd056e..697caffad 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -1,5 +1,7 @@
//! Utility functions for the WebAssembly module
+use anyhow::{bail, Result};
use std::env;
+use std::path::PathBuf;
/// Whether or not Wasmer should print with color
pub fn wasmer_should_print_color() -> bool {
@@ -8,3 +10,34 @@ pub fn wasmer_should_print_color() -> bool {
.and_then(|inner| inner.parse::().ok())
.unwrap_or_else(|| atty::is(atty::Stream::Stdout))
}
+
+/// Parses a mapdir from a string
+pub fn parse_mapdir(entry: &str) -> Result<(String, PathBuf)> {
+ if let [alias, real_dir] = entry.split(':').collect::>()[..] {
+ let pb = PathBuf::from(&real_dir);
+ if let Ok(pb_metadata) = pb.metadata() {
+ if !pb_metadata.is_dir() {
+ bail!("\"{}\" exists, but it is not a directory", &real_dir);
+ }
+ } else {
+ bail!("Directory \"{}\" does not exist", &real_dir);
+ }
+ return Ok((alias.to_string(), pb));
+ }
+ bail!(
+ "Directory mappings must consist of two paths separate by a colon. Found {}",
+ &entry
+ )
+}
+
+/// Parses a mapdir from an env var
+pub fn parse_envvar(entry: &str) -> Result<(String, String)> {
+ if let [env_var, value] = entry.split('=').collect::>()[..] {
+ return Ok((env_var.to_string(), value.to_string()));
+ } else {
+ bail!(
+ "Env vars must be of the form =. Found {}",
+ &entry
+ );
+ }
+}