Re: [PATCH v3 1/2] tools/nolibc: fcntl: Add fallocate()
From: Thomas Weißschuh
Date: Sun May 17 2026 - 11:40:56 EST
On 2026-05-14 22:31:46+0100, David Laight wrote:
> On Thu, 14 May 2026 13:51:59 +0200
> Thomas Weißschuh <linux@xxxxxxxxxxxxxx> wrote:
> > On 2026-05-03 23:38:52+0100, David Laight wrote:
> > > On Sun, 3 May 2026 18:28:39 +0200
> > > Thomas Weißschuh <linux@xxxxxxxxxxxxxx> wrote:
> > >
> > > > On 2026-05-02 22:26:07+0100, David Laight wrote:
> > > > > On Sat, 2 May 2026 05:00:06 +0200
> > > > > Willy Tarreau <w@xxxxxx> wrote:
> > > > >
> > > > > > On Fri, May 01, 2026 at 09:18:31AM +0100, David Laight wrote:
> > > > > > > On Fri, 1 May 2026 01:41:24 +0900
> > > > > > > Daniel Palmer <daniel@xxxxxxxxx> wrote:
> > > >
> > > > (...)
> > > ...
> > > > > Looking again, the code is trying to copy what the compiler generates
> > > > > when it passes a 64bit quantity on stack or in 2 registers.
> > > > > So f(a, b, c, d) is traditionally 'push d; push c; push b, push a; call f'.
> > > > > With the normal 'stack grows down' this gives (in increasing addresses):
> > > > > return address
> > > > > a
> > > > > b
> > > > > c
> > > > > d
> > > > > If 'b,c' is replaced by a 64bit value then you want the stack memory
> > > > > to contain the correct representation of a 64bit value.
> > > > > So for LE you need to pass the low part before the high part.
> > > >
> > > > Most architectures should use registers and not the stack.
> > >
> > > Indeed, but the registers are picked as though they are caching on-stack locations.
> > > So the way the stack would look is relevant.
> >
> > So I was a bit confused about the 64-bit arguments.
> > When I worked on llseek(), the system call definition explicitly splits
> > the 64-bit arguments into two 32-bit ones. For fallocate, that split
> > happens implicitly according to the function-call ABI.
> > I still don't see, where the stack layout is supposed to come into play.
>
> Think about what happens if/when a 64bit value is passed on stack (on 32bit).
> The called code wants the two 32bit values be the same way around and have the
> same alignment constraints as any other 64bit value so that it can be passed
> by address to another function.
> If the 64bit value is put on the stack with two 'push' instructions then the order
> of the 32bit words in memory depends on whether the stack grow up or down.
> Since the last argument is pushed first (so the called code can always find the
> first argument), if the stack grows down (normal) then LE wants the LSW on the
> lower address so it must pushed second and thus be first in the argument list.
> The order needs swapping for BE (as done here) but also swapping if the stack
> grows in the opposite direction.
> When integer arguments are passed in registers AFAICT the registers match what
> would have been placed on the stack.
> So for arm32 the pad that would be required to align an on-stack 64bit value
> results in an unset/unused register.
>
> And, if you think about it, the kernel syscall takes the registers used to pass
> the arguments and converts them back to into a normal C function call without
> knowing anything about the function signature.
> So although the (no)libc stub is copying values into registers they have to
> look like a normal function call.
Fair enough. But the result of all of these considerations are done in
the kernel side. In the end for each specific system call and
architecture the 64-bit argument parts have to end up in certain
registers, which is very similar to how normal system call arguments are
also required to be in certain registers. For nolibc we can ignore *why*
these requirements exist and only need to make sure we satisfy them.
Doing this for arbitrary function prototypes would require the knowledge
about the architecture-specific rules, but we only need to do this for a
few system calls. And the solution from Daniel seems to be sufficient
for fallocate(). I think we shouldn't make things more complicated than
they need to be.
For example all of the stack alignment considerations would matter for
i386, but in fact there is a dedicated system call entry point
ia32_fallocate() which instead explicitly takes the 64-bit as two 32-bit
arguments, so they are irrelevant. For arm 32-bit, we have the implicit
handling. But then in the arm64 compat code the requirements are spelled
out explicitly again and match what Daniel proposed.
This all does not look like an accident but intentional design
decisions. So I trust if we need to support other 64-bit argument system
calls, the result will be similar.
Thomas