diff --git a/src/linux/clone.c b/src/linux/clone.c index 8c1af7d3..32d53a8f 100644 --- a/src/linux/clone.c +++ b/src/linux/clone.c @@ -4,6 +4,25 @@ #include #include "pthread_impl.h" #include "syscall.h" +#include "dynlink.h" + +extern int _Forklike(int (*)(void *), void *); + +struct clone_args { + int flags; + pid_t *ptid, *ctid; +}; + +static int do_clone(void *p) +{ + struct clone_args *args = p; + int mask = CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID; + int r = __syscall(SYS_clone, args->flags & ~mask, 0); + if (r>0 && (args->flags & CLONE_PARENT_SETTID)) *args->ptid = r; + if (!r && (args->flags & CLONE_CHILD_SETTID)) *args->ctid = __syscall(SYS_gettid); + if (!r && (args->flags & CLONE_CHILD_CLEARTID)) __syscall(SYS_set_tid_address, args->ctid); + return r; +} int clone(int (*func)(void *), void *stack, int flags, void *arg, ...) { @@ -17,5 +36,13 @@ int clone(int (*func)(void *), void *stack, int flags, void *arg, ...) ctid = va_arg(ap, pid_t *); va_end(ap); + if (!(flags & CLONE_VM)) { + struct clone_args args = { .flags = flags, .ptid = ptid, .ctid = ctid }; + if (flags & CLONE_THREAD) return __syscall_ret(-EINVAL); + int r = _Forklike(do_clone, &args); + if (r) return r; + CRTJMP(func, stack); + } + return __syscall_ret(__clone(func, stack, flags, arg, ptid, tls, ctid)); }