libc,shared: detect newer library symbols at runtime

For libc syscall wrappers (pidfd_open, fsopen, openat2, etc.) we previously
gated the calls behind build-time HAVE_* checks. Replace these with shim
functions in src/libc/ that fall back to the raw syscall at runtime when the
loaded glibc lacks the symbol. The infrastructure lives in src/libc/libc-shim.h:
DEFINE_SYSCALL_SHIM falls back to a direct syscall, DEFINE_LIBC_SHIM returns
ENOSYS (for posix_spawn-family helpers that have no corresponding syscall), and
DEFINE_LIBC_ERRNO_SHIM sets errno=ENOSYS and returns -1 (for read/write-style
helpers). The weak reference to the libc symbol is bound via __asm__(\"name\")
rename so the bare libc identifier never appears as a C token — this avoids
both #undef boilerplate against override-header redirects and the resulting
-Wredundant-decls warning. Drop the corresponding cc.has_function() loop from
meson.build.

For optional libraries (libcryptsetup, libdw, libarchive), drop the per-symbol
HAVE_* checks. Always declare the prototypes, suppressing the redundant-decl
warnings via DISABLE_WARNING_REDUNDANT_DECLS and NOLINT, and resolve the symbols
after the main dlopen via a new DLSYM_OPTIONAL() helper that only assigns on
success. libcryptsetup's crypt_set_keyring_to_link / crypt_token_set_external_path
and libarchive's *_is_set wrappers use fallback functions as their pointer
initializers (returning -ENOSYS and 0 respectively), so call sites can invoke
the symbol unconditionally and just check for -ENOSYS where the \"not supported\"
distinction matters.

The same shim treatment applies to pidfd_spawn / posix_spawnattr_setcgroup_np
(src/libc/spawn.c) and epoll_pwait2 (src/libc/epoll.c), with corresponding
override headers in src/include/override/spawn.h and
src/include/override/sys/epoll.h. posix_spawn_wrapper() in process-util.c and
epoll_wait_usec() in sd-event.c now detect ENOSYS in the return value instead
of checking the function pointer, falling back to plain posix_spawn() and
epoll_wait() respectively. coredump-config and coredump-submit get a
dlopen_dw_has_dwfl_set_sysroot() helper. The kexec arch gate now uses
defined(__NR_kexec_file_load) directly; pidfd.h uses __has_include_next() to
decide whether to pull in glibc's header.

This lets binaries built against newer glibc / libcryptsetup / libdw /
libarchive headers still load and run on older targets where these symbols are
absent.
This commit is contained in:
Daan De Meyer
2026-05-13 10:29:50 +02:00
parent 5d60cd8539
commit f795d54591
51 changed files with 504 additions and 475 deletions
+4 -4
View File
@@ -3,15 +3,15 @@
#include_next <stdio.h>
#if !HAVE_RENAMEAT2
#ifndef RENAME_NOREPLACE
# define RENAME_NOREPLACE (1 << 0)
# define RENAME_EXCHANGE (1 << 1)
# define RENAME_WHITEOUT (1 << 2)
int missing_renameat2(int __oldfd, const char *__old, int __newfd, const char *__new, unsigned __flags);
# define renameat2 missing_renameat2
#endif
int renameat2_shim(int __oldfd, const char *__old, int __newfd, const char *__new, unsigned __flags);
#define renameat2 renameat2_shim
/* When a stream is opened read-only under glibc, fputs() and friends fail with EBADF. However, they
* succeed under musl. We rely on the glibc behavior in the code base. The following _check_writable()
* functions first check if the passed stream is writable, and refuse to write with EBADF if not. */
+2 -4
View File
@@ -27,7 +27,5 @@
/* Defined since glibc-2.42.
* Supported since kernel v5.6 (fddb5d430ad9fa91b49b1d34d0202ffe2fa0e179). */
#if !HAVE_OPENAT2
int missing_openat2(int dfd, const char *filename, const struct open_how *how, size_t usize);
# define openat2 missing_openat2
#endif
int openat2_shim(int dfd, const char *filename, const struct open_how *how, size_t usize);
#define openat2 openat2_shim
+2 -4
View File
@@ -51,10 +51,8 @@ int __clone2(int (*fn)(void *), void *stack_base, size_t stack_size, int flags,
/* Defined since glibc-2.41.
* Supported since kernel 3.14 (e6cfc0295c7d51b008999a8b13a44fb43f8685ea). */
#if !HAVE_SCHED_SETATTR
int missing_sched_setattr(pid_t pid, struct sched_attr *attr, unsigned flags);
# define sched_setattr missing_sched_setattr
#endif
int sched_setattr_shim(pid_t pid, struct sched_attr *attr, unsigned flags);
#define sched_setattr sched_setattr_shim
/* f0e1a0643a59bf1f922fa209cec86a170b784f3f (6.12),
* defined in sched.h in glibc since glibc-2.41. */
+2 -4
View File
@@ -3,7 +3,5 @@
#include_next <signal.h> /* IWYU pragma: export */
#if !HAVE_RT_TGSIGQUEUEINFO
int missing_rt_tgsigqueueinfo(pid_t tgid, pid_t tid, int sig, siginfo_t *info);
# define rt_tgsigqueueinfo missing_rt_tgsigqueueinfo
#endif
int rt_tgsigqueueinfo_shim(pid_t tgid, pid_t tid, int sig, siginfo_t *info);
#define rt_tgsigqueueinfo rt_tgsigqueueinfo_shim
+21
View File
@@ -0,0 +1,21 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include_next <spawn.h> /* IWYU pragma: export */
/* pidfd_spawn() and posix_spawnattr_setcgroup_np() were added in glibc 2.39. Redirect to shims that
* return ENOSYS at runtime when the libc symbols aren't available, so callers don't need to worry
* about the libc version. */
int pidfd_spawn_shim(pid_t *restrict pidfd, const char *restrict path,
const posix_spawn_file_actions_t *restrict file_actions,
const posix_spawnattr_t *restrict attrp,
char *const argv[restrict], char *const envp[restrict]);
#define pidfd_spawn pidfd_spawn_shim
int posix_spawnattr_setcgroup_np_shim(posix_spawnattr_t *attr, int cgroup);
#define posix_spawnattr_setcgroup_np posix_spawnattr_setcgroup_np_shim
/* Defined in <spawn.h> since glibc 2.39. */
#ifndef POSIX_SPAWN_SETCGROUP
# define POSIX_SPAWN_SETCGROUP 0x100
#endif
+2 -4
View File
@@ -5,7 +5,5 @@
#include <stddef.h>
/* Supported since kernel v3.18 (749730ce42a2121e1c88350d69478bff3994b10a). */
#if !HAVE_BPF
int missing_bpf(int cmd, union bpf_attr *attr, size_t size);
# define bpf missing_bpf
#endif
int bpf_shim(int cmd, union bpf_attr *attr, size_t size);
#define bpf bpf_shim
+10
View File
@@ -0,0 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include_next <sys/epoll.h> /* IWYU pragma: export */
/* epoll_pwait2() was added to glibc 2.35. Redirect to a shim that sets errno=ENOSYS at runtime when
* the libc symbol isn't available, so callers don't need to worry about the libc version. */
int epoll_pwait2_shim(int fd, struct epoll_event *events, int maxevents,
const struct timespec *timeout, const sigset_t *sigmask);
#define epoll_pwait2 epoll_pwait2_shim
+4 -8
View File
@@ -3,12 +3,8 @@
#include <linux/ioprio.h> /* IWYU pragma: export */
#if !HAVE_IOPRIO_GET
int missing_ioprio_get(int which, int who);
# define ioprio_get missing_ioprio_get
#endif
int ioprio_get_shim(int which, int who);
#define ioprio_get ioprio_get_shim
#if !HAVE_IOPRIO_SET
int missing_ioprio_set(int which, int who, int ioprio);
# define ioprio_set missing_ioprio_set
#endif
int ioprio_set_shim(int which, int who, int ioprio);
#define ioprio_set ioprio_set_shim
+2 -4
View File
@@ -5,7 +5,5 @@
#include <sys/types.h>
/* Supported since kernel v3.5 (d97b46a64674a267bc41c9e16132ee2a98c3347d). */
#if !HAVE_KCMP
int missing_kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2);
# define kcmp missing_kcmp
#endif
int kcmp_shim(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2);
#define kcmp kcmp_shim
+3 -5
View File
@@ -6,11 +6,9 @@
/* Supported since kernel v3.17 (cb1052581e2bddd6096544f3f944f4e7fdad4c4f).
* Not available on all architectures. */
#if HAVE_KEXEC_FILE_LOAD || defined __NR_kexec_file_load
# if !HAVE_KEXEC_FILE_LOAD
int missing_kexec_file_load(int kernel_fd, int initrd_fd, unsigned long cmdline_len, const char *cmdline, unsigned long flags);
# define kexec_file_load missing_kexec_file_load
# endif
#ifdef __NR_kexec_file_load
int kexec_file_load_shim(int kernel_fd, int initrd_fd, unsigned long cmdline_len, const char *cmdline, unsigned long flags);
# define kexec_file_load kexec_file_load_shim
# define HAVE_KEXEC_FILE_LOAD_SYSCALL 1
#else
# define HAVE_KEXEC_FILE_LOAD_SYSCALL 0
+6 -12
View File
@@ -4,17 +4,11 @@
#include <linux/keyctl.h> /* IWYU pragma: export */
#include <stddef.h>
#if !HAVE_KEYCTL
long missing_keyctl(int cmd, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);
# define keyctl missing_keyctl
#endif
long keyctl_shim(int cmd, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);
#define keyctl keyctl_shim
#if !HAVE_ADD_KEY
key_serial_t missing_add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t ringid);
# define add_key missing_add_key
#endif
key_serial_t add_key_shim(const char *type, const char *description, const void *payload, size_t plen, key_serial_t ringid);
#define add_key add_key_shim
#if !HAVE_REQUEST_KEY
key_serial_t missing_request_key(const char *type, const char *description, const char *callout_info, key_serial_t destringid);
# define request_key missing_request_key
#endif
key_serial_t request_key_shim(const char *type, const char *description, const char *callout_info, key_serial_t destringid);
#define request_key request_key_shim
+4 -8
View File
@@ -3,12 +3,8 @@
#include <linux/mempolicy.h> /* IWYU pragma: export */
#if !HAVE_SET_MEMPOLICY
int missing_set_mempolicy(int mode, const unsigned long *nodemask, unsigned long maxnode);
# define set_mempolicy missing_set_mempolicy
#endif
int set_mempolicy_shim(int mode, const unsigned long *nodemask, unsigned long maxnode);
#define set_mempolicy set_mempolicy_shim
#if !HAVE_GET_MEMPOLICY
int missing_get_mempolicy(int *mode, unsigned long *nodemask, unsigned long maxnode, void *addr, unsigned long flags);
# define get_mempolicy missing_get_mempolicy
#endif
int get_mempolicy_shim(int *mode, unsigned long *nodemask, unsigned long maxnode, void *addr, unsigned long flags);
#define get_mempolicy get_mempolicy_shim
+14 -42
View File
@@ -39,56 +39,36 @@ extern int umount2(const char *__special_file, int __flags);
/* Open the filesystem referenced by FS_NAME so it can be configured for mounting. */
/* Defined since glibc-2.36.
* Supported since kernel v5.2 (24dcb3d90a1f67fe08c68a004af37df059d74005). */
#if HAVE_FSOPEN
extern int fsopen(const char *__fs_name, unsigned int __flags);
#else
int missing_fsopen(const char *fsname, unsigned flags);
# define fsopen missing_fsopen
#endif
int fsopen_shim(const char *fsname, unsigned flags);
#define fsopen fsopen_shim
/* Create a mount representation for the FD created by fsopen using
FLAGS with ATTR_FLAGS describing how the mount is to be performed. */
/* Defined since glibc-2.36.
* Supported since kernel v5.2 (93766fbd2696c2c4453dd8e1070977e9cd4e6b6d). */
#if HAVE_FSMOUNT
extern int fsmount(int __fd, unsigned int __flags, unsigned int __ms_flags);
#else
int missing_fsmount(int fd, unsigned flags, unsigned ms_flags);
# define fsmount missing_fsmount
#endif
int fsmount_shim(int fd, unsigned flags, unsigned ms_flags);
#define fsmount fsmount_shim
/* Add the mounted FROM_DFD referenced by FROM_PATHNAME filesystem returned
by fsmount in the hierarchy in the place TO_DFD reference by TO_PATHNAME
using FLAGS. */
/* Defined since glibc-2.36.
* Supported since kernel v5.2 (2db154b3ea8e14b04fee23e3fdfd5e9d17fbc6ae). */
#if HAVE_MOVE_MOUNT
extern int move_mount(int __from_dfd, const char *__from_pathname, int __to_dfd, const char *__to_pathname, unsigned int flags);
#else
int missing_move_mount(int from_dfd, const char *from_pathname, int to_dfd, const char *to_pathname, unsigned flags);
# define move_mount missing_move_mount
#endif
int move_mount_shim(int from_dfd, const char *from_pathname, int to_dfd, const char *to_pathname, unsigned flags);
#define move_mount move_mount_shim
/* Set parameters and trigger CMD action on the FD context. KEY, VALUE,
and AUX are used depending ng of the CMD. */
/* Defined since glibc-2.36.
* Supported since kernel v5.2 (ecdab150fddb42fe6a739335257949220033b782). */
#if HAVE_FSCONFIG
extern int fsconfig(int __fd, unsigned int __cmd, const char *__key, const void *__value, int __aux);
#else
int missing_fsconfig(int fd, unsigned cmd, const char *key, const void *value, int aux);
# define fsconfig missing_fsconfig
#endif
int fsconfig_shim(int fd, unsigned cmd, const char *key, const void *value, int aux);
#define fsconfig fsconfig_shim
/* Open the mount point FILENAME in directory DFD using FLAGS. */
/* Defined since glibc-2.36.
* Supported since kernel v5.2 (a07b20004793d8926f78d63eb5980559f7813404). */
#if HAVE_OPEN_TREE
extern int open_tree(int __dfd, const char *__filename, unsigned int __flags);
#else
int missing_open_tree(int dfd, const char *filename, unsigned flags);
# define open_tree missing_open_tree
#endif
int open_tree_shim(int dfd, const char *filename, unsigned flags);
#define open_tree open_tree_shim
/* Change the mount properties of the mount or an entire mount tree. If
PATH is a relative pathname, then it is interpreted relative to the
@@ -97,18 +77,10 @@ int missing_open_tree(int dfd, const char *filename, unsigned flags);
working directory of the calling process. */
/* Defined since glibc-2.36.
* Supported since kernel v5.12 (2a1867219c7b27f928e2545782b86daaf9ad50bd). */
#if HAVE_MOUNT_SETATTR
extern int mount_setattr(int __dfd, const char *__path, unsigned int __flags, struct mount_attr *__uattr, size_t __usize);
#else
int missing_mount_setattr(int dfd, const char *path, unsigned flags, struct mount_attr *attr, size_t size);
# define mount_setattr missing_mount_setattr
#endif
int mount_setattr_shim(int dfd, const char *path, unsigned flags, struct mount_attr *attr, size_t size);
#define mount_setattr mount_setattr_shim
/* Not defined in glibc yet as of glibc-2.41.
* Supported since kernel v6.15 (c4a16820d90199409c9bf01c4f794e1e9e8d8fd8). */
#if HAVE_OPEN_TREE_ATTR
extern int open_tree_attr(int __dfd, const char *__filename, unsigned int __flags, struct mount_attr *__uattr, size_t __usize);
#else
int missing_open_tree_attr(int dfd, const char *filename, unsigned int flags, struct mount_attr *attr, size_t size);
# define open_tree_attr missing_open_tree_attr
#endif
int open_tree_attr_shim(int dfd, const char *filename, unsigned int flags, struct mount_attr *attr, size_t size);
#define open_tree_attr open_tree_attr_shim
+10 -10
View File
@@ -1,8 +1,12 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
/* since glibc-2.36 */
#if HAVE_PIDFD_OPEN
#include <features.h>
/* since glibc-2.36. Only descend into the next <sys/pidfd.h> on glibc — musl ships no such header,
* and musl-gcc's -idirafter /usr/include would otherwise pull in glibc's copy (which depends on
* __THROW and other glibc-isms) from the fallback path. */
#if defined(__GLIBC__) && __has_include_next(<sys/pidfd.h>)
#include_next <sys/pidfd.h> /* IWYU pragma: export */
#endif
@@ -12,17 +16,13 @@
/* Defined since glibc-2.36.
* Supported since kernel v5.3 (7615d9e1780e26e0178c93c55b73309a5dc093d7). */
#if !HAVE_PIDFD_OPEN
int missing_pidfd_open(pid_t pid, unsigned flags);
# define pidfd_open missing_pidfd_open
#endif
int pidfd_open_shim(pid_t pid, unsigned flags);
#define pidfd_open pidfd_open_shim
/* Defined since glibc-2.36.
* Supported since kernel v5.1 (3eb39f47934f9d5a3027fe00d906a45fe3a15fad). */
#if !HAVE_PIDFD_SEND_SIGNAL
int missing_pidfd_send_signal(int fd, int sig, siginfo_t *info, unsigned flags);
# define pidfd_send_signal missing_pidfd_send_signal
#endif
int pidfd_send_signal_shim(int fd, int sig, siginfo_t *info, unsigned flags);
#define pidfd_send_signal pidfd_send_signal_shim
/* since glibc-2.41 */
#ifndef PIDFS_IOCTL_MAGIC
+2 -4
View File
@@ -4,7 +4,5 @@
#include_next <sys/quota.h> /* IWYU pragma: export */
/* Supported since kernel v5.14 (64c2c2c62f92339b176ea24403d8db16db36f9e6). */
#if !HAVE_QUOTACTL_FD
int missing_quotactl_fd(int fd, int cmd, int id, void *addr);
# define quotactl_fd missing_quotactl_fd
#endif
int quotactl_fd_shim(int fd, int cmd, int id, void *addr);
#define quotactl_fd quotactl_fd_shim
+2 -4
View File
@@ -4,7 +4,5 @@
#include_next <sys/stat.h> /* IWYU pragma: export */
/* Supported since kernel v6.6 (78252deb023cf0879256fcfbafe37022c390762b). */
#if !HAVE_FCHMODAT2
int missing_fchmodat2(int dirfd, const char *path, mode_t mode, int flags);
# define fchmodat2 missing_fchmodat2
#endif
int fchmodat2_shim(int dirfd, const char *path, mode_t mode, int flags);
#define fchmodat2 fchmodat2_shim
+4 -8
View File
@@ -8,13 +8,9 @@
#include_next <sys/xattr.h> /* IWYU pragma: export */
/* Supported since kernel v6.13 (6140be90ec70c39fa844741ca3cc807dd0866394). */
#if !HAVE_SETXATTRAT
int missing_setxattrat(int fd, const char *path, int at_flags, const char *name, const struct xattr_args *args, size_t size);
# define setxattrat missing_setxattrat
#endif
int setxattrat_shim(int fd, const char *path, int at_flags, const char *name, const struct xattr_args *args, size_t size);
#define setxattrat setxattrat_shim
/* Supported since kernel v6.13 (6140be90ec70c39fa844741ca3cc807dd0866394). */
#if !HAVE_REMOVEXATTRAT
int missing_removexattrat(int fd, const char *path, int at_flags, const char *name);
# define removexattrat missing_removexattrat
#endif
int removexattrat_shim(int fd, const char *path, int at_flags, const char *name);
#define removexattrat removexattrat_shim
+2 -4
View File
@@ -3,7 +3,5 @@
#include_next <unistd.h> /* IWYU pragma: export */
#if !HAVE_PIVOT_ROOT
int missing_pivot_root(const char *new_root, const char *put_old);
# define pivot_root missing_pivot_root
#endif
int pivot_root_shim(const char *new_root, const char *put_old);
#define pivot_root pivot_root_shim