Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests: vDSO: vdso_standalone_test_x86: Switch to nolibc

vdso_standalone_test_x86 provides its own ASM syscall wrappers and
_start() implementation. The in-tree nolibc library already provides
this functionality for multiple architectures. By making use of nolibc,
the standalone testcase can be built from the exact same codebase as the
non-standalone version.

Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Vincenzo Frascino <vincenzo.frascino@arm.com>
Acked-by: Shuah Khan <skhan@linuxfoundation.org>
Link: https://lore.kernel.org/all/20250226-parse_vdso-nolibc-v2-16-28e14e031ed8@linutronix.de

authored by

Thomas Weißschuh and committed by
Thomas Gleixner
8770a918 4f65df6a

+50 -155
+6 -2
tools/testing/selftests/vDSO/Makefile
··· 22 22 23 23 CFLAGS += $(TOOLS_INCLUDES) 24 24 25 + CFLAGS_NOLIBC := -nostdlib -nostdinc -ffreestanding -fno-asynchronous-unwind-tables \ 26 + -fno-stack-protector -include $(top_srcdir)/tools/include/nolibc/nolibc.h \ 27 + -I$(top_srcdir)/tools/include/nolibc/ $(KHDR_INCLUDES) 28 + 25 29 $(OUTPUT)/vdso_test_gettimeofday: parse_vdso.c vdso_test_gettimeofday.c 26 30 $(OUTPUT)/vdso_test_getcpu: parse_vdso.c vdso_test_getcpu.c 27 31 $(OUTPUT)/vdso_test_abi: parse_vdso.c vdso_test_abi.c 28 32 $(OUTPUT)/vdso_test_clock_getres: vdso_test_clock_getres.c 29 33 30 - $(OUTPUT)/vdso_standalone_test_x86: vdso_standalone_test_x86.c parse_vdso.c 31 - $(OUTPUT)/vdso_standalone_test_x86: CFLAGS +=-nostdlib -fno-asynchronous-unwind-tables -fno-stack-protector 34 + $(OUTPUT)/vdso_standalone_test_x86: vdso_standalone_test_x86.c parse_vdso.c | headers 35 + $(OUTPUT)/vdso_standalone_test_x86: CFLAGS:=$(CFLAGS_NOLIBC) $(CFLAGS) 32 36 33 37 $(OUTPUT)/vdso_test_correctness: vdso_test_correctness.c 34 38 $(OUTPUT)/vdso_test_correctness: LDFLAGS += -ldl
+44 -153
tools/testing/selftests/vDSO/vdso_standalone_test_x86.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 /* 3 - * vdso_test.c: Sample code to test parse_vdso.c on x86 4 - * Copyright (c) 2011-2014 Andy Lutomirski 3 + * vdso_test_gettimeofday.c: Sample code to test parse_vdso.c and 4 + * vDSO gettimeofday() 5 + * Copyright (c) 2014 Andy Lutomirski 5 6 * 6 - * You can amuse yourself by compiling with: 7 - * gcc -std=gnu99 -nostdlib 8 - * -Os -fno-asynchronous-unwind-tables -flto -lgcc_s 9 - * vdso_standalone_test_x86.c parse_vdso.c 10 - * to generate a small binary. On x86_64, you can omit -lgcc_s 11 - * if you want the binary to be completely standalone. 7 + * Compile with: 8 + * gcc -std=gnu99 vdso_test_gettimeofday.c parse_vdso_gettimeofday.c 9 + * 10 + * Tested on x86, 32-bit and 64-bit. It may work on other architectures, too. 12 11 */ 13 12 14 - #include <sys/syscall.h> 13 + #include <stdio.h> 14 + #ifndef NOLIBC 15 + #include <sys/auxv.h> 15 16 #include <sys/time.h> 16 - #include <unistd.h> 17 - #include <stdint.h> 18 - #include <linux/auxvec.h> 17 + #endif 19 18 19 + #include "../kselftest.h" 20 20 #include "parse_vdso.h" 21 + #include "vdso_config.h" 22 + #include "vdso_call.h" 21 23 22 - /* We need some libc functions... */ 23 - int strcmp(const char *a, const char *b) 24 + int main(int argc, char **argv) 24 25 { 25 - /* This implementation is buggy: it never returns -1. */ 26 - while (*a || *b) { 27 - if (*a != *b) 28 - return 1; 29 - if (*a == 0 || *b == 0) 30 - return 1; 31 - a++; 32 - b++; 26 + const char *version = versions[VDSO_VERSION]; 27 + const char **name = (const char **)&names[VDSO_NAMES]; 28 + 29 + unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR); 30 + if (!sysinfo_ehdr) { 31 + printf("AT_SYSINFO_EHDR is not present!\n"); 32 + return KSFT_SKIP; 33 + } 34 + 35 + vdso_init_from_sysinfo_ehdr(getauxval(AT_SYSINFO_EHDR)); 36 + 37 + /* Find gettimeofday. */ 38 + typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz); 39 + gtod_t gtod = (gtod_t)vdso_sym(version, name[0]); 40 + 41 + if (!gtod) { 42 + printf("Could not find %s\n", name[0]); 43 + return KSFT_SKIP; 44 + } 45 + 46 + struct timeval tv; 47 + long ret = VDSO_CALL(gtod, 2, &tv, 0); 48 + 49 + if (ret == 0) { 50 + printf("The time is %lld.%06lld\n", 51 + (long long)tv.tv_sec, (long long)tv.tv_usec); 52 + } else { 53 + printf("%s failed\n", name[0]); 54 + return KSFT_FAIL; 33 55 } 34 56 35 57 return 0; 36 58 } 37 - 38 - /* 39 - * The clang build needs this, although gcc does not. 40 - * Stolen from lib/string.c. 41 - */ 42 - void *memcpy(void *dest, const void *src, size_t count) 43 - { 44 - char *tmp = dest; 45 - const char *s = src; 46 - 47 - while (count--) 48 - *tmp++ = *s++; 49 - return dest; 50 - } 51 - 52 - /* ...and two syscalls. This is x86-specific. */ 53 - static inline long x86_syscall3(long nr, long a0, long a1, long a2) 54 - { 55 - long ret; 56 - #ifdef __x86_64__ 57 - asm volatile ("syscall" : "=a" (ret) : "a" (nr), 58 - "D" (a0), "S" (a1), "d" (a2) : 59 - "cc", "memory", "rcx", 60 - "r8", "r9", "r10", "r11" ); 61 - #else 62 - asm volatile ("int $0x80" : "=a" (ret) : "a" (nr), 63 - "b" (a0), "c" (a1), "d" (a2) : 64 - "cc", "memory" ); 65 - #endif 66 - return ret; 67 - } 68 - 69 - static inline long linux_write(int fd, const void *data, size_t len) 70 - { 71 - return x86_syscall3(__NR_write, fd, (long)data, (long)len); 72 - } 73 - 74 - static inline void linux_exit(int code) 75 - { 76 - x86_syscall3(__NR_exit, code, 0, 0); 77 - } 78 - 79 - void to_base10(char *lastdig, time_t n) 80 - { 81 - while (n) { 82 - *lastdig = (n % 10) + '0'; 83 - n /= 10; 84 - lastdig--; 85 - } 86 - } 87 - 88 - unsigned long getauxval(const unsigned long *auxv, unsigned long type) 89 - { 90 - unsigned long ret; 91 - 92 - if (!auxv) 93 - return 0; 94 - 95 - while (1) { 96 - if (!auxv[0] && !auxv[1]) { 97 - ret = 0; 98 - break; 99 - } 100 - 101 - if (auxv[0] == type) { 102 - ret = auxv[1]; 103 - break; 104 - } 105 - 106 - auxv += 2; 107 - } 108 - 109 - return ret; 110 - } 111 - 112 - void c_main(void **stack) 113 - { 114 - /* Parse the stack */ 115 - long argc = (long)*stack; 116 - stack += argc + 2; 117 - 118 - /* Now we're pointing at the environment. Skip it. */ 119 - while(*stack) 120 - stack++; 121 - stack++; 122 - 123 - /* Now we're pointing at auxv. Initialize the vDSO parser. */ 124 - vdso_init_from_sysinfo_ehdr(getauxval((unsigned long *)stack, AT_SYSINFO_EHDR)); 125 - 126 - /* Find gettimeofday. */ 127 - typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz); 128 - gtod_t gtod = (gtod_t)vdso_sym("LINUX_2.6", "__vdso_gettimeofday"); 129 - 130 - if (!gtod) 131 - linux_exit(1); 132 - 133 - struct timeval tv; 134 - long ret = gtod(&tv, 0); 135 - 136 - if (ret == 0) { 137 - char buf[] = "The time is .000000\n"; 138 - to_base10(buf + 31, tv.tv_sec); 139 - to_base10(buf + 38, tv.tv_usec); 140 - linux_write(1, buf, sizeof(buf) - 1); 141 - } else { 142 - linux_exit(ret); 143 - } 144 - 145 - linux_exit(0); 146 - } 147 - 148 - /* 149 - * This is the real entry point. It passes the initial stack into 150 - * the C entry point. 151 - */ 152 - asm ( 153 - ".text\n" 154 - ".global _start\n" 155 - ".type _start,@function\n" 156 - "_start:\n\t" 157 - #ifdef __x86_64__ 158 - "mov %rsp,%rdi\n\t" 159 - "and $-16,%rsp\n\t" 160 - "sub $8,%rsp\n\t" 161 - "jmp c_main" 162 - #else 163 - "push %esp\n\t" 164 - "call c_main\n\t" 165 - "int $3" 166 - #endif 167 - );