Skip to content

Commit

Permalink
improve networking in emscripten
Browse files Browse the repository at this point in the history
  • Loading branch information
Mark McCaskey committed Jul 1, 2019
1 parent fada36a commit 623bec0
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 73 deletions.
3 changes: 2 additions & 1 deletion lib/emscripten/src/emscripten_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ pub fn _pthread_condattr_setclock(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 {
}
pub fn _pthread_create(_ctx: &mut Ctx, _a: i32, _b: i32, _c: i32, _d: i32) -> i32 {
trace!("emscripten::_pthread_create");
0
// 11 seems to mean "no"
11
}
pub fn _pthread_detach(_ctx: &mut Ctx, _a: i32) -> i32 {
trace!("emscripten::_pthread_detach");
Expand Down
6 changes: 3 additions & 3 deletions lib/emscripten/src/env/unix/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,10 +220,10 @@ pub fn _getaddrinfo(
.deref(memory)
.map(|m| m as *const Cell<c_char> as *const c_char))
.unwrap_or(std::ptr::null()),
(service_str_ptr
service_str_ptr
.deref(memory)
.map(|m| m as *const Cell<c_char> as *const c_char))
.unwrap_or(std::ptr::null()),
.map(|m| m as *const Cell<c_char> as *const c_char)
.unwrap_or(std::ptr::null()),
hints
.as_ref()
.map(|h| h as *const addrinfo)
Expand Down
4 changes: 2 additions & 2 deletions lib/emscripten/src/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ pub fn _emscripten_memcpy_big(ctx: &mut Ctx, dest: u32, src: u32, len: u32) -> u

/// emscripten: _emscripten_get_heap_size
pub fn _emscripten_get_heap_size(ctx: &mut Ctx) -> u32 {
debug!("emscripten::_emscripten_get_heap_size");
trace!("emscripten::_emscripten_get_heap_size");
let result = ctx.memory(0).size().bytes().0 as u32;
debug!("=> {}", result);
trace!("=> {}", result);

result
}
Expand Down
43 changes: 24 additions & 19 deletions lib/emscripten/src/syscalls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,28 +279,38 @@ pub fn ___syscall75(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
// readlink
pub fn ___syscall85(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 {
debug!("emscripten::___syscall85 (readlink)");
let _path = varargs.get_str(ctx);
let pathname_addr = varargs.get_str(ctx);
let buf = varargs.get_str(ctx);
// let buf_addr: i32 = varargs.get(ctx);
let buf_size: i32 = varargs.get(ctx);
let fd = 3;
let ret = unsafe { read(fd, buf as _, buf_size as _) as i32 };
let real_path_owned = get_cstr_path(ctx, pathname_addr);
let real_path = if let Some(ref rp) = real_path_owned {
rp.as_c_str().as_ptr()
} else {
pathname_addr
};
// let fd = 3;
// let ret = unsafe { read(fd, buf as _, buf_size as _) as i32 };
// debug!(
// "=> buf: {}, buf_size: {}, return: {} ",
// unsafe { std::ffi::CStr::from_ptr(buf as _).to_str().unwrap() },
// buf_size,
// ret
// );
let ret = unsafe { libc::readlink(real_path, buf as _, buf_size as _) as i32 };
if ret == -1 {
debug!("readlink failed");
return ret;
}
debug!(
"=> buf: {}, buf_size: {}, return: {} ",
"=> path: {}, buf: {}, buf_size: {}, return: {} ",
unsafe { std::ffi::CStr::from_ptr(real_path).to_str().unwrap() },
unsafe { std::ffi::CStr::from_ptr(buf as _).to_str().unwrap() },
// std::ffi::CStr::from_ptr(buf).to_str().unwrap(),
// buf,
buf_size,
ret
);
// let ret = unsafe {
// readlink(path, buf as _, buf_size as _) as i32
// };
// debug!("=> path: {}, buf: {}, buf_size: {}, return: {} ",
// unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() },
// unsafe { std::ffi::CStr::from_ptr(buf as _).to_str().unwrap() },
// // std::ffi::CStr::from_ptr(buf).to_str().unwrap(),
// // buf,
// buf_size,
// ret);
ret
}

Expand Down Expand Up @@ -531,11 +541,6 @@ pub fn ___syscall146(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 {
}
}

pub fn ___syscall168(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
debug!("emscripten::___syscall168 - stub");
-1
}

pub fn ___syscall191(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 {
let _resource: i32 = varargs.get(ctx);
debug!(
Expand Down
165 changes: 121 additions & 44 deletions lib/emscripten/src/syscalls/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,14 @@ use libc::{
F_GETFD,
F_SETFD,
SOL_SOCKET,
SO_REUSEADDR,
TIOCGWINSZ,
};

#[allow(unused_imports)]
use std::ffi::CStr;
use wasmer_runtime_core::vm::Ctx;
use wasmer_runtime_core::{memory::ptr::WasmPtr, vm::Ctx};

use crate::env::EmSockAddr;
use crate::utils;
#[allow(unused_imports)]
use std::io::Error;
Expand All @@ -101,9 +101,9 @@ use libc::{fallocate, fdatasync, ftruncate64, lstat, madvise, wait4};
// Another conditional constant for name resolution: Macos et iOS use
// SO_NOSIGPIPE as a setsockopt flag to disable SIGPIPE emission on socket.
// Other platforms do otherwise.
#[cfg(target_os = "darwin")]
#[cfg(target_os = "macos")]
use libc::SO_NOSIGPIPE;
#[cfg(not(target_os = "darwin"))]
#[cfg(not(target_os = "macos"))]
const SO_NOSIGPIPE: c_int = 0;

/// open
Expand Down Expand Up @@ -444,6 +444,7 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
let call: u32 = varargs.get(ctx);
let mut socket_varargs: VarArgs = varargs.get(ctx);

// migrating to EmSockAddr, port being separate here is nice, should update that too
#[repr(C)]
pub struct GuestSockaddrIn {
pub sin_family: sa_family_t, // u16
Expand All @@ -458,13 +459,6 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
pub s_addr: in_addr_t, // u32
}

// debug!("GuestSockaddrIn = {}", size_of::<GuestSockaddrIn>());

pub struct LinuxSockAddr {
pub sa_family: u16,
pub sa_data: [c_char; 14],
}

match call {
1 => {
debug!("socket: socket");
Expand All @@ -487,6 +481,7 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
unimplemented!("non blocking sockets");
}

// why is this here?
type T = u32;
let payload = 1 as *const T as _;
unsafe {
Expand All @@ -500,7 +495,7 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
};

debug!(
"=> domain: {} (AF_INET/2), type: {} (SOCK_STREAM/1), protocol: {} = fd: {}",
"=> domain: {}, type: {}, protocol: {} = fd: {}",
domain, ty, protocol, fd
);
fd as _
Expand Down Expand Up @@ -555,47 +550,75 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
5 => {
debug!("socket: accept");
// accept (socket: c_int, address: *mut sockaddr, address_len: *mut socklen_t) -> c_int
let socket = socket_varargs.get(ctx);
let address_addr: u32 = socket_varargs.get(ctx);
let address_len: u32 = socket_varargs.get(ctx);
let address = emscripten_memory_pointer!(ctx.memory(0), address_addr) as *mut sockaddr;
let socket: i32 = socket_varargs.get(ctx);
let address: WasmPtr<EmSockAddr> = socket_varargs.get(ctx);
let address_len: WasmPtr<u32> = socket_varargs.get(ctx);

debug!(
"=> socket: {}, address: {:?}, address_len: {}",
socket, address, address_len
socket,
address.deref(ctx.memory(0)).unwrap().get(),
address_len.deref(ctx.memory(0)).unwrap().get()
);
let address_len_addr =
emscripten_memory_pointer!(ctx.memory(0), address_len) as *mut socklen_t;
unsafe { address_len.deref_mut(ctx.memory(0)).unwrap().get_mut() };
// let mut address_len_addr: socklen_t = 0;

let fd = unsafe { accept(socket, address, address_len_addr) };
let (fd, host_address) = unsafe {
let mut host_address: sockaddr = std::mem::uninitialized();
let fd = accept(socket, &mut host_address, address_len_addr);

unsafe {
let address_linux =
emscripten_memory_pointer!(ctx.memory(0), address_addr) as *mut LinuxSockAddr;
(*address_linux).sa_family = (*address).sa_family as u16;
(*address_linux).sa_data = (*address).sa_data;
(fd, host_address)
};

let address_addr = unsafe { address.deref_mut(ctx.memory(0)).unwrap().get_mut() };

address_addr.sa_family = host_address.sa_family as _;
address_addr.sa_data = host_address.sa_data.clone();

// why is this here?
// set_cloexec
unsafe {
ioctl(fd, FIOCLEX);
};

debug!("fd: {}", fd);
debug!(
"address: {:?}, len: {}, result fd = {}",
address_addr, address_len_addr, fd
);

fd as _
}
6 => {
debug!("socket: getsockname");
// getsockname (socket: c_int, address: *mut sockaddr, address_len: *mut socklen_t) -> c_int
let socket = socket_varargs.get(ctx);
let address: u32 = socket_varargs.get(ctx);
let address_len: u32 = socket_varargs.get(ctx);
let address = emscripten_memory_pointer!(ctx.memory(0), address) as *mut sockaddr;
let socket: i32 = socket_varargs.get(ctx);
let address: WasmPtr<EmSockAddr> = socket_varargs.get(ctx);
let address_len: WasmPtr<u32> = socket_varargs.get(ctx);
let address_len_addr =
emscripten_memory_pointer!(ctx.memory(0), address_len) as *mut socklen_t;
unsafe { getsockname(socket, address, address_len_addr) }
unsafe { address_len.deref_mut(ctx.memory(0)).unwrap().get_mut() };

let (ret, sock_addr_host) = unsafe {
// read host data into new var
let mut address: sockaddr = std::mem::uninitialized();
let ret = getsockname(
socket,
&mut address as *mut sockaddr,
address_len_addr as *mut u32,
);
(ret, address)
};
// translate from host data into emscripten data
let mut address_mut = unsafe { address.deref_mut(ctx.memory(0)).unwrap().get_mut() };
address_mut.sa_family = sock_addr_host.sa_family as _;
address_mut.sa_data = sock_addr_host.sa_data.clone();

debug!(
"=> socket: {}, address, {:?}, address_len: {}, result = {}",
socket, address_mut, address_len_addr, ret
);

ret
}
7 => {
debug!("socket: getpeername");
Expand Down Expand Up @@ -647,33 +670,33 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
}
14 => {
debug!("socket: setsockopt");
// NOTE: Emscripten seems to be passing the wrong values to this syscall
// level: Em passes 1 as SOL_SOCKET; SOL_SOCKET is 0xffff in BSD
// name: Em passes SO_ACCEPTCONN, but Nginx complains about REUSEADDR
// OSX and BSD have completely different values, be very careful here
// https://github.com/openbsd/src/blob/master/sys/sys/socket.h#L156
// setsockopt (socket: c_int, level: c_int, name: c_int, value: *const c_void, option_len: socklen_t) -> c_int

let socket = socket_varargs.get(ctx);
let level: i32 = socket_varargs.get(ctx);
// SOL_SOCKET = 0xffff (BSD, OSX)
let level = if level == 1 { SOL_SOCKET } else { level };
let name: i32 = socket_varargs.get(ctx);
// SO_REUSEADDR = 0x4 (BSD, OSX)
let name = if name == 2 { SO_REUSEADDR } else { name };
let untranslated_name: i32 = socket_varargs.get(ctx);
let value: u32 = socket_varargs.get(ctx);
let option_len = socket_varargs.get(ctx);
let value_addr = emscripten_memory_pointer!(ctx.memory(0), value) as _; // Endian problem
let option_len: u32 = socket_varargs.get(ctx);
let value_addr =
emscripten_memory_pointer!(ctx.memory(0), value) as *const libc::c_void;
let name: i32 = translate_socket_name_flag(untranslated_name);

let ret = unsafe { setsockopt(socket, level, name, value_addr, option_len) };

debug!("=> socketfd: {}, level: {}, name: {}, value_addr: {:?}, option_len: {} = status: {}", socket, level, name, value_addr, option_len, ret);
debug!("=> socketfd: {}, level: {}, name: {}, value_addr: {:?}, option_len: {} = status: {}", socket, level, untranslated_name, value_addr, option_len, ret);
ret
}
15 => {
debug!("socket: getsockopt");
// getsockopt (sockfd: c_int, level: c_int, optname: c_int, optval: *mut c_void, optlen: *mut socklen_t) -> c_int
let socket = socket_varargs.get(ctx);
let level: i32 = socket_varargs.get(ctx);
let name: i32 = socket_varargs.get(ctx);
let level = if level == 1 { SOL_SOCKET } else { level };
let untranslated_name: i32 = socket_varargs.get(ctx);
let name: i32 = translate_socket_name_flag(untranslated_name);
let value: u32 = socket_varargs.get(ctx);
let option_len: u32 = socket_varargs.get(ctx);
let value_addr = emscripten_memory_pointer!(ctx.memory(0), value) as _;
Expand Down Expand Up @@ -706,6 +729,60 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
}
}

/// OSX and BSD have completely different values, we must translate from emscripten's Linuxy
/// value into one that we can pass to native syscalls
fn translate_socket_name_flag(name: i32) -> i32 {
match name {
2 => libc::SO_REUSEADDR,
3 => libc::SO_TYPE,
4 => libc::SO_ERROR,
5 => libc::SO_DONTROUTE,
6 => libc::SO_BROADCAST,
7 => libc::SO_SNDBUF,
8 => libc::SO_RCVBUF,
9 => libc::SO_KEEPALIVE,
10 => libc::SO_OOBINLINE,
13 => libc::SO_LINGER,
18 => libc::SO_RCVLOWAT,
19 => libc::SO_SNDLOWAT,
20 => libc::SO_RCVTIMEO,
21 => libc::SO_SNDTIMEO,
// SO_DEBUG missing
30 => libc::SO_ACCEPTCONN,
otherwise => otherwise,
}
}

#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct EmPollFd {
pub fd: i32,
pub events: i16,
pub revents: i16,
}

unsafe impl wasmer_runtime_core::types::ValueType for EmPollFd {}

/// poll
pub fn ___syscall168(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 {
debug!("emscripten::___syscall168(poll)");
let fds: WasmPtr<EmPollFd> = varargs.get(ctx);
let nfds: u32 = varargs.get(ctx);
let timeout: i32 = varargs.get(ctx);

let fds_mut = unsafe { fds.deref_mut(ctx.memory(0)).unwrap().get_mut() };

let ret = unsafe {
libc::poll(
fds_mut as *mut EmPollFd as *mut libc::pollfd,
nfds as _,
timeout,
)
};

ret
}

// pread
pub fn ___syscall180(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall180 (pread) {}", _which);
Expand Down Expand Up @@ -885,11 +962,11 @@ pub fn ___syscall220(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 {

let dirp = emscripten_memory_pointer!(ctx.memory(0), dirp_addr) as *mut u8;

let mut opened_dirs = &mut get_emscripten_data(ctx).opened_dirs;
let opened_dirs = &mut get_emscripten_data(ctx).opened_dirs;

// need to persist stream across calls?
// let dir: *mut libc::DIR = unsafe { libc::fdopendir(fd) };
let mut dir = &*opened_dirs
let dir = &*opened_dirs
.entry(fd)
.or_insert_with(|| unsafe { Box::new(libc::fdopendir(fd)) });

Expand Down
6 changes: 6 additions & 0 deletions lib/emscripten/src/syscalls/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,12 @@ pub fn ___syscall122(_ctx: &mut Ctx, which: c_int, mut _varargs: VarArgs) -> c_i
-1
}

/// poll
pub fn ___syscall168(_ctx: &mut Ctx, _which: i32, _varargs: VarArgs) -> i32 {
debug!("emscripten::___syscall168(poll) - stub");
-1
}

/// lstat64
pub fn ___syscall196(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
debug!("emscripten::___syscall196 (lstat64) - stub");
Expand Down
Loading

0 comments on commit 623bec0

Please sign in to comment.