Merge pull request #42 from wasmerio/fix/localtime

Fix localtime implementation
This commit is contained in:
Steve Akinyemi 2018-12-11 21:20:06 +01:00 committed by GitHub
commit 83e1a0e75c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 187 additions and 66 deletions

View File

@ -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 { pub extern "C" fn _getpagesize() -> u32 {
debug!("emscripten::_getpagesize"); debug!("emscripten::_getpagesize");
16384 16384

View File

@ -18,7 +18,7 @@ mod time;
mod utils; mod utils;
mod varargs; 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}; pub use self::utils::{is_emscripten_module, allocate_on_stack, allocate_cstr_on_stack};
// TODO: Magic number - how is this calculated? // TODO: Magic number - how is this calculated?
@ -44,12 +44,6 @@ fn dynamictop_ptr(static_bump: u32) -> u32 {
static_bump + DYNAMICTOP_PTR_DIFF 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 emscripten_set_up_memory(memory: &mut LinearMemory) { pub fn emscripten_set_up_memory(memory: &mut LinearMemory) {
let dynamictop_ptr = dynamictop_ptr(STATIC_BUMP) as usize; let dynamictop_ptr = dynamictop_ptr(STATIC_BUMP) as usize;
let dynamictop_ptr_offset = dynamictop_ptr + mem::size_of::<u32>(); let dynamictop_ptr_offset = dynamictop_ptr + mem::size_of::<u32>();
@ -383,6 +377,16 @@ pub fn generate_emscripten_env<'a, 'b>() -> ImportObject<&'a str, &'b str> {
"_clock_gettime", "_clock_gettime",
ImportValue::Func(time::_clock_gettime as _), ImportValue::Func(time::_clock_gettime as _),
); );
import_object.set(
"env",
"_asctime",
ImportValue::Func(time::_asctime as _),
);
import_object.set(
"env",
"_asctime_r",
ImportValue::Func(time::_asctime_r as _),
);
import_object.set( import_object.set(
"env", "env",
"_localtime", "_localtime",
@ -393,7 +397,7 @@ pub fn generate_emscripten_env<'a, 'b>() -> ImportObject<&'a str, &'b str> {
import_object.set( import_object.set(
"env", "env",
"_localtime_r", "_localtime_r",
ImportValue::Func(env::_localtime_r as _), ImportValue::Func(time::_localtime_r as _),
); );
import_object.set( import_object.set(
"env", "env",

View File

@ -4,22 +4,15 @@ pub fn align_memory(ptr: u32) -> u32 {
(ptr + 15) & !15 (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 { // pub fn static_alloc(size: u32, static_top: &mut u32, memory: &LinearMemory) -> u32 {
let old_static_top = *static_top; // let old_static_top = *static_top;
let total_memory = memory.maximum_size() * LinearMemory::PAGE_SIZE; // let total_memory = memory.maximum_size() * LinearMemory::PAGE_SIZE;
// NOTE: The `4294967280` is a u32 conversion of -16 as gotten from emscripten. // // NOTE: The `4294967280` is a u32 conversion of -16 as gotten from emscripten.
*static_top = (*static_top + size + 15) & 4_294_967_280; // *static_top = (*static_top + size + 15) & 4294967280;
assert!( // assert!(
*static_top < total_memory, // *static_top < total_memory,
"not enough memory for static allocation - increase total_memory!" // "not enough memory for static allocation - increase total_memory!"
); // );
old_static_top // old_static_top
} // }

View File

@ -1,13 +1,15 @@
use super::utils::copy_cstr_into_wasm; use super::utils::{copy_cstr_into_wasm, write_to_buf};
use libc::{ use libc::{
c_int, c_int,
c_long, c_long,
clock_gettime as libc_clock_gettime, clock_gettime as libc_clock_gettime,
// tm,
localtime, localtime,
localtime_r,
tm,
time, time,
time_t, time_t,
timespec, timespec,
c_char,
}; };
use std::mem; use std::mem;
use std::time::SystemTime; use std::time::SystemTime;
@ -64,50 +66,163 @@ pub extern "C" fn _clock_gettime(clk_id: c_int, tp: c_int, instance: &mut Instan
0 0
} }
#[repr(C)]
struct guest_tm {
pub tm_sec: c_int, // 0
pub tm_min: c_int, // 4
pub tm_hour: c_int, // 8
pub tm_mday: c_int, // 12
pub tm_mon: c_int, // 16
pub tm_year: c_int, // 20
pub tm_wday: c_int, // 24
pub tm_yday: c_int, // 28
pub tm_isdst: c_int, // 32
pub tm_gmtoff: c_int, // 36
pub tm_zone: c_int, // 40
}
/// emscripten: _tvset
pub extern "C" fn _tvset() {
debug!("emscripten::_tvset UNIMPLEMENTED");
}
/// formats time as a C string
unsafe extern "C" fn fmt_time(time: u32, instance: &Instance) -> *const c_char {
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 year = 1900 + date.tm_year;
let time_str = format!(
// NOTE: TODO: Hack! The 14 accompanying chars are needed for some reason
"{} {} {:2} {:02}:{:02}:{:02} {:4}\n\0\0\0\0\0\0\0\0\0\0\0\0\0",
days[date.tm_wday as usize],
months[date.tm_mon as usize],
date.tm_mday,
date.tm_hour,
date.tm_min,
date.tm_sec,
year
);
time_str[0..26].as_ptr() as _
}
/// emscripten: _asctime
pub extern "C" fn _asctime(time: u32, instance: &mut Instance) -> u32 {
debug!("emscripten::_asctime {}", time);
unsafe {
let time_str_ptr = fmt_time(time, instance);
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));
}
}
/// emscripten: _asctime_r
pub extern "C" fn _asctime_r(time: u32, buf: u32, instance: &mut Instance) -> u32 {
debug!("emscripten::_asctime_r {}", time);
unsafe {
// NOTE: 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_ptr = fmt_time(time, instance);
write_to_buf(time_str_ptr, buf, 26, instance)
// 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));
}
}
/// emscripten: _localtime /// emscripten: _localtime
pub extern "C" fn _localtime(time_p: u32, instance: &mut Instance) -> c_int { pub extern "C" fn _localtime(time_p: u32, instance: &mut Instance) -> c_int {
debug!("emscripten::_localtime {}", time_p); debug!("emscripten::_localtime {}", time_p);
// NOTE: emscripten seems to want tzset() called in this function
#[repr(C)] // https://stackoverflow.com/questions/19170721/real-time-awareness-of-timezone-change-in-localtime-vs-localtime-r
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,
}
unsafe { unsafe {
let time_p_addr = instance.memory_offset_addr(0, time_p as _) as *mut i64; let time_p_addr = instance.memory_offset_addr(0, time_p as _) as *mut i64;
let tm_struct = &*localtime(time_p_addr); let result_tm = &*localtime(time_p_addr);
// Webassembly allocation
let tm_struct_offset = (instance.emscripten_data.as_ref().unwrap().malloc)( let tm_struct_offset = (instance.emscripten_data.as_ref().unwrap().malloc)(
mem::size_of::<GuestTm>() as _, mem::size_of::<guest_tm>() as _,
instance, 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; // debug!(
(*tm_struct_ptr).tm_min = tm_struct.tm_min; // ">>>>>>> time = {}, {}, {}, {}, {}, {}, {}, {}",
(*tm_struct_ptr).tm_hour = tm_struct.tm_hour; // result_tm.tm_sec, result_tm.tm_min, result_tm.tm_hour, result_tm.tm_mday,
(*tm_struct_ptr).tm_mday = tm_struct.tm_mday; // result_tm.tm_mon, result_tm.tm_year, result_tm.tm_wday, result_tm.tm_yday,
(*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;
(*tm_struct_ptr).tm_zone = copy_cstr_into_wasm(instance, tm_struct.tm_zone);
tm_struct_offset as c_int (*tm_struct_ptr).tm_sec = result_tm.tm_sec;
(*tm_struct_ptr).tm_min = result_tm.tm_min;
(*tm_struct_ptr).tm_hour = result_tm.tm_hour;
(*tm_struct_ptr).tm_mday = result_tm.tm_mday;
(*tm_struct_ptr).tm_mon = result_tm.tm_mon;
(*tm_struct_ptr).tm_year = result_tm.tm_year;
(*tm_struct_ptr).tm_wday = result_tm.tm_wday;
(*tm_struct_ptr).tm_yday = result_tm.tm_yday;
(*tm_struct_ptr).tm_isdst = result_tm.tm_isdst;
(*tm_struct_ptr).tm_gmtoff = result_tm.tm_gmtoff as i32;
(*tm_struct_ptr).tm_zone = copy_cstr_into_wasm(instance, result_tm.tm_zone) as i32;
tm_struct_offset as _
}
}
/// 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 _,
};
localtime_r(time_p_addr, &mut result_tm);
// let tm_struct = result_tm;
// debug!(
// ">>>>>>> time = {}, {}, {}, {}, {}, {}, {}, {}",
// result_tm.tm_sec, result_tm.tm_min, result_tm.tm_hour, result_tm.tm_mday,
// result_tm.tm_mon, result_tm.tm_year, result_tm.tm_wday, result_tm.tm_yday,
// );
(*result_addr).tm_sec = result_tm.tm_sec;
(*result_addr).tm_min = result_tm.tm_min;
(*result_addr).tm_hour = result_tm.tm_hour;
(*result_addr).tm_mday = result_tm.tm_mday;
(*result_addr).tm_mon = result_tm.tm_mon;
(*result_addr).tm_year = result_tm.tm_year;
(*result_addr).tm_wday = result_tm.tm_wday;
(*result_addr).tm_yday = result_tm.tm_yday;
(*result_addr).tm_isdst = result_tm.tm_isdst;
(*result_addr).tm_gmtoff = result_tm.tm_gmtoff as _;
(*result_addr).tm_zone = copy_cstr_into_wasm(instance, result_tm.tm_zone) as _;
result as _
} }
} }

View File

@ -17,6 +17,18 @@ pub fn is_emscripten_module(module: &Module) -> bool {
false false
} }
pub unsafe fn write_to_buf(string: *const c_char, buf: u32, max: u32, instance: &Instance) -> u32 {
let buf_addr = instance.memory_offset_addr(0, buf as _) as *mut c_char;
unsafe {
for i in 0..max {
*buf_addr.add(i as _) = *string.add(i as _);
}
}
buf
}
pub unsafe fn copy_cstr_into_wasm(instance: &mut Instance, cstr: *const c_char) -> u32 { pub unsafe fn copy_cstr_into_wasm(instance: &mut Instance, cstr: *const c_char) -> u32 {
let s = CStr::from_ptr(cstr).to_str().unwrap(); let s = CStr::from_ptr(cstr).to_str().unwrap();
let cstr_len = s.len(); let cstr_len = s.len();
@ -28,6 +40,8 @@ pub unsafe fn copy_cstr_into_wasm(instance: &mut Instance, cstr: *const c_char)
*loc = byte; *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; *raw_memory.add(cstr_len) = 0;
space_offset space_offset