[PATCH] [sanitizer_common] Add internal_clone().

Sergey Matveev earthdok at google.com
Fri Aug 30 07:18:53 PDT 2013


Hi kcc, eugenis, glider,

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.

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

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

Index: lib/sanitizer_common/sanitizer_linux.h
===================================================================
--- lib/sanitizer_common/sanitizer_linux.h
+++ lib/sanitizer_common/sanitizer_linux.h
@@ -29,6 +29,8 @@
 uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5);
 uptr internal_sigaltstack(const struct sigaltstack* ss,
                           struct sigaltstack* oss);
+int internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
+                   int *parent_tidptr, void *newtls, int *child_tidptr);
 
 // This class reads thread IDs from /proc/<pid>/task using only syscalls.
 class ThreadLister {
Index: lib/sanitizer_common/sanitizer_linux_libcdep.cc
===================================================================
--- lib/sanitizer_common/sanitizer_linux_libcdep.cc
+++ lib/sanitizer_common/sanitizer_linux_libcdep.cc
@@ -20,9 +20,12 @@
 #include "sanitizer_stacktrace.h"
 
 #include <dlfcn.h>
+#include <errno.h>
 #include <pthread.h>
 #include <sys/prctl.h>
 #include <sys/resource.h>
+#include <sys/syscall.h>
+
 #include <unwind.h>
 
 namespace __sanitizer {
@@ -285,6 +288,68 @@
   }
 }
 
+#if defined(__x86_64__)
+int 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) {
+    errno = EINVAL;
+    return -1;
+  }
+  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(%ebx). */
+                       "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");
+  if (res < 0) {
+    errno = -res;
+    return -1;
+  }
+  return res;
+}
+#endif  // defined(__x86_64__)
 }  // namespace __sanitizer
 
 #endif  // SANITIZER_LINUX
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,9 +370,10 @@
   // 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);
+  pid_t tracer_pid =
+      internal_clone(TracerThread, tracer_stack.Bottom(),
+                     CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_UNTRACED,
+                     &tracer_thread_argument, 0, 0, 0);
   if (tracer_pid < 0) {
     Report("Failed spawning a tracer thread (errno %d).\n", errno);
     tracer_thread_argument.mutex.Unlock();
@@ -448,4 +448,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.1.patch
Type: text/x-patch
Size: 5911 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20130830/01bbbf73/attachment.bin>


More information about the llvm-commits mailing list