xref: /linux/tools/perf/util/debuginfo.c (revision 31088ccf0312b1a547046f1f69890ede07834a30)
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