Fix localtime implementation

This commit is contained in:
Steve Akinyemi 2018-12-07 14:50:35 +01:00
parent bbb2f080cc
commit 181837d7cc
6 changed files with 164 additions and 87 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?
@ -27,6 +27,8 @@ const TOTAL_STACK: u32 = 5242880;
const DYNAMICTOP_PTR_DIFF: u32 = 1088; const DYNAMICTOP_PTR_DIFF: u32 = 1088;
// TODO: make this variable // TODO: make this variable
const STATIC_BUMP: u32 = 215536; const STATIC_BUMP: u32 = 215536;
// TODO: make this variable
const GLOBAL_BASE: u32 = 1024;
fn stacktop(static_bump: u32) -> u32 { fn stacktop(static_bump: u32) -> u32 {
align_memory(dynamictop_ptr(static_bump) + 4) align_memory(dynamictop_ptr(static_bump) + 4)
@ -44,11 +46,10 @@ 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; pub fn statictop(static_bump: u32) -> u32 {
// *static_top = (*static_top + size + 15) & (-16 as usize); GLOBAL_BASE + 5520
// 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;
@ -383,6 +384,11 @@ 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( import_object.set(
"env", "env",
"_localtime", "_localtime",
@ -393,7 +399,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) & 4294967280; // *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

@ -3,11 +3,13 @@ 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,37 +66,101 @@ 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, // 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 /// 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 tm_struct = &*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; (*tm_struct_ptr).tm_sec = tm_struct.tm_sec;
(*tm_struct_ptr).tm_min = tm_struct.tm_min; (*tm_struct_ptr).tm_min = tm_struct.tm_min;
(*tm_struct_ptr).tm_hour = tm_struct.tm_hour; (*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_wday = tm_struct.tm_wday;
(*tm_struct_ptr).tm_yday = tm_struct.tm_yday; (*tm_struct_ptr).tm_yday = tm_struct.tm_yday;
(*tm_struct_ptr).tm_isdst = tm_struct.tm_isdst; (*tm_struct_ptr).tm_isdst = tm_struct.tm_isdst;
(*tm_struct_ptr).tm_gmtoff = tm_struct.tm_gmtoff; (*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); (*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::<guest_tm>() 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 tm_struct_offset as c_int
} }

View File

@ -28,6 +28,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

View File

@ -92,7 +92,8 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
get_instance_function!(instance, func_index); get_instance_function!(instance, func_index);
let (argc, argv) = store_module_arguments(options, &mut instance); 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)); return call_protected!(main(argc, argv, &instance)).map_err(|err| format!("{}", err));
// TODO: We should implement emscripten __ATEXIT__ // TODO: We should implement emscripten __ATEXIT__
} else { } else {
@ -144,37 +145,3 @@ fn store_module_arguments(options: &Run, instance: &mut webassembly::Instance) -
(argc as u32, argv_offset) (argc as u32, argv_offset)
} }
// fn get_module_arguments(options: &Run, instance: &mut webassembly::Instance) -> (u32, u32) {
// // Application Arguments
// let mut arg_values: Vec<String> = 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)
// }