diff options
| author | Mark Brown <broonie@kernel.org> | 2026-09-07 14:48:34 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-09-07 14:48:34 +0100 |
| commit | 99e0bf7b4a171f63abb01ae4cabbcb7ddee53f3f (patch) | |
| tree | b0717ab9b11e149723286ac16d6c1e45f3b80b4d | |
| parent | 9a6ec108829214ff53fb03b45526ffdffe12efc6 (diff) | |
| parent | 6ef847683e7d294a4187a7253924b8f0d8911416 (diff) | |
| download | linux-next-99e0bf7b4a171f63abb01ae4cabbcb7ddee53f3f.tar.gz linux-next-99e0bf7b4a171f63abb01ae4cabbcb7ddee53f3f.zip | |
Merge branch 'for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/nolibc/linux-nolibc.git
| -rw-r--r-- | tools/include/nolibc/Makefile | 19 | ||||
| -rw-r--r-- | tools/include/nolibc/arch-hexagon.h | 164 | ||||
| -rw-r--r-- | tools/include/nolibc/arch.h | 2 | ||||
| -rw-r--r-- | tools/include/nolibc/dirent.h | 18 | ||||
| -rw-r--r-- | tools/include/nolibc/nolibc.h | 1 | ||||
| -rw-r--r-- | tools/include/nolibc/sys/sendfile.h | 41 | ||||
| -rw-r--r-- | tools/testing/selftests/nolibc/Makefile.nolibc | 10 | ||||
| -rw-r--r-- | tools/testing/selftests/nolibc/nolibc-test.c | 70 | ||||
| -rwxr-xr-x | tools/testing/selftests/nolibc/run-tests.sh | 24 |
9 files changed, 341 insertions, 8 deletions
diff --git a/tools/include/nolibc/Makefile b/tools/include/nolibc/Makefile index 6d213d372bf8..0880be0f6ef5 100644 --- a/tools/include/nolibc/Makefile +++ b/tools/include/nolibc/Makefile @@ -17,7 +17,23 @@ endif # it defaults to this nolibc directory. OUTPUT ?= $(CURDIR)/ -architectures := alpha arm arm64 loongarch m68k mips openrisc parisc powerpc riscv s390 sh sparc x86 +architectures := \ + alpha \ + arm \ + arm64 \ + hexagon \ + loongarch \ + m68k \ + mips \ + openrisc \ + parisc \ + powerpc \ + riscv \ + s390 \ + sh \ + sparc \ + x86 \ + arch_files := arch.h $(addsuffix .h, $(addprefix arch-, $(architectures))) all_files := \ alloca.h \ @@ -59,6 +75,7 @@ all_files := \ sys/reboot.h \ sys/resource.h \ sys/select.h \ + sys/sendfile.h \ sys/stat.h \ sys/syscall.h \ sys/sysmacros.h \ diff --git a/tools/include/nolibc/arch-hexagon.h b/tools/include/nolibc/arch-hexagon.h new file mode 100644 index 000000000000..0767054882f3 --- /dev/null +++ b/tools/include/nolibc/arch-hexagon.h @@ -0,0 +1,164 @@ +/* SPDX-License-Identifier: LGPL-2.1 OR MIT */ +/* + * hexagon specific definitions for NOLIBC + * Copyright (C) 2026 Thomas Weißschuh <linux@weissschuh.net> + */ + +#ifndef _NOLIBC_ARCH_HEXAGON_H +#define _NOLIBC_ARCH_HEXAGON_H + +#include <linux/unistd.h> + +#include "compiler.h" +#include "crt.h" + +/* + * Syscalls for hexagon: + * - syscall number is passed in r6 + * - arguments are in r0, r1, r2, r3, r4, r5 + * - the system call is performed by calling trap0(#1) + * - syscall return value is in r0 + */ + +#define _NOLIBC_SYSCALL_CLOBBERLIST "memory" + +#define __nolibc_syscall0(num) \ +({ \ + register long _num __asm__ ("r6") = (num); \ + register long _arg1 __asm__ ("r0"); \ + \ + __asm__ volatile ( \ + "trap0(#1)\n" \ + : "=r"(_arg1) \ + : "r"(_num) \ + : _NOLIBC_SYSCALL_CLOBBERLIST \ + ); \ + _arg1; \ +}) + +#define __nolibc_syscall1(num, arg1) \ +({ \ + register long _num __asm__ ("r6") = (num); \ + register long _arg1 __asm__ ("r0") = (long)(arg1); \ + \ + __asm__ volatile ( \ + "trap0(#1)\n" \ + : "+r"(_arg1) \ + : "r"(_num) \ + : _NOLIBC_SYSCALL_CLOBBERLIST \ + ); \ + _arg1; \ +}) + +#define __nolibc_syscall2(num, arg1, arg2) \ +({ \ + register long _num __asm__ ("r6") = (num); \ + register long _arg1 __asm__ ("r0") = (long)(arg1); \ + register long _arg2 __asm__ ("r1") = (long)(arg2); \ + \ + __asm__ volatile ( \ + "trap0(#1)\n" \ + : "+r"(_arg1) \ + : "r"(_arg2), \ + "r"(_num) \ + : _NOLIBC_SYSCALL_CLOBBERLIST \ + ); \ + _arg1; \ +}) + +#define __nolibc_syscall3(num, arg1, arg2, arg3) \ +({ \ + register long _num __asm__ ("r6") = (num); \ + register long _arg1 __asm__ ("r0") = (long)(arg1); \ + register long _arg2 __asm__ ("r1") = (long)(arg2); \ + register long _arg3 __asm__ ("r2") = (long)(arg3); \ + \ + __asm__ volatile ( \ + "trap0(#1)\n" \ + : "+r"(_arg1) \ + : "r"(_arg2), "r"(_arg3), \ + "r"(_num) \ + : _NOLIBC_SYSCALL_CLOBBERLIST \ + ); \ + _arg1; \ +}) + +#define __nolibc_syscall4(num, arg1, arg2, arg3, arg4) \ +({ \ + register long _num __asm__ ("r6") = (num); \ + register long _arg1 __asm__ ("r0") = (long)(arg1); \ + register long _arg2 __asm__ ("r1") = (long)(arg2); \ + register long _arg3 __asm__ ("r2") = (long)(arg3); \ + register long _arg4 __asm__ ("r3") = (long)(arg4); \ + \ + __asm__ volatile ( \ + "trap0(#1)\n" \ + : "+r"(_arg1) \ + : "r"(_arg2), "r"(_arg3), "r"(_arg4), \ + "r"(_num) \ + : _NOLIBC_SYSCALL_CLOBBERLIST \ + ); \ + _arg1; \ +}) + +#define __nolibc_syscall5(num, arg1, arg2, arg3, arg4, arg5) \ +({ \ + register long _num __asm__ ("r6") = (num); \ + register long _arg1 __asm__ ("r0") = (long)(arg1); \ + register long _arg2 __asm__ ("r1") = (long)(arg2); \ + register long _arg3 __asm__ ("r2") = (long)(arg3); \ + register long _arg4 __asm__ ("r3") = (long)(arg4); \ + register long _arg5 __asm__ ("r4") = (long)(arg5); \ + \ + __asm__ volatile ( \ + "trap0(#1)\n" \ + : "+r"(_arg1) \ + : "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \ + "r"(_num) \ + : _NOLIBC_SYSCALL_CLOBBERLIST \ + ); \ + _arg1; \ +}) + +#define __nolibc_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \ +({ \ + register long _num __asm__ ("r6") = (num); \ + register long _arg1 __asm__ ("r0") = (long)(arg1); \ + register long _arg2 __asm__ ("r1") = (long)(arg2); \ + register long _arg3 __asm__ ("r2") = (long)(arg3); \ + register long _arg4 __asm__ ("r3") = (long)(arg4); \ + register long _arg5 __asm__ ("r4") = (long)(arg5); \ + register long _arg6 __asm__ ("r5") = (long)(arg6); \ + \ + __asm__ volatile ( \ + "trap0(#1)\n" \ + : "+r"(_arg1) \ + : "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), "r"(_arg6), \ + "r"(_num) \ + : _NOLIBC_SYSCALL_CLOBBERLIST \ + ); \ + _arg1; \ +}) + +#ifndef NOLIBC_NO_RUNTIME +/* startup code */ +void __attribute__((weak, noreturn)) +__nolibc_entrypoint __nolibc_no_stack_protector +_start(void) +{ + __asm__ volatile ( + "r0 = sp\n" /* save stack pointer to r0, as arg1 of _start_c */ + "call _start_c\n" /* transfer to c runtime */ + ); + __nolibc_entrypoint_epilogue(); +} +#endif /* NOLIBC_NO_RUNTIME */ + +static __attribute__((unused)) +int _sys_ftruncate64(int fd, uint32_t length0, uint32_t length1) +{ + return __nolibc_syscall4(__NR_ftruncate64, fd, 0, length0, length1); +} +#define _sys_ftruncate64 _sys_ftruncate64 + +#endif /* _NOLIBC_ARCH_HEXAGON_H */ diff --git a/tools/include/nolibc/arch.h b/tools/include/nolibc/arch.h index 06cc2dbe7a33..24e0ec4fb2db 100644 --- a/tools/include/nolibc/arch.h +++ b/tools/include/nolibc/arch.h @@ -34,6 +34,8 @@ #include "arch-parisc.h" #elif defined(__alpha__) #include "arch-alpha.h" +#elif defined(__hexagon__) +#include "arch-hexagon.h" #else #error Unsupported Architecture #endif diff --git a/tools/include/nolibc/dirent.h b/tools/include/nolibc/dirent.h index 4e02ef25e72d..2dbf4052b85a 100644 --- a/tools/include/nolibc/dirent.h +++ b/tools/include/nolibc/dirent.h @@ -30,10 +30,23 @@ typedef struct { static __attribute__((unused)) DIR *fdopendir(int fd) { + struct stat buf; + int ret; + if (fd < 0) { SET_ERRNO(EBADF); return NULL; } + + ret = fstat(fd, &buf); + if (ret < 0) + return NULL; + + if (!S_ISDIR(buf.st_mode)) { + SET_ERRNO(ENOTDIR); + return NULL; + } + return (DIR *)(intptr_t)~fd; } @@ -42,10 +55,11 @@ DIR *opendir(const char *name) { int fd; - fd = open(name, O_RDONLY); + fd = open(name, O_RDONLY | O_DIRECTORY); if (fd == -1) return NULL; - return fdopendir(fd); + + return (DIR *)(intptr_t)~fd; } static __attribute__((unused)) diff --git a/tools/include/nolibc/nolibc.h b/tools/include/nolibc/nolibc.h index faa94f247281..352a207bbedc 100644 --- a/tools/include/nolibc/nolibc.h +++ b/tools/include/nolibc/nolibc.h @@ -106,6 +106,7 @@ #include "sys/reboot.h" #include "sys/resource.h" #include "sys/select.h" +#include "sys/sendfile.h" #include "sys/stat.h" #include "sys/syscall.h" #include "sys/sysmacros.h" diff --git a/tools/include/nolibc/sys/sendfile.h b/tools/include/nolibc/sys/sendfile.h new file mode 100644 index 000000000000..ac7d85847718 --- /dev/null +++ b/tools/include/nolibc/sys/sendfile.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: LGPL-2.1 OR MIT */ +/* + * sendfile for NOLIBC + * Copyright (C) 2026 Daniel Palmer <daniel@thingy.jp> + */ + +/* make sure to include all global symbols */ +#include "../nolibc.h" + +#ifndef _NOLIBC_SYS_SENDFILE_H +#define _NOLIBC_SYS_SENDFILE_H + +#include "../sys.h" +#include <linux/unistd.h> + +/* + * ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count); + */ + +static __attribute__((unused)) +ssize_t _sys_sendfile(int out_fd, int in_fd, off_t *offset, size_t count) +{ + __nolibc_static_assert(sizeof(*offset) == sizeof(__kernel_loff_t)); + +#ifdef __NR_sendfile64 + /* 32-bit applications use sendfile64 so offset is treated as a __kernel_loff_t */ + return __nolibc_syscall4(__NR_sendfile64, out_fd, in_fd, offset, count); +#else + __nolibc_static_assert(sizeof(*offset) == sizeof(__kernel_off_t)); + + return __nolibc_syscall4(__NR_sendfile, out_fd, in_fd, offset, count); +#endif +} + +static __attribute__((unused)) +ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count) +{ + return __sysret(_sys_sendfile(out_fd, in_fd, offset, count)); +} + +#endif /* _NOLIBC_SYS_SENDFILE_H */ diff --git a/tools/testing/selftests/nolibc/Makefile.nolibc b/tools/testing/selftests/nolibc/Makefile.nolibc index f70c8dfca018..d8b71cbfc173 100644 --- a/tools/testing/selftests/nolibc/Makefile.nolibc +++ b/tools/testing/selftests/nolibc/Makefile.nolibc @@ -206,6 +206,8 @@ CFLAGS_mips64be = -EB -mabi=64 -march=mips64r2 CFLAGS_loongarch = $(if $(LLVM),-fuse-ld=lld) CFLAGS_sparc32 = $(call cc-option,-m32) -mcpu=v8 CFLAGS_sh4 = -ml -m4 +# github.com/llvm/llvm-project/issues/216765 +CFLAGS_hexagon = -fno-sanitize=undefined ifeq ($(origin XARCH),command line) CFLAGS_XARCH = $(CFLAGS_$(XARCH)) endif @@ -222,10 +224,16 @@ LDFLAGS := # Modify CFLAGS based on LLVM= include $(srctree)/tools/scripts/Makefile.include +ifeq ($(ARCH),hexagon) +# tools/scripts/Makefile.include requires CROSS_COMPILE which does not exist for hexagon +CFLAGS += --target=hexagon-linux-musl +CLANG_CROSS_FLAGS += --target=hexagon-linux-musl +endif + REPORT ?= awk '/\[OK\][\r]*$$/{p++} /\[FAIL\][\r]*$$/{if (!f) printf("\n"); f++; print;} /\[SKIPPED\][\r]*$$/{s++} \ /^Total number of errors:/{done++} \ END{ printf("\n%3d test(s): %3d passed, %3d skipped, %3d failed => status: ", p+s+f, p, s, f); \ - if (f || !p || !done) printf("failure\n"); else if (s) printf("warning\n"); else printf("success\n");; \ + if (f || !p || !done) printf("failure\n"); else printf("success\n");; \ printf("\nSee all results in %s\n", ARGV[1]); }' # Execute the toplevel kernel Makefile diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index ed860b0a15a1..7091f63f3b25 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -22,6 +22,7 @@ #include <sys/random.h> #include <sys/reboot.h> #include <sys/resource.h> +#include <sys/sendfile.h> #include <sys/stat.h> #include <sys/syscall.h> #include <sys/sysmacros.h> @@ -1563,6 +1564,72 @@ out: return ret; } +int test_sendfile(void) +{ + char data_in[] = "This is some data"; + const size_t data_sz = sizeof(data_in); + char data_out[data_sz + 10]; + int in_fd, out_fd; + int ret; + + /* Create two tmp files */ + in_fd = open("/tmp", O_TMPFILE | O_RDWR, 0644); + out_fd = open("/tmp", O_TMPFILE | O_RDWR, 0644); + + if (in_fd == -1 || out_fd == -1) { + ret = __LINE__; + goto out; + } + + /* Populate the "in" file */ + ret = write(in_fd, data_in, data_sz); + if (ret != data_sz) { + ret = __LINE__; + goto out; + } + + /* Rewind the "in" file */ + if (lseek(in_fd, 0, SEEK_SET)) { + ret = __LINE__; + goto out; + } + + /* Use sendfile() to copy "in" to "out" */ + ret = sendfile(out_fd, in_fd, NULL, data_sz + 5); + if (ret != data_sz) { + ret = __LINE__; + goto out; + } + + /* Rewind the "out" file */ + if (lseek(out_fd, 0, SEEK_SET)) { + ret = __LINE__; + goto out; + } + + /* Read back the transferred data */ + ret = read(out_fd, data_out, sizeof(data_out)); + if (ret != data_sz) { + ret = __LINE__; + goto out; + } + + /* Check we have the same data in both files */ + if (memcmp(data_out, data_in, data_sz)) { + ret = __LINE__; + goto out; + } + + /* Test passed */ + ret = 0; + +out: + close(out_fd); + close(in_fd); + + return ret; +} + /* Run syscall tests between IDs <min> and <max>. * Return 0 on success, non-zero on failure. */ @@ -1644,6 +1711,7 @@ int run_syscall(int min, int max) CASE_TEST(execve_root); EXPECT_SYSER(1, execve("/", (char*[]){ [0] = (char []){"/"}, [1] = NULL }, NULL), -1, EACCES); break; CASE_TEST(fchdir_stdin); EXPECT_SYSER(1, fchdir(STDIN_FILENO), -1, ENOTDIR); break; CASE_TEST(fchdir_badfd); EXPECT_SYSER(1, fchdir(-1), -1, EBADF); break; + CASE_TEST(fdopendir_notdir); EXPECT_SYSER(1, (uintptr_t)fdopendir(STDIN_FILENO), (uintptr_t)NULL, ENOTDIR); break; CASE_TEST(file_stream); EXPECT_SYSZR(1, test_file_stream()); break; CASE_TEST(file_stream_wsr); EXPECT_SYSZR(1, test_file_stream_wsr()); break; CASE_TEST(fork); EXPECT_SYSZR(1, test_fork(FORK_STANDARD)); break; @@ -1672,6 +1740,7 @@ int run_syscall(int min, int max) CASE_TEST(open_blah); EXPECT_SYSER(1, tmp = open("/proc/self/blah", O_RDONLY), -1, ENOENT); if (tmp != -1) close(tmp); break; CASE_TEST(openat_dir); EXPECT_SYSZR(1, test_openat()); break; CASE_TEST(open_mode); EXPECT_SYSZR(1, test_open_mode()); break; + CASE_TEST(opendir_notdir); EXPECT_SYSER(1, (uintptr_t)opendir("/dev/stdin"), (uintptr_t)NULL, ENOTDIR); break; CASE_TEST(pipe); EXPECT_SYSZR(1, test_pipe()); break; CASE_TEST(poll_null); EXPECT_SYSZR(1, poll(NULL, 0, 0)); break; CASE_TEST(poll_stdout); EXPECT_SYSNE(1, ({ struct pollfd fds = { 1, POLLOUT, 0}; poll(&fds, 1, 0); }), -1); break; @@ -1684,6 +1753,7 @@ int run_syscall(int min, int max) CASE_TEST(select_null); EXPECT_SYSZR(1, ({ struct timeval tv = { 0 }; select(0, NULL, NULL, NULL, &tv); })); break; CASE_TEST(select_stdout); EXPECT_SYSNE(1, ({ fd_set fds; FD_ZERO(&fds); FD_SET(1, &fds); select(2, NULL, &fds, NULL, NULL); }), -1); break; CASE_TEST(select_fault); EXPECT_SYSER(1, select(1, (void *)1, NULL, NULL, 0), -1, EFAULT); break; + CASE_TEST(sendfile); EXPECT_SYSZR(1, test_sendfile()); break; CASE_TEST(stat_blah); EXPECT_SYSER(1, stat("/proc/self/blah", &stat_buf), -1, ENOENT); break; CASE_TEST(stat_fault); EXPECT_SYSER(1, stat(NULL, &stat_buf), -1, EFAULT); break; CASE_TEST(stat_rdev); EXPECT_SYSZR(1, ({ int ret = stat("/dev/null", &stat_buf); ret ?: stat_buf.st_rdev != makedev(1, 3); })); break; diff --git a/tools/testing/selftests/nolibc/run-tests.sh b/tools/testing/selftests/nolibc/run-tests.sh index dc0b1649c641..af2a6f510d1d 100755 --- a/tools/testing/selftests/nolibc/run-tests.sh +++ b/tools/testing/selftests/nolibc/run-tests.sh @@ -31,6 +31,7 @@ all_archs=( sh4 parisc32 alpha + hexagon ) archs="${all_archs[@]}" @@ -132,6 +133,13 @@ crosstool_abi() { esac } +need_gcc() { + case "$1" in + hexagon);; + *) echo "1";; + esac +} + download_crosstool() { arch="$(crosstool_arch "$1")" abi="$(crosstool_abi "$1")" @@ -164,14 +172,18 @@ test_arch() { arch=$1 ct_arch=$(crosstool_arch "$arch") ct_abi=$(crosstool_abi "$1") + gcc="$(need_gcc "$1")" + cross_compile= - if [ ! -d "${download_location}gcc-${crosstool_version}-nolibc/${ct_arch}-${ct_abi}/bin/." ]; then + if [ -n "$gcc" ] && [ ! -d "${download_location}gcc-${crosstool_version}-nolibc/${ct_arch}-${ct_abi}/bin/." ]; then echo "No toolchain found in ${download_location}gcc-${crosstool_version}-nolibc/${ct_arch}-${ct_abi}." echo "Did you install the toolchains or set the correct arch ? Rerun with -h for help." return 1 fi - cross_compile=$(realpath "${download_location}gcc-${crosstool_version}-nolibc/${ct_arch}-${ct_abi}/bin/${ct_arch}-${ct_abi}-") + if [ -n "$gcc" ]; then + cross_compile=$(realpath "${download_location}gcc-${crosstool_version}-nolibc/${ct_arch}-${ct_abi}/bin/${ct_arch}-${ct_abi}-") + fi build_dir="${build_location}/${arch}" if [ "$werror" -ne 0 ]; then CFLAGS_EXTRA="$CFLAGS_EXTRA -Werror -Wl,--fatal-warnings" @@ -194,11 +206,15 @@ test_arch() { exit 1 esac printf '%-15s' "$arch:" - if [ "$arch" = "m68k" -o "$arch" = "sh4" -o "$arch" = "openrisc" -o "$arch" = "parisc32" -o "$arch" = "alpha" ] && [ "$llvm" = "1" ]; then + if [ "$llvm" = "1" ] && [ "$arch" = "m68k" -o "$arch" = "sh4" -o "$arch" = "openrisc" -o "$arch" = "parisc32" -o "$arch" = "alpha" ]; then + echo "Unsupported configuration" + return + fi + if [ "$test_mode" = "user" ] && [ "$arch" = "x32" ]; then echo "Unsupported configuration" return fi - if [ "$arch" = "x32" ] && [ "$test_mode" = "user" ]; then + if [ -z "$llvm" -o "$test_mode" = "system" ] && [ "$arch" = "hexagon" ]; then echo "Unsupported configuration" return fi |
