[compiler-rt] r189753 - [sanitizer_common] Add internal_clone().
Sergey Matveev
earthdok at google.com
Mon Sep 2 04:36:19 PDT 2013
Author: smatveev
Date: Mon Sep 2 06:36:19 2013
New Revision: 189753
URL: http://llvm.org/viewvc/llvm-project?rev=189753&view=rev
Log:
[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.
Modified:
compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.cc
compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.h
compiler-rt/trunk/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
compiler-rt/trunk/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc
Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.cc?rev=189753&r1=189752&r2=189753&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.cc (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.cc Mon Sep 2 06:36:19 2013
@@ -679,6 +679,69 @@ void ForEachMappedRegion(link_map *map,
}
#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
Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.h?rev=189753&r1=189752&r2=189753&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.h (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.h Mon Sep 2 06:36:19 2013
@@ -29,6 +29,10 @@ uptr internal_getdents(fd_t fd, struct l
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 {
Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc?rev=189753&r1=189752&r2=189753&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc Mon Sep 2 06:36:19 2013
@@ -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 c
// 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 c
// 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__)
Modified: compiler-rt/trunk/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc?rev=189753&r1=189752&r2=189753&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc (original)
+++ compiler-rt/trunk/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc Mon Sep 2 06:36:19 2013
@@ -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, SuspendThreadsAdvance
} // namespace __sanitizer
-#endif // SANITIZER_LINUX
+#endif // SANITIZER_LINUX && defined(__x86_64__)
More information about the llvm-commits
mailing list