Re: 64 bit types with varargs on ARM

  • From: Mike Pall <mike-1208@xxxxxxxxxx>
  • To: luajit@xxxxxxxxxxxxx
  • Date: Mon, 13 Aug 2012 12:18:51 +0200

Justin Cormack wrote:
> local function fallocate(fd, mode, offset, len)
>   return ffi.C.syscall(fa[ffi.arch], int(fd), uint(0), loff(offset), 
> loff(len))
> end

This is not LuaJIT's fault -- it correctly implements the ARM
calling convention.

But you're not using the correct calling convention for 32 bit
user mode when calling a Linux system call with 64 bit arguments.

It happens to work on x86, because 64 bit arguments are not
specially aligned. And it works on x64, because 64 bit arguments
are passed in a single register.

But most other 32 bit ABIs (e.g. ARM or PPC) align 64 bit arguments
to even/odd register pairs or stack slots. This creates a dummy
padding argument before the offset argument for syscall(). However,
the actual kernel interface has the system call number in a
special register. Then fd is the first argument and there's no
need for padding the offset. With e.g. ftruncate, you'd get the
opposite problem (no padding in syscall, padding in kernel call).

The implication would be that syscall() couldn't simply shift down
its arguments -- it would depend on the system call how to do
that. That's not really workable, so it was decided a long time
ago that 64 bit Linux system call arguments are to be passed as
two 32 bit arguments from 32 bit user mode and as a single 64 bit
argument from 64 bit user mode.

Have a look at the eglibc 32 bit user mode wrapper for fallocate64:

http://www.eglibc.org/cgi-bin/viewvc.cgi/trunk/libc/sysdeps/unix/sysv/linux/fallocate64.c?revision=17195&view=markup

tl;dr: Use the code quoted above for 64 bit archs, but split up
64 bit arguments for 32 bit archs when using syscall().

--Mike

Other related posts: