Merge pull request #20 from WAFoundation/feature/add-emscripten-compatible-apis

Add emscripten compatible apis
This commit is contained in:
Syrus Akbary 2018-11-21 21:11:11 -08:00 committed by GitHub
commit cf22c6f1b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 1233 additions and 222 deletions

View File

@ -1,3 +1,8 @@
ifeq (test, $(firstword $(MAKECMDGOALS)))
runargs := $(wordlist 2, $(words $(MAKECMDGOALS)), $(MAKECMDGOALS))
$(eval $(runargs):;@true)
endif
.PHONY: spectests clean build install
# This will re-generate the Rust test files based on spectests/*.wast
@ -14,7 +19,7 @@ install:
cargo install --path .
test:
cargo test -- --test-threads=1
cargo test -- --test-threads=1 $(runargs)
release:
# If you are in OS-X, you will need mingw-w64 for cross compiling to windows

31
examples/em_abort.wat Normal file
View File

@ -0,0 +1,31 @@
(module
(type $t1 (func (param i32 i32) (result i32)))
(type $t2 (func (param i32)))
(type $t3 (func ))
(func $printf (import "env" "printf") (type $t1))
(func $abort (import "env" "abort") (type $t2))
(func $_abort (import "env" "_abort") (type $t3))
(func $abort_on_cannot_grow_memory (import "env" "abortOnCannotGrowMemory") (type $t3))
(memory 1)
(data (i32.const 0) ">>> First\00")
(data (i32.const 24) ">>> Second\00")
(data (i32.const 48) "Aborting abruptly!\00")
(func $main (export "main")
;; print ">>> First"
(call $printf (i32.const 0) (i32.const 0))
(drop)
;; aborts
(call $_abort) ;; ()
;; aborts
(call $abort_on_cannot_grow_memory) ;; ()
;; aborts
(call $abort (i32.const 48)) ;; (message: u32)
;; print ">>> Second"
(call $printf (i32.const 24) (i32.const 0))
(drop)
)
)

27
examples/em_exit.wat Normal file
View File

@ -0,0 +1,27 @@
(module
(type $t1 (func (param i32)))
(func $putchar (import "env" "putchar") (type $t1))
(func $sys_exit (import "env" "___syscall1") (type $t1))
(memory 1)
(func $main (export "main")
;; print "Hello"
(call $putchar (i32.const 72))
(call $putchar (i32.const 101))
(call $putchar (i32.const 108))
(call $putchar (i32.const 108))
(call $putchar (i32.const 111))
(call $putchar (i32.const 32))
;; exit abruptly
(call $sys_exit (i32.const 255)) ;; (status: c_int) -> c_int
;; print " World!"
(call $putchar (i32.const 87))
(call $putchar (i32.const 111))
(call $putchar (i32.const 114))
(call $putchar (i32.const 108))
(call $putchar (i32.const 100))
(call $putchar (i32.const 33))
(call $putchar (i32.const 10))
)
)

43
examples/em_read_file.wat Normal file
View File

@ -0,0 +1,43 @@
(module
(type $t1 (func (param i32)))
(type $t2 (func (param i32 i32 i32) (result i32)))
(type $t3 (func (param i32) (result i32)))
(type $t4 (func (param i32 i32) (result i32)))
(func $putchar (import "env" "putchar") (type $t1))
(func $printf (import "env" "printf") (type $t4))
(func $sys_open (import "env" "___syscall5") (type $t2))
(func $sys_read (import "env" "___syscall3") (type $t2))
(func $sys_close (import "env" "___syscall6") (type $t3))
(memory 1)
(data $filename (i32.const 0) "/Users/xxxx/Desktop/hello.txt\00")
(func $main (export "main")
;; declare variables
(local $string_buf_addr i32)
(local $string_buf_len i32)
(local $file_access_flag i32)
(local $file_permission_flag i32)
(local $file_descriptor i32)
;; set variables
(set_local $string_buf_addr (i32.const 72)) ;; string_buf_addr at offset 72
(set_local $string_buf_len (i32.const 10)) ;; string_buf_len is 5
(set_local $file_access_flag (i32.const 02)) ;; file_access_flag has O_RDWR permission
(set_local $file_permission_flag (i32.const 700)) ;; file_permission_flag has S_IRWXU permission
;; open file
(call $sys_open (i32.const 0) (get_local $file_access_flag) (get_local $file_permission_flag)) ;; (path: u32, flags: c_int, mode: c_int) -> c_int
(set_local $file_descriptor) ;; set file_descriptor to the value returned by sys_open
;; read file content
(call $sys_read (get_local $file_descriptor) (get_local $string_buf_addr) (get_local $string_buf_len)) ;; (fd: c_int, buf: u32, count: size_t) -> ssize_t
(drop) ;; ignoring errors
;; close file
(call $sys_close (get_local $file_descriptor)) ;; (fd: c_int) -> c_int
(drop) ;; ignoring errors
;; print file content
(call $printf (get_local $string_buf_addr) (i32.const 0))
(drop) ;; ignoring errors
)
)

View File

