From: Daan De Meyer Date: Tue, 12 May 2026 14:29:18 +0000 (+0200) Subject: libc,shared: detect newer library symbols at runtime X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f795d5459151ad84acf77557cf47dddddb3b4bce;p=thirdparty%2Fsystemd.git 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. --- diff --git a/meson.build b/meson.build index 2e78b359e6e..e84303c9cba 100644 --- a/meson.build +++ b/meson.build @@ -582,43 +582,6 @@ long_max = cc.compute_int( assert(long_max > 100000) conf.set_quoted('LONG_MAX_STR', f'@long_max@') -foreach ident : [ - ['renameat2', '''#include '''], # since musl-1.2.6 - ['set_mempolicy', '''#include '''], # declared at numaif.h provided by libnuma, which we do not use - ['get_mempolicy', '''#include '''], # declared at numaif.h provided by libnuma, which we do not use - ['epoll_pwait2', '''#include '''], # since glibc-2.35 - ['fsconfig', '''#include '''], # since glibc-2.36 - ['fsmount', '''#include '''], # since glibc-2.36 - ['fsopen', '''#include '''], # since glibc-2.36 - ['mount_setattr', '''#include '''], # since glibc-2.36 - ['move_mount', '''#include '''], # since glibc-2.36 - ['open_tree', '''#include '''], # since glibc-2.36 - ['openat2', '''#include '''], # since glibc-2.42 - ['pidfd_open', '''#include '''], # since glibc-2.36 - ['pidfd_send_signal', '''#include '''], # since glibc-2.36 - ['pidfd_spawn', '''#include '''], # since glibc-2.39 - ['sched_setattr', '''#include '''], # since glibc-2.41 - ['ioprio_get', '''#include '''], # no known header declares ioprio_get - ['ioprio_set', '''#include '''], # no known header declares ioprio_set - ['rt_tgsigqueueinfo', '''#include '''], # no known header declares rt_tgsigqueueinfo - ['open_tree_attr', '''#include '''], # no known header declares open_tree_attr - ['quotactl_fd', '''#include '''], # no known header declares quotactl_fd - ['fchmodat2', '''#include '''], # no known header declares fchmodat2 - ['bpf', '''#include '''], # no known header declares bpf - ['kcmp', '''#include '''], # no known header declares kcmp - ['kexec_file_load', '''#include '''], # no known header declares kexec_file_load - ['keyctl', '''#include '''], # no known header declares keyctl - ['add_key', '''#include '''], # no known header declares add_key - ['request_key', '''#include '''], # no known header declares request_key - ['setxattrat', '''#include '''], # no known header declares setxattrat - ['removexattrat', '''#include '''], # no known header declares removexattrat - ['pivot_root', '''#include '''], # no known header declares pivot_root -] - - have = cc.has_function(ident[0], prefix : ident[1], args : '-D_GNU_SOURCE') - conf.set10('HAVE_' + ident[0].to_upper(), have) -endforeach - ##################################################################### awk = find_program('awk') @@ -1203,18 +1166,6 @@ conf.set10('HAVE_LIBCRYPTSETUP', have) conf.set10('HAVE_LIBCRYPTSETUP_PLUGINS', libcryptsetup_plugins.allowed() and have) -foreach ident : [ - 'crypt_set_keyring_to_link', # 2.7 - 'crypt_token_set_external_path', # 2.7 - ] - - have_ident = have and cc.has_function( - ident, - prefix : '#include ', - dependencies : libcryptsetup) - conf.set10('HAVE_' + ident.to_upper(), have_ident) -endforeach - libcurl = dependency('libcurl', version : '>= 7.32.0', required : get_option('libcurl')) @@ -1292,10 +1243,6 @@ libelf = dependency('libelf', libelf_cflags = libelf.partial_dependency(includes: true, compile_args: true) conf.set10('HAVE_ELFUTILS', libdw.found() and libelf.found()) -# New in elfutils 0.192 -conf.set10('HAVE_DWFL_SET_SYSROOT', - libdw.found() and cc.has_function('dwfl_set_sysroot', dependencies : libdw)) - libz = dependency('zlib', required : get_option('zlib')) conf.set10('HAVE_ZLIB', libz.found()) @@ -1365,16 +1312,6 @@ libarchive = dependency('libarchive', libarchive_cflags = libarchive.partial_dependency(includes: true, compile_args: true) conf.set10('HAVE_LIBARCHIVE', libarchive.found()) -foreach ident : [ - 'archive_entry_gid_is_set', # since 3.7.3 - 'archive_entry_uid_is_set', # since 3.7.3 - 'archive_entry_hardlink_is_set', # since 3.7.5 - ] - - have = libarchive.found() and cc.has_function(ident, dependencies : libarchive) - conf.set10('HAVE_' + ident.to_upper(), have) -endforeach - libxkbcommon = dependency('xkbcommon', version : '>= 0.3.0', required : get_option('xkbcommon')) diff --git a/src/basic/dlfcn-util.h b/src/basic/dlfcn-util.h index ccf0ec6be3b..0fd78483f85 100644 --- a/src/basic/dlfcn-util.h +++ b/src/basic/dlfcn-util.h @@ -33,6 +33,19 @@ int dlopen_many_sym_or_warn_sentinel(void **dlp, const char *filename, int log_l #define DLSYM_ARG_FORCE(arg) \ &sym_##arg, STRINGIFY(arg) +/* Resolve a single optional symbol from an already-opened library handle. The pointer variable is expected + * to be named sym_ (same convention as DLSYM_ARG). Only assigns on success, so the pointer keeps its + * pre-existing value if the symbol is not present — useful for fallback initialization. dlerror() is + * cleared first so callers can distinguish "symbol not found" from "symbol's value is NULL" by checking + * dlerror() after; for function symbols (which can never be NULL on success) the _v check below is + * sufficient. */ +#define DLSYM_OPTIONAL(dl, name) \ + ({ \ + (void) dlerror(); \ + typeof(sym_##name) _v = (typeof(sym_##name)) dlsym((dl), #name); \ + if (_v) sym_##name = _v; \ + }) + /* If called dlopen_many_sym_or_warn() will fail with EPERM. This can be used to block lazy loading of shared * libs, if we transfer a process into a different namespace. Note that this does not work for all calls of * dlopen(), just those through our dlopen_safe() wrapper (which we use comprehensively in our diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 697498ea5d4..270c119ab72 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -2047,7 +2047,6 @@ int posix_spawn_wrapper( /* Initialization needs to succeed before we can set up a destructor. */ _unused_ _cleanup_(posix_spawnattr_destroyp) posix_spawnattr_t *attr_destructor = &attr; -#if HAVE_PIDFD_SPAWN static bool have_clone_into_cgroup = true; /* kernel 5.7+ */ _cleanup_close_ int cgroup_fd = -EBADF; @@ -2063,12 +2062,13 @@ int posix_spawn_wrapper( return -errno; r = posix_spawnattr_setcgroup_np(&attr, cgroup_fd); - if (r != 0) + if (r == 0) + flags |= POSIX_SPAWN_SETCGROUP; + else if (r != ENOSYS) return -r; - - flags |= POSIX_SPAWN_SETCGROUP; + /* If libc lacks posix_spawnattr_setcgroup_np we silently skip POSIX_SPAWN_SETCGROUP — the + * caller will then need to attach the child to the cgroup themselves. */ } -#endif r = posix_spawnattr_setflags(&attr, flags); if (r != 0) @@ -2077,7 +2077,6 @@ int posix_spawn_wrapper( if (r != 0) return -r; -#if HAVE_PIDFD_SPAWN _cleanup_close_ int pidfd = -EBADF; r = pidfd_spawn(&pidfd, path, NULL, &attr, argv, envp); @@ -2101,15 +2100,18 @@ int posix_spawn_wrapper( r = pidfd_spawn(&pidfd, path, NULL, &attr, argv, envp); } - if (r != 0) + if (r == 0) { + r = pidref_set_pidfd_consume(ret_pidref, TAKE_FD(pidfd)); + if (r < 0) + return r; + + return FLAGS_SET(flags, POSIX_SPAWN_SETCGROUP); + } + if (!ERRNO_IS_NOT_SUPPORTED(r)) return -r; - r = pidref_set_pidfd_consume(ret_pidref, TAKE_FD(pidfd)); - if (r < 0) - return r; + /* pidfd_spawn unavailable (libc or kernel missing) — fall back to plain posix_spawn. */ - return FLAGS_SET(flags, POSIX_SPAWN_SETCGROUP); -#else pid_t pid; r = posix_spawn(&pid, path, NULL, &attr, argv, envp); @@ -2121,7 +2123,6 @@ int posix_spawn_wrapper( return r; return 0; /* We did not use CLONE_INTO_CGROUP so return 0, the caller will have to move the child */ -#endif } int proc_dir_open(DIR **ret) { diff --git a/src/coredump/coredump-config.c b/src/coredump/coredump-config.c index cb7bb1adcc4..bcf38a03c58 100644 --- a/src/coredump/coredump-config.c +++ b/src/coredump/coredump-config.c @@ -2,6 +2,7 @@ #include "conf-parser.h" #include "coredump-config.h" +#include "elf-util.h" #include "format-util.h" #include "journal-def.h" #include "log.h" @@ -45,12 +46,10 @@ int coredump_parse_config(CoredumpConfig *config) { config->journal_size_max = JOURNAL_SIZE_MAX; } -#if !HAVE_DWFL_SET_SYSROOT - if (config->enter_namespace) { + if (config->enter_namespace && !dlopen_dw_has_dwfl_set_sysroot()) { log_warning("EnterNamespace= is enabled but libdw does not support dwfl_set_sysroot(), disabling."); config->enter_namespace = false; } -#endif log_debug("Selected storage '%s'.", coredump_storage_to_string(config->storage)); log_debug("Selected compression %s.", yes_no(config->compress)); diff --git a/src/coredump/coredump-submit.c b/src/coredump/coredump-submit.c index 42d92a32e9c..f0c961ded7e 100644 --- a/src/coredump/coredump-submit.c +++ b/src/coredump/coredump-submit.c @@ -468,7 +468,9 @@ static int maybe_remove_external_coredump( } static int acquire_pid_mount_tree_fd(const CoredumpConfig *config, CoredumpContext *context) { -#if HAVE_DWFL_SET_SYSROOT + if (!dlopen_dw_has_dwfl_set_sysroot()) + return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "dwfl_set_sysroot() is not supported."); + _cleanup_close_ int mntns_fd = -EBADF, root_fd = -EBADF, fd = -EBADF; _cleanup_close_pair_ int pair[2] = EBADF_PAIR; int r; @@ -533,10 +535,6 @@ static int acquire_pid_mount_tree_fd(const CoredumpConfig *config, CoredumpConte context->mount_tree_fd = TAKE_FD(fd); return 0; -#else - /* Don't bother preparing environment if we can't pass it to libdwfl. */ - return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "dwfl_set_sysroot() is not supported."); -#endif } static int attach_mount_tree(const CoredumpConfig *config, CoredumpContext *context) { diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index bf313340bf6..fffecd35738 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -608,7 +608,6 @@ static int parse_one_option(const char *option) { log_warning_errno(r, "Failed to parse %s, ignoring: %m", option); } else if ((val = startswith(option, "link-volume-key="))) { -#if HAVE_CRYPT_SET_KEYRING_TO_LINK _cleanup_free_ char *keyring = NULL, *key_type = NULL, *key_description = NULL; const char *sep; @@ -657,9 +656,6 @@ static int parse_one_option(const char *option) { free_and_replace(arg_link_keyring, keyring); free_and_replace(arg_link_key_type, key_type); free_and_replace(arg_link_key_description, key_description); -#else - log_error("Build lacks libcryptsetup support for linking volume keys in user specified kernel keyrings upon device activation, ignoring: %s", option); -#endif } else if ((val = startswith(option, "fixate-volume-key="))) { r = free_and_strdup(&arg_fixate_volume_key, val); if (r < 0) @@ -2688,14 +2684,14 @@ static int verb_attach(int argc, char *argv[], uintptr_t _data, void *userdata) if (r < 0) return log_error_errno(r, "Failed to load LUKS superblock on device %s: %m", sym_crypt_get_device_name(cd)); -/* since cryptsetup 2.7.0 (Jan 2024) */ -#if HAVE_CRYPT_SET_KEYRING_TO_LINK + /* since cryptsetup 2.7.0 (Jan 2024) */ if (arg_link_key_description) { r = sym_crypt_set_keyring_to_link(cd, arg_link_key_description, NULL, arg_link_key_type, arg_link_keyring); - if (r < 0) + if (r == -ENOSYS) + log_warning("Loaded libcryptsetup does not support linking volume keys in user specified kernel keyrings upon device activation, ignoring."); + else if (r < 0) log_warning_errno(r, "Failed to set keyring or key description to link volume key in, ignoring: %m"); } -#endif if (arg_header) { r = sym_crypt_set_data_device(cd, source); diff --git a/src/include/musl/stdio.h b/src/include/musl/stdio.h index 2ff95e952a4..54ad9ed1104 100644 --- a/src/include/musl/stdio.h +++ b/src/include/musl/stdio.h @@ -3,15 +3,15 @@ #include_next -#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. */ diff --git a/src/include/override/fcntl.h b/src/include/override/fcntl.h index a6f2afe53b8..875f112b009 100644 --- a/src/include/override/fcntl.h +++ b/src/include/override/fcntl.h @@ -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 diff --git a/src/include/override/sched.h b/src/include/override/sched.h index 08f4576eca1..3eed3931186 100644 --- a/src/include/override/sched.h +++ b/src/include/override/sched.h @@ -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. */ diff --git a/src/include/override/signal.h b/src/include/override/signal.h index 436b49a5199..6e596adacce 100644 --- a/src/include/override/signal.h +++ b/src/include/override/signal.h @@ -3,7 +3,5 @@ #include_next /* 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 diff --git a/src/include/override/spawn.h b/src/include/override/spawn.h new file mode 100644 index 00000000000..463825856c5 --- /dev/null +++ b/src/include/override/spawn.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include_next /* 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 since glibc 2.39. */ +#ifndef POSIX_SPAWN_SETCGROUP +# define POSIX_SPAWN_SETCGROUP 0x100 +#endif diff --git a/src/include/override/sys/bpf.h b/src/include/override/sys/bpf.h index 56e1466b890..eaeb89a5a8f 100644 --- a/src/include/override/sys/bpf.h +++ b/src/include/override/sys/bpf.h @@ -5,7 +5,5 @@ #include /* 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 diff --git a/src/include/override/sys/epoll.h b/src/include/override/sys/epoll.h new file mode 100644 index 00000000000..0ed9c27a4b2 --- /dev/null +++ b/src/include/override/sys/epoll.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include_next /* 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 diff --git a/src/include/override/sys/ioprio.h b/src/include/override/sys/ioprio.h index c361eba6e60..8ea4c8f8b2a 100644 --- a/src/include/override/sys/ioprio.h +++ b/src/include/override/sys/ioprio.h @@ -3,12 +3,8 @@ #include /* 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 diff --git a/src/include/override/sys/kcmp.h b/src/include/override/sys/kcmp.h index 4e47825ac5b..f0df5d40201 100644 --- a/src/include/override/sys/kcmp.h +++ b/src/include/override/sys/kcmp.h @@ -5,7 +5,5 @@ #include /* 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 diff --git a/src/include/override/sys/kexec.h b/src/include/override/sys/kexec.h index 4e256bdb6ad..be31c93520e 100644 --- a/src/include/override/sys/kexec.h +++ b/src/include/override/sys/kexec.h @@ -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 diff --git a/src/include/override/sys/keyctl.h b/src/include/override/sys/keyctl.h index 88c9c40ea78..65be2fea2de 100644 --- a/src/include/override/sys/keyctl.h +++ b/src/include/override/sys/keyctl.h @@ -4,17 +4,11 @@ #include /* IWYU pragma: export */ #include -#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 diff --git a/src/include/override/sys/mempolicy.h b/src/include/override/sys/mempolicy.h index 9858f2812be..1ba6d95958b 100644 --- a/src/include/override/sys/mempolicy.h +++ b/src/include/override/sys/mempolicy.h @@ -3,12 +3,8 @@ #include /* 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 diff --git a/src/include/override/sys/mount.h b/src/include/override/sys/mount.h index e6d7ad1b67c..2fa5c902e5b 100644 --- a/src/include/override/sys/mount.h +++ b/src/include/override/sys/mount.h @@ -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 diff --git a/src/include/override/sys/pidfd.h b/src/include/override/sys/pidfd.h index 7f136eb62ee..0e9b2f39989 100644 --- a/src/include/override/sys/pidfd.h +++ b/src/include/override/sys/pidfd.h @@ -1,8 +1,12 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -/* since glibc-2.36 */ -#if HAVE_PIDFD_OPEN +#include + +/* since glibc-2.36. Only descend into the next 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() #include_next /* 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 diff --git a/src/include/override/sys/quota.h b/src/include/override/sys/quota.h index fcf6be988bb..95aa3674460 100644 --- a/src/include/override/sys/quota.h +++ b/src/include/override/sys/quota.h @@ -4,7 +4,5 @@ #include_next /* 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 diff --git a/src/include/override/sys/stat.h b/src/include/override/sys/stat.h index 5cef9f852ee..bfd9b54e7f9 100644 --- a/src/include/override/sys/stat.h +++ b/src/include/override/sys/stat.h @@ -4,7 +4,5 @@ #include_next /* 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 diff --git a/src/include/override/sys/xattr.h b/src/include/override/sys/xattr.h index db31d0ba6f3..8823548d89d 100644 --- a/src/include/override/sys/xattr.h +++ b/src/include/override/sys/xattr.h @@ -8,13 +8,9 @@ #include_next /* 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 diff --git a/src/include/override/unistd.h b/src/include/override/unistd.h index bd558694eeb..7dd56aa06f6 100644 --- a/src/include/override/unistd.h +++ b/src/include/override/unistd.h @@ -3,7 +3,5 @@ #include_next /* 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 diff --git a/src/libc/bpf.c b/src/libc/bpf.c index 4a7498e502a..d87dcb1b0c1 100644 --- a/src/libc/bpf.c +++ b/src/libc/bpf.c @@ -1,11 +1,10 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include -#include -#if !HAVE_BPF -int missing_bpf(int cmd, union bpf_attr *attr, size_t size) { - return syscall(__NR_bpf, cmd, attr, size); -} -#endif +#include "libc-shim.h" + +DEFINE_SYSCALL_SHIM(bpf, int, + int, cmd, + union bpf_attr *, attr, + size_t, size) diff --git a/src/libc/epoll.c b/src/libc/epoll.c new file mode 100644 index 00000000000..0845cf38a87 --- /dev/null +++ b/src/libc/epoll.c @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#include "libc-shim.h" + +DEFINE_LIBC_ERRNO_SHIM(epoll_pwait2, int, + int, fd, + struct epoll_event *, events, + int, maxevents, + const struct timespec *, timeout, + const sigset_t *, sigmask) diff --git a/src/libc/fcntl.c b/src/libc/fcntl.c index ca5e6c7977c..8fd78ca897e 100644 --- a/src/libc/fcntl.c +++ b/src/libc/fcntl.c @@ -1,11 +1,11 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include -#include -#if !HAVE_OPENAT2 -int missing_openat2(int dfd, const char *filename, const struct open_how *how, size_t usize) { - return syscall(__NR_openat2, dfd, filename, how, usize); -} -#endif +#include "libc-shim.h" + +DEFINE_SYSCALL_SHIM(openat2, int, + int, dfd, + const char *, filename, + const struct open_how *, how, + size_t, usize) diff --git a/src/libc/ioprio.c b/src/libc/ioprio.c index 5bb640457dc..053693139c3 100644 --- a/src/libc/ioprio.c +++ b/src/libc/ioprio.c @@ -1,17 +1,14 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include -#include -#if !HAVE_IOPRIO_GET -int missing_ioprio_get(int which, int who) { - return syscall(__NR_ioprio_get, which, who); -} -#endif +#include "libc-shim.h" -#if !HAVE_IOPRIO_SET -int missing_ioprio_set(int which, int who, int ioprio) { - return syscall(__NR_ioprio_set, which, who, ioprio); -} -#endif +DEFINE_SYSCALL_SHIM(ioprio_get, int, + int, which, + int, who) + +DEFINE_SYSCALL_SHIM(ioprio_set, int, + int, which, + int, who, + int, ioprio) diff --git a/src/libc/kcmp.c b/src/libc/kcmp.c index aafff999ef4..0f73f8500dd 100644 --- a/src/libc/kcmp.c +++ b/src/libc/kcmp.c @@ -1,11 +1,12 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include -#include -#if !HAVE_KCMP -int missing_kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2) { - return syscall(__NR_kcmp, pid1, pid2, type, idx1, idx2); -} -#endif +#include "libc-shim.h" + +DEFINE_SYSCALL_SHIM(kcmp, int, + pid_t, pid1, + pid_t, pid2, + int, type, + unsigned long, idx1, + unsigned long, idx2) diff --git a/src/libc/kexec.c b/src/libc/kexec.c index 122acff956c..051253d17bd 100644 --- a/src/libc/kexec.c +++ b/src/libc/kexec.c @@ -1,11 +1,14 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include -#include -#if !HAVE_KEXEC_FILE_LOAD && defined __NR_kexec_file_load -int missing_kexec_file_load(int kernel_fd, int initrd_fd, unsigned long cmdline_len, const char *cmdline, unsigned long flags) { - return syscall(__NR_kexec_file_load, kernel_fd, initrd_fd, cmdline_len, cmdline, flags); -} +#include "libc-shim.h" + +#ifdef __NR_kexec_file_load +DEFINE_SYSCALL_SHIM(kexec_file_load, int, + int, kernel_fd, + int, initrd_fd, + unsigned long, cmdline_len, + const char *, cmdline, + unsigned long, flags) #endif diff --git a/src/libc/keyctl.c b/src/libc/keyctl.c index af4bca87f51..c8651845483 100644 --- a/src/libc/keyctl.c +++ b/src/libc/keyctl.c @@ -1,23 +1,25 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include -#include -#if !HAVE_KEYCTL -long missing_keyctl(int cmd, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) { - return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5); -} -#endif +#include "libc-shim.h" -#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) { - return syscall(__NR_add_key, type, description, payload, plen, ringid); -} -#endif +DEFINE_SYSCALL_SHIM(keyctl, long, + int, cmd, + unsigned long, arg2, + unsigned long, arg3, + unsigned long, arg4, + unsigned long, arg5) -#if !HAVE_REQUEST_KEY -key_serial_t missing_request_key(const char *type, const char *description, const char *callout_info, key_serial_t destringid) { - return syscall(__NR_request_key, type, description, callout_info, destringid); -} -#endif +DEFINE_SYSCALL_SHIM(add_key, key_serial_t, + const char *, type, + const char *, description, + const void *, payload, + size_t, plen, + key_serial_t, ringid) + +DEFINE_SYSCALL_SHIM(request_key, key_serial_t, + const char *, type, + const char *, description, + const char *, callout_info, + key_serial_t, destringid) diff --git a/src/libc/libc-shim.h b/src/libc/libc-shim.h new file mode 100644 index 00000000000..c5cf9c8acda --- /dev/null +++ b/src/libc/libc-shim.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include +#include +#include + +/* Each parameter is passed as a flat (type, name) pair: type1, name1, type2, name2, ... The + * _SHIM_DECL/_SHIM_NAME macros consume two args at a time and emit either "type name" pairs (for + * the function declarator) or just the names (for forwarding). _SHIM_PAIRS counts the number of + * pairs by indexing into a table that increments every two positions. */ +#define _SHIM_CAT(a, b) _SHIM_CAT_(a, b) +#define _SHIM_CAT_(a, b) a##b + +#define _SHIM_NTH(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, N, ...) N +#define _SHIM_PAIRS(...) _SHIM_NTH(__VA_ARGS__, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0) + +#define _SHIM_DECL_1(t, n) t n +#define _SHIM_DECL_2(t, n, ...) t n, _SHIM_DECL_1(__VA_ARGS__) +#define _SHIM_DECL_3(t, n, ...) t n, _SHIM_DECL_2(__VA_ARGS__) +#define _SHIM_DECL_4(t, n, ...) t n, _SHIM_DECL_3(__VA_ARGS__) +#define _SHIM_DECL_5(t, n, ...) t n, _SHIM_DECL_4(__VA_ARGS__) +#define _SHIM_DECL_6(t, n, ...) t n, _SHIM_DECL_5(__VA_ARGS__) +#define _SHIM_DECL_7(t, n, ...) t n, _SHIM_DECL_6(__VA_ARGS__) +#define _SHIM_DECL_8(t, n, ...) t n, _SHIM_DECL_7(__VA_ARGS__) +#define _SHIM_DECL(...) _SHIM_CAT(_SHIM_DECL_, _SHIM_PAIRS(__VA_ARGS__))(__VA_ARGS__) + +#define _SHIM_NAME_1(t, n) n +#define _SHIM_NAME_2(t, n, ...) n, _SHIM_NAME_1(__VA_ARGS__) +#define _SHIM_NAME_3(t, n, ...) n, _SHIM_NAME_2(__VA_ARGS__) +#define _SHIM_NAME_4(t, n, ...) n, _SHIM_NAME_3(__VA_ARGS__) +#define _SHIM_NAME_5(t, n, ...) n, _SHIM_NAME_4(__VA_ARGS__) +#define _SHIM_NAME_6(t, n, ...) n, _SHIM_NAME_5(__VA_ARGS__) +#define _SHIM_NAME_7(t, n, ...) n, _SHIM_NAME_6(__VA_ARGS__) +#define _SHIM_NAME_8(t, n, ...) n, _SHIM_NAME_7(__VA_ARGS__) +#define _SHIM_NAME(...) _SHIM_CAT(_SHIM_NAME_, _SHIM_PAIRS(__VA_ARGS__))(__VA_ARGS__) + +/* Defines a wrapper that calls the libc symbol if available at runtime, or falls back to the + * corresponding direct syscall otherwise. The libc symbol is redeclared as a weak reference so the + * binary still loads on libc versions that don't provide it. Each parameter is passed as type, + * name pairs (flat). + * + * The weak reference is bound to the libc symbol via an __asm__("label") rename so that the bare + * libc identifier never appears as a C token. This matters because override/musl headers + * sometimes #define the libc name to redirect it to the _shim variant — without the rename the + * caller would have to #undef each name before invoking the macro. # and ## operators don't + * macro-expand their operands, so the parameter "func" stays a literal token everywhere. */ +#define DEFINE_SYSCALL_SHIM(func, ret, ...) \ + extern typeof(func##_shim) func##_libc_weak __asm__(#func) __attribute__((__weak__)); \ + ret func##_shim(_SHIM_DECL(__VA_ARGS__)) { \ + if (func##_libc_weak) \ + return func##_libc_weak(_SHIM_NAME(__VA_ARGS__)); \ + return syscall(__NR_##func, _SHIM_NAME(__VA_ARGS__)); \ + } + +/* Like DEFINE_SYSCALL_SHIM but for libc helpers that have no corresponding syscall and report errors + * by returning the positive errno value directly (posix_spawn-family convention). If the libc symbol + * is missing at runtime, ENOSYS is returned. */ +#define DEFINE_LIBC_SHIM(func, ret, ...) \ + extern typeof(func##_shim) func##_libc_weak __asm__(#func) __attribute__((__weak__)); \ + ret func##_shim(_SHIM_DECL(__VA_ARGS__)) { \ + if (func##_libc_weak) \ + return func##_libc_weak(_SHIM_NAME(__VA_ARGS__)); \ + return ENOSYS; \ + } + +/* Like DEFINE_LIBC_SHIM but for libc helpers that report errors via errno + -1 return value. If the + * libc symbol is missing at runtime, errno is set to ENOSYS and -1 is returned. */ +#define DEFINE_LIBC_ERRNO_SHIM(func, ret, ...) \ + extern typeof(func##_shim) func##_libc_weak __asm__(#func) __attribute__((__weak__)); \ + ret func##_shim(_SHIM_DECL(__VA_ARGS__)) { \ + if (func##_libc_weak) \ + return func##_libc_weak(_SHIM_NAME(__VA_ARGS__)); \ + errno = ENOSYS; \ + return -1; \ + } diff --git a/src/libc/mempolicy.c b/src/libc/mempolicy.c index 77b0e1ac010..686aba2b590 100644 --- a/src/libc/mempolicy.c +++ b/src/libc/mempolicy.c @@ -1,17 +1,17 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include -#include -#if !HAVE_SET_MEMPOLICY -int missing_set_mempolicy(int mode, const unsigned long *nodemask, unsigned long maxnode) { - return syscall(__NR_set_mempolicy, mode, nodemask, maxnode); -} -#endif +#include "libc-shim.h" -#if !HAVE_GET_MEMPOLICY -int missing_get_mempolicy(int *mode, unsigned long *nodemask, unsigned long maxnode, void *addr, unsigned long flags) { - return syscall(__NR_get_mempolicy, mode, nodemask, maxnode, addr, flags); -} -#endif +DEFINE_SYSCALL_SHIM(set_mempolicy, int, + int, mode, + const unsigned long *, nodemask, + unsigned long, maxnode) + +DEFINE_SYSCALL_SHIM(get_mempolicy, int, + int *, mode, + unsigned long *, nodemask, + unsigned long, maxnode, + void *, addr, + unsigned long, flags) diff --git a/src/libc/meson.build b/src/libc/meson.build index e100a6f4ea0..9b86168536e 100644 --- a/src/libc/meson.build +++ b/src/libc/meson.build @@ -2,6 +2,7 @@ libc_wrapper_sources = files( 'bpf.c', + 'epoll.c', 'fcntl.c', 'ioprio.c', 'kcmp.c', @@ -13,6 +14,7 @@ libc_wrapper_sources = files( 'quota.c', 'sched.c', 'signal.c', + 'spawn.c', 'stat.c', 'unistd.c', 'xattr.c', diff --git a/src/libc/mount.c b/src/libc/mount.c index 1d324ef3866..3e626e87acd 100644 --- a/src/libc/mount.c +++ b/src/libc/mount.c @@ -1,47 +1,47 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include -#include - -#if !HAVE_FSOPEN -int missing_fsopen(const char *fsname, unsigned flags) { - return syscall(__NR_fsopen, fsname, flags); -} -#endif - -#if !HAVE_FSMOUNT -int missing_fsmount(int fd, unsigned flags, unsigned ms_flags) { - return syscall(__NR_fsmount, fd, flags, ms_flags); -} -#endif - -#if !HAVE_MOVE_MOUNT -int missing_move_mount(int from_dfd, const char *from_pathname, int to_dfd, const char *to_pathname, unsigned flags) { - return syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd, to_pathname, flags); -} -#endif - -#if !HAVE_FSCONFIG -int missing_fsconfig(int fd, unsigned cmd, const char *key, const void *value, int aux) { - return syscall(__NR_fsconfig, fd, cmd, key, value, aux); -} -#endif - -#if !HAVE_OPEN_TREE -int missing_open_tree(int dfd, const char *filename, unsigned flags) { - return syscall(__NR_open_tree, dfd, filename, flags); -} -#endif - -#if !HAVE_MOUNT_SETATTR -int missing_mount_setattr(int dfd, const char *path, unsigned flags, struct mount_attr *attr, size_t size) { - return syscall(__NR_mount_setattr, dfd, path, flags, attr, size); -} -#endif - -#if !HAVE_OPEN_TREE_ATTR -int missing_open_tree_attr(int dfd, const char *filename, unsigned int flags, struct mount_attr *attr, size_t size) { - return syscall(__NR_open_tree_attr, dfd, filename, flags, attr, size); -} -#endif + +#include "libc-shim.h" + +DEFINE_SYSCALL_SHIM(fsopen, int, + const char *, fsname, + unsigned, flags) + +DEFINE_SYSCALL_SHIM(fsmount, int, + int, fd, + unsigned, flags, + unsigned, ms_flags) + +DEFINE_SYSCALL_SHIM(move_mount, int, + int, from_dfd, + const char *, from_pathname, + int, to_dfd, + const char *, to_pathname, + unsigned, flags) + +DEFINE_SYSCALL_SHIM(fsconfig, int, + int, fd, + unsigned, cmd, + const char *, key, + const void *, value, + int, aux) + +DEFINE_SYSCALL_SHIM(open_tree, int, + int, dfd, + const char *, filename, + unsigned, flags) + +DEFINE_SYSCALL_SHIM(mount_setattr, int, + int, dfd, + const char *, path, + unsigned, flags, + struct mount_attr *, attr, + size_t, size) + +DEFINE_SYSCALL_SHIM(open_tree_attr, int, + int, dfd, + const char *, filename, + unsigned int, flags, + struct mount_attr *, attr, + size_t, size) diff --git a/src/libc/musl/stdio.c b/src/libc/musl/stdio.c index 6d02aef5f65..f4e8d9e4c18 100644 --- a/src/libc/musl/stdio.c +++ b/src/libc/musl/stdio.c @@ -3,14 +3,15 @@ #include #include #include -#include -#include -#if !HAVE_RENAMEAT2 -int missing_renameat2(int __oldfd, const char *__old, int __newfd, const char *__new, unsigned __flags) { - return syscall(__NR_renameat2, __oldfd, __old, __newfd, __new, __flags); -} -#endif +#include "../libc-shim.h" + +DEFINE_SYSCALL_SHIM(renameat2, int, + int, __oldfd, + const char *, __old, + int, __newfd, + const char *, __new, + unsigned, __flags) #define DEFINE_PUT(func) \ int func##_check_writable(int c, FILE *stream) { \ diff --git a/src/libc/pidfd.c b/src/libc/pidfd.c index a779e3459c6..8343cf7ff9f 100644 --- a/src/libc/pidfd.c +++ b/src/libc/pidfd.c @@ -1,17 +1,15 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include -#include -#if !HAVE_PIDFD_OPEN -int missing_pidfd_open(pid_t pid, unsigned flags) { - return syscall(__NR_pidfd_open, pid, flags); -} -#endif +#include "libc-shim.h" -#if !HAVE_PIDFD_SEND_SIGNAL -int missing_pidfd_send_signal(int fd, int sig, siginfo_t *info, unsigned flags) { - return syscall(__NR_pidfd_send_signal, fd, sig, info, flags); -} -#endif +DEFINE_SYSCALL_SHIM(pidfd_open, int, + pid_t, pid, + unsigned, flags) + +DEFINE_SYSCALL_SHIM(pidfd_send_signal, int, + int, fd, + int, sig, + siginfo_t *, info, + unsigned, flags) diff --git a/src/libc/quota.c b/src/libc/quota.c index 19695df9b3c..08a7e5b8ae2 100644 --- a/src/libc/quota.c +++ b/src/libc/quota.c @@ -1,11 +1,11 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include -#include -#if !HAVE_QUOTACTL_FD -int missing_quotactl_fd(int fd, int cmd, int id, void *addr) { - return syscall(__NR_quotactl_fd, fd, cmd, id, addr); -} -#endif +#include "libc-shim.h" + +DEFINE_SYSCALL_SHIM(quotactl_fd, int, + int, fd, + int, cmd, + int, id, + void *, addr) diff --git a/src/libc/sched.c b/src/libc/sched.c index cf1752651b3..24de9731beb 100644 --- a/src/libc/sched.c +++ b/src/libc/sched.c @@ -1,11 +1,10 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include -#include -#if !HAVE_SCHED_SETATTR -int missing_sched_setattr(pid_t pid, struct sched_attr *attr, unsigned flags) { - return syscall(__NR_sched_setattr, pid, attr, flags); -} -#endif +#include "libc-shim.h" + +DEFINE_SYSCALL_SHIM(sched_setattr, int, + pid_t, pid, + struct sched_attr *, attr, + unsigned, flags) diff --git a/src/libc/signal.c b/src/libc/signal.c index 1908331b1a4..9370491821f 100644 --- a/src/libc/signal.c +++ b/src/libc/signal.c @@ -1,11 +1,11 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include -#include -#if !HAVE_RT_TGSIGQUEUEINFO -int missing_rt_tgsigqueueinfo(pid_t tgid, pid_t tid, int sig, siginfo_t *info) { - return syscall(__NR_rt_tgsigqueueinfo, tgid, tid, sig, info); -} -#endif +#include "libc-shim.h" + +DEFINE_SYSCALL_SHIM(rt_tgsigqueueinfo, int, + pid_t, tgid, + pid_t, tid, + int, sig, + siginfo_t *, info) diff --git a/src/libc/spawn.c b/src/libc/spawn.c new file mode 100644 index 00000000000..6e2f9cf0429 --- /dev/null +++ b/src/libc/spawn.c @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#include "libc-shim.h" + +DEFINE_LIBC_SHIM(pidfd_spawn, int, + 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 *restrict, argv, + char *const *restrict, envp) + +DEFINE_LIBC_SHIM(posix_spawnattr_setcgroup_np, int, + posix_spawnattr_t *, attr, + int, cgroup) diff --git a/src/libc/stat.c b/src/libc/stat.c index b283005f40b..ffc00f13c64 100644 --- a/src/libc/stat.c +++ b/src/libc/stat.c @@ -1,11 +1,11 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include -#include -#if !HAVE_FCHMODAT2 -int missing_fchmodat2(int dirfd, const char *path, mode_t mode, int flags) { - return syscall(__NR_fchmodat2, dirfd, path, mode, flags); -} -#endif +#include "libc-shim.h" + +DEFINE_SYSCALL_SHIM(fchmodat2, int, + int, dirfd, + const char *, path, + mode_t, mode, + int, flags) diff --git a/src/libc/unistd.c b/src/libc/unistd.c index 81728631c6b..22fb704907e 100644 --- a/src/libc/unistd.c +++ b/src/libc/unistd.c @@ -1,10 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include -#include +#include "libc-shim.h" -#if !HAVE_PIVOT_ROOT -int missing_pivot_root(const char *new_root, const char *put_old) { - return syscall(__NR_pivot_root, new_root, put_old); -} -#endif +DEFINE_SYSCALL_SHIM(pivot_root, int, + const char *, new_root, + const char *, put_old) diff --git a/src/libc/xattr.c b/src/libc/xattr.c index 68fa97ab281..668faa766c9 100644 --- a/src/libc/xattr.c +++ b/src/libc/xattr.c @@ -1,17 +1,19 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include #include -#include -#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) { - return syscall(__NR_setxattrat, fd, path, at_flags, name, args, size); -} -#endif +#include "libc-shim.h" -#if !HAVE_REMOVEXATTRAT -int missing_removexattrat(int fd, const char *path, int at_flags, const char *name) { - return syscall(__NR_removexattrat, fd, path, at_flags, name); -} -#endif +DEFINE_SYSCALL_SHIM(setxattrat, int, + int, fd, + const char *, path, + int, at_flags, + const char *, name, + const struct xattr_args *, args, + size_t, size) + +DEFINE_SYSCALL_SHIM(removexattrat, int, + int, fd, + const char *, path, + int, at_flags, + const char *, name) diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index 9e7ba7813cd..8c419fad306 100644 --- a/src/libsystemd/sd-event/sd-event.c +++ b/src/libsystemd/sd-event/sd-event.c @@ -4629,19 +4629,9 @@ static int epoll_wait_usec( int maxevents, usec_t timeout) { - int msec; - /* A wrapper that uses epoll_pwait2() if available, and falls back to epoll_wait() if not. */ - -#if HAVE_EPOLL_PWAIT2 static bool epoll_pwait2_absent = false; - int r; - - /* epoll_pwait2() was added to Linux 5.11 (2021-02-14) and to glibc in 2.35 (2022-02-03). In contrast - * to other syscalls we don't bother with our own fallback syscall wrappers on old libcs, since this - * is not that obvious to implement given the libc and kernel definitions differ in the last - * argument. Moreover, the only reason to use it is the more accurate timeouts (which is not a - * biggie), let's hence rely on glibc's definitions, and fallback to epoll_pwait() when that's - * missing. */ + int r, msec; + /* A wrapper that uses epoll_pwait2() if available, and falls back to epoll_wait() if not. */ if (!epoll_pwait2_absent && timeout != USEC_INFINITY) { r = epoll_pwait2(fd, @@ -4657,7 +4647,6 @@ static int epoll_wait_usec( epoll_pwait2_absent = true; } -#endif if (timeout == USEC_INFINITY) msec = -1; diff --git a/src/shared/cryptsetup-util.c b/src/shared/cryptsetup-util.c index 47a148459d9..7aa3d5d9c52 100644 --- a/src/shared/cryptsetup-util.c +++ b/src/shared/cryptsetup-util.c @@ -55,9 +55,15 @@ DLSYM_PROTOTYPE(crypt_resume_by_volume_key) = NULL; DLSYM_PROTOTYPE(crypt_set_data_device) = NULL; DLSYM_PROTOTYPE(crypt_set_data_offset) = NULL; DLSYM_PROTOTYPE(crypt_set_debug_level) = NULL; -#if HAVE_CRYPT_SET_KEYRING_TO_LINK -DLSYM_PROTOTYPE(crypt_set_keyring_to_link) = NULL; -#endif +static int missing_crypt_set_keyring_to_link( + struct crypt_device *cd, + const char *key_description, + const char *old_key_description, + const char *key_type_desc, + const char *keyring_to_link_vk) { + return -ENOSYS; +} +DLSYM_PROTOTYPE(crypt_set_keyring_to_link) = missing_crypt_set_keyring_to_link; DLSYM_PROTOTYPE(crypt_set_log_callback) = NULL; DLSYM_PROTOTYPE(crypt_set_metadata_size) = NULL; DLSYM_PROTOTYPE(crypt_set_pbkdf_type) = NULL; @@ -67,9 +73,10 @@ DLSYM_PROTOTYPE(crypt_token_external_path) = NULL; DLSYM_PROTOTYPE(crypt_token_json_get) = NULL; DLSYM_PROTOTYPE(crypt_token_json_set) = NULL; DLSYM_PROTOTYPE(crypt_token_max) = NULL; -#if HAVE_CRYPT_TOKEN_SET_EXTERNAL_PATH -DLSYM_PROTOTYPE(crypt_token_set_external_path) = NULL; -#endif +static int missing_crypt_token_set_external_path(const char *path) { + return -ENOSYS; +} +DLSYM_PROTOTYPE(crypt_token_set_external_path) = missing_crypt_token_set_external_path; DLSYM_PROTOTYPE(crypt_token_status) = NULL; DLSYM_PROTOTYPE(crypt_volume_key_get) = NULL; DLSYM_PROTOTYPE(crypt_volume_key_keyring) = NULL; @@ -318,9 +325,6 @@ int dlopen_cryptsetup(int log_level) { DLSYM_ARG(crypt_set_data_device), DLSYM_ARG(crypt_set_data_offset), DLSYM_ARG(crypt_set_debug_level), -#if HAVE_CRYPT_SET_KEYRING_TO_LINK - DLSYM_ARG(crypt_set_keyring_to_link), -#endif DLSYM_ARG(crypt_set_log_callback), DLSYM_ARG(crypt_set_metadata_size), DLSYM_ARG(crypt_set_pbkdf_type), @@ -330,9 +334,6 @@ int dlopen_cryptsetup(int log_level) { DLSYM_ARG(crypt_token_json_get), DLSYM_ARG(crypt_token_json_set), DLSYM_ARG(crypt_token_max), -#if HAVE_CRYPT_TOKEN_SET_EXTERNAL_PATH - DLSYM_ARG(crypt_token_set_external_path), -#endif DLSYM_ARG(crypt_token_status), DLSYM_ARG(crypt_volume_key_get), DLSYM_ARG(crypt_volume_key_keyring), @@ -341,6 +342,12 @@ int dlopen_cryptsetup(int log_level) { if (r <= 0) return r; + /* Optional symbols: present in libcryptsetup 2.7+ only. If unresolved, the prototype keeps its + * static initializer pointing at a fallback that returns -ENOSYS, so call sites can invoke the + * symbol unconditionally. */ + DLSYM_OPTIONAL(cryptsetup_dl, crypt_set_keyring_to_link); + DLSYM_OPTIONAL(cryptsetup_dl, crypt_token_set_external_path); + /* Redirect the default logging calls of libcryptsetup to our own logging infra. (Note that * libcryptsetup also maintains per-"struct crypt_device" log functions, which we'll also set * whenever allocating a "struct crypt_device" context. Why set both? To be defensive: maybe some @@ -350,13 +357,11 @@ int dlopen_cryptsetup(int log_level) { const char *e = secure_getenv("SYSTEMD_CRYPTSETUP_TOKEN_PATH"); if (e) { -#if HAVE_CRYPT_TOKEN_SET_EXTERNAL_PATH r = sym_crypt_token_set_external_path(e); - if (r < 0) + if (r == -ENOSYS) + log_debug("Loaded libcryptsetup does not support setting the external token path, not setting it to '%s'.", e); + else if (r < 0) log_debug_errno(r, "Failed to set the libcryptsetup external token path to '%s', ignoring: %m", e); -#else - log_debug("libcryptsetup version does not support setting the external token path, not setting it to '%s'.", e); -#endif } return 1; diff --git a/src/shared/cryptsetup-util.h b/src/shared/cryptsetup-util.h index 4e994ce9f99..5c83aad8ee2 100644 --- a/src/shared/cryptsetup-util.h +++ b/src/shared/cryptsetup-util.h @@ -7,6 +7,19 @@ #if HAVE_LIBCRYPTSETUP #include /* IWYU pragma: export */ +/* Available since libcryptsetup 2.7. Always redeclare so DLSYM_PROTOTYPE's typeof() resolves on older + * headers; suppress the warning when newer libcryptsetup already declares them. */ +DISABLE_WARNING_REDUNDANT_DECLS; +/* NOLINTBEGIN(readability-redundant-declaration) */ +extern int crypt_set_keyring_to_link(struct crypt_device *cd, + const char *key_description, + const char *old_key_description, + const char *key_type_desc, + const char *keyring_to_link_vk); +extern int crypt_token_set_external_path(const char *path); +/* NOLINTEND(readability-redundant-declaration) */ +REENABLE_WARNING; + extern DLSYM_PROTOTYPE(crypt_activate_by_passphrase); extern DLSYM_PROTOTYPE(crypt_activate_by_signed_key); extern DLSYM_PROTOTYPE(crypt_activate_by_token_pin); @@ -43,9 +56,7 @@ extern DLSYM_PROTOTYPE(crypt_resume_by_volume_key); extern DLSYM_PROTOTYPE(crypt_set_data_device); extern DLSYM_PROTOTYPE(crypt_set_data_offset); extern DLSYM_PROTOTYPE(crypt_set_debug_level); -#if HAVE_CRYPT_SET_KEYRING_TO_LINK extern DLSYM_PROTOTYPE(crypt_set_keyring_to_link); -#endif extern DLSYM_PROTOTYPE(crypt_set_log_callback); extern DLSYM_PROTOTYPE(crypt_set_metadata_size); extern DLSYM_PROTOTYPE(crypt_set_pbkdf_type); @@ -55,9 +66,7 @@ extern DLSYM_PROTOTYPE(crypt_token_external_path); extern DLSYM_PROTOTYPE(crypt_token_json_get); extern DLSYM_PROTOTYPE(crypt_token_json_set); extern DLSYM_PROTOTYPE(crypt_token_max); -#if HAVE_CRYPT_TOKEN_SET_EXTERNAL_PATH extern DLSYM_PROTOTYPE(crypt_token_set_external_path); -#endif extern DLSYM_PROTOTYPE(crypt_token_status); extern DLSYM_PROTOTYPE(crypt_volume_key_get); extern DLSYM_PROTOTYPE(crypt_volume_key_keyring); diff --git a/src/shared/elf-util.c b/src/shared/elf-util.c index 7c3d10f9e33..02035404a65 100644 --- a/src/shared/elf-util.c +++ b/src/shared/elf-util.c @@ -56,9 +56,12 @@ static DLSYM_PROTOTYPE(dwfl_begin) = NULL; static DLSYM_PROTOTYPE(dwfl_build_id_find_elf) = NULL; static DLSYM_PROTOTYPE(dwfl_core_file_attach) = NULL; static DLSYM_PROTOTYPE(dwfl_core_file_report) = NULL; -#if HAVE_DWFL_SET_SYSROOT +/* New in elfutils 0.192. Always redeclare so DLSYM_PROTOTYPE's typeof() resolves on older headers; suppress + * the warning when newer libdw already declared it. */ +DISABLE_WARNING_REDUNDANT_DECLS; +extern int dwfl_set_sysroot(Dwfl *dwfl, const char *sysroot); /* NOLINT(readability-redundant-declaration) */ +REENABLE_WARNING; static DLSYM_PROTOTYPE(dwfl_set_sysroot) = NULL; -#endif static DLSYM_PROTOTYPE(dwfl_end) = NULL; static DLSYM_PROTOTYPE(dwfl_errmsg) = NULL; static DLSYM_PROTOTYPE(dwfl_errno) = NULL; @@ -121,9 +124,6 @@ int dlopen_dw(int log_level) { DLSYM_ARG(dwfl_module_getelf), DLSYM_ARG(dwfl_begin), DLSYM_ARG(dwfl_core_file_report), -#if HAVE_DWFL_SET_SYSROOT - DLSYM_ARG(dwfl_set_sysroot), -#endif DLSYM_ARG(dwfl_report_end), DLSYM_ARG(dwfl_getmodules), DLSYM_ARG(dwfl_core_file_attach), @@ -139,6 +139,9 @@ int dlopen_dw(int log_level) { if (r <= 0) return r; + /* Optional symbol: present in libdw 0.192+. NULL pointer is fine; call sites check at use. */ + DLSYM_OPTIONAL(dw_dl, dwfl_set_sysroot); + return 1; #else return log_full_errno(log_level, SYNTHETIC_ERRNO(EOPNOTSUPP), @@ -146,6 +149,16 @@ int dlopen_dw(int log_level) { #endif } +bool dlopen_dw_has_dwfl_set_sysroot(void) { +#if HAVE_ELFUTILS + if (dlopen_dw(LOG_DEBUG) < 0) + return false; + return sym_dwfl_set_sysroot; +#else + return false; +#endif +} + int dlopen_elf(int log_level) { #if HAVE_ELFUTILS int r; @@ -660,13 +673,13 @@ static int parse_core( if (empty_or_root(root)) root = NULL; -#if HAVE_DWFL_SET_SYSROOT - if (root && sym_dwfl_set_sysroot(c.dwfl, root) < 0) - return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not set root directory, dwfl_set_sysroot() failed: %s", sym_dwfl_errmsg(sym_dwfl_errno())); -#else - if (root) - log_warning("Compiled without dwfl_set_sysroot() support, ignoring provided root directory."); -#endif + if (root) { + if (sym_dwfl_set_sysroot) { + if (sym_dwfl_set_sysroot(c.dwfl, root) < 0) + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not set root directory, dwfl_set_sysroot() failed: %s", sym_dwfl_errmsg(sym_dwfl_errno())); + } else + log_warning("Loaded libdw does not support dwfl_set_sysroot(), ignoring provided root directory."); + } if (sym_dwfl_core_file_report(c.dwfl, c.elf, executable) < 0) return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse core file, dwfl_core_file_report() failed: %s", sym_dwfl_errmsg(sym_dwfl_errno())); diff --git a/src/shared/elf-util.h b/src/shared/elf-util.h index e9d9e959dab..7ccbec56c64 100644 --- a/src/shared/elf-util.h +++ b/src/shared/elf-util.h @@ -6,6 +6,8 @@ int dlopen_dw(int log_level); int dlopen_elf(int log_level); +bool dlopen_dw_has_dwfl_set_sysroot(void); + /* Parse an ELF object in a forked process, so that errors while iterating over * untrusted and potentially malicious data do not propagate to the main caller's process. * If fork_disable_dump, the child process will not dump core if it crashes. */ diff --git a/src/shared/libarchive-util.c b/src/shared/libarchive-util.c index 2ef0b37959b..f0f3439f928 100644 --- a/src/shared/libarchive-util.c +++ b/src/shared/libarchive-util.c @@ -18,17 +18,15 @@ DLSYM_PROTOTYPE(archive_entry_fflags) = NULL; DLSYM_PROTOTYPE(archive_entry_filetype) = NULL; DLSYM_PROTOTYPE(archive_entry_free) = NULL; DLSYM_PROTOTYPE(archive_entry_gid) = NULL; -#if HAVE_ARCHIVE_ENTRY_GID_IS_SET -DLSYM_PROTOTYPE(archive_entry_gid_is_set) = NULL; -#else -int sym_archive_entry_gid_is_set(struct archive_entry *e) { +static int missing_archive_entry_gid_is_set(struct archive_entry *e) { return gid_is_valid(sym_archive_entry_gid(e)); } -#endif +DLSYM_PROTOTYPE(archive_entry_gid_is_set) = missing_archive_entry_gid_is_set; DLSYM_PROTOTYPE(archive_entry_hardlink) = NULL; -#if HAVE_ARCHIVE_ENTRY_HARDLINK_IS_SET -DLSYM_PROTOTYPE(archive_entry_hardlink_is_set) = NULL; -#endif +static int missing_archive_entry_hardlink_is_set(struct archive_entry *e) { + return !!sym_archive_entry_hardlink(e); +} +DLSYM_PROTOTYPE(archive_entry_hardlink_is_set) = missing_archive_entry_hardlink_is_set; DLSYM_PROTOTYPE(archive_entry_mode) = NULL; DLSYM_PROTOTYPE(archive_entry_mtime) = NULL; DLSYM_PROTOTYPE(archive_entry_mtime_is_set) = NULL; @@ -53,13 +51,10 @@ DLSYM_PROTOTYPE(archive_entry_set_uid) = NULL; DLSYM_PROTOTYPE(archive_entry_sparse_add_entry) = NULL; DLSYM_PROTOTYPE(archive_entry_symlink) = NULL; DLSYM_PROTOTYPE(archive_entry_uid) = NULL; -#if HAVE_ARCHIVE_ENTRY_UID_IS_SET -DLSYM_PROTOTYPE(archive_entry_uid_is_set) = NULL; -#else -int sym_archive_entry_uid_is_set(struct archive_entry *e) { +static int missing_archive_entry_uid_is_set(struct archive_entry *e) { return uid_is_valid(sym_archive_entry_uid(e)); } -#endif +DLSYM_PROTOTYPE(archive_entry_uid_is_set) = missing_archive_entry_uid_is_set; DLSYM_PROTOTYPE(archive_entry_xattr_add_entry) = NULL; DLSYM_PROTOTYPE(archive_entry_xattr_next) = NULL; DLSYM_PROTOTYPE(archive_entry_xattr_reset) = NULL; @@ -84,13 +79,15 @@ DLSYM_PROTOTYPE(archive_write_set_format_pax) = NULL; int dlopen_libarchive(int log_level) { #if HAVE_LIBARCHIVE + int r; + SD_ELF_NOTE_DLOPEN( "archive", "Support for decompressing archive files", SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, "libarchive.so.13"); - return dlopen_many_sym_or_warn( + r = dlopen_many_sym_or_warn( &libarchive_dl, "libarchive.so.13", log_level, @@ -101,13 +98,7 @@ int dlopen_libarchive(int log_level) { DLSYM_ARG(archive_entry_filetype), DLSYM_ARG(archive_entry_free), DLSYM_ARG(archive_entry_gid), -#if HAVE_ARCHIVE_ENTRY_GID_IS_SET - DLSYM_ARG(archive_entry_gid_is_set), -#endif DLSYM_ARG(archive_entry_hardlink), -#if HAVE_ARCHIVE_ENTRY_HARDLINK_IS_SET - DLSYM_ARG(archive_entry_hardlink_is_set), -#endif DLSYM_ARG(archive_entry_mode), DLSYM_ARG(archive_entry_mtime), DLSYM_ARG(archive_entry_mtime_is_set), @@ -132,9 +123,6 @@ int dlopen_libarchive(int log_level) { DLSYM_ARG(archive_entry_sparse_add_entry), DLSYM_ARG(archive_entry_symlink), DLSYM_ARG(archive_entry_uid), -#if HAVE_ARCHIVE_ENTRY_UID_IS_SET - DLSYM_ARG(archive_entry_uid_is_set), -#endif DLSYM_ARG(archive_entry_xattr_add_entry), DLSYM_ARG(archive_entry_xattr_next), DLSYM_ARG(archive_entry_xattr_reset), @@ -155,6 +143,16 @@ int dlopen_libarchive(int log_level) { DLSYM_ARG(archive_write_open_fd), DLSYM_ARG(archive_write_set_format_filter_by_ext), DLSYM_ARG(archive_write_set_format_pax)); + if (r <= 0) + return r; + + /* Optional symbols: present in newer libarchive versions only. If missing, sym_X keeps its + * fallback-function initializer (see above). */ + DLSYM_OPTIONAL(libarchive_dl, archive_entry_gid_is_set); + DLSYM_OPTIONAL(libarchive_dl, archive_entry_uid_is_set); + DLSYM_OPTIONAL(libarchive_dl, archive_entry_hardlink_is_set); + + return 1; #else return log_full_errno(log_level, SYNTHETIC_ERRNO(EOPNOTSUPP), "libarchive support is not compiled in."); diff --git a/src/shared/libarchive-util.h b/src/shared/libarchive-util.h index f1f78aad5d1..9f47ea67eac 100644 --- a/src/shared/libarchive-util.h +++ b/src/shared/libarchive-util.h @@ -9,6 +9,16 @@ #include "dlfcn-util.h" +/* Newer symbols that might not be in the header we build against. Always redeclare so DLSYM_PROTOTYPE's + * typeof() resolves; suppress the warning when newer libarchive already declared them. */ +DISABLE_WARNING_REDUNDANT_DECLS; +/* NOLINTBEGIN(readability-redundant-declaration) */ +extern int archive_entry_gid_is_set(struct archive_entry *e); +extern int archive_entry_hardlink_is_set(struct archive_entry *e); +extern int archive_entry_uid_is_set(struct archive_entry *e); +/* NOLINTEND(readability-redundant-declaration) */ +REENABLE_WARNING; + extern DLSYM_PROTOTYPE(archive_entry_acl_add_entry); extern DLSYM_PROTOTYPE(archive_entry_acl_next); extern DLSYM_PROTOTYPE(archive_entry_acl_reset); @@ -16,19 +26,9 @@ extern DLSYM_PROTOTYPE(archive_entry_fflags); extern DLSYM_PROTOTYPE(archive_entry_filetype); extern DLSYM_PROTOTYPE(archive_entry_free); extern DLSYM_PROTOTYPE(archive_entry_gid); -#if HAVE_ARCHIVE_ENTRY_GID_IS_SET extern DLSYM_PROTOTYPE(archive_entry_gid_is_set); -#else -int sym_archive_entry_gid_is_set(struct archive_entry *e); -#endif extern DLSYM_PROTOTYPE(archive_entry_hardlink); -#if HAVE_ARCHIVE_ENTRY_HARDLINK_IS_SET extern DLSYM_PROTOTYPE(archive_entry_hardlink_is_set); -#else -static inline int sym_archive_entry_hardlink_is_set(struct archive_entry *e) { - return !!sym_archive_entry_hardlink(e); -} -#endif extern DLSYM_PROTOTYPE(archive_entry_mode); extern DLSYM_PROTOTYPE(archive_entry_mtime); extern DLSYM_PROTOTYPE(archive_entry_mtime_is_set); @@ -53,11 +53,7 @@ extern DLSYM_PROTOTYPE(archive_entry_set_uid); extern DLSYM_PROTOTYPE(archive_entry_sparse_add_entry); extern DLSYM_PROTOTYPE(archive_entry_symlink); extern DLSYM_PROTOTYPE(archive_entry_uid); -#if HAVE_ARCHIVE_ENTRY_UID_IS_SET extern DLSYM_PROTOTYPE(archive_entry_uid_is_set); -#else -int sym_archive_entry_uid_is_set(struct archive_entry *e); -#endif extern DLSYM_PROTOTYPE(archive_entry_xattr_add_entry); extern DLSYM_PROTOTYPE(archive_entry_xattr_next); extern DLSYM_PROTOTYPE(archive_entry_xattr_reset);