Re: [PATCH 5/7] tools/nolibc: handle 64-bit system call arguments on MIPS N32
From: David Laight
Date: Sat Apr 18 2026 - 12:33:20 EST
On Sat, 18 Apr 2026 13:54:55 +0200
Thomas Weißschuh <linux@xxxxxxxxxxxxxx> wrote:
> Hey David,
>
> Apr 18, 2026 13:14:46 David Laight <david.laight.linux@xxxxxxxxx>:
>
> > On Sat, 18 Apr 2026 12:20:00 +0200
> > Thomas Weißschuh <linux@xxxxxxxxxxxxxx> wrote:
> >
> >> The N32 system call ABI expects 64-bit values directly in registers.
> >> This does not work on nolibc currently, as a 'long' is only 32 bits
> >> wide. Switch the system call wrappers to use 'long long' instead which
> >> can handle 64-bit values on N32. As on N64 'long' and 'long long' are
> >> the same, this does not change the behavior there.
> >>
> >> Signed-off-by: Thomas Weißschuh <linux@xxxxxxxxxxxxxx>
> >> ---
> >> tools/include/nolibc/arch-mips.h | 94 +++++++++++++++++++++-------------------
> >> 1 file changed, 49 insertions(+), 45 deletions(-)
> >>
> >> diff --git a/tools/include/nolibc/arch-mips.h b/tools/include/nolibc/arch-mips.h
> >> index bb9d580ea1b1..557ef34d9df8 100644
> >> --- a/tools/include/nolibc/arch-mips.h
> >> +++ b/tools/include/nolibc/arch-mips.h
> >> @@ -55,6 +55,8 @@
> >> #define _NOLIBC_SYSCALL_STACK_RESERVE "addiu $sp, $sp, -32\n"
> >> #define _NOLIBC_SYSCALL_STACK_UNRESERVE "addiu $sp, $sp, 32\n"
> >>
> >> +#define _NOLIBC_SYSCALL_REG register long
> >> +
> >> #else /* _ABIN32 || _ABI64 */
> >>
> >> /* binutils, GCC and clang disagree about register aliases, use numbers instead. */
> >> @@ -66,12 +68,14 @@
> >> #define _NOLIBC_SYSCALL_STACK_RESERVE
> >> #define _NOLIBC_SYSCALL_STACK_UNRESERVE
> >>
> >> +#define _NOLIBC_SYSCALL_REG register long long
> >> +
> >> #endif /* _ABIO32 */
> >
> > Since you need to use a #define, did you think about:
> > #define _NOLIBC_SYSCALL_REG(var, reg) register long long var __asm__ (reg)
> > to shorten the lines and the repetitive pattern.
>
> I didn't think of this.
> Personally I am fine with both variants.
> The parameterized macro is a bit weird
> is it breaks the normal syntax.
> Let's wait what Willy thinks.
Actually you could take it one stage further:
#define _NOLIBC_SYSCALL_REG(reg) register long long _##reg __asm__ (#reg)
So you'd have:
_NOLIBC_SYSCALL_REG(a0) = (cast)(arg1);
I'd then use "+r" (_v0), "+r" (_a3).
For the variables that get pushed use the [name] syntax to avoid counting,
they also don't need (well shouldn't need) a local variable - the cast
can be included on the asm line. Giving:
"sw %[arg5], 16($sp)\n"
...
[arg5]"r"((long)(arg5), ...
(Does look like those are all 32bit only, so the (long) cast is correct.)
David
>
> >> #define __nolibc_syscall0(num) \
> >> ({ \
> >> - register long _num __asm__ ("v0") = (num); \
> >> - register long _arg4 __asm__ ("a3"); \
> >> + _NOLIBC_SYSCALL_REG _num __asm__ ("v0") = (num); \
> >> + _NOLIBC_SYSCALL_REG _arg4 __asm__ ("a3"); \
> >
> > __NOLIBC_SYSCALL_REG(_num, "v0") = (num);
> > __NOLIBC_SYSCALL_REG(_arg4, "a3");
> >
> > ...
> >
> > David
>
>