| /* SPDX-License-Identifier: LGPL-2.1 OR MIT */ | |
| /* | |
| * Syscall definitions for NOLIBC (those in man(2)) | |
| * Copyright (C) 2017-2021 Willy Tarreau <w@1wt.eu> | |
| */ | |
| #ifndef _NOLIBC_SYS_H | |
| #define _NOLIBC_SYS_H | |
| #include "std.h" | |
| /* system includes */ | |
| #include <asm/unistd.h> | |
| #include <asm/signal.h> /* for SIGCHLD */ | |
| #include <asm/ioctls.h> | |
| #include <asm/mman.h> | |
| #include <linux/fs.h> | |
| #include <linux/loop.h> | |
| #include <linux/time.h> | |
| #include <linux/auxvec.h> | |
| #include <linux/fcntl.h> /* for O_* and AT_* */ | |
| #include <linux/stat.h> /* for statx() */ | |
| #include <linux/prctl.h> | |
| #include <linux/resource.h> | |
| #include <linux/utsname.h> | |
| #include "arch.h" | |
| #include "errno.h" | |
| #include "stdarg.h" | |
| #include "types.h" | |
| /* Syscall return helper: takes the syscall value in argument and checks for an | |
| * error in it. This may only be used with signed returns (int or long), but | |
| * not with pointers. An error is any value < 0. When an error is encountered, | |
| * -ret is set into errno and -1 is returned. Otherwise the returned value is | |
| * passed as-is with its type preserved. | |
| */ | |
| #define __sysret(arg) \ | |
| ({ \ | |
| __typeof__(arg) __sysret_arg = (arg); \ | |
| (__sysret_arg < 0) /* error ? */ \ | |
| ? (({ SET_ERRNO(-__sysret_arg); }), -1) /* ret -1 with errno = -arg */ \ | |
| : __sysret_arg; /* return original value */ \ | |
| }) | |
| /* Syscall ENOSYS helper: Avoids unused-parameter warnings and provides a | |
| * debugging hook. | |
| */ | |
| static __inline__ int __nolibc_enosys(const char *syscall, ...) | |
| { | |
| (void)syscall; | |
| return -ENOSYS; | |
| } | |
| /* Functions in this file only describe syscalls. They're declared static so | |
| * that the compiler usually decides to inline them while still being allowed | |
| * to pass a pointer to one of their instances. Each syscall exists in two | |
| * versions: | |
| * - the "internal" ones, which matches the raw syscall interface at the | |
| * kernel level, which may sometimes slightly differ from the documented | |
| * libc-level ones. For example most of them return either a valid value | |
| * or -errno. All of these are prefixed with "sys_". They may be called | |
| * by non-portable applications if desired. | |
| * | |
| * - the "exported" ones, whose interface must closely match the one | |
| * documented in man(2), that applications are supposed to expect. These | |
| * ones rely on the internal ones, and set errno. | |
| * | |
| * Each syscall will be defined with the two functions, sorted in alphabetical | |
| * order applied to the exported names. | |
| * | |
| * In case of doubt about the relevance of a function here, only those which | |
| * set errno should be defined here. Wrappers like those appearing in man(3) | |
| * should not be placed here. | |
| */ | |
| /* | |
| * int brk(void *addr); | |
| * void *sbrk(intptr_t inc) | |
| */ | |
| static __attribute__((unused)) | |
| void *sys_brk(void *addr) | |
| { | |
| return (void *)my_syscall1(__NR_brk, addr); | |
| } | |
| static __attribute__((unused)) | |
| int brk(void *addr) | |
| { | |
| void *ret = sys_brk(addr); | |
| if (!ret) { | |
| SET_ERRNO(ENOMEM); | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| static __attribute__((unused)) | |
| void *sbrk(intptr_t inc) | |
| { | |
| /* first call to find current end */ | |
| void *ret = sys_brk(0); | |
| if (ret && sys_brk(ret + inc) == ret + inc) | |
| return ret + inc; | |
| SET_ERRNO(ENOMEM); | |
| return (void *)-1; | |
| } | |
| /* | |
| * int chdir(const char *path); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_chdir(const char *path) | |
| { | |
| return my_syscall1(__NR_chdir, path); | |
| } | |
| static __attribute__((unused)) | |
| int chdir(const char *path) | |
| { | |
| return __sysret(sys_chdir(path)); | |
| } | |
| /* | |
| * int chmod(const char *path, mode_t mode); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_chmod(const char *path, mode_t mode) | |
| { | |
| #ifdef __NR_fchmodat | |
| return my_syscall4(__NR_fchmodat, AT_FDCWD, path, mode, 0); | |
| #elif defined(__NR_chmod) | |
| return my_syscall2(__NR_chmod, path, mode); | |
| #else | |
| return __nolibc_enosys(__func__, path, mode); | |
| #endif | |
| } | |
| static __attribute__((unused)) | |
| int chmod(const char *path, mode_t mode) | |
| { | |
| return __sysret(sys_chmod(path, mode)); | |
| } | |
| /* | |
| * int chown(const char *path, uid_t owner, gid_t group); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_chown(const char *path, uid_t owner, gid_t group) | |
| { | |
| #ifdef __NR_fchownat | |
| return my_syscall5(__NR_fchownat, AT_FDCWD, path, owner, group, 0); | |
| #elif defined(__NR_chown) | |
| return my_syscall3(__NR_chown, path, owner, group); | |
| #else | |
| return __nolibc_enosys(__func__, path, owner, group); | |
| #endif | |
| } | |
| static __attribute__((unused)) | |
| int chown(const char *path, uid_t owner, gid_t group) | |
| { | |
| return __sysret(sys_chown(path, owner, group)); | |
| } | |
| /* | |
| * int chroot(const char *path); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_chroot(const char *path) | |
| { | |
| return my_syscall1(__NR_chroot, path); | |
| } | |
| static __attribute__((unused)) | |
| int chroot(const char *path) | |
| { | |
| return __sysret(sys_chroot(path)); | |
| } | |
| /* | |
| * int close(int fd); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_close(int fd) | |
| { | |
| return my_syscall1(__NR_close, fd); | |
| } | |
| static __attribute__((unused)) | |
| int close(int fd) | |
| { | |
| return __sysret(sys_close(fd)); | |
| } | |
| /* | |
| * int dup(int fd); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_dup(int fd) | |
| { | |
| return my_syscall1(__NR_dup, fd); | |
| } | |
| static __attribute__((unused)) | |
| int dup(int fd) | |
| { | |
| return __sysret(sys_dup(fd)); | |
| } | |
| /* | |
| * int dup2(int old, int new); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_dup2(int old, int new) | |
| { | |
| #ifdef __NR_dup3 | |
| return my_syscall3(__NR_dup3, old, new, 0); | |
| #elif defined(__NR_dup2) | |
| return my_syscall2(__NR_dup2, old, new); | |
| #else | |
| return __nolibc_enosys(__func__, old, new); | |
| #endif | |
| } | |
| static __attribute__((unused)) | |
| int dup2(int old, int new) | |
| { | |
| return __sysret(sys_dup2(old, new)); | |
| } | |
| /* | |
| * int dup3(int old, int new, int flags); | |
| */ | |
| #ifdef __NR_dup3 | |
| static __attribute__((unused)) | |
| int sys_dup3(int old, int new, int flags) | |
| { | |
| return my_syscall3(__NR_dup3, old, new, flags); | |
| } | |
| static __attribute__((unused)) | |
| int dup3(int old, int new, int flags) | |
| { | |
| return __sysret(sys_dup3(old, new, flags)); | |
| } | |
| #endif | |
| /* | |
| * int execve(const char *filename, char *const argv[], char *const envp[]); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_execve(const char *filename, char *const argv[], char *const envp[]) | |
| { | |
| return my_syscall3(__NR_execve, filename, argv, envp); | |
| } | |
| static __attribute__((unused)) | |
| int execve(const char *filename, char *const argv[], char *const envp[]) | |
| { | |
| return __sysret(sys_execve(filename, argv, envp)); | |
| } | |
| /* | |
| * void exit(int status); | |
| */ | |
| static __attribute__((noreturn,unused)) | |
| void sys_exit(int status) | |
| { | |
| my_syscall1(__NR_exit, status & 255); | |
| while(1); /* shut the "noreturn" warnings. */ | |
| } | |
| static __attribute__((noreturn,unused)) | |
| void exit(int status) | |
| { | |
| sys_exit(status); | |
| } | |
| /* | |
| * pid_t fork(void); | |
| */ | |
| #ifndef sys_fork | |
| static __attribute__((unused)) | |
| pid_t sys_fork(void) | |
| { | |
| #ifdef __NR_clone | |
| /* note: some archs only have clone() and not fork(). Different archs | |
| * have a different API, but most archs have the flags on first arg and | |
| * will not use the rest with no other flag. | |
| */ | |
| return my_syscall5(__NR_clone, SIGCHLD, 0, 0, 0, 0); | |
| #elif defined(__NR_fork) | |
| return my_syscall0(__NR_fork); | |
| #else | |
| return __nolibc_enosys(__func__); | |
| #endif | |
| } | |
| #endif | |
| static __attribute__((unused)) | |
| pid_t fork(void) | |
| { | |
| return __sysret(sys_fork()); | |
| } | |
| /* | |
| * int fsync(int fd); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_fsync(int fd) | |
| { | |
| return my_syscall1(__NR_fsync, fd); | |
| } | |
| static __attribute__((unused)) | |
| int fsync(int fd) | |
| { | |
| return __sysret(sys_fsync(fd)); | |
| } | |
| /* | |
| * int getdents64(int fd, struct linux_dirent64 *dirp, int count); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_getdents64(int fd, struct linux_dirent64 *dirp, int count) | |
| { | |
| return my_syscall3(__NR_getdents64, fd, dirp, count); | |
| } | |
| static __attribute__((unused)) | |
| int getdents64(int fd, struct linux_dirent64 *dirp, int count) | |
| { | |
| return __sysret(sys_getdents64(fd, dirp, count)); | |
| } | |
| /* | |
| * uid_t geteuid(void); | |
| */ | |
| static __attribute__((unused)) | |
| uid_t sys_geteuid(void) | |
| { | |
| #ifdef __NR_geteuid32 | |
| return my_syscall0(__NR_geteuid32); | |
| #else | |
| return my_syscall0(__NR_geteuid); | |
| #endif | |
| } | |
| static __attribute__((unused)) | |
| uid_t geteuid(void) | |
| { | |
| return sys_geteuid(); | |
| } | |
| /* | |
| * pid_t getpgid(pid_t pid); | |
| */ | |
| static __attribute__((unused)) | |
| pid_t sys_getpgid(pid_t pid) | |
| { | |
| return my_syscall1(__NR_getpgid, pid); | |
| } | |
| static __attribute__((unused)) | |
| pid_t getpgid(pid_t pid) | |
| { | |
| return __sysret(sys_getpgid(pid)); | |
| } | |
| /* | |
| * pid_t getpgrp(void); | |
| */ | |
| static __attribute__((unused)) | |
| pid_t sys_getpgrp(void) | |
| { | |
| return sys_getpgid(0); | |
| } | |
| static __attribute__((unused)) | |
| pid_t getpgrp(void) | |
| { | |
| return sys_getpgrp(); | |
| } | |
| /* | |
| * pid_t getpid(void); | |
| */ | |
| static __attribute__((unused)) | |
| pid_t sys_getpid(void) | |
| { | |
| return my_syscall0(__NR_getpid); | |
| } | |
| static __attribute__((unused)) | |
| pid_t getpid(void) | |
| { | |
| return sys_getpid(); | |
| } | |
| /* | |
| * pid_t getppid(void); | |
| */ | |
| static __attribute__((unused)) | |
| pid_t sys_getppid(void) | |
| { | |
| return my_syscall0(__NR_getppid); | |
| } | |
| static __attribute__((unused)) | |
| pid_t getppid(void) | |
| { | |
| return sys_getppid(); | |
| } | |
| /* | |
| * pid_t gettid(void); | |
| */ | |
| static __attribute__((unused)) | |
| pid_t sys_gettid(void) | |
| { | |
| return my_syscall0(__NR_gettid); | |
| } | |
| static __attribute__((unused)) | |
| pid_t gettid(void) | |
| { | |
| return sys_gettid(); | |
| } | |
| static unsigned long getauxval(unsigned long key); | |
| /* | |
| * int getpagesize(void); | |
| */ | |
| static __attribute__((unused)) | |
| int getpagesize(void) | |
| { | |
| return __sysret((int)getauxval(AT_PAGESZ) ?: -ENOENT); | |
| } | |
| /* | |
| * int gettimeofday(struct timeval *tv, struct timezone *tz); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_gettimeofday(struct timeval *tv, struct timezone *tz) | |
| { | |
| #ifdef __NR_gettimeofday | |
| return my_syscall2(__NR_gettimeofday, tv, tz); | |
| #else | |
| return __nolibc_enosys(__func__, tv, tz); | |
| #endif | |
| } | |
| static __attribute__((unused)) | |
| int gettimeofday(struct timeval *tv, struct timezone *tz) | |
| { | |
| return __sysret(sys_gettimeofday(tv, tz)); | |
| } | |
| /* | |
| * uid_t getuid(void); | |
| */ | |
| static __attribute__((unused)) | |
| uid_t sys_getuid(void) | |
| { | |
| #ifdef __NR_getuid32 | |
| return my_syscall0(__NR_getuid32); | |
| #else | |
| return my_syscall0(__NR_getuid); | |
| #endif | |
| } | |
| static __attribute__((unused)) | |
| uid_t getuid(void) | |
| { | |
| return sys_getuid(); | |
| } | |
| /* | |
| * int ioctl(int fd, unsigned long req, void *value); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_ioctl(int fd, unsigned long req, void *value) | |
| { | |
| return my_syscall3(__NR_ioctl, fd, req, value); | |
| } | |
| static __attribute__((unused)) | |
| int ioctl(int fd, unsigned long req, void *value) | |
| { | |
| return __sysret(sys_ioctl(fd, req, value)); | |
| } | |
| /* | |
| * int kill(pid_t pid, int signal); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_kill(pid_t pid, int signal) | |
| { | |
| return my_syscall2(__NR_kill, pid, signal); | |
| } | |
| static __attribute__((unused)) | |
| int kill(pid_t pid, int signal) | |
| { | |
| return __sysret(sys_kill(pid, signal)); | |
| } | |
| /* | |
| * int link(const char *old, const char *new); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_link(const char *old, const char *new) | |
| { | |
| #ifdef __NR_linkat | |
| return my_syscall5(__NR_linkat, AT_FDCWD, old, AT_FDCWD, new, 0); | |
| #elif defined(__NR_link) | |
| return my_syscall2(__NR_link, old, new); | |
| #else | |
| return __nolibc_enosys(__func__, old, new); | |
| #endif | |
| } | |
| static __attribute__((unused)) | |
| int link(const char *old, const char *new) | |
| { | |
| return __sysret(sys_link(old, new)); | |
| } | |
| /* | |
| * off_t lseek(int fd, off_t offset, int whence); | |
| */ | |
| static __attribute__((unused)) | |
| off_t sys_lseek(int fd, off_t offset, int whence) | |
| { | |
| #ifdef __NR_lseek | |
| return my_syscall3(__NR_lseek, fd, offset, whence); | |
| #else | |
| return __nolibc_enosys(__func__, fd, offset, whence); | |
| #endif | |
| } | |
| static __attribute__((unused)) | |
| off_t lseek(int fd, off_t offset, int whence) | |
| { | |
| return __sysret(sys_lseek(fd, offset, whence)); | |
| } | |
| /* | |
| * int mkdir(const char *path, mode_t mode); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_mkdir(const char *path, mode_t mode) | |
| { | |
| #ifdef __NR_mkdirat | |
| return my_syscall3(__NR_mkdirat, AT_FDCWD, path, mode); | |
| #elif defined(__NR_mkdir) | |
| return my_syscall2(__NR_mkdir, path, mode); | |
| #else | |
| return __nolibc_enosys(__func__, path, mode); | |
| #endif | |
| } | |
| static __attribute__((unused)) | |
| int mkdir(const char *path, mode_t mode) | |
| { | |
| return __sysret(sys_mkdir(path, mode)); | |
| } | |
| /* | |
| * int rmdir(const char *path); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_rmdir(const char *path) | |
| { | |
| #ifdef __NR_rmdir | |
| return my_syscall1(__NR_rmdir, path); | |
| #elif defined(__NR_unlinkat) | |
| return my_syscall3(__NR_unlinkat, AT_FDCWD, path, AT_REMOVEDIR); | |
| #else | |
| return __nolibc_enosys(__func__, path); | |
| #endif | |
| } | |
| static __attribute__((unused)) | |
| int rmdir(const char *path) | |
| { | |
| return __sysret(sys_rmdir(path)); | |
| } | |
| /* | |
| * int mknod(const char *path, mode_t mode, dev_t dev); | |
| */ | |
| static __attribute__((unused)) | |
| long sys_mknod(const char *path, mode_t mode, dev_t dev) | |
| { | |
| #ifdef __NR_mknodat | |
| return my_syscall4(__NR_mknodat, AT_FDCWD, path, mode, dev); | |
| #elif defined(__NR_mknod) | |
| return my_syscall3(__NR_mknod, path, mode, dev); | |
| #else | |
| return __nolibc_enosys(__func__, path, mode, dev); | |
| #endif | |
| } | |
| static __attribute__((unused)) | |
| int mknod(const char *path, mode_t mode, dev_t dev) | |
| { | |
| return __sysret(sys_mknod(path, mode, dev)); | |
| } | |
| #ifndef sys_mmap | |
| static __attribute__((unused)) | |
| void *sys_mmap(void *addr, size_t length, int prot, int flags, int fd, | |
| off_t offset) | |
| { | |
| int n; | |
| #if defined(__NR_mmap2) | |
| n = __NR_mmap2; | |
| offset >>= 12; | |
| #else | |
| n = __NR_mmap; | |
| #endif | |
| return (void *)my_syscall6(n, addr, length, prot, flags, fd, offset); | |
| } | |
| #endif | |
| /* Note that on Linux, MAP_FAILED is -1 so we can use the generic __sysret() | |
| * which returns -1 upon error and still satisfy user land that checks for | |
| * MAP_FAILED. | |
| */ | |
| static __attribute__((unused)) | |
| void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) | |
| { | |
| void *ret = sys_mmap(addr, length, prot, flags, fd, offset); | |
| if ((unsigned long)ret >= -4095UL) { | |
| SET_ERRNO(-(long)ret); | |
| ret = MAP_FAILED; | |
| } | |
| return ret; | |
| } | |
| static __attribute__((unused)) | |
| int sys_munmap(void *addr, size_t length) | |
| { | |
| return my_syscall2(__NR_munmap, addr, length); | |
| } | |
| static __attribute__((unused)) | |
| int munmap(void *addr, size_t length) | |
| { | |
| return __sysret(sys_munmap(addr, length)); | |
| } | |
| /* | |
| * int mount(const char *source, const char *target, | |
| * const char *fstype, unsigned long flags, | |
| * const void *data); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_mount(const char *src, const char *tgt, const char *fst, | |
| unsigned long flags, const void *data) | |
| { | |
| return my_syscall5(__NR_mount, src, tgt, fst, flags, data); | |
| } | |
| static __attribute__((unused)) | |
| int mount(const char *src, const char *tgt, | |
| const char *fst, unsigned long flags, | |
| const void *data) | |
| { | |
| return __sysret(sys_mount(src, tgt, fst, flags, data)); | |
| } | |
| /* | |
| * int open(const char *path, int flags[, mode_t mode]); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_open(const char *path, int flags, mode_t mode) | |
| { | |
| #ifdef __NR_openat | |
| return my_syscall4(__NR_openat, AT_FDCWD, path, flags, mode); | |
| #elif defined(__NR_open) | |
| return my_syscall3(__NR_open, path, flags, mode); | |
| #else | |
| return __nolibc_enosys(__func__, path, flags, mode); | |
| #endif | |
| } | |
| static __attribute__((unused)) | |
| int open(const char *path, int flags, ...) | |
| { | |
| mode_t mode = 0; | |
| if (flags & O_CREAT) { | |
| va_list args; | |
| va_start(args, flags); | |
| mode = va_arg(args, int); | |
| va_end(args); | |
| } | |
| return __sysret(sys_open(path, flags, mode)); | |
| } | |
| /* | |
| * int pipe2(int pipefd[2], int flags); | |
| * int pipe(int pipefd[2]); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_pipe2(int pipefd[2], int flags) | |
| { | |
| return my_syscall2(__NR_pipe2, pipefd, flags); | |
| } | |
| static __attribute__((unused)) | |
| int pipe2(int pipefd[2], int flags) | |
| { | |
| return __sysret(sys_pipe2(pipefd, flags)); | |
| } | |
| static __attribute__((unused)) | |
| int pipe(int pipefd[2]) | |
| { | |
| return pipe2(pipefd, 0); | |
| } | |
| /* | |
| * int prctl(int option, unsigned long arg2, unsigned long arg3, | |
| * unsigned long arg4, unsigned long arg5); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_prctl(int option, unsigned long arg2, unsigned long arg3, | |
| unsigned long arg4, unsigned long arg5) | |
| { | |
| return my_syscall5(__NR_prctl, option, arg2, arg3, arg4, arg5); | |
| } | |
| static __attribute__((unused)) | |
| int prctl(int option, unsigned long arg2, unsigned long arg3, | |
| unsigned long arg4, unsigned long arg5) | |
| { | |
| return __sysret(sys_prctl(option, arg2, arg3, arg4, arg5)); | |
| } | |
| /* | |
| * int pivot_root(const char *new, const char *old); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_pivot_root(const char *new, const char *old) | |
| { | |
| return my_syscall2(__NR_pivot_root, new, old); | |
| } | |
| static __attribute__((unused)) | |
| int pivot_root(const char *new, const char *old) | |
| { | |
| return __sysret(sys_pivot_root(new, old)); | |
| } | |
| /* | |
| * int poll(struct pollfd *fds, int nfds, int timeout); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_poll(struct pollfd *fds, int nfds, int timeout) | |
| { | |
| #if defined(__NR_ppoll) | |
| struct timespec t; | |
| if (timeout >= 0) { | |
| t.tv_sec = timeout / 1000; | |
| t.tv_nsec = (timeout % 1000) * 1000000; | |
| } | |
| return my_syscall5(__NR_ppoll, fds, nfds, (timeout >= 0) ? &t : NULL, NULL, 0); | |
| #elif defined(__NR_poll) | |
| return my_syscall3(__NR_poll, fds, nfds, timeout); | |
| #else | |
| return __nolibc_enosys(__func__, fds, nfds, timeout); | |
| #endif | |
| } | |
| static __attribute__((unused)) | |
| int poll(struct pollfd *fds, int nfds, int timeout) | |
| { | |
| return __sysret(sys_poll(fds, nfds, timeout)); | |
| } | |
| /* | |
| * ssize_t read(int fd, void *buf, size_t count); | |
| */ | |
| static __attribute__((unused)) | |
| ssize_t sys_read(int fd, void *buf, size_t count) | |
| { | |
| return my_syscall3(__NR_read, fd, buf, count); | |
| } | |
| static __attribute__((unused)) | |
| ssize_t read(int fd, void *buf, size_t count) | |
| { | |
| return __sysret(sys_read(fd, buf, count)); | |
| } | |
| /* | |
| * int reboot(int cmd); | |
| * <cmd> is among LINUX_REBOOT_CMD_* | |
| */ | |
| static __attribute__((unused)) | |
| ssize_t sys_reboot(int magic1, int magic2, int cmd, void *arg) | |
| { | |
| return my_syscall4(__NR_reboot, magic1, magic2, cmd, arg); | |
| } | |
| static __attribute__((unused)) | |
| int reboot(int cmd) | |
| { | |
| return __sysret(sys_reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, cmd, 0)); | |
| } | |
| /* | |
| * int getrlimit(int resource, struct rlimit *rlim); | |
| * int setrlimit(int resource, const struct rlimit *rlim); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_prlimit64(pid_t pid, int resource, | |
| const struct rlimit64 *new_limit, struct rlimit64 *old_limit) | |
| { | |
| return my_syscall4(__NR_prlimit64, pid, resource, new_limit, old_limit); | |
| } | |
| static __attribute__((unused)) | |
| int getrlimit(int resource, struct rlimit *rlim) | |
| { | |
| struct rlimit64 rlim64; | |
| int ret; | |
| ret = __sysret(sys_prlimit64(0, resource, NULL, &rlim64)); | |
| rlim->rlim_cur = rlim64.rlim_cur; | |
| rlim->rlim_max = rlim64.rlim_max; | |
| return ret; | |
| } | |
| static __attribute__((unused)) | |
| int setrlimit(int resource, const struct rlimit *rlim) | |
| { | |
| struct rlimit64 rlim64 = { | |
| .rlim_cur = rlim->rlim_cur, | |
| .rlim_max = rlim->rlim_max, | |
| }; | |
| return __sysret(sys_prlimit64(0, resource, &rlim64, NULL)); | |
| } | |
| /* | |
| * int sched_yield(void); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_sched_yield(void) | |
| { | |
| return my_syscall0(__NR_sched_yield); | |
| } | |
| static __attribute__((unused)) | |
| int sched_yield(void) | |
| { | |
| return __sysret(sys_sched_yield()); | |
| } | |
| /* | |
| * int select(int nfds, fd_set *read_fds, fd_set *write_fds, | |
| * fd_set *except_fds, struct timeval *timeout); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *timeout) | |
| { | |
| #if defined(__ARCH_WANT_SYS_OLD_SELECT) && !defined(__NR__newselect) | |
| struct sel_arg_struct { | |
| unsigned long n; | |
| fd_set *r, *w, *e; | |
| struct timeval *t; | |
| } arg = { .n = nfds, .r = rfds, .w = wfds, .e = efds, .t = timeout }; | |
| return my_syscall1(__NR_select, &arg); | |
| #elif defined(__NR__newselect) | |
| return my_syscall5(__NR__newselect, nfds, rfds, wfds, efds, timeout); | |
| #elif defined(__NR_select) | |
| return my_syscall5(__NR_select, nfds, rfds, wfds, efds, timeout); | |
| #elif defined(__NR_pselect6) | |
| struct timespec t; | |
| if (timeout) { | |
| t.tv_sec = timeout->tv_sec; | |
| t.tv_nsec = timeout->tv_usec * 1000; | |
| } | |
| return my_syscall6(__NR_pselect6, nfds, rfds, wfds, efds, timeout ? &t : NULL, NULL); | |
| #else | |
| return __nolibc_enosys(__func__, nfds, rfds, wfds, efds, timeout); | |
| #endif | |
| } | |
| static __attribute__((unused)) | |
| int select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *timeout) | |
| { | |
| return __sysret(sys_select(nfds, rfds, wfds, efds, timeout)); | |
| } | |
| /* | |
| * int setpgid(pid_t pid, pid_t pgid); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_setpgid(pid_t pid, pid_t pgid) | |
| { | |
| return my_syscall2(__NR_setpgid, pid, pgid); | |
| } | |
| static __attribute__((unused)) | |
| int setpgid(pid_t pid, pid_t pgid) | |
| { | |
| return __sysret(sys_setpgid(pid, pgid)); | |
| } | |
| /* | |
| * pid_t setsid(void); | |
| */ | |
| static __attribute__((unused)) | |
| pid_t sys_setsid(void) | |
| { | |
| return my_syscall0(__NR_setsid); | |
| } | |
| static __attribute__((unused)) | |
| pid_t setsid(void) | |
| { | |
| return __sysret(sys_setsid()); | |
| } | |
| /* | |
| * int statx(int fd, const char *path, int flags, unsigned int mask, struct statx *buf); | |
| * int stat(const char *path, struct stat *buf); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_statx(int fd, const char *path, int flags, unsigned int mask, struct statx *buf) | |
| { | |
| #ifdef __NR_statx | |
| return my_syscall5(__NR_statx, fd, path, flags, mask, buf); | |
| #else | |
| return __nolibc_enosys(__func__, fd, path, flags, mask, buf); | |
| #endif | |
| } | |
| static __attribute__((unused)) | |
| int statx(int fd, const char *path, int flags, unsigned int mask, struct statx *buf) | |
| { | |
| return __sysret(sys_statx(fd, path, flags, mask, buf)); | |
| } | |
| static __attribute__((unused)) | |
| int stat(const char *path, struct stat *buf) | |
| { | |
| struct statx statx; | |
| long ret; | |
| ret = __sysret(sys_statx(AT_FDCWD, path, AT_NO_AUTOMOUNT, STATX_BASIC_STATS, &statx)); | |
| if (ret == -1) | |
| return ret; | |
| buf->st_dev = ((statx.stx_dev_minor & 0xff) | |
| | (statx.stx_dev_major << 8) | |
| | ((statx.stx_dev_minor & ~0xff) << 12)); | |
| buf->st_ino = statx.stx_ino; | |
| buf->st_mode = statx.stx_mode; | |
| buf->st_nlink = statx.stx_nlink; | |
| buf->st_uid = statx.stx_uid; | |
| buf->st_gid = statx.stx_gid; | |
| buf->st_rdev = ((statx.stx_rdev_minor & 0xff) | |
| | (statx.stx_rdev_major << 8) | |
| | ((statx.stx_rdev_minor & ~0xff) << 12)); | |
| buf->st_size = statx.stx_size; | |
| buf->st_blksize = statx.stx_blksize; | |
| buf->st_blocks = statx.stx_blocks; | |
| buf->st_atim.tv_sec = statx.stx_atime.tv_sec; | |
| buf->st_atim.tv_nsec = statx.stx_atime.tv_nsec; | |
| buf->st_mtim.tv_sec = statx.stx_mtime.tv_sec; | |
| buf->st_mtim.tv_nsec = statx.stx_mtime.tv_nsec; | |
| buf->st_ctim.tv_sec = statx.stx_ctime.tv_sec; | |
| buf->st_ctim.tv_nsec = statx.stx_ctime.tv_nsec; | |
| return 0; | |
| } | |
| /* | |
| * int symlink(const char *old, const char *new); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_symlink(const char *old, const char *new) | |
| { | |
| #ifdef __NR_symlinkat | |
| return my_syscall3(__NR_symlinkat, old, AT_FDCWD, new); | |
| #elif defined(__NR_symlink) | |
| return my_syscall2(__NR_symlink, old, new); | |
| #else | |
| return __nolibc_enosys(__func__, old, new); | |
| #endif | |
| } | |
| static __attribute__((unused)) | |
| int symlink(const char *old, const char *new) | |
| { | |
| return __sysret(sys_symlink(old, new)); | |
| } | |
| /* | |
| * mode_t umask(mode_t mode); | |
| */ | |
| static __attribute__((unused)) | |
| mode_t sys_umask(mode_t mode) | |
| { | |
| return my_syscall1(__NR_umask, mode); | |
| } | |
| static __attribute__((unused)) | |
| mode_t umask(mode_t mode) | |
| { | |
| return sys_umask(mode); | |
| } | |
| /* | |
| * int umount2(const char *path, int flags); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_umount2(const char *path, int flags) | |
| { | |
| return my_syscall2(__NR_umount2, path, flags); | |
| } | |
| static __attribute__((unused)) | |
| int umount2(const char *path, int flags) | |
| { | |
| return __sysret(sys_umount2(path, flags)); | |
| } | |
| /* | |
| * int uname(struct utsname *buf); | |
| */ | |
| struct utsname { | |
| char sysname[65]; | |
| char nodename[65]; | |
| char release[65]; | |
| char version[65]; | |
| char machine[65]; | |
| char domainname[65]; | |
| }; | |
| static __attribute__((unused)) | |
| int sys_uname(struct utsname *buf) | |
| { | |
| return my_syscall1(__NR_uname, buf); | |
| } | |
| static __attribute__((unused)) | |
| int uname(struct utsname *buf) | |
| { | |
| return __sysret(sys_uname(buf)); | |
| } | |
| /* | |
| * int unlink(const char *path); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_unlink(const char *path) | |
| { | |
| #ifdef __NR_unlinkat | |
| return my_syscall3(__NR_unlinkat, AT_FDCWD, path, 0); | |
| #elif defined(__NR_unlink) | |
| return my_syscall1(__NR_unlink, path); | |
| #else | |
| return __nolibc_enosys(__func__, path); | |
| #endif | |
| } | |
| static __attribute__((unused)) | |
| int unlink(const char *path) | |
| { | |
| return __sysret(sys_unlink(path)); | |
| } | |
| /* | |
| * pid_t wait(int *status); | |
| * pid_t wait4(pid_t pid, int *status, int options, struct rusage *rusage); | |
| * pid_t waitpid(pid_t pid, int *status, int options); | |
| */ | |
| static __attribute__((unused)) | |
| pid_t sys_wait4(pid_t pid, int *status, int options, struct rusage *rusage) | |
| { | |
| #ifdef __NR_wait4 | |
| return my_syscall4(__NR_wait4, pid, status, options, rusage); | |
| #else | |
| return __nolibc_enosys(__func__, pid, status, options, rusage); | |
| #endif | |
| } | |
| static __attribute__((unused)) | |
| pid_t wait(int *status) | |
| { | |
| return __sysret(sys_wait4(-1, status, 0, NULL)); | |
| } | |
| static __attribute__((unused)) | |
| pid_t wait4(pid_t pid, int *status, int options, struct rusage *rusage) | |
| { | |
| return __sysret(sys_wait4(pid, status, options, rusage)); | |
| } | |
| static __attribute__((unused)) | |
| pid_t waitpid(pid_t pid, int *status, int options) | |
| { | |
| return __sysret(sys_wait4(pid, status, options, NULL)); | |
| } | |
| /* | |
| * ssize_t write(int fd, const void *buf, size_t count); | |
| */ | |
| static __attribute__((unused)) | |
| ssize_t sys_write(int fd, const void *buf, size_t count) | |
| { | |
| return my_syscall3(__NR_write, fd, buf, count); | |
| } | |
| static __attribute__((unused)) | |
| ssize_t write(int fd, const void *buf, size_t count) | |
| { | |
| return __sysret(sys_write(fd, buf, count)); | |
| } | |
| /* | |
| * int memfd_create(const char *name, unsigned int flags); | |
| */ | |
| static __attribute__((unused)) | |
| int sys_memfd_create(const char *name, unsigned int flags) | |
| { | |
| return my_syscall2(__NR_memfd_create, name, flags); | |
| } | |
| static __attribute__((unused)) | |
| int memfd_create(const char *name, unsigned int flags) | |
| { | |
| return __sysret(sys_memfd_create(name, flags)); | |
| } | |
| /* make sure to include all global symbols */ | |
| #include "nolibc.h" | |
| #endif /* _NOLIBC_SYS_H */ |