Merge "Update to kernel headers v4.9.3."
diff --git a/libc/Android.bp b/libc/Android.bp
index 4df94a0..ec00f28 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -1869,6 +1869,21 @@
     ],
 }
 
+cc_object {
+    name: "kuser_helper_on",
+    local_include_dirs: ["include"],
+    arch: {
+        arm: {
+            srcs: ["arch-arm/bionic/kuser_helper_on.S"],
+        },
+    },
+
+    defaults: [
+        "crt_defaults",
+        "crt_so_defaults",
+    ],
+}
+
 // Android.mk:ignore
 cc_object {
     name: "crtbegin_so1",
@@ -1893,6 +1908,11 @@
         "crtbegin_so1",
         "crtbrand",
     ],
+    arch: {
+        arm: {
+            objs: ["kuser_helper_on"],
+        },
+    },
 }
 
 // Android.mk:ignore
@@ -1951,6 +1971,11 @@
         "crtbegin_static1",
         "crtbrand",
     ],
+    arch: {
+        arm: {
+            objs: ["kuser_helper_on"],
+        },
+    },
     defaults: ["crt_defaults"],
 }
 
@@ -1997,6 +2022,11 @@
         "crtbegin_dynamic1",
         "crtbrand",
     ],
+    arch: {
+        arm: {
+            objs: ["kuser_helper_on"],
+        },
+    },
     defaults: ["crt_defaults"],
 }
 
diff --git a/libc/NOTICE b/libc/NOTICE
index fd6cee6..1b996e9 100644
--- a/libc/NOTICE
+++ b/libc/NOTICE
@@ -932,6 +932,50 @@
 
 -------------------------------------------------------------------
 
+Copyright (C) 2017 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+-------------------------------------------------------------------
+
+Copyright (C) 2017 The Android Open Source Project
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+ * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in
+   the documentation and/or other materials provided with the
+   distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
 Copyright (c) 1980, 1983, 1988, 1993
    The Regents of the University of California.  All rights reserved.
 
diff --git a/libc/arch-arm/bionic/kuser_helper_on.S b/libc/arch-arm/bionic/kuser_helper_on.S
new file mode 100644
index 0000000..e59d0d5
--- /dev/null
+++ b/libc/arch-arm/bionic/kuser_helper_on.S
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+	.section	.note.android.kuser_helper_off,"a",%note
+	.align	2
+	.type	abitag, %object
+abitag:
+	.long	2f-1f			/* int32_t namesz */
+	.long	3f-2f			/* int32_t descsz */
+	.long	3			/* int32_t type */
+1:	.ascii	"Android\0"		/* char name[] */
+2:	.long	1	/* int32_t on */
+3:
+	.size	abitag, .-abitag
diff --git a/libc/dns/include/resolv_netid.h b/libc/dns/include/resolv_netid.h
index 7182ca6..37c1891 100644
--- a/libc/dns/include/resolv_netid.h
+++ b/libc/dns/include/resolv_netid.h
@@ -71,7 +71,7 @@
     unsigned dns_netid;
     unsigned dns_mark;
     uid_t uid;
-} __attribute__((packed));
+};
 
 #define NET_CONTEXT_INVALID_UID ((uid_t)-1)
 
diff --git a/libc/dns/include/resolv_params.h b/libc/dns/include/resolv_params.h
index 5ee265f..49ae691 100644
--- a/libc/dns/include/resolv_params.h
+++ b/libc/dns/include/resolv_params.h
@@ -41,6 +41,6 @@
     uint8_t success_threshold; // 0: disable, value / 100 otherwise
     uint8_t min_samples; // min # samples needed for statistics to be considered meaningful
     uint8_t max_samples; // max # samples taken into account for statistics
-} __attribute__((__packed__));
+};
 
 #endif // _RESOLV_PARAMS_H
diff --git a/libc/seccomp/arm64_policy.c b/libc/seccomp/arm64_policy.c
index d5a87d6..6a8bda0 100644
--- a/libc/seccomp/arm64_policy.c
+++ b/libc/seccomp/arm64_policy.c
@@ -41,7 +41,7 @@
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 268, 1, 0),
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 267, 1, 2), //clock_adjtime
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 272, 0, 1), //setns|sendmmsg|process_vm_readv|process_vm_writev
-BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
+BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRAP),
 BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
 };
 