@ -0,0 +1,513 @@
## HOST APIS
#### EMSCRIPTEN APIS
###### PROCESS
- **\_abort** ✅     [:top:](#host-apis)
```rust
fn _abort()
```
- **abort** ✅ 🔥     [:top:](#host-apis)
```rust
fn abort(message: u32, instance: &mut Instance)
```
- **abort_on_cannot_grow_memory**    [:top:](#host-apis)
```rust
fn abort_on_cannot_grow_memory()
```
###### TIMING
- **\_clock_gettime**     [:top:](#host-apis)
```rust
```
###### ENVIRONMENT
- **\_getenv**     [:top:](#host-apis)
```rust
```
###### THREAD
- **\_pthread_getspecific**     [:top:](#host-apis)
```rust
```
- **\_pthread_key_create**     [:top:](#host-apis)
```rust
```
- **\_pthread_setspecific**     [:top:](#host-apis)
```rust
```
- **\_unsetenv**     [:top:](#host-apis)
```rust
```
- **\_\_\_lock**     [:top:](#host-apis)
```rust
```
- **\_\_\_unlock**     [:top:](#host-apis)
```rust
```
###### MEMORY
- **\_emscripten_memcpy_big** ✅ 🔥     [:top:](#host-apis)
```rust
fn _emscripten_memcpy_big(dest: u32, src: u32, len: u32, instance: &mut Instance) -> u32
```
- **enlarge_memory**    [:top:](#host-apis)
```rust
fn enlarge_memory()
```
- **get_total_memory**    [:top:](#host-apis)
```rust
fn get_total_memory(instance: &mut Instance) -> u32
```
###### TIMING
- **\_clock_gettime**     [:top:](#host-apis)
```rust
```
###### STATUS
- **\_\_\_set_err_no**     [:top:](#host-apis)
```rust
```
---
#### EMSCRIPTEN SYSCALLS
- **access** (\_\_\_syscall33)     [:top:](#host-apis)
```rust
```
- **acct** (\_\_\_syscall51)     [:top:](#host-apis)
```rust
```
- **chdir** (\_\_\_syscall12)     [:top:](#host-apis)
```rust
```
- **chmod** (\_\_\_syscall15)     [:top:](#host-apis)
```rust
```
- **chown** (\_\_\_syscall212)     [:top:](#host-apis)
```rust
```
- **clock_nanosleep** (\_\_\_syscall265)     [:top:](#host-apis)
```rust
```
- **close** (\_\_\_syscall6) ✅ ❗️     [:top:](#host-apis)
```rust
fn close(fd: c_int) -> c_int
```
- **dup** (\_\_\_syscall330)     [:top:](#host-apis)
```rust
```
- **dup** (\_\_\_syscall41)     [:top:](#host-apis)
```rust
```
- **dup** (\_\_\_syscall63)     [:top:](#host-apis)
```rust
```
- **exit** (\_\_\_syscall1) ✅     [:top:](#host-apis)
```rust
fn exit(status: c_int)
```
- **faccessat** (\_\_\_syscall307)     [:top:](#host-apis)
```rust
```
- **fadvise** (\_\_\_syscall272)     [:top:](#host-apis)
```rust
```
- **fallocate** (\_\_\_syscall324)     [:top:](#host-apis)
```rust
```
- **fchdir** (\_\_\_syscall133)     [:top:](#host-apis)
```rust
```
- **fchmod** (\_\_\_syscall94)     [:top:](#host-apis)
```rust
```
- **fchmodat** (\_\_\_syscall306)     [:top:](#host-apis)
```rust
```
- **fchown** (\_\_\_syscall207)     [:top:](#host-apis)
```rust
```
- **fchownat** (\_\_\_syscall298)     [:top:](#host-apis)
```rust
```
- **fcntl** (\_\_\_syscall221)     [:top:](#host-apis)
```rust
```
- **fdatasync** (\_\_\_syscall148)     [:top:](#host-apis)
```rust
```
- **fstat** (\_\_\_syscall197)     [:top:](#host-apis)
```rust
```
- **fstatat** (\_\_\_syscall300)     [:top:](#host-apis)
```rust
```
- **fstatfs** (\_\_\_syscall269)     [:top:](#host-apis)
```rust
```
- **fsync** (\_\_\_syscall118)     [:top:](#host-apis)
```rust
```
- **ftruncate** (\_\_\_syscall194)     [:top:](#host-apis)
```rust
```
- **futimesat** (\_\_\_syscall299)     [:top:](#host-apis)
```rust
```
- **getcwd** (\_\_\_syscall183)     [:top:](#host-apis)
```rust
```
- **getdents** (\_\_\_syscall220)     [:top:](#host-apis)
```rust
```
- **getgid** (\_\_\_syscall202)     [:top:](#host-apis)
```rust
```
- **getgroups** (\_\_\_syscall205)     [:top:](#host-apis)
```rust
```
- **getpgid** (\_\_\_syscall132)     [:top:](#host-apis)
```rust
```
- **getpgrp** (\_\_\_syscall65)     [:top:](#host-apis)
```rust
```
- **getpid** (\_\_\_syscall20)     [:top:](#host-apis)
```rust
```
- **getppid** (\_\_\_syscall64)     [:top:](#host-apis)
```rust
```
- **getpriority** (\_\_\_syscall96)     [:top:](#host-apis)
```rust
```
- **getresgid** (\_\_\_syscall211)     [:top:](#host-apis)
```rust
```
- **getrusage** (\_\_\_syscall77)     [:top:](#host-apis)
```rust
```
- **getsid** (\_\_\_syscall147)     [:top:](#host-apis)
```rust
```
- **ioctl** (\_\_\_syscall54)     [:top:](#host-apis)
```rust
```
- **lchown** (\_\_\_syscall198)     [:top:](#host-apis)
```rust
```
- **link** (\_\_\_syscall9)     [:top:](#host-apis)
```rust
```
- **linkat** (\_\_\_syscall303)     [:top:](#host-apis)
```rust
```
- **llseek** (\_\_\_syscall140)     [:top:](#host-apis)
```rust
```
- **lstat** (\_\_\_syscall196)     [:top:](#host-apis)
```rust
```
- **madvise** (\_\_\_syscall219)     [:top:](#host-apis)
```rust
```
- **mincore** (\_\_\_syscall218)     [:top:](#host-apis)
```rust
```
- **mkdir** (\_\_\_syscall39)     [:top:](#host-apis)
```rust
```
- **mkdirat** (\_\_\_syscall296)     [:top:](#host-apis)
```rust
```
- **mknod** (\_\_\_syscall14)     [:top:](#host-apis)
```rust
```
- **mknodat** (\_\_\_syscall297)     [:top:](#host-apis)
```rust
```
- **mmap** (\_\_\_syscall192)     [:top:](#host-apis)
```rust
```
- **mprotect** (\_\_\_syscall125)     [:top:](#host-apis)
```rust
```
- **mremap** (\_\_\_syscall163)     [:top:](#host-apis)
```rust
```
- **msync** (\_\_\_syscall144)     [:top:](#host-apis)
```rust
```
- **munlockall** (\_\_\_syscall153)     [:top:](#host-apis)
```rust
```
- **munmap** (\_\_\_syscall91)     [:top:](#host-apis)
```rust
```
- **newselect** (\_\_\_syscall142)     [:top:](#host-apis)
```rust
```
- **nice** (\_\_\_syscall34)     [:top:](#host-apis)
```rust
```
- **open** (\_\_\_syscall5) ✅ ❗️ 🔥     [:top:](#host-apis)
```rust
fn open(path: u32, flags: c_int, mode: c_int, instance: &mut Instance) -> c_int
```
- **openat** (\_\_\_syscall295)     [:top:](#host-apis)
```rust
```
- **pause** (\_\_\_syscall29)     [:top:](#host-apis)
```rust
```
- **pipe** (\_\_\_syscall331)     [:top:](#host-apis)
```rust
```
- **pipe** (\_\_\_syscall42)     [:top:](#host-apis)
```rust
```
- **poll** (\_\_\_syscall168)     [:top:](#host-apis)
```rust
```
- **pread** (\_\_\_syscall180)     [:top:](#host-apis)
```rust
```
- **preadv** (\_\_\_syscall333)     [:top:](#host-apis)
```rust
```
- **prlimit** (\_\_\_syscall340)     [:top:](#host-apis)
```rust
```
- **pselect** (\_\_\_syscall308)     [:top:](#host-apis)
```rust
```
- **pwrite** (\_\_\_syscall181)     [:top:](#host-apis)
```rust
```
- **pwritev** (\_\_\_syscall334)     [:top:](#host-apis)
```rust
```
- **read** (\_\_\_syscall3) ✅ ❗️     [:top:](#host-apis)
```rust
fn read(fd: c_int, buf: u32, count: size_t, instance: &mut Instance) -> ssize_t
```
- **readlink** (\_\_\_syscall85)     [:top:](#host-apis)
```rust
```
- **readlinkat** (\_\_\_syscall305)     [:top:](#host-apis)
```rust
```
- **readv** (\_\_\_syscall145)     [:top:](#host-apis)
```rust
```
- **recvmmsg** (\_\_\_syscall337)     [:top:](#host-apis)
```rust
```
- **rename** (\_\_\_syscall38)     [:top:](#host-apis)
```rust
```
- **renameat** (\_\_\_syscall302)     [:top:](#host-apis)
```rust
```
- **rmdir** (\_\_\_syscall40)     [:top:](#host-apis)
```rust
```
- **rt_sigqueueinfo** (\_\_\_syscall178)     [:top:](#host-apis)
```rust
```
- **sendmmsg** (\_\_\_syscall345)     [:top:](#host-apis)
```rust
```
- **setdomainname** (\_\_\_syscall121)     [:top:](#host-apis)
```rust
```
- **setgid** (\_\_\_syscall214)     [:top:](#host-apis)
```rust
```
- **setitimer** (\_\_\_syscall104)     [:top:](#host-apis)
```rust
```
- **setpgid** (\_\_\_syscall57)     [:top:](#host-apis)
```rust
```
- **setpriority** (\_\_\_syscall97)     [:top:](#host-apis)
```rust
```
- **setresgid** (\_\_\_syscall210)     [:top:](#host-apis)
```rust
```
- **setrlimit** (\_\_\_syscall75)     [:top:](#host-apis)
```rust
```
- **setsid** (\_\_\_syscall66)     [:top:](#host-apis)
```rust
```
- **socketcall** (\_\_\_syscall102)     [:top:](#host-apis)
```rust
```
- **stat** (\_\_\_syscall195)     [:top:](#host-apis)
```rust
```
- **statfs** (\_\_\_syscall268)     [:top:](#host-apis)
```rust
```
- **symlink** (\_\_\_syscall83)     [:top:](#host-apis)
```rust
```
- **symlinkat** (\_\_\_syscall304)     [:top:](#host-apis)
```rust
```
- **sync** (\_\_\_syscall36)     [:top:](#host-apis)
```rust
```
- **truncate** (\_\_\_syscall193)     [:top:](#host-apis)
```rust
```
- **ugetrlimit** (\_\_\_syscall191)     [:top:](#host-apis)
```rust
```
- **umask** (\_\_\_syscall60)     [:top:](#host-apis)
```rust
```
- **uname** (\_\_\_syscall122)     [:top:](#host-apis)
```rust
```
- **unlink** (\_\_\_syscall10)     [:top:](#host-apis)
```rust
```
- **unlinkat** (\_\_\_syscall301)     [:top:](#host-apis)
```rust
```
- **utimensat** (\_\_\_syscall320)     [:top:](#host-apis)
```rust
```
- **wait** (\_\_\_syscall114)     [:top:](#host-apis)
```rust
```
- **write** (\_\_\_syscall4)     [:top:](#host-apis)
```rust
```
- **writev** (\_\_\_syscall146)     [:top:](#host-apis)
```rust
```

View File

@ -0,0 +1,22 @@
use super::super::host;
/// NOTE: These syscalls only support wasm_32 for now because they take u32 offset
use libc::{c_int};
use std::ffi::CStr;
use std::os::raw::c_char;
use crate::webassembly::Instance;
/// emscripten: _getenv
pub extern "C" fn _getenv(name_ptr: c_int, instance: &mut Instance) -> c_int {
debug!("emscripten::_getenv {}", name_ptr);
let name = unsafe {
let memory_name_ptr = instance.memory_offset_addr(0, name_ptr as usize) as *const c_char;
CStr::from_ptr(memory_name_ptr).to_str().unwrap()
};
match host::get_env(name, instance) {
Ok(_) => {
unimplemented!();
}
Err(_) => 0,
}
}

View File

@ -2,7 +2,12 @@ use libc::printf as _printf;
use crate::webassembly::Instance;
/// putchar
pub use libc::putchar;
/// printf
pub extern "C" fn printf(memory_offset: i32, extra: i32, instance: &Instance) -> i32 {
debug!("emscripten::printf");
let mem = &instance.memories[0];
return unsafe {
let base_memory_offset = mem.mmap.as_ptr().offset(memory_offset as isize) as *const i8;

View File

@ -0,0 +1,31 @@
use libc::{c_void, memcpy, size_t};
use crate::webassembly::Instance;
/// emscripten: _emscripten_memcpy_big
pub extern "C" fn _emscripten_memcpy_big(
dest: u32,
src: u32,
len: u32,
instance: &mut Instance,
) -> u32 {
debug!("emscripten::_emscripten_memcpy_big");
let dest_addr = instance.memory_offset_addr(0, dest as usize) as *mut c_void;
let src_addr = instance.memory_offset_addr(0, src as usize) as *mut c_void;
unsafe {
memcpy(dest_addr, src_addr, len as size_t);
}
dest
}
/// emscripten: getTotalMemory
pub extern "C" fn get_total_memory(instance: &mut Instance) -> u32 {
debug!("emscripten::get_total_memory");
instance.memories[0].current_size()
}
/// emscripten: enlargeMemory
pub extern "C" fn enlarge_memory(_instance: &mut Instance) {
debug!("emscripten::enlarge_memory");
// instance.memories[0].grow(100);
}

118
src/apis/emscripten/mod.rs Normal file
View File

@ -0,0 +1,118 @@
use crate::webassembly::{ImportObject, ImportValue};
// EMSCRIPTEN APIS
mod env;
mod io;
mod memory;
mod process;
mod syscalls;
mod utils;
// SYSCALLS
pub use self::utils::is_emscripten_module;
pub fn generate_emscripten_env<'a, 'b>() -> ImportObject<&'a str, &'b str> {
let mut import_object = ImportObject::new();
import_object.set("env", "printf", ImportValue::Func(io::printf as *const u8));
import_object.set(
"env",
"putchar",
ImportValue::Func(io::putchar as *const u8),
);
// Emscripten Env
import_object.set(
"env",
"_getenv",
ImportValue::Func(env::_getenv as *const u8),
);
// Emscripten syscalls
import_object.set(
"env",
"___syscall3",
ImportValue::Func(syscalls::___syscall3 as *const u8),
);
import_object.set(
"env",
"___syscall4",
ImportValue::Func(syscalls::___syscall4 as *const u8),
);
import_object.set(
"env",
"___syscall5",
ImportValue::Func(syscalls::___syscall5 as *const u8),
);
import_object.set(
"env",
"___syscall54",
ImportValue::Func(syscalls::___syscall54 as *const u8),
);
import_object.set(
"env",
"___syscall122",
ImportValue::Func(syscalls::___syscall122 as *const u8),
);
// Emscripten other APIs
import_object.set(
"env",
"abort",
ImportValue::Func(process::em_abort as *const u8),
);
import_object.set(
"env",
"_abort",
ImportValue::Func(process::_abort as *const u8),
);
import_object.set(
"env",
"abortOnCannotGrowMemory",
ImportValue::Func(process::abort_on_cannot_grow_memory as *const u8),
);
import_object.set(
"env",
"_emscripten_memcpy_big",
ImportValue::Func(memory::_emscripten_memcpy_big as *const u8),
);
import_object.set(
"env",
"enlargeMemory",
ImportValue::Func(memory::enlarge_memory as *const u8),
);
import_object.set(
"env",
"getTotalMemory",
ImportValue::Func(memory::get_total_memory as *const u8),
);
import_object
}
#[cfg(test)]
mod tests {
use super::generate_emscripten_env;
use crate::webassembly::{instantiate, Export, Instance};
#[test]
fn test_putchar() {
let wasm_bytes = include_wast2wasm_bytes!("tests/putchar.wast");
let import_object = generate_emscripten_env();
let result_object = instantiate(wasm_bytes, import_object).expect("Not compiled properly");
let func_index = match result_object.module.info.exports.get("main") {
Some(&Export::Function(index)) => index,
_ => panic!("Function not found"),
};
let main: fn(&Instance) = get_instance_function!(result_object.instance, func_index);
main(&result_object.instance);
}
#[test]
fn test_print() {
let wasm_bytes = include_wast2wasm_bytes!("tests/printf.wast");
let import_object = generate_emscripten_env();
let result_object = instantiate(wasm_bytes, import_object).expect("Not compiled properly");
let func_index = match result_object.module.info.exports.get("main") {
Some(&Export::Function(index)) => index,
_ => panic!("Function not found"),
};
let main: fn(&Instance) = get_instance_function!(result_object.instance, func_index);
main(&result_object.instance);
}
}

View File

@ -0,0 +1,44 @@
use libc::{
// size_t,
// ssize_t,
abort,
// c_int,
// c_void,
c_char,
};
use crate::webassembly::Instance;
use std::ffi::CStr;
extern "C" fn abort_with_message(message: &str) {
debug!("emscripten::abort_with_message");
println!("{}", message);
_abort();
}
/// emscripten: _abort
pub extern "C" fn _abort() {
debug!("emscripten::_abort");
unsafe {
abort();
}
}
/// emscripten: abort
pub extern "C" fn em_abort(message: u32, instance: &mut Instance) {
debug!("emscripten::em_abort");
let message_addr = instance.memory_offset_addr(0, message as usize) as *mut c_char;
unsafe {
let message = CStr::from_ptr(message_addr)
.to_str()
.unwrap_or("Unexpected abort");
abort_with_message(message);
}
}
/// emscripten: abortOnCannotGrowMemory
pub extern "C" fn abort_on_cannot_grow_memory() {
debug!("emscripten::abort_on_cannot_grow_memory");
abort_with_message("Cannot enlarge memory arrays!");
}

View File

@ -0,0 +1,61 @@
/// NOTE: These syscalls only support wasm_32 for now because they take u32 offset
/// Syscall list: https://www.cs.utexas.edu/~bismith/test/syscalls/syscalls32.html
use libc::{c_int, c_void, ssize_t, write};
use crate::webassembly::Instance;
// A macro to retrieve variadic arguments given a varargs offset
macro_rules! vararg {
($name:ident, $type:ident, $instance:ident, $varargs:ident) => (
let ($name, $varargs) = unsafe {
use std::ptr;
let ptr = $instance.memory_offset_addr(0, $varargs as usize);
let ret = ptr::read(ptr as *const $type);
(ret, $varargs + 4)
};
)
}
/// sys_read
pub extern "C" fn ___syscall3(_which: c_int, _varargs: c_int, _instance: &mut Instance) -> ssize_t {
debug!("emscripten::___syscall3");
0
}
/// sys_write
pub extern "C" fn ___syscall4(_which: c_int, varargs: c_int, instance: &mut Instance) -> c_int {
debug!("emscripten::___syscall4");
vararg!(fd, i32, instance, varargs);
vararg!(buf_ptr, u32, instance, varargs);
vararg!(count, u32, instance, varargs);
debug!("fd: {}, buf_ptr: {}, count: {}", fd, buf_ptr, count);
let buf = instance.memory_offset_addr(0, buf_ptr as usize) as *const c_void;
unsafe { write(fd, buf, count as usize) as i32 }
}
/// sys_open
pub extern "C" fn ___syscall5(_which: c_int, varargs: c_int, instance: &mut Instance) -> c_int {
debug!("emscripten::___syscall5");
vararg!(pathname, u32, instance, varargs);
vararg!(flags, u32, instance, varargs);
vararg!(mode, u32, instance, varargs);
debug!("pathname: {}, flags: {}, mode: {}", pathname, flags, mode);
-2
}
// sys_ioctl
pub extern "C" fn ___syscall54(_which: c_int, varargs: c_int, instance: &mut Instance) -> c_int {
debug!("emscripten::___syscall54");
vararg!(stream, u32, instance, varargs);
vararg!(op, u32, instance, varargs);
debug!("stream: {}, op: {}", stream, op);
0
}
// sys_newuname
pub extern "C" fn ___syscall122(_which: c_int, varargs: c_int, instance: &mut Instance) -> c_int {
debug!("emscripten::___syscall122");
vararg!(buf, u32, instance, varargs);
debug!("buf: {}", buf);
0
}

View File

@ -0,0 +1,9 @@
(module
(table 0 anyfunc)
(memory $0 1)
(export "memory" (memory $0))
(export "main" (func $main))
(func $main (; 1 ;) (result i32)
(i32.const 0)
)
)

View File

@ -0,0 +1,17 @@
(module
(type $FUNCSIG$ii (func (param i32) (result i32)))
(import "env" "puts" (func $puts (param i32) (result i32)))
(table 0 anyfunc)
(memory $0 1)
(data (i32.const 16) "hello, world!\00")
(export "memory" (memory $0))
(export "main" (func $main))
(func $main (; 1 ;) (result i32)
(drop
(call $puts
(i32.const 16)
)
)
(i32.const 0)
)
)

View File

@ -0,0 +1,34 @@
use crate::webassembly::module::Module;
/// We check if a provided module is an Emscripten generated one
pub fn is_emscripten_module(module: &Module) -> bool {
for (module, _field) in &module.info.imported_funcs {
if module == "env" {
return true;
}
}
return false;
}
#[cfg(test)]
mod tests {
use super::super::generate_emscripten_env;
use super::is_emscripten_module;
use crate::webassembly::instantiate;
#[test]
fn should_detect_emscripten_files() {
let wasm_bytes = include_wast2wasm_bytes!("tests/is_emscripten_true.wast");
let import_object = generate_emscripten_env();
let result_object = instantiate(wasm_bytes, import_object).expect("Not compiled properly");
assert!(is_emscripten_module(&result_object.module));
}
#[test]
fn should_detect_non_emscripten_files() {
let wasm_bytes = include_wast2wasm_bytes!("tests/is_emscripten_false.wast");
let import_object = generate_emscripten_env();
let result_object = instantiate(wasm_bytes, import_object).expect("Not compiled properly");
assert!(!is_emscripten_module(&result_object.module));
}
}

4
src/apis/host/mod.rs Normal file
View File

@ -0,0 +1,4 @@
mod posix;
// TODO: Make portable
pub use self::posix::*;

View File

@ -0,0 +1,7 @@
use crate::webassembly::Instance;
use std::env;
pub extern "C" fn get_env(name: &str, _instance: &mut Instance) -> Result<String, env::VarError> {
debug!("host::get_env({:?})", name);
env::var(name)
}

View File

@ -0,0 +1,5 @@
pub mod env;
pub mod syscalls;
pub use self::env::*;
pub use self::syscalls::*;

View File

@ -0,0 +1,48 @@
// NOTE: These syscalls only support wasm_32 for now because they take u32 offset
// use libc::{
// c_int,
// c_void,
// size_t,
// ssize_t,
// exit,
// read,
// write,
// open,
// close,
// };
// use crate::webassembly::{Instance};
// /// emscripten: ___syscall1
// pub extern "C" fn sys_exit(status: c_int, _instance: &mut Instance) {
// debug!("host::sys_exit");
// unsafe { exit(status); }
// }
// /// emscripten: ___syscall3
// pub extern "C" fn sys_read(fd: c_int, buf: *mut c_void, count: size_t, instance: &mut Instance) -> ssize_t {
// debug!("host::sys_read");
// let buf_addr = instance.memory_offset_addr(0, buf as usize) as *mut c_void;
// unsafe { read(fd, buf_addr, count) }
// }
// /// emscripten: ___syscall4
// pub extern "C" fn sys_write(which: c_int, mode: c_int, instance: &mut Instance) -> c_int {
// debug!("host::sys_write({}, {})", which, mode);
// // unsafe { write(which, mode) };
// 0
// }
// /// emscripten: ___syscall5
// pub extern "C" fn sys_open(path: u32, flags: c_int, mode: c_int, instance: &mut Instance) -> c_int {
// debug!("host::sys_open({}, {}, {})", path, flags, mode);
// // let path_addr = instance.memory_offset_addr(0, path as usize) as *const i8;
// // unsafe { open(path_addr, flags, mode) };
// -2
// }
// /// emscripten: ___syscall6
// pub extern "C" fn sys_close(fd: c_int, _instance: &mut Instance) -> c_int {
// debug!("host::sys_close");
// unsafe { close(fd) }
// }

4
src/apis/mod.rs Normal file
View File

@ -0,0 +1,4 @@
pub mod emscripten;
pub mod host;
pub use self::emscripten::{generate_emscripten_env, is_emscripten_module};

View File

@ -11,17 +11,13 @@ impl<T> UncheckedSlice<T> {
#[inline]
pub fn get_unchecked(&self, index: usize) -> &T {
let ptr = self.ptr.as_ptr();
unsafe {
&*ptr.add(index)
}
unsafe { &*ptr.add(index) }
}
#[inline]
pub fn get_unchecked_mut(&mut self, index: usize) -> &mut T {
let ptr = self.ptr.as_ptr();
unsafe {
&mut *(ptr.add(index) as *mut _)
}
unsafe { &mut *(ptr.add(index) as *mut _) }
}
pub unsafe fn dangling() -> UncheckedSlice<T> {

View File

@ -1,7 +0,0 @@
use crate::webassembly::Instance;
use std::process;
pub extern "C" fn abort(_code: i32, _instance: &Instance) {
process::abort();
// abort!("Aborted")
}

View File

@ -1,46 +0,0 @@
use crate::webassembly::{ImportObject, ImportValue};
mod abort;
mod printf;
mod putchar;
pub fn generate_emscripten_env<'a, 'b>() -> ImportObject<&'a str, &'b str> {
let mut import_object = ImportObject::new();
import_object.set("env", "printf", ImportValue::Func(printf::printf as *const u8));
import_object.set("env", "putchar", ImportValue::Func(putchar::putchar as *const u8));
import_object.set("env", "abort", ImportValue::Func(abort::abort as *const u8));
import_object.set("env", "_abort", ImportValue::Func(abort::abort as *const u8));
import_object
}
#[cfg(test)]
mod tests {
use super::generate_emscripten_env;
use crate::webassembly::{instantiate, Export, Instance};
#[test]
fn test_putchar() {
let wasm_bytes = include_wast2wasm_bytes!("tests/putchar.wast");
let import_object = generate_emscripten_env();
let result_object = instantiate(wasm_bytes, import_object).expect("Not compiled properly");
let func_index = match result_object.module.info.exports.get("main") {
Some(&Export::Function(index)) => index,
_ => panic!("Function not found"),
};
let main: fn(&Instance) = get_instance_function!(result_object.instance, func_index);
main(&result_object.instance);
}
#[test]
fn test_print() {
let wasm_bytes = include_wast2wasm_bytes!("tests/printf.wast");
let import_object = generate_emscripten_env();
let result_object = instantiate(wasm_bytes, import_object).expect("Not compiled properly");
let func_index = match result_object.module.info.exports.get("main") {
Some(&Export::Function(index)) => index,
_ => panic!("Function not found"),
};
let main: fn(&Instance) = get_instance_function!(result_object.instance, func_index);
main(&result_object.instance);
}
}

View File

@ -1 +0,0 @@
pub use libc::putchar;

View File

@ -1,3 +0,0 @@
pub mod emscripten;
pub use self::emscripten::generate_emscripten_env;

View File

@ -25,8 +25,8 @@ use structopt::StructOpt;
#[macro_use]
mod macros;
pub mod apis;
pub mod common;
pub mod linkers;
pub mod sighandler;
#[cfg(test)]
mod spectests;
@ -71,22 +71,34 @@ fn execute_wasm(wasm_path: PathBuf) -> Result<(), String> {
wasm_binary = wabt::wat2wasm(wasm_binary)
.map_err(|err| format!("Can't convert from wast to wasm: {:?}", err))?;
}
let import_object = linkers::generate_emscripten_env();
// TODO: We should instantiate after compilation, so we provide the
// emscripten environment conditionally based on the module
let import_object = apis::generate_emscripten_env();
let webassembly::ResultObject { module, instance } =
webassembly::instantiate(wasm_binary, import_object)
.map_err(|err| format!("Can't instantiate the WebAssembly module: {}", err))?;
// webassembly::utils::print_instance_offsets(&instance);
let func_index = instance
.start_func
.unwrap_or_else(|| match module.info.exports.get("main").or(module.info.exports.get("_main")) {
if apis::is_emscripten_module(&module) {
let func_index = match module.info.exports.get("_main") {
Some(&webassembly::Export::Function(index)) => index,
_ => panic!("Main function not found"),
});
let main: fn(&webassembly::Instance) = get_instance_function!(instance, func_index);
main(&instance);
_ => panic!("_main emscripten function not found"),
};
let main: extern "C" fn(u32, u32, &webassembly::Instance) =
get_instance_function!(instance, func_index);
main(0, 0, &instance);
} else {
let func_index =
instance
.start_func
.unwrap_or_else(|| match module.info.exports.get("main") {
Some(&webassembly::Export::Function(index)) => index,
_ => panic!("Main function not found"),
});
let main: extern "C" fn(&webassembly::Instance) =
get_instance_function!(instance, func_index);
main(&instance);
}
Ok(())
}

View File

@ -3,10 +3,10 @@
//! or webassembly::Memory objects.
// Code inspired from: https://stackoverflow.com/a/45795699/1072990
// Adapted to the Webassembly use case
use crate::webassembly::LinearMemory;
use std::borrow::Borrow;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use crate::webassembly::LinearMemory;
// We introduced the Pair and BorrowedPair types. We can't use (A, B)
// directly due to the orphan rule E0210. This is fine since the map
@ -75,8 +75,7 @@ impl<A: Eq + Hash, B: Eq + Hash> ImportObject<A, B> {
}
pub fn get(&self, a: &A, b: &B) -> Option<&ImportValue> {
self.map
.get(&BorrowedPair(a, b) as &KeyPair<A, B>)
self.map.get(&BorrowedPair(a, b) as &KeyPair<A, B>)
}
pub fn set(&mut self, a: A, b: B, v: ImportValue) {
@ -127,6 +126,9 @@ mod tests {
fn x() {}
let mut import_object = ImportObject::new();
import_object.set("abc", "def", ImportValue::Func(x as *const u8));
assert_eq!(*import_object.get(&"abc", &"def").unwrap(), ImportValue::Func(x as *const u8));
assert_eq!(
*import_object.get(&"abc", &"def").unwrap(),
ImportValue::Func(x as *const u8)
);
}
}

View File

@ -6,28 +6,28 @@
//! synchronously instantiate a given webassembly::Module object. However, the
//! primary way to get an Instance is through the asynchronous
//! webassembly::instantiate_streaming() function.
use cranelift_codegen::ir::{LibCall, Function};
use cranelift_codegen::{binemit, Context};
use cranelift_codegen::ir::{Function, LibCall};
use cranelift_codegen::isa::TargetIsa;
use cranelift_codegen::{binemit, Context};
use cranelift_entity::EntityRef;
use cranelift_wasm::{FuncIndex, GlobalInit};
use rayon::prelude::*;
use region;
use std::iter::FromIterator;
use std::iter::Iterator;
use std::mem::size_of;
use std::ptr::write_unaligned;
use std::slice;
use std::sync::Arc;
use std::iter::FromIterator;
use std::mem::size_of;
use super::super::common::slice::{BoundedSlice, UncheckedSlice};
use super::errors::ErrorKind;
use super::import_object::{ImportObject, ImportValue};
use super::math_intrinsics;
use super::memory::LinearMemory;
use super::module::{Export, ImportableExportable, Module};
use super::relocation::{Reloc, RelocSink, RelocationType};
use super::math_intrinsics;
type TablesSlice = UncheckedSlice<BoundedSlice<usize>>;
type MemoriesSlice = UncheckedSlice<BoundedSlice<u8>>;
@ -114,7 +114,6 @@ pub struct DataPointers {
// Pointer to globals
pub globals: GlobalsSlice,
}
pub struct InstanceOptions {
@ -125,7 +124,7 @@ pub struct InstanceOptions {
pub isa: Box<TargetIsa>,
}
extern fn mock_fn() -> i32 {
extern "C" fn mock_fn() -> i32 {
return 0;
}
@ -135,7 +134,10 @@ struct CompiledFunction {
trap_sink: binemit::NullTrapSink,
}
fn compile_function(isa: &TargetIsa, function_body: &Function) -> Result<CompiledFunction, ErrorKind> {
fn compile_function(
isa: &TargetIsa,
function_body: &Function,
) -> Result<CompiledFunction, ErrorKind> {
let mut func_context = Context::for_function(function_body.to_owned());
let mut code_buf: Vec<u8> = Vec::new();
@ -152,7 +154,7 @@ fn compile_function(isa: &TargetIsa, function_body: &Function) -> Result<Compile
Ok(CompiledFunction {
code_buf,
reloc_sink,
trap_sink
trap_sink,
})
}
@ -191,23 +193,24 @@ impl Instance {
// We walk through the imported functions and set the relocations
// for each of this functions to be an empty vector (as is defined outside of wasm)
for (module, field) in module.info.imported_funcs.iter() {
let imported = import_object
.get(&module.as_str(), &field.as_str());
let imported = import_object.get(&module.as_str(), &field.as_str());
let function: &*const u8 = match imported {
Some(ImportValue::Func(f)) => f,
None => {
if options.mock_missing_imports {
debug!("The import {}.{} is not provided, therefore will be mocked.", module, field);
debug!(
"The import {}.{} is not provided, therefore will be mocked.",
module, field
);
&(mock_fn as *const u8)
}
else {
} else {
return Err(ErrorKind::LinkError(format!(
"Imported function {}.{} was not provided in the import_functions",
module, field
)));
}
},
other => panic!("Expected function import, received {:?}", other)
}
other => panic!("Expected function import, received {:?}", other),
};
// println!("GET FUNC {:?}", function);
import_functions.push(*function);
@ -218,15 +221,20 @@ impl Instance {
// Compile the functions (from cranelift IR to machine code)
let values: Vec<&Function> = Vec::from_iter(module.info.function_bodies.values());
// let isa: &TargetIsa = &*options.isa;
let compiled_funcs: Vec<CompiledFunction> = values.par_iter().map(|function_body| -> CompiledFunction {
// let r = *Arc::from_raw(isa_ptr);
compile_function(&*options.isa, function_body).unwrap()
// unimplemented!()
}).collect();
let compiled_funcs: Vec<CompiledFunction> = values
.par_iter()
.map(|function_body| -> CompiledFunction {
// let r = *Arc::from_raw(isa_ptr);
compile_function(&*options.isa, function_body).unwrap()
// unimplemented!()
}).collect();
for compiled_func in compiled_funcs.into_iter() {
let CompiledFunction {code_buf, reloc_sink, ..} = compiled_func;
let CompiledFunction {
code_buf,
reloc_sink,
..
} = compiled_func;
// let func_offset = code_buf;
protect_codebuf(&code_buf).unwrap();
@ -322,33 +330,39 @@ impl Instance {
};
for (i, global) in module.info.globals.iter().enumerate() {
let ImportableExportable {entity, import_name, ..} = global;
let ImportableExportable {
entity,
import_name,
..
} = global;
let value: i64 = match entity.initializer {
GlobalInit::I32Const(n) => n as _,
GlobalInit::I64Const(n) => n,
GlobalInit::F32Const(f) => f as _, // unsafe { mem::transmute(f as f64) },
GlobalInit::F64Const(f) => f as _, // unsafe { mem::transmute(f) },
GlobalInit::GlobalRef(global_index) => {
globals_data[global_index.index()]
}
GlobalInit::GlobalRef(global_index) => globals_data[global_index.index()],
GlobalInit::Import() => {
let (module_name, field_name) = import_name.as_ref().expect("Expected a import name for the global import");
let imported = import_object.get(&module_name.as_str(), &field_name.as_str());
let (module_name, field_name) = import_name
.as_ref()
.expect("Expected a import name for the global import");
let imported =
import_object.get(&module_name.as_str(), &field_name.as_str());
match imported {
Some(ImportValue::Global(value)) => {
*value
},
Some(ImportValue::Global(value)) => *value,
None => {
if options.mock_missing_globals {
0
} else {
panic!(
"Imported global value was not provided ({}.{})",
module_name, field_name
)
}
else {
panic!("Imported global value was not provided ({}.{})", module_name, field_name)
}
},
_ => {
panic!("Expected global import, but received {:?} ({}.{})", imported, module_name, field_name)
}
_ => panic!(
"Expected global import, but received {:?} ({}.{})",
imported, module_name, field_name
),
}
}
};
@ -367,27 +381,29 @@ impl Instance {
for table in &module.info.tables {
let table: Vec<usize> = match table.import_name.as_ref() {
Some((module_name, field_name)) => {
let imported = import_object.get(&module_name.as_str(), &field_name.as_str());
let imported =
import_object.get(&module_name.as_str(), &field_name.as_str());
match imported {
Some(ImportValue::Table(t)) => {
t.to_vec()
},
Some(ImportValue::Table(t)) => t.to_vec(),
None => {
if options.mock_missing_tables {
let len = table.entity.size;
let mut v = Vec::with_capacity(len);
v.resize(len, 0);
v
} else {
panic!(
"Imported table value was not provided ({}.{})",
module_name, field_name
)
}
else {
panic!("Imported table value was not provided ({}.{})", module_name, field_name)
}
},
_ => {
panic!("Expected global table, but received {:?} ({}.{})", imported, module_name, field_name)
}
_ => panic!(
"Expected global table, but received {:?} ({}.{})",
imported, module_name, field_name
),
}
},
}
None => {
let len = table.entity.size;
let mut v = Vec::with_capacity(len);
@ -401,10 +417,8 @@ impl Instance {
// instantiate tables
for table_element in &module.info.table_elements {
let base = match table_element.base {
Some(global_index) => {
globals_data[global_index.index()] as usize
},
None => 0
Some(global_index) => globals_data[global_index.index()] as usize,
None => 0,
};
let table = &mut tables[table_element.table_index.index()];
@ -415,7 +429,6 @@ impl Instance {
// let func_index = *elem_index - module.info.imported_funcs.len() as u32;
// let func_addr = functions[func_index.index()].as_ptr();
println!("TABLE LENGTH: {}", table.len());
let func_addr = get_function_addr(&func_index, &import_functions, &functions);
table[base + table_element.offset + i] = func_addr as _;
}
@ -431,10 +444,8 @@ impl Instance {
// Get memories in module
for memory in &module.info.memories {
let memory = memory.entity;
let v = LinearMemory::new(
memory.pages_count as u32,
memory.maximum.map(|m| m as u32),
);
let v =
LinearMemory::new(memory.pages_count as u32, memory.maximum.map(|m| m as u32));
memories.push(v);
}
@ -459,10 +470,14 @@ impl Instance {
// TODO: Refactor repetitive code
let tables_pointer: Vec<BoundedSlice<usize>> =
tables.iter().map(|table| table[..].into()).collect();
let memories_pointer: Vec<BoundedSlice<u8>> =
memories.iter().map(
|mem| BoundedSlice::new(&mem[..], mem.current as usize * LinearMemory::WASM_PAGE_SIZE),
).collect();
let memories_pointer: Vec<BoundedSlice<u8>> = memories
.iter()
.map(|mem| {
BoundedSlice::new(
&mem[..],
mem.current as usize * LinearMemory::WASM_PAGE_SIZE,
)
}).collect();
let globals_pointer: GlobalsSlice = globals[..].into();
let data_pointers = DataPointers {
@ -517,6 +532,11 @@ impl Instance {
.as_ref()[address..address + len]
}
pub fn memory_offset_addr(&self, index: usize, offset: usize) -> *const usize {
let mem = &self.memories[index];
unsafe { mem.mmap.as_ptr().offset(offset as isize) as *const usize }
}
// Shows the value of a global variable.
// pub fn inspect_global(&self, global_index: GlobalIndex, ty: ir::Type) -> &[u8] {
// let offset = global_index * 8;
@ -529,6 +549,7 @@ impl Instance {
// }
}
// TODO: Needs to be moved to more appropriate place
extern "C" fn grow_memory(size: u32, memory_index: u32, instance: &mut Instance) -> i32 {
// TODO: Support for only one LinearMemory for now.
debug_assert_eq!(
@ -543,9 +564,13 @@ extern "C" fn grow_memory(size: u32, memory_index: u32, instance: &mut Instance)
if old_mem_size != -1 {
// Get new memory bytes
let new_mem_bytes = (old_mem_size as usize + size as usize) * LinearMemory::WASM_PAGE_SIZE;
let new_mem_bytes = (old_mem_size as usize + size as usize) * LinearMemory::WASM_PAGE_SIZE;
// Update data_pointer
instance.data_pointers.memories.get_unchecked_mut(memory_index as usize).len = new_mem_bytes;
instance
.data_pointers
.memories
.get_unchecked_mut(memory_index as usize)
.len = new_mem_bytes;
}
old_mem_size

View File

@ -65,6 +65,11 @@ impl LinearMemory {
self.current
}
/// Returns the maximum number of wasm pages allowed.
pub fn maximum_size(&self) -> u32 {
self.maximum.unwrap_or(65536)
}
/// Grow memory by the specified amount of pages.
///
/// Returns `None` if memory can't be grown by the specified amount
@ -127,8 +132,7 @@ impl fmt::Debug for LinearMemory {
// Not comparing based on memory content. That would be inefficient.
impl PartialEq for LinearMemory {
fn eq(&self, other: &LinearMemory) -> bool {
self.current == other.current &&
self.maximum == other.maximum
self.current == other.current && self.maximum == other.maximum
}
}

View File

@ -1,19 +1,18 @@
pub mod errors;
pub mod import_object;
pub mod instance;
pub mod math_intrinsics;
pub mod memory;
pub mod module;
pub mod relocation;
pub mod utils;
pub mod math_intrinsics;
use cranelift_codegen::{isa, settings};
use std::panic;
use std::str::FromStr;
use target_lexicon;
use wasmparser;
use wasmparser::WasmDecoder;
use cranelift_codegen::{isa, settings};
use cranelift_codegen::isa::TargetIsa;
pub use self::errors::{Error, ErrorKind};
pub use self::import_object::{ImportObject, ImportValue};
@ -60,7 +59,7 @@ pub fn instantiate(
mock_missing_imports: true,
mock_missing_globals: true,
mock_missing_tables: true,
isa: isa
isa: isa,
},
)?;
debug!("webassembly - instance created");

View File

@ -15,27 +15,14 @@ use cranelift_codegen::isa::{CallConv, TargetFrontendConfig};
use cranelift_entity::{EntityRef, PrimaryMap};
use cranelift_wasm::{
translate_module,
ReturnMode,
DefinedFuncIndex,
FuncEnvironment as FuncEnvironmentTrait,
FuncIndex,
FuncTranslator,
Global,
GlobalIndex,
GlobalVariable,
Memory,
MemoryIndex,
ModuleEnvironment,
SignatureIndex,
Table,
TableIndex,
WasmResult,
translate_module, DefinedFuncIndex, FuncEnvironment as FuncEnvironmentTrait, FuncIndex,
FuncTranslator, Global, GlobalIndex, GlobalVariable, Memory, MemoryIndex, ModuleEnvironment,
ReturnMode, SignatureIndex, Table, TableIndex, WasmResult,
};
use super::errors::ErrorKind;
use super::memory::LinearMemory;
use super::instance::Instance;
use super::memory::LinearMemory;
/// Get the integer type used for representing pointers on this platform.
fn native_pointer_type() -> ir::Type {
@ -56,10 +43,10 @@ pub fn native_pointer_size() -> i32 {
}
/// Convert a TlsData offset into a `Offset32` for a global decl.
fn offset32(offset: usize) -> ir::immediates::Offset32 {
assert!(offset <= i32::max_value() as usize);
(offset as i32).into()
}
// fn offset32(offset: usize) -> ir::immediates::Offset32 {
// assert!(offset <= i32::max_value() as usize);
// (offset as i32).into()
// }
/// Convert a usize offset into a `Imm64` for an iadd_imm.
fn imm64(offset: usize) -> ir::immediates::Imm64 {
@ -89,7 +76,7 @@ impl<T> ImportableExportable<T> {
Self {
entity,
export_names: Vec::new(),
import_name: import_name
import_name: import_name,
}
}
}
@ -245,7 +232,6 @@ pub struct Module {
// return_mode: ReturnMode,
}
impl Module {
/// Instantiate a Module given WASM bytecode
pub fn from_bytes(
@ -365,7 +351,11 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> {
// however the 32-bit wasmer may be running on 64-bit arch, which means ptr_size here will
// be 8 bytes. That will definitely gove the wrong offset values
fn make_table(&mut self, func: &mut ir::Function, table_index: TableIndex) -> ir::Table {
assert_eq!(table_index.index(), 0, "Only one WebAssembly memory supported");
assert_eq!(
table_index.index(),
0,
"Only one WebAssembly memory supported"
);
let instance = func.create_global_value(ir::GlobalValueData::VMContext);
let ptr_size = native_pointer_size();
// Load value at (instance + TABLES_OFFSET)
@ -415,7 +405,11 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> {
// however the 32-bit wasmer may be running on 64-bit arch, which means ptr_size here will
// be 8 bytes. That will definitely gove the wrong offset values
fn make_heap(&mut self, func: &mut ir::Function, memory_index: MemoryIndex) -> ir::Heap {
debug_assert_eq!(memory_index.index(), 0, "Only one WebAssembly memory supported");
debug_assert_eq!(
memory_index.index(),
0,
"Only one WebAssembly memory supported"
);
let instance = func.create_global_value(ir::GlobalValueData::VMContext);
let ptr_size = native_pointer_size();
@ -438,7 +432,7 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> {
base,
offset: Offset32::new(memory_data_offset),
global_type: self.pointer_type(),
readonly: true
readonly: true,
});
// Load value at the (base + memory_data_offset)
@ -455,16 +449,18 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> {
base: heap_base,
min_size: 0.into(),
guard_size: (LinearMemory::DEFAULT_GUARD_SIZE as i64).into(),
style: ir::HeapStyle::Dynamic {
bound_gv,
},
index_type: I32
style: ir::HeapStyle::Dynamic { bound_gv },
index_type: I32,
});
heap
}
fn make_global(&mut self, func: &mut ir::Function, global_index: GlobalIndex) -> GlobalVariable {
fn make_global(
&mut self,
func: &mut ir::Function,
global_index: GlobalIndex,
) -> GlobalVariable {
let ptr_size = native_pointer_size();
let instance = func.create_global_value(ir::GlobalValueData::VMContext);
@ -525,7 +521,6 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> {
.special_param(ir::ArgumentPurpose::VMContext)
.expect("Missing vmctx parameter");
// The `callee` value is an index into a table of function pointers.
// Apparently, that table is stored at absolute address 0 in this dummy environment.
// TODO: Generate bounds checking code.
@ -541,9 +536,7 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> {
// let entry_size = native_pointer_size() as i64 * 2;
// let callee_scaled = pos.ins().imul_imm(callee_offset, entry_size);
let entry_addr = pos
.ins()
.table_addr(ptr, table, callee_offset, 0);
let entry_addr = pos.ins().table_addr(ptr, table, callee_offset, 0);
let mut mflags = ir::MemFlags::new();
mflags.set_notrap();
@ -597,7 +590,11 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> {
_heap: ir::Heap,
val: ir::Value,
) -> WasmResult<ir::Value> {
debug_assert_eq!(memory_index.index(), 0, "non-default memories not supported yet");
debug_assert_eq!(
memory_index.index(),
0,
"non-default memories not supported yet"
);
let grow_mem_func = self.mod_info.grow_memory_extfunc.unwrap_or_else(|| {
let sig_ref = pos.func.import_signature(Signature {
call_conv: CallConv::SystemV,
@ -624,7 +621,9 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> {
let memory_index_value = pos.ins().iconst(I32, imm64(memory_index.index()));
let vmctx = pos.func.special_param(ArgumentPurpose::VMContext).unwrap();
let call_inst = pos.ins().call(grow_mem_func, &[val, memory_index_value, vmctx]);
let call_inst = pos
.ins()
.call(grow_mem_func, &[val, memory_index_value, vmctx]);
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
}
@ -634,7 +633,11 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> {
memory_index: MemoryIndex,
_heap: ir::Heap,
) -> WasmResult<ir::Value> {
debug_assert_eq!(memory_index.index(), 0, "non-default memories not supported yet");
debug_assert_eq!(
memory_index.index(),
0,
"non-default memories not supported yet"
);
let cur_mem_func = self.mod_info.current_memory_extfunc.unwrap_or_else(|| {
let sig_ref = pos.func.import_signature(Signature {
call_conv: CallConv::SystemV,
@ -712,7 +715,9 @@ impl<'data> ModuleEnvironment<'data> for Module {
self.info.imported_funcs.len(),
"Imported functions must be declared first"
);
self.info.functions.push(ImportableExportable::new(sig_index, None));
self.info
.functions
.push(ImportableExportable::new(sig_index, None));
self.info
.imported_funcs
.push((String::from(module), String::from(field)));
@ -723,7 +728,9 @@ impl<'data> ModuleEnvironment<'data> for Module {
}
fn declare_func_type(&mut self, sig_index: SignatureIndex) {
self.info.functions.push(ImportableExportable::new(sig_index, None));
self.info
.functions
.push(ImportableExportable::new(sig_index, None));
}
fn get_func_type(&self, func_index: FuncIndex) -> SignatureIndex {
@ -731,16 +738,16 @@ impl<'data> ModuleEnvironment<'data> for Module {
}
fn declare_global(&mut self, global: Global) {
self.info.globals.push(ImportableExportable::new(global, None));
self.info
.globals
.push(ImportableExportable::new(global, None));
}
fn declare_global_import(
&mut self,
global: Global,
module: &'data str,
field: &'data str,
) {
self.info.globals.push(ImportableExportable::new(global, Some((String::from(module), String::from(field)))));
fn declare_global_import(&mut self, global: Global, module: &'data str, field: &'data str) {
self.info.globals.push(ImportableExportable::new(
global,
Some((String::from(module), String::from(field))),
));
}
fn get_global(&self, global_index: GlobalIndex) -> &Global {
@ -748,16 +755,16 @@ impl<'data> ModuleEnvironment<'data> for Module {
}
fn declare_table(&mut self, table: Table) {
self.info.tables.push(ImportableExportable::new(table, None));
self.info
.tables
.push(ImportableExportable::new(table, None));
}
fn declare_table_import(
&mut self,
table: Table,
module: &'data str,
field: &'data str,
) {
self.info.tables.push(ImportableExportable::new(table, Some((String::from(module), String::from(field)))));
fn declare_table_import(&mut self, table: Table, module: &'data str, field: &'data str) {
self.info.tables.push(ImportableExportable::new(
table,
Some((String::from(module), String::from(field))),
));
}
fn declare_table_elements(
@ -776,16 +783,16 @@ impl<'data> ModuleEnvironment<'data> for Module {
}
fn declare_memory(&mut self, memory: Memory) {
self.info.memories.push(ImportableExportable::new(memory, None));
self.info
.memories
.push(ImportableExportable::new(memory, None));
}
fn declare_memory_import(
&mut self,
memory: Memory,
module: &'data str,
field: &'data str,
) {
self.info.memories.push(ImportableExportable::new(memory, Some((String::from(module), String::from(field)))));
fn declare_memory_import(&mut self, memory: Memory, module: &'data str, field: &'data str) {
self.info.memories.push(ImportableExportable::new(
memory,
Some((String::from(module), String::from(field))),
));
}
fn declare_data_initialization(

View File

@ -11,12 +11,10 @@ pub fn print_instance_offsets(instance: &Instance) {
let instance_address = instance as *const _ as usize;
let data_ptr = &instance.data_pointers;
let tables_pointer_address_ptr: *const usize =
unsafe { transmute(&data_ptr.tables) };
let tables_pointer_address_ptr: *const usize = unsafe { transmute(&data_ptr.tables) };
let tables_pointer_address = tables_pointer_address_ptr as usize;
let memories_pointer_address_ptr: *const usize =
unsafe { transmute(&data_ptr.memories) };
let memories_pointer_address_ptr: *const usize = unsafe { transmute(&data_ptr.memories) };
let memories_pointer_address = memories_pointer_address_ptr as usize;
let memories_pointer_address_ptr_0: *const usize =
@ -31,8 +29,7 @@ pub fn print_instance_offsets(instance: &Instance) {
unsafe { transmute(&data_ptr.memories.get_unchecked(0).len) };
let memories_pointer_address_0_len = memories_pointer_address_ptr_0_len as usize;
let globals_pointer_address_ptr: *const usize =
unsafe { transmute(&data_ptr.globals) };
let globals_pointer_address_ptr: *const usize = unsafe { transmute(&data_ptr.globals) };
let globals_pointer_address = globals_pointer_address_ptr as usize;
println!(
@ -53,7 +50,6 @@ instance.data_pointers.globals \t- {:X} | offset - {:?}
tables_pointer_address - instance_address,
memories_pointer_address,
memories_pointer_address - instance_address,
memories_pointer_address_0,
0,
memories_pointer_address_0_data,
@ -61,7 +57,6 @@ instance.data_pointers.globals \t- {:X} | offset - {:?}
data_ptr.memories.get_unchecked(0).len,
memories_pointer_address_0_len,
memories_pointer_address_0_len - memories_pointer_address_0_data,
globals_pointer_address,
globals_pointer_address - instance_address,
);