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