mips: FIx clone3 implementation (BZ 31325)
[glibc.git] / sysdeps / unix / sysv / linux / mips / clone3.S
blob481b8ae96366fc7011e206a40d44dfd8dad62c63
1 /* The clone3 syscall wrapper.  Linux/mips version.
2    Copyright (C) 2023-2024 Free Software Foundation, Inc.
4    This file is part of the GNU C Library.
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, see
18    <https://www.gnu.org/licenses/>.  */
20 #include <sys/asm.h>
21 #include <sysdep.h>
22 #define _ERRNO_H        1
23 #include <bits/errno.h>
25 /* The userland implementation is:
26    int clone3 (struct clone_args *cl_args, size_t size,
27                int (*func)(void *arg), void *arg);
29    the kernel entry is:
30    int clone3 (struct clone_args *cl_args, size_t size);
32    The parameters are passed in registers from userland:
33    a0/$4: cl_args
34    a1/$5: size
35    a2/$6: func
36    a3/$7: arg  */
38         .text
39         .set            nomips16
40 #define FRAMESZ ((NARGSAVE*SZREG)+ALSZ)&ALMASK
41 GPOFF= FRAMESZ-(1*SZREG)
42 NESTED(__clone3, SZREG, sp)
43 #ifdef __PIC__
44         SETUP_GP
45 #endif
46 #if FRAMESZ
47         PTR_SUBU sp, FRAMESZ
48         cfi_adjust_cfa_offset (FRAMESZ)
49 #endif
50         SETUP_GP64_STACK (GPOFF, __clone3)
51 #ifdef __PIC__
52         SAVE_GP (GPOFF)
53 #endif
54 #ifdef PROF
55         .set    noat
56         move    $1,ra
57         jal     _mcount
58         .set    at
59 #endif
61         /* Sanity check args.  */
62         li      v0, EINVAL
63         beqz    a0, L(error)    /* No NULL cl_args pointer.  */
64         beqz    a2, L(error)    /* No NULL function pointer.  */
66 #if _MIPS_SIM == _ABIO32
67         /* Both stack and stack_size on clone_args are defined as uint64_t, and
68            there is no need to handle values larger than to 32 bits for o32.  */
69 # if __BYTE_ORDER == __BIG_ENDIAN
70 #  define CL_STACKPOINTER_OFFSET  44
71 #  define CL_STACKSIZE_OFFSET     52
72 # else
73 #  define CL_STACKPOINTER_OFFSET  40
74 #  define CL_STACKSIZE_OFFSET     48
75 # endif
77         /* For o32 we need to setup a minimal stack frame to allow cprestore
78            on __thread_start_clone3.  Also there is no guarantee by kABI that
79            $8 will be preserved after syscall execution (so we need to save it
80            on the provided stack).  */
81         lw      t0, CL_STACKPOINTER_OFFSET(a0)  /* Load the stack pointer.  */
82         lw      t1, CL_STACKSIZE_OFFSET(a0)     /* Load the stack_size.  */
83         addiu   t1, -32                         /* Update the stack size.  */
84         addu    t2, t1, t0                      /* Calculate the thread stack.  */
85         sw      a3, 0(t2)                       /* Save argument pointer.  */
86         sw      t1, CL_STACKSIZE_OFFSET(a0)     /* Save the new stack size.  */
87 #else
88         move    $8, a3          /* a3 is set to 0/1 for syscall success/error
89                                    while a4/$8 is returned unmodified.  */
90 #endif
92         /* Do the system call, the kernel expects:
93            v0: system call number
94            a0: cl_args
95            a1: size  */
96         li              v0, __NR_clone3
97         cfi_endproc
98         syscall
100         bnez            a3, L(error)
101         beqz            v0, L(thread_start_clone3)
103         /* Successful return from the parent */
104         cfi_startproc
105 #if FRAMESZ
106         cfi_adjust_cfa_offset (FRAMESZ)
107 #endif
108         SETUP_GP64_STACK_CFI (GPOFF)
109         cfi_remember_state
110         RESTORE_GP64_STACK
111 #if FRAMESZ
112         PTR_ADDU        sp, FRAMESZ
113         cfi_adjust_cfa_offset (-FRAMESZ)
114 #endif
115         ret
117 L(error):
118         cfi_restore_state
119 #ifdef __PIC__
120         PTR_LA          t9, __syscall_error
121         RESTORE_GP64_STACK
122         PTR_ADDU        sp, FRAMESZ
123         cfi_adjust_cfa_offset (-FRAMESZ)
124         jr              t9
125 #else
126         RESTORE_GP64_STACK
127         PTR_ADDU        sp, FRAMESZ
128         cfi_adjust_cfa_offset (-FRAMESZ)
129         j               __syscall_error
130 #endif
131 END (__clone3)
133 /* Load up the arguments to the function.  Put this block of code in
134    its own function so that we can terminate the stack trace with our
135    debug info.  */
137 ENTRY(__thread_start_clone3)
138 L(thread_start_clone3):
139         cfi_undefined ($31)
140         /* cp is already loaded.  */
141         SAVE_GP (GPOFF)
142         /* The stackframe has been created on entry of clone3.  */
144         /* Restore the arg for user's function.  */
145         move            t9, a2          /* Function pointer.  */
146 #if _MIPS_SIM == _ABIO32
147         PTR_L           a0, 0(sp)
148 #else
149         move            a0, $8          /* Argument pointer.  */
150 #endif
152         /* Call the user's function.  */
153         jal             t9
155         move            a0, v0
156         li              v0, __NR_exit
157         syscall
158 END(__thread_start_clone3)
160 libc_hidden_def (__clone3)
161 weak_alias (__clone3, clone3)