$a0 is saved across _dl_runtime_resolve(); $v0 is not.  Unfortunately,
__syscall_error() uses $v0 for its argument, not $a0 as is the MIPS ABI
standard.  This means that if lazy binding was used for __syscall_error(),
the errno value in $v0 could get corrupted.

The problem can be easily seen in testcases where syscalls in librt fail;
when librt tries to call __syscall_error() in libc, the argument gets
lost and errno gets set to a bogus value:

    # ./tst-mqueue1 ; echo $?
    mq_receive on O_WRONLY mqd_t did not fail with EBADF: Unknown error 
2004684208
    1
    # ./tst-mqueue2 ; echo $?
    mq_timedreceive with too small msg_len did not fail with EMSGSIZE: Unknown 
error 1997360560
    1
    # ./tst-mqueue4 ; echo $?
    mq_timedsend did not fail with ETIMEDOUT: Unknown error 2008747440
    1

When _dl_runtime_resolve() was taken out of the equation, the same test
cases passed:

    # LD_BIND_NOW=y ./tst-mqueue1 ; echo $?
    0
    # LD_BIND_NOW=y ./tst-mqueue2 ; echo $?
    0
    # LD_BIND_NOW=y ./tst-mqueue4 ; echo $?
    0

Changing __syscall_error() to look at $a0 instead of $v0 fixed the
problem.

(Note that there is also a "__syscall_error.c" file which presumably
uses the standard C calling conventions, but I do not think it is used
on MIPS.)

Signed-off-by: Kevin Cernekee <cerne...@gmail.com>
---
 libc/sysdeps/linux/mips/syscall_error.S |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/libc/sysdeps/linux/mips/syscall_error.S 
b/libc/sysdeps/linux/mips/syscall_error.S
index 51a8efa..0cc20da 100644
--- a/libc/sysdeps/linux/mips/syscall_error.S
+++ b/libc/sysdeps/linux/mips/syscall_error.S
@@ -43,7 +43,7 @@ ENTRY(__syscall_error)
 #ifdef __PIC__
        SAVE_GP(GPOFF)
 #endif
-       REG_S   v0, V0OFF(sp)
+       REG_S   a0, V0OFF(sp)
        REG_S   ra, RAOFF(sp)
 
        /* Find our per-thread errno address  */
-- 
1.7.9

_______________________________________________
uClibc mailing list
uClibc@uclibc.org
http://lists.busybox.net/mailman/listinfo/uclibc

Reply via email to