summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-09-07 14:48:34 +0100
committerMark Brown <broonie@kernel.org>2026-09-07 14:48:34 +0100
commit99e0bf7b4a171f63abb01ae4cabbcb7ddee53f3f (patch)
treeb0717ab9b11e149723286ac16d6c1e45f3b80b4d
parent9a6ec108829214ff53fb03b45526ffdffe12efc6 (diff)
parent6ef847683e7d294a4187a7253924b8f0d8911416 (diff)
downloadlinux-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/Makefile19
-rw-r--r--tools/include/nolibc/arch-hexagon.h164
-rw-r--r--tools/include/nolibc/arch.h2
-rw-r--r--tools/include/nolibc/dirent.h18
-rw-r--r--tools/include/nolibc/nolibc.h1
-rw-r--r--tools/include/nolibc/sys/sendfile.h41
-rw-r--r--tools/testing/selftests/nolibc/Makefile.nolibc10
-rw-r--r--tools/testing/selftests/nolibc/nolibc-test.c70
-rwxr-xr-xtools/testing/selftests/nolibc/run-tests.sh24
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