188c51002SIan Rogers // SPDX-License-Identifier: GPL-2.0 288c51002SIan Rogers #include "dso.h" 388c51002SIan Rogers #include "libdw.h" 488c51002SIan Rogers #include "srcline.h" 588c51002SIan Rogers #include "symbol.h" 688c51002SIan Rogers #include "dwarf-aux.h" 788c51002SIan Rogers #include <fcntl.h> 888c51002SIan Rogers #include <unistd.h> 988c51002SIan Rogers #include <elfutils/libdwfl.h> 1088c51002SIan Rogers 11*b7a2b011SIan Rogers static const Dwfl_Callbacks offline_callbacks = { 12*b7a2b011SIan Rogers .find_debuginfo = dwfl_standard_find_debuginfo, 13*b7a2b011SIan Rogers .section_address = dwfl_offline_section_address, 14*b7a2b011SIan Rogers .find_elf = dwfl_build_id_find_elf, 15*b7a2b011SIan Rogers }; 16*b7a2b011SIan Rogers 17*b7a2b011SIan Rogers void dso__free_libdw(struct dso *dso) 1888c51002SIan Rogers { 19*b7a2b011SIan Rogers Dwfl *dwfl = dso__libdw(dso); 2088c51002SIan Rogers 2188c51002SIan Rogers if (dwfl) { 2288c51002SIan Rogers dwfl_end(dwfl); 23*b7a2b011SIan Rogers dso__set_libdw(dso, NULL); 2488c51002SIan Rogers } 2588c51002SIan Rogers } 2688c51002SIan Rogers 27*b7a2b011SIan Rogers struct Dwfl *dso__libdw_dwfl(struct dso *dso) 28*b7a2b011SIan Rogers { 29*b7a2b011SIan Rogers Dwfl *dwfl = dso__libdw(dso); 30*b7a2b011SIan Rogers const char *dso_name; 31*b7a2b011SIan Rogers Dwfl_Module *mod; 32*b7a2b011SIan Rogers int fd; 33*b7a2b011SIan Rogers 34*b7a2b011SIan Rogers if (dwfl) 35*b7a2b011SIan Rogers return dwfl; 36*b7a2b011SIan Rogers 37*b7a2b011SIan Rogers dso_name = dso__long_name(dso); 38*b7a2b011SIan Rogers /* 39*b7a2b011SIan Rogers * Initialize Dwfl session. 40*b7a2b011SIan Rogers * We need to open the DSO file to report it to libdw. 41*b7a2b011SIan Rogers */ 42*b7a2b011SIan Rogers fd = open(dso_name, O_RDONLY); 43*b7a2b011SIan Rogers if (fd < 0) 44*b7a2b011SIan Rogers return NULL; 45*b7a2b011SIan Rogers 46*b7a2b011SIan Rogers dwfl = dwfl_begin(&offline_callbacks); 47*b7a2b011SIan Rogers if (!dwfl) { 48*b7a2b011SIan Rogers close(fd); 49*b7a2b011SIan Rogers return NULL; 50*b7a2b011SIan Rogers } 51*b7a2b011SIan Rogers 52*b7a2b011SIan Rogers /* 53*b7a2b011SIan Rogers * If the report is successful, the file descriptor fd is consumed 54*b7a2b011SIan Rogers * and closed by the Dwfl. If not, it is not closed. 55*b7a2b011SIan Rogers */ 56*b7a2b011SIan Rogers mod = dwfl_report_offline(dwfl, dso_name, dso_name, fd); 57*b7a2b011SIan Rogers if (!mod) { 58*b7a2b011SIan Rogers dwfl_end(dwfl); 59*b7a2b011SIan Rogers close(fd); 60*b7a2b011SIan Rogers return NULL; 61*b7a2b011SIan Rogers } 62*b7a2b011SIan Rogers 63*b7a2b011SIan Rogers dwfl_report_end(dwfl, /*removed=*/NULL, /*arg=*/NULL); 64*b7a2b011SIan Rogers dso__set_libdw(dso, dwfl); 65*b7a2b011SIan Rogers 66*b7a2b011SIan Rogers return dwfl; 67*b7a2b011SIan Rogers } 68*b7a2b011SIan Rogers 6988c51002SIan Rogers struct libdw_a2l_cb_args { 7088c51002SIan Rogers struct dso *dso; 7188c51002SIan Rogers struct symbol *sym; 7288c51002SIan Rogers struct inline_node *node; 7388c51002SIan Rogers char *leaf_srcline; 7488c51002SIan Rogers bool leaf_srcline_used; 7588c51002SIan Rogers }; 7688c51002SIan Rogers 7788c51002SIan Rogers static int libdw_a2l_cb(Dwarf_Die *die, void *_args) 7888c51002SIan Rogers { 7988c51002SIan Rogers struct libdw_a2l_cb_args *args = _args; 8088c51002SIan Rogers struct symbol *inline_sym = new_inline_sym(args->dso, args->sym, dwarf_diename(die)); 8188c51002SIan Rogers const char *call_fname = die_get_call_file(die); 8288c51002SIan Rogers char *call_srcline = srcline__unknown; 8388c51002SIan Rogers struct inline_list *ilist; 8488c51002SIan Rogers 8588c51002SIan Rogers if (!inline_sym) 8688c51002SIan Rogers return -ENOMEM; 8788c51002SIan Rogers 8888c51002SIan Rogers /* Assign caller information to the parent. */ 8988c51002SIan Rogers if (call_fname) 9088c51002SIan Rogers call_srcline = srcline_from_fileline(call_fname, die_get_call_lineno(die)); 9188c51002SIan Rogers 9288c51002SIan Rogers list_for_each_entry(ilist, &args->node->val, list) { 936cc3e0f6SIan Rogers if (args->leaf_srcline == ilist->srcline) 946cc3e0f6SIan Rogers args->leaf_srcline_used = false; 956cc3e0f6SIan Rogers else if (ilist->srcline != srcline__unknown) 966cc3e0f6SIan Rogers free(ilist->srcline); 9788c51002SIan Rogers ilist->srcline = call_srcline; 9888c51002SIan Rogers call_srcline = NULL; 9988c51002SIan Rogers break; 10088c51002SIan Rogers } 1016cc3e0f6SIan Rogers if (call_srcline && call_srcline != srcline__unknown) 10288c51002SIan Rogers free(call_srcline); 10388c51002SIan Rogers 10488c51002SIan Rogers /* Add this symbol to the chain as the leaf. */ 1056cc3e0f6SIan Rogers if (!args->leaf_srcline_used) { 10688c51002SIan Rogers inline_list__append_tail(inline_sym, args->leaf_srcline, args->node); 10788c51002SIan Rogers args->leaf_srcline_used = true; 1086cc3e0f6SIan Rogers } else { 1096cc3e0f6SIan Rogers inline_list__append_tail(inline_sym, strdup(args->leaf_srcline), args->node); 1106cc3e0f6SIan Rogers } 11188c51002SIan Rogers return 0; 11288c51002SIan Rogers } 11388c51002SIan Rogers 114*b7a2b011SIan Rogers int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr, 11588c51002SIan Rogers struct dso *dso, bool unwind_inlines, 11688c51002SIan Rogers struct inline_node *node, struct symbol *sym) 11788c51002SIan Rogers { 118*b7a2b011SIan Rogers Dwfl *dwfl = dso__libdw_dwfl(dso); 11988c51002SIan Rogers Dwfl_Module *mod; 12088c51002SIan Rogers Dwfl_Line *dwline; 12188c51002SIan Rogers Dwarf_Addr bias; 12288c51002SIan Rogers const char *src; 12388c51002SIan Rogers int lineno = 0; 12488c51002SIan Rogers 125*b7a2b011SIan Rogers if (!dwfl) 12688c51002SIan Rogers return 0; 12788c51002SIan Rogers 12888c51002SIan Rogers mod = dwfl_addrmodule(dwfl, addr); 12988c51002SIan Rogers if (!mod) 13088c51002SIan Rogers return 0; 13188c51002SIan Rogers 13288c51002SIan Rogers /* 13388c51002SIan Rogers * Get/ignore the dwarf information. Determine the bias, difference 13488c51002SIan Rogers * between the regular ELF addr2line addresses and those to use with 13588c51002SIan Rogers * libdw. 13688c51002SIan Rogers */ 13788c51002SIan Rogers if (!dwfl_module_getdwarf(mod, &bias)) 13888c51002SIan Rogers return 0; 13988c51002SIan Rogers 14088c51002SIan Rogers /* Find source line information for the address. */ 14188c51002SIan Rogers dwline = dwfl_module_getsrc(mod, addr + bias); 14288c51002SIan Rogers if (!dwline) 14388c51002SIan Rogers return 0; 14488c51002SIan Rogers 14588c51002SIan Rogers /* Get line information. */ 14688c51002SIan Rogers src = dwfl_lineinfo(dwline, /*addr=*/NULL, &lineno, /*col=*/NULL, /*mtime=*/NULL, 14788c51002SIan Rogers /*length=*/NULL); 14888c51002SIan Rogers 14988c51002SIan Rogers if (file) 15088c51002SIan Rogers *file = src ? strdup(src) : NULL; 15188c51002SIan Rogers if (line_nr) 15288c51002SIan Rogers *line_nr = lineno; 15388c51002SIan Rogers 15488c51002SIan Rogers /* Optionally unwind inline function call chain. */ 15588c51002SIan Rogers if (unwind_inlines && node) { 15688c51002SIan Rogers Dwarf_Addr unused_bias; 15788c51002SIan Rogers Dwarf_Die *cudie = dwfl_module_addrdie(mod, addr + bias, &unused_bias); 15888c51002SIan Rogers struct libdw_a2l_cb_args args = { 15988c51002SIan Rogers .dso = dso, 16088c51002SIan Rogers .sym = sym, 16188c51002SIan Rogers .node = node, 16288c51002SIan Rogers .leaf_srcline = srcline_from_fileline(src ?: "<unknown>", lineno), 16388c51002SIan Rogers }; 16488c51002SIan Rogers 16588c51002SIan Rogers /* Walk from the parent down to the leaf. */ 16688c51002SIan Rogers cu_walk_functions_at(cudie, addr, libdw_a2l_cb, &args); 16788c51002SIan Rogers 16888c51002SIan Rogers if (!args.leaf_srcline_used) 16988c51002SIan Rogers free(args.leaf_srcline); 17088c51002SIan Rogers } 17188c51002SIan Rogers return 1; 17288c51002SIan Rogers } 173