Re: [PATCH v3 next 05/17] tools/nolibc: Implement strerror() in terms of strerror_r()
From: Thomas Weißschuh
Date: Wed Feb 25 2026 - 17:10:08 EST
On 2026-02-23 10:17:23+0000, david.laight.linux@xxxxxxxxx wrote:
> From: David Laight <david.laight.linux@xxxxxxxxx>
>
> strerror() can be the only part of a program that has a .data section.
> This requres 4k in the program file.
>
> Add a simple implementation of strerror_r() (ignores the length) and
> use that in strerror() so that the "errno=" string is copied at run-time.
> Use __builtin_memcpy() because that optimises away the input string
> and just writes the required constants to the target buffer.
>
> Put the check for NOLIBC_IGNORE_ERRNO into strerror_r().
> Always call strerror() from __nolibc_printf().
Why?
> Code size change largely depends on whether the inlining decision for
> strerror() changes.
>
> Change the tests to use the normal EXPECT_VFPRINTF() when testing %m.
> Skip the tests when !is_nolibc.
>
> Signed-off-by: David Laight <david.laight.linux@xxxxxxxxx>
> ---
>
> New patch for v3.
>
> tools/include/nolibc/stdio.h | 29 ++++++++++++++------
> tools/testing/selftests/nolibc/nolibc-test.c | 20 ++------------
> 2 files changed, 23 insertions(+), 26 deletions(-)
>
> diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h
> index 233318b0d0f0..2267f50d03b4 100644
> --- a/tools/include/nolibc/stdio.h
> +++ b/tools/include/nolibc/stdio.h
> @@ -373,11 +373,7 @@ int __nolibc_printf(__nolibc_printf_cb cb, intptr_t state, size_t n, const char
> outstr="(null)";
> }
> else if (c == 'm') {
> -#ifdef NOLIBC_IGNORE_ERRNO
> - outstr = "unknown error";
> -#else
> outstr = strerror(errno);
> -#endif /* NOLIBC_IGNORE_ERRNO */
> }
> else if (c == '%') {
> /* queue it verbatim */
> @@ -682,14 +678,31 @@ int setvbuf(FILE *stream __attribute__((unused)),
> return 0;
> }
>
> +static __attribute__((unused,))
> +int strerror_r(int errnum, char *buf, size_t buflen __attribute__((unused)))
This is the GNU variant, but the XSI variant would be better.
Why is the buflen parameter not used?
> +{
> +#ifdef NOLIBC_IGNORE_ERRNO
Why did this move here?
The application can have error numbers without errno.
> + __builtin_memcpy(buf, "unknown error", 14);
> + return 13;
> +#else
> + __builtin_memcpy(buf, "errno=", 6);
> + return 6 + i64toa_r(errnum, buf + 6);
> +#endif
> +}
> +
> static __attribute__((unused))
> -const char *strerror(int errno)
> +const char *strerror(int errnum)
Good point. But also a candidate for its own patch.
> {
> - static char buf[18] = "errno=";
> + static char buf[18];
> + char *b = buf;
> +
> + /* Force gcc to use 'register offset' to access buf[]. */
> + _NOLIBC_OPTIMIZER_HIDE_VAR(b);
>
> - i64toa_r(errno, &buf[6]);
> + /* Use strerror_r() to avoid having the only .data in small programs. */
> + strerror_r(errnum, b, sizeof(buf));
>
> - return buf;
> + return b;
> }
>
> #endif /* _NOLIBC_STDIO_H */
> diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c
> index 029ed63e1ae4..61968fdfeec0 100644
> --- a/tools/testing/selftests/nolibc/nolibc-test.c
> +++ b/tools/testing/selftests/nolibc/nolibc-test.c
> @@ -1793,23 +1793,6 @@ static int test_scanf(void)
> return 0;
> }
>
> -int test_strerror(void)
> -{
> - char buf[100];
> - ssize_t ret;
> -
> - memset(buf, 'A', sizeof(buf));
> -
> - errno = EINVAL;
> - ret = snprintf(buf, sizeof(buf), "%m");
> - if (is_nolibc) {
> - if (ret < 6 || memcmp(buf, "errno=", 6))
> - return 1;
> - }
> -
> - return 0;
> -}
> -
> static int test_printf_error(void)
> {
> int fd, ret, saved_errno;
> @@ -1859,8 +1842,9 @@ static int run_printf(int min, int max)
> CASE_TEST(string_width); EXPECT_VFPRINTF(1, " 1", "%10s", "1"); break;
> CASE_TEST(number_width); EXPECT_VFPRINTF(1, " 1", "%10d", 1); break;
> CASE_TEST(width_trunc); EXPECT_VFPRINTF(1, " 1", "%30d", 1); break;
> + CASE_TEST(errno); EXPECT_VFPRINTF(is_nolibc, "22:errno=22", "%d:%m", errno=22); break;
> + CASE_TEST(errno-neg); EXPECT_VFPRINTF(is_nolibc, "-22: errno=-22", "%d:%12m", errno=-22); break;
Assigning errno from the printf argument is sneaky.
Please avoid sneakyness.
> CASE_TEST(scanf); EXPECT_ZR(1, test_scanf()); break;
> - CASE_TEST(strerror); EXPECT_ZR(1, test_strerror()); break;
> CASE_TEST(printf_error); EXPECT_ZR(1, test_printf_error()); break;
> case __LINE__:
> return ret; /* must be last */
> --
> 2.39.5
>