Reland "Add test to verify the opcodes generated by IMMEDIATE_CRASH()."

This is a reland of 28eb992fafc5cdffe372d132fc014e3975404817

Fixes:
- Disable sanitizers in the test helper. The test is intended to test
  baseline behavior with max optimizations/undefined behavior, and the
  assumption is that sanitized builds will, in fact, be saner.
- Relax the nonce check, as the compiler can reorder IMMEDIATE_CRASH()
  blocks in the function epilogue as it wants.

Original change's description:
> Add test to verify the opcodes generated by IMMEDIATE_CRASH().
>
> Bug: 958675
> Change-Id: Ica6b5bab7849f227567d8016354a39383533799f
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1595585
> Commit-Queue: Daniel Cheng <[email protected]>
> Reviewed-by: Nico Weber <[email protected]>
> Cr-Commit-Position: refs/heads/master@{#658344}

Bug: 958675
Change-Id: Ic1af1994909d58427cc12f1f9aee2765aa5b449c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1604487
Commit-Queue: Nico Weber <[email protected]>
Reviewed-by: Nico Weber <[email protected]>
Cr-Commit-Position: refs/heads/master@{#658523}
diff --git a/base/immediate_crash_unittest.cc b/base/immediate_crash_unittest.cc
new file mode 100644
index 0000000..d733013
--- /dev/null
+++ b/base/immediate_crash_unittest.cc
@@ -0,0 +1,169 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/immediate_crash.h"
+
+#include <stdint.h>
+
+#include <algorithm>
+
+#include "base/containers/span.h"
+#include "base/files/file_path.h"
+#include "base/native_library.h"
+#include "base/optional.h"
+#include "base/strings/string_number_conversions.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+// iOS is excluded, since it doesn't support loading shared libraries.
+#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) ||      \
+    defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_CHROMEOS) || \
+    defined(OS_FUCHSIA)
+
+// Checks that the IMMEDIATE_CRASH() macro produces specific instructions; see
+// comments in immediate_crash.h for the requirements.
+TEST(ImmediateCrashTest, ExpectedOpcodeSequence) {
+  // TestFunction1() and TestFunction2() are defined in a shared library in an
+  // attempt to guarantee that they are located next to each other.
+  NativeLibraryLoadError load_error;
+  // TODO(dcheng): Shouldn't GetNativeLibraryName just return a FilePath?
+  NativeLibrary helper_library = LoadNativeLibrary(
+      FilePath::FromUTF8Unsafe(
+          GetNativeLibraryName("immediate_crash_test_helper")),
+      &load_error);
+  ASSERT_TRUE(helper_library)
+      << "shared library load failed: " << load_error.ToString();
+
+  // TestFunction1() and TestFunction2() each contain two IMMEDIATE_CRASH()
+  // invocations. IMMEDIATE_CRASH() should be treated as a noreturn sequence and
+  // optimized into the function epilogue. The general strategy is to find the
+  // return opcode, then scan the following bytes for the opcodes for two
+  // consecutive IMMEDIATE_CRASH() sequences.
+  void* a =
+      GetFunctionPointerFromNativeLibrary(helper_library, "TestFunction1");
+  ASSERT_TRUE(a);
+  void* b =
+      GetFunctionPointerFromNativeLibrary(helper_library, "TestFunction2");
+  ASSERT_TRUE(b);
+
+#if defined(ARCH_CPU_X86_FAMILY)
+
+  // X86 opcode reference:
+  // https://software.intel.com/en-us/download/intel-64-and-ia-32-architectures-sdm-combined-volumes-1-2a-2b-2c-2d-3a-3b-3c-3d-and-4
+  span<const uint8_t> function_body =
+      a < b ? make_span(static_cast<const uint8_t*>(a),
+                        static_cast<const uint8_t*>(b))
+            : make_span(static_cast<const uint8_t*>(b),
+                        static_cast<const uint8_t*>(a));
+  SCOPED_TRACE(HexEncode(function_body.data(), function_body.size_bytes()));
+
+  // Look for RETN opcode (0xC3). Note that 0xC3 is a substring of several
+  // other opcodes (VMRESUME, MOVNTI), and can also be encoded as part of an
+  // argument to another opcode. None of these other cases are expected to be
+  // present, so a simple byte scan should be Good Enough™.
+  auto it = std::find(function_body.begin(), function_body.end(), 0xC3);
+  ASSERT_NE(function_body.end(), it) << "Failed to find return! ";
+
+  // Look for two IMMEDIATE_CRASH() opcode sequences.
+  base::Optional<uint8_t> nonce;
+  for (int i = 0; i < 2; ++i) {
+    // INT 3
+    EXPECT_EQ(0xCC, *++it);
+    // UD2
+    EXPECT_EQ(0x0F, *++it);
+    EXPECT_EQ(0x0B, *++it);
+    // PUSH
+    EXPECT_EQ(0x6A, *++it);
+    // Immediate nonce argument to PUSH
+    if (!nonce) {
+      nonce = *++it;
+    } else {
+      EXPECT_NE(*nonce, *++it);
+    }
+#if (defined(OS_WIN) && defined(ARCH_CPU_64_BITS)) || defined(OS_MACOSX)
+    // On Windows x64 and Mac, __builtin_unreachable() generates UD2. See
+    // https://crbug.com/958373.
+    EXPECT_EQ(0x0F, *++it);
+    EXPECT_EQ(0x0B, *++it);
+#endif  // defined(OS_WIN) || defined(OS_MACOSX)
+  }
+
+#elif defined(ARCH_CPU_ARMEL)
+
+  // Routines loaded from a shared library will have the LSB in the pointer set
+  // if encoded as T32 instructions. The rest of this test assumes T32.
+  ASSERT_TRUE(reinterpret_cast<uintptr_t>(a) & 0x1)
+      << "Expected T32 opcodes but found A32 opcodes instead.";
+  ASSERT_TRUE(reinterpret_cast<uintptr_t>(b) & 0x1)
+      << "Expected T32 opcodes but found A32 opcodes instead.";
+
+  // Mask off the lowest bit.
+  a = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(a) & ~uintptr_t{0x1});
+  b = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(b) & ~uintptr_t{0x1});
+
+  // T32 opcode reference: https://developer.arm.com/docs/ddi0487/latest
+  span<const uint16_t> function_body =
+      a < b ? make_span(static_cast<const uint16_t*>(a),
+                        static_cast<const uint16_t*>(b))
+            : make_span(static_cast<const uint16_t*>(b),
+                        static_cast<const uint16_t*>(a));
+  SCOPED_TRACE(HexEncode(function_body.data(), function_body.size_bytes()));
+
+  // Look for the standard return opcode sequence (BX LR).
+  auto it = std::find(function_body.begin(), function_body.end(), 0x4770);
+  ASSERT_NE(function_body.end(), it) << "Failed to find return! ";
+
+  // Look for two IMMEDIATE_CRASH() opcode sequences.
+  base::Optional<uint8_t> nonce;
+  for (int i = 0; i < 2; ++i) {
+    // BKPT #0
+    EXPECT_EQ(0xBE00, *++it);
+    // UDF #<nonce>
+    EXPECT_EQ(0xDE00, *++it & 0xFF00);
+    if (!nonce) {
+      nonce = *it & 0x00FF;
+    } else {
+      EXPECT_NE(*nonce, *it & 0x00FF);
+    }
+  }
+
+#elif defined(ARCH_CPU_ARM64)
+
+  // A64 opcode reference: https://developer.arm.com/docs/ddi0487/latest
+  span<const uint32_t> function_body =
+      a < b ? make_span(static_cast<const uint32_t*>(a),
+                        static_cast<const uint32_t*>(b))
+            : make_span(static_cast<const uint32_t*>(b),
+                        static_cast<const uint32_t*>(a));
+  SCOPED_TRACE(HexEncode(function_body.data(), function_body.size_bytes()));
+
+  // Look for RET. There appears to be multiple valid encodings, so this is
+  // hardcoded to whatever clang currently emits...
+  auto it = std::find(function_body.begin(), function_body.end(), 0XD65F03C0);
+  ASSERT_NE(function_body.end(), it) << "Failed to find return! ";
+
+  // Look for two IMMEDIATE_CRASH() opcode sequences.
+  base::Optional<uint16_t> nonce;
+  for (int i = 0; i < 2; ++i) {
+    // BRK #0
+    EXPECT_EQ(0XD4200000, *++it);
+    // HLT #<nonce>
+    EXPECT_EQ(0xD4400000, *++it & 0xFFE00000);
+    if (!nonce) {
+      nonce = (*it >> 5) & 0xFFFF;
+    } else {
+      EXPECT_NE(*nonce, (*it >> 5) & 0xFFFF);
+    }
+  }
+
+#endif  // defined(ARCH_CPU_X86_FAMILY)
+
+  UnloadNativeLibrary(helper_library);
+}
+
+#endif
+
+}  // namespace base