Merge branch 'master' into cranelift-update

This commit is contained in:
Syrus
2020-06-12 04:43:11 -07:00
12 changed files with 253 additions and 180 deletions

View File

@@ -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 '<!-- Build $(SOURCE_VERSION) --><meta http-equiv="refresh" content="0; url=rust/wasmer_runtime/index.html">' > package/docs/index.html
echo '<!-- Build $(SOURCE_VERSION) --><meta http-equiv="refresh" content="0; url=wasmer_runtime/index.html">' > 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

View File

@@ -8,12 +8,12 @@
<a href="https://dev.azure.com/wasmerio/wasmer/_build/latest?definitionId=3&branchName=master">
<img src="https://img.shields.io/azure-devops/build/wasmerio/wasmer/3.svg?style=flat-square" alt="Build Status">
</a>
<a href="https://slack.wasmer.io">
<img src="https://img.shields.io/static/v1?label=Slack&message=join%20chat&color=brighgreen&style=flat-square" alt="Slack channel">
</a>
<a href="https://github.com/wasmerio/wasmer/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/wasmerio/wasmer.svg?style=flat-square" alt="License">
</a>
<a href="https://slack.wasmer.io">
<img src="https://img.shields.io/static/v1?label=Slack&message=join%20chat&color=brighgreen&style=flat-square" alt="Slack channel">
</a>
</p>
<h3>
@@ -21,18 +21,14 @@
<span></span>
<a href="https://docs.wasmer.io">Docs</a>
<span></span>
<a href="https://medium.com/wasmer/">Blog</a>
<span></span>
<a href="https://slack.wasmer.io/">Slack</a>
<span></span>
<a href="https://twitter.com/wasmerio">Twitter</a>
<a href="https://slack.wasmer.io/">Chat</a>
</h3>
</div>
<br />
[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**

View File

@@ -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;
let mut import_object = ImportObject::new();
( $( $ns_name:expr => $ns:tt ),* $(,)? ) => {
{
let mut import_object = $crate::ImportObject::new();
$({
let ns = $crate::import_namespace!($ns);
let namespace = $crate::import_namespace!($ns);
import_object.register($ns_name, ns);
import_object.register($ns_name, namespace);
})*
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),
}
};
}
}

View File

@@ -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,

View File

@@ -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::<Vec<String>>();
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")

View File

@@ -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",
}
}
}

View File

@@ -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<ImportObject, WasiError> {
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);

View File

@@ -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<WasiEnv, WasiStateCreationError> {
let state = self.build()?;
Ok(WasiEnv::new(state))
}
}
/// Builder for preopened directories.

View File

@@ -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<str>) -> WasiStateBuilder {
create_wasi_state(program_name.as_ref())
}
/// Turn the WasiState into bytes

View File

@@ -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");
}
}

View File

@@ -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<PathBuf>,
/// Map a host directory to a different location for the wasm module
#[structopt(long = "mapdir", multiple = true)]
mapped_dirs: Vec<String>,
#[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<String>,
#[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<Vec<(String, PathBuf)>> {
let mut md = vec![];
for entry in self.mapped_dirs.iter() {
if let [alias, real_dir] = entry.split(':').collect::<Vec<&str>>()[..] {
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<Vec<(&str, &str)>> {
let mut env = vec![];
for entry in self.env_vars.iter() {
if let [env_var, value] = entry.split('=').collect::<Vec<&str>>()[..] {
env.push((env_var, value));
} else {
bail!(
"Env vars must be of the form <var_name>=<value>. Found {}",
&entry
);
}
}
Ok(env)
}
/// Gets the WASI version (if any) for the provided module
pub fn get_version(module: &Module) -> Option<WasiVersion> {
// 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<String>,
) -> Result<()> {
let mut wasi_state_builder = WasiState::new(&program_name);
pub fn execute(&self, module: Module, program_name: String, args: Vec<String>) -> 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")?;

View File

@@ -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::<bool>().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::<Vec<&str>>()[..] {
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::<Vec<&str>>()[..] {
return Ok((env_var.to_string(), value.to_string()));
} else {
bail!(
"Env vars must be of the form <var_name>=<value>. Found {}",
&entry
);
}
}