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