[PATCH] [sanitizer_common] Add internal_clone().

Sergey Matveev earthdok at google.com
Fri Aug 30 11:40:56 PDT 2013


    - eliminated a file with whitespace-only changes

Hi pcc, kcc, eugenis, glider,

http://llvm-reviews.chandlerc.com/D1558

CHANGE SINCE LAST DIFF
  http://llvm-reviews.chandlerc.com/D1558?vs=3937&id=3938#toc

Files:
  lib/sanitizer_common/sanitizer_linux.cc
  lib/sanitizer_common/sanitizer_linux.h
  lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
  lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc

Index: lib/sanitizer_common/sanitizer_linux.cc
===================================================================
--- lib/sanitizer_common/sanitizer_linux.cc
+++ lib/sanitizer_common/sanitizer_linux.cc
@@ -677,6 +677,69 @@
 }
 #endif
 
+#if defined(__x86_64__)
+// We cannot use glibc's clone wrapper, because it messes with the child
+// taks'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
Index: lib/sanitizer_common/sanitizer_linux.h
===================================================================
--- lib/sanitizer_common/sanitizer_linux.h
+++ lib/sanitizer_common/sanitizer_linux.h
@@ -29,6 +29,10 @@
 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 {
Index: lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
===================================================================
--- lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
+++ lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
@@ -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 @@
   // 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 @@
     // 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 @@
 }
 }  // namespace __sanitizer
 
-#endif  // SANITIZER_LINUX
+#endif  // SANITIZER_LINUX && defined(__x86_64__)
Index: lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc
===================================================================
--- lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc
+++ lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc
@@ -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 @@
 
 }  // namespace __sanitizer
 
-#endif  // SANITIZER_LINUX
+#endif  // SANITIZER_LINUX && defined(__x86_64__)
-------------- next part --------------
A non-text attachment was scrubbed...
Name: D1558.3.patch
Type: text/x-patch
Size: 6906 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20130830/67ad5128/attachment.bin>


More information about the llvm-commits mailing list