diff --git a/libc/config/baremetal/config.json b/libc/config/baremetal/config.json index dda4c424257556..b7426dd341d977 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 e8feab20175f4a..3a9c08d195445a 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 71107d26ea7ab3..954163947b8a48 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 9c641ef94570f4..24ef2ef189ffd9 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 1f7120e63bfc93..aa1f6c9e484444 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 + +int *__llvm_libc_errno(void) __NOEXCEPT; + +__END_C_DECLS -#define errno __llvmlibc_errno +#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 2622e51261cc3a..b05fd4e31ff68e 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 00000000000000..a2df93513ec625 --- /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 341636f0495c1b..f7bd3a3b9eb4b6 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; -} +// 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 +#define LIBC_ERRNO_MODE LIBC_ERRNO_MODE_SYSTEM +#endif +#endif // LIBC_ERRNO_MODE + +#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; } -void LIBC_NAMESPACE::Errno::operator=(int a) { - __llvmlibc_errno.store(a, cpp::MemoryOrder::RELAXED); +#elif LIBC_ERRNO_MODE == LIBC_ERRNO_MODE_THREAD_LOCAL + +namespace { +LIBC_THREAD_LOCAL int thread_errno; } -LIBC_NAMESPACE::Errno::operator int() { - return __llvmlibc_errno.load(cpp::MemoryOrder::RELAXED); + +extern "C" { +int *__llvm_libc_errno() { return &thread_errno; } } -#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 Errno::operator=(int a) { thread_errno = a; } +Errno::operator int() { return thread_errno; } -void LIBC_NAMESPACE::Errno::operator=(int a) { __llvmlibc_internal_errno = a; } -LIBC_NAMESPACE::Errno::operator int() { return __llvmlibc_internal_errno; } +#elif LIBC_ERRNO_MODE == LIBC_ERRNO_MODE_SHARED + +namespace { +int shared_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; +int *__llvm_libc_errno() { return &shared_errno; } } -void LIBC_NAMESPACE::Errno::operator=(int a) { __llvmlibc_errno = a; } -LIBC_NAMESPACE::Errno::operator int() { return __llvmlibc_errno; } +void Errno::operator=(int a) { shared_errno = a; } +Errno::operator int() { return shared_errno; } -#else -void LIBC_NAMESPACE::Errno::operator=(int a) { errno = a; } -LIBC_NAMESPACE::Errno::operator int() { return errno; } +#elif LIBC_ERRNO_MODE == LIBC_ERRNO_MODE_EXTERNAL -#endif // LIBC_FULL_BUILD +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 { -// Define the global `libc_errno` instance. -Errno libc_errno; } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/errno/libc_errno.h b/libc/src/errno/libc_errno.h index 8d28a01c8b7bb9..c6c6b20b482513 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();