|
| 1 | +// Copyright 2021 the V8 project authors. All rights reserved. |
| 2 | +// Use of this source code is governed by a BSD-style license that can be |
| 3 | +// found in the LICENSE file. |
| 4 | + |
| 5 | +#ifndef V8_BASE_IMMEDIATE_CRASH_H_ |
| 6 | +#define V8_BASE_IMMEDIATE_CRASH_H_ |
| 7 | + |
| 8 | +#include "include/v8config.h" |
| 9 | +#include "src/base/build_config.h" |
| 10 | + |
| 11 | +// Crashes in the fastest possible way with no attempt at logging. |
| 12 | +// There are several constraints; see http://crbug.com/664209 for more context. |
| 13 | +// |
| 14 | +// - TRAP_SEQUENCE_() must be fatal. It should not be possible to ignore the |
| 15 | +// resulting exception or simply hit 'continue' to skip over it in a debugger. |
| 16 | +// - Different instances of TRAP_SEQUENCE_() must not be folded together, to |
| 17 | +// ensure crash reports are debuggable. Unlike __builtin_trap(), asm volatile |
| 18 | +// blocks will not be folded together. |
| 19 | +// Note: TRAP_SEQUENCE_() previously required an instruction with a unique |
| 20 | +// nonce since unlike clang, GCC folds together identical asm volatile |
| 21 | +// blocks. |
| 22 | +// - TRAP_SEQUENCE_() must produce a signal that is distinct from an invalid |
| 23 | +// memory access. |
| 24 | +// - TRAP_SEQUENCE_() must be treated as a set of noreturn instructions. |
| 25 | +// __builtin_unreachable() is used to provide that hint here. clang also uses |
| 26 | +// this as a heuristic to pack the instructions in the function epilogue to |
| 27 | +// improve code density. |
| 28 | +// |
| 29 | +// Additional properties that are nice to have: |
| 30 | +// - TRAP_SEQUENCE_() should be as compact as possible. |
| 31 | +// - The first instruction of TRAP_SEQUENCE_() should not change, to avoid |
| 32 | +// shifting crash reporting clusters. As a consequence of this, explicit |
| 33 | +// assembly is preferred over intrinsics. |
| 34 | +// Note: this last bullet point may no longer be true, and may be removed in |
| 35 | +// the future. |
| 36 | + |
| 37 | +// Note: TRAP_SEQUENCE Is currently split into two macro helpers due to the fact |
| 38 | +// that clang emits an actual instruction for __builtin_unreachable() on certain |
| 39 | +// platforms (see https://crbug.com/958675). In addition, the int3/bkpt/brk will |
| 40 | +// be removed in followups, so splitting it up like this now makes it easy to |
| 41 | +// land the followups. |
| 42 | + |
| 43 | +#if V8_CC_GNU |
| 44 | + |
| 45 | +#if V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_IA32 |
| 46 | + |
| 47 | +// TODO(https://crbug.com/958675): In theory, it should be possible to use just |
| 48 | +// int3. However, there are a number of crashes with SIGILL as the exception |
| 49 | +// code, so it seems likely that there's a signal handler that allows execution |
| 50 | +// to continue after SIGTRAP. |
| 51 | +#define TRAP_SEQUENCE1_() asm volatile("int3") |
| 52 | + |
| 53 | +#if V8_OS_MACOSX |
| 54 | +// Intentionally empty: __builtin_unreachable() is always part of the sequence |
| 55 | +// (see IMMEDIATE_CRASH below) and already emits a ud2 on Mac. |
| 56 | +#define TRAP_SEQUENCE2_() asm volatile("") |
| 57 | +#else |
| 58 | +#define TRAP_SEQUENCE2_() asm volatile("ud2") |
| 59 | +#endif // V8_OS_MACOSX |
| 60 | + |
| 61 | +#elif V8_HOST_ARCH_ARM |
| 62 | + |
| 63 | +// bkpt will generate a SIGBUS when running on armv7 and a SIGTRAP when running |
| 64 | +// as a 32 bit userspace app on arm64. There doesn't seem to be any way to |
| 65 | +// cause a SIGTRAP from userspace without using a syscall (which would be a |
| 66 | +// problem for sandboxing). |
| 67 | +// TODO(https://crbug.com/958675): Remove bkpt from this sequence. |
| 68 | +#define TRAP_SEQUENCE1_() asm volatile("bkpt #0") |
| 69 | +#define TRAP_SEQUENCE2_() asm volatile("udf #0") |
| 70 | + |
| 71 | +#elif V8_HOST_ARCH_ARM64 |
| 72 | + |
| 73 | +// This will always generate a SIGTRAP on arm64. |
| 74 | +// TODO(https://crbug.com/958675): Remove brk from this sequence. |
| 75 | +#define TRAP_SEQUENCE1_() asm volatile("brk #0") |
| 76 | +#define TRAP_SEQUENCE2_() asm volatile("hlt #0") |
| 77 | + |
| 78 | +#else |
| 79 | + |
| 80 | +// Crash report accuracy will not be guaranteed on other architectures, but at |
| 81 | +// least this will crash as expected. |
| 82 | +#define TRAP_SEQUENCE1_() __builtin_trap() |
| 83 | +#define TRAP_SEQUENCE2_() asm volatile("") |
| 84 | + |
| 85 | +#endif // V8_HOST_ARCH_* |
| 86 | + |
| 87 | +#elif V8_CC_MSVC |
| 88 | + |
| 89 | +#if !defined(__clang__) |
| 90 | + |
| 91 | +// MSVC x64 doesn't support inline asm, so use the MSVC intrinsic. |
| 92 | +#define TRAP_SEQUENCE1_() __debugbreak() |
| 93 | +#define TRAP_SEQUENCE2_() |
| 94 | + |
| 95 | +#elif V8_HOST_ARCH_ARM64 |
| 96 | + |
| 97 | +// Windows ARM64 uses "BRK #F000" as its breakpoint instruction, and |
| 98 | +// __debugbreak() generates that in both VC++ and clang. |
| 99 | +#define TRAP_SEQUENCE1_() __debugbreak() |
| 100 | +// Intentionally empty: __builtin_unreachable() is always part of the sequence |
| 101 | +// (see IMMEDIATE_CRASH below) and already emits a ud2 on Win64, |
| 102 | +// https://crbug.com/958373 |
| 103 | +#define TRAP_SEQUENCE2_() __asm volatile("") |
| 104 | + |
| 105 | +#else |
| 106 | + |
| 107 | +#define TRAP_SEQUENCE1_() asm volatile("int3") |
| 108 | +#define TRAP_SEQUENCE2_() asm volatile("ud2") |
| 109 | + |
| 110 | +#endif // __clang__ |
| 111 | + |
| 112 | +#else |
| 113 | + |
| 114 | +#error No supported trap sequence! |
| 115 | + |
| 116 | +#endif // V8_CC_GNU |
| 117 | + |
| 118 | +#define TRAP_SEQUENCE_() \ |
| 119 | + do { \ |
| 120 | + TRAP_SEQUENCE1_(); \ |
| 121 | + TRAP_SEQUENCE2_(); \ |
| 122 | + } while (false) |
| 123 | + |
| 124 | +// CHECK() and the trap sequence can be invoked from a constexpr function. |
| 125 | +// This could make compilation fail on GCC, as it forbids directly using inline |
| 126 | +// asm inside a constexpr function. However, it allows calling a lambda |
| 127 | +// expression including the same asm. |
| 128 | +// The side effect is that the top of the stacktrace will not point to the |
| 129 | +// calling function, but to this anonymous lambda. This is still useful as the |
| 130 | +// full name of the lambda will typically include the name of the function that |
| 131 | +// calls CHECK() and the debugger will still break at the right line of code. |
| 132 | +#if !V8_CC_GNU |
| 133 | + |
| 134 | +#define WRAPPED_TRAP_SEQUENCE_() TRAP_SEQUENCE_() |
| 135 | + |
| 136 | +#else |
| 137 | + |
| 138 | +#define WRAPPED_TRAP_SEQUENCE_() \ |
| 139 | + do { \ |
| 140 | + [] { TRAP_SEQUENCE_(); }(); \ |
| 141 | + } while (false) |
| 142 | + |
| 143 | +#endif // !V8_CC_GCC |
| 144 | + |
| 145 | +#if defined(__clang__) || V8_CC_GCC |
| 146 | + |
| 147 | +// __builtin_unreachable() hints to the compiler that this is noreturn and can |
| 148 | +// be packed in the function epilogue. |
| 149 | +#define IMMEDIATE_CRASH() \ |
| 150 | + ({ \ |
| 151 | + WRAPPED_TRAP_SEQUENCE_(); \ |
| 152 | + __builtin_unreachable(); \ |
| 153 | + }) |
| 154 | + |
| 155 | +#else |
| 156 | + |
| 157 | +// This is supporting build with MSVC where there is no __builtin_unreachable(). |
| 158 | +#define IMMEDIATE_CRASH() WRAPPED_TRAP_SEQUENCE_() |
| 159 | + |
| 160 | +#endif // defined(__clang__) || defined(COMPILER_GCC) |
| 161 | + |
| 162 | +#endif // V8_BASE_IMMEDIATE_CRASH_H_ |
0 commit comments