Getting the numbers right for all architectures has proven to be a constant chore. Let's autogenerate the header from the tables that were imported in one of the previous commits. Fixes #18074. (Hopefully. I cannot verify this on all architectures.) To update the lists, or to update the header after template changes: ninja -C build update-syscall-tables update-syscall-header Note: the generated file is saved in git. Initially I wanted to only store the tables in git, and generate the header during each build. Generation is quick enough, but the header is used in many many places (wherever missing_syscall.h is included, directly or indirectly), which means that we would need to declare the dependency in meson, so the header would be generated early enough. This turned out to be very noisy. Storing the generated header in version control avoids the hassle.
385 lines
11 KiB
C
385 lines
11 KiB
C
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
#pragma once
|
|
|
|
/* Missing glibc definitions to access certain kernel APIs */
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <sys/syscall.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <unistd.h>
|
|
|
|
#ifdef ARCH_MIPS
|
|
#include <asm/sgidefs.h>
|
|
#endif
|
|
|
|
#include "missing_keyctl.h"
|
|
#include "missing_stat.h"
|
|
#include "missing_syscall_def.h"
|
|
|
|
/* linux/kcmp.h */
|
|
#ifndef KCMP_FILE /* 3f4994cfc15f38a3159c6e3a4b3ab2e1481a6b02 (3.19) */
|
|
#define KCMP_FILE 0
|
|
#endif
|
|
|
|
/* ======================================================================= */
|
|
|
|
#if !HAVE_PIVOT_ROOT
|
|
static inline int missing_pivot_root(const char *new_root, const char *put_old) {
|
|
return syscall(__NR_pivot_root, new_root, put_old);
|
|
}
|
|
|
|
# define pivot_root missing_pivot_root
|
|
#endif
|
|
|
|
/* ======================================================================= */
|
|
|
|
#if !HAVE_MEMFD_CREATE
|
|
static inline int missing_memfd_create(const char *name, unsigned int flags) {
|
|
# ifdef __NR_memfd_create
|
|
return syscall(__NR_memfd_create, name, flags);
|
|
# else
|
|
errno = ENOSYS;
|
|
return -1;
|
|
# endif
|
|
}
|
|
|
|
# define memfd_create missing_memfd_create
|
|
#endif
|
|
|
|
/* ======================================================================= */
|
|
|
|
#if !HAVE_GETRANDOM
|
|
static inline int missing_getrandom(void *buffer, size_t count, unsigned flags) {
|
|
# ifdef __NR_getrandom
|
|
return syscall(__NR_getrandom, buffer, count, flags);
|
|
# else
|
|
errno = ENOSYS;
|
|
return -1;
|
|
# endif
|
|
}
|
|
|
|
# define getrandom missing_getrandom
|
|
#endif
|
|
|
|
/* ======================================================================= */
|
|
|
|
/* The syscall has been defined since forever, but the glibc wrapper was missing. */
|
|
#if !HAVE_GETTID
|
|
static inline pid_t missing_gettid(void) {
|
|
# if defined __NR_gettid && __NR_gettid >= 0
|
|
return (pid_t) syscall(__NR_gettid);
|
|
# else
|
|
# error "__NR_gettid not defined"
|
|
# endif
|
|
}
|
|
|
|
# define gettid missing_gettid
|
|
#endif
|
|
|
|
/* ======================================================================= */
|
|
|
|
#if !HAVE_NAME_TO_HANDLE_AT
|
|
struct file_handle {
|
|
unsigned int handle_bytes;
|
|
int handle_type;
|
|
unsigned char f_handle[0];
|
|
};
|
|
|
|
static inline int missing_name_to_handle_at(int fd, const char *name, struct file_handle *handle, int *mnt_id, int flags) {
|
|
# ifdef __NR_name_to_handle_at
|
|
return syscall(__NR_name_to_handle_at, fd, name, handle, mnt_id, flags);
|
|
# else
|
|
errno = ENOSYS;
|
|
return -1;
|
|
# endif
|
|
}
|
|
|
|
# define name_to_handle_at missing_name_to_handle_at
|
|
#endif
|
|
|
|
/* ======================================================================= */
|
|
|
|
#if !HAVE_SETNS
|
|
static inline int missing_setns(int fd, int nstype) {
|
|
# ifdef __NR_setns
|
|
return syscall(__NR_setns, fd, nstype);
|
|
# else
|
|
errno = ENOSYS;
|
|
return -1;
|
|
# endif
|
|
}
|
|
|
|
# define setns missing_setns
|
|
#endif
|
|
|
|
/* ======================================================================= */
|
|
|
|
static inline pid_t raw_getpid(void) {
|
|
#if defined(__alpha__)
|
|
return (pid_t) syscall(__NR_getxpid);
|
|
#else
|
|
return (pid_t) syscall(__NR_getpid);
|
|
#endif
|
|
}
|
|
|
|
/* ======================================================================= */
|
|
|
|
#if !HAVE_RENAMEAT2
|
|
static inline int missing_renameat2(int oldfd, const char *oldname, int newfd, const char *newname, unsigned flags) {
|
|
# ifdef __NR_renameat2
|
|
return syscall(__NR_renameat2, oldfd, oldname, newfd, newname, flags);
|
|
# else
|
|
errno = ENOSYS;
|
|
return -1;
|
|
# endif
|
|
}
|
|
|
|
# define renameat2 missing_renameat2
|
|
#endif
|
|
|
|
/* ======================================================================= */
|
|
|
|
#if !HAVE_KCMP
|
|
static inline int missing_kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2) {
|
|
# if defined __NR_kcmp && __NR_kcmp >= 0
|
|
return syscall(__NR_kcmp, pid1, pid2, type, idx1, idx2);
|
|
# else
|
|
errno = ENOSYS;
|
|
return -1;
|
|
# endif
|
|
}
|
|
|
|
# define kcmp missing_kcmp
|
|
#endif
|
|
|
|
/* ======================================================================= */
|
|
|
|
#if !HAVE_KEYCTL
|
|
static inline long missing_keyctl(int cmd, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) {
|
|
# if defined __NR_keyctl && __NR_keyctl >= 0
|
|
return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5);
|
|
# else
|
|
errno = ENOSYS;
|
|
return -1;
|
|
# endif
|
|
|
|
# define keyctl missing_keyctl
|
|
}
|
|
|
|
static inline key_serial_t missing_add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t ringid) {
|
|
# if defined __NR_add_key && __NR_add_key >= 0
|
|
return syscall(__NR_add_key, type, description, payload, plen, ringid);
|
|
# else
|
|
errno = ENOSYS;
|
|
return -1;
|
|
# endif
|
|
|
|
# define add_key missing_add_key
|
|
}
|
|
|
|
static inline key_serial_t missing_request_key(const char *type, const char *description, const char * callout_info, key_serial_t destringid) {
|
|
# if defined __NR_request_key && __NR_request_key >= 0
|
|
return syscall(__NR_request_key, type, description, callout_info, destringid);
|
|
# else
|
|
errno = ENOSYS;
|
|
return -1;
|
|
# endif
|
|
|
|
# define request_key missing_request_key
|
|
}
|
|
#endif
|
|
|
|
/* ======================================================================= */
|
|
|
|
#if !HAVE_COPY_FILE_RANGE
|
|
static inline ssize_t missing_copy_file_range(int fd_in, loff_t *off_in,
|
|
int fd_out, loff_t *off_out,
|
|
size_t len,
|
|
unsigned int flags) {
|
|
# ifdef __NR_copy_file_range
|
|
return syscall(__NR_copy_file_range, fd_in, off_in, fd_out, off_out, len, flags);
|
|
# else
|
|
errno = ENOSYS;
|
|
return -1;
|
|
# endif
|
|
}
|
|
|
|
# define copy_file_range missing_copy_file_range
|
|
#endif
|
|
|
|
/* ======================================================================= */
|
|
|
|
#if !HAVE_BPF
|
|
union bpf_attr;
|
|
|
|
static inline int missing_bpf(int cmd, union bpf_attr *attr, size_t size) {
|
|
#ifdef __NR_bpf
|
|
return (int) syscall(__NR_bpf, cmd, attr, size);
|
|
#else
|
|
errno = ENOSYS;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
# define bpf missing_bpf
|
|
#endif
|
|
|
|
/* ======================================================================= */
|
|
|
|
#if !HAVE_STATX
|
|
struct statx;
|
|
|
|
static inline ssize_t missing_statx(int dfd, const char *filename, unsigned flags, unsigned int mask, struct statx *buffer) {
|
|
# ifdef __NR_statx
|
|
return syscall(__NR_statx, dfd, filename, flags, mask, buffer);
|
|
# else
|
|
errno = ENOSYS;
|
|
return -1;
|
|
# endif
|
|
}
|
|
#endif
|
|
|
|
/* This typedef is supposed to be always defined. */
|
|
typedef struct statx struct_statx;
|
|
|
|
#if !HAVE_STATX
|
|
# define statx(dfd, filename, flags, mask, buffer) missing_statx(dfd, filename, flags, mask, buffer)
|
|
#endif
|
|
|
|
/* ======================================================================= */
|
|
|
|
#if !HAVE_SET_MEMPOLICY
|
|
enum {
|
|
MPOL_DEFAULT,
|
|
MPOL_PREFERRED,
|
|
MPOL_BIND,
|
|
MPOL_INTERLEAVE,
|
|
MPOL_LOCAL,
|
|
};
|
|
|
|
static inline long missing_set_mempolicy(int mode, const unsigned long *nodemask,
|
|
unsigned long maxnode) {
|
|
long i;
|
|
# if defined __NR_set_mempolicy && __NR_set_mempolicy >= 0
|
|
i = syscall(__NR_set_mempolicy, mode, nodemask, maxnode);
|
|
# else
|
|
errno = ENOSYS;
|
|
i = -1;
|
|
# endif
|
|
return i;
|
|
}
|
|
|
|
# define set_mempolicy missing_set_mempolicy
|
|
#endif
|
|
|
|
#if !HAVE_GET_MEMPOLICY
|
|
static inline long missing_get_mempolicy(int *mode, unsigned long *nodemask,
|
|
unsigned long maxnode, void *addr,
|
|
unsigned long flags) {
|
|
long i;
|
|
# if defined __NR_get_mempolicy && __NR_get_mempolicy >= 0
|
|
i = syscall(__NR_get_mempolicy, mode, nodemask, maxnode, addr, flags);
|
|
# else
|
|
errno = ENOSYS;
|
|
i = -1;
|
|
# endif
|
|
return i;
|
|
}
|
|
|
|
# define get_mempolicy missing_get_mempolicy
|
|
#endif
|
|
|
|
/* ======================================================================= */
|
|
|
|
#if !HAVE_PIDFD_SEND_SIGNAL
|
|
static inline int missing_pidfd_send_signal(int fd, int sig, siginfo_t *info, unsigned flags) {
|
|
# ifdef __NR_pidfd_send_signal
|
|
return syscall(__NR_pidfd_send_signal, fd, sig, info, flags);
|
|
# else
|
|
errno = ENOSYS;
|
|
return -1;
|
|
# endif
|
|
}
|
|
|
|
# define pidfd_send_signal missing_pidfd_send_signal
|
|
#endif
|
|
|
|
#if !HAVE_PIDFD_OPEN
|
|
static inline int missing_pidfd_open(pid_t pid, unsigned flags) {
|
|
# ifdef __NR_pidfd_open
|
|
return syscall(__NR_pidfd_open, pid, flags);
|
|
# else
|
|
errno = ENOSYS;
|
|
return -1;
|
|
# endif
|
|
}
|
|
|
|
# define pidfd_open missing_pidfd_open
|
|
#endif
|
|
|
|
/* ======================================================================= */
|
|
|
|
#if !HAVE_RT_SIGQUEUEINFO
|
|
static inline int missing_rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t *info) {
|
|
# if defined __NR_rt_sigqueueinfo && __NR_rt_sigqueueinfo >= 0
|
|
return syscall(__NR_rt_sigqueueinfo, tgid, sig, info);
|
|
# else
|
|
# error "__NR_rt_sigqueueinfo not defined"
|
|
# endif
|
|
}
|
|
|
|
# define rt_sigqueueinfo missing_rt_sigqueueinfo
|
|
#endif
|
|
|
|
/* ======================================================================= */
|
|
|
|
#if !HAVE_EXECVEAT
|
|
static inline int missing_execveat(int dirfd, const char *pathname,
|
|
char *const argv[], char *const envp[],
|
|
int flags) {
|
|
# if defined __NR_execveat && __NR_execveat >= 0
|
|
return syscall(__NR_execveat, dirfd, pathname, argv, envp, flags);
|
|
# else
|
|
errno = ENOSYS;
|
|
return -1;
|
|
# endif
|
|
}
|
|
|
|
# undef AT_EMPTY_PATH
|
|
# define AT_EMPTY_PATH 0x1000
|
|
# define execveat missing_execveat
|
|
#endif
|
|
|
|
/* ======================================================================= */
|
|
|
|
#if !HAVE_CLOSE_RANGE
|
|
static inline int missing_close_range(int first_fd, int end_fd, unsigned flags) {
|
|
# ifdef __NR_close_range
|
|
/* Kernel-side the syscall expects fds as unsigned integers (just like close() actually), while
|
|
* userspace exclusively uses signed integers for fds. We don't know just yet how glibc is going to
|
|
* wrap this syscall, but let's assume it's going to be similar to what they do for close(),
|
|
* i.e. make the same unsigned → signed type change from the raw kernel syscall compared to the
|
|
* userspace wrapper. There's only one caveat for this: unlike for close() there's the special
|
|
* UINT_MAX fd value for the 'end_fd' argument. Let's safely map that to -1 here. And let's refuse
|
|
* any other negative values. */
|
|
if ((first_fd < 0) || (end_fd < 0 && end_fd != -1)) {
|
|
errno = -EBADF;
|
|
return -1;
|
|
}
|
|
|
|
return syscall(__NR_close_range,
|
|
(unsigned) first_fd,
|
|
end_fd == -1 ? UINT_MAX : (unsigned) end_fd, /* Of course, the compiler should figure out that this is the identity mapping IRL */
|
|
flags);
|
|
# else
|
|
errno = ENOSYS;
|
|
return -1;
|
|
# endif
|
|
}
|
|
|
|
# define close_range missing_close_range
|
|
#endif
|