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_HOST_ARCH_X64 || V8_HOST_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_DARWIN 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_DARWIN 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_ 163