Browse Source

or1k: add clone() from old GNU libc implementation

Waldemar Brodkorb 7 years ago
parent
commit
69ea4c1f65

+ 1 - 1
libc/sysdeps/linux/or1k/Makefile.arch

@@ -6,4 +6,4 @@
 #
 
 CSRC-y := __syscall_error.c __init_brk.c brk.c sbrk.c clone.c
-SSRC-y := __longjmp.S setjmp.S
+SSRC-y := __longjmp.S setjmp.S or1k_clone.S

+ 49 - 78
libc/sysdeps/linux/or1k/clone.c

@@ -1,88 +1,59 @@
-/*
- * clone syscall for OpenRISC
- *
- *  Copyright (c) 2010     Jonas Bonn <jonas@southpole.se>
- *  Copyright (C) 2003     John Williams <jwilliams@itee.uq.edu.au>
- *  Copyright (C) 2002,03  NEC Electronics Corporation
- *  Copyright (C) 2002,03  Miles Bader <miles@gnu.org>
- *
- * This file is subject to the terms and conditions of the GNU Lesser
- * General Public License.  See the file COPYING.LIB in the main
- * directory of this archive for more details.
- *
- * OpenRISC port by Jonas Bonn <jonas@southpole.se>
- */
+/* Copyright (C) 1998, 2002, 2003, 2004 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
 
-#include <errno.h>
-#include <sys/syscall.h>
-#include <sched.h>
-#include <unistd.h>
-
-/* The userland implementation is:
-   int clone (int (*fn)(void *arg), void *child_stack, int flags, void *arg, ...)
-   the kernel entry is:
-   int clone (long flags, void *child_stack)
-*/
-
-int
-clone (int (*fn)(void *arg), void *child_stack, int flags, void *arg, ...)
-{
-        int err;
-
-	/* OK, here's the skinny on this one...
-	 * OR1K GCC does weird things with varargs functions... the last
-	 * parameter is NEVER passed on the stack -- i.e. arg, in this case.
-	 * So we need to push at least 'arg' onto the child stack so that
-	 * the new thread can find it.  Just to be totally safe, we'll
-	 * push both 'fn' and 'arg'; that way we don't need to care what
-	 * GCC does with parameters, whether they are passed in registers
-	 * or on stack.
-	 */
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
 
-	/* Put 'fn' and 'arg' on child stack */
-	__asm__ __volatile__ (
-		"l.sw  -4(%0),%1;"
-		"l.sw  -8(%0),%2;"
-		:
-		: "r" (child_stack), "r" (fn), "r" (arg)
-		);
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
 
-        /* Sanity check the arguments */
-        err = -EINVAL;
-        if (!fn)
-                goto syscall_error;
-        if (!child_stack)
-                goto syscall_error;
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
 
-	err = INLINE_SYSCALL(clone, 2, flags, child_stack);
-
-	/* NB: from here you are in child thread or parent thread.
-	 *
-	 * Do not use any functions here that may write data _up_
-	 * onto the stack because they will overwrite the child's
-	 * thread descriptor... i.e. don't use printf
-	 */
-
-        if (err < 0)
-                goto syscall_error;
-        else if (err != 0) {
-                return err;
-	}
+#include <stdarg.h>
+#include <sysdep.h>
+#include <unistd.h>
 
-	/* NB: from here you exclusively in child thread */
+extern int __or1k_clone (int (*fn)(void *), void *child_stack,
+			 int flags, void *arg, pid_t *ptid,
+			 void *tls, pid_t *ctid);
 
-	/* Grab 'fn' and 'arg' from child stack */
-	__asm__ __volatile__ (
-		"l.lwz  %0,-4(%2);"
-		"l.lwz  %1,-8(%2);"
-		: "=&r" (fn), "=r" (arg)
-		: "r" (child_stack)
-		: "0", "1"
-		);
 
-	_exit(fn(arg));
+/* or1k ABI uses stack for varargs, syscall uses registers.
+ * This function moves from varargs to regs. */
+int
+__clone (int (*fn)(void *), void *child_stack,
+	 int flags, void *arg, ...
+	 /* pid_t *ptid, struct user_desc *tls, pid_t *ctid */ )
+{
+  void *ptid;
+  void *tls;
+  void *ctid;
+  va_list ap;
+  int err;
+
+  va_start (ap, arg);
+  ptid = va_arg (ap, void *);
+  tls = va_arg (ap, void *);
+  ctid = va_arg (ap, void *);
+  va_end (ap);
+
+  /* Sanity check the arguments */
+  err = -EINVAL;
+  if (!fn)
+    goto syscall_error;
+  if (!child_stack)
+    goto syscall_error;
+
+  return __or1k_clone (fn, child_stack, flags, arg, ptid, tls, ctid);
 
 syscall_error:
-        __set_errno (-err);
-        return -1;
+  __set_errno (-err);
+  return -1;
 }
+weak_alias (__clone, clone)

+ 77 - 0
libc/sysdeps/linux/or1k/or1k_clone.S

@@ -0,0 +1,77 @@
+#include <sysdep.h>
+
+#define CLONE_VM      0x00000100
+#define CLONE_THREAD  0x00010000
+
+.text
+ENTRY(__or1k_clone)
+
+	/* To handle GCC varargs we need to use our __clone wrapper to pop
+	   everything from the stack for us.
+	   Now everything is placed in the registers which saves us a lot 
+	   of trouble.
+
+	   The userland implementation is:
+		  int clone (int (*fn)(void *), void *child_stack,
+			    int flags, void *arg, pid_t *ptid,
+			    struct user_desc *tls, pid_t *ctid);
+
+	     The kernel entry is:
+		  int clone (long flags, void *child_stack, int *parent_tid,
+			  int *child_tid, struct void *tls)
+	     NB: tls isn't really an argument, it is read from r7 directly.
+	*/
+
+	/* Put 'fn', 'arg' and 'flags' on child stack */
+	l.addi r4, r4, -12
+	l.sw  8(r4), r3
+	l.sw  4(r4), r6
+	l.sw  0(r4), r5
+
+	l.ori r3, r5, 0
+	/* child_stack is already in r4 */
+	l.ori r5, r7, 0
+	l.lwz r6, 0(r1)
+	l.ori r7, r8, 0
+
+	DO_CALL (clone)
+
+	l.sfgeui r11, 0xf001
+	l.bf L(error)
+	 l.nop
+
+	/* If we are not the child, return the pid */
+	l.sfeqi r11, 0
+	l.bf L(child)
+	 l.nop
+
+	l.jr r9
+	 l.nop
+
+L(child):
+	/* Load flags */
+	l.lwz r3, 0(r1)
+
+	/* Update PID, but only if we do not share the same PID
+	   as our parent */
+	l.srli  r4, r3, 16
+	l.andi r4, r4, hi(CLONE_THREAD)
+	l.sfnei r4, 0
+	l.bf L(oldpid)
+	 l.nop
+
+L(oldpid):
+	/* Load function from stack */
+	l.lwz r11, 8(r1)
+	l.jalr r11
+	 l.lwz r3, 4(r1)
+
+	/* Exit the child thread */
+	l.jal HIDDEN_JUMPTARGET(_exit)
+	 l.ori r3, r11, 0
+
+L(error):
+	l.j SYSCALL_ERROR_NAME
+	 l.ori r3,r11,0
+
+PSEUDO_END (__or1k_clone)

+ 104 - 0
libc/sysdeps/linux/or1k/sysdep.h

@@ -0,0 +1,104 @@
+/* Copyright (C) 2011-2014 Free Software Foundation, Inc.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with the GNU C Library; if not, see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef _LINUX_OR1K_SYSDEP_H
+#define _LINUX_OR1K_SYSDEP_H 1
+
+#include <common/sysdep.h>
+#include <sys/syscall.h>
+
+/* In order to get __set_errno() definition in INLINE_SYSCALL.  */
+#ifndef __ASSEMBLER__
+#include <errno.h>
+#endif
+
+#undef SYS_ify
+#define SYS_ify(syscall_name)	(__NR_##syscall_name)
+
+#ifdef __ASSEMBLER__
+
+/* Local label name for asm code. */
+#define L(name)         .L##name
+
+#undef ret_ERRVAL
+#define ret_ERRVAL   l.jr r9; l.nop
+#define ret_NOERRNO  l.jr r9; l.nop
+
+#undef DO_CALL
+#define DO_CALL(syscall_name) \
+    l.addi r11, r0, SYS_ify (syscall_name); \
+    l.sys 1; \
+    l.nop
+
+#define PSEUDO(name, syscall_name, args) \
+  ENTRY (name); \
+  DO_CALL(syscall_name); \
+  /* if -4096 < ret < 0 holds, it's an error */ \
+  l.sfgeui r11, 0xf001; \
+  l.bf L(pseudo_end); \
+   l.nop
+
+#define PSEUDO_NOERRNO(name, syscall_name, args)  \
+  ENTRY (name);           \
+  DO_CALL(syscall_name)
+
+#define PSEUDO_END(name) \
+L(pseudo_end): \
+  l.j SYSCALL_ERROR_NAME; \
+  l.ori r3,r11,0; \
+  END (name)
+
+#define PSEUDO_END_NOERRNO(name) \
+  END (name)
+
+#ifndef PIC
+/* For static code, on error jump to __syscall_error directly. */
+# define SYSCALL_ERROR_NAME __syscall_error
+#elif NOT_IN_libc
+/* Use the internal name for libc/libpthread shared objects. */
+# define SYSCALL_ERROR_NAME __GI___syscall_error
+#else
+/* Otherwise, on error do a full PLT jump. */
+# define SYSCALL_ERROR_NAME plt(__syscall_error)
+#endif
+
+/* Make use of .size directive.  */
+#define ASM_SIZE_DIRECTIVE(name) .size name,.-name;
+
+/* Define an entry point visible from C.  */
+#define ENTRY(name)                                                           \
+  .globl C_SYMBOL_NAME(name);                                                 \
+  .type C_SYMBOL_NAME(name),@function;                                        \
+  .align 4;                                                                   \
+  C_LABEL(name)                                                               \
+  cfi_startproc;                                                              \
+
+#undef  END
+#define END(name)                                                             \
+  cfi_endproc;                                                                \
+  ASM_SIZE_DIRECTIVE(name)
+
+/* Since C identifiers are not normally prefixed with an underscore
+   on this system, the asm identifier `syscall_error' intrudes on the
+   C name space.  Make sure we use an innocuous name.  */
+#define syscall_error   __syscall_error
+
+/* Specify the size in bytes of a machine register.  */
+#define REGSIZE         4
+
+#endif	/* __ASSEMBLER__ */
+
+#endif /* linux/or1k/sysdep.h */