Merge branch 'master' into feature/clif-cgapi

This commit is contained in:
Brandon Fish 2019-05-29 22:45:31 -05:00 committed by GitHub
commit b2e37cecfa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 446 additions and 94 deletions

View File

@ -64,6 +64,7 @@ build_script:
- rename target wapm-cli-target
- cd ..
- move wapm-cli\wapm-cli-target wapm-cli-target
- cargo build --release --manifest-path lib/runtime-c-api/Cargo.toml
test_script:
- cargo test --manifest-path lib/spectests/Cargo.toml --features clif

View File

@ -191,6 +191,11 @@ jobs:
echo "${CIRCLE_TAG}" >> artifacts/git_version
make build-install
cp ./wasmer.tar.gz ./artifacts/$(./binary-name.sh)
- run:
name: Dynamic library
command: |
cargo build --release --manifest-path lib/runtime-c-api/Cargo.toml
cp target/release/libwasmer_runtime_c_api.so ./artifacts
- persist_to_workspace:
root: .
paths:
@ -278,6 +283,13 @@ jobs:
cp ./wasmer.tar.gz ./artifacts/$(./binary-name.sh)
# VERSION=$(cargo pkgid | cut -d# -f2 | cut -d: -f2)
# echo "${VERSION}" >> artifacts/version
- run:
name: Generate dynamic library for the runtime C API
command: |
export PATH="$HOME/.cargo/bin:$PATH"
cargo build --release --manifest-path lib/runtime-c-api/Cargo.toml
install_name_tool -id "@rpath/libwasmer_runtime_c_api.dylib" target/release/libwasmer_runtime_c_api.dylib
cp target/release/libwasmer_runtime_c_api.dylib ./artifacts
- persist_to_workspace:
root: .
paths:
@ -351,7 +363,7 @@ jobs:
# VERSION_TAG=$(git describe --exact-match --tags)
#if [ "$VERSION" == "$VERSION_TAG" ]; then
# echo "Versions match, publishing to Github"
ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -soft ${VERSION} ./artifacts/
ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} ${VERSION} ./artifacts/ || true
#else
# echo "Versions don't match. Wasmer output version (wasmer --version) is ${VERSION} while Git tag is ${VERSION_TAG}"
# exit 1

View File

