diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -57,6 +57,7 @@ # string.h entrypoints that depend on malloc libc.src.string.strdup libc.src.string.strndup + libc.src.string.strerror # inttypes.h entrypoints libc.src.inttypes.imaxabs diff --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt --- a/libc/src/__support/CMakeLists.txt +++ b/libc/src/__support/CMakeLists.txt @@ -72,6 +72,19 @@ libc.src.errno.errno ) + +add_object_library( + error_to_string + HDRS + error_to_string.h + SRCS + error_to_string.cpp + DEPENDS + libc.include.errno + libc.src.__support.CPP.string_view + libc.src.__support.CPP.stringstream +) + add_header_library( integer_operations HDRS diff --git a/libc/src/__support/CPP/stringstream.h b/libc/src/__support/CPP/stringstream.h --- a/libc/src/__support/CPP/stringstream.h +++ b/libc/src/__support/CPP/stringstream.h @@ -26,7 +26,7 @@ size_t write_ptr = 0; // The current write pointer bool err = false; // If an error occurs while writing - void write(const char *bytes, size_t size) { + constexpr void write(const char *bytes, size_t size) { size_t i = 0; const size_t data_size = data.size(); for (; write_ptr < data_size && i < size; ++i, ++write_ptr) @@ -47,26 +47,26 @@ // null terminator was not explicitly written, then the return value // will not include one. In order to produce a string_view to a null // terminated string, write ENDS explicitly. - string_view str() const { return string_view(data.data(), write_ptr); } + constexpr string_view str() const { + return string_view(data.data(), write_ptr); + } // Write the characters from |str| to the stream. - StringStream &operator<<(string_view str) { + constexpr StringStream &operator<<(string_view str) { write(str.data(), str.size()); return *this; } // Write the |val| as string. template , int> = 0> - StringStream &operator<<(T val) { + constexpr StringStream &operator<<(T val) { char buffer[IntegerToString::dec_bufsize()]; - auto int_to_str = IntegerToString::dec(val, buffer); - if (int_to_str) - return operator<<(*int_to_str); - return *this; + auto int_to_str = IntegerToString::const_convert<10, int>(val, buffer); + return operator<<(int_to_str); } template , int> = 0> - StringStream &operator<<(T) { + constexpr StringStream &operator<<(T) { // If this specialization gets activated, then the static_assert will // trigger a compile error about missing floating point number support. static_assert(!is_floating_point_v, @@ -76,20 +76,20 @@ // Write a null-terminated string. The terminating null character is not // written to allow stremaing to continue. - StringStream &operator<<(const char *str) { + constexpr StringStream &operator<<(const char *str) { return operator<<(string_view(str)); } // Write a single character. - StringStream &operator<<(char a) { + constexpr StringStream &operator<<(char a) { write(&a, 1); return *this; } // Return true if any write operation(s) failed due to insufficient size. - bool overflow() const { return err; } + constexpr bool overflow() const { return err; } - size_t bufsize() const { return data.size(); } + constexpr size_t bufsize() const { return data.size(); } }; } // namespace cpp diff --git a/libc/src/__support/error_to_string.h b/libc/src/__support/error_to_string.h new file mode 100644 --- /dev/null +++ b/libc/src/__support/error_to_string.h @@ -0,0 +1,21 @@ +//===-- Definition of a class for mapping errors to strings -----*- 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 +// +//===----------------------------------------------------------------------===// + +#include "src/__support/CPP/string_view.h" +#include "src/__support/integer_to_string.h" + +#ifndef LLVM_LIBC_SRC_SUPPORT_ERROR_TO_STRING +#define LLVM_LIBC_SRC_SUPPORT_ERROR_TO_STRING + +namespace __llvm_libc { + +cpp::string_view get_error_string(int err_num); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SUPPORT_ERROR_TO_STRING diff --git a/libc/src/__support/error_to_string.cpp b/libc/src/__support/error_to_string.cpp new file mode 100644 --- /dev/null +++ b/libc/src/__support/error_to_string.cpp @@ -0,0 +1,251 @@ +//===-- Implementation of a class for mapping errors to strings -----------===// +// +// 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/__support/error_to_string.h" +#include "src/__support/CPP/string_view.h" +#include "src/__support/CPP/stringstream.h" +#include +#include + +namespace __llvm_libc { +namespace internal { + +constexpr size_t max_buff_size() { + constexpr size_t unknown_str_len = sizeof("Unknown error"); + constexpr size_t max_num_len = + __llvm_libc::IntegerToString::dec_bufsize(); + // the buffer should be able to hold "Unknown error" + ' ' + num_str + return (unknown_str_len + 1 + max_num_len) * sizeof(char); +} + +// This is to hold error strings that have to be custom built. It may be +// rewritten on every call to strerror (or other error to string function). +constexpr size_t BUFFER_SIZE = max_buff_size(); +thread_local char error_buffer[BUFFER_SIZE]; + +struct ErrMsgMapping { + int err_num; + cpp::string_view err_msg; + +public: + constexpr ErrMsgMapping(int num, const char *msg) + : err_num(num), err_msg(msg) { + ; + } +}; + +constexpr ErrMsgMapping raw_err_array[] = { + ErrMsgMapping(0, "Success"), + ErrMsgMapping(EPERM, "Operation not permitted"), + ErrMsgMapping(ENOENT, "No such file or directory"), + ErrMsgMapping(ESRCH, "No such process"), + ErrMsgMapping(EINTR, "Interrupted system call"), + ErrMsgMapping(EIO, "Input/output error"), + ErrMsgMapping(ENXIO, "No such device or address"), + ErrMsgMapping(E2BIG, "Argument list too long"), + ErrMsgMapping(ENOEXEC, "Exec format error"), + ErrMsgMapping(EBADF, "Bad file descriptor"), + ErrMsgMapping(ECHILD, "No child processes"), + ErrMsgMapping(EAGAIN, "Resource temporarily unavailable"), + ErrMsgMapping(ENOMEM, "Cannot allocate memory"), + ErrMsgMapping(EACCES, "Permission denied"), + ErrMsgMapping(EFAULT, "Bad address"), + ErrMsgMapping(ENOTBLK, "Block device required"), + ErrMsgMapping(EBUSY, "Device or resource busy"), + ErrMsgMapping(EEXIST, "File exists"), + ErrMsgMapping(EXDEV, "Invalid cross-device link"), + ErrMsgMapping(ENODEV, "No such device"), + ErrMsgMapping(ENOTDIR, "Not a directory"), + ErrMsgMapping(EISDIR, "Is a directory"), + ErrMsgMapping(EINVAL, "Invalid argument"), + ErrMsgMapping(ENFILE, "Too many open files in system"), + ErrMsgMapping(EMFILE, "Too many open files"), + ErrMsgMapping(ENOTTY, "Inappropriate ioctl for device"), + ErrMsgMapping(ETXTBSY, "Text file busy"), + ErrMsgMapping(EFBIG, "File too large"), + ErrMsgMapping(ENOSPC, "No space left on device"), + ErrMsgMapping(ESPIPE, "Illegal seek"), + ErrMsgMapping(EROFS, "Read-only file system"), + ErrMsgMapping(EMLINK, "Too many links"), + ErrMsgMapping(EPIPE, "Broken pipe"), + ErrMsgMapping(EDOM, "Numerical argument out of domain"), + ErrMsgMapping(ERANGE, "Numerical result out of range"), + ErrMsgMapping(EDEADLK, "Resource deadlock avoided"), + ErrMsgMapping(ENAMETOOLONG, "File name too long"), + ErrMsgMapping(ENOLCK, "No locks available"), + ErrMsgMapping(ENOSYS, "Function not implemented"), + ErrMsgMapping(ENOTEMPTY, "Directory not empty"), + ErrMsgMapping(ELOOP, "Too many levels of symbolic links"), + // No error for 41. Would be EWOULDBLOCK + ErrMsgMapping(ENOMSG, "No message of desired type"), + ErrMsgMapping(EIDRM, "Identifier removed"), + ErrMsgMapping(ECHRNG, "Channel number out of range"), + ErrMsgMapping(EL2NSYNC, "Level 2 not synchronized"), + ErrMsgMapping(EL3HLT, "Level 3 halted"), + ErrMsgMapping(EL3RST, "Level 3 reset"), + ErrMsgMapping(ELNRNG, "Link number out of range"), + ErrMsgMapping(EUNATCH, "Protocol driver not attached"), + ErrMsgMapping(ENOCSI, "No CSI structure available"), + ErrMsgMapping(EL2HLT, "Level 2 halted"), + ErrMsgMapping(EBADE, "Invalid exchange"), + ErrMsgMapping(EBADR, "Invalid request descriptor"), + ErrMsgMapping(EXFULL, "Exchange full"), + ErrMsgMapping(ENOANO, "No anode"), + ErrMsgMapping(EBADRQC, "Invalid request code"), + ErrMsgMapping(EBADSLT, "Invalid slot"), + // No error for 58. Would be EDEADLOCK. + ErrMsgMapping(EBFONT, "Bad font file format"), + ErrMsgMapping(ENOSTR, "Device not a stream"), + ErrMsgMapping(ENODATA, "No data available"), + ErrMsgMapping(ETIME, "Timer expired"), + ErrMsgMapping(ENOSR, "Out of streams resources"), + ErrMsgMapping(ENONET, "Machine is not on the network"), + ErrMsgMapping(ENOPKG, "Package not installed"), + ErrMsgMapping(EREMOTE, "Object is remote"), + ErrMsgMapping(ENOLINK, "Link has been severed"), + ErrMsgMapping(EADV, "Advertise error"), + ErrMsgMapping(ESRMNT, "Srmount error"), + ErrMsgMapping(ECOMM, "Communication error on send"), + ErrMsgMapping(EPROTO, "Protocol error"), + ErrMsgMapping(EMULTIHOP, "Multihop attempted"), + ErrMsgMapping(EDOTDOT, "RFS specific error"), + ErrMsgMapping(EBADMSG, "Bad message"), + ErrMsgMapping(EOVERFLOW, "Value too large for defined data type"), + ErrMsgMapping(ENOTUNIQ, "Name not unique on network"), + ErrMsgMapping(EBADFD, "File descriptor in bad state"), + ErrMsgMapping(EREMCHG, "Remote address changed"), + ErrMsgMapping(ELIBACC, "Can not access a needed shared library"), + ErrMsgMapping(ELIBBAD, "Accessing a corrupted shared library"), + ErrMsgMapping(ELIBSCN, ".lib section in a.out corrupted"), + ErrMsgMapping(ELIBMAX, "Attempting to link in too many shared libraries"), + ErrMsgMapping(ELIBEXEC, "Cannot exec a shared library directly"), + ErrMsgMapping(EILSEQ, "Invalid or incomplete multibyte or wide character"), + ErrMsgMapping(ERESTART, "Interrupted system call should be restarted"), + ErrMsgMapping(ESTRPIPE, "Streams pipe error"), + ErrMsgMapping(EUSERS, "Too many users"), + ErrMsgMapping(ENOTSOCK, "Socket operation on non-socket"), + ErrMsgMapping(EDESTADDRREQ, "Destination address required"), + ErrMsgMapping(EMSGSIZE, "Message too long"), + ErrMsgMapping(EPROTOTYPE, "Protocol wrong type for socket"), + ErrMsgMapping(ENOPROTOOPT, "Protocol not available"), + ErrMsgMapping(EPROTONOSUPPORT, "Protocol not supported"), + ErrMsgMapping(ESOCKTNOSUPPORT, "Socket type not supported"), + ErrMsgMapping(ENOTSUP, "Operation not supported"), + ErrMsgMapping(EPFNOSUPPORT, "Protocol family not supported"), + ErrMsgMapping(EAFNOSUPPORT, "Address family not supported by protocol"), + ErrMsgMapping(EADDRINUSE, "Address already in use"), + ErrMsgMapping(EADDRNOTAVAIL, "Cannot assign requested address"), + ErrMsgMapping(ENETDOWN, "Network is down"), + ErrMsgMapping(ENETUNREACH, "Network is unreachable"), + ErrMsgMapping(ENETRESET, "Network dropped connection on reset"), + ErrMsgMapping(ECONNABORTED, "Software caused connection abort"), + ErrMsgMapping(ECONNRESET, "Connection reset by peer"), + ErrMsgMapping(ENOBUFS, "No buffer space available"), + ErrMsgMapping(EISCONN, "Transport endpoint is already connected"), + ErrMsgMapping(ENOTCONN, "Transport endpoint is not connected"), + ErrMsgMapping(ESHUTDOWN, "Cannot send after transport endpoint shutdown"), + ErrMsgMapping(ETOOMANYREFS, "Too many references: cannot splice"), + ErrMsgMapping(ETIMEDOUT, "Connection timed out"), + ErrMsgMapping(ECONNREFUSED, "Connection refused"), + ErrMsgMapping(EHOSTDOWN, "Host is down"), + ErrMsgMapping(EHOSTUNREACH, "No route to host"), + ErrMsgMapping(EALREADY, "Operation already in progress"), + ErrMsgMapping(EINPROGRESS, "Operation now in progress"), + ErrMsgMapping(ESTALE, "Stale file handle"), + ErrMsgMapping(EUCLEAN, "Structure needs cleaning"), + ErrMsgMapping(ENOTNAM, "Not a XENIX named type file"), + ErrMsgMapping(ENAVAIL, "No XENIX semaphores available"), + ErrMsgMapping(EISNAM, "Is a named type file"), + ErrMsgMapping(EREMOTEIO, "Remote I/O error"), + ErrMsgMapping(EDQUOT, "Disk quota exceeded"), + ErrMsgMapping(ENOMEDIUM, "No medium found"), + ErrMsgMapping(EMEDIUMTYPE, "Wrong medium type"), + ErrMsgMapping(ECANCELED, "Operation canceled"), + ErrMsgMapping(ENOKEY, "Required key not available"), + ErrMsgMapping(EKEYEXPIRED, "Key has expired"), + ErrMsgMapping(EKEYREVOKED, "Key has been revoked"), + ErrMsgMapping(EKEYREJECTED, "Key was rejected by service"), + ErrMsgMapping(EOWNERDEAD, "Owner died"), + ErrMsgMapping(ENOTRECOVERABLE, "State not recoverable"), + ErrMsgMapping(ERFKILL, "Operation not possible due to RF-kill"), + ErrMsgMapping(EHWPOISON, "Memory page has hardware error"), +}; + +constexpr size_t total_str_len(const ErrMsgMapping *array, size_t len) { + size_t total = 0; + for (size_t i = 0; i < len; ++i) { + // add 1 for the null terminator. + total += array[i].err_msg.size() + 1; + } + return total; +} + +// Since the StringMappings array is a map from error numbers to their +// corresponding strings, we have to have an array large enough we can use the +// error numbers as indexes. Thankfully there are 132 errors in the above list +// (41 and 58 are skipped) and the highest number is 133. If other platforms use +// different error numbers, then this number may need to be adjusted. +// Also if negative numbers or particularly large numbers are used, then the +// array should be turned into a proper hashmap. +constexpr size_t ERR_ARRAY_SIZE = 134; + +class ErrorMapper { + + // const char *StringMappings[ERR_ARRAY_SIZE] = {""}; + int err_offsets[ERR_ARRAY_SIZE] = {-1}; + char string_array[total_str_len( + raw_err_array, sizeof(raw_err_array) / sizeof(ErrMsgMapping))] = {'\0'}; + +public: + constexpr ErrorMapper() { + cpp::string_view string_mappings[ERR_ARRAY_SIZE] = {""}; + for (size_t i = 0; i < (sizeof(raw_err_array) / sizeof(ErrMsgMapping)); ++i) + string_mappings[raw_err_array[i].err_num] = raw_err_array[i].err_msg; + + size_t string_array_index = 0; + for (size_t cur_err = 0; cur_err < ERR_ARRAY_SIZE; ++cur_err) { + if (string_mappings[cur_err].size() != 0) { + err_offsets[cur_err] = string_array_index; + // No need to replace with proper strcpy, this is evaluated at compile + // time. + for (size_t i = 0; i < string_mappings[cur_err].size() + 1; + ++i, ++string_array_index) { + string_array[string_array_index] = string_mappings[cur_err][i]; + } + } else { + err_offsets[cur_err] = -1; + } + } + } + + constexpr cpp::string_view get_str(int err_num) const { + if (err_num >= 0 && static_cast(err_num) < ERR_ARRAY_SIZE && + err_offsets[err_num] != -1) { + return const_cast(string_array + err_offsets[err_num]); + } else { + // if the buffer can't hold "Unknown error" + ' ' + num_str, then just + // return "Unknown error". + if (BUFFER_SIZE < + (sizeof("Unknown error") + 1 + IntegerToString::dec_bufsize())) + return const_cast("Unknown error"); + + cpp::StringStream buffer_stream({error_buffer, BUFFER_SIZE}); + buffer_stream << "Unknown error" << ' ' << err_num << '\0'; + return buffer_stream.str(); + } + } +}; + +static constexpr ErrorMapper error_mapper; + +} // namespace internal + +cpp::string_view get_error_string(int err_num) { + return internal::error_mapper.get_str(err_num); +} +} // namespace __llvm_libc diff --git a/libc/src/__support/integer_to_string.h b/libc/src/__support/integer_to_string.h --- a/libc/src/__support/integer_to_string.h +++ b/libc/src/__support/integer_to_string.h @@ -11,9 +11,9 @@ #include -#include "src/__support/CPP/string_view.h" #include "src/__support/CPP/optional.h" #include "src/__support/CPP/span.h" +#include "src/__support/CPP/string_view.h" #include "src/__support/CPP/type_traits.h" namespace __llvm_libc { @@ -45,9 +45,9 @@ // auto str = IntegerToString::convert<30>(a, b30buf); class IntegerToString { static cpp::string_view convert_uintmax(uintmax_t uval, - cpp::span &buffer, - bool lowercase, - const uint8_t conv_base) { + cpp::span &buffer, + bool lowercase, + const uint8_t conv_base) { const char a = lowercase ? 'a' : 'A'; size_t len = 0; @@ -68,8 +68,8 @@ } static cpp::string_view convert_intmax(intmax_t val, cpp::span &buffer, - bool lowercase, - const uint8_t conv_base) { + bool lowercase, + const uint8_t conv_base) { if (val >= 0) return convert_uintmax(uintmax_t(val), buffer, lowercase, conv_base); uintmax_t uval = uintmax_t(-val); @@ -139,7 +139,7 @@ cpp::enable_if_t<2 <= BASE && BASE <= 36 && cpp::is_integral_v, int> = 0> static cpp::optional convert(T val, cpp::span buffer, - bool lowercase = true) { + bool lowercase = true) { if (buffer.size() < bufsize()) return cpp::optional(); if (cpp::is_signed_v) @@ -148,6 +148,18 @@ return convert_uintmax(uintmax_t(val), buffer, lowercase, BASE); } + // This is constexpr, which is useful, but the buffer MUST be large enough. + template , + int> = 0> + static constexpr cpp::string_view const_convert(T val, cpp::span buffer, + bool lowercase = true) { + if (cpp::is_signed_v) + return convert_intmax(intmax_t(val), buffer, lowercase, BASE); + else + return convert_uintmax(uintmax_t(val), buffer, lowercase, BASE); + } + template , int> = 0> static cpp::optional dec(T val, cpp::span buffer) { return convert<10>(val, buffer); @@ -155,7 +167,7 @@ template , int> = 0> static cpp::optional hex(T val, cpp::span buffer, - bool lowercase = true) { + bool lowercase = true) { return convert<16>(val, buffer, lowercase); } diff --git a/libc/src/string/CMakeLists.txt b/libc/src/string/CMakeLists.txt --- a/libc/src/string/CMakeLists.txt +++ b/libc/src/string/CMakeLists.txt @@ -128,6 +128,17 @@ libc.include.stdlib ) +add_entrypoint_object( + strerror + SRCS + strerror.cpp + HDRS + strerror.h + DEPENDS + libc.src.__support.error_to_string + libc.src.__support.CPP.span +) + add_entrypoint_object( strlcat SRCS diff --git a/libc/src/string/strerror.h b/libc/src/string/strerror.h new file mode 100644 --- /dev/null +++ b/libc/src/string/strerror.h @@ -0,0 +1,18 @@ +//===-- Implementation header for strerror ----------------------*- 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_STRING_STRERROR_H +#define LLVM_LIBC_SRC_STRING_STRERROR_H + +namespace __llvm_libc { + +char *strerror(int errnum); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STRING_STRERROR_H diff --git a/libc/src/string/strerror.cpp b/libc/src/string/strerror.cpp new file mode 100644 --- /dev/null +++ b/libc/src/string/strerror.cpp @@ -0,0 +1,20 @@ +//===-- Implementation of strerror ----------------------------------------===// +// +// 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/string/strerror.h" +#include "src/__support/CPP/span.h" +#include "src/__support/common.h" +#include "src/__support/error_to_string.h" + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(char *, strerror, (int err_num)) { + return const_cast(get_error_string(err_num).data()); +} + +} // namespace __llvm_libc diff --git a/libc/test/ErrnoSetterMatcher.h b/libc/test/ErrnoSetterMatcher.h --- a/libc/test/ErrnoSetterMatcher.h +++ b/libc/test/ErrnoSetterMatcher.h @@ -18,7 +18,7 @@ namespace internal { -extern "C" const char *strerror(int); +extern "C" char *strerror(int); template class ErrnoSetterMatcher : public Matcher { T ExpectedReturn; diff --git a/libc/test/src/string/CMakeLists.txt b/libc/test/src/string/CMakeLists.txt --- a/libc/test/src/string/CMakeLists.txt +++ b/libc/test/src/string/CMakeLists.txt @@ -123,6 +123,16 @@ libc.src.string.strdup ) +add_libc_unittest( + strerror_test + SUITE + libc_string_unittests + SRCS + strerror_test.cpp + DEPENDS + libc.src.string.strerror +) + add_libc_unittest( strlcat_test SUITE diff --git a/libc/test/src/string/strerror_test.cpp b/libc/test/src/string/strerror_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/string/strerror_test.cpp @@ -0,0 +1,162 @@ +//===-- Unittests for strerror --------------------------------------------===// +// +// 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/string/strerror.h" +#include "utils/UnitTest/Test.h" + +TEST(LlvmLibcStrErrorTest, KnownErrors) { + ASSERT_STREQ(__llvm_libc::strerror(0), "Success"); + + const char *message_array[] = { + "Success", + "Operation not permitted", + "No such file or directory", + "No such process", + "Interrupted system call", + "Input/output error", + "No such device or address", + "Argument list too long", + "Exec format error", + "Bad file descriptor", + "No child processes", + "Resource temporarily unavailable", + "Cannot allocate memory", + "Permission denied", + "Bad address", + "Block device required", + "Device or resource busy", + "File exists", + "Invalid cross-device link", + "No such device", + "Not a directory", + "Is a directory", + "Invalid argument", + "Too many open files in system", + "Too many open files", + "Inappropriate ioctl for device", + "Text file busy", + "File too large", + "No space left on device", + "Illegal seek", + "Read-only file system", + "Too many links", + "Broken pipe", + "Numerical argument out of domain", + "Numerical result out of range", + "Resource deadlock avoided", + "File name too long", + "No locks available", + "Function not implemented", + "Directory not empty", + "Too many levels of symbolic links", + "Unknown error 41", // Unknown + "No message of desired type", + "Identifier removed", + "Channel number out of range", + "Level 2 not synchronized", + "Level 3 halted", + "Level 3 reset", + "Link number out of range", + "Protocol driver not attached", + "No CSI structure available", + "Level 2 halted", + "Invalid exchange", + "Invalid request descriptor", + "Exchange full", + "No anode", + "Invalid request code", + "Invalid slot", + "Unknown error 58", // Unknown + "Bad font file format", + "Device not a stream", + "No data available", + "Timer expired", + "Out of streams resources", + "Machine is not on the network", + "Package not installed", + "Object is remote", + "Link has been severed", + "Advertise error", + "Srmount error", + "Communication error on send", + "Protocol error", + "Multihop attempted", + "RFS specific error", + "Bad message", + "Value too large for defined data type", + "Name not unique on network", + "File descriptor in bad state", + "Remote address changed", + "Can not access a needed shared library", + "Accessing a corrupted shared library", + ".lib section in a.out corrupted", + "Attempting to link in too many shared libraries", + "Cannot exec a shared library directly", + "Invalid or incomplete multibyte or wide character", + "Interrupted system call should be restarted", + "Streams pipe error", + "Too many users", + "Socket operation on non-socket", + "Destination address required", + "Message too long", + "Protocol wrong type for socket", + "Protocol not available", + "Protocol not supported", + "Socket type not supported", + "Operation not supported", + "Protocol family not supported", + "Address family not supported by protocol", + "Address already in use", + "Cannot assign requested address", + "Network is down", + "Network is unreachable", + "Network dropped connection on reset", + "Software caused connection abort", + "Connection reset by peer", + "No buffer space available", + "Transport endpoint is already connected", + "Transport endpoint is not connected", + "Cannot send after transport endpoint shutdown", + "Too many references: cannot splice", + "Connection timed out", + "Connection refused", + "Host is down", + "No route to host", + "Operation already in progress", + "Operation now in progress", + "Stale file handle", + "Structure needs cleaning", + "Not a XENIX named type file", + "No XENIX semaphores available", + "Is a named type file", + "Remote I/O error", + "Disk quota exceeded", + "No medium found", + "Wrong medium type", + "Operation canceled", + "Required key not available", + "Key has expired", + "Key has been revoked", + "Key was rejected by service", + "Owner died", + "State not recoverable", + "Operation not possible due to RF-kill", + "Memory page has hardware error", + }; + + for (size_t i = 0; i < (sizeof(message_array) / sizeof(char *)); ++i) { + EXPECT_STREQ(__llvm_libc::strerror(i), message_array[i]); + } +} + +TEST(LlvmLibcStrErrorTest, UnknownErrors) { + ASSERT_STREQ(__llvm_libc::strerror(-1), "Unknown error -1"); + ASSERT_STREQ(__llvm_libc::strerror(134), "Unknown error 134"); + ASSERT_STREQ(__llvm_libc::strerror(2147483647), "Unknown error 2147483647"); + ASSERT_STREQ(__llvm_libc::strerror(-2147483648), "Unknown error -2147483648"); +}