]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/coredump/stacktrace.c
ab1ac12a23a57c30245b25256193df552f0351d5
[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 <stdio_ext.h>
6 #include <sys/types.h>
7 #include <unistd.h>
8
9 #include "alloc-util.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
36 assert(frame);
37 assert(c);
38
39 if (c->n_frame >= FRAMES_MAX)
40 return DWARF_CB_ABORT;
41
42 if (!dwfl_frame_pc(frame, &pc, &is_activation))
43 return DWARF_CB_ABORT;
44
45 pc_adjusted = pc - (is_activation ? 0 : 1);
46
47 module = dwfl_addrmodule(c->dwfl, pc_adjusted);
48 if (module) {
49 Dwarf_Die *s, *cudie;
50 int n;
51
52 cudie = dwfl_module_addrdie(module, pc_adjusted, &bias);
53 if (cudie) {
54 n = dwarf_getscopes(cudie, pc_adjusted - bias, &scopes);
55 for (s = scopes; s < scopes + n; s++) {
56 if (IN_SET(dwarf_tag(s), DW_TAG_subprogram, DW_TAG_inlined_subroutine, DW_TAG_entry_point)) {
57 Dwarf_Attribute *a, space;
58
59 a = dwarf_attr_integrate(s, DW_AT_MIPS_linkage_name, &space);
60 if (!a)
61 a = dwarf_attr_integrate(s, DW_AT_linkage_name, &space);
62 if (a)
63 symbol = dwarf_formstring(a);
64 if (!symbol)
65 symbol = dwarf_diename(s);
66
67 if (symbol)
68 break;
69 }
70 }
71 }
72
73 if (!symbol)
74 symbol = dwfl_module_addrname(module, pc_adjusted);
75
76 fname = dwfl_module_info(module, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
77 }
78
79 fprintf(c->f, "#%-2u 0x%016" PRIx64 " %s (%s)\n", c->n_frame, (uint64_t) pc, strna(symbol), strna(fname));
80 c->n_frame++;
81
82 return DWARF_CB_OK;
83 }
84
85 static int thread_callback(Dwfl_Thread *thread, void *userdata) {
86 struct stack_context *c = userdata;
87 pid_t tid;
88
89 assert(thread);
90 assert(c);
91
92 if (c->n_thread >= THREADS_MAX)
93 return DWARF_CB_ABORT;
94
95 if (c->n_thread != 0)
96 fputc('\n', c->f);
97
98 c->n_frame = 0;
99
100 tid = dwfl_thread_tid(thread);
101 fprintf(c->f, "Stack trace of thread " PID_FMT ":\n", tid);
102
103 if (dwfl_thread_getframes(thread, frame_callback, c) < 0)
104 return DWARF_CB_ABORT;
105
106 c->n_thread++;
107
108 return DWARF_CB_OK;
109 }
110
111 int coredump_make_stack_trace(int fd, const char *executable, char **ret) {
112
113 static const Dwfl_Callbacks callbacks = {
114 .find_elf = dwfl_build_id_find_elf,
115 .find_debuginfo = dwfl_standard_find_debuginfo,
116 };
117
118 struct stack_context c = {};
119 char *buf = NULL;
120 size_t sz = 0;
121 int r;
122
123 assert(fd >= 0);
124 assert(ret);
125
126 if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
127 return -errno;
128
129 c.f = open_memstream(&buf, &sz);
130 if (!c.f)
131 return -ENOMEM;
132
133 (void) __fsetlocking(c.f, FSETLOCKING_BYCALLER);
134
135 elf_version(EV_CURRENT);
136
137 c.elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
138 if (!c.elf) {
139 r = -EINVAL;
140 goto finish;
141 }
142
143 c.dwfl = dwfl_begin(&callbacks);
144 if (!c.dwfl) {
145 r = -EINVAL;
146 goto finish;
147 }
148
149 if (dwfl_core_file_report(c.dwfl, c.elf, executable) < 0) {
150 r = -EINVAL;
151 goto finish;
152 }
153
154 if (dwfl_report_end(c.dwfl, NULL, NULL) != 0) {
155 r = -EINVAL;
156 goto finish;
157 }
158
159 if (dwfl_core_file_attach(c.dwfl, c.elf) < 0) {
160 r = -EINVAL;
161 goto finish;
162 }
163
164 if (dwfl_getthreads(c.dwfl, thread_callback, &c) < 0) {
165 r = -EINVAL;
166 goto finish;
167 }
168
169 c.f = safe_fclose(c.f);
170
171 *ret = TAKE_PTR(buf);
172
173 r = 0;
174
175 finish:
176 if (c.dwfl)
177 dwfl_end(c.dwfl);
178
179 if (c.elf)
180 elf_end(c.elf);
181
182 safe_fclose(c.f);
183
184 free(buf);
185
186 return r;
187 }