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
>
>