1 // SPDX-License-Identifier: GPL-2.0 2 #include "dso.h" 3 #include "libdw.h" 4 #include "srcline.h" 5 #include "symbol.h" 6 #include "dwarf-aux.h" 7 #include <fcntl.h> 8 #include <unistd.h> 9 #include <elfutils/libdwfl.h> 10 11 static const Dwfl_Callbacks offline_callbacks = { 12 .find_debuginfo = dwfl_standard_find_debuginfo, 13 .section_address = dwfl_offline_section_address, 14 .find_elf = dwfl_build_id_find_elf, 15 }; 16 17 void dso__free_libdw(struct dso *dso) 18 { 19 Dwfl *dwfl = dso__libdw(dso); 20 21 if (dwfl) { 22 dwfl_end(dwfl); 23 dso__set_libdw(dso, NULL); 24 } 25 } 26 27 struct Dwfl *dso__libdw_dwfl(struct dso *dso) 28 { 29 Dwfl *dwfl = dso__libdw(dso); 30 const char *dso_name; 31 Dwfl_Module *mod; 32 int fd; 33 34 if (dwfl) 35 return dwfl; 36 37 dso_name = dso__long_name(dso); 38 /* 39 * Initialize Dwfl session. 40 * We need to open the DSO file to report it to libdw. 41 */ 42 fd = open(dso_name, O_RDONLY); 43 if (fd < 0) 44 return NULL; 45 46 dwfl = dwfl_begin(&offline_callbacks); 47 if (!dwfl) { 48 close(fd); 49 return NULL; 50 } 51 52 /* 53 * If the report is successful, the file descriptor fd is consumed 54 * and closed by the Dwfl. If not, it is not closed. 55 */ 56 mod = dwfl_report_offline(dwfl, dso_name, dso_name, fd); 57 if (!mod) { 58 dwfl_end(dwfl); 59 close(fd); 60 return NULL; 61 } 62 63 dwfl_report_end(dwfl, /*removed=*/NULL, /*arg=*/NULL); 64 dso__set_libdw(dso, dwfl); 65 66 return dwfl; 67 } 68 69 struct libdw_a2l_cb_args { 70 struct dso *dso; 71 struct symbol *sym; 72 struct inline_node *node; 73 char *leaf_srcline; 74 bool leaf_srcline_used; 75 }; 76 77 static int libdw_a2l_cb(Dwarf_Die *die, void *_args) 78 { 79 struct libdw_a2l_cb_args *args = _args; 80 struct symbol *inline_sym = new_inline_sym(args->dso, args->sym, dwarf_diename(die)); 81 const char *call_fname = die_get_call_file(die); 82 char *call_srcline = srcline__unknown; 83 struct inline_list *ilist; 84 85 if (!inline_sym) 86 return -ENOMEM; 87 88 /* Assign caller information to the parent. */ 89 if (call_fname) 90 call_srcline = srcline_from_fileline(call_fname, die_get_call_lineno(die)); 91 92 list_for_each_entry(ilist, &args->node->val, list) { 93 if (args->leaf_srcline == ilist->srcline) 94 args->leaf_srcline_used = false; 95 else if (ilist->srcline != srcline__unknown) 96 free(ilist->srcline); 97 ilist->srcline = call_srcline; 98 call_srcline = NULL; 99 break; 100 } 101 if (call_srcline && call_srcline != srcline__unknown) 102 free(call_srcline); 103 104 /* Add this symbol to the chain as the leaf. */ 105 if (!args->leaf_srcline_used) { 106 inline_list__append_tail(inline_sym, args->leaf_srcline, args->node); 107 args->leaf_srcline_used = true; 108 } else { 109 inline_list__append_tail(inline_sym, strdup(args->leaf_srcline), args->node); 110 } 111 return 0; 112 } 113 114 int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr, 115 struct dso *dso, bool unwind_inlines, 116 struct inline_node *node, struct symbol *sym) 117 { 118 Dwfl *dwfl = dso__libdw_dwfl(dso); 119 Dwfl_Module *mod; 120 Dwfl_Line *dwline; 121 Dwarf_Addr bias; 122 const char *src; 123 int lineno = 0; 124 125 if (!dwfl) 126 return 0; 127 128 mod = dwfl_addrmodule(dwfl, addr); 129 if (!mod) 130 return 0; 131 132 /* 133 * Get/ignore the dwarf information. Determine the bias, difference 134 * between the regular ELF addr2line addresses and those to use with 135 * libdw. 136 */ 137 if (!dwfl_module_getdwarf(mod, &bias)) 138 return 0; 139 140 /* Find source line information for the address. */ 141 dwline = dwfl_module_getsrc(mod, addr + bias); 142 if (!dwline) 143 return 0; 144 145 /* Get line information. */ 146 src = dwfl_lineinfo(dwline, /*addr=*/NULL, &lineno, /*col=*/NULL, /*mtime=*/NULL, 147 /*length=*/NULL); 148 149 if (file) 150 *file = src ? strdup(src) : NULL; 151 if (line_nr) 152 *line_nr = lineno; 153 154 /* Optionally unwind inline function call chain. */ 155 if (unwind_inlines && node) { 156 Dwarf_Addr unused_bias; 157 Dwarf_Die *cudie = dwfl_module_addrdie(mod, addr + bias, &unused_bias); 158 struct libdw_a2l_cb_args args = { 159 .dso = dso, 160 .sym = sym, 161 .node = node, 162 .leaf_srcline = srcline_from_fileline(src ?: "<unknown>", lineno), 163 }; 164 165 /* Walk from the parent down to the leaf. */ 166 cu_walk_functions_at(cudie, addr, libdw_a2l_cb, &args); 167 168 if (!args.leaf_srcline_used) 169 free(args.leaf_srcline); 170 } 171 return 1; 172 } 173