[libc] Refactor core Linux syscalls to use syscall_wrappers (#185983)

This patch initiates the refactoring of Linux syscalls as described in
the RFC (https://discourse.llvm.org/t/rfc-linux-syscall-cleanup/87248/).

It introduces a new infrastructure in
`src/__support/OSUtil/linux/syscall_wrappers/` to house header-only
syscall wrappers. These wrappers utilize `ErrorOr` to provide a
consistent, type-safe interface for error handling across the library,
standardizing how syscall return values are converted into
errno-compatible Error objects.

Summary of changes:
- Created the `syscall_wrappers` directory and added `close.h`,
`read.h`, `write.h`, and `open.h`.
- Moved the existing `getrandom.h` into the new `syscall_wrappers`
directory and updated its callers (including HashTable/randomness.h).
- Refactored core entrypoints (`close`, `read`, `write`, `open`) to use
the new wrappers, removing direct `syscall_impl` logic and manual errno
setting.
- Updated `shm_open.cpp` to use the new `open` wrapper.
- Cleaned up `OSUtil/linux/fcntl.cpp` by removing redundant internal
implementations of `open` and `close`.
- Added a developer guide in `docs/dev/syscall_wrapper_refactor.rst`
outlining the established pattern for future migrations.

---------

Co-authored-by: Michael Jones <michaelrj@google.com>
This commit is contained in:
Jeff Bailey 2026-03-17 18:27:32 +00:00 committed by GitHub
parent 4b9693a423
commit ebb3309975
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 311 additions and 70 deletions

View File

@ -21,3 +21,4 @@ Navigate to the links below for information on the respective topics:
implementation_standard
undefined_behavior
printf_behavior
syscall_wrapper_refactor

View File

@ -0,0 +1,75 @@
.. _syscall_wrapper_refactor:
==============================
Syscall Wrapper Refactoring
==============================
Purpose
=======
LLVM-libc is transitioning to a centralized system for Linux syscalls. The goal
is to move all direct ``syscall_impl`` calls into a dedicated directory:
``src/__support/OSUtil/linux/syscall_wrappers/``.
This refactor provides several benefits:
* **Reusability**: Allows multiple entrypoints to share a single syscall
implementation without public APIs depending on other public APIs.
* **Type Safety**: Using ``ErrorOr<T>`` ensures that error conditions are
handled explicitly.
* **Consistency**: Standardizes the conversion of syscall return values into
errno-compatible objects.
* **Maintainability**: Centralizes platform-specific syscall logic, making it
easier to audit and update.
The Pattern
===========
Each syscall should have its own header-only library in the ``syscall_wrappers``
directory. The wrapper function should return an ``ErrorOr<T>``. Wrappers live
in the ``linux_syscalls`` namespace to make call sites self-documenting and to
clearly identify any leakage into OS-generic code.
Example Wrapper (``src/__support/OSUtil/linux/syscall_wrappers/read.h``):
--------------------------------------------------------------------------
.. code-block:: c++
#include "hdr/types/ssize_t.h"
#include "src/__support/OSUtil/linux/syscall.h" // For syscall_impl
#include "src/__support/common.h"
#include "src/__support/error_or.h"
#include "src/__support/macros/config.h"
#include <sys/syscall.h> // For syscall numbers
namespace LIBC_NAMESPACE_DECL {
namespace linux_syscalls {
LIBC_INLINE ErrorOr<ssize_t> read(int fd, void *buf, size_t count) {
ssize_t ret = syscall_impl<ssize_t>(SYS_read, fd, buf, count);
if (ret < 0) {
return Error(-static_cast<int>(ret));
}
return ret;
}
} // namespace linux_syscalls
} // namespace LIBC_NAMESPACE_DECL
How to Migrate
==============
1. **Cleanup Existing Implementation**: If the syscall was previously
implemented in ``OSUtil/linux/fcntl.cpp`` (or similar), remove the old
implementation to replace it with the new wrapper.
2. **Create the Wrapper**: Add a new header file in
``src/__support/OSUtil/linux/syscall_wrappers/``.
3. **Update CMake**: Add a ``add_header_library`` target for the new wrapper in
``src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt``.
4. **Refactor Entrypoints**:
* Include the new wrapper header (e.g., ``read.h``).
* Replace direct ``syscall_impl`` calls with
``linux_syscalls::<function_name>``.
* Update the entrypoint's ``DEPENDS`` in ``CMakeLists.txt`` to include the
new wrapper target.

View File

@ -16,7 +16,7 @@ if (NOT ${getrandom_index} EQUAL -1)
message(STATUS "Using getrandom for hashtable randomness")
set(randomness_compile_flags -DLIBC_HASHTABLE_USE_GETRANDOM)
set(randomness_extra_depends
libc.src.__support.OSUtil.linux.getrandom
libc.src.__support.OSUtil.linux.syscall_wrappers.getrandom
libc.hdr.errno_macros)
endif()

View File

@ -15,7 +15,7 @@
#include "src/__support/macros/config.h"
#if defined(LIBC_HASHTABLE_USE_GETRANDOM)
#include "hdr/errno_macros.h"
#include "src/__support/OSUtil/linux/getrandom.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/getrandom.h"
#endif
namespace LIBC_NAMESPACE_DECL {
@ -38,7 +38,7 @@ LIBC_INLINE uint64_t next_random_seed() {
size_t count = sizeof(entropy);
uint8_t *buffer = reinterpret_cast<uint8_t *>(entropy);
while (count > 0) {
auto len = internal::getrandom(buffer, count, 0);
auto len = linux_syscalls::getrandom(buffer, count, 0);
if (!len.has_value()) {
if (len.error() == ENOSYS)
break;

View File

@ -3,6 +3,7 @@ if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_ARCHITECTURE})
endif()
add_subdirectory(${LIBC_TARGET_ARCHITECTURE})
add_subdirectory(syscall_wrappers)
add_object_library(
linux_util

View File

@ -116,27 +116,5 @@ ErrorOr<int> fcntl(int fd, int cmd, void *arg) {
return ret;
}
ErrorOr<int> open(const char *path, int flags, mode_t mode_flags) {
#ifdef SYS_open
int fd = LIBC_NAMESPACE::syscall_impl<int>(SYS_open, path, flags, mode_flags);
#else
int fd = LIBC_NAMESPACE::syscall_impl<int>(SYS_openat, AT_FDCWD, path, flags,
mode_flags);
#endif
if (fd < 0)
return Error(-fd);
return fd;
}
ErrorOr<int> close(int fd) {
int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_close, fd);
if (ret < 0)
return Error(-ret);
return ret;
}
} // namespace internal
} // namespace LIBC_NAMESPACE_DECL

View File

@ -0,0 +1,64 @@
add_header_library(
getrandom
HDRS
getrandom.h
DEPENDS
libc.src.__support.OSUtil.osutil
libc.src.__support.common
libc.src.__support.error_or
libc.src.__support.macros.config
libc.hdr.types.ssize_t
libc.include.sys_syscall
)
add_header_library(
close
HDRS
close.h
DEPENDS
libc.src.__support.OSUtil.osutil
libc.src.__support.common
libc.src.__support.error_or
libc.src.__support.macros.config
libc.include.sys_syscall
)
add_header_library(
read
HDRS
read.h
DEPENDS
libc.src.__support.OSUtil.osutil
libc.src.__support.common
libc.src.__support.error_or
libc.src.__support.macros.config
libc.hdr.types.ssize_t
libc.include.sys_syscall
)
add_header_library(
write
HDRS
write.h
DEPENDS
libc.src.__support.OSUtil.osutil
libc.src.__support.common
libc.src.__support.error_or
libc.src.__support.macros.config
libc.hdr.types.ssize_t
libc.include.sys_syscall
)
add_header_library(
open
HDRS
open.h
DEPENDS
libc.src.__support.OSUtil.osutil
libc.src.__support.common
libc.src.__support.error_or
libc.src.__support.macros.config
libc.hdr.fcntl_macros
libc.hdr.types.mode_t
libc.include.sys_syscall
)

View File

@ -0,0 +1,31 @@
//===-- Implementation header for close -------------------------*- 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_SYSCALL_WRAPPERS_CLOSE_H
#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_CLOSE_H
#include "src/__support/OSUtil/linux/syscall.h" // syscall_impl
#include "src/__support/common.h"
#include "src/__support/error_or.h"
#include "src/__support/macros/config.h"
#include <sys/syscall.h> // For syscall numbers
namespace LIBC_NAMESPACE_DECL {
namespace linux_syscalls {
LIBC_INLINE ErrorOr<int> close(int fd) {
int ret = syscall_impl<int>(SYS_close, fd);
if (ret < 0)
return Error(-static_cast<int>(ret));
return ret;
}
} // namespace linux_syscalls
} // namespace LIBC_NAMESPACE_DECL
#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_CLOSE_H

View File

@ -0,0 +1,33 @@
//===-- Implementation header for getrandom ---------------------*- 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_SYSCALL_WRAPPERS_GETRANDOM_H
#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_GETRANDOM_H
#include "hdr/types/ssize_t.h"
#include "src/__support/OSUtil/linux/syscall.h" // syscall_impl
#include "src/__support/common.h"
#include "src/__support/error_or.h"
#include "src/__support/macros/config.h"
#include <sys/syscall.h> // For syscall numbers
namespace LIBC_NAMESPACE_DECL {
namespace linux_syscalls {
LIBC_INLINE ErrorOr<ssize_t> getrandom(void *buf, size_t buflen,
unsigned int flags) {
ssize_t ret = syscall_impl<ssize_t>(SYS_getrandom, buf, buflen, flags);
if (ret < 0)
return Error(-static_cast<int>(ret));
return ret;
}
} // namespace linux_syscalls
} // namespace LIBC_NAMESPACE_DECL
#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_GETRANDOM_H

View File

@ -0,0 +1,37 @@
//===-- Implementation header for open --------------------------*- 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_SYSCALL_WRAPPERS_OPEN_H
#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_OPEN_H
#include "hdr/fcntl_macros.h"
#include "hdr/types/mode_t.h"
#include "src/__support/OSUtil/linux/syscall.h" // syscall_impl
#include "src/__support/common.h"
#include "src/__support/error_or.h"
#include "src/__support/macros/config.h"
#include <sys/syscall.h> // For syscall numbers
namespace LIBC_NAMESPACE_DECL {
namespace linux_syscalls {
LIBC_INLINE ErrorOr<int> open(const char *path, int flags, mode_t mode_flags) {
#ifdef SYS_open
int fd = syscall_impl<int>(SYS_open, path, flags, mode_flags);
#else
int fd = syscall_impl<int>(SYS_openat, AT_FDCWD, path, flags, mode_flags);
#endif
if (fd < 0)
return Error(-fd);
return fd;
}
} // namespace linux_syscalls
} // namespace LIBC_NAMESPACE_DECL
#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_OPEN_H

View File

@ -1,4 +1,4 @@
//===------------ Implementation of getrandom function ----------*- C++ -*-===//
//===-- Implementation header for read --------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@ -6,8 +6,8 @@
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_GETRANDOM_H
#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_GETRANDOM_H
#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_READ_H
#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_READ_H
#include "hdr/types/ssize_t.h"
#include "src/__support/OSUtil/linux/syscall.h" // syscall_impl
@ -17,19 +17,16 @@
#include <sys/syscall.h> // For syscall numbers
namespace LIBC_NAMESPACE_DECL {
namespace internal {
namespace linux_syscalls {
LIBC_INLINE static ErrorOr<ssize_t> getrandom(void *buf, size_t buflen,
unsigned int flags) {
ssize_t ret =
LIBC_NAMESPACE::syscall_impl<ssize_t>(SYS_getrandom, buf, buflen, flags);
if (ret < 0) {
LIBC_INLINE ErrorOr<ssize_t> read(int fd, void *buf, size_t count) {
ssize_t ret = syscall_impl<ssize_t>(SYS_read, fd, buf, count);
if (ret < 0)
return Error(-static_cast<int>(ret));
}
return ret;
}
} // namespace internal
} // namespace linux_syscalls
} // namespace LIBC_NAMESPACE_DECL
#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_GETRANDOM_H
#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_READ_H

View File

@ -0,0 +1,32 @@
//===-- Implementation header for write -------------------------*- 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_SYSCALL_WRAPPERS_WRITE_H
#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_WRITE_H
#include "hdr/types/ssize_t.h"
#include "src/__support/OSUtil/linux/syscall.h" // syscall_impl
#include "src/__support/common.h"
#include "src/__support/error_or.h"
#include "src/__support/macros/config.h"
#include <sys/syscall.h> // For syscall numbers
namespace LIBC_NAMESPACE_DECL {
namespace linux_syscalls {
LIBC_INLINE ErrorOr<ssize_t> write(int fd, const void *buf, size_t count) {
ssize_t ret = syscall_impl<ssize_t>(SYS_write, fd, buf, count);
if (ret < 0)
return Error(-static_cast<int>(ret));
return ret;
}
} // namespace linux_syscalls
} // namespace LIBC_NAMESPACE_DECL
#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_WRITE_H

View File

@ -31,7 +31,7 @@ add_entrypoint_object(
DEPENDS
libc.hdr.types.mode_t
libc.hdr.fcntl_macros
libc.src.__support.OSUtil.osutil
libc.src.__support.OSUtil.linux.syscall_wrappers.open
libc.src.errno.errno
)

View File

@ -10,7 +10,7 @@
#include "hdr/fcntl_macros.h"
#include "hdr/types/mode_t.h"
#include "src/__support/OSUtil/fcntl.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/open.h"
#include "src/__support/common.h"
#include "src/__support/libc_errno.h"
#include "src/__support/macros/config.h"
@ -29,7 +29,7 @@ LLVM_LIBC_FUNCTION(int, open, (const char *path, int flags, ...)) {
va_end(varargs);
}
auto result = internal::open(path, flags, mode_flags);
auto result = linux_syscalls::open(path, flags, mode_flags);
if (!result.has_value()) {
libc_errno = result.error();

View File

@ -293,8 +293,10 @@ add_entrypoint_object(
HDRS
../shm_open.h
DEPENDS
libc.hdr.fcntl_macros
libc.hdr.types.mode_t
libc.src.errno.errno
libc.src.__support.OSUtil.linux.syscall_wrappers.open
.shm_common
)

View File

@ -10,7 +10,7 @@
#include "hdr/fcntl_macros.h"
#include "hdr/types/mode_t.h"
#include "src/__support/OSUtil/fcntl.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/open.h"
#include "src/__support/libc_errno.h"
#include "src/__support/macros/config.h"
#include "src/sys/mman/linux/shm_common.h"
@ -27,7 +27,7 @@ LLVM_LIBC_FUNCTION(int, shm_open, (const char *name, int oflags, mode_t mode)) {
}
auto open_result =
internal::open(path_result->data(), oflags | DEFAULT_OFLAGS, mode);
linux_syscalls::open(path_result->data(), oflags | DEFAULT_OFLAGS, mode);
if (!open_result.has_value()) {
libc_errno = open_result.error();
return -1;

View File

@ -6,7 +6,6 @@ add_entrypoint_object(
../getrandom.h
DEPENDS
libc.include.sys_random
libc.include.sys_syscall
libc.src.__support.OSUtil.osutil
libc.src.__support.OSUtil.linux.syscall_wrappers.getrandom
libc.src.errno.errno
)

View File

@ -8,8 +8,7 @@
#include "src/sys/random/getrandom.h"
#include "src/__support/OSUtil/linux/getrandom.h"
#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
#include "src/__support/OSUtil/linux/syscall_wrappers/getrandom.h"
#include "src/__support/common.h"
#include "src/__support/error_or.h"
#include "src/__support/libc_errno.h"
@ -19,7 +18,7 @@ namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(ssize_t, getrandom,
(void *buf, size_t buflen, unsigned int flags)) {
auto rand = internal::getrandom(buf, buflen, flags);
auto rand = linux_syscalls::getrandom(buf, buflen, flags);
if (!rand.has_value()) {
libc_errno = static_cast<int>(rand.error());
return -1;

View File

@ -48,8 +48,7 @@ add_entrypoint_object(
../close.h
DEPENDS
libc.include.unistd
libc.include.sys_syscall
libc.src.__support.OSUtil.osutil
libc.src.__support.OSUtil.linux.syscall_wrappers.close
libc.src.errno.errno
)
@ -503,8 +502,7 @@ add_entrypoint_object(
libc.hdr.types.ssize_t
libc.hdr.fcntl_macros
libc.include.unistd
libc.include.sys_syscall
libc.src.__support.OSUtil.osutil
libc.src.__support.OSUtil.linux.syscall_wrappers.read
libc.src.__support.macros.sanitizer
libc.src.errno.errno
)
@ -675,8 +673,7 @@ add_entrypoint_object(
libc.hdr.types.ssize_t
libc.hdr.fcntl_macros
libc.include.unistd
libc.include.sys_syscall
libc.src.__support.OSUtil.osutil
libc.src.__support.OSUtil.linux.syscall_wrappers.write
libc.src.errno.errno
)

View File

@ -8,16 +8,15 @@
#include "src/unistd/close.h"
#include "src/__support/OSUtil/fcntl.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/close.h"
#include "src/__support/common.h"
#include "src/__support/libc_errno.h"
#include "src/__support/macros/config.h"
#include <sys/syscall.h> // For syscall numbers.
namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(int, close, (int fd)) {
auto result = internal::close(fd);
auto result = linux_syscalls::close(fd);
if (!result.has_value()) {
libc_errno = result.error();

View File

@ -8,25 +8,24 @@
#include "src/unistd/read.h"
#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
#include "src/__support/OSUtil/linux/syscall_wrappers/read.h"
#include "src/__support/common.h"
#include "src/__support/libc_errno.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/sanitizer.h" // for MSAN_UNPOISON
#include <sys/syscall.h> // For syscall numbers.
namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(ssize_t, read, (int fd, void *buf, size_t count)) {
ssize_t ret = LIBC_NAMESPACE::syscall_impl<ssize_t>(SYS_read, fd, buf, count);
if (ret < 0) {
libc_errno = static_cast<int>(-ret);
auto result = linux_syscalls::read(fd, buf, count);
if (!result.has_value()) {
libc_errno = result.error();
return -1;
}
// The cast is important since there is a check that dereferences the pointer
// which fails on void*.
MSAN_UNPOISON(reinterpret_cast<char *>(buf), count);
return ret;
return result.value();
}
} // namespace LIBC_NAMESPACE_DECL

View File

@ -8,23 +8,20 @@
#include "src/unistd/write.h"
#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
#include "src/__support/OSUtil/linux/syscall_wrappers/write.h"
#include "src/__support/common.h"
#include "src/__support/libc_errno.h"
#include "src/__support/macros/config.h"
#include <sys/syscall.h> // For syscall numbers.
namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(ssize_t, write, (int fd, const void *buf, size_t count)) {
ssize_t ret =
LIBC_NAMESPACE::syscall_impl<ssize_t>(SYS_write, fd, buf, count);
if (ret < 0) {
libc_errno = static_cast<int>(-ret);
auto result = linux_syscalls::write(fd, buf, count);
if (!result.has_value()) {
libc_errno = result.error();
return -1;
}
return ret;
return result.value();
}
} // namespace LIBC_NAMESPACE_DECL