/* * Copyright (C) 2013 Synopsys, Inc. (www.synopsys.com) * * Licensed under the LGPL v2.1 or later, see the file COPYING.LIB in this tarball. */ #include <asm/errno.h> #include <sys/syscall.h> #include <sysdep.h> ; Per man, libc clone( ) is as follows ; ; int clone(int (*fn)(void *), void *child_stack, ; int flags, void *arg, ... ; /* pid_t *ptid, struct user_desc *tls, pid_t *ctid */); ; ; NOTE: I'm assuming that the last 3 args are NOT var-args and in case all ; 3 are not relevant, caller will nevertheless pass those as NULL. ; Current (Jul 2012) upstream powerpc/clone.S assumes similarly. ; Our LTP (from 2007) doesn't seem to have tests to prove otherwise ; clone syscall in kernel (ABI: CONFIG_CLONE_BACKWARDS) ; ; int sys_clone(unsigned long clone_flags, ; unsigned long newsp, ; int __user *parent_tidptr, ; void *tls, ; int __user *child_tidptr) #define CLONE_VM 0x00000100 #define CLONE_THREAD 0x00010000 #define CLONE_SETTLS 0x00080000 #define CLONE_THREAD_N_VM (CLONE_THREAD | CLONE_VM) ENTRY(clone) cmp r0, 0 ; @fn can't be NULL cmp.ne r1, 0 ; @child_stack can't be NULL bz .L__sys_err ; save some of the orig args ; r0 containg @fn will be clobbered AFTER syscall (with ret val) ; rest are clobbered BEFORE syscall due to different arg ordering mov r10, r0 ; @fn mov r11, r3 ; @args mov r12, r2 ; @clone_flags mov r9, r5 ; @tls ; adjust libc args for syscall mov r0, r2 ; libc @flags is 1st syscall arg mov r2, r4 ; libc @ptid mov r3, r5 ; libc @tls mov r4, r6 ; libc @ctid mov r8, __NR_clone ARC_TRAP_INSN cmp r0, 0 ; return code : 0 new process, !0 parent blt .L__sys_err2 ; < 0 (signed) error jnz [blink] ; Parent returns ; ----- child starts here --------- #if defined(__UCLIBC_HAS_TLS__) ; Setup TP register (since kernel doesn't do that) and.f 0, r12, CLONE_SETTLS bz .Lnext_clone_quirk SET_TP r9 .Lnext_clone_quirk: #ifdef RESET_PID mov_s r2, CLONE_THREAD_N_VM and_s r2, r2, r12 brne r2, r12, .Lgo_thread mov r8, __NR_clone ARC_TRAP_INSN ; r0 has PID THREAD_SELF r1 ; Get to struct pthread (just before TCB) st r0, [r1, PTHREAD_PID] st r0, [r1, PTHREAD_TID] .Lgo_thread: #endif #endif ; child jumps off to @fn with @arg as argument, and returns here jl.d [r10] mov r0, r11 ; falls thru to _exit() with result from @fn (already in r0) b HIDDEN_JUMPTARGET(_exit) .L__sys_err: mov r0, -EINVAL .L__sys_err2: ; (1) No need to make -ve kernel error code as positive errno ; __syscall_error expects the -ve error code returned by kernel ; (2) r0 still had orig -ve kernel error code ; (3) Tail call to __syscall_error so we dont have to come back ; here hence instead of jmp-n-link (reg push/pop) we do jmp ; (4) No need to route __syscall_error via PLT, B is inherently ; position independent b __syscall_error END(clone) libc_hidden_def(clone)