A few functions used by different selftests. Adding them now avoids later conflicts between different selftest serieses.
Also add full support for nolibc-test.c on riscv32. All unsupported syscalls have been replaced.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- Changes in v2: - Rebase onto latest nolibc next branch - Move #include "nolibc.h" to the top of the headers - Include linux/random.h from sys/random.h - Don't block on missing entropy in getrandom() selftest - Also test negative result of difftime() - Simplify fopen() - Link to v1: https://lore.kernel.org/r/20250423-nolibc-misc-v1-0-a925bf40297b@linutronix....
--- Thomas Weißschuh (15): tools/nolibc: add strstr() tools/nolibc: add %m printf format tools/nolibc: add more stat() variants tools/nolibc: add mremap() tools/nolibc: add getrandom() tools/nolibc: add abs() and friends tools/nolibc: add support for access() and faccessat() tools/nolibc: add clock_getres(), clock_gettime() and clock_settime() tools/nolibc: add timer functions tools/nolibc: add timerfd functionality tools/nolibc: add difftime() tools/nolibc: add namespace functionality tools/nolibc: add fopen() tools/nolibc: fall back to sys_clock_gettime() in gettimeofday() tools/nolibc: implement wait() in terms of waitpid()
tools/include/nolibc/Makefile | 4 + tools/include/nolibc/math.h | 31 ++++ tools/include/nolibc/nolibc.h | 4 + tools/include/nolibc/sched.h | 50 +++++ tools/include/nolibc/stdio.h | 27 +++ tools/include/nolibc/stdlib.h | 18 ++ tools/include/nolibc/string.h | 20 ++ tools/include/nolibc/sys/mman.h | 19 ++ tools/include/nolibc/sys/random.h | 34 ++++ tools/include/nolibc/sys/stat.h | 25 ++- tools/include/nolibc/sys/time.h | 15 +- tools/include/nolibc/sys/timerfd.h | 87 +++++++++ tools/include/nolibc/sys/wait.h | 12 +- tools/include/nolibc/time.h | 185 ++++++++++++++++++ tools/include/nolibc/types.h | 3 + tools/include/nolibc/unistd.h | 28 +++ tools/testing/selftests/nolibc/Makefile | 2 + tools/testing/selftests/nolibc/nolibc-test.c | 268 ++++++++++++++++++++++++++- 18 files changed, 820 insertions(+), 12 deletions(-) --- base-commit: 2051d3b830c0889ae55e37e9e8ff0d43a4acd482 change-id: 20250415-nolibc-misc-f2548ccc5ce1
Best regards,
This is used in various selftests and will be handy when integrating those with nolibc.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de Acked-by: Willy Tarreau w@1wt.eu --- tools/include/nolibc/string.h | 20 ++++++++++++++++++++ tools/testing/selftests/nolibc/nolibc-test.c | 3 +++ 2 files changed, 23 insertions(+)
diff --git a/tools/include/nolibc/string.h b/tools/include/nolibc/string.h index febfd69789666e2445b82423a5f4df5e18a1c774..163a17e7dd38b48ba8d9218e10733b3600710477 100644 --- a/tools/include/nolibc/string.h +++ b/tools/include/nolibc/string.h @@ -292,6 +292,26 @@ char *strrchr(const char *s, int c) return (char *)ret; }
+static __attribute__((unused)) +char *strstr(const char *haystack, const char *needle) +{ + size_t len_haystack, len_needle; + + len_needle = strlen(needle); + if (!len_needle) + return NULL; + + len_haystack = strlen(haystack); + while (len_haystack >= len_needle) { + if (!memcmp(haystack, needle, len_needle)) + return (char *)haystack; + haystack++; + len_haystack--; + } + + return NULL; +} + static __attribute__((unused)) int tolower(int c) { diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index 1ad0db92f0ed47f708363b2e558717fa0e686b8f..3e15a25ccddf5135db1f59bce1eefdff2ffe57f6 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1211,6 +1211,9 @@ int run_stdlib(int min, int max) CASE_TEST(strlcpy_2); EXPECT_STRBUFEQ(is_nolibc, strlcpy(buf, "bar", 2), buf, 3, "b"); break; CASE_TEST(strlcpy_3); EXPECT_STRBUFEQ(is_nolibc, strlcpy(buf, "bar", 3), buf, 3, "ba"); break; CASE_TEST(strlcpy_4); EXPECT_STRBUFEQ(is_nolibc, strlcpy(buf, "bar", 4), buf, 3, "bar"); break; + CASE_TEST(strstr_foobar_foo); EXPECT_STREQ(1, strstr("foobar", "foo"), "foobar"); break; + CASE_TEST(strstr_foobar_bar); EXPECT_STREQ(1, strstr("foobar", "bar"), "bar"); break; + CASE_TEST(strstr_foobar_baz); EXPECT_PTREQ(1, strstr("foobar", "baz"), NULL); break; CASE_TEST(memcmp_20_20); EXPECT_EQ(1, memcmp("aaa\x20", "aaa\x20", 4), 0); break; CASE_TEST(memcmp_20_60); EXPECT_LT(1, memcmp("aaa\x20", "aaa\x60", 4), 0); break; CASE_TEST(memcmp_60_20); EXPECT_GT(1, memcmp("aaa\x60", "aaa\x20", 4), 0); break;
The %m format can be used to format the current errno. It is non-standard but supported by other commonly used libcs like glibc and musl, so applications do rely on them.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de Acked-by: Willy Tarreau w@1wt.eu --- tools/include/nolibc/stdio.h | 7 +++++++ tools/testing/selftests/nolibc/nolibc-test.c | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+)
diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h index 8fa98abab2127c2cd175201d2f50d71b78d03559..df5717d591826c8468e3473581ac0d6e76629aa3 100644 --- a/tools/include/nolibc/stdio.h +++ b/tools/include/nolibc/stdio.h @@ -20,6 +20,8 @@ #include "string.h" #include "compiler.h"
+static const char *strerror(int errnum); + #ifndef EOF #define EOF (-1) #endif @@ -292,6 +294,11 @@ int __nolibc_printf(__nolibc_printf_cb cb, intptr_t state, size_t n, const char if (!outstr) outstr="(null)"; } +#ifndef NOLIBC_IGNORE_ERRNO + else if (c == 'm') { + outstr = strerror(errno); + } +#endif /* NOLIBC_IGNORE_ERRNO */ else if (c == '%') { /* queue it verbatim */ continue; diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index 3e15a25ccddf5135db1f59bce1eefdff2ffe57f6..b7440a667db6b541a2548bdf5182bee0277100ed 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1393,6 +1393,23 @@ 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 run_printf(int min, int max) { int test; @@ -1421,6 +1438,7 @@ static int run_printf(int min, int max) CASE_TEST(number_width); EXPECT_VFPRINTF(10, " 1", "%10d", 1); break; CASE_TEST(width_trunc); EXPECT_VFPRINTF(25, " ", "%25d", 1); break; CASE_TEST(scanf); EXPECT_ZR(1, test_scanf()); break; + CASE_TEST(strerror); EXPECT_ZR(1, test_strerror()); break; case __LINE__: return ret; /* must be last */ /* note: do not set any defaults so as to permit holes above */
Add fstat(), fstatat() and lstat(). All of them use the existing implementation based on statx().
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de Acked-by: Willy Tarreau w@1wt.eu --- tools/include/nolibc/sys/stat.h | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-)
diff --git a/tools/include/nolibc/sys/stat.h b/tools/include/nolibc/sys/stat.h index 987c8bb52502f1f758005b0afdfb2e39fc8ea1c6..8b4d80e3ea0360e3e6280f6c6166e87f793ddee4 100644 --- a/tools/include/nolibc/sys/stat.h +++ b/tools/include/nolibc/sys/stat.h @@ -17,6 +17,9 @@ /* * int statx(int fd, const char *path, int flags, unsigned int mask, struct statx *buf); * int stat(const char *path, struct stat *buf); + * int fstatat(int fd, const char *path, struct stat *buf, int flag); + * int fstat(int fildes, struct stat *buf); + * int lstat(const char *path, struct stat *buf); */
static __attribute__((unused)) @@ -37,12 +40,12 @@ int statx(int fd, const char *path, int flags, unsigned int mask, struct statx *
static __attribute__((unused)) -int stat(const char *path, struct stat *buf) +int fstatat(int fd, const char *path, struct stat *buf, int flag) { struct statx statx; long ret;
- ret = __sysret(sys_statx(AT_FDCWD, path, AT_NO_AUTOMOUNT, STATX_BASIC_STATS, &statx)); + ret = __sysret(sys_statx(fd, path, flag | AT_NO_AUTOMOUNT, STATX_BASIC_STATS, &statx)); if (ret == -1) return ret;
@@ -70,4 +73,22 @@ int stat(const char *path, struct stat *buf) return 0; }
+static __attribute__((unused)) +int stat(const char *path, struct stat *buf) +{ + return fstatat(AT_FDCWD, path, buf, 0); +} + +static __attribute__((unused)) +int fstat(int fildes, struct stat *buf) +{ + return fstatat(fildes, "", buf, AT_EMPTY_PATH); +} + +static __attribute__((unused)) +int lstat(const char *path, struct stat *buf) +{ + return fstatat(AT_FDCWD, path, buf, AT_SYMLINK_NOFOLLOW); +} + #endif /* _NOLIBC_SYS_STAT_H */
This is used in various selftests and will be handy when integrating those with nolibc.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de Acked-by: Willy Tarreau w@1wt.eu --- tools/include/nolibc/sys/mman.h | 19 +++++++++++++++++++ tools/testing/selftests/nolibc/nolibc-test.c | 14 +++++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-)
diff --git a/tools/include/nolibc/sys/mman.h b/tools/include/nolibc/sys/mman.h index 41c7bf45e427b96efec949364890e9c2b9c226a5..5228751b458c8d8a1bdfb5006623d016a48155fb 100644 --- a/tools/include/nolibc/sys/mman.h +++ b/tools/include/nolibc/sys/mman.h @@ -48,6 +48,25 @@ void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) return ret; }
+static __attribute__((unused)) +void *sys_mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) +{ + return (void *)my_syscall5(__NR_mremap, old_address, old_size, + new_size, flags, new_address); +} + +static __attribute__((unused)) +void *mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) +{ + void *ret = sys_mremap(old_address, old_size, new_size, flags, new_address); + + if ((unsigned long)ret >= -4095UL) { + SET_ERRNO(-(long)ret); + ret = MAP_FAILED; + } + return ret; +} + static __attribute__((unused)) int sys_munmap(void *addr, size_t length) { diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index b7440a667db6b541a2548bdf5182bee0277100ed..abe0ae794208762f6d91ad81e902fbf77253a1c1 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -926,7 +926,7 @@ int test_mmap_munmap(void) { int ret, fd, i, page_size; void *mem; - size_t file_size, length; + size_t file_size, length, mem_length; off_t offset, pa_offset; struct stat stat_buf; const char * const files[] = { @@ -966,14 +966,22 @@ int test_mmap_munmap(void) offset = 0; length = file_size - offset; pa_offset = offset & ~(page_size - 1); + mem_length = length + offset - pa_offset;
- mem = mmap(NULL, length + offset - pa_offset, PROT_READ, MAP_SHARED, fd, pa_offset); + mem = mmap(NULL, mem_length, PROT_READ, MAP_SHARED, fd, pa_offset); if (mem == MAP_FAILED) { ret = 1; goto end; }
- ret = munmap(mem, length + offset - pa_offset); + mem = mremap(mem, mem_length, mem_length * 2, MREMAP_MAYMOVE, 0); + if (mem == MAP_FAILED) { + munmap(mem, mem_length); + ret = 1; + goto end; + } + + ret = munmap(mem, mem_length * 2);
end: close(fd);
This is used in various selftests and will be handy when integrating those with nolibc.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de Acked-by: Willy Tarreau w@1wt.eu --- tools/include/nolibc/Makefile | 1 + tools/include/nolibc/nolibc.h | 1 + tools/include/nolibc/sys/random.h | 34 ++++++++++++++++++++++++++++ tools/testing/selftests/nolibc/nolibc-test.c | 22 ++++++++++++++++++ 4 files changed, 58 insertions(+)
diff --git a/tools/include/nolibc/Makefile b/tools/include/nolibc/Makefile index 41ef18872a7e9435a5efec9c0b32b9e29fcd4ce8..4bbe3b7fa2805f356d9a5cc1e4a8ee55b05af746 100644 --- a/tools/include/nolibc/Makefile +++ b/tools/include/nolibc/Makefile @@ -47,6 +47,7 @@ all_files := \ sys.h \ sys/auxv.h \ sys/mman.h \ + sys/random.h \ sys/stat.h \ sys/syscall.h \ sys/time.h \ diff --git a/tools/include/nolibc/nolibc.h b/tools/include/nolibc/nolibc.h index d1b949e094eeb7cc0fe875deeafa4c972ecf35b2..3f329feb379c4c808d2e56fbb2b7a6c4b08a7c0d 100644 --- a/tools/include/nolibc/nolibc.h +++ b/tools/include/nolibc/nolibc.h @@ -98,6 +98,7 @@ #include "sys.h" #include "sys/auxv.h" #include "sys/mman.h" +#include "sys/random.h" #include "sys/stat.h" #include "sys/syscall.h" #include "sys/time.h" diff --git a/tools/include/nolibc/sys/random.h b/tools/include/nolibc/sys/random.h new file mode 100644 index 0000000000000000000000000000000000000000..8d9749f1c84572ab07c6dc70f331583063266eaf --- /dev/null +++ b/tools/include/nolibc/sys/random.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: LGPL-2.1 OR MIT */ +/* + * random definitions for NOLIBC + * Copyright (C) 2025 Thomas Weißschuh thomas.weissschuh@linutronix.de + */ + +/* make sure to include all global symbols */ +#include "../nolibc.h" + +#ifndef _NOLIBC_SYS_RANDOM_H +#define _NOLIBC_SYS_RANDOM_H + +#include "../arch.h" +#include "../sys.h" + +#include <linux/random.h> + +/* + * ssize_t getrandom(void *buf, size_t buflen, unsigned int flags); + */ + +static __attribute__((unused)) +ssize_t sys_getrandom(void *buf, size_t buflen, unsigned int flags) +{ + return my_syscall3(__NR_getrandom, buf, buflen, flags); +} + +static __attribute__((unused)) +ssize_t getrandom(void *buf, size_t buflen, unsigned int flags) +{ + return __sysret(sys_getrandom(buf, buflen, flags)); +} + +#endif /* _NOLIBC_SYS_RANDOM_H */ diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index abe0ae794208762f6d91ad81e902fbf77253a1c1..df1cb3e62564f1fa78c55740f7c2403b8390168f 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -20,6 +20,7 @@ #include <sys/mman.h> #include <sys/mount.h> #include <sys/prctl.h> +#include <sys/random.h> #include <sys/reboot.h> #include <sys/resource.h> #include <sys/stat.h> @@ -807,6 +808,26 @@ static int test_dirent(void) return 0; }
+int test_getrandom(void) +{ + uint64_t rng = 0; + ssize_t ret; + + ret = getrandom(&rng, sizeof(rng), GRND_NONBLOCK); + if (ret == -1 && errno == EAGAIN) + return 0; /* No entropy available yet */ + + if (ret != sizeof(rng)) + return ret; + + if (!rng) { + errno = EINVAL; + return -1; + } + + return 0; +} + int test_getpagesize(void) { int x = getpagesize(); @@ -1124,6 +1145,7 @@ int run_syscall(int min, int max) CASE_TEST(getdents64_root); EXPECT_SYSNE(1, test_getdents64("/"), -1); break; CASE_TEST(getdents64_null); EXPECT_SYSER(1, test_getdents64("/dev/null"), -1, ENOTDIR); break; CASE_TEST(directories); EXPECT_SYSZR(proc, test_dirent()); break; + CASE_TEST(getrandom); EXPECT_SYSZR(1, test_getrandom()); break; CASE_TEST(gettimeofday_tv); EXPECT_SYSZR(1, gettimeofday(&tv, NULL)); break; CASE_TEST(gettimeofday_tv_tz);EXPECT_SYSZR(1, gettimeofday(&tv, &tz)); break; CASE_TEST(getpagesize); EXPECT_SYSZR(1, test_getpagesize()); break;
This is used in various selftests and will be handy when integrating those with nolibc.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de Acked-by: Willy Tarreau w@1wt.eu --- tools/include/nolibc/Makefile | 1 + tools/include/nolibc/math.h | 31 ++++++++++++++++++++++++++++ tools/include/nolibc/nolibc.h | 1 + tools/include/nolibc/stdlib.h | 18 ++++++++++++++++ tools/testing/selftests/nolibc/nolibc-test.c | 2 ++ 5 files changed, 53 insertions(+)
diff --git a/tools/include/nolibc/Makefile b/tools/include/nolibc/Makefile index 4bbe3b7fa2805f356d9a5cc1e4a8ee55b05af746..552e44f5a48211f81c68068b9b99c1dd6e44e9d8 100644 --- a/tools/include/nolibc/Makefile +++ b/tools/include/nolibc/Makefile @@ -35,6 +35,7 @@ all_files := \ fcntl.h \ getopt.h \ limits.h \ + math.h \ nolibc.h \ signal.h \ stackprotector.h \ diff --git a/tools/include/nolibc/math.h b/tools/include/nolibc/math.h new file mode 100644 index 0000000000000000000000000000000000000000..9df823ddd41297436015333d66561f810f101d06 --- /dev/null +++ b/tools/include/nolibc/math.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: LGPL-2.1 OR MIT */ +/* + * math definitions for NOLIBC + * Copyright (C) 2025 Thomas Weißschuh thomas.weissschuh@linutronix.de + */ + +/* make sure to include all global symbols */ +#include "nolibc.h" + +#ifndef _NOLIBC_SYS_MATH_H +#define _NOLIBC_SYS_MATH_H + +static __inline__ +double fabs(double x) +{ + return x >= 0 ? x : -x; +} + +static __inline__ +float fabsf(float x) +{ + return x >= 0 ? x : -x; +} + +static __inline__ +long double fabsl(long double x) +{ + return x >= 0 ? x : -x; +} + +#endif /* _NOLIBC_SYS_MATH_H */ diff --git a/tools/include/nolibc/nolibc.h b/tools/include/nolibc/nolibc.h index 3f329feb379c4c808d2e56fbb2b7a6c4b08a7c0d..2207f3d3c5cf92b96150149050f0bde0ed7e7147 100644 --- a/tools/include/nolibc/nolibc.h +++ b/tools/include/nolibc/nolibc.h @@ -115,6 +115,7 @@ #include "dirent.h" #include "fcntl.h" #include "getopt.h" +#include "math.h"
/* Used by programs to avoid std includes */ #define NOLIBC diff --git a/tools/include/nolibc/stdlib.h b/tools/include/nolibc/stdlib.h index 4790298f985c2b0db91ec777140201b7ec869346..5fd99a480f821210212f08d814a4c09c2c804db8 100644 --- a/tools/include/nolibc/stdlib.h +++ b/tools/include/nolibc/stdlib.h @@ -32,6 +32,24 @@ static __attribute__((unused)) char itoa_buffer[21]; * As much as possible, please keep functions alphabetically sorted. */
+static __inline__ +int abs(int j) +{ + return j >= 0 ? j : -j; +} + +static __inline__ +long labs(long j) +{ + return j >= 0 ? j : -j; +} + +static __inline__ +long long llabs(long long j) +{ + return j >= 0 ? j : -j; +} + /* must be exported, as it's used by libgcc for various divide functions */ void abort(void); __attribute__((weak,unused,noreturn,section(".text.nolibc_abort"))) diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index df1cb3e62564f1fa78c55740f7c2403b8390168f..14a27bc6c83e4fda595b10dc29cf56b63904272a 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1318,6 +1318,8 @@ int run_stdlib(int min, int max) CASE_TEST(tolower_noop); EXPECT_EQ(1, tolower('a'), 'a'); break; CASE_TEST(toupper); EXPECT_EQ(1, toupper('a'), 'A'); break; CASE_TEST(toupper_noop); EXPECT_EQ(1, toupper('A'), 'A'); break; + CASE_TEST(abs); EXPECT_EQ(1, abs(-10), 10); break; + CASE_TEST(abs_noop); EXPECT_EQ(1, abs(10), 10); break;
case __LINE__: return ret; /* must be last */
This is used in various selftests and will be handy when integrating those with nolibc.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de Acked-by: Willy Tarreau w@1wt.eu --- tools/include/nolibc/unistd.h | 28 ++++++++++++++++++++++++++++ tools/testing/selftests/nolibc/nolibc-test.c | 2 ++ 2 files changed, 30 insertions(+)
diff --git a/tools/include/nolibc/unistd.h b/tools/include/nolibc/unistd.h index ed253305fdbafd040ca5820207d8c53c6ea9f9fc..25bfc7732ec7e72a60f445588a233f72c008f7ef 100644 --- a/tools/include/nolibc/unistd.h +++ b/tools/include/nolibc/unistd.h @@ -20,6 +20,34 @@ #define STDOUT_FILENO 1 #define STDERR_FILENO 2
+#define F_OK 0 +#define X_OK 1 +#define W_OK 2 +#define R_OK 4 + +/* + * int access(const char *path, int amode); + * int faccessat(int fd, const char *path, int amode, int flag); + */ + +static __attribute__((unused)) +int sys_faccessat(int fd, const char *path, int amode, int flag) +{ + return my_syscall4(__NR_faccessat, fd, path, amode, flag); +} + +static __attribute__((unused)) +int faccessat(int fd, const char *path, int amode, int flag) +{ + return __sysret(sys_faccessat(fd, path, amode, flag)); +} + +static __attribute__((unused)) +int access(const char *path, int amode) +{ + return faccessat(AT_FDCWD, path, amode, 0); +} +
static __attribute__((unused)) int msleep(unsigned int msecs) diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index 14a27bc6c83e4fda595b10dc29cf56b63904272a..10db118b8b111ec17f713d57240f193c7b18c70f 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1112,6 +1112,8 @@ int run_syscall(int min, int max) * test numbers. */ switch (test + __LINE__ + 1) { + CASE_TEST(access); EXPECT_SYSZR(proc, access("/proc/self", R_OK)); break; + CASE_TEST(access_bad); EXPECT_SYSER(proc, access("/proc/self", W_OK), -1, EPERM); break; CASE_TEST(getpid); EXPECT_SYSNE(1, getpid(), -1); break; CASE_TEST(getppid); EXPECT_SYSNE(1, getppid(), -1); break; CASE_TEST(gettid); EXPECT_SYSNE(has_gettid, gettid(), -1); break;
This is used in various selftests and will be handy when integrating those with nolibc.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de Acked-by: Willy Tarreau w@1wt.eu --- tools/include/nolibc/time.h | 92 ++++++++++++++++++++++++++++ tools/include/nolibc/types.h | 2 + tools/testing/selftests/nolibc/nolibc-test.c | 5 ++ 3 files changed, 99 insertions(+)
diff --git a/tools/include/nolibc/time.h b/tools/include/nolibc/time.h index 9502f9aaf62110ec8ee113fcf08dd47de40e924c..7e0b7eac0b7c31910a48ebb9755d5004e5da1a69 100644 --- a/tools/include/nolibc/time.h +++ b/tools/include/nolibc/time.h @@ -15,6 +15,98 @@ #include "types.h" #include "sys.h"
+#include <linux/time.h> + +static __inline__ +void __nolibc_timespec_user_to_kernel(const struct timespec *ts, struct __kernel_timespec *kts) +{ + kts->tv_sec = ts->tv_sec; + kts->tv_nsec = ts->tv_nsec; +} + +static __inline__ +void __nolibc_timespec_kernel_to_user(const struct __kernel_timespec *kts, struct timespec *ts) +{ + ts->tv_sec = kts->tv_sec; + ts->tv_nsec = kts->tv_nsec; +} + +/* + * int clock_getres(clockid_t clockid, struct timespec *res); + * int clock_gettime(clockid_t clockid, struct timespec *tp); + * int clock_settime(clockid_t clockid, const struct timespec *tp); + */ + +static __attribute__((unused)) +int sys_clock_getres(clockid_t clockid, struct timespec *res) +{ +#if defined(__NR_clock_getres) + return my_syscall2(__NR_clock_getres, clockid, res); +#elif defined(__NR_clock_getres_time64) + struct __kernel_timespec kres; + int ret; + + ret = my_syscall2(__NR_clock_getres_time64, clockid, &kres); + if (res) + __nolibc_timespec_kernel_to_user(&kres, res); + return ret; +#else + return __nolibc_enosys(__func__, clockid, res); +#endif +} + +static __attribute__((unused)) +int clock_getres(clockid_t clockid, struct timespec *res) +{ + return __sysret(sys_clock_getres(clockid, res)); +} + +static __attribute__((unused)) +int sys_clock_gettime(clockid_t clockid, struct timespec *tp) +{ +#if defined(__NR_clock_gettime) + return my_syscall2(__NR_clock_gettime, clockid, tp); +#elif defined(__NR_clock_gettime64) + struct __kernel_timespec ktp; + int ret; + + ret = my_syscall2(__NR_clock_gettime64, clockid, &ktp); + if (tp) + __nolibc_timespec_kernel_to_user(&ktp, tp); + return ret; +#else + return __nolibc_enosys(__func__, clockid, tp); +#endif +} + +static __attribute__((unused)) +int clock_gettime(clockid_t clockid, struct timespec *tp) +{ + return __sysret(sys_clock_gettime(clockid, tp)); +} + +static __attribute__((unused)) +int sys_clock_settime(clockid_t clockid, struct timespec *tp) +{ +#if defined(__NR_clock_settime) + return my_syscall2(__NR_clock_settime, clockid, tp); +#elif defined(__NR_clock_settime64) + struct __kernel_timespec ktp; + + __nolibc_timespec_user_to_kernel(tp, &ktp); + return my_syscall2(__NR_clock_settime64, clockid, &ktp); +#else + return __nolibc_enosys(__func__, clockid, tp); +#endif +} + +static __attribute__((unused)) +int clock_settime(clockid_t clockid, struct timespec *tp) +{ + return __sysret(sys_clock_settime(clockid, tp)); +} + + static __attribute__((unused)) time_t time(time_t *tptr) { diff --git a/tools/include/nolibc/types.h b/tools/include/nolibc/types.h index fe97953d16572db3e4c18cdc8921c6a991d64f94..8eb48d98667f5fe65bb8a974f613fd1fd17ae11e 100644 --- a/tools/include/nolibc/types.h +++ b/tools/include/nolibc/types.h @@ -201,6 +201,8 @@ struct stat { union { time_t st_ctime; struct timespec st_ctim; }; /* time of last status change */ };
+typedef __kernel_clockid_t clockid_t; + /* WARNING, it only deals with the 4096 first majors and 256 first minors */ #define makedev(major, minor) ((dev_t)((((major) & 0xfff) << 8) | ((minor) & 0xff))) #define major(dev) ((unsigned int)(((dev) >> 8) & 0xfff)) diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index 10db118b8b111ec17f713d57240f193c7b18c70f..9b8aa41a547b1735014c5a7541e426f4e08c7f90 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -38,6 +38,7 @@ #include <stdarg.h> #include <stddef.h> #include <stdint.h> +#include <time.h> #include <unistd.h> #include <limits.h> #include <ctype.h> @@ -1081,6 +1082,7 @@ int run_syscall(int min, int max) { struct timeval tv; struct timezone tz; + struct timespec ts; struct stat stat_buf; int euid0; int proc; @@ -1114,6 +1116,9 @@ int run_syscall(int min, int max) switch (test + __LINE__ + 1) { CASE_TEST(access); EXPECT_SYSZR(proc, access("/proc/self", R_OK)); break; CASE_TEST(access_bad); EXPECT_SYSER(proc, access("/proc/self", W_OK), -1, EPERM); break; + CASE_TEST(clock_getres); EXPECT_SYSZR(1, clock_getres(CLOCK_MONOTONIC, &ts)); break; + CASE_TEST(clock_gettime); EXPECT_SYSZR(1, clock_gettime(CLOCK_MONOTONIC, &ts)); break; + CASE_TEST(clock_settime); EXPECT_SYSER(1, clock_settime(CLOCK_MONOTONIC, &ts), -1, EINVAL); break; CASE_TEST(getpid); EXPECT_SYSNE(1, getpid(), -1); break; CASE_TEST(getppid); EXPECT_SYSNE(1, getppid(), -1); break; CASE_TEST(gettid); EXPECT_SYSNE(has_gettid, gettid(), -1); break;
This is used in various selftests and will be handy when integrating those with nolibc.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de Acked-by: Willy Tarreau w@1wt.eu --- tools/include/nolibc/time.h | 86 ++++++++++++++++++++++++++++ tools/include/nolibc/types.h | 1 + tools/testing/selftests/nolibc/nolibc-test.c | 51 +++++++++++++++++ 3 files changed, 138 insertions(+)
diff --git a/tools/include/nolibc/time.h b/tools/include/nolibc/time.h index 7e0b7eac0b7c31910a48ebb9755d5004e5da1a69..48f602b661b300a0bfbac5a231315fc95727c04e 100644 --- a/tools/include/nolibc/time.h +++ b/tools/include/nolibc/time.h @@ -15,6 +15,7 @@ #include "types.h" #include "sys.h"
+#include <linux/signal.h> #include <linux/time.h>
static __inline__ @@ -120,4 +121,89 @@ time_t time(time_t *tptr) return tv.tv_sec; }
+ +/* + * int timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid); + * int timer_gettime(timer_t timerid, struct itimerspec *curr_value); + * int timer_settime(timer_t timerid, int flags, const struct itimerspec *new_value, struct itimerspec *old_value); + */ + +static __attribute__((unused)) +int sys_timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid) +{ + return my_syscall3(__NR_timer_create, clockid, evp, timerid); +} + +static __attribute__((unused)) +int timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid) +{ + return __sysret(sys_timer_create(clockid, evp, timerid)); +} + +static __attribute__((unused)) +int sys_timer_delete(timer_t timerid) +{ + return my_syscall1(__NR_timer_delete, timerid); +} + +static __attribute__((unused)) +int timer_delete(timer_t timerid) +{ + return __sysret(sys_timer_delete(timerid)); +} + +static __attribute__((unused)) +int sys_timer_gettime(timer_t timerid, struct itimerspec *curr_value) +{ +#if defined(__NR_timer_gettime) + return my_syscall2(__NR_timer_gettime, timerid, curr_value); +#elif defined(__NR_timer_gettime64) + struct __kernel_itimerspec kcurr_value; + int ret; + + ret = my_syscall2(__NR_timer_gettime64, timerid, &kcurr_value); + __nolibc_timespec_kernel_to_user(&kcurr_value.it_interval, &curr_value->it_interval); + __nolibc_timespec_kernel_to_user(&kcurr_value.it_value, &curr_value->it_value); + return ret; +#else + return __nolibc_enosys(__func__, timerid, curr_value); +#endif +} + +static __attribute__((unused)) +int timer_gettime(timer_t timerid, struct itimerspec *curr_value) +{ + return __sysret(sys_timer_gettime(timerid, curr_value)); +} + +static __attribute__((unused)) +int sys_timer_settime(timer_t timerid, int flags, + const struct itimerspec *new_value, struct itimerspec *old_value) +{ +#if defined(__NR_timer_settime) + return my_syscall4(__NR_timer_settime, timerid, flags, new_value, old_value); +#elif defined(__NR_timer_settime64) + struct __kernel_itimerspec knew_value, kold_value; + int ret; + + __nolibc_timespec_user_to_kernel(&new_value->it_value, &knew_value.it_value); + __nolibc_timespec_user_to_kernel(&new_value->it_interval, &knew_value.it_interval); + ret = my_syscall4(__NR_timer_settime64, timerid, flags, &knew_value, &kold_value); + if (old_value) { + __nolibc_timespec_kernel_to_user(&kold_value.it_interval, &old_value->it_interval); + __nolibc_timespec_kernel_to_user(&kold_value.it_value, &old_value->it_value); + } + return ret; +#else + return __nolibc_enosys(__func__, timerid, flags, new_value, old_value); +#endif +} + +static __attribute__((unused)) +int timer_settime(timer_t timerid, int flags, + const struct itimerspec *new_value, struct itimerspec *old_value) +{ + return __sysret(sys_timer_settime(timerid, flags, new_value, old_value)); +} + #endif /* _NOLIBC_TIME_H */ diff --git a/tools/include/nolibc/types.h b/tools/include/nolibc/types.h index 8eb48d98667f5fe65bb8a974f613fd1fd17ae11e..2b02d4531427ca071d6e55abac5260a0b0226c40 100644 --- a/tools/include/nolibc/types.h +++ b/tools/include/nolibc/types.h @@ -202,6 +202,7 @@ struct stat { };
typedef __kernel_clockid_t clockid_t; +typedef int timer_t;
/* WARNING, it only deals with the 4096 first majors and 256 first minors */ #define makedev(major, minor) ((dev_t)((((major) & 0xfff) << 8) | ((minor) & 0xff))) diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index 9b8aa41a547b1735014c5a7541e426f4e08c7f90..7530b442941f5538cd1d54974e7c8aa435513653 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -905,6 +905,56 @@ int test_stat_timestamps(void) return 0; }
+int test_timer(void) +{ + struct itimerspec timerspec; + struct sigevent evp; + timer_t timer; + int ret; + + evp.sigev_notify = SIGEV_NONE; + + ret = timer_create(CLOCK_MONOTONIC, &evp, &timer); + if (ret) + return ret; + + timerspec = (struct itimerspec) { + .it_value.tv_sec = 1000000, + }; + ret = timer_settime(timer, 0, &timerspec, NULL); + if (ret) + goto err; + + timerspec = (struct itimerspec) { + .it_value.tv_sec = -1, + .it_value.tv_nsec = -1, + .it_interval.tv_sec = -1, + .it_interval.tv_nsec = -1, + }; + ret = timer_gettime(timer, &timerspec); + if (ret) + goto err; + + errno = EINVAL; + ret = -1; + + if (timerspec.it_interval.tv_sec || timerspec.it_interval.tv_nsec) + goto err; + + if (timerspec.it_value.tv_sec > 1000000) + goto err; + + ret = timer_delete(timer); + if (ret) + return ret; + + return 0; + +err: + timer_delete(timer); + return ret; +} + int test_uname(void) { struct utsname buf; @@ -1186,6 +1236,7 @@ int run_syscall(int min, int max) CASE_TEST(stat_fault); EXPECT_SYSER(1, stat(NULL, &stat_buf), -1, EFAULT); break; CASE_TEST(stat_timestamps); EXPECT_SYSZR(1, test_stat_timestamps()); break; CASE_TEST(symlink_root); EXPECT_SYSER(1, symlink("/", "/"), -1, EEXIST); break; + CASE_TEST(timer); EXPECT_SYSZR(1, test_timer()); break; CASE_TEST(uname); EXPECT_SYSZR(proc, test_uname()); break; CASE_TEST(uname_fault); EXPECT_SYSER(1, uname(NULL), -1, EFAULT); break; CASE_TEST(unlink_root); EXPECT_SYSER(1, unlink("/"), -1, EISDIR); break;
This is used in various selftests and will be handy when integrating those with nolibc.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de Acked-by: Willy Tarreau w@1wt.eu --- tools/include/nolibc/Makefile | 1 + tools/include/nolibc/nolibc.h | 1 + tools/include/nolibc/sys/timerfd.h | 87 ++++++++++++++++++++++++++++ tools/testing/selftests/nolibc/nolibc-test.c | 48 +++++++++++++++ 4 files changed, 137 insertions(+)
diff --git a/tools/include/nolibc/Makefile b/tools/include/nolibc/Makefile index 552e44f5a48211f81c68068b9b99c1dd6e44e9d8..d54646387872d3b0d0158a35c4122f4713c435ce 100644 --- a/tools/include/nolibc/Makefile +++ b/tools/include/nolibc/Makefile @@ -52,6 +52,7 @@ all_files := \ sys/stat.h \ sys/syscall.h \ sys/time.h \ + sys/timerfd.h \ sys/types.h \ sys/wait.h \ time.h \ diff --git a/tools/include/nolibc/nolibc.h b/tools/include/nolibc/nolibc.h index 2207f3d3c5cf92b96150149050f0bde0ed7e7147..13628261b9cc92d545745acc9ebef541b185f2bd 100644 --- a/tools/include/nolibc/nolibc.h +++ b/tools/include/nolibc/nolibc.h @@ -102,6 +102,7 @@ #include "sys/stat.h" #include "sys/syscall.h" #include "sys/time.h" +#include "sys/timerfd.h" #include "sys/wait.h" #include "ctype.h" #include "elf.h" diff --git a/tools/include/nolibc/sys/timerfd.h b/tools/include/nolibc/sys/timerfd.h new file mode 100644 index 0000000000000000000000000000000000000000..4375d546ba58f849b3ba48e7b221e6ec3c15fcf6 --- /dev/null +++ b/tools/include/nolibc/sys/timerfd.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: LGPL-2.1 OR MIT */ +/* + * timerfd definitions for NOLIBC + * Copyright (C) 2025 Thomas Weißschuh thomas.weissschuh@linutronix.de + */ + +/* make sure to include all global symbols */ +#include "../nolibc.h" + +#ifndef _NOLIBC_SYS_TIMERFD_H +#define _NOLIBC_SYS_TIMERFD_H + +#include "../sys.h" +#include "../time.h" + +#include <linux/timerfd.h> + + +static __attribute__((unused)) +int sys_timerfd_create(int clockid, int flags) +{ + return my_syscall2(__NR_timerfd_create, clockid, flags); +} + +static __attribute__((unused)) +int timerfd_create(int clockid, int flags) +{ + return __sysret(sys_timerfd_create(clockid, flags)); +} + + +static __attribute__((unused)) +int sys_timerfd_gettime(int fd, struct itimerspec *curr_value) +{ +#if defined(__NR_timerfd_gettime) + return my_syscall2(__NR_timerfd_gettime, fd, curr_value); +#elif defined(__NR_timerfd_gettime64) + struct __kernel_itimerspec kcurr_value; + int ret; + + ret = my_syscall2(__NR_timerfd_gettime64, fd, &kcurr_value); + __nolibc_timespec_kernel_to_user(&kcurr_value.it_interval, &curr_value->it_interval); + __nolibc_timespec_kernel_to_user(&kcurr_value.it_value, &curr_value->it_value); + return ret; +#else + return __nolibc_enosys(__func__, fd, curr_value); +#endif +} + +static __attribute__((unused)) +int timerfd_gettime(int fd, struct itimerspec *curr_value) +{ + return __sysret(sys_timerfd_gettime(fd, curr_value)); +} + + +static __attribute__((unused)) +int sys_timerfd_settime(int fd, int flags, + const struct itimerspec *new_value, struct itimerspec *old_value) +{ +#if defined(__NR_timerfd_settime) + return my_syscall4(__NR_timerfd_settime, fd, flags, new_value, old_value); +#elif defined(__NR_timerfd_settime64) + struct __kernel_itimerspec knew_value, kold_value; + int ret; + + __nolibc_timespec_user_to_kernel(&new_value->it_value, &knew_value.it_value); + __nolibc_timespec_user_to_kernel(&new_value->it_interval, &knew_value.it_interval); + ret = my_syscall4(__NR_timerfd_settime64, fd, flags, &knew_value, &kold_value); + if (old_value) { + __nolibc_timespec_kernel_to_user(&kold_value.it_interval, &old_value->it_interval); + __nolibc_timespec_kernel_to_user(&kold_value.it_value, &old_value->it_value); + } + return ret; +#else + return __nolibc_enosys(__func__, fd, flags, new_value, old_value); +#endif +} + +static __attribute__((unused)) +int timerfd_settime(int fd, int flags, + const struct itimerspec *new_value, struct itimerspec *old_value) +{ + return __sysret(sys_timerfd_settime(fd, flags, new_value, old_value)); +} + +#endif /* _NOLIBC_SYS_TIMERFD_H */ diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index 7530b442941f5538cd1d54974e7c8aa435513653..d73125c41096fd208b7b8719e61efa2da9865673 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -27,6 +27,7 @@ #include <sys/syscall.h> #include <sys/sysmacros.h> #include <sys/time.h> +#include <sys/timerfd.h> #include <sys/utsname.h> #include <sys/wait.h> #include <dirent.h> @@ -955,6 +956,52 @@ int test_timer(void) return ret; }
+int test_timerfd(void) +{ + struct itimerspec timerspec; + int timer, ret; + + timer = timerfd_create(CLOCK_MONOTONIC, 0); + if (timer == -1) + return -1; + + timerspec = (struct itimerspec) { + .it_value.tv_sec = 1000000, + }; + ret = timerfd_settime(timer, 0, &timerspec, NULL); + if (ret) + goto err; + + timerspec = (struct itimerspec) { + .it_value.tv_sec = -1, + .it_value.tv_nsec = -1, + .it_interval.tv_sec = -1, + .it_interval.tv_nsec = -1, + }; + ret = timerfd_gettime(timer, &timerspec); + if (ret) + goto err; + + errno = EINVAL; + ret = -1; + + if (timerspec.it_interval.tv_sec || timerspec.it_interval.tv_nsec) + goto err; + + if (timerspec.it_value.tv_sec > 1000000) + goto err; + + ret = close(timer); + if (ret) + return ret; + + return 0; + +err: + close(timer); + return ret; +} + int test_uname(void) { struct utsname buf; @@ -1237,6 +1284,7 @@ int run_syscall(int min, int max) CASE_TEST(stat_timestamps); EXPECT_SYSZR(1, test_stat_timestamps()); break; CASE_TEST(symlink_root); EXPECT_SYSER(1, symlink("/", "/"), -1, EEXIST); break; CASE_TEST(timer); EXPECT_SYSZR(1, test_timer()); break; + CASE_TEST(timerfd); EXPECT_SYSZR(1, test_timerfd()); break; CASE_TEST(uname); EXPECT_SYSZR(proc, test_uname()); break; CASE_TEST(uname_fault); EXPECT_SYSER(1, uname(NULL), -1, EFAULT); break; CASE_TEST(unlink_root); EXPECT_SYSER(1, unlink("/"), -1, EISDIR); break;
This is used in various selftests and will be handy when integrating those with nolibc.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de Acked-by: Willy Tarreau w@1wt.eu --- tools/include/nolibc/time.h | 7 +++++++ tools/testing/selftests/nolibc/nolibc-test.c | 12 ++++++++++++ 2 files changed, 19 insertions(+)
diff --git a/tools/include/nolibc/time.h b/tools/include/nolibc/time.h index 48f602b661b300a0bfbac5a231315fc95727c04e..fc387940d51f389d4233bd5712588dced31ae6e5 100644 --- a/tools/include/nolibc/time.h +++ b/tools/include/nolibc/time.h @@ -108,6 +108,13 @@ int clock_settime(clockid_t clockid, struct timespec *tp) }
+static __inline__ +double difftime(time_t time1, time_t time2) +{ + return time1 - time2; +} + + static __attribute__((unused)) time_t time(time_t *tptr) { diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index d73125c41096fd208b7b8719e61efa2da9865673..665d7631fbc6e9ecae469a4b143440ec0a43d4aa 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1304,6 +1304,17 @@ int run_syscall(int min, int max) return ret; }
+int test_difftime(void) +{ + if (difftime(200., 100.) != 100.) + return 1; + + if (difftime(100., 200.) != -100.) + return 1; + + return 0; +} + int run_stdlib(int min, int max) { int test; @@ -1426,6 +1437,7 @@ int run_stdlib(int min, int max) CASE_TEST(toupper_noop); EXPECT_EQ(1, toupper('A'), 'A'); break; CASE_TEST(abs); EXPECT_EQ(1, abs(-10), 10); break; CASE_TEST(abs_noop); EXPECT_EQ(1, abs(10), 10); break; + CASE_TEST(difftime); EXPECT_ZR(1, test_difftime()); break;
case __LINE__: return ret; /* must be last */
This is used in various selftests and will be handy when integrating those with nolibc.
Not all configurations support namespaces, so skip the tests where necessary. Also if the tests are running without privileges. Enable the namespace configuration for those architectures where it is not enabled by default.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de Acked-by: Willy Tarreau w@1wt.eu --- tools/include/nolibc/Makefile | 1 + tools/include/nolibc/nolibc.h | 1 + tools/include/nolibc/sched.h | 50 +++++++++++++++++++++ tools/testing/selftests/nolibc/Makefile | 2 + tools/testing/selftests/nolibc/nolibc-test.c | 67 ++++++++++++++++++++++++++++ 5 files changed, 121 insertions(+)
diff --git a/tools/include/nolibc/Makefile b/tools/include/nolibc/Makefile index d54646387872d3b0d0158a35c4122f4713c435ce..48f89567c03fb3cbcbe1d20accb9d293056d4142 100644 --- a/tools/include/nolibc/Makefile +++ b/tools/include/nolibc/Makefile @@ -37,6 +37,7 @@ all_files := \ limits.h \ math.h \ nolibc.h \ + sched.h \ signal.h \ stackprotector.h \ std.h \ diff --git a/tools/include/nolibc/nolibc.h b/tools/include/nolibc/nolibc.h index 13628261b9cc92d545745acc9ebef541b185f2bd..d6722e22f441956e70340dcab004aa73a3125691 100644 --- a/tools/include/nolibc/nolibc.h +++ b/tools/include/nolibc/nolibc.h @@ -106,6 +106,7 @@ #include "sys/wait.h" #include "ctype.h" #include "elf.h" +#include "sched.h" #include "signal.h" #include "unistd.h" #include "stdio.h" diff --git a/tools/include/nolibc/sched.h b/tools/include/nolibc/sched.h new file mode 100644 index 0000000000000000000000000000000000000000..32221562c1663f6d1d78fab82bb6e8d05ad68da9 --- /dev/null +++ b/tools/include/nolibc/sched.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: LGPL-2.1 OR MIT */ +/* + * sched function definitions for NOLIBC + * Copyright (C) 2025 Thomas Weißschuh linux@weissschuh.net + */ + +/* make sure to include all global symbols */ +#include "nolibc.h" + +#ifndef _NOLIBC_SCHED_H +#define _NOLIBC_SCHED_H + +#include "sys.h" + +#include <linux/sched.h> + +/* + * int setns(int fd, int nstype); + */ + +static __attribute__((unused)) +int sys_setns(int fd, int nstype) +{ + return my_syscall2(__NR_setns, fd, nstype); +} + +static __attribute__((unused)) +int setns(int fd, int nstype) +{ + return __sysret(sys_setns(fd, nstype)); +} + + +/* + * int unshare(int flags); + */ + +static __attribute__((unused)) +int sys_unshare(int flags) +{ + return my_syscall1(__NR_unshare, flags); +} + +static __attribute__((unused)) +int unshare(int flags) +{ + return __sysret(sys_unshare(flags)); +} + +#endif /* _NOLIBC_SCHED_H */ diff --git a/tools/testing/selftests/nolibc/Makefile b/tools/testing/selftests/nolibc/Makefile index 2671383045dbb51682fd62ac8c8df9c317157317..94176ffe46463548cc9bc787933b6cefa83d6502 100644 --- a/tools/testing/selftests/nolibc/Makefile +++ b/tools/testing/selftests/nolibc/Makefile @@ -109,6 +109,8 @@ DEFCONFIG = $(DEFCONFIG_$(XARCH))
EXTRACONFIG_m68k = -e CONFIG_BLK_DEV_INITRD EXTRACONFIG = $(EXTRACONFIG_$(XARCH)) +EXTRACONFIG_arm = -e CONFIG_NAMESPACES +EXTRACONFIG_armthumb = -e CONFIG_NAMESPACES
# optional tests to run (default = all) TEST = diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index 665d7631fbc6e9ecae469a4b143440ec0a43d4aa..d1157319b5d715dbc94ff9203b1d2a2e932aabcf 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1172,6 +1172,72 @@ int test_openat(void) return 0; }
+int test_namespace(void) +{ + int original_ns, new_ns, ret; + ino_t original_ns_ino; + struct stat stat_buf; + + original_ns = open("/proc/self/ns/uts", O_RDONLY); + if (original_ns == -1) + return -1; + + ret = fstat(original_ns, &stat_buf); + if (ret) + goto out; + + original_ns_ino = stat_buf.st_ino; + + ret = unshare(CLONE_NEWUTS); + if (ret) + goto out; + + new_ns = open("/proc/self/ns/uts", O_RDONLY); + if (new_ns == -1) { + ret = new_ns; + goto out; + } + + ret = fstat(new_ns, &stat_buf); + close(new_ns); + if (ret) + goto out; + + if (stat_buf.st_ino == original_ns_ino) { + errno = EINVAL; + ret = -1; + goto out; + } + + ret = setns(original_ns, CLONE_NEWUTS); + if (ret) + goto out; + + new_ns = open("/proc/self/ns/uts", O_RDONLY); + if (new_ns == -1) { + ret = new_ns; + goto out; + } + + ret = fstat(new_ns, &stat_buf); + if (ret) + goto out; + + close(new_ns); + + if (stat_buf.st_ino != original_ns_ino) { + errno = EINVAL; + ret = -1; + goto out; + } + + ret = 0; + +out: + close(original_ns); + return ret; +} + /* Run syscall tests between IDs <min> and <max>. * Return 0 on success, non-zero on failure. */ @@ -1296,6 +1362,7 @@ int run_syscall(int min, int max) CASE_TEST(write_zero); EXPECT_SYSZR(1, write(1, &tmp, 0)); break; CASE_TEST(syscall_noargs); EXPECT_SYSEQ(1, syscall(__NR_getpid), getpid()); break; CASE_TEST(syscall_args); EXPECT_SYSER(1, syscall(__NR_statx, 0, NULL, 0, 0, NULL), -1, EFAULT); break; + CASE_TEST(namespace); EXPECT_SYSZR(euid0 && proc, test_namespace()); break; case __LINE__: return ret; /* must be last */ /* note: do not set any defaults so as to permit holes above */
This is used in various selftests and will be handy when integrating those with nolibc.
Only the standard POSIX modes are supported. No extensions nor the (noop) "b" from ISO C are accepted.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de Acked-by: Willy Tarreau w@1wt.eu --- tools/include/nolibc/stdio.h | 20 ++++++++++++++++++++ tools/testing/selftests/nolibc/nolibc-test.c | 24 ++++++++++++++++++++++++ 2 files changed, 44 insertions(+)
diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h index df5717d591826c8468e3473581ac0d6e76629aa3..61b7252ab6d04f8758367f5265687146326456a3 100644 --- a/tools/include/nolibc/stdio.h +++ b/tools/include/nolibc/stdio.h @@ -13,6 +13,7 @@ #include "std.h" #include "arch.h" #include "errno.h" +#include "fcntl.h" #include "types.h" #include "sys.h" #include "stdarg.h" @@ -55,6 +56,25 @@ FILE *fdopen(int fd, const char *mode __attribute__((unused))) return (FILE*)(intptr_t)~fd; }
+static __attribute__((unused)) +FILE *fopen(const char *pathname, const char *mode) +{ + int flags, fd; + + switch (*mode) { + case 'r': flags = O_RDONLY; break; + case 'w': flags = O_WRONLY | O_CREAT | O_TRUNC; break; + case 'a': flags = O_WRONLY | O_CREAT | O_APPEND; break; + default : SET_ERRNO(EINVAL); return NULL; + } + + if (mode[1] == '+') + flags = (flags & ~(O_RDONLY|O_WRONLY)) | O_RDWR; + + fd = open(pathname, flags, 0666); + return fdopen(fd, mode); +} + /* provides the fd of stream. */ static __attribute__((unused)) int fileno(FILE *stream) diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index d1157319b5d715dbc94ff9203b1d2a2e932aabcf..0391c7d01380ea2f20d1d07497ea1964bcb6a9f4 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -859,6 +859,29 @@ int test_getpagesize(void) return !c; }
+int test_file_stream(void) +{ + FILE *f; + int r; + + f = fopen("/dev/null", "r"); + if (!f) + return -1; + + errno = 0; + r = fwrite("foo", 1, 3, f); + if (r != 0 || errno != EBADF) { + fclose(f); + return -1; + } + + r = fclose(f); + if (r == EOF) + return -1; + + return 0; +} + int test_fork(void) { int status; @@ -1311,6 +1334,7 @@ int run_syscall(int min, int max) CASE_TEST(dup3_0); tmp = dup3(0, 100, 0); EXPECT_SYSNE(1, tmp, -1); close(tmp); break; CASE_TEST(dup3_m1); tmp = dup3(-1, 100, 0); EXPECT_SYSER(1, tmp, -1, EBADF); if (tmp != -1) close(tmp); break; CASE_TEST(execve_root); EXPECT_SYSER(1, execve("/", (char*[]){ [0] = "/", [1] = NULL }, NULL), -1, EACCES); break; + CASE_TEST(file_stream); EXPECT_SYSZR(1, test_file_stream()); break; CASE_TEST(fork); EXPECT_SYSZR(1, test_fork()); break; CASE_TEST(getdents64_root); EXPECT_SYSNE(1, test_getdents64("/"), -1); break; CASE_TEST(getdents64_null); EXPECT_SYSER(1, test_getdents64("/dev/null"), -1, ENOTDIR); break;
Newer architectures (like riscv32) do not implement sys_gettimeofday(). In those cases fall back to sys_clock_gettime(). While that does not support the timezone argument of sys_gettimeofday(), specifying this argument invokes undefined behaviour, so it's safe to ignore.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de Acked-by: Willy Tarreau w@1wt.eu --- tools/include/nolibc/sys/time.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/tools/include/nolibc/sys/time.h b/tools/include/nolibc/sys/time.h index 785961c52fa3b6606ee30c19a4a1c11e86ccacc0..33782a19aae92907ccc636a656fc0f27b838dd61 100644 --- a/tools/include/nolibc/sys/time.h +++ b/tools/include/nolibc/sys/time.h @@ -13,6 +13,8 @@ #include "../arch.h" #include "../sys.h"
+static int sys_clock_gettime(clockid_t clockid, struct timespec *tp); + /* * int gettimeofday(struct timeval *tv, struct timezone *tz); */ @@ -23,7 +25,18 @@ int sys_gettimeofday(struct timeval *tv, struct timezone *tz) #ifdef __NR_gettimeofday return my_syscall2(__NR_gettimeofday, tv, tz); #else - return __nolibc_enosys(__func__, tv, tz); + (void) tz; /* Non-NULL tz is undefined behaviour */ + + struct timespec tp; + int ret; + + ret = sys_clock_gettime(CLOCK_REALTIME, &tp); + if (!ret && tv) { + tv->tv_sec = tp.tv_sec; + tv->tv_usec = tp.tv_nsec / 1000; + } + + return ret; #endif }
Newer architectures like riscv 32-bit are missing sys_wait4(). Make use of the fact that wait(&status) is defined to be equivalent to waitpid(-1, status, 0) to implment it on all architectures.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de Acked-by: Willy Tarreau w@1wt.eu --- tools/include/nolibc/sys/wait.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/tools/include/nolibc/sys/wait.h b/tools/include/nolibc/sys/wait.h index f27be86ad5e480f3817b4500ac42fae445cdfc27..4d44e3da0ba814d00368027d893e2eb1155b86f3 100644 --- a/tools/include/nolibc/sys/wait.h +++ b/tools/include/nolibc/sys/wait.h @@ -31,12 +31,6 @@ pid_t sys_wait4(pid_t pid, int *status, int options, struct rusage *rusage) #endif }
-static __attribute__((unused)) -pid_t wait(int *status) -{ - return __sysret(sys_wait4(-1, status, 0, NULL)); -} - static __attribute__((unused)) pid_t wait4(pid_t pid, int *status, int options, struct rusage *rusage) { @@ -113,4 +107,10 @@ pid_t waitpid(pid_t pid, int *status, int options) return info.si_pid; }
+static __attribute__((unused)) +pid_t wait(int *status) +{ + return waitpid(-1, status, 0); +} + #endif /* _NOLIBC_SYS_WAIT_H */
linux-kselftest-mirror@lists.linaro.org