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