diff mbox

MIPS64 N64 fork is broken

Message ID 20131221121829.GA14600@waldemar-brodkorb.de
State Accepted
Commit a7e8c6aa9b192075f17774c0bbdf6829f41ba62f
Headers show

Commit Message

Waldemar Brodkorb Dec. 21, 2013, 12:18 p.m. UTC
fork() is broken for MIPS64 N64 ABI. You can check it with a simple
C program statically linked with qemu-mips64 user emulation.
Internally fork() is using the clone system call (at least with NPTL)
with 5 arguments. See ./libpthread/nptl/sysdeps/unix/sysv/linux/i386/fork.c.
The calling conventions for MIPS N32 and N64 allow to use up to 8 registers
for that. See http://en.wikipedia.org/wiki/Calling_convention#MIPS
This is correctly implemented in libc/sysdeps/linux/mips/bits/syscalls.h,
but not in libc/sysdeps/linux/mips/sysdep.h. fork.c uses the later one.
It seems that fork() works fine for MIPS64 N32 with just using the stack like
with the O32 case. There is a user of INLINE_SYSCALL with 7 arguments in
libc/sysdeps/linux/common/sync_file_range.c for MIPS64 N32, so I decided to
only use the macros for the MIPS64 N64 case. With this patch my uClibc based
Linux system boots up fine in qemu-system-mips64.

Signed-off-by: Waldemar Brodkorb <wbx@openadk.org>
---
 libc/sysdeps/linux/mips/sysdep.h |   63 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)

Comments

Waldemar Brodkorb Jan. 20, 2014, 8:37 a.m. UTC | #1
Hi,

any news/comment about this?

thx
 Waldemar

Waldemar Brodkorb wrote,