diff --git a/libc/seccomp/arm_policy.c b/libc/seccomp/arm_policy.c
index 44e734e..de03f45 100644
--- a/libc/seccomp/arm_policy.c
+++ b/libc/seccomp/arm_policy.c
@@ -139,7 +139,7 @@
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 983045, 1, 0),
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 983043, 1, 2), //__ARM_NR_cacheflush
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 983046, 0, 1), //__ARM_NR_set_tls
-BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
+BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRAP),
 BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
 };
 
diff --git a/libc/tools/genseccomp.py b/libc/tools/genseccomp.py
index b82bb12..bd003a3 100755
--- a/libc/tools/genseccomp.py
+++ b/libc/tools/genseccomp.py
@@ -126,7 +126,7 @@
              ", 0, " + str(len(bpf)) + "),")
 
   # Add the error and allow calls at the end
-  bpf.append("BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),")
+  bpf.append("BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRAP),")
   bpf.append("BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),")
 
   # And output policy
diff --git a/linker/Android.bp b/linker/Android.bp
index 6d93571..5c205d5 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -22,6 +22,7 @@
         "linker_gdb_support.cpp",
         "linker_globals.cpp",
         "linker_libc_support.c",
+        "linker_libcxx_support.cpp",
         "linker_main.cpp",
         "linker_namespaces.cpp",
         "linker_logger.cpp",
@@ -111,6 +112,7 @@
 
     static_libs: [
         "libc_nomalloc",
+        "libm",
         "libziparchive",
         "libutils",
         "libbase",
diff --git a/linker/linker_libcxx_support.cpp b/linker/linker_libcxx_support.cpp
new file mode 100644
index 0000000..0efe4d8
--- /dev/null
+++ b/linker/linker_libcxx_support.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "private/libc_logging.h"
+
+void* __find_icu_symbol(const char* symbol_name __attribute__((__unused__))) {
+  __libc_fatal("__find_icu_symbol should not be called in the linker");
+}
diff --git a/tests/Android.bp b/tests/Android.bp
index 64a5c30..2ccdbf8 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -179,32 +179,6 @@
 }
 
 cc_test_library {
-    name: "libfortify1-tests-gcc",
-    defaults: ["bionic_fortify_tests_defaults", "bionic_tests_defaults"],
-    clang: false,
-    cflags: [
-        "-D_FORTIFY_SOURCE=1",
-        "-DTEST_NAME=Fortify1_gcc"
-    ],
-    shared: {
-        enabled: false,
-    },
-}
-
-cc_test_library {
-    name: "libfortify2-tests-gcc",
-    defaults: ["bionic_fortify_tests_defaults", "bionic_tests_defaults"],
-    clang: false,
-    cflags: [
-        "-D_FORTIFY_SOURCE=2",
-        "-DTEST_NAME=Fortify2_gcc"
-    ],
-    shared: {
-        enabled: false,
-    },
-}
-
-cc_test_library {
     name: "libfortify1-tests-clang",
     defaults: ["bionic_fortify_tests_defaults", "bionic_tests_defaults"],
     clang: true,
@@ -238,8 +212,6 @@
     defaults: ["bionic_tests_defaults"],
     whole_static_libs: [
         "libBionicStandardTests",
-        "libfortify1-tests-gcc",
-        "libfortify2-tests-gcc",
         "libfortify1-tests-clang",
         "libfortify2-tests-clang",
     ],
@@ -412,12 +384,6 @@
     },
 }
 
-cc_test {
-    name: "bionic-unit-tests-gcc",
-    defaults: ["bionic_unit_tests_defaults", "bionic_tests_defaults"],
-    clang: false,
-}
-
 // -----------------------------------------------------------------------------
 // Tests for the device linked against bionic's static library. Run with:
 //   adb shell /data/nativetest/bionic-unit-tests-static/bionic-unit-tests-static32
