]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
tools/nolibc: Implement strerror() in terms of strerror_r()
authorDavid Laight <david.laight.linux@gmail.com>
Sun, 8 Mar 2026 11:37:28 +0000 (11:37 +0000)
committerThomas Weißschuh <linux@weissschuh.net>
Fri, 20 Mar 2026 16:46:07 +0000 (17:46 +0100)
strerror() can be the only part of a program that has a .data section.
This requires 4k in the program file.

Add a simple implementation of strerror_r() 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.

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@gmail.com>
Acked-by: Willy Tarreau <w@1wt.eu>
Link: https://patch.msgid.link/20260308113742.12649-4-david.laight.linux@gmail.com
Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
tools/include/nolibc/stdio.h
tools/testing/selftests/nolibc/nolibc-test.c

index a4df72d9a2d37e68e1f816b7fe6d7615d0379fab..b324fc438ea0289aa2986b1bf1e5ec507990ced3 100644 (file)
@@ -722,14 +722,30 @@ int setvbuf(FILE *stream __attribute__((unused)),
        return 0;
 }
 
+static __attribute__((unused))
+int strerror_r(int errnum, char *buf, size_t buflen)
+{
+       if (buflen < 18)
+               return ERANGE;
+
+       __builtin_memcpy(buf, "errno=", 6);
+       i64toa_r(errnum, buf + 6);
+       return 0;
+}
+
 static __attribute__((unused))
 const char *strerror(int errno)
 {
-       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(errno, b, sizeof(buf));
 
-       return buf;
+       return b;
 }
 
 #endif /* _NOLIBC_STDIO_H */
index 5c3bf96f57bb5c12406547645fbbca74ab73b1a9..d8949aa1dee3c895272ad81c0ea298bfc8c92a13 100644 (file)
@@ -1794,23 +1794,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;
@@ -1860,8 +1843,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", "%25d", 1); break;
+               CASE_TEST(errno);        errno = 22; EXPECT_VFPRINTF(is_nolibc, "errno=22", "%m"); break;
+               CASE_TEST(errno-neg);    errno = -22; EXPECT_VFPRINTF(is_nolibc, "   errno=-22", "%12m"); break;
                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 */