1 /** 2 * Basic D language bindings for LLVM libunwind 3 * 4 * There are two available libunwind: The "upstream" one, inherited 5 * from HP, which is maintained as a GNU project, 6 * and the LLVM one, part of llvm-project, and the default on Mac OSX. 7 * 8 * They are both essential part of other languages ABI, and are available 9 * in both GCC and LLVM. However, in GCC, only the higher-level functions 10 * are exposed (e.g. `_Unwind_*`) while LLVM expose the higher-level 11 * and lower-level (`unw_*`) functions. 12 * Many distributions have a `libunwind` package as well, that provides 13 * the `unw_*` functions, but since it also supports remote unwinding, 14 * the function names are actually platform dependent and binding them 15 * is a pain as many things rely on `#define`. 16 * 17 * In the future, we would like to implement backtrace using only the 18 * higher-level functions (`_Unwind_*`), which will allow us to not 19 * use `backtrace` and friends directly, and only retrieve the functions 20 * names when needed (currently we need to eagerly get the functions names). 21 * 22 * Authors: Mathias 'Geod24' Lang 23 * Copyright: D Language Foundation - 2020 24 * See_Also: 25 * - https://www.nongnu.org/libunwind/man/libunwind(3).html 26 * - https://clang.llvm.org/docs/Toolchain.html#unwind-library 27 */ 28 module core.internal.backtrace.libunwind; 29 30 version (DRuntime_Use_Libunwind): 31 32 // Libunwind supports Windows as well, but we currently use a different 33 // mechanism for Windows, so the bindings haven't been brought in yet. 34 version (Posix): 35 36 import core.stdc.inttypes; 37 38 extern(C): 39 @nogc: 40 nothrow: 41 42 /* 43 * Bindings for libunwind.h 44 */ 45 alias unw_word_t = uintptr_t; 46 47 /// 48 struct unw_context_t 49 { 50 ulong[_LIBUNWIND_CONTEXT_SIZE] data = void; 51 } 52 53 /// 54 struct unw_cursor_t 55 { 56 ulong[_LIBUNWIND_CURSOR_SIZE] data = void; 57 } 58 59 /// 60 struct unw_proc_info_t 61 { 62 unw_word_t start_ip; /* start address of function */ 63 unw_word_t end_ip; /* address after end of function */ 64 unw_word_t lsda; /* address of language specific data area, */ 65 /* or zero if not used */ 66 unw_word_t handler; /* personality routine, or zero if not used */ 67 unw_word_t gp; /* not used */ 68 unw_word_t flags; /* not used */ 69 uint format; /* compact unwind encoding, or zero if none */ 70 uint unwind_info_size; /* size of DWARF unwind info, or zero if none */ 71 // Note: It's a `void*` with LLVM and a `unw_word_t` with upstream 72 unw_word_t unwind_info; /* address of DWARF unwind info, or zero */ 73 // Note: upstream might not have this member at all, or it might be a single 74 // byte, however we never pass an array of this type, so this is safe to 75 // just use the bigger (LLVM's) value. 76 unw_word_t extra; /* mach_header of mach-o image containing func */ 77 } 78 79 /// Initialize the context at the current call site 80 int unw_getcontext(unw_context_t*); 81 /// Initialize a cursor at the call site 82 int unw_init_local(unw_cursor_t*, unw_context_t*); 83 /// Goes one level up in the call chain 84 int unw_step(unw_cursor_t*); 85 /// Get infos about the current procedure (function) 86 int unw_get_proc_info(unw_cursor_t*, unw_proc_info_t*); 87 /// Get the name of the current procedure (function) 88 int unw_get_proc_name(unw_cursor_t*, char*, size_t, unw_word_t*); 89 90 private: 91 92 // The API between libunwind and llvm-libunwind is almost the same, 93 // at least for our use case, and only the struct size change, 94 // so handle the difference here. 95 // Upstream: https://github.com/libunwind/libunwind/tree/master/include 96 // LLVM: https://github.com/llvm/llvm-project/blob/20c926e0797e074bfb946d2c8ce002888ebc2bcd/libunwind/include/__libunwind_config.h#L29-L141 97 version (X86) 98 { 99 enum _LIBUNWIND_CONTEXT_SIZE = 8; 100 101 version (Android) 102 enum _LIBUNWIND_CURSOR_SIZE = 19; // NDK r21 103 else 104 enum _LIBUNWIND_CURSOR_SIZE = 15; 105 } 106 else version (X86_64) 107 { 108 version (Win64) 109 { 110 enum _LIBUNWIND_CONTEXT_SIZE = 54; 111 // # ifdef __SEH__ 112 // # define _LIBUNWIND_CURSOR_SIZE 204 113 enum _LIBUNWIND_CURSOR_SIZE = 66; 114 } else { 115 enum _LIBUNWIND_CONTEXT_SIZE = 21; 116 enum _LIBUNWIND_CURSOR_SIZE = 33; 117 } 118 } 119 else version (PPC64) 120 { 121 enum _LIBUNWIND_CONTEXT_SIZE = 167; 122 enum _LIBUNWIND_CURSOR_SIZE = 179; 123 } 124 else version (PPC) 125 { 126 enum _LIBUNWIND_CONTEXT_SIZE = 117; 127 enum _LIBUNWIND_CURSOR_SIZE = 124; 128 } 129 else version (AArch64) 130 { 131 enum _LIBUNWIND_CONTEXT_SIZE = 66; 132 // # if defined(__SEH__) 133 // # define _LIBUNWIND_CURSOR_SIZE 164 134 enum _LIBUNWIND_CURSOR_SIZE = 78; 135 } 136 else version (ARM) 137 { 138 // # if defined(__SEH__) 139 // # define _LIBUNWIND_CONTEXT_SIZE 42 140 // # define _LIBUNWIND_CURSOR_SIZE 80 141 // # elif defined(__ARM_WMMX) 142 // # define _LIBUNWIND_CONTEXT_SIZE 61 143 // # define _LIBUNWIND_CURSOR_SIZE 68 144 enum _LIBUNWIND_CONTEXT_SIZE = 42; 145 enum _LIBUNWIND_CURSOR_SIZE = 49; 146 } 147 else version (SPARC) 148 { 149 enum _LIBUNWIND_CONTEXT_SIZE = 16; 150 enum _LIBUNWIND_CURSOR_SIZE = 23; 151 } 152 else version (RISCV64) // 32 is not supported 153 { 154 enum _LIBUNWIND_CONTEXT_SIZE = 64; 155 enum _LIBUNWIND_CURSOR_SIZE = 76; 156 } 157 else 158 static assert(0, "Platform not supported");