From 83df3ad35fc24f2da1cd3707867f608e4d9c90d6 Mon Sep 17 00:00:00 2001 From: Mackenzie Clark Date: Wed, 6 Feb 2019 18:57:11 -0800 Subject: [PATCH] fix putenv and setenv for windows (#140) --- lib/emscripten/src/env/mod.rs | 11 + .../src/{env.rs => env/unix/mod.rs} | 4 +- lib/emscripten/src/env/windows/mod.rs | 218 ++++++++++++++++++ lib/emscripten/src/utils.rs | 14 +- 4 files changed, 243 insertions(+), 4 deletions(-) create mode 100644 lib/emscripten/src/env/mod.rs rename lib/emscripten/src/{env.rs => env/unix/mod.rs} (98%) create mode 100644 lib/emscripten/src/env/windows/mod.rs diff --git a/lib/emscripten/src/env/mod.rs b/lib/emscripten/src/env/mod.rs new file mode 100644 index 000000000..aea5e6fc2 --- /dev/null +++ b/lib/emscripten/src/env/mod.rs @@ -0,0 +1,11 @@ +#[cfg(unix)] +mod unix; + +#[cfg(windows)] +mod windows; + +#[cfg(unix)] +pub use self::unix::*; + +#[cfg(windows)] +pub use self::windows::*; diff --git a/lib/emscripten/src/env.rs b/lib/emscripten/src/env/unix/mod.rs similarity index 98% rename from lib/emscripten/src/env.rs rename to lib/emscripten/src/env/unix/mod.rs index 479f80c55..b4016d83d 100644 --- a/lib/emscripten/src/env.rs +++ b/lib/emscripten/src/env/unix/mod.rs @@ -7,8 +7,8 @@ use std::ffi::CStr; use std::mem; use std::os::raw::c_char; -use super::utils::{allocate_on_stack, copy_cstr_into_wasm, copy_terminated_array_of_cstrs}; -use super::EmscriptenData; +use crate::utils::{allocate_on_stack, copy_cstr_into_wasm, copy_terminated_array_of_cstrs}; +use crate::EmscriptenData; use wasmer_runtime_core::vm::Ctx; pub fn _getaddrinfo(_one: i32, _two: i32, _three: i32, _four: i32, _ctx: &mut Ctx) -> i32 { diff --git a/lib/emscripten/src/env/windows/mod.rs b/lib/emscripten/src/env/windows/mod.rs new file mode 100644 index 000000000..0bffaf4a0 --- /dev/null +++ b/lib/emscripten/src/env/windows/mod.rs @@ -0,0 +1,218 @@ +/// NOTE: These syscalls only support wasm_32 for now because they take u32 offset +use libc::{ + c_int, + c_long, + getenv, + //sysconf, unsetenv, +}; + +use core::slice; +use std::ffi::{CStr, CString}; +use std::mem; +use std::os::raw::c_char; + +use crate::utils::{ + allocate_on_stack, copy_cstr_into_wasm, copy_terminated_array_of_cstrs, read_string_from_wasm, +}; +use crate::EmscriptenData; +use wasmer_runtime_core::memory::Memory; +use wasmer_runtime_core::vm::Ctx; + +#[link(name = "c")] +extern "C" { + #[link_name = "_putenv"] + pub fn putenv(s: *const c_char) -> c_int; +} + +pub fn _getaddrinfo(_one: i32, _two: i32, _three: i32, _four: i32, _ctx: &mut Ctx) -> i32 { + debug!("emscripten::_getaddrinfo"); + -1 +} + +// #[no_mangle] +/// emscripten: _getenv // (name: *const char) -> *const c_char; +pub fn _getenv(name: i32, ctx: &mut Ctx) -> u32 { + debug!("emscripten::_getenv"); + + let memory = ctx.memory(0); + + let name_addr = emscripten_memory_pointer!(ctx.memory(0), name) as _; + + debug!("=> name({:?})", unsafe { CStr::from_ptr(name_addr) }); + + let c_str: *mut c_char = unsafe { getenv(name_addr as _) } as _; + if c_str.is_null() { + return 0; + } + + unsafe { copy_cstr_into_wasm(ctx, c_str) } +} + +/// emscripten: _setenv // (name: *const char, name: *const value, overwrite: int); +pub fn _setenv(name: u32, value: u32, overwrite: u32, ctx: &mut Ctx) -> c_int { + debug!("emscripten::_setenv"); + let name_addr = emscripten_memory_pointer!(ctx.memory(0), name); + let value_addr = emscripten_memory_pointer!(ctx.memory(0), value); + // setenv does not exist on windows, so we hack it with _putenv + let name = read_string_from_wasm(ctx.memory(0), name); + let value = read_string_from_wasm(ctx.memory(0), value); + let putenv_string = format!("{}={}", name, value); + let putenv_cstring = CString::new(putenv_string).unwrap(); + let putenv_raw_ptr = putenv_cstring.as_ptr(); + debug!("=> name({:?})", name); + debug!("=> value({:?})", value); + unsafe { putenv(putenv_raw_ptr) } +} + +/// emscripten: _putenv // (name: *const char); +pub fn _putenv(name: c_int, ctx: &mut Ctx) -> c_int { + debug!("emscripten::_putenv"); + + let name_addr = emscripten_memory_pointer!(ctx.memory(0), name) as *const c_char; + + debug!("=> name({:?})", unsafe { CStr::from_ptr(name_addr) }); + unsafe { putenv(name_addr) } +} + +/// emscripten: _unsetenv // (name: *const char); +pub fn _unsetenv(name: c_int, ctx: &mut Ctx) -> c_int { + debug!("emscripten::_unsetenv"); + + let name_addr = emscripten_memory_pointer!(ctx.memory(0), name); + + debug!("=> name({:?})", unsafe { CStr::from_ptr(name_addr) }); + + unsafe { unsetenv(name_addr) } +} + +#[allow(clippy::cast_ptr_alignment)] +pub fn _getpwnam(name_ptr: c_int, ctx: &mut Ctx) -> c_int { + debug!("emscripten::_getpwnam {}", name_ptr); + + #[repr(C)] + struct GuestPasswd { + pw_name: u32, + pw_passwd: u32, + pw_uid: u32, + pw_gid: u32, + pw_gecos: u32, + pw_dir: u32, + pw_shell: u32, + } + + let name = unsafe { + let memory_name_ptr = emscripten_memory_pointer!(ctx.memory(0), name_ptr) as *const c_char; + CStr::from_ptr(memory_name_ptr) + }; + + unsafe { + let passwd = &*libc_getpwnam(name.as_ptr()); + let passwd_struct_offset = call_malloc(mem::size_of::() as _, ctx); + + let passwd_struct_ptr = + emscripten_memory_pointer!(ctx.memory(0), passwd_struct_offset) as *mut GuestPasswd; + (*passwd_struct_ptr).pw_name = copy_cstr_into_wasm(ctx, passwd.pw_name); + (*passwd_struct_ptr).pw_passwd = copy_cstr_into_wasm(ctx, passwd.pw_passwd); + (*passwd_struct_ptr).pw_gecos = copy_cstr_into_wasm(ctx, passwd.pw_gecos); + (*passwd_struct_ptr).pw_dir = copy_cstr_into_wasm(ctx, passwd.pw_dir); + (*passwd_struct_ptr).pw_shell = copy_cstr_into_wasm(ctx, passwd.pw_shell); + (*passwd_struct_ptr).pw_uid = passwd.pw_uid; + (*passwd_struct_ptr).pw_gid = passwd.pw_gid; + + passwd_struct_offset as c_int + } +} + +#[allow(clippy::cast_ptr_alignment)] +pub fn _getgrnam(name_ptr: c_int, ctx: &mut Ctx) -> c_int { + debug!("emscripten::_getgrnam {}", name_ptr); + + #[repr(C)] + struct GuestGroup { + gr_name: u32, + gr_passwd: u32, + gr_gid: u32, + gr_mem: u32, + } + + let name = unsafe { + let memory_name_ptr = emscripten_memory_pointer!(ctx.memory(0), name_ptr) as *const c_char; + CStr::from_ptr(memory_name_ptr) + }; + + unsafe { + let group = &*libc_getgrnam(name.as_ptr()); + let group_struct_offset = call_malloc(mem::size_of::() as _, ctx); + + let group_struct_ptr = + emscripten_memory_pointer!(ctx.memory(0), group_struct_offset) as *mut GuestGroup; + (*group_struct_ptr).gr_name = copy_cstr_into_wasm(ctx, group.gr_name); + (*group_struct_ptr).gr_passwd = copy_cstr_into_wasm(ctx, group.gr_passwd); + (*group_struct_ptr).gr_gid = group.gr_gid; + (*group_struct_ptr).gr_mem = copy_terminated_array_of_cstrs(ctx, group.gr_mem); + + group_struct_offset as c_int + } +} + +pub fn call_malloc(size: u32, ctx: &mut Ctx) -> u32 { + get_emscripten_data(ctx).malloc.call(size).unwrap() +} + +pub fn call_memalign(alignment: u32, size: u32, ctx: &mut Ctx) -> u32 { + get_emscripten_data(ctx) + .memalign + .call(alignment, size) + .unwrap() +} + +pub fn call_memset(pointer: u32, value: u32, size: u32, ctx: &mut Ctx) -> u32 { + get_emscripten_data(ctx) + .memset + .call(pointer, value, size) + .unwrap() +} + +pub(crate) fn get_emscripten_data(ctx: &mut Ctx) -> &mut EmscriptenData { + unsafe { &mut *(ctx.data as *mut EmscriptenData) } +} + +pub fn _getpagesize(_ctx: &mut Ctx) -> u32 { + debug!("emscripten::_getpagesize"); + 16384 +} + +#[allow(clippy::cast_ptr_alignment)] +pub fn ___build_environment(environ: c_int, ctx: &mut Ctx) { + debug!("emscripten::___build_environment {}", environ); + const MAX_ENV_VALUES: u32 = 64; + const TOTAL_ENV_SIZE: u32 = 1024; + let environment = emscripten_memory_pointer!(ctx.memory(0), environ) as *mut c_int; + unsafe { + let (pool_offset, _pool_slice): (u32, &mut [u8]) = + allocate_on_stack(TOTAL_ENV_SIZE as u32, ctx); + let (env_offset, _env_slice): (u32, &mut [u8]) = + allocate_on_stack((MAX_ENV_VALUES * 4) as u32, ctx); + let env_ptr = emscripten_memory_pointer!(ctx.memory(0), env_offset) as *mut c_int; + let mut _pool_ptr = emscripten_memory_pointer!(ctx.memory(0), pool_offset) as *mut c_int; + *env_ptr = pool_offset as i32; + *environment = env_offset as i32; + + // *env_ptr = 0; + }; + // unsafe { + // *env_ptr = 0; + // }; +} + +pub fn _sysconf(name: c_int, _ctx: &mut Ctx) -> c_long { + debug!("emscripten::_sysconf {}", name); + // TODO: Implement like emscripten expects regarding memory/page size + unsafe { sysconf(name) } +} + +pub fn ___assert_fail(a: c_int, b: c_int, c: c_int, d: c_int, _ctx: &mut Ctx) { + debug!("emscripten::___assert_fail {} {} {} {}", a, b, c, d); + // TODO: Implement like emscripten expects regarding memory/page size + // TODO raise an error +} diff --git a/lib/emscripten/src/utils.rs b/lib/emscripten/src/utils.rs index c87ea323b..79f5c8df7 100644 --- a/lib/emscripten/src/utils.rs +++ b/lib/emscripten/src/utils.rs @@ -4,7 +4,9 @@ use libc::stat; use std::ffi::CStr; use std::mem::size_of; use std::os::raw::c_char; +use std::os::raw::c_int; use std::slice; +use wasmer_runtime_core::memory::Memory; use wasmer_runtime_core::{ module::Module, structures::TypedIndex, @@ -54,11 +56,11 @@ pub unsafe fn copy_cstr_into_wasm(ctx: &mut Ctx, cstr: *const c_char) -> u32 { let s = CStr::from_ptr(cstr).to_str().unwrap(); let cstr_len = s.len(); let space_offset = env::call_malloc((cstr_len as u32) + 1, ctx); - let raw_memory = emscripten_memory_pointer!(ctx.memory(0), space_offset) as *mut u8; + let raw_memory = emscripten_memory_pointer!(ctx.memory(0), space_offset) as *mut c_char; let slice = slice::from_raw_parts_mut(raw_memory, cstr_len); for (byte, loc) in s.bytes().zip(slice.iter_mut()) { - *loc = byte; + *loc = byte as _; } // TODO: Appending null byte won't work, because there is CStr::from_ptr(cstr) @@ -155,6 +157,14 @@ pub unsafe fn copy_stat_into_wasm(ctx: &mut Ctx, buf: u32, stat: &stat) { (*stat_ptr).st_ino = stat.st_ino as _; } +pub fn read_string_from_wasm(memory: &Memory, offset: u32) -> String { + memory.view::()[(offset as usize)..] + .iter() + .take_while(|cell| cell.get() != 0) + .map(|cell| cell.get() as char) + .collect() +} + #[cfg(test)] mod tests { use super::is_emscripten_module;