open_coroutine_hook/syscall/
unix.rs

1use libc::{
2    fd_set, iovec, mode_t, msghdr, off_t, pthread_cond_t, pthread_mutex_t, size_t, sockaddr,
3    socklen_t, ssize_t, timespec, timeval,
4};
5use std::ffi::{c_char, c_int, c_uint, c_void};
6
7// check https://www.rustwiki.org.cn/en/reference/introduction.html for help information
8#[allow(unused_macros)]
9macro_rules! impl_hook {
10    ( $field_name: ident, $syscall: ident($($arg: ident : $arg_type: ty),*) -> $result: ty ) => {
11        #[no_mangle]
12        pub extern "C" fn $syscall(
13            $($arg: $arg_type),*
14        ) -> $result {
15            static $field_name: once_cell::sync::Lazy<
16                extern "C" fn($($arg_type, )*) -> $result,
17            > = once_cell::sync::Lazy::new(|| unsafe {
18                let syscall: &str = open_coroutine_core::common::constants::SyscallName::$syscall.into();
19                let symbol = std::ffi::CString::new(String::from(syscall))
20                    .unwrap_or_else(|_| panic!("can not transfer \"{syscall}\" to CString"));
21                let ptr = libc::dlsym(libc::RTLD_NEXT, symbol.as_ptr());
22                assert!(!ptr.is_null(), "syscall \"{syscall}\" not found !");
23                std::mem::transmute(ptr)
24            });
25            let fn_ptr = once_cell::sync::Lazy::force(&$field_name);
26            if $crate::hook()
27                || open_coroutine_core::scheduler::SchedulableCoroutine::current().is_some()
28                || cfg!(feature = "ci")
29            {
30                return open_coroutine_core::syscall::$syscall(Some(fn_ptr), $($arg, )*);
31            }
32            (fn_ptr)($($arg),*)
33        }
34    }
35}
36
37// The following are supported syscall
38impl_hook!(SLEEP, sleep(secs: c_uint) -> c_uint);
39impl_hook!(USLEEP, usleep(microseconds: c_uint) -> c_int);
40impl_hook!(NANOSLEEP, nanosleep(rqtp: *const timespec, rmtp: *mut timespec) -> c_int);
41impl_hook!(SELECT, select(nfds: c_int, readfds: *mut fd_set, writefds: *mut fd_set, errorfds: *mut fd_set, timeout: *mut timeval) -> c_int);
42impl_hook!(SOCKET, socket(domain: c_int, type_: c_int, protocol: c_int) -> c_int);
43impl_hook!(SETSOCKOPT, setsockopt(socket: c_int, level: c_int, name: c_int, value: *const c_void, option_len: socklen_t) -> c_int);
44impl_hook!(CONNECT, connect(fd: c_int, address: *const sockaddr, len: socklen_t) -> c_int);
45impl_hook!(LISTEN, listen(fd: c_int, backlog: c_int) -> c_int);
46impl_hook!(ACCEPT, accept(fd: c_int, address: *mut sockaddr, address_len: *mut socklen_t) -> c_int);
47#[cfg(any(
48    target_os = "linux",
49    target_os = "l4re",
50    target_os = "android",
51    target_os = "emscripten"
52))]
53impl_hook!(ACCEPT4, accept4(fd: c_int, addr: *mut sockaddr, len: *mut socklen_t, flg: c_int) -> c_int);
54impl_hook!(SHUTDOWN, shutdown(fd: c_int, how: c_int) -> c_int);
55impl_hook!(RECV, recv(fd: c_int, buf: *mut c_void, len: size_t, flags: c_int) -> ssize_t);
56impl_hook!(RECVFROM, recvfrom(fd: c_int, buf: *mut c_void, len: size_t, flags: c_int, addr: *mut sockaddr, addrlen: *mut socklen_t) -> ssize_t);
57#[cfg(not(all(target_os = "linux", feature = "io_uring")))]
58impl_hook!(READ, read(fd: c_int, buf: *mut c_void, count: size_t) -> ssize_t);
59impl_hook!(PREAD, pread(fd: c_int, buf: *mut c_void, count: size_t, offset: off_t) -> ssize_t);
60impl_hook!(READV, readv(fd: c_int, iov: *const iovec, iovcnt: c_int) -> ssize_t);
61impl_hook!(PREADV, preadv(fd: c_int, iov: *const iovec, iovcnt: c_int, offset: off_t) -> ssize_t);
62impl_hook!(RECVMSG, recvmsg(fd: c_int, msg: *mut msghdr, flags: c_int) -> ssize_t);
63impl_hook!(SEND, send(fd: c_int, buf: *const c_void, len: size_t, flags: c_int) -> ssize_t);
64impl_hook!(SENDTO, sendto(fd: c_int, buf: *const c_void, len: size_t, flags: c_int, addr: *const sockaddr, addrlen: socklen_t) -> ssize_t);
65impl_hook!(PWRITE, pwrite(fd: c_int, buf: *const c_void, count: size_t, offset: off_t) -> ssize_t);
66impl_hook!(WRITEV, writev(fd: c_int, iov: *const iovec, iovcnt: c_int) -> ssize_t);
67impl_hook!(PWRITEV, pwritev(fd: c_int, iov: *const iovec, iovcnt: c_int, offset: off_t) -> ssize_t);
68impl_hook!(SENDMSG, sendmsg(fd: c_int, msg: *const msghdr, flags: c_int) -> ssize_t);
69impl_hook!(PTHREAD_COND_TIMEDWAIT, pthread_cond_timedwait(cond: *mut pthread_cond_t, lock: *mut pthread_mutex_t, abstime: *const timespec) -> c_int);
70impl_hook!(PTHREAD_MUTEX_TRYLOCK, pthread_mutex_trylock(lock: *mut pthread_mutex_t) -> c_int);
71impl_hook!(MKDIR, mkdir(path: *const c_char, mode: mode_t) -> c_int);
72impl_hook!(RMDIR, rmdir(path: *const c_char) -> c_int);
73impl_hook!(LSEEK, lseek(fd: c_int, offset: off_t, whence: c_int) -> off_t);
74impl_hook!(LINK, link(src: *const c_char, dst: *const c_char) -> c_int);
75impl_hook!(UNLINK, unlink(src: *const c_char) -> c_int);
76impl_hook!(FSYNC, fsync(fd: c_int) -> c_int);
77impl_hook!(MKDIRAT, mkdirat(dirfd: c_int, pathname: *const c_char, mode: mode_t) -> c_int);
78impl_hook!(RENAMEAT, renameat(olddirfd: c_int, oldpath: *const c_char, newdirfd: c_int, newpath: *const c_char) -> c_int);
79#[cfg(target_os = "linux")]
80impl_hook!(RENAMEAT2, renameat2(olddirfd: c_int, oldpath: *const c_char, newdirfd: c_int, newpath: *const c_char, flags: c_uint) -> c_int);
81
82// NOTE: unhook poll due to mio's poller
83// impl_hook!(POLL, poll(fds: *mut pollfd, nfds: nfds_t, timeout: c_int) -> c_int);
84
85// NOTE: unhook write/pthread_mutex_lock/pthread_mutex_unlock due to stack overflow or bug
86// impl_hook!(WRITE, write(fd: c_int, buf: *const c_void, count: size_t) -> ssize_t);
87// impl_hook!(PTHREAD_MUTEX_LOCK, pthread_mutex_lock(lock: *mut pthread_mutex_t) -> c_int);
88// impl_hook!(PTHREAD_MUTEX_UNLOCK, pthread_mutex_unlock(lock: *mut pthread_mutex_t) -> c_int);