Skip to content

Commit 42bcb35

Browse files
[libc] add strerror
Strerror maps error numbers to strings. Additionally, a utility for mapping errors to strings was added so that it could be reused for perror and similar. Reviewed By: sivachandra Differential Revision: https://reviews.llvm.org/D134074
1 parent 41f3602 commit 42bcb35

11 files changed

+516
-9
lines changed

libc/config/linux/x86_64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ set(TARGET_LIBC_ENTRYPOINTS
5757
# string.h entrypoints that depend on malloc
5858
libc.src.string.strdup
5959
libc.src.string.strndup
60+
libc.src.string.strerror
6061

6162
# inttypes.h entrypoints
6263
libc.src.inttypes.imaxabs

libc/src/__support/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,19 @@ add_header_library(
7272
libc.src.errno.errno
7373
)
7474

75+
76+
add_object_library(
77+
error_to_string
78+
HDRS
79+
error_to_string.h
80+
SRCS
81+
error_to_string.cpp
82+
DEPENDS
83+
libc.include.errno
84+
libc.src.__support.CPP.string_view
85+
libc.src.__support.CPP.stringstream
86+
)
87+
7588
add_header_library(
7689
integer_operations
7790
HDRS
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
//===-- Implementation of a class for mapping errors to strings -----------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/__support/error_to_string.h"
10+
#include "src/__support/CPP/string_view.h"
11+
#include "src/__support/CPP/stringstream.h"
12+
#include <errno.h>
13+
#include <stddef.h>
14+
15+
namespace __llvm_libc {
16+
namespace internal {
17+
18+
constexpr size_t max_buff_size() {
19+
constexpr size_t unknown_str_len = sizeof("Unknown error");
20+
constexpr size_t max_num_len =
21+
__llvm_libc::IntegerToString::dec_bufsize<int>();
22+
// the buffer should be able to hold "Unknown error" + ' ' + num_str
23+
return (unknown_str_len + 1 + max_num_len) * sizeof(char);
24+
}
25+
26+
// This is to hold error strings that have to be custom built. It may be
27+
// rewritten on every call to strerror (or other error to string function).
28+
constexpr size_t BUFFER_SIZE = max_buff_size();
29+
thread_local char error_buffer[BUFFER_SIZE];
30+
31+
struct ErrMsgMapping {
32+
int err_num;
33+
cpp::string_view err_msg;
34+
35+
public:
36+
constexpr ErrMsgMapping(int num, const char *msg)
37+
: err_num(num), err_msg(msg) {
38+
;
39+
}
40+
};
41+
42+
constexpr ErrMsgMapping raw_err_array[] = {
43+
ErrMsgMapping(0, "Success"),
44+
ErrMsgMapping(EPERM, "Operation not permitted"),
45+
ErrMsgMapping(ENOENT, "No such file or directory"),
46+
ErrMsgMapping(ESRCH, "No such process"),
47+
ErrMsgMapping(EINTR, "Interrupted system call"),
48+
ErrMsgMapping(EIO, "Input/output error"),
49+
ErrMsgMapping(ENXIO, "No such device or address"),
50+
ErrMsgMapping(E2BIG, "Argument list too long"),
51+
ErrMsgMapping(ENOEXEC, "Exec format error"),
52+
ErrMsgMapping(EBADF, "Bad file descriptor"),
53+
ErrMsgMapping(ECHILD, "No child processes"),
54+
ErrMsgMapping(EAGAIN, "Resource temporarily unavailable"),
55+
ErrMsgMapping(ENOMEM, "Cannot allocate memory"),
56+
ErrMsgMapping(EACCES, "Permission denied"),
57+
ErrMsgMapping(EFAULT, "Bad address"),
58+
ErrMsgMapping(ENOTBLK, "Block device required"),
59+
ErrMsgMapping(EBUSY, "Device or resource busy"),
60+
ErrMsgMapping(EEXIST, "File exists"),
61+
ErrMsgMapping(EXDEV, "Invalid cross-device link"),
62+
ErrMsgMapping(ENODEV, "No such device"),
63+
ErrMsgMapping(ENOTDIR, "Not a directory"),
64+
ErrMsgMapping(EISDIR, "Is a directory"),
65+
ErrMsgMapping(EINVAL, "Invalid argument"),
66+
ErrMsgMapping(ENFILE, "Too many open files in system"),
67+
ErrMsgMapping(EMFILE, "Too many open files"),
68+
ErrMsgMapping(ENOTTY, "Inappropriate ioctl for device"),
69+
ErrMsgMapping(ETXTBSY, "Text file busy"),
70+
ErrMsgMapping(EFBIG, "File too large"),
71+
ErrMsgMapping(ENOSPC, "No space left on device"),
72+
ErrMsgMapping(ESPIPE, "Illegal seek"),
73+
ErrMsgMapping(EROFS, "Read-only file system"),
74+
ErrMsgMapping(EMLINK, "Too many links"),
75+
ErrMsgMapping(EPIPE, "Broken pipe"),
76+
ErrMsgMapping(EDOM, "Numerical argument out of domain"),
77+
ErrMsgMapping(ERANGE, "Numerical result out of range"),
78+
ErrMsgMapping(EDEADLK, "Resource deadlock avoided"),
79+
ErrMsgMapping(ENAMETOOLONG, "File name too long"),
80+
ErrMsgMapping(ENOLCK, "No locks available"),
81+
ErrMsgMapping(ENOSYS, "Function not implemented"),
82+
ErrMsgMapping(ENOTEMPTY, "Directory not empty"),
83+
ErrMsgMapping(ELOOP, "Too many levels of symbolic links"),
84+
// No error for 41. Would be EWOULDBLOCK
85+
ErrMsgMapping(ENOMSG, "No message of desired type"),
86+
ErrMsgMapping(EIDRM, "Identifier removed"),
87+
ErrMsgMapping(ECHRNG, "Channel number out of range"),
88+
ErrMsgMapping(EL2NSYNC, "Level 2 not synchronized"),
89+
ErrMsgMapping(EL3HLT, "Level 3 halted"),
90+
ErrMsgMapping(EL3RST, "Level 3 reset"),
91+
ErrMsgMapping(ELNRNG, "Link number out of range"),
92+
ErrMsgMapping(EUNATCH, "Protocol driver not attached"),
93+
ErrMsgMapping(ENOCSI, "No CSI structure available"),
94+
ErrMsgMapping(EL2HLT, "Level 2 halted"),
95+
ErrMsgMapping(EBADE, "Invalid exchange"),
96+
ErrMsgMapping(EBADR, "Invalid request descriptor"),
97+
ErrMsgMapping(EXFULL, "Exchange full"),
98+
ErrMsgMapping(ENOANO, "No anode"),
99+
ErrMsgMapping(EBADRQC, "Invalid request code"),
100+
ErrMsgMapping(EBADSLT, "Invalid slot"),
101+
// No error for 58. Would be EDEADLOCK.
102+
ErrMsgMapping(EBFONT, "Bad font file format"),
103+
ErrMsgMapping(ENOSTR, "Device not a stream"),
104+
ErrMsgMapping(ENODATA, "No data available"),
105+
ErrMsgMapping(ETIME, "Timer expired"),
106+
ErrMsgMapping(ENOSR, "Out of streams resources"),
107+
ErrMsgMapping(ENONET, "Machine is not on the network"),
108+
ErrMsgMapping(ENOPKG, "Package not installed"),
109+
ErrMsgMapping(EREMOTE, "Object is remote"),
110+
ErrMsgMapping(ENOLINK, "Link has been severed"),
111+
ErrMsgMapping(EADV, "Advertise error"),
112+
ErrMsgMapping(ESRMNT, "Srmount error"),
113+
ErrMsgMapping(ECOMM, "Communication error on send"),
114+
ErrMsgMapping(EPROTO, "Protocol error"),
115+
ErrMsgMapping(EMULTIHOP, "Multihop attempted"),
116+
ErrMsgMapping(EDOTDOT, "RFS specific error"),
117+
ErrMsgMapping(EBADMSG, "Bad message"),
118+
ErrMsgMapping(EOVERFLOW, "Value too large for defined data type"),
119+
ErrMsgMapping(ENOTUNIQ, "Name not unique on network"),
120+
ErrMsgMapping(EBADFD, "File descriptor in bad state"),
121+
ErrMsgMapping(EREMCHG, "Remote address changed"),
122+
ErrMsgMapping(ELIBACC, "Can not access a needed shared library"),
123+
ErrMsgMapping(ELIBBAD, "Accessing a corrupted shared library"),
124+
ErrMsgMapping(ELIBSCN, ".lib section in a.out corrupted"),
125+
ErrMsgMapping(ELIBMAX, "Attempting to link in too many shared libraries"),
126+
ErrMsgMapping(ELIBEXEC, "Cannot exec a shared library directly"),
127+
ErrMsgMapping(EILSEQ, "Invalid or incomplete multibyte or wide character"),
128+
ErrMsgMapping(ERESTART, "Interrupted system call should be restarted"),
129+
ErrMsgMapping(ESTRPIPE, "Streams pipe error"),
130+
ErrMsgMapping(EUSERS, "Too many users"),
131+
ErrMsgMapping(ENOTSOCK, "Socket operation on non-socket"),
132+
ErrMsgMapping(EDESTADDRREQ, "Destination address required"),
133+
ErrMsgMapping(EMSGSIZE, "Message too long"),
134+
ErrMsgMapping(EPROTOTYPE, "Protocol wrong type for socket"),
135+
ErrMsgMapping(ENOPROTOOPT, "Protocol not available"),
136+
ErrMsgMapping(EPROTONOSUPPORT, "Protocol not supported"),
137+
ErrMsgMapping(ESOCKTNOSUPPORT, "Socket type not supported"),
138+
ErrMsgMapping(ENOTSUP, "Operation not supported"),
139+
ErrMsgMapping(EPFNOSUPPORT, "Protocol family not supported"),
140+
ErrMsgMapping(EAFNOSUPPORT, "Address family not supported by protocol"),
141+
ErrMsgMapping(EADDRINUSE, "Address already in use"),
142+
ErrMsgMapping(EADDRNOTAVAIL, "Cannot assign requested address"),
143+
ErrMsgMapping(ENETDOWN, "Network is down"),
144+
ErrMsgMapping(ENETUNREACH, "Network is unreachable"),
145+
ErrMsgMapping(ENETRESET, "Network dropped connection on reset"),
146+
ErrMsgMapping(ECONNABORTED, "Software caused connection abort"),
147+
ErrMsgMapping(ECONNRESET, "Connection reset by peer"),
148+
ErrMsgMapping(ENOBUFS, "No buffer space available"),
149+
ErrMsgMapping(EISCONN, "Transport endpoint is already connected"),
150+
ErrMsgMapping(ENOTCONN, "Transport endpoint is not connected"),
151+
ErrMsgMapping(ESHUTDOWN, "Cannot send after transport endpoint shutdown"),
152+
ErrMsgMapping(ETOOMANYREFS, "Too many references: cannot splice"),
153+
ErrMsgMapping(ETIMEDOUT, "Connection timed out"),
154+
ErrMsgMapping(ECONNREFUSED, "Connection refused"),
155+
ErrMsgMapping(EHOSTDOWN, "Host is down"),
156+
ErrMsgMapping(EHOSTUNREACH, "No route to host"),
157+
ErrMsgMapping(EALREADY, "Operation already in progress"),
158+
ErrMsgMapping(EINPROGRESS, "Operation now in progress"),
159+
ErrMsgMapping(ESTALE, "Stale file handle"),
160+
ErrMsgMapping(EUCLEAN, "Structure needs cleaning"),
161+
ErrMsgMapping(ENOTNAM, "Not a XENIX named type file"),
162+
ErrMsgMapping(ENAVAIL, "No XENIX semaphores available"),
163+
ErrMsgMapping(EISNAM, "Is a named type file"),
164+
ErrMsgMapping(EREMOTEIO, "Remote I/O error"),
165+
ErrMsgMapping(EDQUOT, "Disk quota exceeded"),
166+
ErrMsgMapping(ENOMEDIUM, "No medium found"),
167+
ErrMsgMapping(EMEDIUMTYPE, "Wrong medium type"),
168+
ErrMsgMapping(ECANCELED, "Operation canceled"),
169+
ErrMsgMapping(ENOKEY, "Required key not available"),
170+
ErrMsgMapping(EKEYEXPIRED, "Key has expired"),
171+
ErrMsgMapping(EKEYREVOKED, "Key has been revoked"),
172+
ErrMsgMapping(EKEYREJECTED, "Key was rejected by service"),
173+
ErrMsgMapping(EOWNERDEAD, "Owner died"),
174+
ErrMsgMapping(ENOTRECOVERABLE, "State not recoverable"),
175+
ErrMsgMapping(ERFKILL, "Operation not possible due to RF-kill"),
176+
ErrMsgMapping(EHWPOISON, "Memory page has hardware error"),
177+
};
178+
179+
constexpr size_t total_str_len(const ErrMsgMapping *array, size_t len) {
180+
size_t total = 0;
181+
for (size_t i = 0; i < len; ++i) {
182+
// add 1 for the null terminator.
183+
total += array[i].err_msg.size() + 1;
184+
}
185+
return total;
186+
}
187+
188+
// Since the StringMappings array is a map from error numbers to their
189+
// corresponding strings, we have to have an array large enough we can use the
190+
// error numbers as indexes. Thankfully there are 132 errors in the above list
191+
// (41 and 58 are skipped) and the highest number is 133. If other platforms use
192+
// different error numbers, then this number may need to be adjusted.
193+
// Also if negative numbers or particularly large numbers are used, then the
194+
// array should be turned into a proper hashmap.
195+
constexpr size_t ERR_ARRAY_SIZE = 134;
196+
197+
class ErrorMapper {
198+
199+
// const char *StringMappings[ERR_ARRAY_SIZE] = {""};
200+
int err_offsets[ERR_ARRAY_SIZE] = {-1};
201+
char string_array[total_str_len(
202+
raw_err_array, sizeof(raw_err_array) / sizeof(ErrMsgMapping))] = {'\0'};
203+
204+
public:
205+
constexpr ErrorMapper() {
206+
cpp::string_view string_mappings[ERR_ARRAY_SIZE] = {""};
207+
for (size_t i = 0; i < (sizeof(raw_err_array) / sizeof(ErrMsgMapping)); ++i)
208+
string_mappings[raw_err_array[i].err_num] = raw_err_array[i].err_msg;
209+
210+
size_t string_array_index = 0;
211+
for (size_t cur_err = 0; cur_err < ERR_ARRAY_SIZE; ++cur_err) {
212+
if (string_mappings[cur_err].size() != 0) {
213+
err_offsets[cur_err] = string_array_index;
214+
// No need to replace with proper strcpy, this is evaluated at compile
215+
// time.
216+
for (size_t i = 0; i < string_mappings[cur_err].size() + 1;
217+
++i, ++string_array_index) {
218+
string_array[string_array_index] = string_mappings[cur_err][i];
219+
}
220+
} else {
221+
err_offsets[cur_err] = -1;
222+
}
223+
}
224+
}
225+
226+
cpp::string_view get_str(int err_num) const {
227+
if (err_num >= 0 && static_cast<size_t>(err_num) < ERR_ARRAY_SIZE &&
228+
err_offsets[err_num] != -1) {
229+
return const_cast<char *>(string_array + err_offsets[err_num]);
230+
} else {
231+
// if the buffer can't hold "Unknown error" + ' ' + num_str, then just
232+
// return "Unknown error".
233+
if (BUFFER_SIZE <
234+
(sizeof("Unknown error") + 1 + IntegerToString::dec_bufsize<int>()))
235+
return const_cast<char *>("Unknown error");
236+
237+
cpp::StringStream buffer_stream({error_buffer, BUFFER_SIZE});
238+
buffer_stream << "Unknown error" << ' ' << err_num << '\0';
239+
return buffer_stream.str();
240+
}
241+
}
242+
};
243+
244+
static constexpr ErrorMapper error_mapper;
245+
246+
} // namespace internal
247+
248+
cpp::string_view get_error_string(int err_num) {
249+
return internal::error_mapper.get_str(err_num);
250+
}
251+
} // namespace __llvm_libc

libc/src/__support/error_to_string.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//===-- Definition of a class for mapping errors to strings -----*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/__support/CPP/string_view.h"
10+
#include "src/__support/integer_to_string.h"
11+
12+
#ifndef LLVM_LIBC_SRC_SUPPORT_ERROR_TO_STRING
13+
#define LLVM_LIBC_SRC_SUPPORT_ERROR_TO_STRING
14+
15+
namespace __llvm_libc {
16+
17+
cpp::string_view get_error_string(int err_num);
18+
19+
} // namespace __llvm_libc
20+
21+
#endif // LLVM_LIBC_SRC_SUPPORT_ERROR_TO_STRING

libc/src/__support/integer_to_string.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111

1212
#include <stdint.h>
1313

14-
#include "src/__support/CPP/string_view.h"
1514
#include "src/__support/CPP/optional.h"
1615
#include "src/__support/CPP/span.h"
16+
#include "src/__support/CPP/string_view.h"
1717
#include "src/__support/CPP/type_traits.h"
1818

1919
namespace __llvm_libc {
@@ -45,9 +45,9 @@ namespace __llvm_libc {
4545
// auto str = IntegerToString::convert<30>(a, b30buf);
4646
class IntegerToString {
4747
static cpp::string_view convert_uintmax(uintmax_t uval,
48-
cpp::span<char> &buffer,
49-
bool lowercase,
50-
const uint8_t conv_base) {
48+
cpp::span<char> &buffer,
49+
bool lowercase,
50+
const uint8_t conv_base) {
5151
const char a = lowercase ? 'a' : 'A';
5252

5353
size_t len = 0;
@@ -68,8 +68,8 @@ class IntegerToString {
6868
}
6969

7070
static cpp::string_view convert_intmax(intmax_t val, cpp::span<char> &buffer,
71-
bool lowercase,
72-
const uint8_t conv_base) {
71+
bool lowercase,
72+
const uint8_t conv_base) {
7373
if (val >= 0)
7474
return convert_uintmax(uintmax_t(val), buffer, lowercase, conv_base);
7575
uintmax_t uval = uintmax_t(-val);
@@ -139,7 +139,7 @@ class IntegerToString {
139139
cpp::enable_if_t<2 <= BASE && BASE <= 36 && cpp::is_integral_v<T>,
140140
int> = 0>
141141
static cpp::optional<cpp::string_view> convert(T val, cpp::span<char> buffer,
142-
bool lowercase = true) {
142+
bool lowercase = true) {
143143
if (buffer.size() < bufsize<BASE, T>())
144144
return cpp::optional<cpp::string_view>();
145145
if (cpp::is_signed_v<T>)
@@ -155,7 +155,7 @@ class IntegerToString {
155155

156156
template <typename T, cpp::enable_if_t<cpp::is_integral_v<T>, int> = 0>
157157
static cpp::optional<cpp::string_view> hex(T val, cpp::span<char> buffer,
158-
bool lowercase = true) {
158+
bool lowercase = true) {
159159
return convert<16>(val, buffer, lowercase);
160160
}
161161

libc/src/string/CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,17 @@ add_entrypoint_object(
128128
libc.include.stdlib
129129
)
130130

131+
add_entrypoint_object(
132+
strerror
133+
SRCS
134+
strerror.cpp
135+
HDRS
136+
strerror.h
137+
DEPENDS
138+
libc.src.__support.error_to_string
139+
libc.src.__support.CPP.span
140+
)
141+
131142
add_entrypoint_object(
132143
strlcat
133144
SRCS

libc/src/string/strerror.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//===-- Implementation of strerror ----------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/string/strerror.h"
10+
#include "src/__support/CPP/span.h"
11+
#include "src/__support/common.h"
12+
#include "src/__support/error_to_string.h"
13+
14+
namespace __llvm_libc {
15+
16+
LLVM_LIBC_FUNCTION(char *, strerror, (int err_num)) {
17+
return const_cast<char *>(get_error_string(err_num).data());
18+
}
19+
20+
} // namespace __llvm_libc

0 commit comments

Comments
 (0)