1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * DWARF debug information handling code. Copied from probe-finder.c. 4 * 5 * Written by Masami Hiramatsu <mhiramat@redhat.com> 6 */ 7 8 #include <errno.h> 9 #include <fcntl.h> 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <string.h> 13 #include <unistd.h> 14 #include <linux/zalloc.h> 15 16 #include "build-id.h" 17 #include "dso.h" 18 #include "debug.h" 19 #include "debuginfo.h" 20 #include "symbol.h" 21 22 #ifdef HAVE_DEBUGINFOD_SUPPORT 23 #include <elfutils/debuginfod.h> 24 #endif 25 26 /* Dwarf FL wrappers */ 27 static char *debuginfo_path; /* Currently dummy */ 28 29 static const Dwfl_Callbacks offline_callbacks = { 30 .find_debuginfo = dwfl_standard_find_debuginfo, 31 .debuginfo_path = &debuginfo_path, 32 33 .section_address = dwfl_offline_section_address, 34 35 /* We use this table for core files too. */ 36 .find_elf = dwfl_build_id_find_elf, 37 }; 38 39 /* Get a Dwarf from offline image */ 40 static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, 41 const char *path) 42 { 43 GElf_Addr dummy; 44 int fd; 45 bool fd_consumed = false; 46 47 fd = open(path, O_RDONLY); 48 if (fd < 0) 49 return fd; 50 51 dbg->dwfl = dwfl_begin(&offline_callbacks); 52 if (!dbg->dwfl) 53 goto error; 54 55 dwfl_report_begin(dbg->dwfl); 56 dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd); 57 if (!dbg->mod) 58 goto error; 59 fd_consumed = true; 60 61 dbg->dbg = dwfl_module_getdwarf(dbg->mod, &dbg->bias); 62 if (!dbg->dbg) 63 goto error; 64 65 dwfl_module_build_id(dbg->mod, &dbg->build_id, &dummy); 66 67 if (dwfl_report_end(dbg->dwfl, NULL, NULL) != 0) 68 goto error; 69 70 return 0; 71 error: 72 if (dbg->dwfl) 73 dwfl_end(dbg->dwfl); 74 if (!fd_consumed) 75 close(fd); 76 memset(dbg, 0, sizeof(*dbg)); 77 78 return -ENOENT; 79 } 80 81 static struct debuginfo *__debuginfo__new(const char *path) 82 { 83 struct debuginfo *dbg = zalloc(sizeof(*dbg)); 84 if (!dbg) 85 return NULL; 86 87 if (debuginfo__init_offline_dwarf(dbg, path) < 0) 88 zfree(&dbg); 89 if (dbg) 90 pr_debug("Open Debuginfo file: %s\n", path); 91 return dbg; 92 } 93 94 struct debuginfo *debuginfo__new(const char *path) 95 { 96 static const enum dso_binary_type distro_dwarf_types[] = { 97 DSO_BINARY_TYPE__FEDORA_DEBUGINFO, 98 DSO_BINARY_TYPE__UBUNTU_DEBUGINFO, 99 DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, 100 DSO_BINARY_TYPE__BUILDID_DEBUGINFO, 101 DSO_BINARY_TYPE__MIXEDUP_UBUNTU_DEBUGINFO, 102 DSO_BINARY_TYPE__NOT_FOUND, 103 }; 104 const enum dso_binary_type *type; 105 char buf[PATH_MAX], nil = '\0'; 106 struct dso *dso; 107 struct debuginfo *dinfo = NULL; 108 struct build_id bid = { .size = 0}; 109 110 /* Try to open distro debuginfo files */ 111 dso = dso__new(path); 112 if (!dso) 113 goto out; 114 115 /* 116 * Set the build id for DSO_BINARY_TYPE__BUILDID_DEBUGINFO. Don't block 117 * incase the path isn't for a regular file. 118 */ 119 assert(!dso__has_build_id(dso)); 120 if (filename__read_build_id(path, &bid) > 0) 121 dso__set_build_id(dso, &bid); 122 123 for (type = distro_dwarf_types; 124 !dinfo && *type != DSO_BINARY_TYPE__NOT_FOUND; 125 type++) { 126 if (dso__read_binary_type_filename(dso, *type, &nil, 127 buf, PATH_MAX) < 0) 128 continue; 129 dinfo = __debuginfo__new(buf); 130 } 131 dso__put(dso); 132 133 out: 134 if (dinfo) 135 return dinfo; 136 137 /* if failed to open all distro debuginfo, open given binary */ 138 symbol__join_symfs(buf, path); 139 return __debuginfo__new(buf); 140 } 141 142 void debuginfo__delete(struct debuginfo *dbg) 143 { 144 if (dbg) { 145 if (dbg->dwfl) 146 dwfl_end(dbg->dwfl); 147 free(dbg); 148 } 149 } 150 151 /* For the kernel module, we need a special code to get a DIE */ 152 int debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs, 153 bool adjust_offset) 154 { 155 int n, i; 156 Elf32_Word shndx; 157 Elf_Scn *scn; 158 Elf *elf; 159 GElf_Shdr mem, *shdr; 160 const char *p; 161 162 elf = dwfl_module_getelf(dbg->mod, &dbg->bias); 163 if (!elf) 164 return -EINVAL; 165 166 /* Get the number of relocations */ 167 n = dwfl_module_relocations(dbg->mod); 168 if (n < 0) 169 return -ENOENT; 170 /* Search the relocation related .text section */ 171 for (i = 0; i < n; i++) { 172 p = dwfl_module_relocation_info(dbg->mod, i, &shndx); 173 if (p && strcmp(p, ".text") == 0) { 174 /* OK, get the section header */ 175 scn = elf_getscn(elf, shndx); 176 if (!scn) 177 return -ENOENT; 178 shdr = gelf_getshdr(scn, &mem); 179 if (!shdr) 180 return -ENOENT; 181 *offs = shdr->sh_addr; 182 if (adjust_offset) 183 *offs -= shdr->sh_offset; 184 } 185 } 186 return 0; 187 } 188 189 #ifdef HAVE_DEBUGINFOD_SUPPORT 190 int get_source_from_debuginfod(const char *raw_path, 191 const char *sbuild_id, char **new_path) 192 { 193 debuginfod_client *c = debuginfod_begin(); 194 const char *p = raw_path; 195 int fd; 196 197 if (!c) 198 return -ENOMEM; 199 200 fd = debuginfod_find_source(c, (const unsigned char *)sbuild_id, 201 0, p, new_path); 202 pr_debug("Search %s from debuginfod -> %d\n", p, fd); 203 if (fd >= 0) 204 close(fd); 205 debuginfod_end(c); 206 if (fd < 0) { 207 pr_debug("Failed to find %s in debuginfod (%s)\n", 208 raw_path, sbuild_id); 209 return -ENOENT; 210 } 211 pr_debug("Got a source %s\n", *new_path); 212 213 return 0; 214 } 215 #endif /* HAVE_DEBUGINFOD_SUPPORT */ 216