1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
4 #include <elfutils/libdwelf.h>
5 #include <elfutils/libdwfl.h>
10 #include "alloc-util.h"
13 #include "format-util.h"
14 #include "hexdecoct.h"
16 #include "stacktrace.h"
17 #include "string-util.h"
21 #define THREADS_MAX 64
22 #define ELF_PACKAGE_METADATA_ID 0xcafe1a7e
24 struct stack_context
{
30 JsonVariant
**package_metadata
;
34 static int frame_callback(Dwfl_Frame
*frame
, void *userdata
) {
35 struct stack_context
*c
= userdata
;
36 Dwarf_Addr pc
, pc_adjusted
, bias
= 0;
37 _cleanup_free_ Dwarf_Die
*scopes
= NULL
;
38 const char *fname
= NULL
, *symbol
= NULL
;
41 uint64_t module_offset
= 0;
46 if (c
->n_frame
>= FRAMES_MAX
)
47 return DWARF_CB_ABORT
;
49 if (!dwfl_frame_pc(frame
, &pc
, &is_activation
))
50 return DWARF_CB_ABORT
;
52 pc_adjusted
= pc
- (is_activation
? 0 : 1);
54 module
= dwfl_addrmodule(c
->dwfl
, pc_adjusted
);
60 cudie
= dwfl_module_addrdie(module
, pc_adjusted
, &bias
);
62 n
= dwarf_getscopes(cudie
, pc_adjusted
- bias
, &scopes
);
63 for (s
= scopes
; s
< scopes
+ n
; s
++) {
64 if (IN_SET(dwarf_tag(s
), DW_TAG_subprogram
, DW_TAG_inlined_subroutine
, DW_TAG_entry_point
)) {
65 Dwarf_Attribute
*a
, space
;
67 a
= dwarf_attr_integrate(s
, DW_AT_MIPS_linkage_name
, &space
);
69 a
= dwarf_attr_integrate(s
, DW_AT_linkage_name
, &space
);
71 symbol
= dwarf_formstring(a
);
73 symbol
= dwarf_diename(s
);
82 symbol
= dwfl_module_addrname(module
, pc_adjusted
);
84 fname
= dwfl_module_info(module
, NULL
, &start
, NULL
, NULL
, NULL
, NULL
, NULL
);
85 module_offset
= pc
- start
;
88 fprintf(c
->f
, "#%-2u 0x%016" PRIx64
" %s (%s + 0x%" PRIx64
")\n", c
->n_frame
, (uint64_t) pc
, strna(symbol
), strna(fname
), module_offset
);
94 static int thread_callback(Dwfl_Thread
*thread
, void *userdata
) {
95 struct stack_context
*c
= userdata
;
101 if (c
->n_thread
>= THREADS_MAX
)
102 return DWARF_CB_ABORT
;
104 if (c
->n_thread
!= 0)
109 tid
= dwfl_thread_tid(thread
);
110 fprintf(c
->f
, "Stack trace of thread " PID_FMT
":\n", tid
);
112 if (dwfl_thread_getframes(thread
, frame_callback
, c
) < 0)
113 return DWARF_CB_ABORT
;
120 static int parse_package_metadata(const char *name
, JsonVariant
*id_json
, Elf
*elf
, struct stack_context
*c
) {
121 size_t n_program_headers
;
128 /* When iterating over PT_LOAD we will visit modules more than once */
129 if (set_contains(*c
->modules
, name
))
132 r
= elf_getphdrnum(elf
, &n_program_headers
);
133 if (r
< 0) /* Not the handle we are looking for - that's ok, skip it */
136 /* Iterate over all program headers in that ELF object. These will have been copied by
137 * the kernel verbatim when the core file is generated. */
138 for (size_t i
= 0; i
< n_program_headers
; ++i
) {
139 size_t note_offset
= 0, name_offset
, desc_offset
;
140 GElf_Phdr mem
, *program_header
;
141 GElf_Nhdr note_header
;
144 /* Package metadata is in PT_NOTE headers. */
145 program_header
= gelf_getphdr(elf
, i
, &mem
);
146 if (!program_header
|| program_header
->p_type
!= PT_NOTE
)
149 /* Fortunately there is an iterator we can use to walk over the
150 * elements of a PT_NOTE program header. We are interested in the
152 data
= elf_getdata_rawchunk(elf
,
153 program_header
->p_offset
,
154 program_header
->p_filesz
,
157 while (note_offset
< data
->d_size
&&
158 (note_offset
= gelf_getnote(data
, note_offset
, ¬e_header
, &name_offset
, &desc_offset
)) > 0) {
159 const char *note_name
= (const char *)data
->d_buf
+ name_offset
;
160 const char *payload
= (const char *)data
->d_buf
+ desc_offset
;
162 if (note_header
.n_namesz
== 0 || note_header
.n_descsz
== 0)
165 /* Package metadata might have different owners, but the
166 * magic ID is always the same. */
167 if (note_header
.n_type
== ELF_PACKAGE_METADATA_ID
) {
168 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
, *w
= NULL
;
170 r
= json_parse(payload
, 0, &v
, NULL
, NULL
);
172 log_error_errno(r
, "json_parse on %s failed: %m", payload
);
173 return DWARF_CB_ABORT
;
176 /* First pretty-print to the buffer, so that the metadata goes as
177 * plaintext in the journal. */
178 fprintf(c
->f
, "Metadata for module %s owned by %s found: ",
180 json_variant_dump(v
, JSON_FORMAT_NEWLINE
|JSON_FORMAT_PRETTY
, c
->f
, NULL
);
183 /* Secondly, if we have a build-id, merge it in the same JSON object
184 * so that it appears all nicely together in the logs/metadata. */
186 r
= json_variant_merge(&v
, id_json
);
188 log_error_errno(r
, "json_variant_merge of package meta with buildid failed: %m");
189 return DWARF_CB_ABORT
;
193 /* Then we build a new object using the module name as the key, and merge it
194 * with the previous parses, so that in the end it all fits together in a single
196 r
= json_build(&w
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR(name
, JSON_BUILD_VARIANT(v
))));
198 log_error_errno(r
, "Failed to build JSON object: %m");
199 return DWARF_CB_ABORT
;
201 r
= json_variant_merge(c
->package_metadata
, w
);
203 log_error_errno(r
, "json_variant_merge of package meta with buildid failed: %m");
204 return DWARF_CB_ABORT
;
207 /* Finally stash the name, so we avoid double visits. */
208 r
= set_put_strdup(c
->modules
, name
);
210 log_error_errno(r
, "set_put_strdup failed: %m");
211 return DWARF_CB_ABORT
;
219 /* Didn't find package metadata for this module - that's ok, just go to the next. */
223 static int module_callback(Dwfl_Module
*mod
, void **userdata
, const char *name
, Dwarf_Addr start
, void *arg
) {
224 _cleanup_(json_variant_unrefp
) JsonVariant
*id_json
= NULL
;
225 struct stack_context
*c
= arg
;
226 size_t n_program_headers
;
227 GElf_Addr id_vaddr
, bias
;
228 const unsigned char *id
;
236 name
= "(unnamed)"; /* For logging purposes */
238 /* We are iterating on each "module", which is what dwfl calls ELF objects contained in the
239 * core file, and extracting the build-id first and then the package metadata.
240 * We proceed in a best-effort fashion - not all ELF objects might contain both or either.
241 * The build-id is easy, as libdwfl parses it during the dwfl_core_file_report() call and
242 * stores it separately in an internal library struct. */
243 id_len
= dwfl_module_build_id(mod
, &id
, &id_vaddr
);
245 /* If we don't find a build-id, note it in the journal message, and try
246 * anyway to find the package metadata. It's unlikely to have the latter
247 * without the former, but there's no hard rule. */
248 fprintf(c
->f
, "Found module %s without build-id.\n", name
);
250 JsonVariant
*build_id
;
252 /* We will later parse package metadata json and pass it to our caller. Prepare the
253 * build-id in json format too, so that it can be appended and parsed cleanly. It
254 * will then be added as metadata to the journal message with the stack trace. */
255 r
= json_build(&id_json
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("buildId", JSON_BUILD_HEX(id
, id_len
))));
257 log_error_errno(r
, "json_build on build-id failed: %m");
258 return DWARF_CB_ABORT
;
261 build_id
= json_variant_by_key(id_json
, "buildId");
263 fprintf(c
->f
, "Found module %s with build-id: %s\n", name
, json_variant_string(build_id
));
266 /* The .note.package metadata is more difficult. From the module, we need to get a reference
267 * to the ELF object first. We might be lucky and just get it from elfutils. */
268 elf
= dwfl_module_getelf(mod
, &bias
);
270 return parse_package_metadata(name
, id_json
, elf
, c
);
272 /* We did not get the ELF object. That is likely because we didn't get direct
273 * access to the executable, and the version of elfutils does not yet support
274 * parsing it out of the core file directly.
275 * So fallback to manual extraction - get the PT_LOAD section from the core,
276 * and if it's the right one we can interpret it as an Elf object, and parse
277 * its notes manually. */
279 r
= elf_getphdrnum(c
->elf
, &n_program_headers
);
281 log_warning("Could not parse number of program headers from core file: %s",
282 elf_errmsg(-1)); /* -1 retrieves the most recent error */
286 for (size_t i
= 0; i
< n_program_headers
; ++i
) {
287 GElf_Phdr mem
, *program_header
;
290 /* The core file stores the ELF files in the PT_LOAD segment .*/
291 program_header
= gelf_getphdr(c
->elf
, i
, &mem
);
292 if (!program_header
|| program_header
->p_type
!= PT_LOAD
)
295 /* Now get a usable Elf reference, and parse the notes from it. */
296 data
= elf_getdata_rawchunk(c
->elf
,
297 program_header
->p_offset
,
298 program_header
->p_filesz
,
301 Elf
*memelf
= elf_memory(data
->d_buf
, data
->d_size
);
304 r
= parse_package_metadata(name
, id_json
, memelf
, c
);
305 if (r
!= DWARF_CB_OK
)
312 static int parse_core(int fd
, const char *executable
, char **ret
, JsonVariant
**ret_package_metadata
) {
314 static const Dwfl_Callbacks callbacks
= {
315 .find_elf
= dwfl_build_id_find_elf
,
316 .section_address
= dwfl_offline_section_address
,
317 .find_debuginfo
= dwfl_standard_find_debuginfo
,
320 _cleanup_(json_variant_unrefp
) JsonVariant
*package_metadata
= NULL
;
321 _cleanup_(set_freep
) Set
*modules
= NULL
;
322 struct stack_context c
= {
323 .package_metadata
= &package_metadata
,
333 if (lseek(fd
, 0, SEEK_SET
) == (off_t
) -1)
336 c
.f
= open_memstream_unlocked(&buf
, &sz
);
340 elf_version(EV_CURRENT
);
342 c
.elf
= elf_begin(fd
, ELF_C_READ_MMAP
, NULL
);
348 c
.dwfl
= dwfl_begin(&callbacks
);
354 if (dwfl_core_file_report(c
.dwfl
, c
.elf
, executable
) < 0) {
359 if (dwfl_report_end(c
.dwfl
, NULL
, NULL
) != 0) {
364 if (dwfl_getmodules(c
.dwfl
, &module_callback
, &c
, 0) < 0) {
369 if (dwfl_core_file_attach(c
.dwfl
, c
.elf
) < 0) {
374 if (dwfl_getthreads(c
.dwfl
, thread_callback
, &c
) < 0) {
379 c
.f
= safe_fclose(c
.f
);
381 *ret
= TAKE_PTR(buf
);
382 if (ret_package_metadata
)
383 *ret_package_metadata
= TAKE_PTR(package_metadata
);
401 void coredump_parse_core(int fd
, const char *executable
, char **ret
, JsonVariant
**ret_package_metadata
) {
404 r
= parse_core(fd
, executable
, ret
, ret_package_metadata
);
406 log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno()));
408 log_warning_errno(r
, "Failed to generate stack trace: %m");