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 */
debuginfo__init_offline_dwarf(struct debuginfo * dbg,const char * path)40 static int debuginfo__init_offline_dwarf(struct debuginfo *dbg,
41 const char *path)
42 {
43 GElf_Addr dummy;
44 int fd;
45
46 fd = open(path, O_RDONLY);
47 if (fd < 0)
48 return fd;
49
50 dbg->dwfl = dwfl_begin(&offline_callbacks);
51 if (!dbg->dwfl)
52 goto error;
53
54 dwfl_report_begin(dbg->dwfl);
55 dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd);
56 if (!dbg->mod)
57 goto error;
58
59 dbg->dbg = dwfl_module_getdwarf(dbg->mod, &dbg->bias);
60 if (!dbg->dbg)
61 goto error;
62
63 dwfl_module_build_id(dbg->mod, &dbg->build_id, &dummy);
64
65 dwfl_report_end(dbg->dwfl, NULL, NULL);
66
67 return 0;
68 error:
69 if (dbg->dwfl)
70 dwfl_end(dbg->dwfl);
71 else
72 close(fd);
73 memset(dbg, 0, sizeof(*dbg));
74
75 return -ENOENT;
76 }
77
__debuginfo__new(const char * path)78 static struct debuginfo *__debuginfo__new(const char *path)
79 {
80 struct debuginfo *dbg = zalloc(sizeof(*dbg));
81 if (!dbg)
82 return NULL;
83
84 if (debuginfo__init_offline_dwarf(dbg, path) < 0)
85 zfree(&dbg);
86 if (dbg)
87 pr_debug("Open Debuginfo file: %s\n", path);
88 return dbg;
89 }
90
91 enum dso_binary_type distro_dwarf_types[] = {
92 DSO_BINARY_TYPE__FEDORA_DEBUGINFO,
93 DSO_BINARY_TYPE__UBUNTU_DEBUGINFO,
94 DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO,
95 DSO_BINARY_TYPE__BUILDID_DEBUGINFO,
96 DSO_BINARY_TYPE__MIXEDUP_UBUNTU_DEBUGINFO,
97 DSO_BINARY_TYPE__NOT_FOUND,
98 };
99
debuginfo__new(const char * path)100 struct debuginfo *debuginfo__new(const char *path)
101 {
102 enum dso_binary_type *type;
103 char buf[PATH_MAX], nil = '\0';
104 struct dso *dso;
105 struct debuginfo *dinfo = NULL;
106 struct build_id bid = { .size = 0};
107
108 /* Try to open distro debuginfo files */
109 dso = dso__new(path);
110 if (!dso)
111 goto out;
112
113 /*
114 * Set the build id for DSO_BINARY_TYPE__BUILDID_DEBUGINFO. Don't block
115 * incase the path isn't for a regular file.
116 */
117 assert(!dso__has_build_id(dso));
118 if (filename__read_build_id(path, &bid, /*block=*/false) > 0)
119 dso__set_build_id(dso, &bid);
120
121 for (type = distro_dwarf_types;
122 !dinfo && *type != DSO_BINARY_TYPE__NOT_FOUND;
123 type++) {
124 if (dso__read_binary_type_filename(dso, *type, &nil,
125 buf, PATH_MAX) < 0)
126 continue;
127 dinfo = __debuginfo__new(buf);
128 }
129 dso__put(dso);
130
131 out:
132 if (dinfo)
133 return dinfo;
134
135 /* if failed to open all distro debuginfo, open given binary */
136 symbol__join_symfs(buf, path);
137 return __debuginfo__new(buf);
138 }
139
debuginfo__delete(struct debuginfo * dbg)140 void debuginfo__delete(struct debuginfo *dbg)
141 {
142 if (dbg) {
143 if (dbg->dwfl)
144 dwfl_end(dbg->dwfl);
145 free(dbg);
146 }
147 }
148
149 /* For the kernel module, we need a special code to get a DIE */
debuginfo__get_text_offset(struct debuginfo * dbg,Dwarf_Addr * offs,bool adjust_offset)150 int debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs,
151 bool adjust_offset)
152 {
153 int n, i;
154 Elf32_Word shndx;
155 Elf_Scn *scn;
156 Elf *elf;
157 GElf_Shdr mem, *shdr;
158 const char *p;
159
160 elf = dwfl_module_getelf(dbg->mod, &dbg->bias);
161 if (!elf)
162 return -EINVAL;
163
164 /* Get the number of relocations */
165 n = dwfl_module_relocations(dbg->mod);
166 if (n < 0)
167 return -ENOENT;
168 /* Search the relocation related .text section */
169 for (i = 0; i < n; i++) {
170 p = dwfl_module_relocation_info(dbg->mod, i, &shndx);
171 if (strcmp(p, ".text") == 0) {
172 /* OK, get the section header */
173 scn = elf_getscn(elf, shndx);
174 if (!scn)
175 return -ENOENT;
176 shdr = gelf_getshdr(scn, &mem);
177 if (!shdr)
178 return -ENOENT;
179 *offs = shdr->sh_addr;
180 if (adjust_offset)
181 *offs -= shdr->sh_offset;
182 }
183 }
184 return 0;
185 }
186
187 #ifdef HAVE_DEBUGINFOD_SUPPORT
get_source_from_debuginfod(const char * raw_path,const char * sbuild_id,char ** new_path)188 int get_source_from_debuginfod(const char *raw_path,
189 const char *sbuild_id, char **new_path)
190 {
191 debuginfod_client *c = debuginfod_begin();
192 const char *p = raw_path;
193 int fd;
194
195 if (!c)
196 return -ENOMEM;
197
198 fd = debuginfod_find_source(c, (const unsigned char *)sbuild_id,
199 0, p, new_path);
200 pr_debug("Search %s from debuginfod -> %d\n", p, fd);
201 if (fd >= 0)
202 close(fd);
203 debuginfod_end(c);
204 if (fd < 0) {
205 pr_debug("Failed to find %s in debuginfod (%s)\n",
206 raw_path, sbuild_id);
207 return -ENOENT;
208 }
209 pr_debug("Got a source %s\n", *new_path);
210
211 return 0;
212 }
213 #endif /* HAVE_DEBUGINFOD_SUPPORT */
214