> fork() is broken for MIPS64 N64 ABI. You can check it with a simple
> C program statically linked with qemu-mips64 user emulation.
> Internally fork() is using the clone system call (at least with NPTL)
> with 5 arguments. See ./libpthread/nptl/sysdeps/unix/sysv/linux/i386/fork.c.
> The calling conventions for MIPS N32 and N64 allow to use up to 8 registers
> for that. See http://en.wikipedia.org/wiki/Calling_convention#MIPS
> This is correctly implemented in libc/sysdeps/linux/mips/bits/syscalls.h,
> but not in libc/sysdeps/linux/mips/sysdep.h. fork.c uses the later one.
> It seems that fork() works fine for MIPS64 N32 with just using the stack like
> with the O32 case. There is a user of INLINE_SYSCALL with 7 arguments in
> libc/sysdeps/linux/common/sync_file_range.c for MIPS64 N32, so I decided to
> only use the macros for the MIPS64 N64 case. With this patch my uClibc based
> Linux system boots up fine in qemu-system-mips64.
> 
> Signed-off-by: Waldemar Brodkorb <wbx@openadk.org>
> ---
>  libc/sysdeps/linux/mips/sysdep.h |   63 ++++++++++++++++++++++++++++++++++++++
>  1 file changed, 63 insertions(+)
> 
> diff --git a/libc/sysdeps/linux/mips/sysdep.h b/libc/sysdeps/linux/mips/sysdep.h
> index 6dba1fb..46b6c53 100644
> --- a/libc/sysdeps/linux/mips/sysdep.h
> +++ b/libc/sysdeps/linux/mips/sysdep.h
> @@ -279,6 +279,8 @@ L(syse1):
>  	_sys_result;							\
>  })
>  
> +#if _MIPS_SIM == _ABIO32 || _MIPS_SIM == _ABIN32
> +
>  /* We need to use a frame pointer for the functions in which we
>     adjust $sp around the syscall, or debug information and unwind
>     information will be $sp relative and thus wrong during the syscall.  As
> @@ -382,6 +384,67 @@ L(syse1):
>  #define __SYSCALL_CLOBBERS "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", \
>  	"$14", "$15", "$24", "$25", "memory"
>  
> +#else /* N64 */
> +
> +#undef internal_syscall5
> +#define internal_syscall5(ncs_init, cs_init, input, err, arg1, arg2, arg3, arg4, arg5) \
> +({ 									\
> +	long _sys_result;						\
> +									\
> +	{								\
> +	register long __v0 __asm__("$2") ncs_init;			\
> +	register long __a0 __asm__("$4") = (long) arg1; 	\
> +	register long __a1 __asm__("$5") = (long) arg2; 	\
> +	register long __a2 __asm__("$6") = (long) arg3; 	\
> +	register long __a3 __asm__("$7") = (long) arg4; 	\
> +	register long __a4 __asm__("$8") = (long) arg5; 	\
> +	__asm__ __volatile__ ( 						\
> +	".set\tnoreorder\n\t" 						\
> +	cs_init								\
> +	"syscall\n\t" 							\
> +	".set\treorder" 						\
> +	: "=r" (__v0), "+r" (__a3) 					\
> +	: input, "r" (__a0), "r" (__a1), "r" (__a2), "r" (__a4)		\
> +	: __SYSCALL_CLOBBERS); 						\
> +	err = __a3;							\
> +	_sys_result = __v0;						\
> +	}								\
> +	_sys_result;							\
> +})
> +
> +#undef internal_syscall6
> +#define internal_syscall6(ncs_init, cs_init, input, err, arg1, arg2, arg3, arg4, arg5, arg6) \
> +({ 									\
> +	long _sys_result;						\
> +									\
> +	{								\
> +	register long __v0 __asm__("$2") ncs_init;			\
> +	register long __a0 __asm__("$4") = (long) arg1; 	\
> +	register long __a1 __asm__("$5") = (long) arg2; 	\
> +	register long __a2 __asm__("$6") = (long) arg3; 	\
> +	register long __a3 __asm__("$7") = (long) arg4; 	\
> +	register long __a4 __asm__("$8") = (long) arg5; 	\
> +	register long __a5 __asm__("$9") = (long) arg6; 	\
> +	__asm__ __volatile__ ( 						\
> +	".set\tnoreorder\n\t" 						\
> +	cs_init								\
> +	"syscall\n\t" 							\
> +	".set\treorder" 						\
> +	: "=r" (__v0), "+r" (__a3) 					\
> +	: input, "r" (__a0), "r" (__a1), "r" (__a2), "r" (__a4),	\
> +	  "r" (__a5)							\
> +	: __SYSCALL_CLOBBERS); 						\
> +	err = __a3;							\
> +	_sys_result = __v0;						\
> +	}								\
> +	_sys_result;							\
> +})
> +
> +#define __SYSCALL_CLOBBERS "$1", "$3", "$10", "$11", "$12", "$13", \
> +	"$14", "$15", "$24", "$25", "hi", "lo", "memory"
> +
> +#endif
> +
>  /* Pointer mangling is not yet supported for MIPS.  */
>  #define PTR_MANGLE(var) (void) (var)
>  #define PTR_DEMANGLE(var) (void) (var)
> -- 
> 1.7.10.4
> 
> _______________________________________________
> uClibc mailing list
> uClibc@uclibc.org
> http://lists.busybox.net/mailman/listinfo/uclibc
>
Bernhard Reutner-Fischer Jan. 22, 2014, 8:06 p.m. UTC | #2
On 20 January 2014 09:37, Waldemar Brodkorb <wbx@openadk.org> wrote:
> Hi,
>
> any news/comment about this?
>
> thx
>  Waldemar
>
> Waldemar Brodkorb wrote,
>
>> fork() is broken for MIPS64 N64 ABI. You can check it with a simple
>> C program statically linked with qemu-mips64 user emulation.
>> Internally fork() is using the clone system call (at least with NPTL)
>> with 5 arguments. See ./libpthread/nptl/sysdeps/unix/sysv/linux/i386/fork.c.
>> The calling conventions for MIPS N32 and N64 allow to use up to 8 registers
>> for that. See http://en.wikipedia.org/wiki/Calling_convention#MIPS
>> This is correctly implemented in libc/sysdeps/linux/mips/bits/syscalls.h,
>> but not in libc/sysdeps/linux/mips/sysdep.h. fork.c uses the later one.
>> It seems that fork() works fine for MIPS64 N32 with just using the stack like
>> with the O32 case. There is a user of INLINE_SYSCALL with 7 arguments in
>> libc/sysdeps/linux/common/sync_file_range.c for MIPS64 N32, so I decided to
>> only use the macros for the MIPS64 N64 case. With this patch my uClibc based
>> Linux system boots up fine in qemu-system-mips64.

The mips sysdeps looks highly repetitive, it would be nice to massage it to use
something like the i386 variant. Maybe you want to do that?

