From 181837d7cc1cb7c16e2341349d7e95eeb18bf0f8 Mon Sep 17 00:00:00 2001 From: Steve Akinyemi Date: Fri, 7 Dec 2018 14:50:35 +0100 Subject: [PATCH] Fix localtime implementation --- src/apis/emscripten/env.rs | 5 -- src/apis/emscripten/mod.rs | 20 +++-- src/apis/emscripten/storage.rs | 29 +++--- src/apis/emscripten/time.rs | 158 ++++++++++++++++++++++++++++----- src/apis/emscripten/utils.rs | 2 + src/bin/wasmer.rs | 37 +------- 6 files changed, 164 insertions(+), 87 deletions(-) diff --git a/src/apis/emscripten/env.rs b/src/apis/emscripten/env.rs index cd4e8db4b..91445570b 100644 --- a/src/apis/emscripten/env.rs +++ b/src/apis/emscripten/env.rs @@ -97,11 +97,6 @@ pub extern "C" fn _getgrnam(name_ptr: c_int, instance: &mut Instance) -> c_int { } } -pub extern "C" fn _localtime_r() -> u32 { - debug!("emscripten::_localtime_r"); - 0 -} - pub extern "C" fn _getpagesize() -> u32 { debug!("emscripten::_getpagesize"); 16384 diff --git a/src/apis/emscripten/mod.rs b/src/apis/emscripten/mod.rs index d581c3f90..1b5acc389 100644 --- a/src/apis/emscripten/mod.rs +++ b/src/apis/emscripten/mod.rs @@ -18,7 +18,7 @@ mod time; mod utils; mod varargs; -pub use self::storage::{align_memory, static_alloc}; +pub use self::storage::{align_memory}; pub use self::utils::{is_emscripten_module, allocate_on_stack, allocate_cstr_on_stack}; // TODO: Magic number - how is this calculated? @@ -27,6 +27,8 @@ const TOTAL_STACK: u32 = 5242880; const DYNAMICTOP_PTR_DIFF: u32 = 1088; // TODO: make this variable const STATIC_BUMP: u32 = 215536; +// TODO: make this variable +const GLOBAL_BASE: u32 = 1024; fn stacktop(static_bump: u32) -> u32 { align_memory(dynamictop_ptr(static_bump) + 4) @@ -44,11 +46,10 @@ fn dynamictop_ptr(static_bump: u32) -> u32 { static_bump + DYNAMICTOP_PTR_DIFF } -// fn static_alloc(size: usize, static_top: &mut size) -> usize { -// let ret = *static_top; -// *static_top = (*static_top + size + 15) & (-16 as usize); -// ret -// } + +pub fn statictop(static_bump: u32) -> u32 { + GLOBAL_BASE + 5520 +} pub fn emscripten_set_up_memory(memory: &mut LinearMemory) { let dynamictop_ptr = dynamictop_ptr(STATIC_BUMP) as usize; @@ -383,6 +384,11 @@ pub fn generate_emscripten_env<'a, 'b>() -> ImportObject<&'a str, &'b str> { "_clock_gettime", ImportValue::Func(time::_clock_gettime as _), ); + import_object.set( + "env", + "_asctime", + ImportValue::Func(time::_asctime as _), + ); import_object.set( "env", "_localtime", @@ -393,7 +399,7 @@ pub fn generate_emscripten_env<'a, 'b>() -> ImportObject<&'a str, &'b str> { import_object.set( "env", "_localtime_r", - ImportValue::Func(env::_localtime_r as _), + ImportValue::Func(time::_localtime_r as _), ); import_object.set( "env", diff --git a/src/apis/emscripten/storage.rs b/src/apis/emscripten/storage.rs index 382bc2e43..16d4d6d0e 100644 --- a/src/apis/emscripten/storage.rs +++ b/src/apis/emscripten/storage.rs @@ -4,22 +4,15 @@ pub fn align_memory(ptr: u32) -> u32 { (ptr + 15) & !15 } -// pub fn static_alloc(size: u32, instance: &mut Instance) -> u32 { -// let static_top = instance.emscripten_data.static_top; -// let total_memory = instance.memories[0].maximum.unwrap_or(LinearMemory::DEFAULT_HEAP_SIZE as u32); -// instance.emscripten_data.static_top = (static_top + size + 15) & 4294967280; -// assert!(static_top < total_memory, "not enough memory for static allocation - increase total_memory!"); -// static_top -// } -pub fn static_alloc(size: u32, static_top: &mut u32, memory: &LinearMemory) -> u32 { - let old_static_top = *static_top; - let total_memory = memory.maximum_size() * LinearMemory::PAGE_SIZE; - // NOTE: The `4294967280` is a u32 conversion of -16 as gotten from emscripten. - *static_top = (*static_top + size + 15) & 4294967280; - assert!( - *static_top < total_memory, - "not enough memory for static allocation - increase total_memory!" - ); - old_static_top -} +// pub fn static_alloc(size: u32, static_top: &mut u32, memory: &LinearMemory) -> u32 { +// let old_static_top = *static_top; +// let total_memory = memory.maximum_size() * LinearMemory::PAGE_SIZE; +// // NOTE: The `4294967280` is a u32 conversion of -16 as gotten from emscripten. +// *static_top = (*static_top + size + 15) & 4294967280; +// assert!( +// *static_top < total_memory, +// "not enough memory for static allocation - increase total_memory!" +// ); +// old_static_top +// } diff --git a/src/apis/emscripten/time.rs b/src/apis/emscripten/time.rs index e8aa2d06c..59a9cc0c1 100644 --- a/src/apis/emscripten/time.rs +++ b/src/apis/emscripten/time.rs @@ -3,11 +3,13 @@ use libc::{ c_int, c_long, clock_gettime as libc_clock_gettime, - // tm, localtime, + localtime_r, + tm, time, time_t, timespec, + c_char, }; use std::mem; use std::time::SystemTime; @@ -64,37 +66,101 @@ pub extern "C" fn _clock_gettime(clk_id: c_int, tp: c_int, instance: &mut Instan 0 } +#[repr(C)] +struct guest_tm { + pub tm_sec: c_int, // 4 - 0 + pub tm_min: c_int, // 4 - 4 + pub tm_hour: c_int, // 4 - 8 + pub tm_mday: c_int, // 4 - 12 + pub tm_mon: c_int, // 4 - 16 + pub tm_year: c_int, // 4 - 20 + pub tm_wday: c_int, // 4 - 24 + pub tm_yday: c_int, // 4 - 28 + pub tm_isdst: c_int, // 4 - 32 + pub tm_gmtoff: c_int, // 4 - 36 + pub tm_zone: c_int, // 4 - 40 +} + +// #[repr(C)] +// struct asctime_tm { +// pub tm_sec: c_int, // 4 - 0 +// pub tm_min: c_int, // 4 - 4 +// pub tm_hour: c_int, // 4 - 8 +// pub tm_mday: c_int, // 4 - 12 +// pub tm_mon: c_int, // 4 - 16 +// pub tm_year: c_int, // 4 - 20 +// pub tm_wday: c_int, // 4 - 24 +// } + +/// emscripten: _asctime +pub extern "C" fn _asctime(time: u32, instance: &mut Instance) -> u32 { + debug!("emscripten::_asctime {}", time); + + unsafe { + let date = &*(instance.memory_offset_addr(0, time as _) as *mut guest_tm); + + let days = vec!["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; + let months = vec!["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; + + let day = if date.tm_mday < 10 {" "} else {" "}; + let hour = if date.tm_hour < 10 {" 0"} else {" "}; + let min = if date.tm_min < 10 {":0"} else {":"}; + let sec = if date.tm_sec < 10 {":0"} else {":"}; + let year = 1900 + date.tm_year; + + let mut time_str = format!( + // "{} {}{}{}{}{}{}{}{}{} {}\0 \0\0", + "{} {}{}{}{}{}{}{}{}{} {}\0", + days[date.tm_wday as usize], + months[date.tm_mon as usize], + day, date.tm_mday, + hour, date.tm_hour, + min, date.tm_min, + sec, date.tm_sec, + year + ); + + let time_str_ptr = time_str.as_ptr() as _; + + // TODO: asctime_r is specced to behave in an undefined manner if the algorithm would attempt + // to write out more than 26 bytes (including the null terminator). + // See http://pubs.opengroup.org/onlinepubs/9699919799/functions/asctime.html + // Our undefined behavior is to truncate the write to at most 26 bytes, including null terminator. + let time_str_offset = copy_cstr_into_wasm(instance, time_str_ptr); + + let c_str = instance.memory_offset_addr(0, time_str_offset as _) as *mut i8; + use std::ffi::CStr; + debug!("########## cstr = {:?}", CStr::from_ptr(c_str)); + println!("Kaboom!"); + + // std::mem::forget(time_str); + time_str_offset + } +} + +/// emscripten: _tvset +pub extern "C" fn _tvset() { + debug!("emscripten::_tvset UNIMPLEMENTED"); +} + /// emscripten: _localtime pub extern "C" fn _localtime(time_p: u32, instance: &mut Instance) -> c_int { debug!("emscripten::_localtime {}", time_p); - - #[repr(C)] - struct GuestTm { - tm_sec: i32, - tm_min: i32, - tm_hour: i32, - tm_mday: i32, - tm_mon: i32, - tm_year: i32, - tm_wday: i32, - tm_yday: i32, - tm_isdst: i32, - tm_gmtoff: c_long, - tm_zone: u32, - } + // NOTE: emscripten seems to want tzset() called in this function + // https://stackoverflow.com/questions/19170721/real-time-awareness-of-timezone-change-in-localtime-vs-localtime-r unsafe { let time_p_addr = instance.memory_offset_addr(0, time_p as _) as *mut i64; + let tm_struct = &*localtime(time_p_addr); - // Webassembly allocation let tm_struct_offset = (instance.emscripten_data.as_ref().unwrap().malloc)( - mem::size_of::() as _, + mem::size_of::() as _, instance, ); - let tm_struct_ptr = instance.memory_offset_addr(0, tm_struct_offset as _) as *mut GuestTm; - // Initializing + let tm_struct_ptr = instance.memory_offset_addr(0, tm_struct_offset as _) as *mut guest_tm; + (*tm_struct_ptr).tm_sec = tm_struct.tm_sec; (*tm_struct_ptr).tm_min = tm_struct.tm_min; (*tm_struct_ptr).tm_hour = tm_struct.tm_hour; @@ -104,8 +170,56 @@ pub extern "C" fn _localtime(time_p: u32, instance: &mut Instance) -> c_int { (*tm_struct_ptr).tm_wday = tm_struct.tm_wday; (*tm_struct_ptr).tm_yday = tm_struct.tm_yday; (*tm_struct_ptr).tm_isdst = tm_struct.tm_isdst; - (*tm_struct_ptr).tm_gmtoff = tm_struct.tm_gmtoff; - (*tm_struct_ptr).tm_zone = copy_cstr_into_wasm(instance, tm_struct.tm_zone); + (*tm_struct_ptr).tm_gmtoff = tm_struct.tm_gmtoff as i32; + (*tm_struct_ptr).tm_zone = copy_cstr_into_wasm(instance, tm_struct.tm_zone) as i32; + + tm_struct_offset as c_int + } +} +/// emscripten: _localtime_r +pub extern "C" fn _localtime_r(time_p: u32, result: u32, instance: &mut Instance) -> c_int { + debug!("emscripten::_localtime_r {}", time_p); + + // NOTE: emscripten seems to want tzset() called in this function + // https://stackoverflow.com/questions/19170721/real-time-awareness-of-timezone-change-in-localtime-vs-localtime-r + + unsafe { + let time_p_addr = instance.memory_offset_addr(0, time_p as _) as *mut i64; + let result_addr = instance.memory_offset_addr(0, result as _) as *mut guest_tm; + let mut result_tm = tm { + tm_sec: (*result_addr).tm_sec, + tm_min: (*result_addr).tm_min, + tm_hour: (*result_addr).tm_hour, + tm_mday: (*result_addr).tm_mday, + tm_mon: (*result_addr).tm_mon, + tm_year: (*result_addr).tm_year, + tm_wday: (*result_addr).tm_wday, + tm_yday: (*result_addr).tm_yday, + tm_isdst: (*result_addr).tm_isdst, + tm_gmtoff: (*result_addr).tm_gmtoff as _, + tm_zone: instance.memory_offset_addr(0, (*result_addr).tm_zone as _) as _, + }; + + let tm_struct = &*localtime_r(time_p_addr, &mut result_tm); + + let tm_struct_offset = (instance.emscripten_data.as_ref().unwrap().malloc)( + mem::size_of::() as _, + instance, + ); + + let tm_struct_ptr = instance.memory_offset_addr(0, tm_struct_offset as _) as *mut guest_tm; + + (*tm_struct_ptr).tm_sec = tm_struct.tm_sec; + (*tm_struct_ptr).tm_min = tm_struct.tm_min; + (*tm_struct_ptr).tm_hour = tm_struct.tm_hour; + (*tm_struct_ptr).tm_mday = tm_struct.tm_mday; + (*tm_struct_ptr).tm_mon = tm_struct.tm_mon; + (*tm_struct_ptr).tm_year = tm_struct.tm_year; + (*tm_struct_ptr).tm_wday = tm_struct.tm_wday; + (*tm_struct_ptr).tm_yday = tm_struct.tm_yday; + (*tm_struct_ptr).tm_isdst = tm_struct.tm_isdst; + (*tm_struct_ptr).tm_gmtoff = tm_struct.tm_gmtoff as i32; + (*tm_struct_ptr).tm_zone = copy_cstr_into_wasm(instance, tm_struct.tm_zone) as i32; tm_struct_offset as c_int } diff --git a/src/apis/emscripten/utils.rs b/src/apis/emscripten/utils.rs index c0f4e02ab..af96463a8 100644 --- a/src/apis/emscripten/utils.rs +++ b/src/apis/emscripten/utils.rs @@ -28,6 +28,8 @@ pub unsafe fn copy_cstr_into_wasm(instance: &mut Instance, cstr: *const c_char) *loc = byte; } + // TODO: Appending null byte won't work, because there is CStr::from_ptr(cstr) + // at the top that crashes when there is no null byte *raw_memory.add(cstr_len) = 0; space_offset diff --git a/src/bin/wasmer.rs b/src/bin/wasmer.rs index e15ef0d7c..b873cbbbf 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -92,7 +92,8 @@ fn execute_wasm(options: &Run) -> Result<(), String> { get_instance_function!(instance, func_index); let (argc, argv) = store_module_arguments(options, &mut instance); - + + // TODO: This assumes argc and argv are always passed. return call_protected!(main(argc, argv, &instance)).map_err(|err| format!("{}", err)); // TODO: We should implement emscripten __ATEXIT__ } else { @@ -144,37 +145,3 @@ fn store_module_arguments(options: &Run, instance: &mut webassembly::Instance) - (argc as u32, argv_offset) } - -// fn get_module_arguments(options: &Run, instance: &mut webassembly::Instance) -> (u32, u32) { -// // Application Arguments -// let mut arg_values: Vec = Vec::new(); -// let mut arg_addrs: Vec<*const u8> = Vec::new(); -// let arg_length = options.args.len() + 1; - -// arg_values.reserve_exact(arg_length); -// arg_addrs.reserve_exact(arg_length); - -// // Push name of wasm file -// arg_values.push(format!("{}\0", options.path.to_str().unwrap())); -// arg_addrs.push(arg_values[0].as_ptr()); - -// // Push additional arguments -// for (i, arg) in options.args.iter().enumerate() { -// arg_values.push(format!("{}\0", arg)); -// arg_addrs.push(arg_values[i + 1].as_ptr()); -// } - -// // Get argument count and pointer to addresses -// let argv = arg_addrs.as_ptr() as *mut *mut i8; -// let argc = arg_length as u32; - -// // Copy the the arguments into the wasm memory and get offset -// let argv_offset = unsafe { -// copy_cstr_array_into_wasm(argc, argv, instance) -// }; - -// debug!("argc = {:?}", argc); -// debug!("argv = {:?}", arg_addrs); - -// (argc, argv_offset) -// }