Systemd/src/coredump/stacktrace.c

199 lines
5.4 KiB
C
Raw Normal View History

/* SPDX-License-Identifier: LGPL-2.1+ */
#include <dwarf.h>
#include <elfutils/libdwfl.h>
#include <sys/types.h>
#include <unistd.h>
#include "alloc-util.h"
2019-04-04 11:46:44 +02:00
#include "fileio.h"
#include "fd-util.h"
#include "format-util.h"
#include "macro.h"
#include "stacktrace.h"
#include "string-util.h"
#include "util.h"
#define FRAMES_MAX 64
#define THREADS_MAX 64
struct stack_context {
FILE *f;
Dwfl *dwfl;
Elf *elf;
unsigned n_thread;
unsigned n_frame;
};
static int frame_callback(Dwfl_Frame *frame, void *userdata) {
struct stack_context *c = userdata;
Dwarf_Addr pc, pc_adjusted, bias = 0;
_cleanup_free_ Dwarf_Die *scopes = NULL;
const char *fname = NULL, *symbol = NULL;
Dwfl_Module *module;
bool is_activation;
coredump: Include module offsets in stack traces These offsets can be useful to decode stack traces through modules that don't have symbol names. For example, with a simple test that crashes after calling through several static functions, systemd-coredump reports this: Oct 17 : Process 640333 (a.out) of user 1000 dumped core. Stack trace of thread 640333: #0 0x00005562c2b9f11d n/a (/tmp/a.out) #1 0x00005562c2b9f12d n/a (/tmp/a.out) #2 0x00005562c2b9f139 n/a (/tmp/a.out) #3 0x00005562c2b9f145 n/a (/tmp/a.out) #4 0x00007fc768b39153 __libc_start_main (libc.so.6) #5 0x00005562c2b9f04e n/a (/tmp/a.out) With this change: Stack trace of thread 666897: #0 0x0000555668fbe11d n/a (/tmp/a.out + 0x111d) #1 0x0000555668fbe12d n/a (/tmp/a.out + 0x112d) #2 0x0000555668fbe139 n/a (/tmp/a.out + 0x1139) #3 0x0000555668fbe145 n/a (/tmp/a.out + 0x1145) #4 0x00007f7b5c828153 __libc_start_main (libc.so.6 + 0x27153) #5 0x0000555668fbe04e n/a (/tmp/a.out + 0x104e) Disassembling the test binary shows that these offsets line up: 0000000000001119 <crash>: 1119: 55 push %rbp 111a: 48 89 e5 mov %rsp,%rbp 111d: 0f 0b ud2 <---- #0 000000000000111f <b>: 111f: 55 push %rbp 1120: 48 89 e5 mov %rsp,%rbp 1123: b8 00 00 00 00 mov $0x0,%eax 1128: e8 ec ff ff ff callq 1119 <crash> 112d: 90 nop <---- #1 112e: 5d pop %rbp 112f: c3 retq 0000000000001130 <a>: 1130: 55 push %rbp 1131: 48 89 e5 mov %rsp,%rbp 1134: e8 e6 ff ff ff callq 111f <b> 1139: 90 nop <---- #2 113a: 5d pop %rbp 113b: c3 retq 000000000000113c <main>: 113c: 55 push %rbp 113d: 48 89 e5 mov %rsp,%rbp 1140: e8 eb ff ff ff callq 1130 <a> 1145: b8 00 00 00 00 mov $0x0,%eax <---- #3 114a: 5d pop %rbp 114b: c3 retq 114c: 0f 1f 40 00 nopl 0x0(%rax) (from libc.so.6) 0000000000027060 <__libc_start_main>: 27060: f3 0f 1e fa endbr64 27064: 41 56 push %r14 27066: 31 c0 xor %eax,%eax [...] 2714c: 48 8b 44 24 18 mov 0x18(%rsp),%rax 27151: ff d0 callq *%rax 27153: 89 c7 mov %eax,%edi <---- #4 27155: e8 e6 76 01 00 callq 3e840 <exit>
2019-10-17 21:56:15 +02:00
uint64_t module_offset = 0;
assert(frame);
assert(c);
if (c->n_frame >= FRAMES_MAX)
return DWARF_CB_ABORT;
if (!dwfl_frame_pc(frame, &pc, &is_activation))
return DWARF_CB_ABORT;
pc_adjusted = pc - (is_activation ? 0 : 1);
module = dwfl_addrmodule(c->dwfl, pc_adjusted);
if (module) {
Dwarf_Die *s, *cudie;
int n;
coredump: Include module offsets in stack traces These offsets can be useful to decode stack traces through modules that don't have symbol names. For example, with a simple test that crashes after calling through several static functions, systemd-coredump reports this: Oct 17 : Process 640333 (a.out) of user 1000 dumped core. Stack trace of thread 640333: #0 0x00005562c2b9f11d n/a (/tmp/a.out) #1 0x00005562c2b9f12d n/a (/tmp/a.out) #2 0x00005562c2b9f139 n/a (/tmp/a.out) #3 0x00005562c2b9f145 n/a (/tmp/a.out) #4 0x00007fc768b39153 __libc_start_main (libc.so.6) #5 0x00005562c2b9f04e n/a (/tmp/a.out) With this change: Stack trace of thread 666897: #0 0x0000555668fbe11d n/a (/tmp/a.out + 0x111d) #1 0x0000555668fbe12d n/a (/tmp/a.out + 0x112d) #2 0x0000555668fbe139 n/a (/tmp/a.out + 0x1139) #3 0x0000555668fbe145 n/a (/tmp/a.out + 0x1145) #4 0x00007f7b5c828153 __libc_start_main (libc.so.6 + 0x27153) #5 0x0000555668fbe04e n/a (/tmp/a.out + 0x104e) Disassembling the test binary shows that these offsets line up: 0000000000001119 <crash>: 1119: 55 push %rbp 111a: 48 89 e5 mov %rsp,%rbp 111d: 0f 0b ud2 <---- #0 000000000000111f <b>: 111f: 55 push %rbp 1120: 48 89 e5 mov %rsp,%rbp 1123: b8 00 00 00 00 mov $0x0,%eax 1128: e8 ec ff ff ff callq 1119 <crash> 112d: 90 nop <---- #1 112e: 5d pop %rbp 112f: c3 retq 0000000000001130 <a>: 1130: 55 push %rbp 1131: 48 89 e5 mov %rsp,%rbp 1134: e8 e6 ff ff ff callq 111f <b> 1139: 90 nop <---- #2 113a: 5d pop %rbp 113b: c3 retq 000000000000113c <main>: 113c: 55 push %rbp 113d: 48 89 e5 mov %rsp,%rbp 1140: e8 eb ff ff ff callq 1130 <a> 1145: b8 00 00 00 00 mov $0x0,%eax <---- #3 114a: 5d pop %rbp 114b: c3 retq 114c: 0f 1f 40 00 nopl 0x0(%rax) (from libc.so.6) 0000000000027060 <__libc_start_main>: 27060: f3 0f 1e fa endbr64 27064: 41 56 push %r14 27066: 31 c0 xor %eax,%eax [...] 2714c: 48 8b 44 24 18 mov 0x18(%rsp),%rax 27151: ff d0 callq *%rax 27153: 89 c7 mov %eax,%edi <---- #4 27155: e8 e6 76 01 00 callq 3e840 <exit>
2019-10-17 21:56:15 +02:00
Dwarf_Addr start;
cudie = dwfl_module_addrdie(module, pc_adjusted, &bias);
if (cudie) {
n = dwarf_getscopes(cudie, pc_adjusted - bias, &scopes);
for (s = scopes; s < scopes + n; s++) {
if (IN_SET(dwarf_tag(s), DW_TAG_subprogram, DW_TAG_inlined_subroutine, DW_TAG_entry_point)) {
Dwarf_Attribute *a, space;
a = dwarf_attr_integrate(s, DW_AT_MIPS_linkage_name, &space);
if (!a)
a = dwarf_attr_integrate(s, DW_AT_linkage_name, &space);
if (a)
symbol = dwarf_formstring(a);
if (!symbol)
symbol = dwarf_diename(s);
if (symbol)
break;
}
}
}
if (!symbol)
symbol = dwfl_module_addrname(module, pc_adjusted);
coredump: Include module offsets in stack traces These offsets can be useful to decode stack traces through modules that don't have symbol names. For example, with a simple test that crashes after calling through several static functions, systemd-coredump reports this: Oct 17 : Process 640333 (a.out) of user 1000 dumped core. Stack trace of thread 640333: #0 0x00005562c2b9f11d n/a (/tmp/a.out) #1 0x00005562c2b9f12d n/a (/tmp/a.out) #2 0x00005562c2b9f139 n/a (/tmp/a.out) #3 0x00005562c2b9f145 n/a (/tmp/a.out) #4 0x00007fc768b39153 __libc_start_main (libc.so.6) #5 0x00005562c2b9f04e n/a (/tmp/a.out) With this change: Stack trace of thread 666897: #0 0x0000555668fbe11d n/a (/tmp/a.out + 0x111d) #1 0x0000555668fbe12d n/a (/tmp/a.out + 0x112d) #2 0x0000555668fbe139 n/a (/tmp/a.out + 0x1139) #3 0x0000555668fbe145 n/a (/tmp/a.out + 0x1145) #4 0x00007f7b5c828153 __libc_start_main (libc.so.6 + 0x27153) #5 0x0000555668fbe04e n/a (/tmp/a.out + 0x104e) Disassembling the test binary shows that these offsets line up: 0000000000001119 <crash>: 1119: 55 push %rbp 111a: 48 89 e5 mov %rsp,%rbp 111d: 0f 0b ud2 <---- #0 000000000000111f <b>: 111f: 55 push %rbp 1120: 48 89 e5 mov %rsp,%rbp 1123: b8 00 00 00 00 mov $0x0,%eax 1128: e8 ec ff ff ff callq 1119 <crash> 112d: 90 nop <---- #1 112e: 5d pop %rbp 112f: c3 retq 0000000000001130 <a>: 1130: 55 push %rbp 1131: 48 89 e5 mov %rsp,%rbp 1134: e8 e6 ff ff ff callq 111f <b> 1139: 90 nop <---- #2 113a: 5d pop %rbp 113b: c3 retq 000000000000113c <main>: 113c: 55 push %rbp 113d: 48 89 e5 mov %rsp,%rbp 1140: e8 eb ff ff ff callq 1130 <a> 1145: b8 00 00 00 00 mov $0x0,%eax <---- #3 114a: 5d pop %rbp 114b: c3 retq 114c: 0f 1f 40 00 nopl 0x0(%rax) (from libc.so.6) 0000000000027060 <__libc_start_main>: 27060: f3 0f 1e fa endbr64 27064: 41 56 push %r14 27066: 31 c0 xor %eax,%eax [...] 2714c: 48 8b 44 24 18 mov 0x18(%rsp),%rax 27151: ff d0 callq *%rax 27153: 89 c7 mov %eax,%edi <---- #4 27155: e8 e6 76 01 00 callq 3e840 <exit>
2019-10-17 21:56:15 +02:00
fname = dwfl_module_info(module, NULL, &start, NULL, NULL, NULL, NULL, NULL);
module_offset = pc - start;
}
coredump: Include module offsets in stack traces These offsets can be useful to decode stack traces through modules that don't have symbol names. For example, with a simple test that crashes after calling through several static functions, systemd-coredump reports this: Oct 17 : Process 640333 (a.out) of user 1000 dumped core. Stack trace of thread 640333: #0 0x00005562c2b9f11d n/a (/tmp/a.out) #1 0x00005562c2b9f12d n/a (/tmp/a.out) #2 0x00005562c2b9f139 n/a (/tmp/a.out) #3 0x00005562c2b9f145 n/a (/tmp/a.out) #4 0x00007fc768b39153 __libc_start_main (libc.so.6) #5 0x00005562c2b9f04e n/a (/tmp/a.out) With this change: Stack trace of thread 666897: #0 0x0000555668fbe11d n/a (/tmp/a.out + 0x111d) #1 0x0000555668fbe12d n/a (/tmp/a.out + 0x112d) #2 0x0000555668fbe139 n/a (/tmp/a.out + 0x1139) #3 0x0000555668fbe145 n/a (/tmp/a.out + 0x1145) #4 0x00007f7b5c828153 __libc_start_main (libc.so.6 + 0x27153) #5 0x0000555668fbe04e n/a (/tmp/a.out + 0x104e) Disassembling the test binary shows that these offsets line up: 0000000000001119 <crash>: 1119: 55 push %rbp 111a: 48 89 e5 mov %rsp,%rbp 111d: 0f 0b ud2 <---- #0 000000000000111f <b>: 111f: 55 push %rbp 1120: 48 89 e5 mov %rsp,%rbp 1123: b8 00 00 00 00 mov $0x0,%eax 1128: e8 ec ff ff ff callq 1119 <crash> 112d: 90 nop <---- #1 112e: 5d pop %rbp 112f: c3 retq 0000000000001130 <a>: 1130: 55 push %rbp 1131: 48 89 e5 mov %rsp,%rbp 1134: e8 e6 ff ff ff callq 111f <b> 1139: 90 nop <---- #2 113a: 5d pop %rbp 113b: c3 retq 000000000000113c <main>: 113c: 55 push %rbp 113d: 48 89 e5 mov %rsp,%rbp 1140: e8 eb ff ff ff callq 1130 <a> 1145: b8 00 00 00 00 mov $0x0,%eax <---- #3 114a: 5d pop %rbp 114b: c3 retq 114c: 0f 1f 40 00 nopl 0x0(%rax) (from libc.so.6) 0000000000027060 <__libc_start_main>: 27060: f3 0f 1e fa endbr64 27064: 41 56 push %r14 27066: 31 c0 xor %eax,%eax [...] 2714c: 48 8b 44 24 18 mov 0x18(%rsp),%rax 27151: ff d0 callq *%rax 27153: 89 c7 mov %eax,%edi <---- #4 27155: e8 e6 76 01 00 callq 3e840 <exit>
2019-10-17 21:56:15 +02:00
fprintf(c->f, "#%-2u 0x%016" PRIx64 " %s (%s + 0x%" PRIx64 ")\n", c->n_frame, (uint64_t) pc, strna(symbol), strna(fname), module_offset);
c->n_frame++;
return DWARF_CB_OK;
}
static int thread_callback(Dwfl_Thread *thread, void *userdata) {
struct stack_context *c = userdata;
pid_t tid;
assert(thread);
assert(c);
if (c->n_thread >= THREADS_MAX)
return DWARF_CB_ABORT;
if (c->n_thread != 0)
fputc('\n', c->f);
c->n_frame = 0;
tid = dwfl_thread_tid(thread);
fprintf(c->f, "Stack trace of thread " PID_FMT ":\n", tid);
if (dwfl_thread_getframes(thread, frame_callback, c) < 0)
return DWARF_CB_ABORT;
c->n_thread++;
return DWARF_CB_OK;
}
static int make_stack_trace(int fd, const char *executable, char **ret) {
static const Dwfl_Callbacks callbacks = {
.find_elf = dwfl_build_id_find_elf,
.find_debuginfo = dwfl_standard_find_debuginfo,
};
struct stack_context c = {};
char *buf = NULL;
size_t sz = 0;
int r;
assert(fd >= 0);
assert(ret);
if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
return -errno;
2019-04-04 11:46:44 +02:00
c.f = open_memstream_unlocked(&buf, &sz);
if (!c.f)
return -ENOMEM;
elf_version(EV_CURRENT);
c.elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
if (!c.elf) {
r = -EINVAL;
goto finish;
}
c.dwfl = dwfl_begin(&callbacks);
if (!c.dwfl) {
r = -EINVAL;
goto finish;
}
if (dwfl_core_file_report(c.dwfl, c.elf, executable) < 0) {
r = -EINVAL;
goto finish;
}
if (dwfl_report_end(c.dwfl, NULL, NULL) != 0) {
r = -EINVAL;
goto finish;
}
if (dwfl_core_file_attach(c.dwfl, c.elf) < 0) {
r = -EINVAL;
goto finish;
}
if (dwfl_getthreads(c.dwfl, thread_callback, &c) < 0) {
r = -EINVAL;
goto finish;
}
c.f = safe_fclose(c.f);
*ret = TAKE_PTR(buf);
r = 0;
finish:
if (c.dwfl)
dwfl_end(c.dwfl);
if (c.elf)
elf_end(c.elf);
safe_fclose(c.f);
free(buf);
return r;
}
void coredump_make_stack_trace(int fd, const char *executable, char **ret) {
int r;
r = make_stack_trace(fd, executable, ret);
if (r == -EINVAL)
log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno()));
else if (r < 0)
log_warning_errno(r, "Failed to generate stack trace: %m");
}