From 69258491d201bf6d96a8a9bac2ea80a1b14d9cd4 Mon Sep 17 00:00:00 2001 From: Petr Hosek Date: Sat, 13 Jul 2024 10:52:42 -0700 Subject: [PATCH] [libc] Support configurable errno modes (#98287) Rather than selecting the errno implementation based on the platform which doesn't provide the necessary flexibility, make it configurable. The errno value location is returned by `int *__llvm_libc_errno()` which is a common design used by other C libraries. --- libc/config/baremetal/config.json | 5 ++ libc/config/config.json | 6 ++ libc/config/gpu/config.json | 5 ++ libc/docs/configure.rst | 2 + libc/include/errno.h.def | 18 ++--- libc/src/errno/CMakeLists.txt | 6 ++ libc/src/errno/errno.h | 14 ++++ libc/src/errno/libc_errno.cpp | 111 +++++++++++++++++++++--------- libc/src/errno/libc_errno.h | 1 + 9 files changed, 122 insertions(+), 46 deletions(-) create mode 100644 libc/src/errno/errno.h diff --git a/libc/config/baremetal/config.json b/libc/config/baremetal/config.json index dda4c4242575..b7426dd341d9 100644 --- a/libc/config/baremetal/config.json +++ b/libc/config/baremetal/config.json @@ -1,4 +1,9 @@ { + "errno": { + "LIBC_CONF_ERRNO_MODE": { + "value": "LIBC_ERRNO_MODE_EXTERNAL" + } + }, "printf": { "LIBC_CONF_PRINTF_DISABLE_FLOAT": { "value": true diff --git a/libc/config/config.json b/libc/config/config.json index e8feab20175f..3a9c08d19544 100644 --- a/libc/config/config.json +++ b/libc/config/config.json @@ -1,4 +1,10 @@ { + "errno": { + "LIBC_CONF_ERRNO_MODE": { + "value": "", + "doc": "The implementation used for errno, acceptable values are LIBC_ERRNO_MODE_UNDEFINED, LIBC_ERRNO_MODE_THREAD_LOCAL, LIBC_ERRNO_MODE_SHARED, LIBC_ERRNO_MODE_EXTERNAL, and LIBC_ERRNO_MODE_SYSTEM." + } + }, "printf": { "LIBC_CONF_PRINTF_DISABLE_FLOAT": { "value": false, diff --git a/libc/config/gpu/config.json b/libc/config/gpu/config.json index 71107d26ea7a..954163947b8a 100644 --- a/libc/config/gpu/config.json +++ b/libc/config/gpu/config.json @@ -1,4 +1,9 @@ { + "errno": { + "LIBC_CONF_ERRNO_MODE": { + "value": "LIBC_ERRNO_MODE_SHARED" + } + }, "printf": { "LIBC_CONF_PRINTF_DISABLE_FLOAT": { "value": true diff --git a/libc/docs/configure.rst b/libc/docs/configure.rst index 9c641ef94570..24ef2ef189ff 100644 --- a/libc/docs/configure.rst +++ b/libc/docs/configure.rst @@ -28,6 +28,8 @@ to learn about the defaults for your platform and target. * **"codegen" options** - ``LIBC_CONF_ENABLE_STRONG_STACK_PROTECTOR``: Enable -fstack-protector-strong to defend against stack smashing attack. - ``LIBC_CONF_KEEP_FRAME_POINTER``: Keep frame pointer in functions for better debugging experience. +* **"errno" options** + - ``LIBC_CONF_ERRNO_MODE``: The implementation used for errno, acceptable values are LIBC_ERRNO_MODE_UNDEFINED, LIBC_ERRNO_MODE_THREAD_LOCAL, LIBC_ERRNO_MODE_SHARED, LIBC_ERRNO_MODE_EXTERNAL, and LIBC_ERRNO_MODE_SYSTEM. * **"malloc" options** - ``LIBC_CONF_FREELIST_MALLOC_BUFFER_SIZE``: Default size for the constinit freelist buffer used for the freelist malloc implementation (default 1o 1GB). * **"math" options** diff --git a/libc/include/errno.h.def b/libc/include/errno.h.def index 1f7120e63bfc..aa1f6c9e4844 100644 --- a/libc/include/errno.h.def +++ b/libc/include/errno.h.def @@ -25,18 +25,12 @@ #include "llvm-libc-macros/generic-error-number-macros.h" #endif -#if defined(__AMDGPU__) || defined(__NVPTX__) -extern int __llvmlibc_errno; // Not thread_local! -#else -#ifdef __cplusplus -extern "C" { -extern thread_local int __llvmlibc_errno; -} -#else -extern _Thread_local int __llvmlibc_errno; -#endif // __cplusplus -#endif +__BEGIN_C_DECLS -#define errno __llvmlibc_errno +int *__llvm_libc_errno(void) __NOEXCEPT; + +__END_C_DECLS + +#define errno (*__llvm_libc_errno()) #endif // LLVM_LIBC_ERRNO_H diff --git a/libc/src/errno/CMakeLists.txt b/libc/src/errno/CMakeLists.txt index 2622e51261cc..b05fd4e31ff6 100644 --- a/libc/src/errno/CMakeLists.txt +++ b/libc/src/errno/CMakeLists.txt @@ -9,14 +9,20 @@ if(LLVM_LIBC_FULL_BUILD) set(full_build_flag "-DLIBC_FULL_BUILD") endif() +if(LIBC_CONF_ERRNO_MODE) + set(errno_config_copts "-DLIBC_ERRNO_MODE=${LIBC_CONF_ERRNO_MODE}") +endif() + add_entrypoint_object( errno SRCS libc_errno.cpp HDRS + errno.h libc_errno.h # Include this COMPILE_OPTIONS ${full_build_flag} + ${errno_config_copts} DEPENDS libc.hdr.errno_macros libc.src.__support.common diff --git a/libc/src/errno/errno.h b/libc/src/errno/errno.h new file mode 100644 index 000000000000..a2df93513ec6 --- /dev/null +++ b/libc/src/errno/errno.h @@ -0,0 +1,14 @@ +//===-- Implementation header for errno -------------------------*- 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_ERRNO_ERRNO_H +#define LLVM_LIBC_SRC_ERRNO_ERRNO_H + +extern "C" int *__llvm_libc_errno(); + +#endif // LLVM_LIBC_SRC_ERRNO_ERRNO_H diff --git a/libc/src/errno/libc_errno.cpp b/libc/src/errno/libc_errno.cpp index 341636f0495c..f7bd3a3b9eb4 100644 --- a/libc/src/errno/libc_errno.cpp +++ b/libc/src/errno/libc_errno.cpp @@ -7,47 +7,90 @@ //===----------------------------------------------------------------------===// #include "libc_errno.h" -#include "src/__support/CPP/atomic.h" +#include "src/errno/errno.h" #include "src/__support/macros/config.h" -#ifdef LIBC_TARGET_ARCH_IS_GPU -// LIBC_THREAD_LOCAL on GPU currently does nothing. So essentially this is just -// a global errno for gpu to use for now. -extern "C" { -LIBC_THREAD_LOCAL LIBC_NAMESPACE::cpp::Atomic __llvmlibc_errno; -} - -void LIBC_NAMESPACE::Errno::operator=(int a) { - __llvmlibc_errno.store(a, cpp::MemoryOrder::RELAXED); -} -LIBC_NAMESPACE::Errno::operator int() { - return __llvmlibc_errno.load(cpp::MemoryOrder::RELAXED); -} - -#elif !defined(LIBC_COPT_PUBLIC_PACKAGING) -// This mode is for unit testing. We just use our internal errno. -LIBC_THREAD_LOCAL int __llvmlibc_internal_errno; - -void LIBC_NAMESPACE::Errno::operator=(int a) { __llvmlibc_internal_errno = a; } -LIBC_NAMESPACE::Errno::operator int() { return __llvmlibc_internal_errno; } - -#elif defined(LIBC_FULL_BUILD) -// This mode is for public libc archive, hermetic, and integration tests. -// In full build mode, we provide the errno storage ourselves. -extern "C" { -LIBC_THREAD_LOCAL int __llvmlibc_errno; -} - -void LIBC_NAMESPACE::Errno::operator=(int a) { __llvmlibc_errno = a; } -LIBC_NAMESPACE::Errno::operator int() { return __llvmlibc_errno; } +// libc never stores a value; `errno` macro uses get link-time failure. +#define LIBC_ERRNO_MODE_UNDEFINED 1 +// libc maintains per-thread state (requires C++ `thread_local` support). +#define LIBC_ERRNO_MODE_THREAD_LOCAL 2 +// libc maintains shared state used by all threads, contrary to standard C +// semantics unless always single-threaded; nothing prevents data races. +#define LIBC_ERRNO_MODE_SHARED 3 +// libc doesn't maintain any internal state, instead the embedder must define +// `int *__llvm_libc_errno(void);` C function. +#define LIBC_ERRNO_MODE_EXTERNAL 4 +// libc uses system `` `errno` macro directly in the overlay mode; in +// fullbuild mode, effectively the same as `LIBC_ERRNO_MODE_EXTERNAL`. +#define LIBC_ERRNO_MODE_SYSTEM 5 +#ifndef LIBC_ERRNO_MODE +#if defined(LIBC_FULL_BUILD) || !defined(LIBC_COPT_PUBLIC_PACKAGING) +#define LIBC_ERRNO_MODE LIBC_ERRNO_MODE_THREAD_LOCAL #else -void LIBC_NAMESPACE::Errno::operator=(int a) { errno = a; } -LIBC_NAMESPACE::Errno::operator int() { return errno; } +#define LIBC_ERRNO_MODE LIBC_ERRNO_MODE_SYSTEM +#endif +#endif // LIBC_ERRNO_MODE -#endif // LIBC_FULL_BUILD +#if LIBC_ERRNO_MODE != LIBC_ERRNO_MODE_UNDEFINED && \ + LIBC_ERRNO_MODE != LIBC_ERRNO_MODE_THREAD_LOCAL && \ + LIBC_ERRNO_MODE != LIBC_ERRNO_MODE_SHARED && \ + LIBC_ERRNO_MODE != LIBC_ERRNO_MODE_EXTERNAL && \ + LIBC_ERRNO_MODE != LIBC_ERRNO_MODE_SYSTEM +#error LIBC_ERRNO_MODE must be one of the following values: \ +LIBC_ERRNO_MODE_UNDEFINED, \ +LIBC_ERRNO_MODE_THREAD_LOCAL, \ +LIBC_ERRNO_MODE_SHARED, \ +LIBC_ERRNO_MODE_EXTERNAL, \ +LIBC_ERRNO_MODE_SYSTEM +#endif namespace LIBC_NAMESPACE_DECL { + // Define the global `libc_errno` instance. Errno libc_errno; + +#if LIBC_ERRNO_MODE == LIBC_ERRNO_MODE_UNDEFINED + +void Errno::operator=(int) {} +Errno::operator int() { return 0; } + +#elif LIBC_ERRNO_MODE == LIBC_ERRNO_MODE_THREAD_LOCAL + +namespace { +LIBC_THREAD_LOCAL int thread_errno; +} + +extern "C" { +int *__llvm_libc_errno() { return &thread_errno; } +} + +void Errno::operator=(int a) { thread_errno = a; } +Errno::operator int() { return thread_errno; } + +#elif LIBC_ERRNO_MODE == LIBC_ERRNO_MODE_SHARED + +namespace { +int shared_errno; +} + +extern "C" { +int *__llvm_libc_errno() { return &shared_errno; } +} + +void Errno::operator=(int a) { shared_errno = a; } +Errno::operator int() { return shared_errno; } + +#elif LIBC_ERRNO_MODE == LIBC_ERRNO_MODE_EXTERNAL + +void Errno::operator=(int a) { *__llvm_libc_errno() = a; } +Errno::operator int() { return *__llvm_libc_errno(); } + +#elif LIBC_ERRNO_MODE == LIBC_ERRNO_MODE_SYSTEM + +void Errno::operator=(int a) { errno = a; } +Errno::operator int() { return errno; } + +#endif + } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/errno/libc_errno.h b/libc/src/errno/libc_errno.h index 8d28a01c8b7b..c6c6b20b4825 100644 --- a/libc/src/errno/libc_errno.h +++ b/libc/src/errno/libc_errno.h @@ -32,6 +32,7 @@ // - Still depend on libc.src.errno.errno namespace LIBC_NAMESPACE_DECL { + struct Errno { void operator=(int); operator int();