@@ -484,8 +450,6 @@
     whole_static_libs: [
         "libBionicStandardTests",
         "libBionicGtestMain",
-        "libfortify1-tests-gcc",
-        "libfortify2-tests-gcc",
         "libfortify1-tests-clang",
         "libfortify2-tests-clang",
     ],
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index 2e4d635..a56e3a7 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -921,7 +921,25 @@
 #else
 #define PATH_TO_SYSTEM_LIB "/system/lib/"
 #endif
+#if defined (__aarch64__)
+#define ALTERNATE_PATH_TO_SYSTEM_LIB "/system/lib/arm64/"
+#elif defined (__arm__)
+#define ALTERNATE_PATH_TO_SYSTEM_LIB "/system/lib/arm/"
+#elif defined (__i386__)
+#define ALTERNATE_PATH_TO_SYSTEM_LIB "/system/lib/x86/"
+#elif defined (__x86_64__)
+#define ALTERNATE_PATH_TO_SYSTEM_LIB "/system/lib/x86_64/"
+#elif defined (__mips__)
+#if defined(__LP64__)
+#define ALTERNATE_PATH_TO_SYSTEM_LIB "/system/lib/mips64/"
+#else
+#define ALTERNATE_PATH_TO_SYSTEM_LIB "/system/lib/mips/"
+#endif
+#else
+#error "Unknown architecture"
+#endif
 #define PATH_TO_LIBC PATH_TO_SYSTEM_LIB "libc.so"
