* [PATCH 1/5] selftests/nolibc: make the broken MIPS N32 argument detection reusable
2026-09-27 8:49 [PATCH 0/5] tools/nolibc: pwrite() and pread() support Thomas Weißschuh
@ 2026-09-27 8:49 ` Thomas Weißschuh
2026-09-27 8:49 ` [PATCH 2/5] tools/nolibc: make 64-bit systemcall argument padding generic Thomas Weißschuh
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Thomas Weißschuh @ 2026-09-27 8:49 UTC (permalink / raw)
To: Willy Tarreau, Shuah Khan
Cc: linux-kernel, linux-kselftest, Thomas Weißschuh
Another test is about to be added which needs to make use of this.
Move the definition into a reusable variable.
As a side-effect, the test is now properly marked as skipped.
Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
tools/testing/selftests/nolibc/nolibc-test.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c
index 1995d67dcca6..c344675ad0c9 100644
--- a/tools/testing/selftests/nolibc/nolibc-test.c
+++ b/tools/testing/selftests/nolibc/nolibc-test.c
@@ -1525,12 +1525,6 @@ int test_large_file(void)
ssize_t written;
off_t off;
-#if defined(__mips__) && defined(_ABIN32)
- /* https://lore.kernel.org/qemu-devel/fed03914-a95a-4522-a432-f129264cb2ac@t-8ch.de/ */
- if (getpid() != 1)
- return 0;
-#endif
-
if (large_seek < UINT32_MAX) {
errno = EOVERFLOW;
return -1;
@@ -1651,6 +1645,7 @@ int run_syscall(int min, int max)
void *p1, *p2;
int has_gettid = 1;
int has_brk;
+ int has_working_64bit_syscall_arg = 1;
/* <proc> indicates whether or not /proc is mounted */
proc = stat("/proc", &stat_buf) == 0;
@@ -1666,6 +1661,11 @@ int run_syscall(int min, int max)
/* on musl setting brk()/sbrk() always fails */
has_brk = brk(0) == 0;
+#if defined(__mips__) && defined(_ABIN32)
+ /* https://lore.kernel.org/qemu-devel/fed03914-a95a-4522-a432-f129264cb2ac@t-8ch.de/ */
+ has_working_64bit_syscall_arg = getpid() == 1;
+#endif
+
for (test = min; test >= 0 && test <= max; test++) {
int llen = 0; /* line length */
@@ -1781,7 +1781,7 @@ int run_syscall(int min, int max)
CASE_TEST(_syscall_noargs); EXPECT_SYSEQ(is_nolibc, _syscall(__NR_getpid), getpid()); break;
CASE_TEST(_syscall_args); EXPECT_SYSEQ(is_nolibc, _syscall(__NR_statx, 0, NULL, 0, 0, NULL), -EFAULT); break;
CASE_TEST(namespace); EXPECT_SYSZR(euid0 && proc, test_namespace()); break;
- CASE_TEST(largefile); EXPECT_SYSZR(1, test_large_file()); break;
+ CASE_TEST(largefile); EXPECT_SYSZR(has_working_64bit_syscall_arg, test_large_file()); break;
case __LINE__:
return ret; /* must be last */
/* note: do not set any defaults so as to permit holes above */
--
2.55.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH 2/5] tools/nolibc: make 64-bit systemcall argument padding generic
2026-09-27 8:49 [PATCH 0/5] tools/nolibc: pwrite() and pread() support Thomas Weißschuh
2026-09-27 8:49 ` [PATCH 1/5] selftests/nolibc: make the broken MIPS N32 argument detection reusable Thomas Weißschuh
@ 2026-09-27 8:49 ` Thomas Weißschuh
2026-09-27 8:49 ` [PATCH 3/5] tools/nolibc: move __NOLIBC_LLARGPART() to compiler.h Thomas Weißschuh
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Thomas Weißschuh @ 2026-09-27 8:49 UTC (permalink / raw)
To: Willy Tarreau, Shuah Khan
Cc: linux-kernel, linux-kselftest, Thomas Weißschuh
Several 32-bit architectures pass a 64-bit systemcall argument
as two 32-bit values requiring alignment to an even register pair.
This case is currently relevant for the ftruncate64() wrapper and handled
by those architectures providing custom wrappers.
With the introduction of pread() and pwrite() wrappers, the same pattern
would lead to a lot of repeated code.
Add a #define, so that the generic code can do the padding where
necessary, without any per-architecture duplication.
Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
tools/include/nolibc/arch-arm.h | 7 +------
tools/include/nolibc/arch-hexagon.h | 7 +------
tools/include/nolibc/arch-mips.h | 7 +------
tools/include/nolibc/arch-powerpc.h | 7 +------
tools/include/nolibc/unistd.h | 4 ++++
5 files changed, 8 insertions(+), 24 deletions(-)
diff --git a/tools/include/nolibc/arch-arm.h b/tools/include/nolibc/arch-arm.h
index b88686fb19c4..b5b3456c75cb 100644
--- a/tools/include/nolibc/arch-arm.h
+++ b/tools/include/nolibc/arch-arm.h
@@ -196,11 +196,6 @@ void __attribute__((weak, noreturn)) __nolibc_entrypoint __nolibc_no_stack_prote
}
#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
+#define __NOLIBC_PAD_64BIT_SYSCALL_ARGUMENT_PAIR
#endif /* _NOLIBC_ARCH_ARM_H */
diff --git a/tools/include/nolibc/arch-hexagon.h b/tools/include/nolibc/arch-hexagon.h
index 0767054882f3..68f73529a6dd 100644
--- a/tools/include/nolibc/arch-hexagon.h
+++ b/tools/include/nolibc/arch-hexagon.h
@@ -154,11 +154,6 @@ _start(void)
}
#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
+#define __NOLIBC_PAD_64BIT_SYSCALL_ARGUMENT_PAIR
#endif /* _NOLIBC_ARCH_HEXAGON_H */
diff --git a/tools/include/nolibc/arch-mips.h b/tools/include/nolibc/arch-mips.h
index 26ad413cec62..21b9959a08e2 100644
--- a/tools/include/nolibc/arch-mips.h
+++ b/tools/include/nolibc/arch-mips.h
@@ -286,12 +286,7 @@ void __attribute__((weak, noreturn)) __nolibc_entrypoint __nolibc_no_stack_prote
#endif /* NOLIBC_NO_RUNTIME */
#if defined(_ABIO32)
-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
+#define __NOLIBC_PAD_64BIT_SYSCALL_ARGUMENT_PAIR
#endif
#endif /* _NOLIBC_ARCH_MIPS_H */
diff --git a/tools/include/nolibc/arch-powerpc.h b/tools/include/nolibc/arch-powerpc.h
index dbe2e5205aaa..a4711638cb66 100644
--- a/tools/include/nolibc/arch-powerpc.h
+++ b/tools/include/nolibc/arch-powerpc.h
@@ -222,12 +222,7 @@ void __attribute__((weak, noreturn)) __nolibc_entrypoint __nolibc_no_stack_prote
#endif /* NOLIBC_NO_RUNTIME */
#if !defined(__powerpc64__)
-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
+#define __NOLIBC_PAD_64BIT_SYSCALL_ARGUMENT_PAIR
#endif
#endif /* _NOLIBC_ARCH_POWERPC_H */
diff --git a/tools/include/nolibc/unistd.h b/tools/include/nolibc/unistd.h
index a264a20da13d..939d4e05a55d 100644
--- a/tools/include/nolibc/unistd.h
+++ b/tools/include/nolibc/unistd.h
@@ -52,7 +52,11 @@ int access(const char *path, int amode)
static __attribute__((unused))
int _sys_ftruncate64(int fd, uint32_t length0, uint32_t length1)
{
+#if defined(__NOLIBC_PAD_64BIT_SYSCALL_ARGUMENT_PAIR)
+ return __nolibc_syscall4(__NR_ftruncate64, fd, 0, length0, length1);
+#else
return __nolibc_syscall3(__NR_ftruncate64, fd, length0, length1);
+#endif
}
#define _sys_ftruncate64 _sys_ftruncate64
#endif
--
2.55.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH 5/5] tools/nolibc: add support for pread() and pwrite()
2026-09-27 8:49 [PATCH 0/5] tools/nolibc: pwrite() and pread() support Thomas Weißschuh
` (3 preceding siblings ...)
2026-09-27 8:49 ` [PATCH 4/5] tools/nolibc: introduce __NOLIBC_BITS_PER_SYSCALL_ARG Thomas Weißschuh
@ 2026-09-27 8:49 ` Thomas Weißschuh
2026-09-27 9:10 ` [PATCH 0/5] tools/nolibc: pwrite() and pread() support Willy Tarreau
5 siblings, 0 replies; 7+ messages in thread
From: Thomas Weißschuh @ 2026-09-27 8:49 UTC (permalink / raw)
To: Willy Tarreau, Shuah Khan
Cc: linux-kernel, linux-kselftest, Thomas Weißschuh
These are standard function to interact with a file descriptor without
touching its internal file offset.
Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
tools/include/nolibc/arch-sh.h | 18 ++++++++++
tools/include/nolibc/sys.h | 50 ++++++++++++++++++++++++++++
tools/testing/selftests/nolibc/nolibc-test.c | 46 +++++++++++++++++++++++++
3 files changed, 114 insertions(+)
diff --git a/tools/include/nolibc/arch-sh.h b/tools/include/nolibc/arch-sh.h
index a32378fd621f..996cfbe1bee3 100644
--- a/tools/include/nolibc/arch-sh.h
+++ b/tools/include/nolibc/arch-sh.h
@@ -10,6 +10,8 @@
#include "compiler.h"
#include "crt.h"
+#include <linux/unistd.h>
+
/*
* Syscalls for SuperH:
* - registers are 32bit wide
@@ -161,4 +163,20 @@ void __attribute__((weak, noreturn)) __nolibc_entrypoint __nolibc_no_stack_prote
}
#endif /* NOLIBC_NO_RUNTIME */
+static __attribute__((unused))
+ssize_t _sys_pread(int fd, void *buf, size_t count, off_t offset)
+{
+ return __nolibc_syscall6(__NR_pread64, fd, buf, count, 0,
+ __NOLIBC_LLARGPART(offset, 0), __NOLIBC_LLARGPART(offset, 1));
+}
+#define _sys_pread _sys_pread
+
+static __attribute__((unused))
+ssize_t _sys_pwrite(int fd, const void *buf, size_t count, off_t offset)
+{
+ return __nolibc_syscall6(__NR_pwrite64, fd, buf, count, 0,
+ __NOLIBC_LLARGPART(offset, 0), __NOLIBC_LLARGPART(offset, 1));
+}
+#define _sys_pwrite _sys_pwrite
+
#endif /* _NOLIBC_ARCH_SH_H */
diff --git a/tools/include/nolibc/sys.h b/tools/include/nolibc/sys.h
index 7f06d27da612..b63fa6c618c5 100644
--- a/tools/include/nolibc/sys.h
+++ b/tools/include/nolibc/sys.h
@@ -745,6 +745,56 @@ int pivot_root(const char *new, const char *old)
}
+/*
+ * ssize_t pread(int fd, void *buf, size_t count, off_t offset);
+ * ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
+ */
+
+#ifndef _sys_pread
+static __attribute__((unused))
+ssize_t _sys_pread(int fd, void *buf, size_t count, off_t offset)
+{
+#if __NOLIBC_BITS_PER_SYSCALL_ARG == 64
+ return __nolibc_syscall4(__NR_pread64, fd, buf, count, offset);
+#elif defined(__NOLIBC_PAD_64BIT_SYSCALL_ARGUMENT_PAIR)
+ return __nolibc_syscall6(__NR_pread64, fd, buf, count, 0,
+ __NOLIBC_LLARGPART(offset, 0), __NOLIBC_LLARGPART(offset, 1));
+#else
+ return __nolibc_syscall5(__NR_pread64, fd, buf, count,
+ __NOLIBC_LLARGPART(offset, 0), __NOLIBC_LLARGPART(offset, 1));
+#endif
+}
+#endif /* _sys_pread */
+
+static __attribute__((unused))
+ssize_t pread(int fd, void *buf, size_t count, off_t offset)
+{
+ return __sysret(_sys_pread(fd, buf, count, offset));
+}
+
+#ifndef _sys_pwrite
+static __attribute__((unused))
+ssize_t _sys_pwrite(int fd, const void *buf, size_t count, off_t offset)
+{
+#if __NOLIBC_BITS_PER_SYSCALL_ARG == 64
+ return __nolibc_syscall4(__NR_pwrite64, fd, buf, count, offset);
+#elif defined(__NOLIBC_PAD_64BIT_SYSCALL_ARGUMENT_PAIR)
+ return __nolibc_syscall6(__NR_pwrite64, fd, buf, count, 0,
+ __NOLIBC_LLARGPART(offset, 0), __NOLIBC_LLARGPART(offset, 1));
+#else
+ return __nolibc_syscall5(__NR_pwrite64, fd, buf, count,
+ __NOLIBC_LLARGPART(offset, 0), __NOLIBC_LLARGPART(offset, 1));
+#endif
+}
+#endif /* _sys_pwrite */
+
+static __attribute__((unused))
+ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset)
+{
+ return __sysret(_sys_pwrite(fd, buf, count, offset));
+}
+
+
/*
* ssize_t read(int fd, void *buf, size_t count);
*/
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c
index c344675ad0c9..f3d0d27cdbe1 100644
--- a/tools/testing/selftests/nolibc/nolibc-test.c
+++ b/tools/testing/selftests/nolibc/nolibc-test.c
@@ -1369,6 +1369,51 @@ int test_pipe(void)
return !!memcmp(buf, msg, len);
}
+int test_pwrite_pread(void)
+{
+ const off_t offset = (0x12ULL << 32) | 0x34;
+ ssize_t ret;
+ char buf[2];
+ int fd, err;
+ off_t offs;
+
+ fd = open("/tmp", O_TMPFILE | O_RDWR, 0644);
+ if (fd == -1)
+ return __LINE__;
+
+ /* Write into the middle of the file. */
+ ret = pwrite(fd, "abc", 3, offset);
+ if (ret != 3) {
+ err = __LINE__;
+ goto out;
+ }
+
+ /* Make sure the file offset points somewhere else before pread(). */
+ offs = lseek(fd, 0, SEEK_END);
+ if (offs != offset + 3) {
+ err = __LINE__;
+ goto out;
+ }
+
+ ret = pread(fd, buf, 2, offset + 1);
+ if (ret != 2) {
+ err = __LINE__;
+ goto out;
+ }
+
+ if (buf[0] != 'b' || buf[1] != 'c') {
+ err = __LINE__;
+ goto out;
+ }
+
+ err = 0;
+
+out:
+ close(fd);
+
+ return err;
+}
+
int test_rlimit(void)
{
struct rlimit rlim = {
@@ -1746,6 +1791,7 @@ int run_syscall(int min, int max)
CASE_TEST(poll_stdout); EXPECT_SYSNE(1, ({ struct pollfd fds = { 1, POLLOUT, 0}; poll(&fds, 1, 0); }), -1); break;
CASE_TEST(poll_fault); EXPECT_SYSER(1, poll(NULL, 1, 0), -1, EFAULT); break;
CASE_TEST(prctl); EXPECT_SYSER(1, prctl(PR_SET_NAME, (unsigned long)NULL, 0, 0, 0), -1, EFAULT); break;
+ CASE_TEST(pwrite_pread); EXPECT_SYSZR(has_working_64bit_syscall_arg, test_pwrite_pread()); break;
CASE_TEST(read_badf); EXPECT_SYSER(1, read(-1, &tmp, 1), -1, EBADF); break;
CASE_TEST(rlimit); EXPECT_SYSZR(1, test_rlimit()); break;
CASE_TEST(rmdir_blah); EXPECT_SYSER(1, rmdir("/blah"), -1, ENOENT); break;
--
2.55.0
^ permalink raw reply [flat|nested] 7+ messages in thread