blob: ed6cf81c9322d238d55d87a4310e109aa0c42a46 [file]
//===----------------------------------------------------------------------===//
//
// 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