xref: /llvm-project/libcxx/src/system_error.cpp (revision 07d08f4eb44e09e888c0773d87872570048e02ac)
1 //===----------------------------------------------------------------------===//
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 <__assert>
10 #include <__config>
11 #include <__verbose_abort>
12 #include <cerrno>
13 #include <cstdio>
14 #include <cstdlib>
15 #include <cstring>
16 #include <string>
17 #include <string.h>
18 #include <system_error>
19 
20 #include "include/config_elast.h"
21 
22 #if defined(__ANDROID__)
23 #include <android/api-level.h>
24 #endif
25 
26 _LIBCPP_BEGIN_NAMESPACE_STD
27 
28 namespace {
29 #if !defined(_LIBCPP_HAS_NO_THREADS)
30 
31 //  GLIBC also uses 1024 as the maximum buffer size internally.
32 constexpr size_t strerror_buff_size = 1024;
33 
34 string do_strerror_r(int ev);
35 
36 #if defined(_LIBCPP_MSVCRT_LIKE)
37 string do_strerror_r(int ev) {
38   char buffer[strerror_buff_size];
39   if (::strerror_s(buffer, strerror_buff_size, ev) == 0)
40     return string(buffer);
41   std::snprintf(buffer, strerror_buff_size, "unknown error %d", ev);
42   return string(buffer);
43 }
44 #else
45 
46 // Only one of the two following functions will be used, depending on
47 // the return type of strerror_r:
48 
49 // For the GNU variant, a char* return value:
50 __attribute__((unused)) const char *
51 handle_strerror_r_return(char *strerror_return, char *buffer) {
52   // GNU always returns a string pointer in its return value. The
53   // string might point to either the input buffer, or a static
54   // buffer, but we don't care which.
55   return strerror_return;
56 }
57 
58 // For the POSIX variant: an int return value.
59 __attribute__((unused)) const char *
60 handle_strerror_r_return(int strerror_return, char *buffer) {
61   // The POSIX variant either:
62   // - fills in the provided buffer and returns 0
63   // - returns a positive error value, or
64   // - returns -1 and fills in errno with an error value.
65   if (strerror_return == 0)
66     return buffer;
67 
68   // Only handle EINVAL. Other errors abort.
69   int new_errno = strerror_return == -1 ? errno : strerror_return;
70   if (new_errno == EINVAL)
71     return "";
72 
73   _LIBCPP_ASSERT_UNCATEGORIZED(new_errno == ERANGE, "unexpected error from ::strerror_r");
74   // FIXME maybe? 'strerror_buff_size' is likely to exceed the
75   // maximum error size so ERANGE shouldn't be returned.
76   std::abort();
77 }
78 
79 // This function handles both GNU and POSIX variants, dispatching to
80 // one of the two above functions.
81 string do_strerror_r(int ev) {
82     char buffer[strerror_buff_size];
83     // Preserve errno around the call. (The C++ standard requires that
84     // system_error functions not modify errno).
85     const int old_errno = errno;
86     const char *error_message = handle_strerror_r_return(
87         ::strerror_r(ev, buffer, strerror_buff_size), buffer);
88     // If we didn't get any message, print one now.
89     if (!error_message[0]) {
90       std::snprintf(buffer, strerror_buff_size, "Unknown error %d", ev);
91       error_message = buffer;
92     }
93     errno = old_errno;
94     return string(error_message);
95 }
96 #endif
97 
98 #endif // !defined(_LIBCPP_HAS_NO_THREADS)
99 
100 string make_error_str(const error_code& ec, string what_arg) {
101     if (ec) {
102         if (!what_arg.empty()) {
103             what_arg += ": ";
104         }
105         what_arg += ec.message();
106     }
107     return what_arg;
108 }
109 
110 string make_error_str(const error_code& ec) {
111     if (ec) {
112         return ec.message();
113     }
114     return string();
115 }
116 } // end namespace
117 
118 string
119 __do_message::message(int ev) const
120 {
121 #if defined(_LIBCPP_HAS_NO_THREADS)
122     return string(::strerror(ev));
123 #else
124     return do_strerror_r(ev);
125 #endif
126 }
127 
128 class _LIBCPP_HIDDEN __generic_error_category
129     : public __do_message
130 {
131 public:
132     virtual const char* name() const noexcept;
133     virtual string message(int ev) const;
134 };
135 
136 const char*
137 __generic_error_category::name() const noexcept
138 {
139     return "generic";
140 }
141 
142 string
143 __generic_error_category::message(int ev) const
144 {
145 #ifdef _LIBCPP_ELAST
146     if (ev > _LIBCPP_ELAST)
147       return string("unspecified generic_category error");
148 #endif // _LIBCPP_ELAST
149     return __do_message::message(ev);
150 }
151 
152 const error_category&
153 generic_category() noexcept
154 {
155     union AvoidDestroyingGenericCategory {
156         __generic_error_category generic_error_category;
157         constexpr explicit AvoidDestroyingGenericCategory() : generic_error_category() {}
158         ~AvoidDestroyingGenericCategory() {}
159     };
160     constinit static AvoidDestroyingGenericCategory helper;
161     return helper.generic_error_category;
162 }
163 
164 class _LIBCPP_HIDDEN __system_error_category
165     : public __do_message
166 {
167 public:
168     virtual const char* name() const noexcept;
169     virtual string message(int ev) const;
170     virtual error_condition default_error_condition(int ev) const noexcept;
171 };
172 
173 const char*
174 __system_error_category::name() const noexcept
175 {
176     return "system";
177 }
178 
179 string
180 __system_error_category::message(int ev) const
181 {
182 #ifdef _LIBCPP_ELAST
183     if (ev > _LIBCPP_ELAST)
184       return string("unspecified system_category error");
185 #endif // _LIBCPP_ELAST
186     return __do_message::message(ev);
187 }
188 
189 error_condition
190 __system_error_category::default_error_condition(int ev) const noexcept
191 {
192 #ifdef _LIBCPP_ELAST
193     if (ev > _LIBCPP_ELAST)
194       return error_condition(ev, system_category());
195 #endif // _LIBCPP_ELAST
196     return error_condition(ev, generic_category());
197 }
198 
199 const error_category&
200 system_category() noexcept
201 {
202     union AvoidDestroyingSystemCategory {
203         __system_error_category system_error_category;
204         constexpr explicit AvoidDestroyingSystemCategory() : system_error_category() {}
205         ~AvoidDestroyingSystemCategory() {}
206     };
207     constinit static AvoidDestroyingSystemCategory helper;
208     return helper.system_error_category;
209 }
210 
211 // error_condition
212 
213 string
214 error_condition::message() const
215 {
216     return __cat_->message(__val_);
217 }
218 
219 // error_code
220 
221 string
222 error_code::message() const
223 {
224     return __cat_->message(__val_);
225 }
226 
227 // system_error
228 
229 system_error::system_error(error_code ec, const string& what_arg)
230     : runtime_error(make_error_str(ec, what_arg)),
231       __ec_(ec)
232 {
233 }
234 
235 system_error::system_error(error_code ec, const char* what_arg)
236     : runtime_error(make_error_str(ec, what_arg)),
237       __ec_(ec)
238 {
239 }
240 
241 system_error::system_error(error_code ec)
242     : runtime_error(make_error_str(ec)),
243       __ec_(ec)
244 {
245 }
246 
247 system_error::system_error(int ev, const error_category& ecat, const string& what_arg)
248     : runtime_error(make_error_str(error_code(ev, ecat), what_arg)),
249       __ec_(error_code(ev, ecat))
250 {
251 }
252 
253 system_error::system_error(int ev, const error_category& ecat, const char* what_arg)
254     : runtime_error(make_error_str(error_code(ev, ecat), what_arg)),
255       __ec_(error_code(ev, ecat))
256 {
257 }
258 
259 system_error::system_error(int ev, const error_category& ecat)
260     : runtime_error(make_error_str(error_code(ev, ecat))),
261       __ec_(error_code(ev, ecat))
262 {
263 }
264 
265 system_error::~system_error() noexcept
266 {
267 }
268 
269 void
270 __throw_system_error(int ev, const char* what_arg)
271 {
272 #ifndef _LIBCPP_HAS_NO_EXCEPTIONS
273     std::__throw_system_error(error_code(ev, system_category()), what_arg);
274 #else
275     // The above could also handle the no-exception case, but for size, avoid referencing system_category() unnecessarily.
276     _LIBCPP_VERBOSE_ABORT("system_error was thrown in -fno-exceptions mode with error %i and message \"%s\"", ev, what_arg);
277 #endif
278 }
279 
280 _LIBCPP_END_NAMESPACE_STD
281