+#define ALTERNATE_PATH_TO_LIBC ALTERNATE_PATH_TO_SYSTEM_LIB "libc.so"
 
 TEST(dlfcn, dladdr_libc) {
 #if defined(__BIONIC__)
@@ -929,9 +947,18 @@
   void* addr = reinterpret_cast<void*>(puts); // well-known libc function
   ASSERT_TRUE(dladdr(addr, &info) != 0);
 
-  // /system/lib is symlink when this test is executed on host.
   char libc_realpath[PATH_MAX];
-  ASSERT_TRUE(realpath(PATH_TO_LIBC, libc_realpath) == libc_realpath);
+
+  // Check if libc is in canonical path or in alternate path.
+  if (strncmp(ALTERNATE_PATH_TO_SYSTEM_LIB,
+              info.dli_fname,
+              sizeof(ALTERNATE_PATH_TO_SYSTEM_LIB) - 1) == 0) {
+    // Platform with emulated architecture.  Symlink on ARC++.
+    ASSERT_TRUE(realpath(ALTERNATE_PATH_TO_LIBC, libc_realpath) == libc_realpath);
+  } else {
+    // /system/lib is symlink when this test is executed on host.
+    ASSERT_TRUE(realpath(PATH_TO_LIBC, libc_realpath) == libc_realpath);
+  }
 
   ASSERT_STREQ(libc_realpath, info.dli_fname);
   // TODO: add check for dfi_fbase
@@ -1248,8 +1275,14 @@
 }
 
 void validate_compatibility_of_native_library(const char* soname) {
-  std::string path = std::string(PATH_TO_SYSTEM_LIB) + soname;
+  // On the systems with emulation system libraries would be of different
+  // architecture.  Try to use alternate paths first.
+  std::string path = std::string(ALTERNATE_PATH_TO_SYSTEM_LIB) + soname;
   auto binary_or_error = llvm::object::createBinary(path);
+  if (!binary_or_error) {
+    path = std::string(PATH_TO_SYSTEM_LIB) + soname;
+    binary_or_error = llvm::object::createBinary(path);
+  }
   ASSERT_FALSE(!binary_or_error);
 
   llvm::object::Binary* binary = binary_or_error.get().getBinary();
diff --git a/tests/sys_ptrace_test.cpp b/tests/sys_ptrace_test.cpp
index bdd6a89..7483754 100644
--- a/tests/sys_ptrace_test.cpp
+++ b/tests/sys_ptrace_test.cpp
@@ -30,8 +30,8 @@
 #define TRAP_HWBKPT 4
 #endif
 
-template<typename T>
-static void __attribute__((noreturn)) fork_child(unsigned cpu, T &data) {
+template <typename T>
+static void __attribute__((noreturn)) watchpoint_fork_child(unsigned cpu, T& data) {
   // Extra precaution: make sure we go away if anything happens to our parent.
   if (prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0) == -1) {
     perror("prctl(PR_SET_PDEATHSIG)");
@@ -71,7 +71,9 @@
   pid_t pid;
 };
 
-static bool are_watchpoints_supported(pid_t child) {
+enum class HwFeature { Watchpoint, Breakpoint };
+
+static bool is_hw_feature_supported(pid_t child, HwFeature feature) {
 #if defined(__arm__)
   long capabilities;
   long result = ptrace(PTRACE_GETHBPREGS, child, 0, &capabilities);
@@ -79,26 +81,34 @@
     EXPECT_EQ(EIO, errno);
     return false;
   }
-  return ((capabilities >> 8) & 0xff) > 0;
+  switch (feature) {
+    case HwFeature::Watchpoint:
+      return ((capabilities >> 8) & 0xff) > 0;
+    case HwFeature::Breakpoint:
+      return (capabilities & 0xff) > 0;
+  }
 #elif defined(__aarch64__)
   user_hwdebug_state dreg_state;
   iovec iov;
   iov.iov_base = &dreg_state;
   iov.iov_len = sizeof(dreg_state);
 
-  long result = ptrace(PTRACE_GETREGSET, child, NT_ARM_HW_WATCH, &iov);
+  long result = ptrace(PTRACE_GETREGSET, child,
+                       feature == HwFeature::Watchpoint ? NT_ARM_HW_WATCH : NT_ARM_HW_BREAK, &iov);
   if (result == -1) {
     EXPECT_EQ(EINVAL, errno);
     return false;
   }
   return (dreg_state.dbg_info & 0xff) > 0;
 #elif defined(__i386__) || defined(__x86_64__)
-  // We assume watchpoints are always supported on x86.
+  // We assume watchpoints and breakpoints are always supported on x86.
   (void) child;
+  (void)feature;
   return true;
 #else
   // TODO: mips support.
   (void) child;
+  (void)feature;
   return false;
 #endif
 }
@@ -153,7 +163,7 @@
 
   pid_t child = fork();
   ASSERT_NE(-1, child) << strerror(errno);
-  if (child == 0) fork_child(cpu, data);
+  if (child == 0) watchpoint_fork_child(cpu, data);
 
   ChildGuard guard(child);
 
@@ -162,7 +172,10 @@
   ASSERT_TRUE(WIFSTOPPED(status)) << "Status was: " << status;
   ASSERT_EQ(SIGSTOP, WSTOPSIG(status)) << "Status was: " << status;
 
-  if (!are_watchpoints_supported(child)) return;
+  if (!is_hw_feature_supported(child, HwFeature::Watchpoint)) {
+    GTEST_LOG_(INFO) << "Skipping test because hardware support is not available.\n";
+    return;
+  }
 
   set_watchpoint(child, &data, sizeof data);
 
@@ -191,7 +204,7 @@
 // Test watchpoint API. The test is considered successful if our watchpoints get hit OR the
 // system reports that watchpoint support is not present. We run the test for different
 // watchpoint sizes, while pinning the process to each cpu in turn, for better coverage.
-TEST(ptrace, watchpoint_stress) {
+TEST(sys_ptrace, watchpoint_stress) {
   cpu_set_t available_cpus;
   ASSERT_EQ(0, sched_getaffinity(0, sizeof available_cpus, &available_cpus));
 
@@ -200,3 +213,103 @@
     run_watchpoint_test(cpu);
   }
 }
+
+static void __attribute__((noinline)) breakpoint_func() {
+  asm volatile("");
+}
+
+static void __attribute__((noreturn)) breakpoint_fork_child() {
+  // Extra precaution: make sure we go away if anything happens to our parent.
+  if (prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0) == -1) {
+    perror("prctl(PR_SET_PDEATHSIG)");
+    _exit(1);
+  }
+
+  if (ptrace(PTRACE_TRACEME, 0, nullptr, nullptr) == -1) {
+    perror("ptrace(PTRACE_TRACEME)");
+    _exit(2);
+  }
+
+  raise(SIGSTOP);  // Synchronize with the tracer, let it set the breakpoint.
+
+  breakpoint_func();  // Now trigger the breakpoint.
+
+  _exit(0);
+}
+
+static void set_breakpoint(pid_t child) {
+  uintptr_t address = uintptr_t(breakpoint_func);
+#if defined(__arm__) || defined(__aarch64__)
+  address &= ~3;
+  const unsigned byte_mask = 0xf;
+  const unsigned enable = 1;
+  const unsigned control = byte_mask << 5 | enable;
+
+#ifdef __arm__
+  ASSERT_EQ(0, ptrace(PTRACE_SETHBPREGS, child, 1, &address)) << strerror(errno);
+  ASSERT_EQ(0, ptrace(PTRACE_SETHBPREGS, child, 2, &control)) << strerror(errno);
+#else  // aarch64
+  user_hwdebug_state dreg_state;
+  memset(&dreg_state, 0, sizeof dreg_state);
+  dreg_state.dbg_regs[0].addr = reinterpret_cast<uintptr_t>(address);
+  dreg_state.dbg_regs[0].ctrl = control;
+
+  iovec iov;
+  iov.iov_base = &dreg_state;
+  iov.iov_len = offsetof(user_hwdebug_state, dbg_regs) + sizeof(dreg_state.dbg_regs[0]);
+
+  ASSERT_EQ(0, ptrace(PTRACE_SETREGSET, child, NT_ARM_HW_BREAK, &iov)) << strerror(errno);
+#endif
+#elif defined(__i386__) || defined(__x86_64__)
+  ASSERT_EQ(0, ptrace(PTRACE_POKEUSER, child, offsetof(user, u_debugreg[0]), address))
+      << strerror(errno);
+  errno = 0;
+  unsigned data = ptrace(PTRACE_PEEKUSER, child, offsetof(user, u_debugreg[7]), nullptr);
+  ASSERT_EQ(0, errno);
+
+  const unsigned size = 0;
+  const unsigned enable = 1;
+  const unsigned type = 0;  // Execute
+
+  const unsigned mask = 3 << 18 | 3 << 16 | 1;
+  const unsigned value = size << 18 | type << 16 | enable;
+  data &= mask;
+  data |= value;
+  ASSERT_EQ(0, ptrace(PTRACE_POKEUSER, child, offsetof(user, u_debugreg[7]), data))
+      << strerror(errno);
+#else
+  (void)child;
+  (void)address;
+#endif
+}
+
+// Test hardware breakpoint API. The test is considered successful if the breakpoints get hit OR the
+// system reports that hardware breakpoint support is not present.
+TEST(sys_ptrace, hardware_breakpoint) {
+  pid_t child = fork();
+  ASSERT_NE(-1, child) << strerror(errno);
+  if (child == 0) breakpoint_fork_child();
+
+  ChildGuard guard(child);
+
+  int status;
+  ASSERT_EQ(child, waitpid(child, &status, __WALL)) << strerror(errno);
+  ASSERT_TRUE(WIFSTOPPED(status)) << "Status was: " << status;
+  ASSERT_EQ(SIGSTOP, WSTOPSIG(status)) << "Status was: " << status;
+
+  if (!is_hw_feature_supported(child, HwFeature::Breakpoint)) {
+    GTEST_LOG_(INFO) << "Skipping test because hardware support is not available.\n";
+    return;
+  }
+
+  set_breakpoint(child);
+
+  ASSERT_EQ(0, ptrace(PTRACE_CONT, child, nullptr, nullptr)) << strerror(errno);
+  ASSERT_EQ(child, waitpid(child, &status, __WALL)) << strerror(errno);
+  ASSERT_TRUE(WIFSTOPPED(status)) << "Status was: " << status;
+  ASSERT_EQ(SIGTRAP, WSTOPSIG(status)) << "Status was: " << status;
+
+  siginfo_t siginfo;
+  ASSERT_EQ(0, ptrace(PTRACE_GETSIGINFO, child, nullptr, &siginfo)) << strerror(errno);
+  ASSERT_EQ(TRAP_HWBKPT, siginfo.si_code);
+}