@ -2,7 +2,7 @@
Wasmer uses the following components:
- [Cranelift](https://github.com/cranestation/cranelift): for compiling Wasm binaries to machine code
- Compiler backends: for compiling Wasm binaries to machine code ([more info here](https://github.com/wasmerio/wasmer/tree/master/lib#backends))
- [wabt](https://github.com/pepyakin/wabt-rs): for transforming `.wast` files to `.wasm` and running WebAssembly spec tests
- [wasmparser](https://github.com/yurydelendik/wasmparser.rs): for parsing the `.wasm` files and translating them into WebAssembly modules
@ -67,3 +67,8 @@ Once that's finished, we will have a `Instance` function that will be ready to e
Wasmer's Emscripten integration tries to wrap (and emulate) all the different syscalls that Emscripten needs.
We provide this integration by filling the `import_object` with the Emscripten functions, while instantiating the WebAssembly Instance.
## WASI
Wasmer's WASI integration implements all the different syscalls that WASI needs.
We provide this integration by filling the `import_object` with the WASI functions, while instantiating the WebAssembly Instance.

View File

@ -6,6 +6,7 @@ Blocks of changes will separated by version increments.
## **[Unreleased]**
- [#470](https://github.com/wasmerio/wasmer/pull/470) Add mapdir support to Emscripten, implement getdents for Unix
- [#467](https://github.com/wasmerio/wasmer/pull/467) `wasmer_instantiate` returns better error messages in the runtime C API
- [#463](https://github.com/wasmerio/wasmer/pull/463) Fix bug in WASI path_open allowing one level above preopened dir to be accessed
- [#461](https://github.com/wasmerio/wasmer/pull/461) Prevent passing negative lengths in various places in the runtime C API

1
Cargo.lock generated
View File

@ -1412,6 +1412,7 @@ version = "0.4.2"
dependencies = [
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -28,11 +28,12 @@ curl https://get.wasmer.io -sSfL | sh
Wasmer runtime can also be embedded in different languages, so you can use WebAssembly anywhere ✨:
* [**Rust**](https://github.com/wasmerio/wasmer-rust-example)
* [🦀 **Rust**](https://github.com/wasmerio/wasmer-rust-example)
* [**C/C++**](https://github.com/wasmerio/wasmer-c-api)
* [**🐘 PHP**](https://github.com/wasmerio/php-ext-wasm)
* [**🐍 Python**](https://github.com/wasmerio/python-ext-wasm)
* [**💎 Ruby**](https://github.com/wasmerio/ruby-ext-wasm)
* [**🐹 Go**](https://github.com/wasmerio/go-ext-wasm)
### Usage

19
SECURITY.md Normal file
View File

@ -0,0 +1,19 @@
# Security Policy
## Supported Versions
While in beta, the latest published version of `wasmer` (`0.x`) will be supported with security updates.
| Version | Supported |
| ------- | ------------------ |
| 0.x | :white_check_mark: |
## Reporting a Vulnerability
The Wasmer team and community take security bugs in Wasmer seriously.
We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions.
To report a security issue, email security@wasmer.io and include the word "SECURITY" in the subject line.
The Wasmer team will send a response indicating the next steps in handling your report.
After the initial reply to your report, the security team will keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance.

View File

@ -9,14 +9,15 @@ edition = "2018"
build = "build/mod.rs"
[dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.4.2" }
byteorder = "1"
hashbrown = "0.1"
lazy_static = "1.2.0"
libc = "0.2.49"
byteorder = "1"
time = "0.1.41"
wasmer-clif-backend = { path = "../clif-backend", version = "0.4.2" }
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.2", optional = true }
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.4.2", optional = true }
wasmer-runtime-core = { path = "../runtime-core", version = "0.4.2" }
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.2", optional = true }
[target.'cfg(windows)'.dependencies]
rand = "0.6"

View File

@ -10,6 +10,8 @@ pub use self::unix::*;
#[cfg(windows)]
pub use self::windows::*;
use libc::c_char;
use crate::{allocate_on_stack, EmscriptenData};
use std::os::raw::c_int;
use wasmer_runtime_core::vm::Ctx;
@ -112,3 +114,41 @@ pub fn ___assert_fail(_ctx: &mut Ctx, _a: c_int, _b: c_int, _c: c_int, _d: c_int
// TODO: Implement like emscripten expects regarding memory/page size
// TODO raise an error
}
pub fn _pathconf(ctx: &mut Ctx, path_addr: c_int, name: c_int) -> c_int {
debug!(
"emscripten::_pathconf {} {} - UNIMPLEMENTED",
path_addr, name
);
let _path = emscripten_memory_pointer!(ctx.memory(0), path_addr) as *const c_char;
match name {
0 => 32000,
1 | 2 | 3 => 255,
4 | 5 | 16 | 17 | 18 => 4096,
6 | 7 | 20 => 1,
8 => 0,
9 | 10 | 11 | 12 | 14 | 15 | 19 => -1,
13 => 64,
_ => {
// ___setErrNo(22);
-1
}
}
}
pub fn _fpathconf(_ctx: &mut Ctx, _fildes: c_int, name: c_int) -> c_int {
debug!("emscripten::_fpathconf {} {}", _fildes, name);
match name {
0 => 32000,
1 | 2 | 3 => 255,
4 | 5 | 16 | 17 | 18 => 4096,
6 | 7 | 20 => 1,
8 => 0,
9 | 10 | 11 | 12 | 14 | 15 | 19 => -1,
13 => 64,
_ => {
// ___setErrNo(22);
-1
}
}
}

View File

@ -3,8 +3,10 @@
#[macro_use]
extern crate wasmer_runtime_core;
use hashbrown::HashMap;
use lazy_static::lazy_static;
use std::cell::UnsafeCell;
use std::path::PathBuf;
use std::{f64, ffi::c_void};
use wasmer_runtime_core::{
error::CallResult,
@ -141,10 +143,14 @@ pub struct EmscriptenData<'a> {
pub stack_save: Option<Func<'a, (), i32>>,
pub stack_restore: Option<Func<'a, (i32)>>,
pub set_threw: Option<Func<'a, (i32, i32)>>,
pub mapped_dirs: HashMap<String, PathBuf>,
}
impl<'a> EmscriptenData<'a> {
pub fn new(instance: &'a mut Instance) -> EmscriptenData<'a> {
pub fn new(
instance: &'a mut Instance,
mapped_dirs: HashMap<String, PathBuf>,
) -> EmscriptenData<'a> {
let malloc = instance.func("_malloc").unwrap();
let free = instance.func("_free").unwrap();
let memalign = instance.func("_memalign").ok();
@ -272,6 +278,7 @@ impl<'a> EmscriptenData<'a> {
stack_save,
stack_restore,
set_threw,
mapped_dirs,
}
}
}
@ -282,8 +289,9 @@ pub fn run_emscripten_instance(
path: &str,
args: Vec<&str>,
entrypoint: Option<String>,
mapped_dirs: Vec<(String, PathBuf)>,
) -> CallResult<()> {
let mut data = EmscriptenData::new(instance);
let mut data = EmscriptenData::new(instance, mapped_dirs.into_iter().collect());
let data_ptr = &mut data as *mut _ as *mut c_void;
instance.context_mut().data = data_ptr;
@ -545,6 +553,8 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
"_sysconf" => func!(crate::env::_sysconf),
"_getaddrinfo" => func!(crate::env::_getaddrinfo),
"_times" => func!(crate::env::_times),
"_pathconf" => func!(crate::env::_pathconf),
"_fpathconf" => func!(crate::env::_fpathconf),
// Syscalls
"___syscall1" => func!(crate::syscalls::___syscall1),
@ -687,6 +697,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
"_asctime_r" => func!(crate::time::_asctime_r),
"_localtime" => func!(crate::time::_localtime),
"_time" => func!(crate::time::_time),
"_timegm" => func!(crate::time::_timegm),
"_strftime" => func!(crate::time::_strftime),
"_strftime_l" => func!(crate::time::_strftime_l),
"_localtime_r" => func!(crate::time::_localtime_r),
@ -705,6 +716,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
"_llvm_exp2_f32" => func!(crate::math::_llvm_exp2_f32),
"_llvm_exp2_f64" => func!(crate::math::_llvm_exp2_f64),
"_llvm_trunc_f64" => func!(crate::math::_llvm_trunc_f64),
"_llvm_fma_f64" => func!(crate::math::_llvm_fma_f64),
"_emscripten_random" => func!(crate::math::_emscripten_random),
// Jump

View File

@ -49,6 +49,11 @@ pub fn _llvm_trunc_f64(_ctx: &mut Ctx, value: f64) -> f64 {
value.trunc()
}
pub fn _llvm_fma_f64(_ctx: &mut Ctx, value: f64, a: f64, b: f64) -> f64 {
debug!("emscripten::_llvm_fma_f64");
value.mul_add(a, b)
}
pub fn _emscripten_random(_ctx: &mut Ctx) -> f64 {
debug!("emscripten::_emscripten_random");
-1.0

View File

@ -10,7 +10,8 @@ pub use self::unix::*;
#[cfg(windows)]
pub use self::windows::*;
use super::utils::copy_stat_into_wasm;
use crate::utils::{copy_stat_into_wasm, get_cstr_path, get_current_directory};
use super::varargs::VarArgs;
use byteorder::{ByteOrder, LittleEndian};
/// NOTE: TODO: These syscalls only support wasm_32 for now because they assume offsets are u32
@ -38,6 +39,7 @@ use libc::{
// writev,
stat,
write,
// readlink,
};
use wasmer_runtime_core::vm::Ctx;
@ -93,14 +95,20 @@ pub fn ___syscall6(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int
// chdir
pub fn ___syscall12(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall12 (chdir) {}", _which);
let path_addr: i32 = varargs.get(ctx);
unsafe {
let path_ptr = emscripten_memory_pointer!(ctx.memory(0), path_addr) as *const i8;
let _path = std::ffi::CStr::from_ptr(path_ptr);
let ret = chdir(path_ptr);
debug!("=> path: {:?}, ret: {}", _path, ret);
let path_ptr = varargs.get_str(ctx);
let real_path_owned = get_cstr_path(ctx, path_ptr);
let real_path = if let Some(ref rp) = real_path_owned {
rp.as_c_str().as_ptr()
} else {
path_ptr
};
let ret = unsafe { chdir(real_path) };
debug!(
"=> path: {:?}, ret: {}",
unsafe { std::ffi::CStr::from_ptr(real_path) },
ret
}
);
ret
}
pub fn ___syscall10(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
@ -122,15 +130,25 @@ pub fn ___syscall20(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
// rename
pub fn ___syscall38(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 {
debug!("emscripten::___syscall38 (rename)");
let old_path_addr: u32 = varargs.get(ctx);
let new_path_addr: u32 = varargs.get(ctx);
let old_path = emscripten_memory_pointer!(ctx.memory(0), old_path_addr) as *const i8;
let new_path = emscripten_memory_pointer!(ctx.memory(0), new_path_addr) as *const i8;
let result = unsafe { rename(old_path, new_path) };
let old_path = varargs.get_str(ctx);
let new_path = varargs.get_str(ctx);
let real_old_path_owned = get_cstr_path(ctx, old_path);
let real_old_path = if let Some(ref rp) = real_old_path_owned {
rp.as_c_str().as_ptr()
} else {
old_path
};
let real_new_path_owned = get_cstr_path(ctx, new_path);
let real_new_path = if let Some(ref rp) = real_new_path_owned {
rp.as_c_str().as_ptr()
} else {
new_path
};
let result = unsafe { rename(real_old_path, real_new_path) };
debug!(
"=> old_path: {}, new_path: {}, result: {}",
unsafe { std::ffi::CStr::from_ptr(old_path).to_str().unwrap() },
unsafe { std::ffi::CStr::from_ptr(new_path).to_str().unwrap() },
unsafe { std::ffi::CStr::from_ptr(real_old_path).to_str().unwrap() },
unsafe { std::ffi::CStr::from_ptr(real_new_path).to_str().unwrap() },
result
);
result
@ -139,9 +157,14 @@ pub fn ___syscall38(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 {
// rmdir
pub fn ___syscall40(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall40 (rmdir)");
let pathname: u32 = varargs.get(ctx);
let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8;
unsafe { rmdir(pathname_addr) }
let pathname_addr = varargs.get_str(ctx);
let real_path_owned = get_cstr_path(ctx, pathname_addr);
let real_path = if let Some(ref rp) = real_path_owned {
rp.as_c_str().as_ptr()
} else {
pathname_addr
};
unsafe { rmdir(real_path) }
}
// pipe
@ -201,9 +224,32 @@ pub fn ___syscall75(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
-1
}
pub fn ___syscall85(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
debug!("emscripten::___syscall85");
-1
// readlink
pub fn ___syscall85(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 {
debug!("emscripten::___syscall85 (readlink)");
let _path = varargs.get_str(ctx);
let buf = varargs.get_str(ctx);
// let buf_addr: i32 = varargs.get(ctx);
let buf_size: i32 = varargs.get(ctx);
let fd = 3;
let ret = unsafe { read(fd, buf as _, buf_size as _) as i32 };
debug!(
"=> buf: {}, buf_size: {}, return: {} ",
unsafe { std::ffi::CStr::from_ptr(buf as _).to_str().unwrap() },
buf_size,
ret
);
// let ret = unsafe {
// readlink(path, buf as _, buf_size as _) as i32
// };
// debug!("=> path: {}, buf: {}, buf_size: {}, return: {} ",
// unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() },
// unsafe { std::ffi::CStr::from_ptr(buf as _).to_str().unwrap() },
// // std::ffi::CStr::from_ptr(buf).to_str().unwrap(),
// // buf,
// buf_size,
// ret);
ret
}
pub fn ___syscall91(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
@ -224,10 +270,9 @@ pub fn ___syscall110(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
// getcwd
pub fn ___syscall183(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 {
debug!("emscripten::___syscall183");
use std::env;
let buf_offset: c_int = varargs.get(ctx);
let _size: c_int = varargs.get(ctx);
let path = env::current_dir();
let path = get_current_directory(ctx);
let path_string = path.unwrap().display().to_string();
let len = path_string.len();
unsafe {
@ -397,19 +442,23 @@ pub fn ___syscall199(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
// stat64
pub fn ___syscall195(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall195 (stat64) {}", _which);
let pathname: u32 = varargs.get(ctx);
let pathname_addr = varargs.get_str(ctx);
let buf: u32 = varargs.get(ctx);
let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8;
let real_path_owned = get_cstr_path(ctx, pathname_addr);
let real_path = if let Some(ref rp) = real_path_owned {
rp.as_c_str().as_ptr()
} else {
pathname_addr
};
unsafe {
let mut _stat: stat = std::mem::zeroed();
let ret = stat(pathname_addr, &mut _stat);
let ret = stat(real_path, &mut _stat);
debug!(
"=> pathname: {}, buf: {}, path: {} = {}\nlast os error: {}",
pathname,
"=> pathname: {}, buf: {} = {}, last os error: {}",
std::ffi::CStr::from_ptr(real_path).to_str().unwrap(),
buf,
std::ffi::CStr::from_ptr(pathname_addr).to_str().unwrap(),
ret,
Error::last_os_error()
);
@ -440,11 +489,6 @@ pub fn ___syscall197(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
0
}
pub fn ___syscall220(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
debug!("emscripten::___syscall220");
-1
}
// fcntl64
pub fn ___syscall221(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall221 (fcntl64) {}", _which);
@ -457,7 +501,7 @@ pub fn ___syscall221(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
// |FNONBLOCK - 0x04
debug!("=> fd: {}, cmd: {}", _fd, cmd);
match cmd {
2 => 0,
1 | 2 => 0,
13 | 14 => 0, // pretend file locking worked
_ => -1,
}

View File

@ -43,6 +43,7 @@ use libc::{
pid_t,
pread,
pwrite,
readdir,
// readv,
recvfrom,
recvmsg,
@ -104,19 +105,23 @@ const SO_NOSIGPIPE: c_int = 0;
/// open
pub fn ___syscall5(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall5 (open) {}", _which);
let pathname: u32 = varargs.get(ctx);
let pathname_addr = varargs.get_str(ctx);
let flags: i32 = varargs.get(ctx);
let mode: u32 = varargs.get(ctx);
let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8;
let _path_str = unsafe { std::ffi::CStr::from_ptr(pathname_addr).to_str().unwrap() };
let fd = unsafe { open(pathname_addr, flags, mode) };
let real_path_owned = utils::get_cstr_path(ctx, pathname_addr);
let real_path = if let Some(ref rp) = real_path_owned {
rp.as_c_str().as_ptr()
} else {
pathname_addr
};
let _path_str = unsafe { std::ffi::CStr::from_ptr(real_path).to_str().unwrap() };
let fd = unsafe { open(real_path, flags, mode) };
debug!(
"=> pathname: {}, flags: {}, mode: {} = fd: {}\npath: {}\nlast os error: {}",
pathname,
"=> path: {}, flags: {}, mode: {} = fd: {}, last os error: {}",
_path_str,
flags,
mode,
fd,
_path_str,
Error::last_os_error(),
);
fd
@ -126,10 +131,8 @@ pub fn ___syscall5(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int
pub fn ___syscall9(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall9 (link) {}", _which);
let oldname: c_int = varargs.get(ctx);
let newname: c_int = varargs.get(ctx);
let oldname_ptr = emscripten_memory_pointer!(ctx.memory(0), oldname) as *const i8;
let newname_ptr = emscripten_memory_pointer!(ctx.memory(0), newname) as *const i8;
let oldname_ptr = varargs.get_str(ctx);
let newname_ptr = varargs.get_str(ctx);
let result = unsafe { link(oldname_ptr, newname_ptr) };
debug!(
"=> oldname: {}, newname: {}, result: {}",
@ -156,15 +159,25 @@ pub fn ___syscall77(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int
pub fn ___syscall83(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall83 (symlink) {}", _which);
let path1_ptr: c_int = varargs.get(ctx);
let path2_ptr: c_int = varargs.get(ctx);
let path1 = emscripten_memory_pointer!(ctx.memory(0), path1_ptr) as *mut i8;
let path2 = emscripten_memory_pointer!(ctx.memory(0), path2_ptr) as *mut i8;
let result = unsafe { symlink(path1, path2) };
let path1 = varargs.get_str(ctx);
let path2 = varargs.get_str(ctx);
let real_path1_owned = utils::get_cstr_path(ctx, path1);
let real_path1 = if let Some(ref rp) = real_path1_owned {
rp.as_c_str().as_ptr()
} else {
path1
};
let real_path2_owned = utils::get_cstr_path(ctx, path2);
let real_path2 = if let Some(ref rp) = real_path2_owned {
rp.as_c_str().as_ptr()
} else {
path2
};
let result = unsafe { symlink(real_path1, real_path2) };
debug!(
"=> path1: {}, path2: {}, result: {}",
unsafe { std::ffi::CStr::from_ptr(path1).to_str().unwrap() },
unsafe { std::ffi::CStr::from_ptr(path2).to_str().unwrap() },
unsafe { std::ffi::CStr::from_ptr(real_path1).to_str().unwrap() },
unsafe { std::ffi::CStr::from_ptr(real_path2).to_str().unwrap() },
result,
);
result
@ -186,14 +199,19 @@ pub fn ___syscall194(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
/// lchown
pub fn ___syscall198(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall198 (lchown) {}", _which);
let path: c_int = varargs.get(ctx);
let path_ptr = varargs.get_str(ctx);
let real_path_owned = utils::get_cstr_path(ctx, path_ptr);
let real_path = if let Some(ref rp) = real_path_owned {
rp.as_c_str().as_ptr()
} else {
path_ptr
};
let uid: uid_t = varargs.get(ctx);
let gid: gid_t = varargs.get(ctx);
let path_ptr = emscripten_memory_pointer!(ctx.memory(0), path) as *const i8;
let result = unsafe { lchown(path_ptr, uid, gid) };
let result = unsafe { lchown(real_path, uid, gid) };
debug!(
"=> path: {}, uid: {}, gid: {}, result: {}",
unsafe { std::ffi::CStr::from_ptr(path_ptr).to_str().unwrap() },
unsafe { std::ffi::CStr::from_ptr(real_path).to_str().unwrap() },
uid,
gid,
result,
@ -222,13 +240,17 @@ pub fn ___syscall205(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
pub fn ___syscall212(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall212 (chown) {}", _which);
let pathname: u32 = varargs.get(ctx);
let pathname_addr = varargs.get_str(ctx);
let real_path_owned = utils::get_cstr_path(ctx, pathname_addr);
let real_path = if let Some(ref rp) = real_path_owned {
rp.as_c_str().as_ptr()
} else {
pathname_addr
};
let owner: u32 = varargs.get(ctx);
let group: u32 = varargs.get(ctx);
let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8;
unsafe { chown(pathname_addr, owner, group) }
unsafe { chown(real_path, owner, group) }
}
/// madvise
@ -247,13 +269,18 @@ pub fn ___syscall219(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
/// access
pub fn ___syscall33(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall33 (access) {}", _which);
let path_ptr: c_int = varargs.get(ctx);
let path = varargs.get_str(ctx);
let real_path_owned = utils::get_cstr_path(ctx, path);
let real_path = if let Some(ref rp) = real_path_owned {
rp.as_c_str().as_ptr()
} else {
path
};
let amode: c_int = varargs.get(ctx);
let path = emscripten_memory_pointer!(ctx.memory(0), path_ptr) as *const i8;
let result = unsafe { access(path, amode) };
let result = unsafe { access(real_path, amode) };
debug!(
"=> path: {}, amode: {}, result: {}",
unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() },
unsafe { std::ffi::CStr::from_ptr(real_path).to_str().unwrap() },
amode,
result
);
@ -270,10 +297,15 @@ pub fn ___syscall34(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int
// mkdir
pub fn ___syscall39(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall39 (mkdir) {}", _which);
let pathname: u32 = varargs.get(ctx);
let pathname_addr = varargs.get_str(ctx);
let real_path_owned = utils::get_cstr_path(ctx, pathname_addr);
let real_path = if let Some(ref rp) = real_path_owned {
rp.as_c_str().as_ptr()
} else {
pathname_addr
};
let mode: u32 = varargs.get(ctx);
let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8;
unsafe { mkdir(pathname_addr, mode as _) }
unsafe { mkdir(real_path, mode as _) }
}
/// dup
@ -771,9 +803,14 @@ pub fn ___syscall122(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
/// lstat64
pub fn ___syscall196(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 {
debug!("emscripten::___syscall196 (lstat64) {}", _which);
let path_ptr: c_int = varargs.get(ctx);
let path = varargs.get_str(ctx);
let real_path_owned = utils::get_cstr_path(ctx, path);
let real_path = if let Some(ref rp) = real_path_owned {
rp.as_c_str().as_ptr()
} else {
path
};
let buf_ptr: u32 = varargs.get(ctx);
let path = emscripten_memory_pointer!(ctx.memory(0), path_ptr) as *const i8;
unsafe {
let mut stat: stat = std::mem::zeroed();
@ -783,9 +820,9 @@ pub fn ___syscall196(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 {
let stat_ptr = &mut stat as *mut stat;
#[cfg(target_os = "macos")]
let ret = lstat64(path, stat_ptr);
let ret = lstat64(real_path, stat_ptr);
#[cfg(not(target_os = "macos"))]
let ret = lstat(path, stat_ptr);
let ret = lstat(real_path, stat_ptr);
debug!("ret: {}", ret);
if ret != 0 {
@ -796,6 +833,45 @@ pub fn ___syscall196(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 {
0
}
// getdents
// dirent structure is
// i64, i64, u16 (280), i8, [i8; 256]
pub fn ___syscall220(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 {
debug!("emscripten::___syscall220");
let fd: i32 = varargs.get(ctx);
let dirp_addr: i32 = varargs.get(ctx);
let count: u32 = varargs.get(ctx);
let dirp = emscripten_memory_pointer!(ctx.memory(0), dirp_addr) as *mut u8;
// need to persist stream across calls?
let dir: *mut libc::DIR = unsafe { libc::fdopendir(fd) };
let mut pos = 0;
let offset = 280;
while pos + offset <= count as usize {
let dirent = unsafe { readdir(dir) };
if dirent.is_null() {
break;
}
#[allow(clippy::cast_ptr_alignment)]
unsafe {
*(dirp.add(pos) as *mut u64) = (*dirent).d_ino;
*(dirp.add(pos + 8) as *mut u64) = pos as u64 + offset as u64;
*(dirp.add(pos + 16) as *mut u16) = offset as u16;
*(dirp.add(pos + 18) as *mut u8) = (*dirent).d_type;
let upper_bound = std::cmp::min((*dirent).d_reclen, 254) as usize;
let mut i = 0;
while i < upper_bound {
*(dirp.add(pos + 19 + i) as *mut i8) = (*dirent).d_name[i];
i += 1;
}
*(dirp.add(pos + 19 + i) as *mut i8) = 0 as i8;
}
pos += offset;
}
pos as i32
}
/// fallocate
pub fn ___syscall324(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall324 (fallocate) {}", _which);

View File

@ -1,4 +1,4 @@
use crate::utils::copy_cstr_into_wasm;
use crate::utils::{copy_cstr_into_wasm, get_cstr_path};
use crate::varargs::VarArgs;
use libc::mkdir;
use libc::open;
@ -18,11 +18,16 @@ pub fn ___syscall5(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall5 (open) {}", which);
#[cfg(not(feature = "debug"))]
let _ = which;
let pathname: u32 = varargs.get(ctx);
let pathname_addr = varargs.get_str(ctx);
let real_path_owned = get_cstr_path(ctx, pathname_addr);
let real_path = if let Some(ref rp) = real_path_owned {
rp.as_c_str().as_ptr()
} else {
pathname_addr
};
let flags: i32 = varargs.get(ctx);
let mode: u32 = varargs.get(ctx);
let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8;
let path_str = unsafe { std::ffi::CStr::from_ptr(pathname_addr).to_str().unwrap() };
let path_str = unsafe { std::ffi::CStr::from_ptr(real_path).to_str().unwrap() };
match path_str {
"/dev/urandom" => {
// create a fake urandom file for windows, super hacky
@ -43,15 +48,15 @@ pub fn ___syscall5(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int {
let fd = unsafe { open(raw_pointer_to_urandom_file, flags, mode) };
debug!(
"=> pathname: {}, flags: {}, mode: {} = fd: {}",
pathname, flags, mode, fd
path_str, flags, mode, fd
);
fd
}
_ => {
let fd = unsafe { open(pathname_addr, flags, mode) };
let fd = unsafe { open(real_path, flags, mode) };
debug!(
"=> pathname: {}, flags: {}, mode: {} = fd: {}\npath: {}",
pathname, flags, mode, fd, path_str
path_str, flags, mode, fd, path_str
);
fd
}
@ -95,9 +100,14 @@ pub fn ___syscall39(ctx: &mut Ctx, which: c_int, mut varargs: VarArgs) -> c_int
debug!("emscripten::___syscall39 (mkdir) {}", which);
#[cfg(not(feature = "debug"))]
let _ = which;
let pathname: u32 = varargs.get(ctx);
let pathname_addr = emscripten_memory_pointer!(ctx.memory(0), pathname) as *const i8;
unsafe { mkdir(pathname_addr) }
let pathname_addr = varargs.get_str(ctx);
let real_path_owned = get_cstr_path(ctx, pathname_addr);
let real_path = if let Some(ref rp) = real_path_owned {
rp.as_c_str().as_ptr()
} else {
pathname_addr
};
unsafe { mkdir(real_path) }
}
/// dup
@ -251,6 +261,12 @@ pub fn ___syscall196(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
-1
}
// getdents
pub fn ___syscall220(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
debug!("emscripten::___syscall220");
-1
}
/// fchown
pub fn ___syscall207(_ctx: &mut Ctx, _which: c_int, _varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall207 (fchown) {}", _which);

View File

@ -4,7 +4,9 @@ use std::mem;
use std::time::SystemTime;
#[cfg(not(target_os = "windows"))]
use libc::{clockid_t, time as libc_time};
use libc::{clockid_t, time as libc_time, timegm as libc_timegm, tm as libc_tm};
#[cfg(not(target_os = "windows"))]
use std::ffi::CString;
#[cfg(target_os = "windows")]
use libc::time_t;
@ -298,6 +300,57 @@ pub fn _time(ctx: &mut Ctx, time_p: u32) -> i32 {
}
}
/// emscripten: _timegm
#[cfg(not(target_os = "windows"))]
#[allow(clippy::cast_ptr_alignment)]
pub fn _timegm(ctx: &mut Ctx, time_ptr: u32) -> i32 {
debug!("emscripten::_timegm {}", time_ptr);
unsafe {
let time_p_addr = emscripten_memory_pointer!(ctx.memory(0), time_ptr) as *mut guest_tm;
let x: *mut c_char = CString::new("").expect("CString::new failed").into_raw();
let mut rust_tm = libc_tm {
tm_sec: 0,
tm_min: 0,
tm_hour: 0,
tm_mday: 0,
tm_mon: 0,
tm_year: 0,
tm_wday: 0,
tm_yday: 0,
tm_isdst: 0,
tm_gmtoff: 0,
tm_zone: x,
};
let result = libc_timegm(&mut rust_tm) as i32;
if result != 0 {
(*time_p_addr).tm_sec = rust_tm.tm_sec;
(*time_p_addr).tm_min = rust_tm.tm_min;
(*time_p_addr).tm_hour = rust_tm.tm_hour;
(*time_p_addr).tm_mday = rust_tm.tm_mday;
(*time_p_addr).tm_mon = rust_tm.tm_mon;
(*time_p_addr).tm_year = rust_tm.tm_year;
(*time_p_addr).tm_wday = rust_tm.tm_wday;
(*time_p_addr).tm_yday = rust_tm.tm_yday;
(*time_p_addr).tm_isdst = rust_tm.tm_isdst;
(*time_p_addr).tm_gmtoff = rust_tm.tm_gmtoff as _;
(*time_p_addr).tm_zone = 0;
}
result
}
}
#[cfg(target_os = "windows")]
pub fn _timegm(_ctx: &mut Ctx, _time_ptr: c_int) -> i32 {
debug!(
"emscripten::_timegm - UNIMPLEMENTED IN WINDOWS {}",
_time_ptr
);
-1
}
/// emscripten: _strftime
pub fn _strftime(
ctx: &mut Ctx,

View File

@ -5,6 +5,7 @@ use libc::stat;
use std::ffi::CStr;
use std::mem::size_of;
use std::os::raw::c_char;
use std::path::PathBuf;
use std::slice;
use wasmer_runtime_core::memory::Memory;
use wasmer_runtime_core::{
@ -204,6 +205,61 @@ pub fn read_string_from_wasm(memory: &Memory, offset: u32) -> String {
String::from_utf8_lossy(&v).to_owned().to_string()
}
/// This function trys to find an entry in mapdir
/// translating paths into their correct value
pub fn get_cstr_path(ctx: &mut Ctx, path: *const i8) -> Option<std::ffi::CString> {
use std::collections::VecDeque;
let path_str = unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() }.to_string();
let data = get_emscripten_data(ctx);
let path = PathBuf::from(path_str);
let mut prefix_added = false;
let mut components = path.components().collect::<VecDeque<_>>();
// TODO(mark): handle absolute/non-canonical/non-relative paths too (this
// functionality should be shared among the abis)
if components.len() == 1 {
components.push_front(std::path::Component::CurDir);
prefix_added = true;
}
let mut cumulative_path = PathBuf::new();
for c in components.into_iter() {
cumulative_path.push(c);
if let Some(val) = data
.mapped_dirs
.get(&cumulative_path.to_string_lossy().to_string())
{
let rest_of_path = if !prefix_added {
path.strip_prefix(cumulative_path).ok()?
} else {
&path
};
let rebased_path = val.join(rest_of_path);
return std::ffi::CString::new(rebased_path.to_string_lossy().as_bytes()).ok();
}
}
None
}
/// gets the current directory
/// handles mapdir logic
pub fn get_current_directory(ctx: &mut Ctx) -> Option<PathBuf> {
if let Some(val) = get_emscripten_data(ctx).mapped_dirs.get(".") {
return Some(val.clone());
}
std::env::current_dir()
.map(|cwd| {
if let Some(val) = get_emscripten_data(ctx)
.mapped_dirs
.get(&cwd.to_string_lossy().to_string())
{
val.clone()
} else {
cwd
}
})
.ok()
}
#[cfg(test)]
mod tests {
use super::is_emscripten_module;

View File

@ -1,5 +1,7 @@
use std::mem;
use wasmer_runtime_core::{types::WasmExternType, vm::Ctx};
// use std::ffi::CStr;
use std::os::raw::c_char;
#[repr(transparent)]
#[derive(Copy, Clone)]
@ -13,6 +15,14 @@ impl VarArgs {
self.pointer += mem::size_of::<T>() as u32;
unsafe { (ptr as *const T).read() }
}
// pub fn getStr<'a>(&mut self, ctx: &mut Ctx) -> &'a CStr {
pub fn get_str(&mut self, ctx: &mut Ctx) -> *const c_char {
let ptr_addr: u32 = self.get(ctx);
let ptr = emscripten_memory_pointer!(ctx.memory(0), ptr_addr) as *const c_char;
ptr
// unsafe { CStr::from_ptr(ptr) }
}
}
unsafe impl WasmExternType for VarArgs {

View File

@ -56,6 +56,7 @@ macro_rules! assert_emscripten_output {
$name,
$args,
None,
vec![],
).expect("run_emscripten_instance finishes");
let output = capturer.end().unwrap().0;

View File

@ -445,9 +445,6 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
.instantiate(&import_object)
.map_err(|e| format!("Can't instantiate module: {:?}", e))?;
if !mapped_dirs.is_empty() {
eprintln!("WARN: mapdir is not implemented for emscripten targets");
}
wasmer_emscripten::run_emscripten_instance(
&module,
&mut instance,
@ -458,6 +455,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
},
options.args.iter().map(|arg| arg.as_str()).collect(),
options.em_entrypoint.clone(),
mapped_dirs,
)
.map_err(|e| format!("{:?}", e))?;
} else {