I am applying the below for now though, thanks!
cheers,
>>
>> Signed-off-by: Waldemar Brodkorb <wbx@openadk.org>
>> ---
>>  libc/sysdeps/linux/mips/sysdep.h |   63 ++++++++++++++++++++++++++++++++++++++
>>  1 file changed, 63 insertions(+)
>>
>> diff --git a/libc/sysdeps/linux/mips/sysdep.h b/libc/sysdeps/linux/mips/sysdep.h
>> index 6dba1fb..46b6c53 100644
>> --- a/libc/sysdeps/linux/mips/sysdep.h
>> +++ b/libc/sysdeps/linux/mips/sysdep.h
>> @@ -279,6 +279,8 @@ L(syse1):
>>       _sys_result;                                                    \
>>  })
>>
>> +#if _MIPS_SIM == _ABIO32 || _MIPS_SIM == _ABIN32
>> +
>>  /* We need to use a frame pointer for the functions in which we
>>     adjust $sp around the syscall, or debug information and unwind
>>     information will be $sp relative and thus wrong during the syscall.  As
>> @@ -382,6 +384,67 @@ L(syse1):
>>  #define __SYSCALL_CLOBBERS "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", \
>>       "$14", "$15", "$24", "$25", "memory"
>>
>> +#else /* N64 */
>> +
>> +#undef internal_syscall5
>> +#define internal_syscall5(ncs_init, cs_init, input, err, arg1, arg2, arg3, arg4, arg5) \
>> +({                                                                   \
>> +     long _sys_result;                                               \
>> +                                                                     \
>> +     {                                                               \
>> +     register long __v0 __asm__("$2") ncs_init;                      \
>> +     register long __a0 __asm__("$4") = (long) arg1;         \
>> +     register long __a1 __asm__("$5") = (long) arg2;         \
>> +     register long __a2 __asm__("$6") = (long) arg3;         \
>> +     register long __a3 __asm__("$7") = (long) arg4;         \
>> +     register long __a4 __asm__("$8") = (long) arg5;         \
>> +     __asm__ __volatile__ (                                          \
>> +     ".set\tnoreorder\n\t"                                           \
>> +     cs_init                                                         \
>> +     "syscall\n\t"                                                   \
>> +     ".set\treorder"                                                 \
>> +     : "=r" (__v0), "+r" (__a3)                                      \
>> +     : input, "r" (__a0), "r" (__a1), "r" (__a2), "r" (__a4)         \
>> +     : __SYSCALL_CLOBBERS);                                          \
>> +     err = __a3;                                                     \
>> +     _sys_result = __v0;                                             \
>> +     }                                                               \
>> +     _sys_result;                                                    \
>> +})
>> +
>> +#undef internal_syscall6
>> +#define internal_syscall6(ncs_init, cs_init, input, err, arg1, arg2, arg3, arg4, arg5, arg6) \
>> +({                                                                   \
>> +     long _sys_result;                                               \
>> +                                                                     \
>> +     {                                                               \
>> +     register long __v0 __asm__("$2") ncs_init;                      \
>> +     register long __a0 __asm__("$4") = (long) arg1;         \
>> +     register long __a1 __asm__("$5") = (long) arg2;         \
>> +     register long __a2 __asm__("$6") = (long) arg3;         \
>> +     register long __a3 __asm__("$7") = (long) arg4;         \
>> +     register long __a4 __asm__("$8") = (long) arg5;         \
>> +     register long __a5 __asm__("$9") = (long) arg6;         \
>> +     __asm__ __volatile__ (                                          \
>> +     ".set\tnoreorder\n\t"                                           \
>> +     cs_init                                                         \
>> +     "syscall\n\t"                                                   \
>> +     ".set\treorder"                                                 \
>> +     : "=r" (__v0), "+r" (__a3)                                      \
>> +     : input, "r" (__a0), "r" (__a1), "r" (__a2), "r" (__a4),        \
>> +       "r" (__a5)                                                    \
>> +     : __SYSCALL_CLOBBERS);                                          \
>> +     err = __a3;                                                     \
>> +     _sys_result = __v0;                                             \
>> +     }                                                               \
>> +     _sys_result;                                                    \
>> +})
>> +
>> +#define __SYSCALL_CLOBBERS "$1", "$3", "$10", "$11", "$12", "$13", \
>> +     "$14", "$15", "$24", "$25", "hi", "lo", "memory"
>> +
>> +#endif
>> +
>>  /* Pointer mangling is not yet supported for MIPS.  */
>>  #define PTR_MANGLE(var) (void) (var)
>>  #define PTR_DEMANGLE(var) (void) (var)
>> --
>> 1.7.10.4
>>
>> _______________________________________________
>> uClibc mailing list
>> uClibc@uclibc.org
>> http://lists.busybox.net/mailman/listinfo/uclibc
>>
> _______________________________________________
> uClibc mailing list
> uClibc@uclibc.org
> http://lists.busybox.net/mailman/listinfo/uclibc
Rich Felker Feb. 4, 2014, 5:38 a.m. UTC | #3
On Sat, Dec 21, 2013 at 01:18:29PM +0100, Waldemar Brodkorb wrote:
> +#undef internal_syscall5
> +#define internal_syscall5(ncs_init, cs_init, input, err, arg1, arg2, arg3, arg4, arg5) \
> +({ 									\
> +	long _sys_result;						\
> +									\
> +	{								\
> +	register long __v0 __asm__("$2") ncs_init;			\
> +	register long __a0 __asm__("$4") = (long) arg1; 	\
> +	register long __a1 __asm__("$5") = (long) arg2; 	\
> +	register long __a2 __asm__("$6") = (long) arg3; 	\
> +	register long __a3 __asm__("$7") = (long) arg4; 	\
> +	register long __a4 __asm__("$8") = (long) arg5; 	\
> +	__asm__ __volatile__ ( 						\
> +	".set\tnoreorder\n\t" 						\
> +	cs_init								\
> +	"syscall\n\t" 							\
> +	".set\treorder" 						\

I seem to recall MIPS assembler having push/pop directives for
settings like this. Wouldn't it be better to use them to restore the
old setting of reorder rather than forcing it on after asm, so as not
to assume the compiler had, and wants, reorder on?

Rich
diff mbox

Patch

diff --git a/libc/sysdeps/linux/mips/sysdep.h b/libc/sysdeps/linux/mips/sysdep.h
index 6dba1fb..46b6c53 100644
--- a/libc/sysdeps/linux/mips/sysdep.h
+++ b/libc/sysdeps/linux/mips/sysdep.h
@@ -279,6 +279,8 @@  L(syse1):
 	_sys_result;							\
 })
 
+#if _MIPS_SIM == _ABIO32 || _MIPS_SIM == _ABIN32
+
 /* We need to use a frame pointer for the functions in which we
    adjust $sp around the syscall, or debug information and unwind
    information will be $sp relative and thus wrong during the syscall.  As
@@ -382,6 +384,67 @@  L(syse1):
 #define __SYSCALL_CLOBBERS "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", \
 	"$14", "$15", "$24", "$25", "memory"
 
+#else /* N64 */
+
+#undef internal_syscall5
+#define internal_syscall5(ncs_init, cs_init, input, err, arg1, arg2, arg3, arg4, arg5) \
+({ 									\
+	long _sys_result;						\
+									\
+	{								\
+	register long __v0 __asm__("$2") ncs_init;			\
+	register long __a0 __asm__("$4") = (long) arg1; 	\
+	register long __a1 __asm__("$5") = (long) arg2; 	\
+	register long __a2 __asm__("$6") = (long) arg3; 	\
+	register long __a3 __asm__("$7") = (long) arg4; 	\
+	register long __a4 __asm__("$8") = (long) arg5; 	\
+	__asm__ __volatile__ ( 						\
+	".set\tnoreorder\n\t" 						\
+	cs_init								\
+	"syscall\n\t" 							\
+	".set\treorder" 						\
+	: "=r" (__v0), "+r" (__a3) 					\
+	: input, "r" (__a0), "r" (__a1), "r" (__a2), "r" (__a4)		\
+	: __SYSCALL_CLOBBERS); 						\
+	err = __a3;							\
+	_sys_result = __v0;						\
+	}								\
+	_sys_result;							\
+})
+
+#undef internal_syscall6
+#define internal_syscall6(ncs_init, cs_init, input, err, arg1, arg2, arg3, arg4, arg5, arg6) \
+({ 									\
+	long _sys_result;						\
+									\
+	{								\
+	register long __v0 __asm__("$2") ncs_init;			\
+	register long __a0 __asm__("$4") = (long) arg1; 	\
+	register long __a1 __asm__("$5") = (long) arg2; 	\
+	register long __a2 __asm__("$6") = (long) arg3; 	\
+	register long __a3 __asm__("$7") = (long) arg4; 	\
+	register long __a4 __asm__("$8") = (long) arg5; 	\
+	register long __a5 __asm__("$9") = (long) arg6; 	\
+	__asm__ __volatile__ ( 						\
+	".set\tnoreorder\n\t" 						\
+	cs_init								\
+	"syscall\n\t" 							\
+	".set\treorder" 						\
+	: "=r" (__v0), "+r" (__a3) 					\
+	: input, "r" (__a0), "r" (__a1), "r" (__a2), "r" (__a4),	\
+	  "r" (__a5)							\
+	: __SYSCALL_CLOBBERS); 						\
+	err = __a3;							\
+	_sys_result = __v0;						\
+	}								\
+	_sys_result;							\
+})
+
+#define __SYSCALL_CLOBBERS "$1", "$3", "$10", "$11", "$12", "$13", \
+	"$14", "$15", "$24", "$25", "hi", "lo", "memory"
+
+#endif
+
 /* Pointer mangling is not yet supported for MIPS.  */
 #define PTR_MANGLE(var) (void) (var)
 #define PTR_DEMANGLE(var) (void) (var)