diff options
Diffstat (limited to 'src/linux')
-rw-r--r-- | src/linux/adjtime.c | 2 | ||||
-rw-r--r-- | src/linux/adjtimex.c | 4 | ||||
-rw-r--r-- | src/linux/cache.c | 34 | ||||
-rw-r--r-- | src/linux/clock_adjtime.c | 144 | ||||
-rw-r--r-- | src/linux/clone.c | 56 | ||||
-rw-r--r-- | src/linux/copy_file_range.c | 8 | ||||
-rw-r--r-- | src/linux/epoll.c | 5 | ||||
-rw-r--r-- | src/linux/fallocate.c | 3 | ||||
-rw-r--r-- | src/linux/getdents.c | 4 | ||||
-rw-r--r-- | src/linux/gettid.c | 8 | ||||
-rw-r--r-- | src/linux/membarrier.c | 72 | ||||
-rw-r--r-- | src/linux/ppoll.c | 10 | ||||
-rw-r--r-- | src/linux/preadv2.c | 17 | ||||
-rw-r--r-- | src/linux/prlimit.c | 3 | ||||
-rw-r--r-- | src/linux/pwritev2.c | 17 | ||||
-rw-r--r-- | src/linux/sendfile.c | 2 | ||||
-rw-r--r-- | src/linux/setgroups.c | 30 | ||||
-rw-r--r-- | src/linux/settimeofday.c | 7 | ||||
-rw-r--r-- | src/linux/statx.c | 42 | ||||
-rw-r--r-- | src/linux/timerfd.c | 42 | ||||
-rw-r--r-- | src/linux/wait4.c | 34 |
21 files changed, 509 insertions, 35 deletions
diff --git a/src/linux/adjtime.c b/src/linux/adjtime.c index fa8af9f0..5a707f2f 100644 --- a/src/linux/adjtime.c +++ b/src/linux/adjtime.c @@ -15,7 +15,7 @@ int adjtime(const struct timeval *in, struct timeval *out) tx.offset = in->tv_sec*1000000 + in->tv_usec; tx.modes = ADJ_OFFSET_SINGLESHOT; } - if (syscall(SYS_adjtimex, &tx) < 0) return -1; + if (adjtimex(&tx) < 0) return -1; if (out) { out->tv_sec = tx.offset / 1000000; if ((out->tv_usec = tx.offset % 1000000) < 0) { diff --git a/src/linux/adjtimex.c b/src/linux/adjtimex.c index 91de6824..e9d727cf 100644 --- a/src/linux/adjtimex.c +++ b/src/linux/adjtimex.c @@ -1,7 +1,7 @@ #include <sys/timex.h> -#include "syscall.h" +#include <time.h> int adjtimex(struct timex *tx) { - return syscall(SYS_adjtimex, tx); + return clock_adjtime(CLOCK_REALTIME, tx); } diff --git a/src/linux/cache.c b/src/linux/cache.c index 84a138a4..e76f7812 100644 --- a/src/linux/cache.c +++ b/src/linux/cache.c @@ -1,4 +1,6 @@ +#include <errno.h> #include "syscall.h" +#include "atomic.h" #ifdef SYS_cacheflush int _flush_cache(void *addr, int len, int op) @@ -15,3 +17,35 @@ int __cachectl(void *addr, int len, int op) } weak_alias(__cachectl, cachectl); #endif + +#ifdef SYS_riscv_flush_icache + +#define VDSO_FLUSH_ICACHE_SYM "__vdso_flush_icache" +#define VDSO_FLUSH_ICACHE_VER "LINUX_4.15" + +static void *volatile vdso_func; + +static int flush_icache_init(void *start, void *end, unsigned long int flags) +{ + void *p = __vdsosym(VDSO_FLUSH_ICACHE_VER, VDSO_FLUSH_ICACHE_SYM); + int (*f)(void *, void *, unsigned long int) = + (int (*)(void *, void *, unsigned long int))p; + a_cas_p(&vdso_func, (void *)flush_icache_init, p); + return f ? f(start, end, flags) : -ENOSYS; +} + +static void *volatile vdso_func = (void *)flush_icache_init; + +int __riscv_flush_icache(void *start, void *end, unsigned long int flags) +{ + int (*f)(void *, void *, unsigned long int) = + (int (*)(void *, void *, unsigned long int))vdso_func; + if (f) { + int r = f(start, end, flags); + if (!r) return r; + if (r != -ENOSYS) return __syscall_ret(r); + } + return syscall(SYS_riscv_flush_icache, start, end, flags); +} +weak_alias(__riscv_flush_icache, riscv_flush_icache); +#endif diff --git a/src/linux/clock_adjtime.c b/src/linux/clock_adjtime.c index 056ad6d3..d4d03d24 100644 --- a/src/linux/clock_adjtime.c +++ b/src/linux/clock_adjtime.c @@ -1,7 +1,151 @@ #include <sys/timex.h> +#include <time.h> +#include <errno.h> #include "syscall.h" +#define IS32BIT(x) !((x)+0x80000000ULL>>32) + +struct ktimex64 { + unsigned modes; + int :32; + long long offset, freq, maxerror, esterror; + int status; + int :32; + long long constant, precision, tolerance; + long long time_sec, time_usec; + long long tick, ppsfreq, jitter; + int shift; + int :32; + long long stabil, jitcnt, calcnt, errcnt, stbcnt; + int tai; + int __padding[11]; +}; + +struct ktimex { + unsigned modes; + long offset, freq, maxerror, esterror; + int status; + long constant, precision, tolerance; + long time_sec, time_usec; + long tick, ppsfreq, jitter; + int shift; + long stabil, jitcnt, calcnt, errcnt, stbcnt; + int tai; + int __padding[11]; +}; + int clock_adjtime (clockid_t clock_id, struct timex *utx) { + int r = -ENOSYS; +#ifdef SYS_clock_adjtime64 + struct ktimex64 ktx = { + .modes = utx->modes, + .offset = utx->offset, + .freq = utx->freq, + .maxerror = utx->maxerror, + .esterror = utx->esterror, + .status = utx->status, + .constant = utx->constant, + .precision = utx->precision, + .tolerance = utx->tolerance, + .time_sec = utx->time.tv_sec, + .time_usec = utx->time.tv_usec, + .tick = utx->tick, + .ppsfreq = utx->ppsfreq, + .jitter = utx->jitter, + .shift = utx->shift, + .stabil = utx->stabil, + .jitcnt = utx->jitcnt, + .calcnt = utx->calcnt, + .errcnt = utx->errcnt, + .stbcnt = utx->stbcnt, + .tai = utx->tai, + }; + r = __syscall(SYS_clock_adjtime64, clock_id, &ktx); + if (r>=0) { + utx->modes = ktx.modes; + utx->offset = ktx.offset; + utx->freq = ktx.freq; + utx->maxerror = ktx.maxerror; + utx->esterror = ktx.esterror; + utx->status = ktx.status; + utx->constant = ktx.constant; + utx->precision = ktx.precision; + utx->tolerance = ktx.tolerance; + utx->time.tv_sec = ktx.time_sec; + utx->time.tv_usec = ktx.time_usec; + utx->tick = ktx.tick; + utx->ppsfreq = ktx.ppsfreq; + utx->jitter = ktx.jitter; + utx->shift = ktx.shift; + utx->stabil = ktx.stabil; + utx->jitcnt = ktx.jitcnt; + utx->calcnt = ktx.calcnt; + utx->errcnt = ktx.errcnt; + utx->stbcnt = ktx.stbcnt; + utx->tai = ktx.tai; + } + if (SYS_clock_adjtime == SYS_clock_adjtime64 || r!=-ENOSYS) + return __syscall_ret(r); + if ((utx->modes & ADJ_SETOFFSET) && !IS32BIT(utx->time.tv_sec)) + return __syscall_ret(-ENOTSUP); +#endif + if (sizeof(time_t) > sizeof(long)) { + struct ktimex ktx = { + .modes = utx->modes, + .offset = utx->offset, + .freq = utx->freq, + .maxerror = utx->maxerror, + .esterror = utx->esterror, + .status = utx->status, + .constant = utx->constant, + .precision = utx->precision, + .tolerance = utx->tolerance, + .time_sec = utx->time.tv_sec, + .time_usec = utx->time.tv_usec, + .tick = utx->tick, + .ppsfreq = utx->ppsfreq, + .jitter = utx->jitter, + .shift = utx->shift, + .stabil = utx->stabil, + .jitcnt = utx->jitcnt, + .calcnt = utx->calcnt, + .errcnt = utx->errcnt, + .stbcnt = utx->stbcnt, + .tai = utx->tai, + }; +#ifdef SYS_adjtimex + if (clock_id==CLOCK_REALTIME) r = __syscall(SYS_adjtimex, &ktx); + else +#endif + r = __syscall(SYS_clock_adjtime, clock_id, &ktx); + if (r>=0) { + utx->modes = ktx.modes; + utx->offset = ktx.offset; + utx->freq = ktx.freq; + utx->maxerror = ktx.maxerror; + utx->esterror = ktx.esterror; + utx->status = ktx.status; + utx->constant = ktx.constant; + utx->precision = ktx.precision; + utx->tolerance = ktx.tolerance; + utx->time.tv_sec = ktx.time_sec; + utx->time.tv_usec = ktx.time_usec; + utx->tick = ktx.tick; + utx->ppsfreq = ktx.ppsfreq; + utx->jitter = ktx.jitter; + utx->shift = ktx.shift; + utx->stabil = ktx.stabil; + utx->jitcnt = ktx.jitcnt; + utx->calcnt = ktx.calcnt; + utx->errcnt = ktx.errcnt; + utx->stbcnt = ktx.stbcnt; + utx->tai = ktx.tai; + } + return __syscall_ret(r); + } +#ifdef SYS_adjtimex + if (clock_id==CLOCK_REALTIME) return syscall(SYS_adjtimex, utx); +#endif return syscall(SYS_clock_adjtime, clock_id, utx); } diff --git a/src/linux/clone.c b/src/linux/clone.c index 8c1af7d3..257c1cec 100644 --- a/src/linux/clone.c +++ b/src/linux/clone.c @@ -4,18 +4,62 @@ #include <sched.h> #include "pthread_impl.h" #include "syscall.h" +#include "lock.h" +#include "fork_impl.h" + +struct clone_start_args { + int (*func)(void *); + void *arg; + sigset_t sigmask; +}; + +static int clone_start(void *arg) +{ + struct clone_start_args *csa = arg; + __post_Fork(0); + __restore_sigs(&csa->sigmask); + return csa->func(csa->arg); +} int clone(int (*func)(void *), void *stack, int flags, void *arg, ...) { + struct clone_start_args csa; va_list ap; - pid_t *ptid, *ctid; - void *tls; + pid_t *ptid = 0, *ctid = 0; + void *tls = 0; + + /* Flags that produce an invalid thread/TLS state are disallowed. */ + int badflags = CLONE_THREAD | CLONE_SETTLS | CLONE_CHILD_CLEARTID; + + if ((flags & badflags) || !stack) + return __syscall_ret(-EINVAL); va_start(ap, arg); - ptid = va_arg(ap, pid_t *); - tls = va_arg(ap, void *); - ctid = va_arg(ap, pid_t *); + if (flags & (CLONE_PIDFD | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID)) + ptid = va_arg(ap, pid_t *); + if (flags & CLONE_CHILD_SETTID) { + tls = va_arg(ap, void *); + ctid = va_arg(ap, pid_t *); + } va_end(ap); - return __syscall_ret(__clone(func, stack, flags, arg, ptid, tls, ctid)); + /* If CLONE_VM is used, it's impossible to give the child a consistent + * thread structure. In this case, the best we can do is assume the + * caller is content with an extremely restrictive execution context + * like the one vfork() would provide. */ + if (flags & CLONE_VM) return __syscall_ret( + __clone(func, stack, flags, arg, ptid, tls, ctid)); + + __block_all_sigs(&csa.sigmask); + LOCK(__abort_lock); + + /* Setup the a wrapper start function for the child process to do + * mimic _Fork in producing a consistent execution state. */ + csa.func = func; + csa.arg = arg; + int ret = __clone(clone_start, stack, flags, &csa, ptid, tls, ctid); + + __post_Fork(ret); + __restore_sigs(&csa.sigmask); + return __syscall_ret(ret); } diff --git a/src/linux/copy_file_range.c b/src/linux/copy_file_range.c new file mode 100644 index 00000000..dd4b1333 --- /dev/null +++ b/src/linux/copy_file_range.c @@ -0,0 +1,8 @@ +#define _GNU_SOURCE +#include <unistd.h> +#include "syscall.h" + +ssize_t copy_file_range(int fd_in, off_t *off_in, int fd_out, off_t *off_out, size_t len, unsigned flags) +{ + return syscall(SYS_copy_file_range, fd_in, off_in, fd_out, off_out, len, flags); +} diff --git a/src/linux/epoll.c b/src/linux/epoll.c index deff5b10..e56e8f4c 100644 --- a/src/linux/epoll.c +++ b/src/linux/epoll.c @@ -5,6 +5,7 @@ int epoll_create(int size) { + if (size<=0) return __syscall_ret(-EINVAL); return epoll_create1(0); } @@ -24,9 +25,9 @@ int epoll_ctl(int fd, int op, int fd2, struct epoll_event *ev) int epoll_pwait(int fd, struct epoll_event *ev, int cnt, int to, const sigset_t *sigs) { - int r = __syscall(SYS_epoll_pwait, fd, ev, cnt, to, sigs, _NSIG/8); + int r = __syscall_cp(SYS_epoll_pwait, fd, ev, cnt, to, sigs, _NSIG/8); #ifdef SYS_epoll_wait - if (r==-ENOSYS && !sigs) r = __syscall(SYS_epoll_wait, fd, ev, cnt, to); + if (r==-ENOSYS && !sigs) r = __syscall_cp(SYS_epoll_wait, fd, ev, cnt, to); #endif return __syscall_ret(r); } diff --git a/src/linux/fallocate.c b/src/linux/fallocate.c index 7d68bc8f..9146350e 100644 --- a/src/linux/fallocate.c +++ b/src/linux/fallocate.c @@ -7,6 +7,3 @@ int fallocate(int fd, int mode, off_t base, off_t len) return syscall(SYS_fallocate, fd, mode, __SYSCALL_LL_E(base), __SYSCALL_LL_E(len)); } - -#undef fallocate64 -weak_alias(fallocate, fallocate64); diff --git a/src/linux/getdents.c b/src/linux/getdents.c index de6de3b4..97f76e14 100644 --- a/src/linux/getdents.c +++ b/src/linux/getdents.c @@ -1,10 +1,10 @@ #define _BSD_SOURCE #include <dirent.h> +#include <limits.h> #include "syscall.h" int getdents(int fd, struct dirent *buf, size_t len) { + if (len>INT_MAX) len = INT_MAX; return syscall(SYS_getdents, fd, buf, len); } - -weak_alias(getdents, getdents64); diff --git a/src/linux/gettid.c b/src/linux/gettid.c new file mode 100644 index 00000000..70767137 --- /dev/null +++ b/src/linux/gettid.c @@ -0,0 +1,8 @@ +#define _GNU_SOURCE +#include <unistd.h> +#include "pthread_impl.h" + +pid_t gettid(void) +{ + return __pthread_self()->tid; +} diff --git a/src/linux/membarrier.c b/src/linux/membarrier.c new file mode 100644 index 00000000..f64fe7e1 --- /dev/null +++ b/src/linux/membarrier.c @@ -0,0 +1,72 @@ +#include <sys/membarrier.h> +#include <semaphore.h> +#include <signal.h> +#include <string.h> +#include "pthread_impl.h" +#include "syscall.h" + +static void dummy_0(void) +{ +} + +weak_alias(dummy_0, __tl_lock); +weak_alias(dummy_0, __tl_unlock); + +static sem_t barrier_sem; + +static void bcast_barrier(int s) +{ + sem_post(&barrier_sem); +} + +int __membarrier(int cmd, int flags) +{ + int r = __syscall(SYS_membarrier, cmd, flags); + /* Emulate the private expedited command, which is needed by the + * dynamic linker for installation of dynamic TLS, for older + * kernels that lack the syscall. Unlike the syscall, this only + * synchronizes with threads of the process, not other processes + * sharing the VM, but such sharing is not a supported usage + * anyway. */ + if (r && cmd == MEMBARRIER_CMD_PRIVATE_EXPEDITED && !flags) { + pthread_t self=__pthread_self(), td; + sigset_t set; + __block_app_sigs(&set); + __tl_lock(); + sem_init(&barrier_sem, 0, 0); + struct sigaction sa = { + .sa_flags = SA_RESTART | SA_ONSTACK, + .sa_handler = bcast_barrier + }; + memset(&sa.sa_mask, -1, sizeof sa.sa_mask); + if (!__libc_sigaction(SIGSYNCCALL, &sa, 0)) { + for (td=self->next; td!=self; td=td->next) + __syscall(SYS_tkill, td->tid, SIGSYNCCALL); + for (td=self->next; td!=self; td=td->next) + sem_wait(&barrier_sem); + r = 0; + sa.sa_handler = SIG_IGN; + __libc_sigaction(SIGSYNCCALL, &sa, 0); + } + sem_destroy(&barrier_sem); + __tl_unlock(); + __restore_sigs(&set); + } + return __syscall_ret(r); +} + +void __membarrier_init(void) +{ + /* If membarrier is linked, attempt to pre-register to be able to use + * the private expedited command before the process becomes multi- + * threaded, since registering later has bad, potentially unbounded + * latency. This syscall should be essentially free, and it's arguably + * a mistake in the API design that registration was even required. + * For other commands, registration may impose some cost, so it's left + * to the application to do so if desired. Unfortunately this means + * library code initialized after the process becomes multi-threaded + * cannot use these features without accepting registration latency. */ + __syscall(SYS_membarrier, MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, 0); +} + +weak_alias(__membarrier, membarrier); diff --git a/src/linux/ppoll.c b/src/linux/ppoll.c deleted file mode 100644 index 9e262477..00000000 --- a/src/linux/ppoll.c +++ /dev/null @@ -1,10 +0,0 @@ -#define _GNU_SOURCE -#include <poll.h> -#include <signal.h> -#include "syscall.h" - -int ppoll(struct pollfd *fds, nfds_t n, const struct timespec *to, const sigset_t *mask) -{ - return syscall_cp(SYS_ppoll, fds, n, - to ? (struct timespec []){*to} : 0, mask, _NSIG/8); -} diff --git a/src/linux/preadv2.c b/src/linux/preadv2.c new file mode 100644 index 00000000..5e7ab70f --- /dev/null +++ b/src/linux/preadv2.c @@ -0,0 +1,17 @@ +#define _GNU_SOURCE +#include <sys/uio.h> +#include <unistd.h> +#include "syscall.h" + +ssize_t preadv2(int fd, const struct iovec *iov, int count, off_t ofs, int flags) +{ +#ifdef SYS_preadv + if (!flags) { + if (ofs==-1) return readv(fd, iov, count); + return syscall_cp(SYS_preadv, fd, iov, count, + (long)(ofs), (long)(ofs>>32)); + } +#endif + return syscall_cp(SYS_preadv2, fd, iov, count, + (long)(ofs), (long)(ofs>>32), flags); +} diff --git a/src/linux/prlimit.c b/src/linux/prlimit.c index 3df9ffba..fcf45aab 100644 --- a/src/linux/prlimit.c +++ b/src/linux/prlimit.c @@ -21,6 +21,3 @@ int prlimit(pid_t pid, int resource, const struct rlimit *new_limit, struct rlim } return r; } - -#undef prlimit64 -weak_alias(prlimit, prlimit64); diff --git a/src/linux/pwritev2.c b/src/linux/pwritev2.c new file mode 100644 index 00000000..ece90d7c --- /dev/null +++ b/src/linux/pwritev2.c @@ -0,0 +1,17 @@ +#define _GNU_SOURCE +#include <sys/uio.h> +#include <unistd.h> +#include "syscall.h" + +ssize_t pwritev2(int fd, const struct iovec *iov, int count, off_t ofs, int flags) +{ +#ifdef SYS_pwritev + if (!flags) { + if (ofs==-1) return writev(fd, iov, count); + return syscall_cp(SYS_pwritev, fd, iov, count, + (long)(ofs), (long)(ofs>>32)); + } +#endif + return syscall_cp(SYS_pwritev2, fd, iov, count, + (long)(ofs), (long)(ofs>>32), flags); +} diff --git a/src/linux/sendfile.c b/src/linux/sendfile.c index 9afe6dd6..fc1577d3 100644 --- a/src/linux/sendfile.c +++ b/src/linux/sendfile.c @@ -5,5 +5,3 @@ ssize_t sendfile(int out_fd, int in_fd, off_t *ofs, size_t count) { return syscall(SYS_sendfile, out_fd, in_fd, ofs, count); } - -weak_alias(sendfile, sendfile64); diff --git a/src/linux/setgroups.c b/src/linux/setgroups.c index 1248fdbf..47142f14 100644 --- a/src/linux/setgroups.c +++ b/src/linux/setgroups.c @@ -1,8 +1,36 @@ #define _GNU_SOURCE #include <unistd.h> +#include <signal.h> #include "syscall.h" +#include "libc.h" + +struct ctx { + size_t count; + const gid_t *list; + int ret; +}; + +static void do_setgroups(void *p) +{ + struct ctx *c = p; + if (c->ret<0) return; + int ret = __syscall(SYS_setgroups, c->count, c->list); + if (ret && !c->ret) { + /* If one thread fails to set groups after another has already + * succeeded, forcibly killing the process is the only safe + * thing to do. State is inconsistent and dangerous. Use + * SIGKILL because it is uncatchable. */ + __block_all_sigs(0); + __syscall(SYS_kill, __syscall(SYS_getpid), SIGKILL); + } + c->ret = ret; +} int setgroups(size_t count, const gid_t list[]) { - return syscall(SYS_setgroups, count, list); + /* ret is initially nonzero so that failure of the first thread does not + * trigger the safety kill above. */ + struct ctx c = { .count = count, .list = list, .ret = 1 }; + __synccall(do_setgroups, &c); + return __syscall_ret(c.ret); } diff --git a/src/linux/settimeofday.c b/src/linux/settimeofday.c index 15c18c63..860fb5de 100644 --- a/src/linux/settimeofday.c +++ b/src/linux/settimeofday.c @@ -1,8 +1,13 @@ #define _BSD_SOURCE #include <sys/time.h> +#include <time.h> +#include <errno.h> #include "syscall.h" int settimeofday(const struct timeval *tv, const struct timezone *tz) { - return syscall(SYS_settimeofday, tv, 0); + if (!tv) return 0; + if (tv->tv_usec >= 1000000ULL) return __syscall_ret(-EINVAL); + return clock_settime(CLOCK_REALTIME, &((struct timespec){ + .tv_sec = tv->tv_sec, .tv_nsec = tv->tv_usec * 1000})); } diff --git a/src/linux/statx.c b/src/linux/statx.c new file mode 100644 index 00000000..4616bff4 --- /dev/null +++ b/src/linux/statx.c @@ -0,0 +1,42 @@ +#define _GNU_SOURCE +#include <sys/stat.h> +#include <string.h> +#include <syscall.h> +#include <sys/sysmacros.h> +#include <errno.h> + +int statx(int dirfd, const char *restrict path, int flags, unsigned mask, struct statx *restrict stx) +{ + int ret = __syscall(SYS_statx, dirfd, path, flags, mask, stx); + +#ifndef SYS_fstatat + return __syscall_ret(ret); +#endif + + if (ret != -ENOSYS) return __syscall_ret(ret); + + struct stat st; + ret = fstatat(dirfd, path, &st, flags); + if (ret) return ret; + + stx->stx_dev_major = major(st.st_dev); + stx->stx_dev_minor = minor(st.st_dev); + stx->stx_ino = st.st_ino; + stx->stx_mode = st.st_mode; + stx->stx_nlink = st.st_nlink; + stx->stx_uid = st.st_uid; + stx->stx_gid = st.st_gid; + stx->stx_size = st.st_size; + stx->stx_blksize = st.st_blksize; + stx->stx_blocks = st.st_blocks; + stx->stx_atime.tv_sec = st.st_atim.tv_sec; + stx->stx_atime.tv_nsec = st.st_atim.tv_nsec; + stx->stx_mtime.tv_sec = st.st_mtim.tv_sec; + stx->stx_mtime.tv_nsec = st.st_mtim.tv_nsec; + stx->stx_ctime.tv_sec = st.st_ctim.tv_sec; + stx->stx_ctime.tv_nsec = st.st_ctim.tv_nsec; + stx->stx_btime = (struct statx_timestamp){.tv_sec=0, .tv_nsec=0}; + stx->stx_mask = STATX_BASIC_STATS; + + return 0; +} diff --git a/src/linux/timerfd.c b/src/linux/timerfd.c index 62cc2773..5bdfaf16 100644 --- a/src/linux/timerfd.c +++ b/src/linux/timerfd.c @@ -1,6 +1,9 @@ #include <sys/timerfd.h> +#include <errno.h> #include "syscall.h" +#define IS32BIT(x) !((x)+0x80000000ULL>>32) + int timerfd_create(int clockid, int flags) { return syscall(SYS_timerfd_create, clockid, flags); @@ -8,10 +11,49 @@ int timerfd_create(int clockid, int flags) int timerfd_settime(int fd, int flags, const struct itimerspec *new, struct itimerspec *old) { +#ifdef SYS_timerfd_settime64 + time_t is = new->it_interval.tv_sec, vs = new->it_value.tv_sec; + long ins = new->it_interval.tv_nsec, vns = new->it_value.tv_nsec; + int r = -ENOSYS; + if (SYS_timerfd_settime == SYS_timerfd_settime64 + || !IS32BIT(is) || !IS32BIT(vs) || (sizeof(time_t)>4 && old)) + r = __syscall(SYS_timerfd_settime64, fd, flags, + ((long long[]){is, ins, vs, vns}), old); + if (SYS_timerfd_settime == SYS_timerfd_settime64 || r!=-ENOSYS) + return __syscall_ret(r); + if (!IS32BIT(is) || !IS32BIT(vs)) + return __syscall_ret(-ENOTSUP); + long old32[4]; + r = __syscall(SYS_timerfd_settime, fd, flags, + ((long[]){is, ins, vs, vns}), old32); + if (!r && old) { + old->it_interval.tv_sec = old32[0]; + old->it_interval.tv_nsec = old32[1]; + old->it_value.tv_sec = old32[2]; + old->it_value.tv_nsec = old32[3]; + } + return __syscall_ret(r); +#endif return syscall(SYS_timerfd_settime, fd, flags, new, old); } int timerfd_gettime(int fd, struct itimerspec *cur) { +#ifdef SYS_timerfd_gettime64 + int r = -ENOSYS; + if (sizeof(time_t) > 4) + r = __syscall(SYS_timerfd_gettime64, fd, cur); + if (SYS_timerfd_gettime == SYS_timerfd_gettime64 || r!=-ENOSYS) + return __syscall_ret(r); + long cur32[4]; + r = __syscall(SYS_timerfd_gettime, fd, cur32); + if (!r) { + cur->it_interval.tv_sec = cur32[0]; + cur->it_interval.tv_nsec = cur32[1]; + cur->it_value.tv_sec = cur32[2]; + cur->it_value.tv_nsec = cur32[3]; + } + return __syscall_ret(r); +#endif return syscall(SYS_timerfd_gettime, fd, cur); } diff --git a/src/linux/wait4.c b/src/linux/wait4.c index 97f12cc5..fb08c0d0 100644 --- a/src/linux/wait4.c +++ b/src/linux/wait4.c @@ -1,9 +1,39 @@ #define _GNU_SOURCE #include <sys/wait.h> #include <sys/resource.h> +#include <string.h> +#include <errno.h> #include "syscall.h" -pid_t wait4(pid_t pid, int *status, int options, struct rusage *usage) +pid_t wait4(pid_t pid, int *status, int options, struct rusage *ru) { - return syscall(SYS_wait4, pid, status, options, usage); + int r; +#ifdef SYS_wait4_time64 + if (ru) { + long long kru64[18]; + r = __syscall(SYS_wait4_time64, pid, status, options, kru64); + if (r > 0) { + ru->ru_utime = (struct timeval) + { .tv_sec = kru64[0], .tv_usec = kru64[1] }; + ru->ru_stime = (struct timeval) + { .tv_sec = kru64[2], .tv_usec = kru64[3] }; + char *slots = (char *)&ru->ru_maxrss; + for (int i=0; i<14; i++) + *(long *)(slots + i*sizeof(long)) = kru64[4+i]; + } + if (SYS_wait4_time64 == SYS_wait4 || r != -ENOSYS) + return __syscall_ret(r); + } +#endif + char *dest = ru ? (char *)&ru->ru_maxrss - 4*sizeof(long) : 0; + r = __sys_wait4(pid, status, options, dest); + if (r>0 && ru && sizeof(time_t) > sizeof(long)) { + long kru[4]; + memcpy(kru, dest, 4*sizeof(long)); + ru->ru_utime = (struct timeval) + { .tv_sec = kru[0], .tv_usec = kru[1] }; + ru->ru_stime = (struct timeval) + { .tv_sec = kru[2], .tv_usec = kru[3] }; + } + return __syscall_ret(r); } |