]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/coredump/stacktrace.c
Merge pull request #13635 from fbuihuu/no-aliases-with-enable
[thirdparty/systemd.git] / src / coredump / stacktrace.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <dwarf.h>
4 #include <elfutils/libdwfl.h>
5 #include <sys/types.h>
6 #include <unistd.h>
7
8 #include "alloc-util.h"
9 #include "fileio.h"
10 #include "fd-util.h"
11 #include "format-util.h"
12 #include "macro.h"
13 #include "stacktrace.h"
14 #include "string-util.h"
15 #include "util.h"
16
17 #define FRAMES_MAX 64
18 #define THREADS_MAX 64
19
20 struct stack_context {
21 FILE *f;
22 Dwfl *dwfl;
23 Elf *elf;
24 unsigned n_thread;
25 unsigned n_frame;
26 };
27
28 static int frame_callback(Dwfl_Frame *frame, void *userdata) {
29 struct stack_context *c = userdata;
30 Dwarf_Addr pc, pc_adjusted, bias = 0;
31 _cleanup_free_ Dwarf_Die *scopes = NULL;
32 const char *fname = NULL, *symbol = NULL;
33 Dwfl_Module *module;
34 bool is_activation;
35 uint64_t module_offset = 0;
36
37 assert(frame);
38 assert(c);
39
40 if (c->n_frame >= FRAMES_MAX)
41 return DWARF_CB_ABORT;
42
43 if (!dwfl_frame_pc(frame, &pc, &is_activation))
44 return DWARF_CB_ABORT;
45
46 pc_adjusted = pc - (is_activation ? 0 : 1);
47
48 module = dwfl_addrmodule(c->dwfl, pc_adjusted);
49 if (module) {
50 Dwarf_Die *s, *cudie;
51 int n;
52 Dwarf_Addr start;
53
54 cudie = dwfl_module_addrdie(module, pc_adjusted, &bias);
55 if (cudie) {
56 n = dwarf_getscopes(cudie, pc_adjusted - bias, &scopes);
57 for (s = scopes; s < scopes + n; s++) {
58 if (IN_SET(dwarf_tag(s), DW_TAG_subprogram, DW_TAG_inlined_subroutine, DW_TAG_entry_point)) {
59 Dwarf_Attribute *a, space;
60
61 a = dwarf_attr_integrate(s, DW_AT_MIPS_linkage_name, &space);
62 if (!a)
63 a = dwarf_attr_integrate(s, DW_AT_linkage_name, &space);
64 if (a)
65 symbol = dwarf_formstring(a);
66 if (!symbol)
67 symbol = dwarf_diename(s);
68
69 if (symbol)
70 break;
71 }
72 }
73 }
74
75 if (!symbol)
76 symbol = dwfl_module_addrname(module, pc_adjusted);
77
78 fname = dwfl_module_info(module, NULL, &start, NULL, NULL, NULL, NULL, NULL);
79 module_offset = pc - start;
80 }
81
82 fprintf(c->f, "#%-2u 0x%016" PRIx64 " %s (%s + 0x%" PRIx64 ")\n", c->n_frame, (uint64_t) pc, strna(symbol), strna(fname), module_offset);
83 c->n_frame++;
84
85 return DWARF_CB_OK;
86 }
87
88 static int thread_callback(Dwfl_Thread *thread, void *userdata) {
89 struct stack_context *c = userdata;
90 pid_t tid;
91
92 assert(thread);
93 assert(c);
94
95 if (c->n_thread >= THREADS_MAX)
96 return DWARF_CB_ABORT;
97
98 if (c->n_thread != 0)
99 fputc('\n', c->f);
100
101 c->n_frame = 0;
102
103 tid = dwfl_thread_tid(thread);
104 fprintf(c->f, "Stack trace of thread " PID_FMT ":\n", tid);
105
106 if (dwfl_thread_getframes(thread, frame_callback, c) < 0)
107 return DWARF_CB_ABORT;
108
109 c->n_thread++;
110
111 return DWARF_CB_OK;
112 }
113
114 static int make_stack_trace(int fd, const char *executable, char **ret) {
115
116 static const Dwfl_Callbacks callbacks = {
117 .find_elf = dwfl_build_id_find_elf,
118 .find_debuginfo = dwfl_standard_find_debuginfo,
119 };
120
121 struct stack_context c = {};
122 char *buf = NULL;
123 size_t sz = 0;
124 int r;
125
126 assert(fd >= 0);
127 assert(ret);
128
129 if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
130 return -errno;
131
132 c.f = open_memstream_unlocked(&buf, &sz);
133 if (!c.f)
134 return -ENOMEM;
135
136 elf_version(EV_CURRENT);
137
138 c.elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
139 if (!c.elf) {
140 r = -EINVAL;
141 goto finish;
142 }
143
144 c.dwfl = dwfl_begin(&callbacks);
145 if (!c.dwfl) {
146 r = -EINVAL;
147 goto finish;
148 }
149
150 if (dwfl_core_file_report(c.dwfl, c.elf, executable) < 0) {
151 r = -EINVAL;
152 goto finish;
153 }
154
155 if (dwfl_report_end(c.dwfl, NULL, NULL) != 0) {
156 r = -EINVAL;
157 goto finish;
158 }
159
160 if (dwfl_core_file_attach(c.dwfl, c.elf) < 0) {
161 r = -EINVAL;
162 goto finish;
163 }
164
165 if (dwfl_getthreads(c.dwfl, thread_callback, &c) < 0) {
166 r = -EINVAL;
167 goto finish;
168 }
169
170 c.f = safe_fclose(c.f);
171
172 *ret = TAKE_PTR(buf);
173
174 r = 0;
175
176 finish:
177 if (c.dwfl)
178 dwfl_end(c.dwfl);
179
180 if (c.elf)
181 elf_end(c.elf);
182
183 safe_fclose(c.f);
184
185 free(buf);
186
187 return r;
188 }
189
190 void coredump_make_stack_trace(int fd, const char *executable, char **ret) {
191 int r;
192
193 r = make_stack_trace(fd, executable, ret);
194 if (r == -EINVAL)
195 log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno()));
196 else if (r < 0)
197 log_warning_errno(r, "Failed to generate stack trace: %m");
198 }