get_size provides its own _start and is linked statically against the C
library with -nostartfiles. That combination assumes startup and TLS
details that vary between architectures and C library versions: errno
lives in thread-local storage that is never set up, and the static link
expects symbols from the skipped startup files. CI builds have failed
with undefined _init/_fini on ARM with clang and an undefined _DYNAMIC
on LoongArch.

Build the test with the in-tree nolibc instead. nolibc provides the
startup code, so the test returns from main(), and it provides the
write() and syscall() wrappers without the TLS errno problem. A failing
sysinfo() is now reported as a TAP failure instead of crashing the
test. Keep the static link and the existing TAP output.

Since the test no longer uses the system C library headers, the kernel
UAPI headers have to be installed before it is built. Add the headers
target as an order-only prerequisite, as nolibc-test does, so that a
standalone build installs them. Link libgcc only when the selected
compiler is GCC, which needs it for division helpers on ARM, and detect
that from the compiler rather than from LLVM so that CC=clang builds do
not try to link a libgcc that clang does not provide.

nolibc does not support arc, csky, microblaze, nios2 or xtensa, so the
test can no longer be built for those architectures. This conversion
also makes the separate i386 vDSO workaround proposed for get_size
unnecessary.

Link: https://lore.kernel.org/r/[email protected]
Fixes: 3ce51050fadd ("selftest: size: Add size test for Linux kernel")
Cc: [email protected] # 6.18.x
Signed-off-by: Sasha Levin <[email protected]>
---
 tools/testing/selftests/size/Makefile   |  9 ++++++-
 tools/testing/selftests/size/get_size.c | 33 +++++++------------------
 2 files changed, 17 insertions(+), 25 deletions(-)

diff --git a/tools/testing/selftests/size/Makefile 
b/tools/testing/selftests/size/Makefile
index b87facc00a6e9..884b42d7fd0e2 100644
--- a/tools/testing/selftests/size/Makefile
+++ b/tools/testing/selftests/size/Makefile
@@ -1,6 +1,13 @@
 # SPDX-License-Identifier: GPL-2.0-only
-CFLAGS := -static -ffreestanding -nostartfiles -s
+CFLAGS = -Os -static -nostdlib -nostdinc -ffreestanding -fno-stack-protector -s
+CFLAGS += -isystem $(top_srcdir)/tools/include/nolibc $(KHDR_INCLUDES)
 
 TEST_GEN_PROGS := get_size
 
 include ../lib.mk
+
+CC_IS_GCC := $(shell printf '\043if defined(__GNUC__) && 
!defined(__clang__)\nGCC\n\043endif\n' | \
+       $(CC) $(CFLAGS) -E -P -x c - 2>/dev/null)
+LDLIBS += $(if $(filter GCC,$(CC_IS_GCC)),-lgcc)
+
+$(OUTPUT)/get_size: | headers
diff --git a/tools/testing/selftests/size/get_size.c 
b/tools/testing/selftests/size/get_size.c
index 2980b1a63366b..ada6a271711a7 100644
--- a/tools/testing/selftests/size/get_size.c
+++ b/tools/testing/selftests/size/get_size.c
@@ -11,26 +11,12 @@
  * own execution.  It also attempts to have as few dependencies
  * on kernel features as possible.
  *
- * It should be statically linked, with startup libs avoided.  It uses
- * no library calls except the syscall() function for the following 3
- * syscalls:
- *   sysinfo(), write(), and _exit()
- *
- * For output, it avoids printf (which in some C libraries
- * has large external dependencies) by  implementing it's own
- * number output and print routines, and using __builtin_strlen()
- *
- * The test may crash if any of the above syscalls fails because in some
- * libc implementations (e.g. the GNU C Library) errno is saved in
- * thread-local storage, which does not get initialized due to avoiding
- * startup libs.
+ * It is statically linked with nolibc startup and syscall wrappers.
+ * For output, it avoids printf by implementing its own print routines.
  */
 
-#include <sys/sysinfo.h>
-#include <unistd.h>
-#include <sys/syscall.h>
-
-#define STDOUT_FILENO 1
+#include <nolibc.h>
+#include <linux/sysinfo.h>
 
 static int print(const char *s)
 {
@@ -39,7 +25,7 @@ static int print(const char *s)
        while (s[len] != '\0')
                len++;
 
-       return syscall(SYS_write, STDOUT_FILENO, s, len);
+       return write(STDOUT_FILENO, s, len);
 }
 
 static inline char *num_to_str(unsigned long num, char *buf, int len)
@@ -80,8 +66,7 @@ static int print_k_value(const char *s, unsigned long num, 
unsigned long units)
        return ccode;
 }
 
-/* this program has no main(), as startup libraries are not used */
-void _start(void)
+int main(void)
 {
        int ccode;
        struct sysinfo info;
@@ -91,12 +76,12 @@ void _start(void)
        print("TAP version 13\n");
        print("# Testing system size.\n");
 
-       ccode = syscall(SYS_sysinfo, &info);
+       ccode = syscall(__NR_sysinfo, &info);
        if (ccode < 0) {
                print("not ok 1");
                print(test_name);
                print(" ---\n reason: \"could not get sysinfo\"\n ...\n");
-               syscall(SYS_exit, ccode);
+               return 1;
        }
        print("ok 1");
        print(test_name);
@@ -112,5 +97,5 @@ void _start(void)
        print(" ...\n");
        print("1..1\n");
 
-       syscall(SYS_exit, 0);
+       return 0;
 }
-- 
2.53.0


Reply via email to