[v3,19/19] linux-user: Split out some process syscalls

Message ID 20180612005145.3375-20-richard.henderson@linaro.org
State New
Headers show
Series
  • linux-user: Split do_syscall
Related show

Commit Message

Richard Henderson June 12, 2018, 12:51 a.m.
This includes clone, getgroups, gettid, setfsgid, setfsuid,
setgroups, setsid, setuid, fork, getegid, getegid32, geteuid,
geteuid32, getgid, getgid32, getgroups32, getpgrp, getpid,
getppid, getresgid, getresgid32, getresuid, getresuid32,
getuid, getuid32, getxgid, getxpid, getxuid, setfsgid32,
setgsuid32, setgid32, setgroups32, setregid, setregid32,
setresgid, setresgid32, setresuid, setresuid32, setreuid,
setreuid32, setuid32, vfork.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>

---
 linux-user/syscall.h           | 108 ++++
 linux-user/strace.c            |  36 +-
 linux-user/syscall.c           | 799 ----------------------------
 linux-user/syscall_proc.c      | 914 +++++++++++++++++++++++++++++++++
 linux-user/Makefile.objs       |   5 +-
 linux-user/gen_syscall_list.py |  43 ++
 linux-user/strace.list         | 144 ------
 7 files changed, 1072 insertions(+), 977 deletions(-)
 create mode 100644 linux-user/syscall_proc.c

-- 
2.17.1

Patch

diff --git a/linux-user/syscall.h b/linux-user/syscall.h
index 0bbbf6f7b0..de99ae5a3b 100644
--- a/linux-user/syscall.h
+++ b/linux-user/syscall.h
@@ -53,6 +53,7 @@  typedef enum {
     /* These print as sets of flags.  */
     ARG_ATDIRFD,
     ARG_ATFLAG,
+    ARG_CLONEFLAG,
     ARG_MMAPFLAG,
     ARG_MMAPPROT,
     ARG_MODEFLAG,
@@ -221,6 +222,61 @@  static inline uint64_t target_offset64(abi_ulong word0, abi_ulong word1)
 #endif
 }
 
+#ifdef USE_UID16
+static inline int high2lowuid(int uid)
+{
+    return uid > 65535 ? 65534 : uid;
+}
+
+static inline int high2lowgid(int gid)
+{
+    return gid > 65535 ? 65534 : gid;
+}
+
+static inline int low2highuid(int uid)
+{
+    return (int16_t)uid == -1 ? -1 : uid;
+}
+
+static inline int low2highgid(int gid)
+{
+    return (int16_t)gid == -1 ? -1 : gid;
+}
+static inline int tswapid(int id)
+{
+    return tswap16(id);
+}
+
+#define put_user_id(x, gaddr) put_user_u16(x, gaddr)
+#else /* !USE_UID16 */
+static inline int high2lowuid(int uid)
+{
+    return uid;
+}
+
+static inline int high2lowgid(int gid)
+{
+    return gid;
+}
+
+static inline int low2highuid(int uid)
+{
+    return uid;
+}
+
+static inline int low2highgid(int gid)
+{
+    return gid;
+}
+
+static inline int tswapid(int id)
+{
+    return tswap32(id);
+}
+
+#define put_user_id(x, gaddr) put_user_u32(x, gaddr)
+#endif /* USE_UID16 */
+
 /* Temporary declarations from syscall_foo.c back to main syscall.c.
  * These indicate incomplete conversion.
  */
@@ -277,5 +333,57 @@  static type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
     return safe_syscall(__NR_##name, arg1, arg2, arg3, arg4, arg5, arg6); \
 }
 
