Daniel Cheng | ed0471b | 2019-05-10 11:43:36 | [diff] [blame^] | 1 | // Copyright 2019 The Chromium 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 BASE_IMMEDIATE_CRASH_H_ |
| 6 | #define BASE_IMMEDIATE_CRASH_H_ |
| 7 | |
| 8 | #include "build/build_config.h" |
| 9 | |
| 10 | // Crashes in the fastest possible way with no attempt at logging. |
| 11 | // There are different constraints to satisfy here, see http://crbug.com/664209 |
| 12 | // for more context: |
| 13 | // - The trap instructions, and hence the PC value at crash time, have to be |
| 14 | // distinct and not get folded into the same opcode by the compiler. |
| 15 | // On Linux/Android this is tricky because GCC still folds identical |
| 16 | // asm volatile blocks. The workaround is generating distinct opcodes for |
| 17 | // each CHECK using the __COUNTER__ macro. |
| 18 | // - The debug info for the trap instruction has to be attributed to the source |
| 19 | // line that has the CHECK(), to make crash reports actionable. This rules |
| 20 | // out the ability of using a inline function, at least as long as clang |
| 21 | // doesn't support attribute(artificial). |
| 22 | // - Failed CHECKs should produce a signal that is distinguishable from an |
| 23 | // invalid memory access, to improve the actionability of crash reports. |
| 24 | // - The compiler should treat the CHECK as no-return instructions, so that the |
| 25 | // trap code can be efficiently packed in the prologue of the function and |
| 26 | // doesn't interfere with the main execution flow. |
| 27 | // - When debugging, developers shouldn't be able to accidentally step over a |
| 28 | // CHECK. This is achieved by putting opcodes that will cause a non |
| 29 | // continuable exception after the actual trap instruction. |
| 30 | // - Don't cause too much binary bloat. |
| 31 | #if defined(COMPILER_GCC) |
| 32 | |
| 33 | #if defined(ARCH_CPU_X86_FAMILY) && !defined(OS_NACL) |
| 34 | // int 3 will generate a SIGTRAP. |
| 35 | #define TRAP_SEQUENCE() \ |
| 36 | asm volatile( \ |
| 37 | "int3; ud2; push %0;" ::"i"(static_cast<unsigned char>(__COUNTER__))) |
| 38 | |
| 39 | #elif defined(ARCH_CPU_ARMEL) && !defined(OS_NACL) |
| 40 | // bkpt will generate a SIGBUS when running on armv7 and a SIGTRAP when running |
| 41 | // as a 32 bit userspace app on arm64. There doesn't seem to be any way to |
| 42 | // cause a SIGTRAP from userspace without using a syscall (which would be a |
| 43 | // problem for sandboxing). |
| 44 | #define TRAP_SEQUENCE() \ |
| 45 | asm volatile("bkpt #0; udf %0;" ::"i"(__COUNTER__ % 256)) |
| 46 | |
| 47 | #elif defined(ARCH_CPU_ARM64) && !defined(OS_NACL) |
| 48 | // This will always generate a SIGTRAP on arm64. |
| 49 | #define TRAP_SEQUENCE() \ |
| 50 | asm volatile("brk #0; hlt %0;" ::"i"(__COUNTER__ % 65536)) |
| 51 | |
| 52 | #else |
| 53 | // Crash report accuracy will not be guaranteed on other architectures, but at |
| 54 | // least this will crash as expected. |
| 55 | #define TRAP_SEQUENCE() __builtin_trap() |
| 56 | #endif // ARCH_CPU_* |
| 57 | |
| 58 | #elif defined(COMPILER_MSVC) |
| 59 | |
| 60 | // Clang is cleverer about coalescing int3s, so we need to add a unique-ish |
| 61 | // instruction following the __debugbreak() to have it emit distinct locations |
| 62 | // for CHECKs rather than collapsing them all together. It would be nice to use |
| 63 | // a short intrinsic to do this (and perhaps have only one implementation for |
| 64 | // both clang and MSVC), however clang-cl currently does not support intrinsics. |
| 65 | // On the flip side, MSVC x64 doesn't support inline asm. So, we have to have |
| 66 | // two implementations. Normally clang-cl's version will be 5 bytes (1 for |
| 67 | // `int3`, 2 for `ud2`, 2 for `push byte imm`, however, TODO(scottmg): |
| 68 | // https://crbug.com/694670 clang-cl doesn't currently support %'ing |
| 69 | // __COUNTER__, so eventually it will emit the dword form of push. |
| 70 | // TODO(scottmg): Reinvestigate a short sequence that will work on both |
| 71 | // compilers once clang supports more intrinsics. See https://crbug.com/693713. |
| 72 | #if !defined(__clang__) |
| 73 | #define TRAP_SEQUENCE() __debugbreak() |
| 74 | #elif defined(ARCH_CPU_ARM64) |
| 75 | #define TRAP_SEQUENCE() \ |
| 76 | __asm volatile("brk #0\n hlt %0\n" ::"i"(__COUNTER__ % 65536)); |
| 77 | #else |
| 78 | #define TRAP_SEQUENCE() ({ {__asm int 3 __asm ud2 __asm push __COUNTER__}; }) |
| 79 | #endif // __clang__ |
| 80 | |
| 81 | #else |
| 82 | #error Port |
| 83 | #endif // COMPILER_GCC |
| 84 | |
| 85 | // CHECK() and the trap sequence can be invoked from a constexpr function. |
| 86 | // This could make compilation fail on GCC, as it forbids directly using inline |
| 87 | // asm inside a constexpr function. However, it allows calling a lambda |
| 88 | // expression including the same asm. |
| 89 | // The side effect is that the top of the stacktrace will not point to the |
| 90 | // calling function, but to this anonymous lambda. This is still useful as the |
| 91 | // full name of the lambda will typically include the name of the function that |
| 92 | // calls CHECK() and the debugger will still break at the right line of code. |
| 93 | #if !defined(COMPILER_GCC) |
| 94 | #define WRAPPED_TRAP_SEQUENCE() TRAP_SEQUENCE() |
| 95 | #else |
| 96 | #define WRAPPED_TRAP_SEQUENCE() \ |
| 97 | do { \ |
| 98 | [] { TRAP_SEQUENCE(); }(); \ |
| 99 | } while (false) |
| 100 | #endif |
| 101 | |
| 102 | #if defined(__clang__) || defined(COMPILER_GCC) |
| 103 | #define IMMEDIATE_CRASH() \ |
| 104 | ({ \ |
| 105 | WRAPPED_TRAP_SEQUENCE(); \ |
| 106 | __builtin_unreachable(); \ |
| 107 | }) |
| 108 | #else |
| 109 | // This is supporting non-chromium user of logging.h to build with MSVC, like |
| 110 | // pdfium. On MSVC there is no __builtin_unreachable(). |
| 111 | #define IMMEDIATE_CRASH() WRAPPED_TRAP_SEQUENCE() |
| 112 | #endif |
| 113 | |
| 114 | #endif // BASE_IMMEDIATE_CRASH_H_ |