1 | /* clone() implementation for ARC. |
2 | Copyright (C) 2020-2024 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | |
20 | #include <sysdep.h> |
21 | #define _ERRNO_H 1 |
22 | #include <bits/errno.h> |
23 | |
24 | /* int clone(int (*fn)(void *), void *child_stack, |
25 | int flags, void *arg, ... |
26 | < pid_t *ptid, struct user_desc *tls, pid_t *ctid > ); |
27 | |
28 | NOTE: I'm assuming that the last 3 args are NOT var-args and in case all |
29 | 3 are not relevant, caller will nevertheless pass those as NULL. |
30 | |
31 | clone syscall in kernel (ABI: CONFIG_CLONE_BACKWARDS) |
32 | |
33 | int sys_clone(unsigned long int clone_flags, |
34 | unsigned long int newsp, |
35 | int __user *parent_tidptr, |
36 | void *tls, |
37 | int __user *child_tidptr). */ |
38 | |
39 | ENTRY (__clone) |
40 | cmp r0, 0 /* @fn can't be NULL. */ |
41 | and r1,r1,-4 /* @child_stack be 4 bytes aligned per ABI. */ |
42 | cmp.ne r1, 0 /* @child_stack can't be NULL. */ |
43 | bz L (__sys_err) |
44 | |
45 | /* save some of the orig args |
46 | r0 containing @fn will be clobbered AFTER syscall (with ret val) |
47 | rest are clobbered BEFORE syscall due to different arg ordering. */ |
48 | mov r10, r0 /* @fn. */ |
49 | mov r11, r3 /* @args. */ |
50 | mov r12, r2 /* @clone_flags. */ |
51 | mov r9, r5 /* @tls. */ |
52 | |
53 | /* adjust libc args for syscall. */ |
54 | |
55 | mov r0, r2 /* libc @flags is 1st syscall arg. */ |
56 | mov r2, r4 /* libc @ptid. */ |
57 | mov r3, r5 /* libc @tls. */ |
58 | mov r4, r6 /* libc @ctid. */ |
59 | mov r8, __NR_clone |
60 | ARC_TRAP_INSN |
61 | |
62 | cmp r0, 0 /* return code : 0 new process, !0 parent. */ |
63 | beq thread_start_clone |
64 | blt L (__sys_err2) /* < 0 (signed) error. */ |
65 | j [blink] /* Parent returns. */ |
66 | |
67 | L (__sys_err): |
68 | mov r0, -EINVAL |
69 | L (__sys_err2): |
70 | /* (1) No need to make -ve kernel error code as positive errno |
71 | __syscall_error expects the -ve error code returned by kernel |
72 | (2) r0 still had orig -ve kernel error code |
73 | (3) Tail call to __syscall_error so we dont have to come back |
74 | here hence instead of jmp-n-link (reg push/pop) we do jmp |
75 | (4) No need to route __syscall_error via PLT, B is inherently |
76 | position independent. */ |
77 | b __syscall_error |
78 | PSEUDO_END (__clone) |
79 | |
80 | |
81 | .align 4 |
82 | .type thread_start_clone, %function |
83 | thread_start_clone: |
84 | cfi_startproc |
85 | /* Terminate call stack by noting ra is undefined. */ |
86 | cfi_undefined (blink) |
87 | |
88 | /* Child jumps off to @fn with @arg as argument. */ |
89 | jl.d [r10] |
90 | mov r0, r11 |
91 | |
92 | /* exit() with result from @fn (already in r0). */ |
93 | mov r8, __NR_exit |
94 | ARC_TRAP_INSN |
95 | |
96 | /* In case it ever came back. */ |
97 | flag 1 |
98 | |
99 | cfi_endproc |
100 | .size thread_start_clone, .-thread_start_clone |
101 | |
102 | libc_hidden_def (__clone) |
103 | weak_alias (__clone, clone) |
104 | |