[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