]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/coredump/stacktrace.c
Merge pull request #4536 from poettering/seccomp-namespaces
[thirdparty/systemd.git] / src / coredump / stacktrace.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2014 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <dwarf.h>
21 #include <elfutils/libdwfl.h>
22
23 #include "alloc-util.h"
24 #include "fd-util.h"
25 #include "format-util.h"
26 #include "macro.h"
27 #include "stacktrace.h"
28 #include "string-util.h"
29 #include "util.h"
30
31 #define FRAMES_MAX 64
32 #define THREADS_MAX 64
33
34 struct stack_context {
35 FILE *f;
36 Dwfl *dwfl;
37 Elf *elf;
38 unsigned n_thread;
39 unsigned n_frame;
40 };
41
42 static int frame_callback(Dwfl_Frame *frame, void *userdata) {
43 struct stack_context *c = userdata;
44 Dwarf_Addr pc, pc_adjusted, bias = 0;
45 _cleanup_free_ Dwarf_Die *scopes = NULL;
46 const char *fname = NULL, *symbol = NULL;
47 Dwfl_Module *module;
48 bool is_activation;
49
50 assert(frame);
51 assert(c);
52
53 if (c->n_frame >= FRAMES_MAX)
54 return DWARF_CB_ABORT;
55
56 if (!dwfl_frame_pc(frame, &pc, &is_activation))
57 return DWARF_CB_ABORT;
58
59 pc_adjusted = pc - (is_activation ? 0 : 1);
60
61 module = dwfl_addrmodule(c->dwfl, pc_adjusted);
62 if (module) {
63 Dwarf_Die *s, *cudie;
64 int n;
65
66 cudie = dwfl_module_addrdie(module, pc_adjusted, &bias);
67 if (cudie) {
68 n = dwarf_getscopes(cudie, pc_adjusted - bias, &scopes);
69 for (s = scopes; s < scopes + n; s++) {
70 if (IN_SET(dwarf_tag(s), DW_TAG_subprogram, DW_TAG_inlined_subroutine, DW_TAG_entry_point)) {
71 Dwarf_Attribute *a, space;
72
73 a = dwarf_attr_integrate(s, DW_AT_MIPS_linkage_name, &space);
74 if (!a)
75 a = dwarf_attr_integrate(s, DW_AT_linkage_name, &space);
76 if (a)
77 symbol = dwarf_formstring(a);
78 if (!symbol)
79 symbol = dwarf_diename(s);
80
81 if (symbol)
82 break;
83 }
84 }
85 }
86
87 if (!symbol)
88 symbol = dwfl_module_addrname(module, pc_adjusted);
89
90 fname = dwfl_module_info(module, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
91 }
92
93 fprintf(c->f, "#%-2u 0x%016" PRIx64 " %s (%s)\n", c->n_frame, (uint64_t) pc, strna(symbol), strna(fname));
94 c->n_frame++;
95
96 return DWARF_CB_OK;
97 }
98
99 static int thread_callback(Dwfl_Thread *thread, void *userdata) {
100 struct stack_context *c = userdata;
101 pid_t tid;
102
103 assert(thread);
104 assert(c);
105
106 if (c->n_thread >= THREADS_MAX)
107 return DWARF_CB_ABORT;
108
109 if (c->n_thread != 0)
110 fputc('\n', c->f);
111
112 c->n_frame = 0;
113
114 tid = dwfl_thread_tid(thread);
115 fprintf(c->f, "Stack trace of thread " PID_FMT ":\n", tid);
116
117 if (dwfl_thread_getframes(thread, frame_callback, c) < 0)
118 return DWARF_CB_ABORT;
119
120 c->n_thread++;
121
122 return DWARF_CB_OK;
123 }
124
125 int coredump_make_stack_trace(int fd, const char *executable, char **ret) {
126
127 static const Dwfl_Callbacks callbacks = {
128 .find_elf = dwfl_build_id_find_elf,
129 .find_debuginfo = dwfl_standard_find_debuginfo,
130 };
131
132 struct stack_context c = {};
133 char *buf = NULL;
134 size_t sz = 0;
135 int r;
136
137 assert(fd >= 0);
138 assert(ret);
139
140 if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
141 return -errno;
142
143 c.f = open_memstream(&buf, &sz);
144 if (!c.f)
145 return -ENOMEM;
146
147 elf_version(EV_CURRENT);
148
149 c.elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
150 if (!c.elf) {
151 r = -EINVAL;
152 goto finish;
153 }
154
155 c.dwfl = dwfl_begin(&callbacks);
156 if (!c.dwfl) {
157 r = -EINVAL;
158 goto finish;
159 }
160
161 if (dwfl_core_file_report(c.dwfl, c.elf, executable) < 0) {
162 r = -EINVAL;
163 goto finish;
164 }
165
166 if (dwfl_report_end(c.dwfl, NULL, NULL) != 0) {
167 r = -EINVAL;
168 goto finish;
169 }
170
171 if (dwfl_core_file_attach(c.dwfl, c.elf) < 0) {
172 r = -EINVAL;
173 goto finish;
174 }
175
176 if (dwfl_getthreads(c.dwfl, thread_callback, &c) < 0) {
177 r = -EINVAL;
178 goto finish;
179 }
180
181 c.f = safe_fclose(c.f);
182
183 *ret = buf;
184 buf = NULL;
185
186 r = 0;
187
188 finish:
189 if (c.dwfl)
190 dwfl_end(c.dwfl);
191
192 if (c.elf)
193 elf_end(c.elf);
194
195 safe_fclose(c.f);
196
197 free(buf);
198
199 return r;
200 }