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");