arc: clone: Simplify CLONE_THREAD detection
[uclibc-ng.git] / libc / sysdeps / linux / arc / clone.S
blob3942b88f7a7ec64d7890bb85fa3eddcbe078830d
1 /*
2  * Copyright (C) 2013 Synopsys, Inc. (www.synopsys.com)
3  *
4  * Licensed under the LGPL v2.1 or later, see the file COPYING.LIB in this tarball.
5  */
7 #include <asm/errno.h>
8 #include <sys/syscall.h>
9 #include <sysdep.h>
11 ; Per man, libc clone( ) is as follows
13 ; int clone(int (*fn)(void *), void *child_stack,
14 ;           int flags, void *arg, ...
15 ;           /* pid_t *ptid, struct user_desc *tls, pid_t *ctid */);
17 ; NOTE: I'm assuming that the last 3 args are NOT var-args and in case all
18 ;       3 are not relevant, caller will nevertheless pass those as NULL.
19 ;       Current (Jul 2012) upstream powerpc/clone.S assumes similarly.
20 ;       Our LTP (from 2007) doesn't seem to have tests to prove otherwise
22 ; clone syscall in kernel (ABI: CONFIG_CLONE_BACKWARDS)
24 ; int sys_clone(unsigned long clone_flags,
25 ;               unsigned long newsp,
26 ;               int __user *parent_tidptr,
27 ;               void *tls,
28 ;               int __user *child_tidptr)
30 #define CLONE_SETTLS            0x00080000
32 ENTRY(clone)
33         cmp     r0, 0           ; @fn can't be NULL
34         cmp.ne  r1, 0           ; @child_stack can't be NULL
35         bz      .L__sys_err
37         ; save some of the orig args
38         ; r0 containg @fn will be clobbered AFTER syscall (with ret val)
39         ; rest are clobbered BEFORE syscall due to different arg ordering
40         mov     r10, r0         ; @fn
41         mov     r11, r3         ; @args
42         mov     r12, r2         ; @clone_flags
43         mov     r9,  r5         ; @tls
45         ; adjust libc args for syscall
47         mov     r0, r2          ; libc @flags is 1st syscall arg
48         mov     r2, r4          ; libc @ptid
49         mov     r3, r5          ; libc @tls
50         mov     r4, r6          ; libc @ctid
51         mov     r8, __NR_clone
52         ARC_TRAP_INSN
54         cmp     r0, 0           ; return code : 0 new process, !0 parent
55         blt     .L__sys_err2    ; < 0 (signed) error
56         jnz     [blink]         ; Parent returns
58         ; ----- child starts here ---------
60 #if defined(__UCLIBC_HAS_TLS__)
61         ; Setup TP register (since kernel doesn't do that)
62         and.f   0, r12, CLONE_SETTLS
63         bz      .Lnext_clone_quirk
64         SET_TP  r9
66 .Lnext_clone_quirk:
67 #ifdef RESET_PID
68         bbit1   r12, 16, .Lgo_thread    ; CLONE_THREAD = (1 << 16)
70         mov     r8, __NR_getpid
71         ARC_TRAP_INSN           ; r0 has PID
72         THREAD_SELF r1          ; Get to struct pthread (just before TCB)
73         st      r0, [r1, PTHREAD_PID]
74         st      r0, [r1, PTHREAD_TID]
76 .Lgo_thread:
77 #endif
78 #endif
79         ; child jumps off to @fn with @arg as argument, and returns here
80         jl.d    [r10]
81         mov     r0, r11
83         ; falls thru to _exit() with result from @fn (already in r0)
84         b       HIDDEN_JUMPTARGET(_exit)
86 .L__sys_err:
87         mov     r0, -EINVAL
88 .L__sys_err2:
89         ; (1) No need to make -ve kernel error code as positive errno
90         ;   __syscall_error expects the -ve error code returned by kernel
91         ; (2) r0 still had orig -ve kernel error code
92         ; (3) Tail call to __syscall_error so we dont have to come back
93         ;     here hence instead of jmp-n-link (reg push/pop) we do jmp
94         ; (4) No need to route __syscall_error via PLT, B is inherently
95         ;     position independent
96         b   __syscall_error
97 END(clone)
98 libc_hidden_def(clone)