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;
Hi Thomas,
On Wed, Apr 23, 2025 at 05:01:35PM +0200, Thomas Weißschuh wrote:
--- /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
- */
Note: don't forget to add your nolibc include here from the other series.
+#ifndef _NOLIBC_SYS_RANDOM_H +#define _NOLIBC_SYS_RANDOM_H
+#include "../arch.h" +#include "../sys.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
(...)
+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;
+}
Just a thought about this one: in a not-so-distant past, getrandom() could hang forever when lacking entropy (classical problem when booting a headless machine having no RNG), and since a recent kernel it turned to "only" multiple seconds. I'm not seeing any easy solution to this, but we need to keep an eye on this one, and in case of bad reports, maybe have this test as an opt-in or something like this. Anyway the best way to know is to have it right now and wait for reports to arrive.
Willy
On Sat, Apr 26, 2025 at 12:31:58PM +0200, Willy Tarreau wrote:
Hi Thomas,
On Wed, Apr 23, 2025 at 05:01:35PM +0200, Thomas Weißschuh wrote:
--- /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
- */
Note: don't forget to add your nolibc include here from the other series.
Ack. This series was sent before I got the idea about moving "#include nolibc.h". I'll fix it up for v2.
+#ifndef _NOLIBC_SYS_RANDOM_H +#define _NOLIBC_SYS_RANDOM_H
+#include "../arch.h" +#include "../sys.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
(...)
+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;
+}
Just a thought about this one: in a not-so-distant past, getrandom() could hang forever when lacking entropy (classical problem when booting a headless machine having no RNG), and since a recent kernel it turned to "only" multiple seconds. I'm not seeing any easy solution to this, but we need to keep an eye on this one, and in case of bad reports, maybe have this test as an opt-in or something like this. Anyway the best way to know is to have it right now and wait for reports to arrive.
What about using GRND_NONBLOCK and mark the test as skipped if no entropy is available?
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;
On Wed, Apr 23, 2025 at 05:01:40PM +0200, Thomas Weißschuh wrote:
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
- */
Same note about not forgetting to add the nolibc include from the other series here.
+#ifndef _NOLIBC_SYS_TIMERFD_H +#define _NOLIBC_SYS_TIMERFD_H
+#include "../sys.h" +#include "../time.h"
Willy
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 */
On Wed, Apr 23, 2025 at 05:01:41PM +0200, Thomas Weißschuh wrote:
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;
+}
Thanks for making me discover difftime(). I've never heard about it, and seeing it return a double looks totally weird, but that's how it is indeed.
In case time_t would be unsigned, this would return a large unsigned value. I think it could be more robust to explicitly cast the two inputs to long:
return (long)time1 - (long)time2;
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;
Then here maybe test it in reverse to make sure the types don't cause trouble:
CASE_TEST(difftime); EXPECT_EQ(1, difftime(100., 200.), -100.); break;
Willy
On Sat, Apr 26, 2025 at 12:45:20PM +0200, Willy Tarreau wrote:
On Wed, Apr 23, 2025 at 05:01:41PM +0200, Thomas Weißschuh wrote:
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;
+}
Thanks for making me discover difftime(). I've never heard about it, and seeing it return a double looks totally weird, but that's how it is indeed.
In case time_t would be unsigned, this would return a large unsigned value. I think it could be more robust to explicitly cast the two inputs to long:
return (long)time1 - (long)time2;
time_t is explicitly defined as "signed long" in nolibc's std.h. If we change it at some time in the future we will have to audit any usage site of time_t in any case.
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;
Then here maybe test it in reverse to make sure the types don't cause trouble:
Ack.
CASE_TEST(difftime); EXPECT_EQ(1, difftime(100., 200.), -100.); 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/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 */
On Wed, Apr 23, 2025 at 05:01:42PM +0200, Thomas Weißschuh wrote:
This is used in various selftests and will be handy when integrating those with nolibc.
It could be useful to add a small note here mentioning that the test takes care of the optional OS support, and will run it only if started as root.
Willy
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;
On Wed, Apr 23, 2025 at 05:01:43PM +0200, Thomas Weißschuh wrote:
+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;
- }
I'm concerned by the size of the function due to the repeated strcmp() calls (also I find strcmp()==0 more readable that !strcmp() which I tend to read as "not string compares").
I have not tried the code below but I think it could cover it in a maybe lighter way:
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;
I think it does the same but should be significantly lighter.
Willy
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"
Hi Thomas,
On Wed, Apr 23, 2025 at 05:01:30PM +0200, Thomas Weißschuh wrote:
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.
As usual, the series looks overall good to me (and I expect it to be quite useful). I sent a few reminders about not forgetting to include "nolibc.h" from the other series into the newly added files, though I guess you have these in mind anyway.
Regardless of the small comments, you can take my ack for the whole series: Acked-by: Willy Tarreau w@1wt.eu
Thanks! Willy
linux-kselftest-mirror@lists.linaro.org