diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td --- a/libc/config/linux/api.td +++ b/libc/config/linux/api.td @@ -1,5 +1,7 @@ include "config/public_api.td" +include "spec/linux.td" +include "spec/posix.td" include "spec/stdc.td" def FloatT : TypeDecl<"float_t"> { @@ -28,6 +30,13 @@ }]; } +def ErrnoMacro : MacroDef<"errno"> { + let Defn = [{ + int *__errno_location(); + #define errno (*__errno_location()) + }]; +} + def MathAPI : PublicAPI<"math.h"> { let Functions = [ "acos", @@ -83,3 +92,22 @@ "snprintf", ]; } + +def ErrnoAPI : PublicAPI<"errno.h"> { + let Macros = [ + ErrnoMacro, + // We largely depend on linux/errno.h to give us the + // various error macro definitions. However, some libc + // implementations have chosen to provide definitions + // for some of the error macros to account for the ones + // missing in linux/errno.h. There is no harm in doing + // the same here if we define the macros only when they + // are not already defined. + MacroDefineIfNot<"ENOTSUP", "EOPNOTSUPP">, + MacroDefineIfNot<"ECANCELED", "125">, + MacroDefineIfNot<"EOWNERDEAD", "130">, + MacroDefineIfNot<"ENOTRECOVERABLE", "131">, + MacroDefineIfNot<"ERFKILL", "132">, + MacroDefineIfNot<"EHWPOISON", "133">, + ]; +} diff --git a/libc/config/linux/errno.h.in b/libc/config/linux/errno.h.in new file mode 100644 --- /dev/null +++ b/libc/config/linux/errno.h.in @@ -0,0 +1,11 @@ +//===---------------- Linux specific errno.h definitions ------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +%%begin() + +#include diff --git a/libc/config/public_api.td b/libc/config/public_api.td --- a/libc/config/public_api.td +++ b/libc/config/public_api.td @@ -10,6 +10,16 @@ string Defn = ""; } +class SimpleMacroDef : MacroDef { + let Defn = !strconcat("#define ", name, " ", value); +} + +class MacroDefineIfNot : MacroDef { + let Defn = !strconcat("#ifndef ", name, "\n", + "#define " , name, " ", value, "\n", + "#endif // ", name); +} + class PublicAPI { string HeaderName = name; list Macros = []; diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt --- a/libc/include/CMakeLists.txt +++ b/libc/include/CMakeLists.txt @@ -28,3 +28,13 @@ DEPENDS llvm_libc_common_h ) + +add_gen_header( + errno_h + DEF_FILE errno.h.def + PARAMS + platform_errno=../config/${LIBC_TARGET_OS}/errno.h.in + GEN_HDR errno.h + DATA_FILES + ../config/${LIBC_TARGET_OS}/errno.h.in +) diff --git a/libc/include/errno.h.def b/libc/include/errno.h.def new file mode 100644 --- /dev/null +++ b/libc/include/errno.h.def @@ -0,0 +1,18 @@ +//===---------------- C standard library header errno.h ------------------===// +// +// 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_ERRNO_H +#define LLVM_LIBC_ERRNO_H + +#include <__llvm-libc-common.h> + +%%include_file(${platform_errno}) + +%%public_api() + +#endif // LLVM_LIBC_ERRNO_H diff --git a/libc/lib/CMakeLists.txt b/libc/lib/CMakeLists.txt --- a/libc/lib/CMakeLists.txt +++ b/libc/lib/CMakeLists.txt @@ -2,8 +2,10 @@ add_entrypoint_library( llvmlibc DEPENDS + # errno.h entrypoints + __errno_location + # string.h entrypoints - ## C standard library entrypoints strcpy strcat ) diff --git a/libc/spec/linux.td b/libc/spec/linux.td new file mode 100644 --- /dev/null +++ b/libc/spec/linux.td @@ -0,0 +1,66 @@ +def Linux : StandardSpec<"Linux"> { + HeaderSpec Errno = HeaderSpec< + "errno.h", + [ + Macro<"ENOMEDIUM">, + Macro<"ENOTBLK">, + Macro<"EMEDIUMTYPE">, + Macro<"EBADSLT">, + Macro<"ECHRNG">, + Macro<"ERFKILL">, + Macro<"EUSERS">, + Macro<"EBADR">, + Macro<"EL3HLT">, + Macro<"ENOTUNIQ">, + Macro<"EXFULL">, + Macro<"EHOSTDOWN">, + Macro<"EL3RST">, + Macro<"ENOPKG">, + Macro<"ENOCSI">, + Macro<"EUNATCH">, + Macro<"EREMCHG">, + Macro<"ETOOMANYREFS">, + Macro<"EL2HLT">, + Macro<"EBADFD">, + Macro<"EREMOTEIO">, + Macro<"ENAVAIL">, + Macro<"ELIBEXEC">, + Macro<"ESHUTDOWN">, + Macro<"ENOKEY">, + Macro<"ESTRPIPE">, + Macro<"EKEYREJECTED">, + Macro<"ESRMNT">, + Macro<"EKEYREVOKED">, + Macro<"EBADE">, + Macro<"ELIBBAD">, + Macro<"EISNAM">, + Macro<"EBFONT">, + Macro<"EPFNOSUPPORT">, + Macro<"EREMOTE">, + Macro<"EDEADLOCK">, + Macro<"ENONET">, + Macro<"EDOTDOT">, + Macro<"EKEYEXPIRED">, + Macro<"ELIBSCN">, + Macro<"ERESTART">, + Macro<"EBADRQC">, + Macro<"EUCLEAN">, + Macro<"ENOANO">, + Macro<"ELIBACC">, + Macro<"EHWPOISON">, + Macro<"ELIBMAX">, + Macro<"ESOCKTNOSUPPORT">, + Macro<"ENOTNAM">, + Macro<"ELNRNG">, + Macro<"EL2NSYNC">, + Macro<"EADV">, + Macro<"ECOMM">, + ], + [], // Types + [] // Functions + >; + + let Headers = [ + Errno, + ]; +} diff --git a/libc/spec/posix.td b/libc/spec/posix.td new file mode 100644 --- /dev/null +++ b/libc/spec/posix.td @@ -0,0 +1,91 @@ +def POSIX : StandardSpec<"POSIX"> { + HeaderSpec Errno = HeaderSpec< + "errno.h", + [ + Macro<"E2BIG">, + Macro<"EACCES">, + Macro<"EADDRINUSE">, + Macro<"EADDRNOTAVAIL">, + Macro<"EAFNOSUPPORT">, + Macro<"EAGAIN">, + Macro<"EALREADY">, + Macro<"EBADF">, + Macro<"EBADMSG">, + Macro<"EBUSY">, + Macro<"ECANCELED">, + Macro<"ECHILD">, + Macro<"ECONNABORTED">, + Macro<"ECONNREFUSED">, + Macro<"ECONNRESET">, + Macro<"EDEADLK">, + Macro<"EDESTADDRREQ">, + Macro<"EDQUOT">, + Macro<"EEXIST">, + Macro<"EFAULT">, + Macro<"EFBIG">, + Macro<"EHOSTUNREACH">, + Macro<"EIDRM">, + Macro<"EINPROGRESS">, + Macro<"EINTR">, + Macro<"EINVAL">, + Macro<"EIO">, + Macro<"EISCONN">, + Macro<"EISDIR">, + Macro<"ELOOP">, + Macro<"EMFILE">, + Macro<"EMLINK">, + Macro<"EMSGSIZE">, + Macro<"EMULTIHOP">, + Macro<"ENAMETOOLONG">, + Macro<"ENETDOWN">, + Macro<"ENETRESET">, + Macro<"ENETUNREACH">, + Macro<"ENFILE">, + Macro<"ENOBUFS">, + Macro<"ENODATA">, + Macro<"ENODEV">, + Macro<"ENOENT">, + Macro<"ENOEXEC">, + Macro<"ENOLCK">, + Macro<"ENOLINK">, + Macro<"ENOMEM">, + Macro<"ENOMSG">, + Macro<"ENOPROTOOPT">, + Macro<"ENOSPC">, + Macro<"ENOSR">, + Macro<"ENOSTR">, + Macro<"ENOSYS">, + Macro<"ENOTCONN">, + Macro<"ENOTDIR">, + Macro<"ENOTEMPTY">, + Macro<"ENOTRECOVERABLE">, + Macro<"ENOTSOCK">, + Macro<"ENOTSUP">, + Macro<"ENOTTY">, + Macro<"ENXIO">, + Macro<"EOPNOTSUPP">, + Macro<"EOVERFLOW">, + Macro<"EOWNERDEAD">, + Macro<"EPERM">, + Macro<"EPIPE">, + Macro<"EPROTO">, + Macro<"EPROTONOSUPPORT">, + Macro<"EPROTOTYPE">, + Macro<"EROFS">, + Macro<"ESPIPE">, + Macro<"ESRCH">, + Macro<"ESTALE">, + Macro<"ETIME">, + Macro<"ETIMEDOUT">, + Macro<"ETXTBSY">, + Macro<"EWOULDBLOCK">, + Macro<"EXDEV">, + ], + [], // Types + [] // Functions + >; + + let Headers = [ + Errno, + ]; +} diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -170,7 +170,20 @@ ] >; + HeaderSpec Errno = HeaderSpec< + "errno.h", + [ + Macro<"errno">, + Macro<"EDOM">, + Macro<"EILSEQ">, + Macro<"ERANGE">, + ], + [], // Types + [] // Functions + >; + let Headers = [ + Errno, Math, String, StdIO, diff --git a/libc/src/CMakeLists.txt b/libc/src/CMakeLists.txt --- a/libc/src/CMakeLists.txt +++ b/libc/src/CMakeLists.txt @@ -1,4 +1,5 @@ -add_subdirectory(string) +add_subdirectory(errno) add_subdirectory(math) +add_subdirectory(string) add_subdirectory(__support) diff --git a/libc/src/errno/CMakeLists.txt b/libc/src/errno/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/src/errno/CMakeLists.txt @@ -0,0 +1,19 @@ +add_entrypoint_object( + __errno_location + SRCS + errno_location.cpp + HDRS + llvmlibc_errno.h +) + +add_custom_target(libc_errno_unittests) + +add_libc_unittest( + errno_test + SUITE + libc_errno_unittests + SRCS + errno_test.cpp + DEPENDS + __errno_location +) diff --git a/libc/src/errno/errno_location.cpp b/libc/src/errno/errno_location.cpp new file mode 100644 --- /dev/null +++ b/libc/src/errno/errno_location.cpp @@ -0,0 +1,22 @@ +//===----------------- Implementation of __errno_location -----------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/errno/llvmlibc_errno.h" + +#include "src/__support/common.h" + +namespace __llvm_libc { + +static thread_local int __errno = 0; + +// __errno_location is not really an entry point but we still want it to behave +// like an entry point because the errno macro resolves to the C symbol +// "__errno_location". +int *LLVM_LIBC_ENTRYPOINT(__errno_location)() { return &__errno; } + +} // namespace __llvm_libc diff --git a/libc/src/errno/errno_test.cpp b/libc/src/errno/errno_test.cpp new file mode 100644 --- /dev/null +++ b/libc/src/errno/errno_test.cpp @@ -0,0 +1,17 @@ +//===---------------------- Unittests for errno --------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/errno/llvmlibc_errno.h" + +#include "gtest/gtest.h" + +TEST(ErrnoTest, Basic) { + int test_val = 123; + llvmlibc_errno = test_val; + ASSERT_EQ(test_val, llvmlibc_errno); +} diff --git a/libc/src/errno/llvmlibc_errno.h b/libc/src/errno/llvmlibc_errno.h new file mode 100644 --- /dev/null +++ b/libc/src/errno/llvmlibc_errno.h @@ -0,0 +1,22 @@ +//===------------------ Implementation header for errno -------------------===// +// +// 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_LLVMLIBC_ERRNO_H +#define LLVM_LIBC_SRC_ERRNO_LLVMLIBC_ERRNO_H + +// Internal code should use this and not use the errno macro from the +// public header. +#define llvmlibc_errno (*__llvm_libc::__errno_location()) + +namespace __llvm_libc { + +int *__errno_location(); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_ERRNO_LLVMLIBC_ERRNO_H