[RFC] Use sysret
wrapper on linux (original) (raw)
According to nolibc
, __sysret
is used to handle most cases when returning from kernel and setting errno
. __sysret identifier - Linux source code (v6.8-rc1) - Bootlin.
__sysret
is defined as
/* Syscall return helper: takes the syscall value in argument and checks for an
* error in it. This may only be used with signed returns (int or long), but
* not with pointers. An error is any value < 0. When an error is encountered,
* -ret is set into errno and -1 is returned. Otherwise the returned value is
* passed as-is with its type preserved.
*/
#define __sysret(arg) \
({ \
__typeof__(arg) __sysret_arg = (arg); \
(__sysret_arg < 0) /* error ? */ \
? (({ SET_ERRNO(-__sysret_arg); }), -1) /* ret -1 with errno = -arg */ \
: __sysret_arg; /* return original value */ \
})
I suppose we can add a similar implementation:
//===--- Linux Syscall Errno Wrapper ----------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SYSRET_H
#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SYSRET_H
#include "src/__support/CPP/optional.h"
#include "src/__support/OSUtil/linux/syscall.h"
#include "src/errno/libc_errno.h"
namespace LIBC_NAMESPACE {
template <typename R, typename... Ts>
LIBC_INLINE cpp::optional<R> sysret(long number, Ts... ts) {
long ret = syscall_impl<long>(number, ts...);
if (ret < 0) {
libc_errno = static_cast<int>(-ret);
return cpp::nullopt;
}
return {cpp::bit_or_static_cast<R>(ret)};
}
template <typename R = int, R DEFAULT = R(-1), typename... Ts>
LIBC_INLINE R sysret_default(long number, Ts... ts) {
auto ret = sysret<R>(number, ts...);
return ret ? *ret : DEFAULT;
}
} // namespace LIBC_NAMESPACE
#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SYSRET_H
In this way, we can rewrite many syscall wrappers. For example,
namespace LIBC_NAMESPACE {
LLVM_LIBC_FUNCTION(int, creat, (const char *path, int mode_flags)) {
#ifdef SYS_open
int fd = LIBC_NAMESPACE::syscall_impl<int>(
SYS_open, path, O_CREAT | O_WRONLY | O_TRUNC, mode_flags);
#else
int fd = LIBC_NAMESPACE::syscall_impl<int>(
SYS_openat, AT_FDCWD, path, O_CREAT | O_WRONLY | O_TRUNC, mode_flags);
#endif
if (fd > 0)
return fd;
libc_errno = -fd;
return -1;
}
} // namespace LIBC_NAMESPACE
can be shortened into
namespace LIBC_NAMESPACE {
LLVM_LIBC_FUNCTION(int, creat, (const char *path, int mode_flags)) {
#ifdef SYS_open
return sysret_default(SYS_open, path, O_CREAT | O_WRONLY | O_TRUNC,
mode_flags);
#else
return sysret_default(SYS_openat, AT_FDCWD, path,
O_CREAT | O_WRONLY | O_TRUNC, mode_flags);
#endif
}
} // namespace LIBC_NAMESPACE