| //===----------------------------------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| /// | |
| /// \file | |
| /// Linux implementation of clone. | |
| /// | |
| //===----------------------------------------------------------------------===// | |
| #include "src/sched/clone.h" | |
| #include "hdr/errno_macros.h" | |
| #include "hdr/sched_macros.h" | |
| #include "hdr/types/pid_t.h" | |
| #include "src/__support/OSUtil/linux/syscall_wrappers/clone.h" | |
| #include "src/__support/common.h" | |
| #include "src/__support/libc_errno.h" | |
| #include "src/__support/macros/config.h" | |
| #include "src/__support/threads/identifier.h" | |
| #include <stdarg.h> | |
| namespace LIBC_NAMESPACE_DECL { | |
| LLVM_LIBC_FUNCTION(int, clone, | |
| (int (*func)(void *), void *stack, int flags, void *arg, | |
| ...)) { | |
| if (func == nullptr || stack == nullptr) { | |
| libc_errno = EINVAL; | |
| return -1; | |
| } | |
| pid_t *parent_tid = nullptr; | |
| void *tls = nullptr; | |
| pid_t *child_tid = nullptr; | |
| if (flags & (CLONE_PARENT_SETTID | CLONE_SETTLS | CLONE_CHILD_SETTID | | |
| CLONE_CHILD_CLEARTID | CLONE_PIDFD)) { | |
| va_list args; | |
| va_start(args, arg); | |
| parent_tid = va_arg(args, pid_t *); | |
| if (flags & (CLONE_SETTLS | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) | |
| tls = va_arg(args, void *); | |
| if (flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) | |
| child_tid = va_arg(args, pid_t *); | |
| va_end(args); | |
| } | |
| uintptr_t stack_as_integer = reinterpret_cast<uintptr_t>(stack); | |
| stack_as_integer &= ~(linux_syscalls::CLONE_STACK_ALIGNMENT - 1); | |
| stack = reinterpret_cast<void *>(stack_as_integer); | |
| // In some situations, we need to invalidate parent's tid cache. We cannot do | |
| // this in the child because a signal handler may observe the wrong tid before | |
| // we get a chance to set it. We need to clear the tid if: | |
| // - the child is sharing the TLS block with us (!clone_tls); and | |
| // - the child is NOT sharing the address space with us (!clone_vm) or the | |
| // parent-child execution is serialized (clone_vfork). | |
| // We cannot safely do this in the clone_vm && !clone_vfork case, as the | |
| // parent and child would race to overwrite each other's values, so we leave | |
| // this case unsupported. | |
| bool clone_settls = flags & CLONE_SETTLS; | |
| bool clone_vm = flags & CLONE_VM; | |
| bool clone_vfork = flags & CLONE_VFORK; | |
| bool should_clear_tid = !clone_settls && (!clone_vm || clone_vfork); | |
| pid_t tid_of_parent; | |
| if (should_clear_tid) { | |
| tid_of_parent = internal::gettid(); | |
| internal::force_set_tid(0); | |
| } | |
| auto ret = linux_syscalls::clone(func, stack, flags, arg, parent_tid, tls, | |
| child_tid); | |
| if (should_clear_tid) | |
| internal::force_set_tid(tid_of_parent); | |
| if (!ret.has_value()) { | |
| libc_errno = ret.error(); | |
| return -1; | |
| } | |
| return ret.value(); | |
| } | |
| } // namespace LIBC_NAMESPACE_DECL |