mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-18 19:06:44 +00:00
[sanitizer_common] Add internal_clone().
Add a wrapper for the clone syscall for use in StopTheWorld. We implement it only for x86_64, so stop building StopTheWorld for other platforms (no one uses it outside x86_64 anyway). See https://code.google.com/p/address-sanitizer/issues/detail?id=214 for why we can't use the glibc clone() wrapper. llvm-svn: 189753
This commit is contained in:
parent
2bbc79039d
commit
69931c5841
@ -679,6 +679,69 @@ void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr)) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__x86_64__)
|
||||
// We cannot use glibc's clone wrapper, because it messes with the child
|
||||
// task's TLS. It writes the PID and TID of the child task to its thread
|
||||
// descriptor, but in our case the child task shares the thread descriptor with
|
||||
// the parent (because we don't know how to allocate a new thread
|
||||
// descriptor to keep glibc happy). So the stock version of clone(), when
|
||||
// used with CLONE_VM, would end up corrupting the parent's thread descriptor.
|
||||
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
|
||||
int *parent_tidptr, void *newtls, int *child_tidptr) {
|
||||
long long res;
|
||||
if (!fn || !child_stack)
|
||||
return -EINVAL;
|
||||
CHECK_EQ(0, (uptr)child_stack % 16);
|
||||
child_stack = (char *)child_stack - 2 * sizeof(void *);
|
||||
((void **)child_stack)[0] = (void *)(uptr)fn;
|
||||
((void **)child_stack)[1] = arg;
|
||||
__asm__ __volatile__(
|
||||
/* %rax = syscall(%rax = __NR_clone,
|
||||
* %rdi = flags,
|
||||
* %rsi = child_stack,
|
||||
* %rdx = parent_tidptr,
|
||||
* %r8 = new_tls,
|
||||
* %r10 = child_tidptr)
|
||||
*/
|
||||
"movq %6,%%r8\n"
|
||||
"movq %7,%%r10\n"
|
||||
".cfi_endproc\n"
|
||||
"syscall\n"
|
||||
|
||||
/* if (%rax != 0)
|
||||
* return;
|
||||
*/
|
||||
"testq %%rax,%%rax\n"
|
||||
"jnz 1f\n"
|
||||
|
||||
/* In the child. Terminate unwind chain. */
|
||||
".cfi_startproc\n"
|
||||
".cfi_undefined %%rip;\n"
|
||||
"xorq %%rbp,%%rbp\n"
|
||||
|
||||
/* Call "fn(arg)". */
|
||||
"popq %%rax\n"
|
||||
"popq %%rdi\n"
|
||||
"call *%%rax\n"
|
||||
|
||||
/* Call _exit(%rax). */
|
||||
"movq %%rax,%%rdi\n"
|
||||
"movq %2,%%rax\n"
|
||||
"syscall\n"
|
||||
|
||||
/* Return to parent. */
|
||||
"1:\n"
|
||||
: "=a" (res)
|
||||
: "a"(__NR_clone), "i"(__NR_exit),
|
||||
"S"(child_stack),
|
||||
"D"(flags),
|
||||
"d"(parent_tidptr),
|
||||
"r"(newtls),
|
||||
"r"(child_tidptr)
|
||||
: "rsp", "memory", "r8", "r10", "r11", "rcx");
|
||||
return res;
|
||||
}
|
||||
#endif // defined(__x86_64__)
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_LINUX
|
||||
|
@ -29,6 +29,10 @@ uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count);
|
||||
uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5);
|
||||
uptr internal_sigaltstack(const struct sigaltstack* ss,
|
||||
struct sigaltstack* oss);
|
||||
#ifdef __x86_64__
|
||||
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
|
||||
int *parent_tidptr, void *newtls, int *child_tidptr);
|
||||
#endif
|
||||
|
||||
// This class reads thread IDs from /proc/<pid>/task using only syscalls.
|
||||
class ThreadLister {
|
||||
|
@ -14,12 +14,12 @@
|
||||
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
#if SANITIZER_LINUX
|
||||
#if SANITIZER_LINUX && defined(__x86_64__)
|
||||
|
||||
#include "sanitizer_stoptheworld.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <sched.h> // for clone
|
||||
#include <sched.h> // for CLONE_* definitions
|
||||
#include <stddef.h>
|
||||
#include <sys/prctl.h> // for PR_* definitions
|
||||
#include <sys/ptrace.h> // for PTRACE_* definitions
|
||||
@ -71,7 +71,6 @@
|
||||
// after it has exited. The following functions are used in this manner:
|
||||
// sigdelset()
|
||||
// sigprocmask()
|
||||
// clone()
|
||||
|
||||
COMPILER_CHECK(sizeof(SuspendedThreadID) == sizeof(pid_t));
|
||||
|
||||
@ -371,11 +370,14 @@ void StopTheWorld(StopTheWorldCallback callback, void *argument) {
|
||||
// Block the execution of TracerThread until after we have set ptrace
|
||||
// permissions.
|
||||
tracer_thread_argument.mutex.Lock();
|
||||
pid_t tracer_pid = clone(TracerThread, tracer_stack.Bottom(),
|
||||
CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_UNTRACED,
|
||||
&tracer_thread_argument);
|
||||
if (tracer_pid < 0) {
|
||||
Report("Failed spawning a tracer thread (errno %d).\n", errno);
|
||||
uptr tracer_pid = internal_clone(
|
||||
TracerThread, tracer_stack.Bottom(),
|
||||
CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_UNTRACED,
|
||||
&tracer_thread_argument, 0 /* parent_tidptr */, 0 /* newtls */, 0
|
||||
/* child_tidptr */);
|
||||
int local_errno = 0;
|
||||
if (internal_iserror(tracer_pid, &local_errno)) {
|
||||
Report("Failed spawning a tracer thread (errno %d).\n", local_errno);
|
||||
tracer_thread_argument.mutex.Unlock();
|
||||
} else {
|
||||
// On some systems we have to explicitly declare that we want to be traced
|
||||
@ -390,9 +392,8 @@ void StopTheWorld(StopTheWorldCallback callback, void *argument) {
|
||||
// At this point, any signal will either be blocked or kill us, so waitpid
|
||||
// should never return (and set errno) while the tracer thread is alive.
|
||||
uptr waitpid_status = internal_waitpid(tracer_pid, NULL, __WALL);
|
||||
int wperrno;
|
||||
if (internal_iserror(waitpid_status, &wperrno))
|
||||
Report("Waiting on the tracer thread failed (errno %d).\n", wperrno);
|
||||
if (internal_iserror(waitpid_status, &local_errno))
|
||||
Report("Waiting on the tracer thread failed (errno %d).\n", local_errno);
|
||||
}
|
||||
}
|
||||
|
||||
@ -448,4 +449,4 @@ uptr SuspendedThreadsList::RegisterCount() {
|
||||
}
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_LINUX
|
||||
#endif // SANITIZER_LINUX && defined(__x86_64__)
|
||||
|
@ -12,7 +12,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_common/sanitizer_platform.h"
|
||||
#if SANITIZER_LINUX
|
||||
#if SANITIZER_LINUX && defined(__x86_64__)
|
||||
|
||||
#include "sanitizer_common/sanitizer_stoptheworld.h"
|
||||
#include "gtest/gtest.h"
|
||||
@ -191,4 +191,4 @@ TEST(StopTheWorld, SuspendThreadsAdvanced) {
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_LINUX
|
||||
#endif // SANITIZER_LINUX && defined(__x86_64__)
|
||||
|
Loading…
x
Reference in New Issue
Block a user