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 --- 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: implement 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 | 34 ++++ 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 | 32 ++++ 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 | 254 ++++++++++++++++++++++++++- 18 files changed, 811 insertions(+), 12 deletions(-) --- base-commit: e90ce42e81381665dbcedc5fa12e74759ee89639 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 --- 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 f0d335f0e467ec870066811289dfd11e46e60a92..b7b2b93bc90c9330dec7186d45c18fd3f24373d0 100644 --- a/tools/include/nolibc/string.h +++ b/tools/include/nolibc/string.h @@ -289,6 +289,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 --- 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 fb0417477759ee6c9663e84807c1d1067e735dec..02426682c34bcd82d9556036c6c3e73a6a5a3b4d 100644 --- a/tools/include/nolibc/stdio.h +++ b/tools/include/nolibc/stdio.h @@ -17,6 +17,8 @@ #include "string.h" #include "compiler.h"
+static const char *strerror(int errnum); + #ifndef EOF #define EOF (-1) #endif @@ -289,6 +291,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 --- 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 0eaf5496ce233a4d8b5a239eef5ecefe05a39dd6..53c5c8addc829e36b927c3dd39b2a24f3472dcf0 100644 --- a/tools/include/nolibc/sys/stat.h +++ b/tools/include/nolibc/sys/stat.h @@ -14,6 +14,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)) @@ -34,12 +37,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;
@@ -67,6 +70,24 @@ 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); +} +
/* make sure to include all global symbols */ #include "../nolibc.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 --- 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 ad9d06b6b7919ec76a0652266158366cf639a77a..d1c213c19d7fa55c9db3a9527dd35b6072ca9485 100644 --- a/tools/include/nolibc/sys/mman.h +++ b/tools/include/nolibc/sys/mman.h @@ -45,6 +45,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 --- tools/include/nolibc/Makefile | 1 + tools/include/nolibc/nolibc.h | 1 + tools/include/nolibc/sys/random.h | 32 ++++++++++++++++++++++++++++ tools/testing/selftests/nolibc/nolibc-test.c | 19 +++++++++++++++++ 4 files changed, 53 insertions(+)
diff --git a/tools/include/nolibc/Makefile b/tools/include/nolibc/Makefile index e05862cd08051685112f067d6eb45716613dd43c..b22ff1e268b2e4bd788e974d6d8f2e1ef96dfc5f 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..b5a904dffbfb5039ca8f9efb9eaf68e7bb1716b5 --- /dev/null +++ b/tools/include/nolibc/sys/random.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: LGPL-2.1 OR MIT */ +/* + * random definitions for NOLIBC + * Copyright (C) 2025 Thomas Weißschuh thomas.weissschuh@linutronix.de + */ + +#ifndef _NOLIBC_SYS_RANDOM_H +#define _NOLIBC_SYS_RANDOM_H + +#include "../arch.h" +#include "../sys.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)); +} + +/* make sure to include all global symbols */ +#include "../nolibc.h" + +#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..95d08e9ccf5b3be924548100e9621cd47f39e8c2 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,23 @@ static int test_dirent(void) return 0; }
+int test_getrandom(void) +{ + uint64_t rng = 0; + ssize_t ret; + + ret = getrandom(&rng, sizeof(rng), 0); + if (ret != sizeof(rng)) + return ret; + + if (!rng) { + errno = EINVAL; + return -1; + } + + return 0; +} + int test_getpagesize(void) { int x = getpagesize(); @@ -1124,6 +1142,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 --- 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 b22ff1e268b2e4bd788e974d6d8f2e1ef96dfc5f..3a82e036a2c1762da5c3916ff9f4e158fb6106ff 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..4906d3eb7c1aed0c402d3ab39526dd1c93b5df2a --- /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 + */ + +#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; +} + +/* make sure to include all global symbols */ +#include "nolibc.h" + +#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 69cf1d4418f1c1f59192aee40ea3a5b684d2709a..c8c44859aee20e2b9eb55ddae488f1f70b710fbb 100644 --- a/tools/include/nolibc/stdlib.h +++ b/tools/include/nolibc/stdlib.h @@ -29,6 +29,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 95d08e9ccf5b3be924548100e9621cd47f39e8c2..b6724c314766b0cf34d7cae5cac28a9102ebe66e 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1315,6 +1315,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 --- 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 ac7d53d986cd11ac84dd0a17e5a7055c779b2b10..a18e318399880dec12284ea86cb91cf46cfc4aaa 100644 --- a/tools/include/nolibc/unistd.h +++ b/tools/include/nolibc/unistd.h @@ -17,6 +17,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 b6724c314766b0cf34d7cae5cac28a9102ebe66e..7090e6b7c37e49afe751835bbff6a7af4f4fcf2a 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1109,6 +1109,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 --- 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 84655361b9ad2ce141f9af2759f2b128ae8a83a3..fb330628a59ad8a8b2b01c027b861472888ddee4 100644 --- a/tools/include/nolibc/time.h +++ b/tools/include/nolibc/time.h @@ -12,6 +12,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 32d0929c633bbae29263375c461a0c82baf7f869..08c9fdd1c7991656aabca5dd4dd363da45e00dd8 100644 --- a/tools/include/nolibc/types.h +++ b/tools/include/nolibc/types.h @@ -198,6 +198,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 7090e6b7c37e49afe751835bbff6a7af4f4fcf2a..7eb69971667a196cb0855d156e3e18085851de7d 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> @@ -1078,6 +1079,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; @@ -1111,6 +1113,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 --- 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 fb330628a59ad8a8b2b01c027b861472888ddee4..28a1549adb14e2087fa8fbdb7e9c35e1c3f22c2a 100644 --- a/tools/include/nolibc/time.h +++ b/tools/include/nolibc/time.h @@ -12,6 +12,7 @@ #include "types.h" #include "sys.h"
+#include <linux/signal.h> #include <linux/time.h>
static __inline__ @@ -117,6 +118,91 @@ 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)); +} + /* make sure to include all global symbols */ #include "nolibc.h"
diff --git a/tools/include/nolibc/types.h b/tools/include/nolibc/types.h index 08c9fdd1c7991656aabca5dd4dd363da45e00dd8..3528782670bca23fc9eb0323eb5af3100a037855 100644 --- a/tools/include/nolibc/types.h +++ b/tools/include/nolibc/types.h @@ -199,6 +199,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 7eb69971667a196cb0855d156e3e18085851de7d..6af55a04e3c6bcea4e09c941778ffd1f3b186690 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -902,6 +902,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; @@ -1183,6 +1233,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 --- 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 3a82e036a2c1762da5c3916ff9f4e158fb6106ff..e3aeb247c74e88af5d769efe64965c5bcbb40611 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..2d61fc76fe9a9ba7571f52ff157a8a4807d7d9c9 --- /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 + */ + +#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)); +} + +/* make sure to include all global symbols */ +#include "../nolibc.h" + +#endif /* _NOLIBC_SYS_TIMERFD_H */ diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index 6af55a04e3c6bcea4e09c941778ffd1f3b186690..aed71de4b4f3dd1f183c7fc25e5a5cee466600ed 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> @@ -952,6 +953,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; @@ -1234,6 +1281,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 --- tools/include/nolibc/time.h | 7 +++++++ tools/testing/selftests/nolibc/nolibc-test.c | 1 + 2 files changed, 8 insertions(+)
diff --git a/tools/include/nolibc/time.h b/tools/include/nolibc/time.h index 28a1549adb14e2087fa8fbdb7e9c35e1c3f22c2a..760133c574ece97165e3bba5616a387deaf07aff 100644 --- a/tools/include/nolibc/time.h +++ b/tools/include/nolibc/time.h @@ -105,6 +105,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 aed71de4b4f3dd1f183c7fc25e5a5cee466600ed..fd8bab42e75157967658690005bc9142360fc135 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1423,6 +1423,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_EQ(1, difftime(200., 100.), 100.); 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 --- 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 e3aeb247c74e88af5d769efe64965c5bcbb40611..5fed167f26aa3475eebc7a56bd97820932e97ff9 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..19108d33c5c6063d41916bbf46ef1f7b1b49595c --- /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 + */ + +#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)); +} + +/* make sure to include all global symbols */ +#include "nolibc.h" + +#endif /* _NOLIBC_SCHED_H */ diff --git a/tools/testing/selftests/nolibc/Makefile b/tools/testing/selftests/nolibc/Makefile index 94f3e8be7a68f63ecd639c4f283b3cd10764ce74..37526891af8de338d1d55315d7d6a7179e695cd0 100644 --- a/tools/testing/selftests/nolibc/Makefile +++ b/tools/testing/selftests/nolibc/Makefile @@ -106,6 +106,8 @@ DEFCONFIG_sparc64 = sparc64_defconfig DEFCONFIG = $(DEFCONFIG_$(XARCH))
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 fd8bab42e75157967658690005bc9142360fc135..ab9c3bcffd9750981d68c6d16245d285ce0657c8 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1169,6 +1169,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. */ @@ -1293,6 +1359,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 --- tools/include/nolibc/stdio.h | 27 +++++++++++++++++++++++++++ tools/testing/selftests/nolibc/nolibc-test.c | 24 ++++++++++++++++++++++++ 2 files changed, 51 insertions(+)
diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h index 02426682c34bcd82d9556036c6c3e73a6a5a3b4d..86a1132abcd4e5e5b0f821cfe974aca8a6d4966f 100644 --- a/tools/include/nolibc/stdio.h +++ b/tools/include/nolibc/stdio.h @@ -10,6 +10,7 @@ #include "std.h" #include "arch.h" #include "errno.h" +#include "fcntl.h" #include "types.h" #include "sys.h" #include "stdarg.h" @@ -52,6 +53,32 @@ 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; + + if (!strcmp(mode, "r")) + flags = O_RDONLY; + else if (!strcmp(mode, "w")) + flags = O_WRONLY | O_CREAT | O_TRUNC; + else if (!strcmp(mode, "a")) + flags = O_WRONLY | O_CREAT | O_APPEND; + else if (!strcmp(mode, "r+")) + flags = O_RDWR; + else if (!strcmp(mode, "w+")) + flags = O_RDWR | O_CREAT | O_TRUNC; + else if (!strcmp(mode, "a+")) + flags = O_RDWR | O_CREAT | O_APPEND; + else { + SET_ERRNO(EINVAL); + return NULL; + } + + 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 ab9c3bcffd9750981d68c6d16245d285ce0657c8..f576edc3725b42ce086d6d83d989f38d3fab6eb8 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -856,6 +856,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; @@ -1308,6 +1331,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 --- 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 1d326c05ee627b6bdb7d2777cbb11eea385f2407..e949844bf642a4ffd8a95d95752d4b9c37d71ac5 100644 --- a/tools/include/nolibc/sys/time.h +++ b/tools/include/nolibc/sys/time.h @@ -10,6 +10,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); */ @@ -20,7 +22,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 --- 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 9a68e6a6b1df8f938225007eb0de0574257ccf00..9783632a80bc20e0175a8842e7a7aea27defeb27 100644 --- a/tools/include/nolibc/sys/wait.h +++ b/tools/include/nolibc/sys/wait.h @@ -28,12 +28,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) { @@ -110,6 +104,12 @@ 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); +} +
/* make sure to include all global symbols */ #include "../nolibc.h"
linux-kselftest-mirror@lists.linaro.org