Hi Aurelien,

Got it, thanks for the explanation.

Just curious, does the same problem exist on X86-64(64bit kernel + 32 bit
userland)? If yes, should the caller of syscall() need to split each 64bit
argument into two 32bit arguments?

2011/3/26 Aurelien Jarno <aurel...@aurel32.net>

> On Wed, Mar 16, 2011 at 12:36:30PM +0000, Chen Jie wrote:
> > Package: libc6
> > Version: 2.11.2-10
> >
> > syscall(__NR_fanotify_mark, fanotify_fd, flags, mask, dfd, pathname),
> > failed with 'Invalid argument'.
> > strace shows 'syscall' didn't pass the correct parameters:
> >     syscall(0x10f1, 0x3, 0x11, 0, 0x20, 0, 0xffffff9c, 0x4013e0)
> >     ( The expected one is: syscall(0x10f1, 0x3, 0x17, 0x20, 0, -100,
> 0x4013e0) )
>
> So let me summarize the difference between the real and expected values:
> - arg1: 0x10f1 in both cases, no problem
> - arg2: 0x3 in both cases, no problem
> - arg3: 0x11 in the syscall, 0x17 expected. The problem is actually the
>  test program which uses %d to display an hexadecimal number 0x11 = 17.
>  No problem on the glibc side here.
> - arg4: 0 in the syscall, not expected, will explain below why.
> - arg5 (syscall) / arg4 (expected): 0x20 in both cases, no problem
> - arg6 (syscall) / arg5 (expected): 0 in both cases, no problem
> - arg7 (syscall) / arg6 (expected) 0xffffff9c in the syscall, -100
>  expected. This is actually the same value.
> - arg8 (syscall) / arg7 (expected): 0x4013e0 in both cases, no problem.
>
> So the only non-explained problem is the 4th argument as displayed by
> strace. The syscall() function takes a variable number of arguments, and
> pass them to the __NR_syscall syscall (4000), which is the indirect
> syscall, as shown in the code below:
>
>   eecd0:       3c1c0009        lui     gp,0x9
>   eecd4:       279c5c90        addiu   gp,gp,23696
>   eecd8:       0399e021        addu    gp,gp,t9
>   eecdc:       24020fa0        li      v0,4000
>   eece0:       0000000c        syscall
>   eece4:       14e0fff6        bnez    a3,eecc0 <syslog+0x54>
>   eece8:       00200825        move    at,at
>   eecec:       03e00008        jr      ra
>   eecf0:       00200825        move    at,at
>
> syscall() follows the MIPS ABI, which means a 64-bit argument has to be
> 64-bit aligned. That's why the arguments are organized as below:
> - a0:    0x000010f1
> - a1:    0x00000003
> - a2:    0x00000011
> - a3:    not used (due to 64-bit alignement)
> - sp+16: 0x00000020
> - sp+20: 0x00000000
> - sp+24: 0xffffff9c
> - sp+28: 0x004013e0
>
> On the kernel side, the arguments are simply shifted by one (to remove
> the syscall number), without taking into account the ABI and the real
> syscall called. That's why there is an additional argument to the
> syscall.
>
> Now I don't think there is any way to fix it on the libc6 side.
> syscall() doesn't know what are the type of the passed arguments, and is
> following the MIPS ABI. It might be fixed on the kernel side, but it
> means having a table with the type of all arguments. It doesn't seems to
> be easy to do.
>
> That said, there is an easy workaround by passing only 32-bit values to
> the syscall() function on a 32-bit machine.
>
> --
> Aurelien Jarno                          GPG: 1024D/F1BCDB73
> aurel...@aurel32.net                 http://www.aurel32.net
>

Reply via email to