Merge branch 'master' into fix/llvm-trap-windows

This commit is contained in:
Syrus Akbary 2019-08-09 00:40:10 -07:00 committed by GitHub
commit 5f3df4dd7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 605 additions and 122 deletions

View File

@ -9,6 +9,7 @@ Blocks of changes will separated by version increments.
Special thanks to @YaronWittenstein @penberg for their contributions.
- [#636](https://github.com/wasmerio/wasmer/issues/636) Fix traps with LLVM backend in Windows
- [#643](https://github.com/wasmerio/wasmer/issues/643) Implement `wasi::path_symlink` and improve WASI FS public api IO error reporting
- [#608](https://github.com/wasmerio/wasmer/issues/608) Implement wasi syscalls `fd_allocate`, `fd_sync`, `fd_pread`, `path_link`, `path_filestat_set_times`; update WASI fs API in a WIP way; reduce coupling of WASI code to host filesystem; make debug messages from WASI more readable; improve rights-checking when calling syscalls; implement reference counting on inodes; misc bug fixes and improvements
- [#616](https://github.com/wasmerio/wasmer/issues/616) Create the import object separately from instance instantiation in `runtime-c-api`
- [#620](https://github.com/wasmerio/wasmer/issues/620) Replace one `throw()` with `noexcept` in llvm backend

View File

@ -33,7 +33,7 @@ use wasmer_runtime_core::{
use wasmparser::Type as WpType;
pub struct CraneliftModuleCodeGenerator {
isa: Box<isa::TargetIsa>,
isa: Box<dyn isa::TargetIsa>,
signatures: Option<Arc<Map<SigIndex, FuncSig>>>,
pub clif_signatures: Map<SigIndex, ir::Signature>,
function_signatures: Option<Arc<Map<FuncIndex, SigIndex>>>,

View File

@ -26,7 +26,7 @@ extern crate serde_derive;
extern crate rayon;
extern crate serde;
fn get_isa() -> Box<isa::TargetIsa> {
fn get_isa() -> Box<dyn isa::TargetIsa> {
let flags = {
let mut builder = settings::builder();
builder.set("opt_level", "best").unwrap();

View File

@ -88,7 +88,7 @@ impl FuncResolverBuilder {
}
pub fn new(
isa: &isa::TargetIsa,
isa: &dyn isa::TargetIsa,
function_bodies: Map<LocalFuncIndex, ir::Function>,
info: &ModuleInfo,
) -> CompileResult<(Self, HandlerData)> {

View File

@ -66,7 +66,7 @@ impl Trampolines {
}
}
pub fn new(isa: &isa::TargetIsa, module: &ModuleInfo) -> Self {
pub fn new(isa: &dyn isa::TargetIsa, module: &ModuleInfo) -> Self {
let func_index_iter = module
.exports
.values()

View File

@ -11,7 +11,6 @@ wasmparser = "0.35.1"
smallvec = "0.6.10"
goblin = "0.0.24"
libc = "0.2.60"
nix = "0.14.1"
capstone = { version = "0.6.0", optional = true }
[dependencies.inkwell]
@ -20,6 +19,9 @@ branch = "llvm8-0"
default-features = false
features = ["llvm8-0", "target-x86"]
[target.'cfg(unix)'.dependencies]
nix = "0.14.1"
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.7", features = ["memoryapi"] }

View File

@ -1,5 +1,5 @@
//! This file was mostly taken from the llvm-sys crate.
//! (https://bitbucket.org/tari/llvm-sys.rs/src/21ab524ec4df1450035df895209c3f8fbeb8775f/build.rs?at=default&fileviewer=file-view-default)
//! (https://bitbucket.org/tari/llvm-sys.rs/raw/94361c1083a88f439b9d24c59b2d2831517413d7/build.rs)
use lazy_static::lazy_static;
use regex::Regex;
@ -10,23 +10,54 @@ use std::io::{self, ErrorKind};
use std::path::PathBuf;
use std::process::Command;
// Version of the llvm-sys crate that we (through inkwell) depend on.
const LLVM_SYS_MAJOR_VERSION: &str = "80";
const LLVM_SYS_MINOR_VERSION: &str = "0";
// Environment variables that can guide compilation
//
// When adding new ones, they should also be added to main() to force a
// rebuild if they are changed.
lazy_static! {
/// A single path to search for LLVM in (containing bin/llvm-config)
static ref ENV_LLVM_PREFIX: String =
format!("LLVM_SYS_{}_PREFIX", LLVM_SYS_MAJOR_VERSION);
/// If exactly "YES", ignore the version blacklist
static ref ENV_IGNORE_BLACKLIST: String =
format!("LLVM_SYS_{}_IGNORE_BLACKLIST", LLVM_SYS_MAJOR_VERSION);
/// If set, enforce precise correspondence between crate and binary versions.
static ref ENV_STRICT_VERSIONING: String =
format!("LLVM_SYS_{}_STRICT_VERSIONING", LLVM_SYS_MAJOR_VERSION);
/// If set, do not attempt to strip irrelevant options for llvm-config --cflags
static ref ENV_NO_CLEAN_CXXFLAGS: String =
format!("LLVM_SYS_{}_NO_CLEAN_CXXFLAGS", LLVM_SYS_MAJOR_VERSION);
/// If set and targeting MSVC, force the debug runtime library
static ref ENV_USE_DEBUG_MSVCRT: String =
format!("LLVM_SYS_{}_USE_DEBUG_MSVCRT", LLVM_SYS_MAJOR_VERSION);
/// If set, always link against libffi
static ref ENV_FORCE_FFI: String =
format!("LLVM_SYS_{}_FFI_WORKAROUND", LLVM_SYS_MAJOR_VERSION);
}
lazy_static! {
/// LLVM version used by this version of the crate.
static ref CRATE_VERSION: Version = {
let crate_version = Version::parse(env!("CARGO_PKG_VERSION"))
.expect("Crate version is somehow not valid semver");
Version {
major: crate_version.major / 10,
minor: crate_version.major % 10,
.. crate_version
}
Version::new(LLVM_SYS_MAJOR_VERSION.parse::<u64>().unwrap() / 10,
LLVM_SYS_MINOR_VERSION.parse::<u64>().unwrap() % 10,
0)
};
static ref LLVM_CONFIG_BINARY_NAMES: Vec<String> = {
vec![
"llvm-config".into(),
// format!("llvm-config-{}", CRATE_VERSION.major),
// format!("llvm-config-{}.{}", CRATE_VERSION.major, CRATE_VERSION.minor),
format!("llvm-config-{}", CRATE_VERSION.major),
format!("llvm-config-{}.{}", CRATE_VERSION.major, CRATE_VERSION.minor),
]
};
@ -41,21 +72,7 @@ lazy_static! {
// Did the user give us a binary path to use? If yes, try
// to use that and fail if it doesn't work.
let binary_prefix_var = "LLVM_SYS_80_PREFIX";
let path = if let Some(path) = env::var_os(&binary_prefix_var) {
Some(path.to_str().unwrap().to_owned())
} else if let Ok(mut file) = std::fs::File::open(".llvmenv") {
use std::io::Read;
let mut s = String::new();
file.read_to_string(&mut s).unwrap();
s.truncate(s.len() - 4);
Some(s)
} else {
None
};
if let Some(path) = path {
if let Some(path) = env::var_os(&*ENV_LLVM_PREFIX) {
for binary_name in LLVM_CONFIG_BINARY_NAMES.iter() {
let mut pb: PathBuf = path.clone().into();
pb.push("bin");
@ -67,7 +84,7 @@ lazy_static! {
return pb;
} else {
println!("LLVM binaries specified by {} are the wrong version.
(Found {}, need {}.)", binary_prefix_var, ver, *CRATE_VERSION);
(Found {}, need {}.)", *ENV_LLVM_PREFIX, ver, *CRATE_VERSION);
}
}
}
@ -79,7 +96,7 @@ lazy_static! {
refer to the llvm-sys documentation for more information.
llvm-sys: https://crates.io/crates/llvm-sys
llvmenv: https://crates.io/crates/llvmenv", binary_prefix_var);
llvmenv: https://crates.io/crates/llvmenv", *ENV_LLVM_PREFIX);
panic!("Could not find a compatible version of LLVM");
};
}
@ -115,15 +132,55 @@ fn locate_system_llvm_config() -> Option<&'static str> {
None
}
/// Check whether the given version of LLVM is blacklisted,
/// returning `Some(reason)` if it is.
fn is_blacklisted_llvm(llvm_version: &Version) -> Option<&'static str> {
static BLACKLIST: &'static [(u64, u64, u64, &'static str)] = &[];
if let Some(x) = env::var_os(&*ENV_IGNORE_BLACKLIST) {
if &x == "YES" {
println!(
"cargo:warning=Ignoring blacklist entry for LLVM {}",
llvm_version
);
return None;
} else {
println!(
"cargo:warning={} is set but not exactly \"YES\"; blacklist is still honored.",
*ENV_IGNORE_BLACKLIST
);
}
}
for &(major, minor, patch, reason) in BLACKLIST.iter() {
let bad_version = Version {
major: major,
minor: minor,
patch: patch,
pre: vec![],
build: vec![],
};
if &bad_version == llvm_version {
return Some(reason);
}
}
None
}
/// Check whether the given LLVM version is compatible with this version of
/// the crate.
fn is_compatible_llvm(llvm_version: &Version) -> bool {
let strict = env::var_os(format!(
"LLVM_SYS_{}_STRICT_VERSIONING",
env!("CARGO_PKG_VERSION_MAJOR")
))
.is_some()
|| cfg!(feature = "strict-versioning");
if let Some(reason) = is_blacklisted_llvm(llvm_version) {
println!(
"Found LLVM {}, which is blacklisted: {}",
llvm_version, reason
);
return false;
}
let strict =
env::var_os(&*ENV_STRICT_VERSIONING).is_some() || cfg!(feature = "strict-versioning");
if strict {
llvm_version.major == CRATE_VERSION.major && llvm_version.minor == CRATE_VERSION.minor
} else {
@ -184,11 +241,7 @@ fn get_llvm_cxxflags() -> String {
// include flags that aren't understood by the default compiler we're
// using. Unless requested otherwise, clean CFLAGS of options that are
// known to be possibly-harmful.
let no_clean = env::var_os(format!(
"LLVM_SYS_{}_NO_CLEAN_CFLAGS",
env!("CARGO_PKG_VERSION_MAJOR")
))
.is_some();
let no_clean = env::var_os(&*ENV_NO_CLEAN_CXXFLAGS).is_some();
if no_clean || cfg!(target_env = "msvc") {
// MSVC doesn't accept -W... options, so don't try to strip them and
// possibly strip something that should be retained. Also do nothing if
@ -204,20 +257,43 @@ fn get_llvm_cxxflags() -> String {
.join(" ")
}
fn is_llvm_debug() -> bool {
// Has to be either Debug or Release
llvm_config("--build-mode").contains("Debug")
}
fn main() {
println!("cargo:rustc-link-lib=static=llvm-backend");
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=cpp/object_loader.cpp");
println!("cargo:rerun-if-changed=cpp/object_loader.hh");
println!("cargo:rerun-if-env-changed={}", &*ENV_LLVM_PREFIX);
println!("cargo:rerun-if-env-changed={}", &*ENV_IGNORE_BLACKLIST);
println!("cargo:rerun-if-env-changed={}", &*ENV_STRICT_VERSIONING);
println!("cargo:rerun-if-env-changed={}", &*ENV_NO_CLEAN_CXXFLAGS);
println!("cargo:rerun-if-env-changed={}", &*ENV_USE_DEBUG_MSVCRT);
println!("cargo:rerun-if-env-changed={}", &*ENV_FORCE_FFI);
std::env::set_var("CXXFLAGS", get_llvm_cxxflags());
cc::Build::new()
.cpp(true)
.file("cpp/object_loader.cpp")
.compile("llvm-backend");
println!("cargo:rustc-link-lib=static=llvm-backend");
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=cpp/object_loader.cpp");
println!("cargo:rerun-if-changed=cpp/object_loader.hh");
// Enable "nightly" cfg if the current compiler is nightly.
if rustc_version::version_meta().unwrap().channel == rustc_version::Channel::Nightly {
println!("cargo:rustc-cfg=nightly");
}
let use_debug_msvcrt = env::var_os(&*ENV_USE_DEBUG_MSVCRT).is_some();
if cfg!(target_env = "msvc") && (use_debug_msvcrt || is_llvm_debug()) {
println!("cargo:rustc-link-lib={}", "msvcrtd");
}
// Link libffi if the user requested this workaround.
// See https://bitbucket.org/tari/llvm-sys.rs/issues/12/
let force_ffi = env::var_os(&*ENV_FORCE_FFI).is_some();
if force_ffi {
println!("cargo:rustc-link-lib=dylib={}", "ffi");
}
}

View File

@ -10,6 +10,8 @@ use libc::c_char;
use std::{
any::Any,
ffi::{c_void, CString},
fs::File,
io::Write,
mem,
ops::Deref,
ptr::{self, NonNull},
@ -177,6 +179,14 @@ impl LLVMBackend {
.unwrap();
let mem_buf_slice = memory_buffer.as_slice();
if let Some(path) = unsafe { &crate::GLOBAL_OPTIONS.obj_file } {
let mut file = File::create(path).unwrap();
let mut pos = 0;
while pos < mem_buf_slice.len() {
pos += file.write(&mem_buf_slice[pos..]).unwrap();
}
}
let callbacks = get_callbacks();
let mut module: *mut LLVMModule = ptr::null_mut();

View File

@ -4639,6 +4639,10 @@ impl ModuleCodeGenerator<LLVMFunctionCodeGenerator, LLVMBackend, CodegenError>
self.intrinsics.as_ref().unwrap(),
);
if let Some(path) = unsafe { &crate::GLOBAL_OPTIONS.pre_opt_ir } {
self.module.print_to_file(path).unwrap();
}
let pass_manager = PassManager::create(());
if cfg!(test) {
pass_manager.add_verifier_pass();
@ -4658,7 +4662,9 @@ impl ModuleCodeGenerator<LLVMFunctionCodeGenerator, LLVMBackend, CodegenError>
pass_manager.add_slp_vectorize_pass();
pass_manager.run_on(&self.module);
// self.module.print_to_stderr();
if let Some(path) = unsafe { &crate::GLOBAL_OPTIONS.post_opt_ir } {
self.module.print_to_file(path).unwrap();
}
let (backend, cache_gen) = LLVMBackend::new(self.module, self.intrinsics.take().unwrap());
Ok((backend, Box::new(cache_gen)))

View File

@ -16,6 +16,8 @@ mod state;
mod structs;
mod trampolines;
use std::path::PathBuf;
pub use code::LLVMFunctionCodeGenerator as FunctionCodeGenerator;
pub use code::LLVMModuleCodeGenerator as ModuleCodeGenerator;
@ -27,3 +29,22 @@ pub type LLVMCompiler = SimpleStreamingCompilerGen<
backend::LLVMBackend,
code::CodegenError,
>;
#[derive(Debug, Clone)]
/// LLVM backend flags.
pub struct LLVMOptions {
/// Emit LLVM IR before optimization pipeline.
pub pre_opt_ir: Option<PathBuf>,
/// Emit LLVM IR after optimization pipeline.
pub post_opt_ir: Option<PathBuf>,
/// Emit LLVM generated native code object file.
pub obj_file: Option<PathBuf>,
}
pub static mut GLOBAL_OPTIONS: LLVMOptions = LLVMOptions {
pre_opt_ir: None,
post_opt_ir: None,
obj_file: None,
};

View File

@ -18,7 +18,7 @@ use wasmparser::{self, WasmDecoder};
use wasmparser::{Operator, Type as WpType};
pub type BreakpointHandler =
Box<Fn(BreakpointInfo) -> Result<(), Box<dyn Any>> + Send + Sync + 'static>;
Box<dyn Fn(BreakpointInfo) -> Result<(), Box<dyn Any>> + Send + Sync + 'static>;
pub type BreakpointMap = Arc<HashMap<usize, BreakpointHandler>>;
#[derive(Debug)]
@ -232,7 +232,7 @@ impl<'a, 'b> EventSink<'a, 'b> {
}
pub struct MiddlewareChain {
chain: Vec<Box<GenericFunctionMiddleware>>,
chain: Vec<Box<dyn GenericFunctionMiddleware>>,
}
impl MiddlewareChain {

View File

@ -35,7 +35,7 @@ type SetJmpBuffer = [i32; SETJMP_BUFFER_LEN];
struct UnwindInfo {
jmpbuf: SetJmpBuffer, // in
breakpoints: Option<BreakpointMap>,
payload: Option<Box<Any>>, // out
payload: Option<Box<dyn Any>>, // out
}
thread_local! {
@ -89,7 +89,7 @@ pub unsafe fn clear_wasm_interrupt() {
pub unsafe fn catch_unsafe_unwind<R, F: FnOnce() -> R>(
f: F,
breakpoints: Option<BreakpointMap>,
) -> Result<R, Box<Any>> {
) -> Result<R, Box<dyn Any>> {
let unwind = UNWIND.with(|x| x.get());
let old = (*unwind).take();
*unwind = Some(UnwindInfo {
@ -111,7 +111,7 @@ pub unsafe fn catch_unsafe_unwind<R, F: FnOnce() -> R>(
}
}
pub unsafe fn begin_unsafe_unwind(e: Box<Any>) -> ! {
pub unsafe fn begin_unsafe_unwind(e: Box<dyn Any>) -> ! {
let unwind = UNWIND.with(|x| x.get());
let inner = (*unwind)
.as_mut()

View File

@ -46,7 +46,7 @@ impl IsExport for Export {
/// ```
pub struct ImportObject {
map: Rc<RefCell<HashMap<String, Box<dyn LikeNamespace>>>>,
pub(crate) state_creator: Option<Rc<Fn() -> (*mut c_void, fn(*mut c_void))>>,
pub(crate) state_creator: Option<Rc<dyn Fn() -> (*mut c_void, fn(*mut c_void))>>,
pub allow_missing_functions: bool,
}

View File

@ -763,7 +763,7 @@ mod vm_ctx_tests {
x: u32,
y: bool,
str: String,
finalizer: Box<FnMut()>,
finalizer: Box<dyn FnMut()>,
}
impl Drop for TestData {

View File

@ -148,7 +148,7 @@ pub struct X64FunctionCode {
breakpoints: Option<
HashMap<
AssemblyOffset,
Box<Fn(BreakpointInfo) -> Result<(), Box<dyn Any>> + Send + Sync + 'static>,
Box<dyn Fn(BreakpointInfo) -> Result<(), Box<dyn Any>> + Send + Sync + 'static>,
>,
>,
returns: SmallVec<[WpType; 1]>,
@ -294,7 +294,7 @@ impl RunnableModule for X64ExecutionContext {
})
}
unsafe fn do_early_trap(&self, data: Box<Any>) -> ! {
unsafe fn do_early_trap(&self, data: Box<dyn Any>) -> ! {
protect_unix::TRAP_EARLY_DATA.with(|x| x.set(Some(data)));
protect_unix::trigger_trap();
}

View File

@ -21,6 +21,7 @@ clif:skip:elem.wast:229 # Spec running forever
clif:skip:names.wast:* # Names file has parsing error?
clif:skip:simd.wast:* # SIMD not implemented
clif:skip:simd_binaryen.wast:* # SIMD not implemented
clif:fail:binary-leb128.wast:74 # Module - caught panic Any
clif:fail:binary-leb128.wast:86 # Module - caught panic Any
clif:fail:binary-leb128.wast:98 # Module - caught panic Any
@ -39,19 +40,11 @@ clif:fail:data.wast:137 # Module - caught panic Any
clif:fail:data.wast:143 # Module - caught panic Any
clif:fail:data.wast:149 # Module - caught panic Any
clif:fail:data.wast:154 # Module - caught panic Any
clif:fail:data.wast:162 # AssertUnlinkable - caught panic Any
clif:fail:data.wast:170 # AssertUnlinkable - caught panic Any
clif:fail:data.wast:178 # AssertUnlinkable - caught panic Any
clif:fail:data.wast:186 # AssertUnlinkable - instantiate successful, expected unlinkable
clif:fail:data.wast:194 # AssertUnlinkable - instantiate successful, expected unlinkable
clif:fail:data.wast:211 # AssertUnlinkable - caught panic Any
clif:fail:data.wast:220 # AssertUnlinkable - caught panic Any
clif:fail:data.wast:227 # AssertUnlinkable - caught panic Any
clif:fail:data.wast:235 # AssertUnlinkable - caught panic Any
clif:fail:data.wast:243 # AssertUnlinkable - caught panic Any
clif:fail:data.wast:251 # AssertUnlinkable - caught panic Any
clif:fail:data.wast:258 # AssertUnlinkable - caught panic Any
clif:fail:data.wast:266 # AssertUnlinkable - caught panic Any
clif:fail:data.wast:273 # AssertUnlinkable - caught panic Any
clif:fail:elem.wast:101 # Module - caught panic Any
clif:fail:elem.wast:143 # AssertUnlinkable - instantiate successful, expected unlinkable
@ -162,20 +155,20 @@ clif:fail:linking.wast:81 # AssertReturn Get - Expected Global I32(241) got: I32
clif:fail:linking.wast:83 # AssertReturn - result I32(142) ("0x8e") does not match expected I32(241) ("0xf1")
clif:fail:linking.wast:87 # AssertUnlinkable - caught panic Any
clif:fail:linking.wast:91 # AssertUnlinkable - caught panic Any
clif:fail:linking.wast:137 # AssertTrap - expected trap, got Runtime:Error "unknown trap at 0x106ea0062 - illegal instruction"
clif:fail:linking.wast:139 # AssertTrap - expected trap, got Runtime:Error "unknown trap at 0x106ea0062 - illegal instruction"
clif:fail:linking.wast:142 # AssertTrap - expected trap, got Runtime:Error "unknown trap at 0x106ea0062 - illegal instruction"
clif:fail:linking.wast:144 # AssertTrap - expected trap, got Runtime:Error "unknown trap at 0x106ea0062 - illegal instruction"
clif:fail:linking.wast:147 # AssertTrap - expected trap, got Runtime:Error "unknown trap at 0x106ea0037 - illegal instruction"
clif:fail:linking.wast:149 # AssertTrap - expected trap, got Runtime:Error "unknown trap at 0x106ea0037 - illegal instruction"
clif:fail:linking.wast:137 # AssertTrap - expected trap, got Runtime:Error "unknown trap at 0x109d44062 - illegal instruction"
clif:fail:linking.wast:139 # AssertTrap - expected trap, got Runtime:Error "unknown trap at 0x109d44062 - illegal instruction"
clif:fail:linking.wast:142 # AssertTrap - expected trap, got Runtime:Error "unknown trap at 0x109d44062 - illegal instruction"
clif:fail:linking.wast:144 # AssertTrap - expected trap, got Runtime:Error "unknown trap at 0x109d44062 - illegal instruction"
clif:fail:linking.wast:147 # AssertTrap - expected trap, got Runtime:Error "unknown trap at 0x109d44037 - illegal instruction"
clif:fail:linking.wast:149 # AssertTrap - expected trap, got Runtime:Error "unknown trap at 0x109d44037 - illegal instruction"
clif:fail:linking.wast:172 # AssertReturn - result I32(4) ("0x4") does not match expected I32(-4) ("0xfffffffc")
clif:fail:linking.wast:173 # AssertReturn - result I32(4) ("0x4") does not match expected I32(-4) ("0xfffffffc")
clif:fail:linking.wast:175 # AssertReturn - result I32(4) ("0x4") does not match expected I32(-4) ("0xfffffffc")
clif:fail:linking.wast:178 # AssertReturn - Call failed RuntimeError: WebAssembly trap occurred during runtime: `call_indirect` out-of-bounds
clif:fail:linking.wast:179 # AssertReturn - Call failed RuntimeError: "unknown trap at 0x106ea0062 - illegal instruction"
clif:fail:linking.wast:181 # AssertReturn - Call failed RuntimeError: "unknown trap at 0x106ea0062 - illegal instruction"
clif:fail:linking.wast:185 # AssertTrap - expected trap, got Runtime:Error "unknown trap at 0x106ea0062 - illegal instruction"
clif:fail:linking.wast:187 # AssertTrap - expected trap, got Runtime:Error "unknown trap at 0x106ea0062 - illegal instruction"
clif:fail:linking.wast:179 # AssertReturn - Call failed RuntimeError: "unknown trap at 0x109d44062 - illegal instruction"
clif:fail:linking.wast:181 # AssertReturn - Call failed RuntimeError: "unknown trap at 0x109d44062 - illegal instruction"
clif:fail:linking.wast:185 # AssertTrap - expected trap, got Runtime:Error "unknown trap at 0x109d44062 - illegal instruction"
clif:fail:linking.wast:187 # AssertTrap - expected trap, got Runtime:Error "unknown trap at 0x109d44062 - illegal instruction"
clif:fail:linking.wast:200 # Module - caught panic Any
clif:fail:linking.wast:204 # AssertReturn Get - No instance available Some("$G2")
clif:fail:linking.wast:288 # AssertReturn - result I32(2) ("0x2") does not match expected I32(167) ("0xa7")
@ -382,6 +375,14 @@ clif:fail:memory.wast:5:windows # Module - caught panic Any
clif:fail:memory.wast:6:windows # Module - caught panic Any
clif:fail:stack.wast:137:windows # Module - caught panic Any
clif:fail:type.wast:3:windows # Module - caught panic Any
clif:fail:data.wast:162:windows # AssertUnlinkable - caught panic Any
clif:fail:data.wast:170:windows # AssertUnlinkable - caught panic Any
clif:fail:data.wast:178:windows # AssertUnlinkable - caught panic Any
clif:fail:data.wast:220:windows # AssertUnlinkable - caught panic Any
clif:fail:data.wast:235:windows # AssertUnlinkable - caught panic Any
clif:fail:data.wast:243:windows # AssertUnlinkable - caught panic Any
clif:fail:data.wast:251:windows # AssertUnlinkable - caught panic Any
clif:fail:data.wast:266:windows # AssertUnlinkable - caught panic Any
# LLVM bug with min/max over NaNs
llvm:skip:f32.wast:1651
@ -966,19 +967,11 @@ llvm:fail:data.wast:137 # Module - caught panic Any
llvm:fail:data.wast:143 # Module - caught panic Any
llvm:fail:data.wast:149 # Module - caught panic Any
llvm:fail:data.wast:154 # Module - caught panic Any
llvm:fail:data.wast:162 # AssertUnlinkable - caught panic Any
llvm:fail:data.wast:170 # AssertUnlinkable - caught panic Any
llvm:fail:data.wast:178 # AssertUnlinkable - caught panic Any
llvm:fail:data.wast:186 # AssertUnlinkable - instantiate successful, expected unlinkable
llvm:fail:data.wast:194 # AssertUnlinkable - instantiate successful, expected unlinkable
llvm:fail:data.wast:211 # AssertUnlinkable - caught panic Any
llvm:fail:data.wast:220 # AssertUnlinkable - caught panic Any
llvm:fail:data.wast:227 # AssertUnlinkable - caught panic Any
llvm:fail:data.wast:235 # AssertUnlinkable - caught panic Any
llvm:fail:data.wast:243 # AssertUnlinkable - caught panic Any
llvm:fail:data.wast:251 # AssertUnlinkable - caught panic Any
llvm:fail:data.wast:258 # AssertUnlinkable - caught panic Any
llvm:fail:data.wast:266 # AssertUnlinkable - caught panic Any
llvm:fail:data.wast:273 # AssertUnlinkable - caught panic Any
llvm:fail:elem.wast:101 # Module - caught panic Any
llvm:fail:elem.wast:143 # AssertUnlinkable - instantiate successful, expected unlinkable
@ -995,10 +988,10 @@ llvm:fail:elem.wast:380 # AssertReturn - result I32(65) ("0x41") does not match
llvm:fail:elem.wast:381 # AssertReturn - result I32(66) ("0x42") does not match expected I32(70) ("0x46")
llvm:fail:globals.wast:243 # AssertInvalid - caught panic Any
llvm:fail:globals.wast:301 # Module - caught panic Any
llvm:fail:i32.wast:243 # AssertReturn - result I32(286499151) ("0x1113a14f") does not match expected I32(32) ("0x20")
llvm:fail:i32.wast:252 # AssertReturn - result I32(286499168) ("0x1113a160") does not match expected I32(32) ("0x20")
llvm:fail:i64.wast:243 # AssertReturn - result I64(4581466607) ("0x11113a1ef") does not match expected I64(64) ("0x40")
llvm:fail:i64.wast:252 # AssertReturn - result I64(4581466592) ("0x11113a1e0") does not match expected I64(64) ("0x40")
llvm:fail:i32.wast:243 # AssertReturn - result I32(79069519) ("0x4b6814f") does not match expected I32(32) ("0x20")
llvm:fail:i32.wast:252 # AssertReturn - result I32(79069536) ("0x4b68160") does not match expected I32(32) ("0x20")
llvm:fail:i64.wast:243 # AssertReturn - result I64(4374036975) ("0x104b681ef") does not match expected I64(64) ("0x40")
llvm:fail:i64.wast:252 # AssertReturn - result I64(4374036960) ("0x104b681e0") does not match expected I64(64) ("0x40")
llvm:fail:imports.wast:98 # Module - caught panic Any
llvm:fail:imports.wast:99 # Module - caught panic Any
llvm:fail:imports.wast:100 # Module - caught panic Any
@ -1122,7 +1115,7 @@ llvm:fail:linking.wast:324 # AssertUnlinkable - caught panic Any
llvm:fail:linking.wast:335 # AssertUnlinkable - caught panic Any
llvm:fail:linking.wast:387 # AssertReturn - result I32(0) ("0x0") does not match expected I32(104) ("0x68")
llvm:fail:linking.wast:388 # AssertReturn - Call failed RuntimeError: WebAssembly trap occurred during runtime: incorrect `call_indirect` signature
llvm:fail:load.wast:201 # AssertReturn - result I32(290889759) ("0x1156a01f") does not match expected I32(32) ("0x20")
llvm:fail:load.wast:201 # AssertReturn - result I32(80568351) ("0x4cd601f") does not match expected I32(32) ("0x20")
llvm:fail:start.wast:92 # Module - caught panic Any
llvm:fail:type.wast:3 # Module - caught panic Any
@ -1149,25 +1142,25 @@ singlepass:fail:address.wast:202 # AssertTrap - expected trap, got []
singlepass:fail:address.wast:203 # AssertTrap - expected trap, got []
singlepass:fail:address.wast:204 # AssertTrap - expected trap, got []
singlepass:fail:address.wast:479 # AssertTrap - expected trap, got Runtime:Error unknown error
singlepass:fail:address.wast:481 # AssertTrap - expected trap, got Runtime:Error unknown error
singlepass:fail:address.wast:482 # AssertTrap - expected trap, got Runtime:Error unknown error
singlepass:fail:address.wast:483 # AssertTrap - expected trap, got Runtime:Error unknown error
singlepass:fail:address.wast:484 # AssertTrap - expected trap, got Runtime:Error unknown error
singlepass:fail:address.wast:485 # AssertTrap - expected trap, got Runtime:Error unknown error
singlepass:fail:address.wast:486 # AssertTrap - expected trap, got Runtime:Error unknown error
singlepass:fail:address.wast:487 # AssertTrap - expected trap, got Runtime:Error unknown error
singlepass:fail:address.wast:481 # AssertTrap - expected trap, got []
singlepass:fail:address.wast:482 # AssertTrap - expected trap, got []
singlepass:fail:address.wast:483 # AssertTrap - expected trap, got []
singlepass:fail:address.wast:484 # AssertTrap - expected trap, got []
singlepass:fail:address.wast:485 # AssertTrap - expected trap, got []
singlepass:fail:address.wast:486 # AssertTrap - expected trap, got []
singlepass:fail:address.wast:487 # AssertTrap - expected trap, got []
singlepass:fail:address.wast:489 # AssertTrap - expected trap, got []
singlepass:fail:address.wast:490 # AssertTrap - expected trap, got []
singlepass:fail:address.wast:491 # AssertTrap - expected trap, got []
singlepass:fail:address.wast:492 # AssertTrap - expected trap, got []
singlepass:fail:address.wast:493 # AssertTrap - expected trap, got Runtime:Error unknown error
singlepass:fail:address.wast:494 # AssertTrap - expected trap, got Runtime:Error unknown error
singlepass:fail:address.wast:493 # AssertTrap - expected trap, got []
singlepass:fail:address.wast:494 # AssertTrap - expected trap, got []
singlepass:fail:address.wast:495 # AssertTrap - expected trap, got []
singlepass:fail:address.wast:539 # AssertTrap - expected trap, got Runtime:Error unknown error
singlepass:fail:address.wast:541 # AssertTrap - expected trap, got []
singlepass:fail:address.wast:542 # AssertTrap - expected trap, got []
singlepass:fail:address.wast:586 # AssertTrap - expected trap, got Runtime:Error unknown error
singlepass:fail:address.wast:588 # AssertTrap - expected trap, got Runtime:Error unknown error
singlepass:fail:address.wast:588 # AssertTrap - expected trap, got []
singlepass:fail:address.wast:589 # AssertTrap - expected trap, got []
singlepass:fail:align.wast:864 # AssertTrap - expected trap, got Runtime:Error unknown error
singlepass:fail:binary-leb128.wast:2 # Module - caught panic Any

View File

@ -16,6 +16,7 @@ mod fseek;
mod hello;
mod mapdir;
mod path_link;
mod path_symlink;
mod quine;
mod readlink;
mod wasi_sees_virtual_root;

View File

@ -0,0 +1,20 @@
#[test]
fn test_path_symlink() {
assert_wasi_output!(
"../../wasitests/path_symlink.wasm",
"path_symlink",
vec![],
vec![
(
"temp".to_string(),
::std::path::PathBuf::from("wasitests/test_fs/temp")
),
(
"hamlet".to_string(),
::std::path::PathBuf::from("wasitests/test_fs/hamlet")
),
],
vec![],
"../../wasitests/path_symlink.out"
);
}

View File

@ -0,0 +1,4 @@
ACT III
SCENE I. A room in the castle.
Enter KING CLAUDIUS,

View File

@ -0,0 +1,30 @@
// Args:
// mapdir: temp:wasitests/test_fs/temp
// mapdir: hamlet:wasitests/test_fs/hamlet
use std::fs;
use std::io::{Read, Seek, SeekFrom};
use std::path::PathBuf;
fn main() {
#[cfg(not(target_os = "wasi"))]
let mut base = PathBuf::from("wasitests/test_fs");
#[cfg(target_os = "wasi")]
let mut base = PathBuf::from("/");
let symlink_loc = base.join("temp/act3");
let symlink_target = "../hamlet/act3";
let scene1 = symlink_loc.join("scene1.txt");
std::fs::soft_link(&symlink_target, &symlink_loc);
let mut file = fs::File::open(&scene1).expect("Could not open file");
let mut buffer = [0u8; 64];
assert_eq!(file.read(&mut buffer).unwrap(), 64);
let str_val = std::str::from_utf8(&buffer[..]).unwrap();
println!("{}", str_val);
std::fs::remove_file(symlink_loc).unwrap();
}

Binary file not shown.

View File

@ -393,8 +393,11 @@ impl WasiFs {
let path: &Path = Path::new(path);
let mut cur_inode = base_dir.inode;
let n_components = path.components().count();
// TODO: rights checks
'path_iter: for component in path.components() {
'path_iter: for (i, component) in path.components().enumerate() {
// used to terminate symlink resolution properly
let last_component = i + 1 == n_components;
// for each component traverse file structure
// loading inodes as necessary
'symlink_resolution: while symlink_count < MAX_SYMLINKS {
@ -430,7 +433,6 @@ impl WasiFs {
cd.push(component);
cd
};
// TODO: verify this returns successfully when given a non-symlink
let metadata = file.symlink_metadata().ok().ok_or(__WASI_EINVAL)?;
let file_type = metadata.file_type();
// we want to insert newly opened dirs and files, but not transient symlinks
@ -488,6 +490,7 @@ impl WasiFs {
cur_inode = new_inode;
if loop_for_symlink && follow_symlinks {
debug!("Following symlink to {:?}", cur_inode);
continue 'symlink_resolution;
}
}
@ -530,6 +533,7 @@ impl WasiFs {
base.push(relative_path);
base.to_string_lossy().to_string()
};
debug!("Following symlink recursively");
let symlink_inode = self.get_inode_at_path_inner(
new_base_dir,
&new_path,
@ -537,7 +541,15 @@ impl WasiFs {
follow_symlinks,
)?;
cur_inode = symlink_inode;
//continue 'symlink_resolution;
// if we're at the very end and we found a file, then we're done
// TODO: figure out if this should also happen for directories?
if let Kind::File { .. } = &self.inodes[cur_inode].kind {
// check if on last step
if last_component {
break 'symlink_resolution;
}
}
continue 'symlink_resolution;
}
}
break 'symlink_resolution;
@ -571,6 +583,64 @@ impl WasiFs {
Err(__WASI_EINVAL) // this may not make sense
}
// if this is still dead code and the year is 2020 or later, please delete this function
#[allow(dead_code)]
pub(crate) fn path_relative_to_fd(
&self,
fd: __wasi_fd_t,
inode: Inode,
) -> Result<PathBuf, __wasi_errno_t> {
let mut stack = vec![];
let base_fd = self.get_fd(fd)?;
let base_inode = base_fd.inode;
let mut cur_inode = inode;
while cur_inode != base_inode {
stack.push(self.inodes[cur_inode].name.clone());
match &self.inodes[cur_inode].kind {
Kind::Dir { parent, .. } => {
if let Some(p) = parent {
cur_inode = *p;
}
}
_ => return Err(__WASI_EINVAL),
}
}
let mut out = PathBuf::new();
for p in stack.iter().rev() {
out.push(p);
}
Ok(out)
}
/// finds the number of directories between the fd and the inode if they're connected
/// expects inode to point to a directory
pub(crate) fn path_depth_from_fd(
&self,
fd: __wasi_fd_t,
inode: Inode,
) -> Result<usize, __wasi_errno_t> {
let mut counter = 0;
let base_fd = self.get_fd(fd)?;
let base_inode = base_fd.inode;
let mut cur_inode = inode;
while cur_inode != base_inode {
counter += 1;
match &self.inodes[cur_inode].kind {
Kind::Dir { parent, .. } => {
if let Some(p) = parent {
cur_inode = *p;
}
}
_ => return Err(__WASI_EINVAL),
}
}
Ok(counter)
}
/// gets a host file from a base directory and a path
/// this function ensures the fs remains sandboxed
// NOTE: follow symlinks is super weird right now
@ -704,6 +774,24 @@ impl WasiFs {
}))
}
/// creates an inode and inserts it given a Kind, does not assume the file exists to
pub fn create_inode_with_default_stat(
&mut self,
kind: Kind,
is_preopened: bool,
name: String,
) -> Inode {
let mut stat = __wasi_filestat_t::default();
stat.st_ino = self.get_next_inode_index();
self.inodes.insert(InodeVal {
stat,
is_preopened,
name,
kind,
})
}
pub fn create_fd(
&mut self,
rights: __wasi_rights_t,

View File

@ -8,7 +8,7 @@ use std::{
};
/// Error type for external users
#[derive(Debug, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[allow(dead_code)]
// dead code beacuse this is for external use
pub enum WasiFsError {
@ -23,6 +23,38 @@ pub enum WasiFsError {
/// Something failed when doing IO. These errors can generally not be handled.
/// It may work if tried again.
IOError,
/// The address was in use
AddressInUse,
/// The address could not be found
AddressNotAvailable,
/// A pipe was closed
BrokenPipe,
/// The connection was aborted
ConnectionAborted,
/// The connection request was refused
ConnectionRefused,
/// The connection was reset
ConnectionReset,
/// The operation was interrupted before it could finish
Interrupted,
/// Invalid internal data, if the argument data is invalid, use `InvalidInput`
InvalidData,
/// The provided data is invalid
InvalidInput,
/// Could not perform the operation because there was not an open connection
NotConnected,
/// The requested file or directory could not be found
EntityNotFound,
/// Caller was not allowed to perform this operation
PermissionDenied,
/// The operation did not complete within the given amount of time
TimedOut,
/// Found EOF when EOF was not expected
UnexpectedEof,
/// Operation would block, this error lets the caller know that they can try again
WouldBlock,
/// A call to write returned 0
WriteZero,
/// A WASI error without an external name. If you encounter this it means
/// that there's probably a bug on our side (maybe as simple as forgetting to wrap
/// this error, but perhaps something broke)
@ -35,9 +67,51 @@ impl WasiFsError {
__WASI_EBADF => WasiFsError::InvalidFd,
__WASI_EEXIST => WasiFsError::AlreadyExists,
__WASI_EIO => WasiFsError::IOError,
__WASI_EADDRINUSE => WasiFsError::AddressInUse,
__WASI_EADDRNOTAVAIL => WasiFsError::AddressNotAvailable,
__WASI_EPIPE => WasiFsError::BrokenPipe,
__WASI_ECONNABORTED => WasiFsError::ConnectionAborted,
__WASI_ECONNREFUSED => WasiFsError::ConnectionRefused,
__WASI_ECONNRESET => WasiFsError::ConnectionReset,
__WASI_EINTR => WasiFsError::Interrupted,
__WASI_EINVAL => WasiFsError::InvalidInput,
__WASI_ENOTCONN => WasiFsError::NotConnected,
__WASI_ENOENT => WasiFsError::EntityNotFound,
__WASI_EPERM => WasiFsError::PermissionDenied,
__WASI_ETIMEDOUT => WasiFsError::TimedOut,
__WASI_EPROTO => WasiFsError::UnexpectedEof,
__WASI_EAGAIN => WasiFsError::WouldBlock,
__WASI_ENOSPC => WasiFsError::WriteZero,
_ => WasiFsError::UnknownError(err),
}
}
pub fn into_wasi_err(self) -> __wasi_errno_t {
match self {
WasiFsError::AlreadyExists => __WASI_EEXIST,
WasiFsError::AddressInUse => __WASI_EADDRINUSE,
WasiFsError::AddressNotAvailable => __WASI_EADDRNOTAVAIL,
WasiFsError::BaseNotDirectory => __WASI_ENOTDIR,
WasiFsError::BrokenPipe => __WASI_EPIPE,
WasiFsError::ConnectionAborted => __WASI_ECONNABORTED,
WasiFsError::ConnectionRefused => __WASI_ECONNREFUSED,
WasiFsError::ConnectionReset => __WASI_ECONNRESET,
WasiFsError::Interrupted => __WASI_EINTR,
WasiFsError::InvalidData => __WASI_EIO,
WasiFsError::InvalidFd => __WASI_EBADF,
WasiFsError::InvalidInput => __WASI_EINVAL,
WasiFsError::IOError => __WASI_EIO,
WasiFsError::NotAFile => __WASI_EINVAL,
WasiFsError::NotConnected => __WASI_ENOTCONN,
WasiFsError::EntityNotFound => __WASI_ENOENT,
WasiFsError::PermissionDenied => __WASI_EPERM,
WasiFsError::TimedOut => __WASI_ETIMEDOUT,
WasiFsError::UnexpectedEof => __WASI_EPROTO,
WasiFsError::WouldBlock => __WASI_EAGAIN,
WasiFsError::WriteZero => __WASI_ENOSPC,
WasiFsError::UnknownError(ec) => ec,
}
}
}
/// This trait relies on your file closing when it goes out of scope via `Drop`
@ -68,20 +142,20 @@ pub trait WasiFile: std::fmt::Debug + Write + Read + Seek {
/// Change the size of the file, if the `new_size` is greater than the current size
/// the extra bytes will be allocated and zeroed
// TODO: stablize this in 0.7.0 by removing default impl
fn set_len(&mut self, _new_size: __wasi_filesize_t) -> Option<()> {
fn set_len(&mut self, _new_size: __wasi_filesize_t) -> Result<(), WasiFsError> {
panic!("Default implementation for compatibilty in the 0.6.X releases; this will be removed in 0.7.0. Please implement WasiFile::allocate for your type before then");
}
/// Request deletion of the file
// TODO: break this out into a WasiPath trait which is dynamically in Kind::File
// this change can't be done until before release
fn unlink(&mut self) -> Option<()> {
fn unlink(&mut self) -> Result<(), WasiFsError> {
panic!("Default implementation for compatibilty in the 0.6.X releases; this will be removed in 0.7.0. Please implement WasiFile::unlink for your type before then");
}
/// Store file contents and metadata to disk
// TODO: stablize this in 0.7.0 by removing default impl
fn sync_to_disk(&self) -> Option<()> {
fn sync_to_disk(&self) -> Result<(), WasiFsError> {
panic!("Default implementation for compatibilty in the 0.6.X releases; this will be removed in 0.7.0. Please implement WasiFile::sync_to_disk for your type before then");
}
}
@ -187,15 +261,42 @@ impl WasiFile for HostFile {
self.metadata().len()
}
fn set_len(&mut self, new_size: __wasi_filesize_t) -> Option<()> {
fs::File::set_len(&self.inner, new_size).ok()
fn set_len(&mut self, new_size: __wasi_filesize_t) -> Result<(), WasiFsError> {
fs::File::set_len(&self.inner, new_size).map_err(Into::into)
}
fn unlink(&mut self) -> Option<()> {
std::fs::remove_file(&self.host_path).ok()
fn unlink(&mut self) -> Result<(), WasiFsError> {
std::fs::remove_file(&self.host_path).map_err(Into::into)
}
fn sync_to_disk(&self) -> Option<()> {
self.inner.sync_all().ok()
fn sync_to_disk(&self) -> Result<(), WasiFsError> {
self.inner.sync_all().map_err(Into::into)
}
}
impl From<io::Error> for WasiFsError {
fn from(io_error: io::Error) -> Self {
match io_error.kind() {
io::ErrorKind::AddrInUse => WasiFsError::AddressInUse,
io::ErrorKind::AddrNotAvailable => WasiFsError::AddressNotAvailable,
io::ErrorKind::AlreadyExists => WasiFsError::AlreadyExists,
io::ErrorKind::BrokenPipe => WasiFsError::BrokenPipe,
io::ErrorKind::ConnectionAborted => WasiFsError::ConnectionAborted,
io::ErrorKind::ConnectionRefused => WasiFsError::ConnectionRefused,
io::ErrorKind::ConnectionReset => WasiFsError::ConnectionReset,
io::ErrorKind::Interrupted => WasiFsError::Interrupted,
io::ErrorKind::InvalidData => WasiFsError::InvalidData,
io::ErrorKind::InvalidInput => WasiFsError::InvalidInput,
io::ErrorKind::NotConnected => WasiFsError::NotConnected,
io::ErrorKind::NotFound => WasiFsError::EntityNotFound,
io::ErrorKind::PermissionDenied => WasiFsError::PermissionDenied,
io::ErrorKind::TimedOut => WasiFsError::TimedOut,
io::ErrorKind::UnexpectedEof => WasiFsError::UnexpectedEof,
io::ErrorKind::WouldBlock => WasiFsError::WouldBlock,
io::ErrorKind::WriteZero => WasiFsError::WriteZero,
io::ErrorKind::Other => WasiFsError::IOError,
// if the following triggers, a new error type was added to this non-exhaustive enum
_ => WasiFsError::UnknownError(__WASI_EIO),
}
}
}

View File

@ -10,7 +10,7 @@ use crate::{
ptr::{Array, WasmPtr},
state::{
self, host_file_type_to_wasi_file_type, Fd, HostFile, Inode, InodeVal, Kind, WasiFile,
WasiState, MAX_SYMLINKS,
WasiFsError, WasiState, MAX_SYMLINKS,
},
ExitCode,
};
@ -338,7 +338,7 @@ pub fn fd_allocate(
match &mut state.fs.inodes[inode].kind {
Kind::File { handle, .. } => {
if let Some(handle) = handle {
wasi_try!(handle.set_len(new_size), __WASI_EIO);
wasi_try!(handle.set_len(new_size).map_err(WasiFsError::into_wasi_err));
} else {
return __WASI_EBADF;
}
@ -549,7 +549,7 @@ pub fn fd_filestat_set_size(
match &mut state.fs.inodes[inode].kind {
Kind::File { handle, .. } => {
if let Some(handle) = handle {
wasi_try!(handle.set_len(st_size), __WASI_EIO);
wasi_try!(handle.set_len(st_size).map_err(WasiFsError::into_wasi_err));
} else {
return __WASI_EBADF;
}
@ -1159,7 +1159,7 @@ pub fn fd_sync(ctx: &mut Ctx, fd: __wasi_fd_t) -> __wasi_errno_t {
match &mut state.fs.inodes[inode].kind {
Kind::File { handle, .. } => {
if let Some(h) = handle {
wasi_try!(h.sync_to_disk(), __WASI_EIO);
wasi_try!(h.sync_to_disk().map_err(WasiFsError::into_wasi_err));
} else {
return __WASI_EINVAL;
}
@ -1679,6 +1679,10 @@ pub fn path_open(
dirflags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0,
);
if let Ok(m) = maybe_inode {
dbg!(&state.fs.inodes[m]);
}
// TODO: traverse rights of dirs properly
// COMMENTED OUT: WASI isn't giving appropriate rights here when opening
// TODO: look into this; file a bug report if this is a bug
@ -1935,6 +1939,20 @@ pub fn path_rename(
debug!("wasi::path_rename");
unimplemented!("wasi::path_rename")
}
/// ### `path_symlink()`
/// Create a symlink
/// Inputs:
/// - `const char *old_path`
/// Array of UTF-8 bytes representing the source path
/// - `u32 old_path_len`
/// The number of bytes to read from `old_path`
/// - `__wasi_fd_t fd`
/// The base directory from which the paths are understood
/// - `const char *new_path`
/// Array of UTF-8 bytes representing the target path
/// - `u32 new_path_len`
/// The number of bytes to read from `new_path`
pub fn path_symlink(
ctx: &mut Ctx,
old_path: WasmPtr<u8, Array>,
@ -1944,16 +1962,89 @@ pub fn path_symlink(
new_path_len: u32,
) -> __wasi_errno_t {
debug!("wasi::path_symlink");
unimplemented!("wasi::path_symlink")
let state = get_wasi_state(ctx);
let memory = ctx.memory(0);
let old_path_str = wasi_try!(
old_path.get_utf8_string(memory, old_path_len),
__WASI_EINVAL
);
let new_path_str = wasi_try!(
new_path.get_utf8_string(memory, new_path_len),
__WASI_EINVAL
);
let base_fd = wasi_try!(state.fs.get_fd(fd));
if !has_rights(base_fd.rights, __WASI_RIGHT_PATH_SYMLINK) {
return __WASI_EACCES;
}
// get the depth of the parent + 1 (UNDER INVESTIGATION HMMMMMMMM THINK FISH ^ THINK FISH)
let old_path_path = std::path::Path::new(old_path_str);
let (source_inode, _) = wasi_try!(state.fs.get_parent_inode_at_path(fd, old_path_path, true));
let depth = wasi_try!(state.fs.path_depth_from_fd(fd, source_inode)) - 1;
let new_path_path = std::path::Path::new(new_path_str);
let (target_parent_inode, entry_name) =
wasi_try!(state.fs.get_parent_inode_at_path(fd, new_path_path, true));
// short circuit if anything is wrong, before we create an inode
match &state.fs.inodes[target_parent_inode].kind {
Kind::Dir { entries, .. } => {
if entries.contains_key(&entry_name) {
return __WASI_EEXIST;
}
}
Kind::Root { .. } => return __WASI_ENOTCAPABLE,
Kind::File { .. } | Kind::Symlink { .. } | Kind::Buffer { .. } => {
unreachable!("get_parent_inode_at_path returned something other than a Dir or Root")
}
}
let mut source_path = std::path::Path::new(old_path_str);
let mut relative_path = std::path::PathBuf::new();
for _ in 0..depth {
relative_path.push("..");
}
relative_path.push(source_path);
debug!(
"Symlinking {} to {}",
new_path_str,
relative_path.to_string_lossy()
);
let kind = Kind::Symlink {
base_po_dir: fd,
path_to_symlink: std::path::PathBuf::from(new_path_str),
relative_path,
};
let new_inode = state
.fs
.create_inode_with_default_stat(kind, false, entry_name.clone());
if let Kind::Dir {
ref mut entries, ..
} = &mut state.fs.inodes[target_parent_inode].kind
{
entries.insert(entry_name, new_inode);
}
__WASI_ESUCCESS
}
/// ### `path_unlink_file()`
/// Unlink a file, deleting if the number of hardlinks is 1
/// Inputs:
/// - `__wasi_fd_t fd`
/// The base file descriptor from which the path is understood
/// - `const char *path`
/// Array of UTF-8 bytes representing the path
/// - `u32 path_len`
/// The number of bytes in the `path` array
pub fn path_unlink_file(
ctx: &mut Ctx,
fd: __wasi_fd_t,
path: WasmPtr<u8, Array>,
path_len: u32,
) -> __wasi_errno_t {
// TODO check if fd is a dir, ensure it's within sandbox, etc.
debug!("wasi::path_unlink_file");
let state = get_wasi_state(ctx);
let memory = ctx.memory(0);
@ -1992,7 +2083,7 @@ pub fn path_unlink_file(
match &mut state.fs.inodes[removed_inode].kind {
Kind::File { handle, path } => {
if let Some(h) = handle {
wasi_try!(h.unlink().ok_or(__WASI_EIO));
wasi_try!(h.unlink().map_err(WasiFsError::into_wasi_err));
} else {
// File is closed
// problem with the abstraction, we can't call unlink because there's no handle
@ -2000,7 +2091,11 @@ pub fn path_unlink_file(
wasi_try!(std::fs::remove_file(path).map_err(|_| __WASI_EIO));
}
}
_ => unimplemented!("wasi::path_unlink_file for non-files"),
Kind::Dir { .. } | Kind::Root { .. } => return __WASI_EISDIR,
Kind::Symlink { .. } => {
// TODO: actually delete real symlinks and do nothing for virtual symlinks
}
_ => unimplemented!("wasi::path_unlink_file for Buffer"),
}
let inode_was_removed = unsafe { state.fs.remove_inode(removed_inode) };
assert!(

View File

@ -21,7 +21,7 @@ use structopt::StructOpt;
use wasmer::*;
use wasmer_clif_backend::CraneliftCompiler;
#[cfg(feature = "backend-llvm")]
use wasmer_llvm_backend::LLVMCompiler;
use wasmer_llvm_backend::{LLVMCompiler, LLVMOptions};
use wasmer_runtime::{
cache::{Cache as BaseCache, FileSystemCache, WasmHash},
Func, Value, VERSION,
@ -88,6 +88,23 @@ struct PrestandardFeatures {
all: bool,
}
#[cfg(feature = "backend-llvm")]
#[derive(Debug, StructOpt, Clone)]
/// LLVM backend flags.
pub struct LLVMCLIOptions {
/// Emit LLVM IR before optimization pipeline.
#[structopt(long = "llvm-pre-opt-ir", parse(from_os_str))]
pre_opt_ir: Option<PathBuf>,
/// Emit LLVM IR after optimization pipeline.
#[structopt(long = "llvm-post-opt-ir", parse(from_os_str))]
post_opt_ir: Option<PathBuf>,
/// Emit LLVM generated native code object file.
#[structopt(long = "backend-llvm-object-file", parse(from_os_str))]
obj_file: Option<PathBuf>,
}
#[derive(Debug, StructOpt)]
struct Run {
// Disable the cache
@ -155,6 +172,10 @@ struct Run {
#[structopt(long = "cache-key", hidden = true)]
cache_key: Option<String>,
#[cfg(feature = "backend-llvm")]
#[structopt(flatten)]
backend_llvm_options: LLVMCLIOptions,
#[structopt(flatten)]
features: PrestandardFeatures,
@ -356,6 +377,20 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
.map_err(|e| format!("Can't convert from wast to wasm: {:?}", e))?;
}
#[cfg(feature = "backend-llvm")]
{
if options.backend == Backend::LLVM {
let options = options.backend_llvm_options.clone();
unsafe {
wasmer_llvm_backend::GLOBAL_OPTIONS = LLVMOptions {
pre_opt_ir: options.pre_opt_ir,
post_opt_ir: options.post_opt_ir,
obj_file: options.obj_file,
}
}
}
}
let compiler: Box<dyn Compiler> = match options.backend {
#[cfg(feature = "backend-singlepass")]
Backend::Singlepass => Box::new(SinglePassCompiler::new()),
@ -488,7 +523,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
let index = instance
.resolve_func("_start")
.expect("The loader requires a _start function to be present in the module");
let mut ins: Box<LoadedInstance<Error = String>> = match loader {
let mut ins: Box<dyn LoadedInstance<Error = String>> = match loader {
LoaderName::Local => Box::new(
instance
.load(LocalLoader)