+/* Declarators for non-interruptable system calls.  */
+
+#undef _syscall0
+#undef _syscall1
+#undef _syscall2
+#undef _syscall3
+#undef _syscall4
+#undef _syscall5
+#undef _syscall6
+
+#define _syscall0(type, name)                   \
+    static type name(void)                      \
+    {                                           \
+        return syscall(__NR_##name);            \
+    }
+
+#define _syscall1(T0, name, T1, A1)             \
+    static T0 name(T1 A1)                       \
+    {                                           \
+        return syscall(__NR_##name, A1);        \
+    }
+
+#define _syscall2(T0, name, T1, A1, T2, A2)     \
+    static T0 name(T1 A1, T2 A2)                \
+    {                                           \
+        return syscall(__NR_##name, A1, A2);    \
+    }
+
+#define _syscall3(T0, name, T1, A1, T2, A2, T3, A3)     \
+    static T0 name(T1 A1, T2 A2, T3 A3)                 \
+    {                                                   \
+        return syscall(__NR_##name, A1, A2, A3);        \
+    }
+
+#define _syscall4(T0, name, T1, A1, T2, A2, T3, A3, T4, A4)     \
+    static T0 name(T1 A1, T2 A2, T3 A3, T4 A4)                  \
+    {                                                           \
+        return syscall(__NR_##name, A1, A2, A3, A4);            \
+    }
+
+#define _syscall5(T0, name, T1, A1, T2, A2, T3, A3, T4, A4, T5, A5)     \
+    static T0 name(T1 A1, T2 A2, T3 A3, T4 A4, T5 A5)                   \
+    {                                                                   \
+        return syscall(__NR_##name, A1, A2, A3, A4, A5);                \
+    }
+
+#define _syscall6(T0, name, T1, A1, T2, A2, T3, A3, T4, A4, T5, A5, T6, A6) \
+    static T0 name(T1 A1, T2 A2, T3 A3, T4 A4, T5 A5, T6 A6)            \
+    {                                                                   \
+        return syscall(__NR_##name, A1, A2, A3, A4, A5, A6);            \
+    }
+
 /* Include declarations of syscall definitions.  */
 #include "syscall_list.h"
diff --git a/linux-user/strace.c b/linux-user/strace.c
index 6ebd261a84..8b0104afce 100644
--- a/linux-user/strace.c
+++ b/linux-user/strace.c
@@ -836,7 +836,7 @@  static struct flags const mmap_flags[] = {
     FLAG_END,
 };
 
-UNUSED static struct flags clone_flags[] = {
+static struct flags const clone_flags[] = {
     FLAG_GENERIC(CLONE_VM),
     FLAG_GENERIC(CLONE_FS),
     FLAG_GENERIC(CLONE_FILES),
@@ -1218,37 +1218,6 @@  print_clock_adjtime(const struct syscallname *name,
 }
 #endif
 
-#ifdef TARGET_NR_clone
-static void do_print_clone(unsigned int flags, abi_ulong newsp,
-                           abi_ulong parent_tidptr, target_ulong newtls,
-                           abi_ulong child_tidptr)
-{
-    print_flags(clone_flags, flags, 0);
-    print_raw_param("child_stack=0x" TARGET_ABI_FMT_lx, newsp, 0);
-    print_raw_param("parent_tidptr=0x" TARGET_ABI_FMT_lx, parent_tidptr, 0);
-    print_raw_param("tls=0x" TARGET_ABI_FMT_lx, newtls, 0);
-    print_raw_param("child_tidptr=0x" TARGET_ABI_FMT_lx, child_tidptr, 1);
-}
-
-static void
-print_clone(const struct syscallname *name,
-    abi_long arg1, abi_long arg2, abi_long arg3,
-    abi_long arg4, abi_long arg5, abi_long arg6)
-{
-    print_syscall_prologue(name);
-#if defined(TARGET_MICROBLAZE)
-    do_print_clone(arg1, arg2, arg4, arg6, arg5);
-#elif defined(TARGET_CLONE_BACKWARDS)
-    do_print_clone(arg1, arg2, arg3, arg4, arg5);
-#elif defined(TARGET_CLONE_BACKWARDS2)
-    do_print_clone(arg2, arg1, arg3, arg5, arg4);
-#else
-    do_print_clone(arg1, arg2, arg3, arg5, arg4);
-#endif
-    print_syscall_epilogue(name);
-}
-#endif
-
 #ifdef TARGET_NR_creat
 static void
 print_creat(const struct syscallname *name,
@@ -2568,6 +2537,9 @@  static void print_syscall_def1(const SyscallDef *def, int64_t args[6])
         case ARG_ATFLAG:
             len = add_flags(b, rest, at_file_flags, arg, false);
             break;
+        case ARG_CLONEFLAG:
+            len = add_flags(b, rest, clone_flags, arg, false);
+            break;
         case ARG_MMAPFLAG:
             len = add_flags(b, rest, mmap_flags, arg, false);
             break;
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 67fbf7f674..58754c9674 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -22,11 +22,9 @@ 
 #include "qemu/path.h"
 #include <elf.h>
 #include <endian.h>
-#include <grp.h>
 #include <sys/wait.h>
 #include <sys/mount.h>
 #include <sys/file.h>
-#include <sys/fsuid.h>
 #include <sys/personality.h>
 #include <sys/prctl.h>
 #include <sys/resource.h>
@@ -109,57 +107,6 @@ 
 #include "qemu.h"
 #include "syscall.h"
 
-#ifndef CLONE_IO
-#define CLONE_IO                0x80000000      /* Clone io context */
-#endif
-
-/* We can't directly call the host clone syscall, because this will
- * badly confuse libc (breaking mutexes, for example). So we must
- * divide clone flags into:
- *  * flag combinations that look like pthread_create()
- *  * flag combinations that look like fork()
- *  * flags we can implement within QEMU itself
- *  * flags we can't support and will return an error for
- */
-/* For thread creation, all these flags must be present; for
- * fork, none must be present.
- */
-#define CLONE_THREAD_FLAGS                              \
-    (CLONE_VM | CLONE_FS | CLONE_FILES |                \
-     CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM)
-
-/* These flags are ignored:
- * CLONE_DETACHED is now ignored by the kernel;
- * CLONE_IO is just an optimisation hint to the I/O scheduler
- */
-#define CLONE_IGNORED_FLAGS                     \
-    (CLONE_DETACHED | CLONE_IO)
-
-/* Flags for fork which we can implement within QEMU itself */
-#define CLONE_OPTIONAL_FORK_FLAGS               \
-    (CLONE_SETTLS | CLONE_PARENT_SETTID |       \
-     CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID)
-
-/* Flags for thread creation which we can implement within QEMU itself */
-#define CLONE_OPTIONAL_THREAD_FLAGS                             \
-    (CLONE_SETTLS | CLONE_PARENT_SETTID |                       \
-     CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID | CLONE_PARENT)
-
-#define CLONE_INVALID_FORK_FLAGS                                        \
-    (~(CSIGNAL | CLONE_OPTIONAL_FORK_FLAGS | CLONE_IGNORED_FLAGS))
-
-#define CLONE_INVALID_THREAD_FLAGS                                      \
-    (~(CSIGNAL | CLONE_THREAD_FLAGS | CLONE_OPTIONAL_THREAD_FLAGS |     \
-       CLONE_IGNORED_FLAGS))
-
-/* CLONE_VFORK is special cased early in do_fork(). The other flag bits
- * have almost all been allocated. We cannot support any of
- * CLONE_NEWNS, CLONE_NEWCGROUP, CLONE_NEWUTS, CLONE_NEWIPC,
- * CLONE_NEWUSER, CLONE_NEWPID, CLONE_NEWNET, CLONE_PTRACE, CLONE_UNTRACED.
- * The checks against the invalid thread masks above will catch these.
- * (The one remaining unallocated bit is 0x1000 which used to be CLONE_PID.)
- */
-
 /* Define DEBUG_ERESTARTSYS to force every syscall to be restarted
  * once. This exercises the codepaths for restart.
  */
@@ -169,61 +116,6 @@ 
 #define	VFAT_IOCTL_READDIR_BOTH		_IOR('r', 1, struct linux_dirent [2])
 #define	VFAT_IOCTL_READDIR_SHORT	_IOR('r', 2, struct linux_dirent [2])
 
-#undef _syscall0
-#undef _syscall1
-#undef _syscall2
-#undef _syscall3
-#undef _syscall4
-#undef _syscall5
-#undef _syscall6
-
-#define _syscall0(type,name)		\
-static type name (void)			\
-{					\
-	return syscall(__NR_##name);	\
-}
-
-#define _syscall1(type,name,type1,arg1)		\
-static type name (type1 arg1)			\
-{						\
-	return syscall(__NR_##name, arg1);	\
-}
-
-#define _syscall2(type,name,type1,arg1,type2,arg2)	\
-static type name (type1 arg1,type2 arg2)		\
-{							\
-	return syscall(__NR_##name, arg1, arg2);	\
-}
-
-#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3)	\
-static type name (type1 arg1,type2 arg2,type3 arg3)		\
-{								\
-	return syscall(__NR_##name, arg1, arg2, arg3);		\
-}
-
-#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4)	\
-static type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4)			\
-{										\
-	return syscall(__NR_##name, arg1, arg2, arg3, arg4);			\
-}
-
-#define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,	\
-		  type5,arg5)							\
-static type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5)	\
-{										\
-	return syscall(__NR_##name, arg1, arg2, arg3, arg4, arg5);		\
-}
-
-
-#define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,	\
-		  type5,arg5,type6,arg6)					\
-static type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5,	\
-                  type6 arg6)							\
-{										\
-	return syscall(__NR_##name, arg1, arg2, arg3, arg4, arg5, arg6);	\
-}
-
-
 #define __NR_sys_uname __NR_uname
 #define __NR_sys_getcwd1 __NR_getcwd
 #define __NR_sys_getdents __NR_getdents
@@ -246,16 +138,6 @@  static type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5,	\
 #define TARGET_NR__llseek TARGET_NR_llseek
 #endif
 
-#ifdef __NR_gettid
-_syscall0(int, gettid)
-#else
-/* This is a replacement for the host gettid() and must return a host
-   errno. */
-static int gettid(void) {
-    return -ENOSYS;
-}
-#endif
-
 /* For the 64-bit guest on 32-bit host case we must emulate
  * getdents using getdents64, because otherwise the host
  * might hand us back more dirent records than we can fit
@@ -285,9 +167,6 @@  _syscall3(int,sys_syslog,int,type,char*,bufp,int,len)
 #ifdef __NR_exit_group
 _syscall1(int,exit_group,int,error_code)
 #endif
-#if defined(TARGET_NR_set_tid_address) && defined(__NR_set_tid_address)
-_syscall1(int,set_tid_address,int *,tidptr)
-#endif
 #if defined(TARGET_NR_futex) && defined(__NR_futex)
 _syscall6(int,sys_futex,int *,uaddr,int,op,int,val,
           const struct timespec *,timeout,int *,uaddr2,int,val3)
@@ -5123,53 +5002,6 @@  install:
     lp[1] = tswap32(entry_2);
     return 0;
 }
-
-static abi_long do_get_thread_area(CPUX86State *env, abi_ulong ptr)
-{
-    struct target_modify_ldt_ldt_s *target_ldt_info;
-    uint64_t *gdt_table = g2h(env->gdt.base);
-    uint32_t base_addr, limit, flags;
-    int seg_32bit, contents, read_exec_only, limit_in_pages, idx;
-    int seg_not_present, useable, lm;
-    uint32_t *lp, entry_1, entry_2;
-
-    lock_user_struct(VERIFY_WRITE, target_ldt_info, ptr, 1);
-    if (!target_ldt_info)
-        return -TARGET_EFAULT;
-    idx = tswap32(target_ldt_info->entry_number);
-    if (idx < TARGET_GDT_ENTRY_TLS_MIN ||
-        idx > TARGET_GDT_ENTRY_TLS_MAX) {
-        unlock_user_struct(target_ldt_info, ptr, 1);
-        return -TARGET_EINVAL;
-    }
-    lp = (uint32_t *)(gdt_table + idx);
-    entry_1 = tswap32(lp[0]);
-    entry_2 = tswap32(lp[1]);
-    
-    read_exec_only = ((entry_2 >> 9) & 1) ^ 1;
-    contents = (entry_2 >> 10) & 3;
-    seg_not_present = ((entry_2 >> 15) & 1) ^ 1;
-    seg_32bit = (entry_2 >> 22) & 1;
-    limit_in_pages = (entry_2 >> 23) & 1;
-    useable = (entry_2 >> 20) & 1;
-#ifdef TARGET_ABI32
-    lm = 0;
-#else
-    lm = (entry_2 >> 21) & 1;
-#endif
-    flags = (seg_32bit << 0) | (contents << 1) |
-        (read_exec_only << 3) | (limit_in_pages << 4) |
-        (seg_not_present << 5) | (useable << 6) | (lm << 7);
-    limit = (entry_1 & 0xffff) | (entry_2  & 0xf0000);
-    base_addr = (entry_1 >> 16) | 
-        (entry_2 & 0xff000000) | 
-        ((entry_2 & 0xff) << 16);
-    target_ldt_info->base_addr = tswapal(base_addr);
-    target_ldt_info->limit = tswap32(limit);
-    target_ldt_info->flags = tswap32(flags);
-    unlock_user_struct(target_ldt_info, ptr, 1);
-    return 0;
-}
 #endif /* TARGET_I386 && TARGET_ABI32 */
 
 #ifndef TARGET_ABI32
@@ -5209,194 +5041,6 @@  abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr)
 
 #endif /* defined(TARGET_I386) */
 
-#define NEW_STACK_SIZE 0x40000
-
-
-static pthread_mutex_t clone_lock = PTHREAD_MUTEX_INITIALIZER;
-typedef struct {
-    CPUArchState *env;
-    pthread_mutex_t mutex;
-    pthread_cond_t cond;
-    pthread_t thread;
-    uint32_t tid;
-    abi_ulong child_tidptr;
-    abi_ulong parent_tidptr;
-    sigset_t sigmask;
-} new_thread_info;
-
-static void *clone_func(void *arg)
-{
-    new_thread_info *info = arg;
-    CPUArchState *env;
-    CPUState *cpu;
-    TaskState *ts;
-
-    rcu_register_thread();
-    tcg_register_thread();
-    env = info->env;
-    cpu = ENV_GET_CPU(env);
-    thread_cpu = cpu;
-    ts = (TaskState *)cpu->opaque;
-    info->tid = gettid();
-    task_settid(ts);
-    if (info->child_tidptr)
-        put_user_u32(info->tid, info->child_tidptr);
-    if (info->parent_tidptr)
-        put_user_u32(info->tid, info->parent_tidptr);
-    /* Enable signals.  */
-    sigprocmask(SIG_SETMASK, &info->sigmask, NULL);
-    /* Signal to the parent that we're ready.  */
-    pthread_mutex_lock(&info->mutex);
-    pthread_cond_broadcast(&info->cond);
-    pthread_mutex_unlock(&info->mutex);
-    /* Wait until the parent has finished initializing the tls state.  */
-    pthread_mutex_lock(&clone_lock);
-    pthread_mutex_unlock(&clone_lock);
-    cpu_loop(env);
-    /* never exits */
-    return NULL;
-}
-
-/* do_fork() Must return host values and target errnos (unlike most
-   do_*() functions). */
-static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp,
-                   abi_ulong parent_tidptr, target_ulong newtls,
-                   abi_ulong child_tidptr)
-{
-    CPUState *cpu = ENV_GET_CPU(env);
-    int ret;
-    TaskState *ts;
-    CPUState *new_cpu;
-    CPUArchState *new_env;
-    sigset_t sigmask;
-
-    flags &= ~CLONE_IGNORED_FLAGS;
-
-    /* Emulate vfork() with fork() */
-    if (flags & CLONE_VFORK)
-        flags &= ~(CLONE_VFORK | CLONE_VM);
-
-    if (flags & CLONE_VM) {
-        TaskState *parent_ts = (TaskState *)cpu->opaque;
-        new_thread_info info;
-        pthread_attr_t attr;
-
-        if (((flags & CLONE_THREAD_FLAGS) != CLONE_THREAD_FLAGS) ||
-            (flags & CLONE_INVALID_THREAD_FLAGS)) {
-            return -TARGET_EINVAL;
-        }
-
-        ts = g_new0(TaskState, 1);
-        init_task_state(ts);
-
-        /* Grab a mutex so that thread setup appears atomic.  */
-        pthread_mutex_lock(&clone_lock);
-
-        /* we create a new CPU instance. */
-        new_env = cpu_copy(env);
-        /* Init regs that differ from the parent.  */
-        cpu_clone_regs(new_env, newsp);
-        new_cpu = ENV_GET_CPU(new_env);
-        new_cpu->opaque = ts;
-        ts->bprm = parent_ts->bprm;
-        ts->info = parent_ts->info;
-        ts->signal_mask = parent_ts->signal_mask;
-
-        if (flags & CLONE_CHILD_CLEARTID) {
-            ts->child_tidptr = child_tidptr;
-        }
-
-        if (flags & CLONE_SETTLS) {
-            cpu_set_tls (new_env, newtls);
-        }
-
-        memset(&info, 0, sizeof(info));
-        pthread_mutex_init(&info.mutex, NULL);
-        pthread_mutex_lock(&info.mutex);
-        pthread_cond_init(&info.cond, NULL);
-        info.env = new_env;
-        if (flags & CLONE_CHILD_SETTID) {
-            info.child_tidptr = child_tidptr;
-        }
-        if (flags & CLONE_PARENT_SETTID) {
-            info.parent_tidptr = parent_tidptr;
-        }
-
-        ret = pthread_attr_init(&attr);
-        ret = pthread_attr_setstacksize(&attr, NEW_STACK_SIZE);
-        ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-        /* It is not safe to deliver signals until the child has finished
-           initializing, so temporarily block all signals.  */
-        sigfillset(&sigmask);
-        sigprocmask(SIG_BLOCK, &sigmask, &info.sigmask);
-
-        /* If this is our first additional thread, we need to ensure we
-         * generate code for parallel execution and flush old translations.
-         */
-        if (!parallel_cpus) {
-            parallel_cpus = true;
-            tb_flush(cpu);
-        }
-
-        ret = pthread_create(&info.thread, &attr, clone_func, &info);
-        /* TODO: Free new CPU state if thread creation failed.  */
-
-        sigprocmask(SIG_SETMASK, &info.sigmask, NULL);
-        pthread_attr_destroy(&attr);
-        if (ret == 0) {
-            /* Wait for the child to initialize.  */
-            pthread_cond_wait(&info.cond, &info.mutex);
-            ret = info.tid;
-        } else {
-            ret = -1;
-        }
-        pthread_mutex_unlock(&info.mutex);
-        pthread_cond_destroy(&info.cond);
-        pthread_mutex_destroy(&info.mutex);
-        pthread_mutex_unlock(&clone_lock);
-    } else {
-        /* if no CLONE_VM, we consider it is a fork */
-        if (flags & CLONE_INVALID_FORK_FLAGS) {
-            return -TARGET_EINVAL;
-        }
-
-        /* We can't support custom termination signals */
-        if ((flags & CSIGNAL) != TARGET_SIGCHLD) {
-            return -TARGET_EINVAL;
-        }
-
-        if (block_signals()) {
-            return -TARGET_ERESTARTSYS;
-        }
-
-        fork_start();
-        ret = fork();
-        if (ret == 0) {
-            /* Child Process.  */
-            cpu_clone_regs(env, newsp);
-            fork_end(1);
-            /* There is a race condition here.  The parent process could
-               theoretically read the TID in the child process before the child
-               tid is set.  This would require using either ptrace
-               (not implemented) or having *_tidptr to point at a shared memory
-               mapping.  We can't repeat the spinlock hack used above because
-               the child process gets its own copy of the lock.  */
-            if (flags & CLONE_CHILD_SETTID)
-                put_user_u32(gettid(), child_tidptr);
-            if (flags & CLONE_PARENT_SETTID)
-                put_user_u32(gettid(), parent_tidptr);
-            ts = (TaskState *)cpu->opaque;
-            if (flags & CLONE_SETTLS)
-                cpu_set_tls (env, newtls);
-            if (flags & CLONE_CHILD_CLEARTID)
-                ts->child_tidptr = child_tidptr;
-        } else {
-            fork_end(0);
-        }
-    }
-    return ret;
-}
-
 /* warning : doesn't handle linux specific flags... */
 static int target_to_host_fcntl_cmd(int cmd)
 {
@@ -5732,106 +5376,6 @@  static abi_long do_fcntl(int fd, int cmd, abi_ulong arg)
     return ret;
 }
 
-#ifdef USE_UID16
-
-static inline int high2lowuid(int uid)
-{
-    if (uid > 65535)
-        return 65534;
-    else
-        return uid;
-}
-
-static inline int high2lowgid(int gid)
-{
-    if (gid > 65535)
-        return 65534;
-    else
-        return gid;
-}
-
-static inline int low2highuid(int uid)
-{
-    if ((int16_t)uid == -1)
-        return -1;
-    else
-        return uid;
-}
-
-static inline int low2highgid(int gid)
-{
-    if ((int16_t)gid == -1)
-        return -1;
-    else
-        return gid;
-}
-static inline int tswapid(int id)
-{
-    return tswap16(id);
-}
-
-#define put_user_id(x, gaddr) put_user_u16(x, gaddr)
-
-#else /* !USE_UID16 */
-static inline int high2lowuid(int uid)
-{
-    return uid;
-}
-static inline int high2lowgid(int gid)
-{
-    return gid;
-}
-static inline int low2highuid(int uid)
-{
-    return uid;
-}
-static inline int low2highgid(int gid)
-{
-    return gid;
-}
-static inline int tswapid(int id)
-{
-    return tswap32(id);
-}
-
-#define put_user_id(x, gaddr) put_user_u32(x, gaddr)
-
-#endif /* USE_UID16 */
-
-/* We must do direct syscalls for setting UID/GID, because we want to
- * implement the Linux system call semantics of "change only for this thread",
- * not the libc/POSIX semantics of "change for all threads in process".
- * (See http://ewontfix.com/17/ for more details.)
- * We use the 32-bit version of the syscalls if present; if it is not
- * then either the host architecture supports 32-bit UIDs natively with
- * the standard syscall, or the 16-bit UID is the best we can do.
- */
-#ifdef __NR_setuid32
-#define __NR_sys_setuid __NR_setuid32
-#else
-#define __NR_sys_setuid __NR_setuid
-#endif
-#ifdef __NR_setgid32
-#define __NR_sys_setgid __NR_setgid32
-#else
-#define __NR_sys_setgid __NR_setgid
-#endif
-#ifdef __NR_setresuid32
-#define __NR_sys_setresuid __NR_setresuid32
-#else
-#define __NR_sys_setresuid __NR_setresuid
-#endif
-#ifdef __NR_setresgid32
-#define __NR_sys_setresgid __NR_setresgid32
-#else
-#define __NR_sys_setresgid __NR_setresgid
-#endif
-
-_syscall1(int, sys_setuid, uid_t, uid)
-_syscall1(int, sys_setgid, gid_t, gid)
-_syscall3(int, sys_setresuid, uid_t, ruid, uid_t, euid, uid_t, suid)
-_syscall3(int, sys_setresgid, gid_t, rgid, gid_t, egid, gid_t, sgid)
-
 void syscall_init(void)
 {
     IOCTLEntry *ie;
@@ -6485,10 +6029,6 @@  static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
         return 0; /* avoid warning */
     case TARGET_NR_brk:
         return do_brk(arg1);
-#ifdef TARGET_NR_fork
-    case TARGET_NR_fork:
-        return get_errno(do_fork(cpu_env, TARGET_SIGCHLD, 0, 0, 0, 0));
-#endif
 #ifdef TARGET_NR_waitpid
     case TARGET_NR_waitpid:
         {
@@ -6713,16 +6253,6 @@  static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
 #endif
     case TARGET_NR_lseek:
         return get_errno(lseek(arg1, arg2, arg3));
-#if defined(TARGET_NR_getxpid) && defined(TARGET_ALPHA)
-    /* Alpha specific */
-    case TARGET_NR_getxpid:
-        ((CPUAlphaState *)cpu_env)->ir[IR_A4] = getppid();
-        return get_errno(getpid());
-#endif
-#ifdef TARGET_NR_getpid
-    case TARGET_NR_getpid:
-        return get_errno(getpid());
-#endif
     case TARGET_NR_mount:
         {
             /* need to look at the data field */
@@ -7062,16 +6592,6 @@  static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
         return ret;
     }
 #endif
-#ifdef TARGET_NR_getppid /* not on alpha */
-    case TARGET_NR_getppid:
-        return get_errno(getppid());
-#endif
-#ifdef TARGET_NR_getpgrp
-    case TARGET_NR_getpgrp:
-        return get_errno(getpgrp());
-#endif
-    case TARGET_NR_setsid:
-        return get_errno(setsid());
 #ifdef TARGET_NR_sigaction
     case TARGET_NR_sigaction:
         {
@@ -8204,23 +7724,6 @@  static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
         return ret;
     case TARGET_NR_fsync:
         return get_errno(fsync(arg1));
-    case TARGET_NR_clone:
-        /* Linux manages to have three different orderings for its
-         * arguments to clone(); the BACKWARDS and BACKWARDS2 defines
-         * match the kernel's CONFIG_CLONE_* settings.
-         * Microblaze is further special in that it uses a sixth
-         * implicit argument to clone for the TLS pointer.
-         */
-#if defined(TARGET_MICROBLAZE)
-        ret = get_errno(do_fork(cpu_env, arg1, arg2, arg4, arg6, arg5));
-#elif defined(TARGET_CLONE_BACKWARDS)
-        ret = get_errno(do_fork(cpu_env, arg1, arg2, arg3, arg4, arg5));
-#elif defined(TARGET_CLONE_BACKWARDS2)
-        ret = get_errno(do_fork(cpu_env, arg2, arg1, arg3, arg5, arg4));
-#else
-        ret = get_errno(do_fork(cpu_env, arg1, arg2, arg3, arg5, arg4));
-#endif
-        return ret;
 #ifdef __NR_exit_group
         /* new thread calls */
     case TARGET_NR_exit_group:
@@ -8966,12 +8469,6 @@  static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
     }
 #endif
 #endif
-#ifdef TARGET_NR_vfork
-    case TARGET_NR_vfork:
-        return get_errno(do_fork(cpu_env,
-                         CLONE_VFORK | CLONE_VM | TARGET_SIGCHLD,
-                         0, 0, 0, 0));
-#endif
 #ifdef TARGET_NR_ugetrlimit
     case TARGET_NR_ugetrlimit:
     {
@@ -9054,66 +8551,6 @@  static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
         unlock_user(p, arg1, 0);
         return ret;
 #endif
-#ifdef TARGET_NR_getuid
-    case TARGET_NR_getuid:
-        return get_errno(high2lowuid(getuid()));
-#endif
-#ifdef TARGET_NR_getgid
-    case TARGET_NR_getgid:
-        return get_errno(high2lowgid(getgid()));
-#endif
-#ifdef TARGET_NR_geteuid
-    case TARGET_NR_geteuid:
-        return get_errno(high2lowuid(geteuid()));
-#endif
-#ifdef TARGET_NR_getegid
-    case TARGET_NR_getegid:
-        return get_errno(high2lowgid(getegid()));
-#endif
-    case TARGET_NR_setreuid:
-        return get_errno(setreuid(low2highuid(arg1), low2highuid(arg2)));
-    case TARGET_NR_setregid:
-        return get_errno(setregid(low2highgid(arg1), low2highgid(arg2)));
-    case TARGET_NR_getgroups:
-        {
-            int gidsetsize = arg1;
-            target_id *target_grouplist;
-            gid_t *grouplist;
-            int i;
-
-            grouplist = alloca(gidsetsize * sizeof(gid_t));
-            ret = get_errno(getgroups(gidsetsize, grouplist));
-            if (gidsetsize == 0)
-                return ret;
-            if (!is_error(ret)) {
-                target_grouplist = lock_user(VERIFY_WRITE, arg2, gidsetsize * sizeof(target_id), 0);
-                if (!target_grouplist)
-                    return -TARGET_EFAULT;
-                for(i = 0;i < ret; i++)
-                    target_grouplist[i] = tswapid(high2lowgid(grouplist[i]));
-                unlock_user(target_grouplist, arg2, gidsetsize * sizeof(target_id));
-            }
-        }
-        return ret;
-    case TARGET_NR_setgroups:
-        {
-            int gidsetsize = arg1;
-            target_id *target_grouplist;
-            gid_t *grouplist = NULL;
-            int i;
-            if (gidsetsize) {
-                grouplist = alloca(gidsetsize * sizeof(gid_t));
-                target_grouplist = lock_user(VERIFY_READ, arg2, gidsetsize * sizeof(target_id), 1);
-                if (!target_grouplist) {
-                    return -TARGET_EFAULT;
-                }
-                for (i = 0; i < gidsetsize; i++) {
-                    grouplist[i] = low2highgid(tswapid(target_grouplist[i]));
-                }
-                unlock_user(target_grouplist, arg2, 0);
-            }
-            return get_errno(setgroups(gidsetsize, grouplist));
-        }
     case TARGET_NR_fchown:
         return get_errno(fchown(arg1, low2highuid(arg2), low2highgid(arg3)));
 #if defined(TARGET_NR_fchownat)
@@ -9125,46 +8562,6 @@  static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
         unlock_user(p, arg2, 0);
         return ret;
 #endif
-#ifdef TARGET_NR_setresuid
-    case TARGET_NR_setresuid:
-        return get_errno(sys_setresuid(low2highuid(arg1),
-                                       low2highuid(arg2),
-                                       low2highuid(arg3)));
-#endif
-#ifdef TARGET_NR_getresuid
-    case TARGET_NR_getresuid:
-        {
-            uid_t ruid, euid, suid;
-            ret = get_errno(getresuid(&ruid, &euid, &suid));
-            if (!is_error(ret)) {
-                if (put_user_id(high2lowuid(ruid), arg1)
-                    || put_user_id(high2lowuid(euid), arg2)
-                    || put_user_id(high2lowuid(suid), arg3))
-                    return -TARGET_EFAULT;
-            }
-        }
-        return ret;
-#endif
-#ifdef TARGET_NR_getresgid
-    case TARGET_NR_setresgid:
-        return get_errno(sys_setresgid(low2highgid(arg1),
-                                       low2highgid(arg2),
-                                       low2highgid(arg3)));
-#endif
-#ifdef TARGET_NR_getresgid
-    case TARGET_NR_getresgid:
-        {
-            gid_t rgid, egid, sgid;
-            ret = get_errno(getresgid(&rgid, &egid, &sgid));
-            if (!is_error(ret)) {
-                if (put_user_id(high2lowgid(rgid), arg1)
-                    || put_user_id(high2lowgid(egid), arg2)
-                    || put_user_id(high2lowgid(sgid), arg3))
-                    return -TARGET_EFAULT;
-            }
-        }
-        return ret;
-#endif
 #ifdef TARGET_NR_chown
     case TARGET_NR_chown:
         if (!(p = lock_user_string(arg1)))
@@ -9173,15 +8570,6 @@  static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
         unlock_user(p, arg1, 0);
         return ret;
 #endif
-    case TARGET_NR_setuid:
-        return get_errno(sys_setuid(low2highuid(arg1)));
-    case TARGET_NR_setgid:
-        return get_errno(sys_setgid(low2highgid(arg1)));
-    case TARGET_NR_setfsuid:
-        return get_errno(setfsuid(arg1));
-    case TARGET_NR_setfsgid:
-        return get_errno(setfsgid(arg1));
-
 #ifdef TARGET_NR_lchown32
     case TARGET_NR_lchown32:
         if (!(p = lock_user_string(arg1)))
@@ -9190,31 +8578,6 @@  static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
         unlock_user(p, arg1, 0);
         return ret;
 #endif
-#ifdef TARGET_NR_getuid32
-    case TARGET_NR_getuid32:
-        return get_errno(getuid());
-#endif
-
-#if defined(TARGET_NR_getxuid) && defined(TARGET_ALPHA)
-   /* Alpha specific */
-    case TARGET_NR_getxuid:
-         {
-            uid_t euid;
-            euid=geteuid();
-            ((CPUAlphaState *)cpu_env)->ir[IR_A4]=euid;
-         }
-        return get_errno(getuid());
-#endif
-#if defined(TARGET_NR_getxgid) && defined(TARGET_ALPHA)
-   /* Alpha specific */
-    case TARGET_NR_getxgid:
-         {
-            uid_t egid;
-            egid=getegid();
-            ((CPUAlphaState *)cpu_env)->ir[IR_A4]=egid;
-         }
-        return get_errno(getgid());
-#endif
 #if defined(TARGET_NR_osf_getsysinfo) && defined(TARGET_ALPHA)
     /* Alpha specific */
     case TARGET_NR_osf_getsysinfo:
@@ -9375,110 +8738,10 @@  static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
         }
         return ret;
 #endif
-
-#ifdef TARGET_NR_getgid32
-    case TARGET_NR_getgid32:
-        return get_errno(getgid());
-#endif
-#ifdef TARGET_NR_geteuid32
-    case TARGET_NR_geteuid32:
-        return get_errno(geteuid());
-#endif
-#ifdef TARGET_NR_getegid32
-    case TARGET_NR_getegid32:
-        return get_errno(getegid());
-#endif
-#ifdef TARGET_NR_setreuid32
-    case TARGET_NR_setreuid32:
-        return get_errno(setreuid(arg1, arg2));
-#endif
-#ifdef TARGET_NR_setregid32
-    case TARGET_NR_setregid32:
-        return get_errno(setregid(arg1, arg2));
-#endif
-#ifdef TARGET_NR_getgroups32
-    case TARGET_NR_getgroups32:
-        {
-            int gidsetsize = arg1;
-            uint32_t *target_grouplist;
-            gid_t *grouplist;
-            int i;
-
-            grouplist = alloca(gidsetsize * sizeof(gid_t));
-            ret = get_errno(getgroups(gidsetsize, grouplist));
-            if (gidsetsize == 0)
-                return ret;
-            if (!is_error(ret)) {
-                target_grouplist = lock_user(VERIFY_WRITE, arg2, gidsetsize * 4, 0);
-                if (!target_grouplist) {
-                    return -TARGET_EFAULT;
-                }
-                for(i = 0;i < ret; i++)
-                    target_grouplist[i] = tswap32(grouplist[i]);
-                unlock_user(target_grouplist, arg2, gidsetsize * 4);
-            }
-        }
-        return ret;
-#endif
-#ifdef TARGET_NR_setgroups32
-    case TARGET_NR_setgroups32:
-        {
-            int gidsetsize = arg1;
-            uint32_t *target_grouplist;
-            gid_t *grouplist;
-            int i;
-
-            grouplist = alloca(gidsetsize * sizeof(gid_t));
-            target_grouplist = lock_user(VERIFY_READ, arg2, gidsetsize * 4, 1);
-            if (!target_grouplist) {
-                return -TARGET_EFAULT;
-            }
-            for(i = 0;i < gidsetsize; i++)
-                grouplist[i] = tswap32(target_grouplist[i]);
-            unlock_user(target_grouplist, arg2, 0);
-            return get_errno(setgroups(gidsetsize, grouplist));
-        }
-#endif
 #ifdef TARGET_NR_fchown32
     case TARGET_NR_fchown32:
         return get_errno(fchown(arg1, arg2, arg3));
 #endif
-#ifdef TARGET_NR_setresuid32
-    case TARGET_NR_setresuid32:
-        return get_errno(sys_setresuid(arg1, arg2, arg3));
-#endif
-#ifdef TARGET_NR_getresuid32
-    case TARGET_NR_getresuid32:
-        {
-            uid_t ruid, euid, suid;
-            ret = get_errno(getresuid(&ruid, &euid, &suid));
-            if (!is_error(ret)) {
-                if (put_user_u32(ruid, arg1)
-                    || put_user_u32(euid, arg2)
-                    || put_user_u32(suid, arg3))
-                    return -TARGET_EFAULT;
-            }
-        }
-        return ret;
-#endif
-#ifdef TARGET_NR_setresgid32
-    case TARGET_NR_setresgid32:
-        return get_errno(sys_setresgid(arg1, arg2, arg3));
-#endif
-#ifdef TARGET_NR_getresgid32
-    case TARGET_NR_getresgid32:
-        {
-            gid_t rgid, egid, sgid;
-            ret = get_errno(getresgid(&rgid, &egid, &sgid));
-            if (!is_error(ret)) {
-                if (put_user_u32(rgid, arg1)
-                    || put_user_u32(egid, arg2)
-                    || put_user_u32(sgid, arg3))
-                    return -TARGET_EFAULT;
-            }
-        }
-        return ret;
-#endif
 #ifdef TARGET_NR_chown32
     case TARGET_NR_chown32:
         if (!(p = lock_user_string(arg1)))
@@ -9487,22 +8750,6 @@  static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
         unlock_user(p, arg1, 0);
         return ret;
 #endif
-#ifdef TARGET_NR_setuid32
-    case TARGET_NR_setuid32:
-        return get_errno(sys_setuid(arg1));
-#endif
-#ifdef TARGET_NR_setgid32
-    case TARGET_NR_setgid32:
-        return get_errno(sys_setgid(arg1));
-#endif
-#ifdef TARGET_NR_setfsuid32
-    case TARGET_NR_setfsuid32:
-        return get_errno(setfsuid(arg1));
-#endif
-#ifdef TARGET_NR_setfsgid32
-    case TARGET_NR_setfsgid32:
-        return get_errno(setfsgid(arg1));
-#endif
 #ifdef TARGET_NR_mincore
     case TARGET_NR_mincore:
         {
@@ -9661,8 +8908,6 @@  static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
     case TARGET_NR_getpagesize:
         return TARGET_PAGE_SIZE;
 #endif
-    case TARGET_NR_gettid:
-        return get_errno(gettid());
 #ifdef TARGET_NR_readahead
     case TARGET_NR_readahead:
 #if TARGET_ABI_BITS == 32
@@ -9839,44 +9084,6 @@  static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
         return ret;
 #endif
 #endif /* CONFIG_ATTR */
-#ifdef TARGET_NR_set_thread_area
-    case TARGET_NR_set_thread_area:
-#if defined(TARGET_MIPS)
-      ((CPUMIPSState *) cpu_env)->active_tc.CP0_UserLocal = arg1;
-      return 0;
-#elif defined(TARGET_CRIS)
-      if (arg1 & 0xff)
-          ret = -TARGET_EINVAL;
-      else {
-          ((CPUCRISState *) cpu_env)->pregs[PR_PID] = arg1;
-          ret = 0;
-      }
-      return ret;
-#elif defined(TARGET_I386) && defined(TARGET_ABI32)
-      return do_set_thread_area(cpu_env, arg1);
-#elif defined(TARGET_M68K)
-      {
-          TaskState *ts = cpu->opaque;
-          ts->tp_value = arg1;
-          return 0;
-      }
-#else
-      return -TARGET_ENOSYS;
-#endif
-#endif
-#ifdef TARGET_NR_get_thread_area
-    case TARGET_NR_get_thread_area:
-#if defined(TARGET_I386) && defined(TARGET_ABI32)
-        return do_get_thread_area(cpu_env, arg1);
-#elif defined(TARGET_M68K)
-        {
-            TaskState *ts = cpu->opaque;
-            return ts->tp_value;
-        }
-#else
-        return -TARGET_ENOSYS;
-#endif
-#endif
 #ifdef TARGET_NR_getdomainname
     case TARGET_NR_getdomainname:
         return -TARGET_ENOSYS;
@@ -9936,12 +9143,6 @@  static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
         return ret;
     }
 #endif
-
-#if defined(TARGET_NR_set_tid_address) && defined(__NR_set_tid_address)
-    case TARGET_NR_set_tid_address:
-        return get_errno(set_tid_address((int *)g2h(arg1)));
-#endif
-
     case TARGET_NR_tkill:
         return get_errno(safe_tkill((int)arg1, target_to_host_signal(arg2)));
 
diff --git a/linux-user/syscall_proc.c b/linux-user/syscall_proc.c
new file mode 100644
index 0000000000..80c0027625
--- /dev/null
+++ b/linux-user/syscall_proc.c
@@ -0,0 +1,914 @@ 
+/*
+ *  Linux process related syscalls
+ *  Copyright (c) 2003 Fabrice Bellard
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "qemu/osdep.h"
+#include "qemu/cutils.h"
+#include "qemu/path.h"
+#include "qemu.h"
+#include "syscall.h"
+#include <grp.h>
+#include <sys/fsuid.h>
+#include <linux/unistd.h>
+
+
+/* We must do direct syscalls for setting UID/GID, because we want to
+ * implement the Linux system call semantics of "change only for this thread",
+ * not the libc/POSIX semantics of "change for all threads in process".
+ * (See http://ewontfix.com/17/ for more details.)
+ * We use the 32-bit version of the syscalls if present; if it is not
+ * then either the host architecture supports 32-bit UIDs natively with
+ * the standard syscall, or the 16-bit UID is the best we can do.
+ */
+#ifdef __NR_setuid32
+#define __NR_sys_setuid __NR_setuid32
+#else
+#define __NR_sys_setuid __NR_setuid
+#endif
+#ifdef __NR_setgid32
+#define __NR_sys_setgid __NR_setgid32
+#else
+#define __NR_sys_setgid __NR_setgid
+#endif
+#ifdef __NR_setresuid32
+#define __NR_sys_setresuid __NR_setresuid32
+#else
+#define __NR_sys_setresuid __NR_setresuid
+#endif
+#ifdef __NR_setresgid32
+#define __NR_sys_setresgid __NR_setresgid32
+#else
+#define __NR_sys_setresgid __NR_setresgid
+#endif
+
+_syscall1(int, sys_setuid, uid_t, uid)
+_syscall1(int, sys_setgid, gid_t, gid)
+_syscall3(int, sys_setresuid, uid_t, ruid, uid_t, euid, uid_t, suid)
+_syscall3(int, sys_setresgid, gid_t, rgid, gid_t, egid, gid_t, sgid)
+
+#ifndef __NR_gettid
+#define __NR_gettid  -1
+#endif
+_syscall0(int, gettid)
+
+#ifndef __NR_set_tid_address
+#define __NR_set_tid_address  -1
+#endif
+_syscall1(int, set_tid_address, int *, tidptr)
+
+
+/* CLONE_VFORK is special cased early in do_fork(). The other flag bits
+ * have almost all been allocated. We cannot support any of
+ * CLONE_NEWNS, CLONE_NEWCGROUP, CLONE_NEWUTS, CLONE_NEWIPC,
+ * CLONE_NEWUSER, CLONE_NEWPID, CLONE_NEWNET, CLONE_PTRACE, CLONE_UNTRACED.
+ * The checks against the invalid thread masks above will catch these.
+ * (The one remaining unallocated bit is 0x1000 which used to be CLONE_PID.)
+ */
+
+#ifndef CLONE_IO
+#define CLONE_IO                0x80000000      /* Clone io context */
+#endif
+
+/* We can't directly call the host clone syscall, because this will
+ * badly confuse libc (breaking mutexes, for example). So we must
+ * divide clone flags into:
+ *  * flag combinations that look like pthread_create()
+ *  * flag combinations that look like fork()
+ *  * flags we can implement within QEMU itself
+ *  * flags we can't support and will return an error for
+ */
+/* For thread creation, all these flags must be present; for
+ * fork, none must be present.
+ */
+#define CLONE_THREAD_FLAGS                              \
+    (CLONE_VM | CLONE_FS | CLONE_FILES |                \
+     CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM)
+
+/* These flags are ignored:
+ * CLONE_DETACHED is now ignored by the kernel;
+ * CLONE_IO is just an optimisation hint to the I/O scheduler
+ */
+#define CLONE_IGNORED_FLAGS                     \
+    (CLONE_DETACHED | CLONE_IO)
+
+/* Flags for fork which we can implement within QEMU itself */
+#define CLONE_OPTIONAL_FORK_FLAGS               \
+    (CLONE_SETTLS | CLONE_PARENT_SETTID |       \
+     CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID)
+
+/* Flags for thread creation which we can implement within QEMU itself */
+#define CLONE_OPTIONAL_THREAD_FLAGS                             \
+    (CLONE_SETTLS | CLONE_PARENT_SETTID |                       \
+     CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID | CLONE_PARENT)
+
+#define CLONE_INVALID_FORK_FLAGS                                        \
+    (~(CSIGNAL | CLONE_OPTIONAL_FORK_FLAGS | CLONE_IGNORED_FLAGS))
+
+#define CLONE_INVALID_THREAD_FLAGS                                      \
+    (~(CSIGNAL | CLONE_THREAD_FLAGS | CLONE_OPTIONAL_THREAD_FLAGS |     \
+       CLONE_IGNORED_FLAGS))
+
+#define NEW_STACK_SIZE 0x40000
+
+static pthread_mutex_t clone_lock = PTHREAD_MUTEX_INITIALIZER;
+typedef struct {
+    CPUArchState *env;
+    pthread_mutex_t mutex;
+    pthread_cond_t cond;
+    pthread_t thread;
+    uint32_t tid;
+    abi_ulong child_tidptr;
+    abi_ulong parent_tidptr;
+    sigset_t sigmask;
+} new_thread_info;
+
+static void *clone_func(void *arg)
+{
+    new_thread_info *info = arg;
+    CPUArchState *env = info->env;
+    CPUState *cpu = ENV_GET_CPU(env);
+    TaskState *ts = (TaskState *)cpu->opaque;
+
+    rcu_register_thread();
+    tcg_register_thread();
+    thread_cpu = cpu;
+    info->tid = gettid();
+    task_settid(ts);
+    if (info->child_tidptr) {
+        put_user_u32(info->tid, info->child_tidptr);
+    }
+    if (info->parent_tidptr) {
+        put_user_u32(info->tid, info->parent_tidptr);
+    }
+    /* Enable signals.  */
+    sigprocmask(SIG_SETMASK, &info->sigmask, NULL);
+    /* Signal to the parent that we're ready.  */
+    pthread_mutex_lock(&info->mutex);
+    pthread_cond_broadcast(&info->cond);
+    pthread_mutex_unlock(&info->mutex);
+    /* Wait until the parent has finished initializing the tls state.  */
+    pthread_mutex_lock(&clone_lock);
+    pthread_mutex_unlock(&clone_lock);
+    cpu_loop(env);
+    /* never exits */
+    return NULL;
+}
+
+static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp,
+                   abi_ulong parent_tidptr, abi_ulong child_tidptr,
+                   target_ulong newtls)
+{
+    CPUState *cpu = ENV_GET_CPU(env);
+    int ret;
+    TaskState *ts;
+    CPUState *new_cpu;
+    CPUArchState *new_env;
+    sigset_t sigmask;
+
+    flags &= ~CLONE_IGNORED_FLAGS;
+
+    /* Emulate vfork() with fork() */
+    if (flags & CLONE_VFORK) {
+        flags &= ~(CLONE_VFORK | CLONE_VM);
+    }
+
+    if (flags & CLONE_VM) {
+        TaskState *parent_ts = (TaskState *)cpu->opaque;
+        new_thread_info info;
+        pthread_attr_t attr;
+
+        if (((flags & CLONE_THREAD_FLAGS) != CLONE_THREAD_FLAGS) ||
+            (flags & CLONE_INVALID_THREAD_FLAGS)) {
+            return -TARGET_EINVAL;
+        }
+
+        ts = g_new0(TaskState, 1);
+        init_task_state(ts);
+
+        /* Grab a mutex so that thread setup appears atomic.  */
+        pthread_mutex_lock(&clone_lock);
+
+        /* we create a new CPU instance. */
+        new_env = cpu_copy(env);
+        /* Init regs that differ from the parent.  */
+        cpu_clone_regs(new_env, newsp);
+        new_cpu = ENV_GET_CPU(new_env);
+        new_cpu->opaque = ts;
+        ts->bprm = parent_ts->bprm;
+        ts->info = parent_ts->info;
+        ts->signal_mask = parent_ts->signal_mask;
+
+        if (flags & CLONE_CHILD_CLEARTID) {
+            ts->child_tidptr = child_tidptr;
+        }
+
+        if (flags & CLONE_SETTLS) {
+            cpu_set_tls(new_env, newtls);
+        }
+
+        memset(&info, 0, sizeof(info));
+        pthread_mutex_init(&info.mutex, NULL);
+        pthread_mutex_lock(&info.mutex);
+        pthread_cond_init(&info.cond, NULL);
+        info.env = new_env;
+        if (flags & CLONE_CHILD_SETTID) {
+            info.child_tidptr = child_tidptr;
+        }
+        if (flags & CLONE_PARENT_SETTID) {
+            info.parent_tidptr = parent_tidptr;
+        }
+
+        ret = pthread_attr_init(&attr);
+        ret = pthread_attr_setstacksize(&attr, NEW_STACK_SIZE);
+        ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+        /* It is not safe to deliver signals until the child has finished
+           initializing, so temporarily block all signals.  */
+        sigfillset(&sigmask);
+        sigprocmask(SIG_BLOCK, &sigmask, &info.sigmask);
+
+        /* If this is our first additional thread, we need to ensure we
+         * generate code for parallel execution and flush old translations.
+         */
+        if (!parallel_cpus) {
+            parallel_cpus = true;
+            tb_flush(cpu);
+        }
+
+        ret = pthread_create(&info.thread, &attr, clone_func, &info);
+
+        /* TODO: Free new CPU state if thread creation failed.  */
+
+        sigprocmask(SIG_SETMASK, &info.sigmask, NULL);
+        pthread_attr_destroy(&attr);
+        if (ret == 0) {
+            /* Wait for the child to initialize.  */
+            pthread_cond_wait(&info.cond, &info.mutex);
+            ret = info.tid;
+        } else {
+            ret = -host_to_target_errno(ret);
+        }
+        pthread_mutex_unlock(&info.mutex);
+        pthread_cond_destroy(&info.cond);
+        pthread_mutex_destroy(&info.mutex);
+        pthread_mutex_unlock(&clone_lock);
+    } else {
+        /* if no CLONE_VM, we consider it is a fork */
+        if (flags & CLONE_INVALID_FORK_FLAGS) {
+            return -TARGET_EINVAL;
+        }
+
+        /* We can't support custom termination signals */
+        if ((flags & CSIGNAL) != TARGET_SIGCHLD) {
+            return -TARGET_EINVAL;
+        }
+
+        if (block_signals()) {
+            return -TARGET_ERESTARTSYS;
+        }
+
+        fork_start();
+        ret = fork();
+        if (ret < 0) {
+            return get_errno(-1);
+        }
+        if (ret == 0) {
+            /* Child Process.  */
+            cpu_clone_regs(env, newsp);
+            fork_end(1);
+            /* There is a race condition here.  The parent process could
+               theoretically read the TID in the child process before the child
+               tid is set.  This would require using either ptrace
+               (not implemented) or having *_tidptr to point at a shared memory
+               mapping.  We can't repeat the spinlock hack used above because
+               the child process gets its own copy of the lock.  */
+            if (flags & CLONE_CHILD_SETTID) {
+                put_user_u32(gettid(), child_tidptr);
+            }
+            if (flags & CLONE_PARENT_SETTID) {
+                put_user_u32(gettid(), parent_tidptr);
+            }
+            ts = (TaskState *)cpu->opaque;
+            if (flags & CLONE_SETTLS) {
+                cpu_set_tls(env, newtls);
+            }
+            if (flags & CLONE_CHILD_CLEARTID) {
+                ts->child_tidptr = child_tidptr;
+            }
+        } else {
+            fork_end(0);
+        }
+    }
+    return ret;
+}
+
+SYSCALL_ARGS(clone)
+{
+    abi_ulong fl, sp, ptid, ctid, tls;
+
+    /* Linux manages to have three different orderings for its
+     * arguments to clone(); the BACKWARDS and BACKWARDS2 defines
+     * match the kernel's CONFIG_CLONE_* settings.
+     * Microblaze is further special in that it uses a sixth
+     * implicit argument to clone for the TLS pointer.
+     */
+#if defined(TARGET_MICROBLAZE)
+    fl = in[0], sp = in[1], ptid = in[3], ctid = in[4], tls = in[5];
+#elif defined(TARGET_CLONE_BACKWARDS)
+    fl = in[0], sp = in[1], ptid = in[2], tls = in[3], ctid = in[4];
+#elif defined(TARGET_CLONE_BACKWARDS2)
+    sp = in[0], fl = in[1], ptid = in[2], ctid = in[3], tls = in[4];
+#else
+    fl = in[0], sp = in[1], ptid = in[2], ctid = in[3], tls = in[4];
+#endif
+    out[0] = fl, out[1] = sp, out[2] = ptid, out[3] = ctid, out[4] = tls;
+    return def;
+}
+
+SYSCALL_IMPL(clone)
+{
+    /* We've done all of the odd ABI adjustment above.  */
+    return do_fork(cpu_env, arg1, arg2, arg3, arg4, arg5);
+}
+SYSCALL_DEF_ARGS(clone, ARG_CLONEFLAG, ARG_PTR, ARG_PTR, ARG_PTR, ARG_PTR);
+
+#ifdef TARGET_NR_fork
+SYSCALL_IMPL(fork)
+{
+    return do_fork(cpu_env, TARGET_SIGCHLD, 0, 0, 0, 0);
+}
+SYSCALL_DEF(fork);
+#endif
+
+#ifdef TARGET_NR_vfork
+SYSCALL_IMPL(vfork)
+{
+    return do_fork(cpu_env, CLONE_VFORK | CLONE_VM | TARGET_SIGCHLD,
+                   0, 0, 0, 0);
+}
+SYSCALL_DEF(vfork);
+#endif
+
+#ifdef TARGET_NR_getegid
+SYSCALL_IMPL(getegid)
+{
+    return get_errno(high2lowgid(getegid()));
+}
+SYSCALL_DEF(getegid);
+#endif
+
+#ifdef TARGET_NR_getegid32
+SYSCALL_IMPL(getegid32)
+{
+    return get_errno(getegid());
+}
+SYSCALL_DEF(getegid32);
+#endif
+
+#ifdef TARGET_NR_geteuid
+SYSCALL_IMPL(geteuid)
+{
+    return get_errno(high2lowuid(geteuid()));
+}
+SYSCALL_DEF(geteuid);
+#endif
+
+#ifdef TARGET_NR_geteuid32
+SYSCALL_IMPL(geteuid32)
+{
+    return get_errno(geteuid());
+}
+SYSCALL_DEF(geteuid32);
+#endif
+
+#ifdef TARGET_NR_getgid
+SYSCALL_IMPL(getgid)
+{
+    return get_errno(high2lowgid(getgid()));
+}
+SYSCALL_DEF(getgid);
+#endif
+
+#ifdef TARGET_NR_getgid32
+SYSCALL_IMPL(getgid32)
+{
+    return get_errno(getgid());
+}
+SYSCALL_DEF(getgid32);
+#endif
+
+SYSCALL_IMPL(getgroups)
+{
+    int gidsetsize = arg1;
+    gid_t *grouplist;
+    abi_long ret;
+
+    grouplist = g_try_new(gid_t, gidsetsize);
+    if (!grouplist) {
+        return -TARGET_ENOMEM;
+    }
+    ret = get_errno(getgroups(gidsetsize, grouplist));
+
+    if (!is_error(ret) && gidsetsize != 0) {
+        size_t target_grouplist_size = gidsetsize * sizeof(target_id);
+        target_id *target_grouplist
+            = lock_user(VERIFY_WRITE, arg2, target_grouplist_size, 0);
+        if (target_grouplist) {
+            int i;
+            for (i = 0; i < ret; i++) {
+                target_grouplist[i] = tswapid(high2lowgid(grouplist[i]));
+            }
+            unlock_user(target_grouplist, arg2, target_grouplist_size);
+        } else {
+            ret = -TARGET_EFAULT;
+        }
+    }
+    g_free(grouplist);
+    return ret;
+}
+SYSCALL_DEF(getgroups, ARG_DEC, ARG_PTR);
+
+#ifdef TARGET_NR_getgroups32
+SYSCALL_IMPL(getgroups32)
+{
+    int gidsetsize = arg1;
+    gid_t *grouplist;
+    abi_long ret;
+
+    grouplist = g_try_new(gid_t, gidsetsize);
+    if (!grouplist) {
+        return -TARGET_ENOMEM;
+    }
+    ret = get_errno(getgroups(gidsetsize, grouplist));
+
+    if (!is_error(ret) && gidsetsize != 0) {
+        uint32_t *target_grouplist
+            = lock_user(VERIFY_WRITE, arg2, gidsetsize * 4, 0);
+        if (target_grouplist) {
+            int i;
+            for (i = 0; i < ret; i++) {
+                target_grouplist[i] = tswap32(grouplist[i]);
+            }
+            unlock_user(target_grouplist, arg2, gidsetsize * 4);
+        } else {
+            ret = -TARGET_EFAULT;
+        }
+    }
+    return ret;
+}
+SYSCALL_DEF(getgroups32, ARG_DEC, ARG_PTR);
+#endif
+
+#ifdef TARGET_NR_getresgid
+SYSCALL_IMPL(getresgid)
+{
+    gid_t rgid, egid, sgid;
+    abi_long ret = get_errno(getresgid(&rgid, &egid, &sgid));
+
+    if (!is_error(ret) &&
+        (put_user_id(high2lowgid(rgid), arg1) ||
+         put_user_id(high2lowgid(egid), arg2) ||
+         put_user_id(high2lowgid(sgid), arg3))) {
+        return -TARGET_EFAULT;
+    }
+    return ret;
+}
+SYSCALL_DEF(getresgid, ARG_PTR, ARG_PTR, ARG_PTR);
+#endif
+
+#ifdef TARGET_NR_getresgid32
+SYSCALL_IMPL(getresgid32)
+{
+    gid_t rgid, egid, sgid;
+    abi_long ret = get_errno(getresgid(&rgid, &egid, &sgid));
+
+    if (!is_error(ret) &&
+        (put_user_u32(rgid, arg1) ||
+         put_user_u32(egid, arg2) ||
+         put_user_u32(sgid, arg3))) {
+        return -TARGET_EFAULT;
+    }
+    return ret;
+}
+SYSCALL_DEF(getresgid32, ARG_PTR, ARG_PTR, ARG_PTR);
+#endif
+
+#ifdef TARGET_NR_getresuid
+SYSCALL_IMPL(getresuid)
+{
+    uid_t ruid, euid, suid;
+    abi_long ret = get_errno(getresuid(&ruid, &euid, &suid));
+
+    if (!is_error(ret) &&
+        (put_user_id(high2lowuid(ruid), arg1) ||
+         put_user_id(high2lowuid(euid), arg2) ||
+         put_user_id(high2lowuid(suid), arg3))) {
+        return -TARGET_EFAULT;
+    }
+    return ret;
+}
+SYSCALL_DEF(getresuid, ARG_PTR, ARG_PTR, ARG_PTR);
+#endif
+
+#ifdef TARGET_NR_getresuid32
+SYSCALL_IMPL(getresuid32)
+{
+    uid_t ruid, euid, suid;
+    abi_long ret = get_errno(getresuid(&ruid, &euid, &suid));
+
+    if (!is_error(ret) &&
+        (put_user_u32(ruid, arg1) ||
+         put_user_u32(euid, arg2) ||
+         put_user_u32(suid, arg3))) {
+        return -TARGET_EFAULT;
+    }
+    return ret;
+}
+SYSCALL_DEF(getresuid32, ARG_PTR, ARG_PTR, ARG_PTR);
+#endif
+
+#ifdef TARGET_NR_getpgrp
+SYSCALL_IMPL(getpgrp)
+{
+    return get_errno(getpgrp());
+}
+SYSCALL_DEF(getpgrp);
+#endif
+
+#ifdef TARGET_NR_getpid
+SYSCALL_IMPL(getpid)
+{
+    return get_errno(getpid());
+}
+SYSCALL_DEF(getpid);
+#endif
+
+#ifdef TARGET_NR_getppid
+SYSCALL_IMPL(getppid)
+{
+    return get_errno(getppid());
+}
+SYSCALL_DEF(getppid);
+#endif
+
+SYSCALL_IMPL(gettid)
+{
+    return get_errno(gettid());
+}
+SYSCALL_DEF(gettid);
+
+#ifdef TARGET_NR_getuid
+SYSCALL_IMPL(getuid)
+{
+    return get_errno(high2lowuid(getuid()));
+}
+SYSCALL_DEF(getuid);
+#endif
+
+#ifdef TARGET_NR_getuid32
+SYSCALL_IMPL(getuid32)
+{
+    return get_errno(getuid());
+}
+SYSCALL_DEF(getuid32);
+#endif
+
+#ifdef TARGET_NR_getxgid
+SYSCALL_IMPL(getxgid)
+{
+    /* Alpha specific */
+    cpu_env->ir[IR_A4] = getegid();
+    return get_errno(getgid());
+}
+SYSCALL_DEF(getxgid);
+#endif
+
+#ifdef TARGET_NR_getxpid
+SYSCALL_IMPL(getxpid)
+{
+    /* Alpha specific */
+    cpu_env->ir[IR_A4] = getppid();
+    return get_errno(getpid());
+}
+SYSCALL_DEF(getxpid);
+#endif
+
+#ifdef TARGET_NR_getxuid
+SYSCALL_IMPL(getxuid)
+{
+    /* Alpha specific */
+    cpu_env->ir[IR_A4] = geteuid();
+    return get_errno(getuid());
+}
+SYSCALL_DEF(getxuid);
+#endif
+
+SYSCALL_IMPL(setfsgid)
+{
+    return get_errno(setfsgid(arg1));
+}
+SYSCALL_DEF(setfsgid, ARG_DEC);
+
+#ifdef TARGET_NR_setfsgid32
+SYSCALL_IMPL(setfsgid32)
+{
+    return get_errno(setfsgid(arg1));
+}
+SYSCALL_DEF(setfsgid32, ARG_DEC);
+#endif
+
+SYSCALL_IMPL(setfsuid)
+{
+    return get_errno(setfsuid(arg1));
+}
+SYSCALL_DEF(setfsuid, ARG_DEC);
+
+#ifdef TARGET_NR_setfsuid32
+SYSCALL_IMPL(setfsuid32)
+{
+    return get_errno(setfsuid(arg1));
+}
+SYSCALL_DEF(setfsuid32, ARG_DEC);
+#endif
+
+SYSCALL_IMPL(setgid)
+{
+    return get_errno(sys_setgid(low2highgid(arg1)));
+}
+SYSCALL_DEF(setgid, ARG_DEC);
+
+#ifdef TARGET_NR_setgid32
+SYSCALL_IMPL(setgid32)
+{
+    return get_errno(sys_setgid(arg1));
+}
+SYSCALL_DEF(setgid32, ARG_DEC);
+#endif
+
+SYSCALL_IMPL(setgroups)
+{
+    int gidsetsize = arg1;
+    gid_t *grouplist = NULL;
+    abi_long ret;
+
+    if (gidsetsize != 0) {
+        size_t target_grouplist_size = gidsetsize * sizeof(target_id);
+        target_id *target_grouplist
+            = lock_user(VERIFY_READ, arg2, target_grouplist_size, 1);
+        int i;
+
+        if (!target_grouplist) {
+            return -TARGET_EFAULT;
+        }
+        grouplist = g_try_new(gid_t, gidsetsize);
+        if (!grouplist) {
+            unlock_user(target_grouplist, arg2, 0);
+            return -TARGET_ENOMEM;
+        }
+
+        for (i = 0; i < gidsetsize; i++) {
+            grouplist[i] = low2highgid(tswapid(target_grouplist[i]));
+        }
+        unlock_user(target_grouplist, arg2, 0);
+    }
+    ret = get_errno(setgroups(gidsetsize, grouplist));
+    g_free(grouplist);
+    return ret;
+}
+SYSCALL_DEF(setgroups, ARG_DEC, ARG_PTR);
+
+#ifdef TARGET_NR_setgroups32
+SYSCALL_IMPL(setgroups32)
+{
+    int gidsetsize = arg1;
+    gid_t *grouplist = NULL;
+    abi_long ret;
+
+    if (gidsetsize != 0) {
+        uint32_t *target_grouplist
+            = lock_user(VERIFY_READ, arg2, gidsetsize * 4, 1);
+        int i;
+
+        if (!target_grouplist) {
+            return -TARGET_EFAULT;
+        }
+        grouplist = g_try_new(gid_t, gidsetsize);
+        if (!grouplist) {
+            unlock_user(target_grouplist, arg2, 0);
+            return -TARGET_ENOMEM;
+        }
+
+        for (i = 0; i < gidsetsize; i++) {
+            grouplist[i] = tswap32(target_grouplist[i]);
+        }
+        unlock_user(target_grouplist, arg2, 0);
+    }
+    ret = get_errno(setgroups(gidsetsize, grouplist));
+    g_free(grouplist);
+    return ret;
+}
+SYSCALL_DEF(setgroups32, ARG_DEC, ARG_PTR);
+#endif
+
+SYSCALL_IMPL(setregid)
+{
+    return get_errno(setregid(low2highgid(arg1), low2highgid(arg2)));
+}
+SYSCALL_DEF(setregid, ARG_DEC, ARG_DEC);
+
+#ifdef TARGET_NR_setregid32
+SYSCALL_IMPL(setregid32)
+{
+    return get_errno(setregid(arg1, arg2));
+}
+SYSCALL_DEF(setregid32, ARG_DEC, ARG_DEC);
+#endif
+
+#ifdef TARGET_NR_setresgid
+SYSCALL_IMPL(setresgid)
+{
+    return get_errno(sys_setresgid(low2highgid(arg1),
+                                   low2highgid(arg2),
+                                   low2highgid(arg3)));
+}
+SYSCALL_DEF(setresgid, ARG_DEC, ARG_DEC, ARG_DEC);
+#endif
+
+#ifdef TARGET_NR_setresgid32
+SYSCALL_IMPL(setresgid32)
+{
+    return get_errno(sys_setresgid(arg1, arg2, arg3));
+}
+SYSCALL_DEF(setresgid32, ARG_DEC, ARG_DEC, ARG_DEC);
+#endif
+
+#ifdef TARGET_NR_setresuid
+SYSCALL_IMPL(setresuid)
+{
+    return get_errno(sys_setresuid(low2highuid(arg1),
+                                   low2highuid(arg2),
+                                   low2highuid(arg3)));
+}
+SYSCALL_DEF(setresuid, ARG_DEC, ARG_DEC, ARG_DEC);
+#endif
+
+#ifdef TARGET_NR_setresuid32
+SYSCALL_IMPL(setresuid32)
+{
+    return get_errno(sys_setresuid(arg1, arg2, arg3));
+}
+SYSCALL_DEF(setresuid32, ARG_DEC, ARG_DEC, ARG_DEC);
+#endif
+
+SYSCALL_IMPL(setreuid)
+{
+    return get_errno(setreuid(low2highuid(arg1), low2highuid(arg2)));
+}
+SYSCALL_DEF(setreuid, ARG_DEC, ARG_DEC);
+
+#ifdef TARGET_NR_setreuid32
+SYSCALL_IMPL(setreuid32)
+{
+    return get_errno(setreuid(arg1, arg2));
+}
+SYSCALL_DEF(setreuid32, ARG_DEC, ARG_DEC);
+#endif
+
+SYSCALL_IMPL(setsid)
+{
+    return get_errno(setsid());
+}
+SYSCALL_DEF(setsid);
+
+SYSCALL_IMPL(setuid)
+{
+    return get_errno(sys_setuid(low2highuid(arg1)));
+}
+SYSCALL_DEF(setuid, ARG_DEC);
+
+#ifdef TARGET_NR_setuid32
+SYSCALL_IMPL(setuid32)
+{
+    return get_errno(sys_setuid(arg1));
+}
+SYSCALL_DEF(setuid32, ARG_DEC);
+#endif
+
+#ifdef TARGET_NR_get_thread_area
+#if defined(TARGET_I386) && defined(TARGET_ABI32)
+static abi_long do_get_thread_area(CPUX86State *env, abi_ulong ptr)
+{
+    struct target_modify_ldt_ldt_s *target_ldt_info;
+    uint64_t *gdt_table = g2h(env->gdt.base);
+    uint32_t base_addr, limit, flags;
+    int seg_32bit, contents, read_exec_only, limit_in_pages, idx;
+    int seg_not_present, useable, lm;
+    uint32_t *lp, entry_1, entry_2;
+
+    lock_user_struct(VERIFY_WRITE, target_ldt_info, ptr, 1);
+    if (!target_ldt_info) {
+        return -TARGET_EFAULT;
+    }
+    idx = tswap32(target_ldt_info->entry_number);
+    if (idx < TARGET_GDT_ENTRY_TLS_MIN ||
+        idx > TARGET_GDT_ENTRY_TLS_MAX) {
+        unlock_user_struct(target_ldt_info, ptr, 1);
+        return -TARGET_EINVAL;
+    }
+    lp = (uint32_t *)(gdt_table + idx);
+    entry_1 = tswap32(lp[0]);
+    entry_2 = tswap32(lp[1]);
+
+    read_exec_only = ((entry_2 >> 9) & 1) ^ 1;
+    contents = (entry_2 >> 10) & 3;
+    seg_not_present = ((entry_2 >> 15) & 1) ^ 1;
+    seg_32bit = (entry_2 >> 22) & 1;
+    limit_in_pages = (entry_2 >> 23) & 1;
+    useable = (entry_2 >> 20) & 1;
+#ifdef TARGET_ABI32
+    lm = 0;
+#else
+    lm = (entry_2 >> 21) & 1;
+#endif
+    flags = (seg_32bit << 0) | (contents << 1) |
+        (read_exec_only << 3) | (limit_in_pages << 4) |
+        (seg_not_present << 5) | (useable << 6) | (lm << 7);
+    limit = (entry_1 & 0xffff) | (entry_2  & 0xf0000);
+    base_addr = (entry_1 >> 16) |
+        (entry_2 & 0xff000000) |
+        ((entry_2 & 0xff) << 16);
+    target_ldt_info->base_addr = tswapal(base_addr);
+    target_ldt_info->limit = tswap32(limit);
+    target_ldt_info->flags = tswap32(flags);
+    unlock_user_struct(target_ldt_info, ptr, 1);
+    return 0;
+}
+#endif
+
+SYSCALL_IMPL(get_thread_area)
+{
+#if defined(TARGET_I386) && defined(TARGET_ABI32)
+    return do_get_thread_area(cpu_env, arg1);
+#elif defined(TARGET_M68K)
+    CPUState *cpu = ENV_GET_CPU(cpu_env);
+    TaskState *ts = cpu->opaque;
+    return ts->tp_value;
+#else
+    return -TARGET_ENOSYS;
+#endif
+}
+
+const SyscallDef def_get_thread_area = {
+    .name = "get_thread_area",
+    .impl = impl_get_thread_area,
+    .print_ret = print_syscall_ptr_ret,
+#if defined(TARGET_I386) && defined(TARGET_ABI32)
+    .arg_type = { ARG_PTR }
+#endif
+};
+#endif
+
+#ifdef TARGET_NR_set_thread_area
+SYSCALL_IMPL(set_thread_area)
+{
+#if defined(TARGET_MIPS)
+    cpu_env->active_tc.CP0_UserLocal = arg1;
+    return 0;
+#elif defined(TARGET_CRIS)
+    if (arg1 & 0xff) {
+        return -TARGET_EINVAL;
+    }
+    cpu_env->pregs[PR_PID] = arg1;
+    return 0;
+#elif defined(TARGET_I386) && defined(TARGET_ABI32)
+    return do_set_thread_area(cpu_env, arg1);
+#elif defined(TARGET_M68K)
+    CPUState *cpu = ENV_GET_CPU(cpu_env);
+    TaskState *ts = cpu->opaque;
+    ts->tp_value = arg1;
+    return 0;
+#else
+    return -TARGET_ENOSYS;
+#endif
+}
+SYSCALL_DEF(set_thread_area, ARG_PTR);
+#endif
+
+SYSCALL_IMPL(set_tid_address)
+{
+    return get_errno(set_tid_address((int *)g2h(arg1)));
+}
+SYSCALL_DEF(set_tid_address, ARG_PTR);
+
diff --git a/linux-user/Makefile.objs b/linux-user/Makefile.objs
index 92ffe66fef..0e18fc88e6 100644
--- a/linux-user/Makefile.objs
+++ b/linux-user/Makefile.objs
@@ -1,5 +1,5 @@ 
 obj-y = main.o syscall.o syscall_file.o syscall_ipc.o syscall_mem.o \
-	strace.o mmap.o signal.o \
+	syscall_proc.o strace.o mmap.o signal.o \
 	elfload.o linuxload.o uaccess.o uname.o \
 	safe-syscall.o $(TARGET_ABI_DIR)/signal.o \
         $(TARGET_ABI_DIR)/cpu_loop.o
@@ -18,6 +18,7 @@  $(SYSCALL_LIST): $(GEN_SYSCALL_LIST)
 
 linux-user/syscall.o \
 linux-user/syscall_file.o \
-linux-user/syscall_mem.o \
 linux-user/syscall_ipc.o \
+linux-user/syscall_mem.o \
+linux-user/syscall_proc.o \
 linux-user/strace.o: $(SYSCALL_LIST)
diff --git a/linux-user/gen_syscall_list.py b/linux-user/gen_syscall_list.py
index 4eb83ae9ce..9c28221bdd 100644
--- a/linux-user/gen_syscall_list.py
+++ b/linux-user/gen_syscall_list.py
@@ -25,7 +25,10 @@  import sys
 # These syscalls are supported by all targets.
 # Avoiding ifdefs for these can diagnose typos in $cpu/syscall_nr.h
 unconditional_syscalls = [
+    "clone",
     "close",
+    "getgroups",
+    "gettid",
     "mlock",
     "mlockall",
     "mprotect",
@@ -43,12 +46,38 @@  unconditional_syscalls = [
     "pwritev",
     "read",
     "readv",
+    "setfsgid",
+    "setfsuid",
+    "setgid",
+    "setgroups",
+    "setsid",
+    "setuid",
     "write",
     "writev",
 ]
 
 # These syscalls are only supported by some target or abis.
 conditional_syscalls = [
+    "fork",
+    "getegid",
+    "getegid32",
+    "geteuid",
+    "geteuid32",
+    "getgid",
+    "getgid32",
+    "getgroups32",
+    "getpgrp",
+    "getpid",
+    "getppid",
+    "getresgid",
+    "getresgid32",
+    "getresuid",
+    "getresuid32",
+    "getuid",
+    "getuid32",
+    "getxgid",
+    "getxpid",
+    "getxuid",
     "ipc",
     "mmap",
     "mmap2",
@@ -60,10 +89,24 @@  conditional_syscalls = [
     "semctl",
     "semget",
     "semop",
+    "setfsgid32",
+    "setfsuid32",
+    "setgid32",
+    "setgroups32",
+    "setregid",
+    "setregid32",
+    "setresgid",
+    "setresgid32",
+    "setresuid",
+    "setresuid32",
+    "setreuid",
+    "setreuid32",
+    "setuid32",
     "shmat",
     "shmctl",
     "shmdt",
     "shmget",
+    "vfork",
 ]
 
 
diff --git a/linux-user/strace.list b/linux-user/strace.list
index 28d1c616ac..d03b5d5096 100644
--- a/linux-user/strace.list
+++ b/linux-user/strace.list
@@ -94,9 +94,6 @@ 
 #ifdef TARGET_NR_clock_settime
 { TARGET_NR_clock_settime, "clock_settime" , NULL, NULL, NULL },
 #endif
-#ifdef TARGET_NR_clone
-{ TARGET_NR_clone, "clone" , NULL, print_clone, NULL },
-#endif
 #ifdef TARGET_NR_connect
 { TARGET_NR_connect, "connect" , "%s(%d,%#x,%d)", NULL, NULL },
 #endif
@@ -223,9 +220,6 @@ 
 #ifdef TARGET_NR_flock
 { TARGET_NR_flock, "flock" , NULL, NULL, NULL },
 #endif
-#ifdef TARGET_NR_fork
-{ TARGET_NR_fork, "fork" , "%s()", NULL, NULL },
-#endif
 #ifdef TARGET_NR_fremovexattr
 { TARGET_NR_fremovexattr, "fremovexattr" , NULL, NULL, NULL },
 #endif
@@ -280,30 +274,6 @@ 
 #ifdef TARGET_NR_getdtablesize
 { TARGET_NR_getdtablesize, "getdtablesize" , NULL, NULL, NULL },
 #endif
-#ifdef TARGET_NR_getegid
-{ TARGET_NR_getegid, "getegid" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_getegid32
-{ TARGET_NR_getegid32, "getegid32" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_geteuid
-{ TARGET_NR_geteuid, "geteuid" , "%s()", NULL, NULL },
-#endif
-#ifdef TARGET_NR_geteuid32
-{ TARGET_NR_geteuid32, "geteuid32" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_getgid
-{ TARGET_NR_getgid, "getgid" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_getgid32
-{ TARGET_NR_getgid32, "getgid32" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_getgroups
-{ TARGET_NR_getgroups, "getgroups" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_getgroups32
-{ TARGET_NR_getgroups32, "getgroups32" , NULL, NULL, NULL },
-#endif
 #ifdef TARGET_NR_gethostname
 { TARGET_NR_gethostname, "gethostname" , NULL, NULL, NULL },
 #endif
@@ -322,39 +292,15 @@ 
 #ifdef TARGET_NR_getpeername
 { TARGET_NR_getpeername, "getpeername" , NULL, NULL, NULL },
 #endif
-#ifdef TARGET_NR_getpgid
-{ TARGET_NR_getpgid, "getpgid" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_getpgrp
-{ TARGET_NR_getpgrp, "getpgrp" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_getpid
-{ TARGET_NR_getpid, "getpid" , "%s()", NULL, NULL },
-#endif
 #ifdef TARGET_NR_getpmsg
 { TARGET_NR_getpmsg, "getpmsg" , NULL, NULL, NULL },
 #endif
-#ifdef TARGET_NR_getppid
-{ TARGET_NR_getppid, "getppid" , "%s()", NULL, NULL },
-#endif
 #ifdef TARGET_NR_getpriority
 { TARGET_NR_getpriority, "getpriority", "%s(%#x,%#x)", NULL, NULL },
 #endif
 #ifdef TARGET_NR_getrandom
 { TARGET_NR_getrandom, "getrandom", NULL, NULL, NULL },
 #endif
-#ifdef TARGET_NR_getresgid
-{ TARGET_NR_getresgid, "getresgid" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_getresgid32
-{ TARGET_NR_getresgid32, "getresgid32" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_getresuid
-{ TARGET_NR_getresuid, "getresuid" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_getresuid32
-{ TARGET_NR_getresuid32, "getresuid32" , NULL, NULL, NULL },
-#endif
 #ifdef TARGET_NR_getrlimit
 { TARGET_NR_getrlimit, "getrlimit" , NULL, NULL, NULL },
 #endif
@@ -364,9 +310,6 @@ 
 #ifdef TARGET_NR_getrusage
 { TARGET_NR_getrusage, "getrusage" , NULL, NULL, NULL },
 #endif
-#ifdef TARGET_NR_getsid
-{ TARGET_NR_getsid, "getsid" , NULL, NULL, NULL },
-#endif
 #ifdef TARGET_NR_getsockname
 { TARGET_NR_getsockname, "getsockname" , NULL, NULL, NULL },
 #endif
@@ -377,30 +320,12 @@ 
 { TARGET_NR_get_thread_area, "get_thread_area", "%s(0x"TARGET_ABI_FMT_lx")",
   NULL, NULL },
 #endif
-#ifdef TARGET_NR_gettid
-{ TARGET_NR_gettid, "gettid" , "%s()", NULL, NULL },
-#endif
 #ifdef TARGET_NR_gettimeofday
 { TARGET_NR_gettimeofday, "gettimeofday" , NULL, NULL, NULL },
 #endif
-#ifdef TARGET_NR_getuid
-{ TARGET_NR_getuid, "getuid" , "%s()", NULL, NULL },
-#endif
-#ifdef TARGET_NR_getuid32
-{ TARGET_NR_getuid32, "getuid32" , NULL, NULL, NULL },
-#endif
 #ifdef TARGET_NR_getxattr
 { TARGET_NR_getxattr, "getxattr" , NULL, NULL, NULL },
 #endif
-#ifdef TARGET_NR_getxgid
-{ TARGET_NR_getxgid, "getxgid" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_getxpid
-{ TARGET_NR_getxpid, "getxpid" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_getxuid
-{ TARGET_NR_getxuid, "getxuid" , NULL, NULL, NULL },
-#endif
 #ifdef TARGET_NR_gtty
 { TARGET_NR_gtty, "gtty" , NULL, NULL, NULL },
 #endif
@@ -632,9 +557,6 @@ 
 #ifdef TARGET_NR_osf_alt_plock
 { TARGET_NR_osf_alt_plock, "osf_alt_plock" , NULL, NULL, NULL },
 #endif
-#ifdef TARGET_NR_osf_alt_setsid
-{ TARGET_NR_osf_alt_setsid, "osf_alt_setsid" , NULL, NULL, NULL },
-#endif
 #ifdef TARGET_NR_osf_alt_sigpending
 { TARGET_NR_osf_alt_sigpending, "osf_alt_sigpending" , NULL, NULL, NULL },
 #endif
@@ -1160,30 +1082,6 @@ 
 #ifdef TARGET_NR_setdomainname
 { TARGET_NR_setdomainname, "setdomainname" , NULL, NULL, NULL },
 #endif
-#ifdef TARGET_NR_setfsgid
-{ TARGET_NR_setfsgid, "setfsgid" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_setfsgid32
-{ TARGET_NR_setfsgid32, "setfsgid32" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_setfsuid
-{ TARGET_NR_setfsuid, "setfsuid" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_setfsuid32
-{ TARGET_NR_setfsuid32, "setfsuid32" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_setgid
-{ TARGET_NR_setgid, "setgid" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_setgid32
-{ TARGET_NR_setgid32, "setgid32" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_setgroups
-{ TARGET_NR_setgroups, "setgroups" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_setgroups32
-{ TARGET_NR_setgroups32, "setgroups32" , NULL, NULL, NULL },
-#endif
 #ifdef TARGET_NR_sethae
 { TARGET_NR_sethae, "sethae" , NULL, NULL, NULL },
 #endif
@@ -1199,48 +1097,15 @@ 
 #ifdef TARGET_NR_setns
 { TARGET_NR_setns, "setns" , NULL, NULL, NULL },
 #endif
-#ifdef TARGET_NR_setpgid
-{ TARGET_NR_setpgid, "setpgid" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_setpgrp
-{ TARGET_NR_setpgrp, "setpgrp" , NULL, NULL, NULL },
-#endif
 #ifdef TARGET_NR_setpriority
 { TARGET_NR_setpriority, "setpriority" , NULL, NULL, NULL },
 #endif
-#ifdef TARGET_NR_setregid
-{ TARGET_NR_setregid, "setregid" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_setregid32
-{ TARGET_NR_setregid32, "setregid32" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_setresgid
-{ TARGET_NR_setresgid, "setresgid" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_setresgid32
-{ TARGET_NR_setresgid32, "setresgid32" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_setresuid
-{ TARGET_NR_setresuid, "setresuid" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_setresuid32
-{ TARGET_NR_setresuid32, "setresuid32" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_setreuid
-{ TARGET_NR_setreuid, "setreuid" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_setreuid32
-{ TARGET_NR_setreuid32, "setreuid32" , NULL, NULL, NULL },
-#endif
 #ifdef TARGET_NR_setrlimit
 { TARGET_NR_setrlimit, "setrlimit" , NULL, NULL, NULL },
 #endif
 #ifdef TARGET_NR_set_robust_list
 { TARGET_NR_set_robust_list, "set_robust_list" , NULL, NULL, NULL },
 #endif
-#ifdef TARGET_NR_setsid
-{ TARGET_NR_setsid, "setsid" , NULL, NULL, NULL },
-#endif
 #ifdef TARGET_NR_setsockopt
 { TARGET_NR_setsockopt, "setsockopt" , NULL, NULL, NULL },
 #endif
@@ -1254,12 +1119,6 @@ 
 #ifdef TARGET_NR_settimeofday
 { TARGET_NR_settimeofday, "settimeofday" , NULL, NULL, NULL },
 #endif
-#ifdef TARGET_NR_setuid
-{ TARGET_NR_setuid, "setuid" , NULL, NULL, NULL },
-#endif
-#ifdef TARGET_NR_setuid32
-{ TARGET_NR_setuid32, "setuid32" , NULL, NULL, NULL },
-#endif
 #ifdef TARGET_NR_setxattr
 { TARGET_NR_setxattr, "setxattr" , NULL, NULL, NULL },
 #endif
@@ -1494,9 +1353,6 @@ 
 #ifdef TARGET_NR_utrap_install
 { TARGET_NR_utrap_install, "utrap_install" , NULL, NULL, NULL },
 #endif
-#ifdef TARGET_NR_vfork
-{ TARGET_NR_vfork, "vfork" , NULL, NULL, NULL },
-#endif
 #ifdef TARGET_NR_vhangup
 { TARGET_NR_vhangup, "vhangup" , NULL, NULL, NULL },
 #endif