1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4
5#include <linux/kernel.h>
6
7#include "util/dso.h"
8#include "util/util.h"
9#include "util/debug.h"
10
11#include "symbol.h"
12
13#ifdef HAVE_LIBBFD_SUPPORT
14
15/*
16 * Implement addr2line using libbfd.
17 */
18#define PACKAGE "perf"
19#include <bfd.h>
20
21struct a2l_data {
22	const char 	*input;
23	u64	 	addr;
24
25	bool 		found;
26	const char 	*filename;
27	const char 	*funcname;
28	unsigned 	line;
29
30	bfd 		*abfd;
31	asymbol 	**syms;
32};
33
34static int bfd_error(const char *string)
35{
36	const char *errmsg;
37
38	errmsg = bfd_errmsg(bfd_get_error());
39	fflush(stdout);
40
41	if (string)
42		pr_debug("%s: %s\n", string, errmsg);
43	else
44		pr_debug("%s\n", errmsg);
45
46	return -1;
47}
48
49static int slurp_symtab(bfd *abfd, struct a2l_data *a2l)
50{
51	long storage;
52	long symcount;
53	asymbol **syms;
54	bfd_boolean dynamic = FALSE;
55
56	if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0)
57		return bfd_error(bfd_get_filename(abfd));
58
59	storage = bfd_get_symtab_upper_bound(abfd);
60	if (storage == 0L) {
61		storage = bfd_get_dynamic_symtab_upper_bound(abfd);
62		dynamic = TRUE;
63	}
64	if (storage < 0L)
65		return bfd_error(bfd_get_filename(abfd));
66
67	syms = malloc(storage);
68	if (dynamic)
69		symcount = bfd_canonicalize_dynamic_symtab(abfd, syms);
70	else
71		symcount = bfd_canonicalize_symtab(abfd, syms);
72
73	if (symcount < 0) {
74		free(syms);
75		return bfd_error(bfd_get_filename(abfd));
76	}
77
78	a2l->syms = syms;
79	return 0;
80}
81
82static void find_address_in_section(bfd *abfd, asection *section, void *data)
83{
84	bfd_vma pc, vma;
85	bfd_size_type size;
86	struct a2l_data *a2l = data;
87
88	if (a2l->found)
89		return;
90
91	if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0)
92		return;
93
94	pc = a2l->addr;
95	vma = bfd_get_section_vma(abfd, section);
96	size = bfd_get_section_size(section);
97
98	if (pc < vma || pc >= vma + size)
99		return;
100
101	a2l->found = bfd_find_nearest_line(abfd, section, a2l->syms, pc - vma,
102					   &a2l->filename, &a2l->funcname,
103					   &a2l->line);
104}
105
106static struct a2l_data *addr2line_init(const char *path)
107{
108	bfd *abfd;
109	struct a2l_data *a2l = NULL;
110
111	abfd = bfd_openr(path, NULL);
112	if (abfd == NULL)
113		return NULL;
114
115	if (!bfd_check_format(abfd, bfd_object))
116		goto out;
117
118	a2l = zalloc(sizeof(*a2l));
119	if (a2l == NULL)
120		goto out;
121
122	a2l->abfd = abfd;
123	a2l->input = strdup(path);
124	if (a2l->input == NULL)
125		goto out;
126
127	if (slurp_symtab(abfd, a2l))
128		goto out;
129
130	return a2l;
131
132out:
133	if (a2l) {
134		zfree((char **)&a2l->input);
135		free(a2l);
136	}
137	bfd_close(abfd);
138	return NULL;
139}
140
141static void addr2line_cleanup(struct a2l_data *a2l)
142{
143	if (a2l->abfd)
144		bfd_close(a2l->abfd);
145	zfree((char **)&a2l->input);
146	zfree(&a2l->syms);
147	free(a2l);
148}
149
150static int addr2line(const char *dso_name, u64 addr,
151		     char **file, unsigned int *line, struct dso *dso)
152{
153	int ret = 0;
154	struct a2l_data *a2l = dso->a2l;
155
156	if (!a2l) {
157		dso->a2l = addr2line_init(dso_name);
158		a2l = dso->a2l;
159	}
160
161	if (a2l == NULL) {
162		pr_warning("addr2line_init failed for %s\n", dso_name);
163		return 0;
164	}
165
166	a2l->addr = addr;
167	a2l->found = false;
168
169	bfd_map_over_sections(a2l->abfd, find_address_in_section, a2l);
170
171	if (a2l->found && a2l->filename) {
172		*file = strdup(a2l->filename);
173		*line = a2l->line;
174
175		if (*file)
176			ret = 1;
177	}
178
179	return ret;
180}
181
182void dso__free_a2l(struct dso *dso)
183{
184	struct a2l_data *a2l = dso->a2l;
185
186	if (!a2l)
187		return;
188
189	addr2line_cleanup(a2l);
190
191	dso->a2l = NULL;
192}
193
194#else /* HAVE_LIBBFD_SUPPORT */
195
196static int addr2line(const char *dso_name, u64 addr,
197		     char **file, unsigned int *line_nr,
198		     struct dso *dso __maybe_unused)
199{
200	FILE *fp;
201	char cmd[PATH_MAX];
202	char *filename = NULL;
203	size_t len;
204	char *sep;
205	int ret = 0;
206
207	scnprintf(cmd, sizeof(cmd), "addr2line -e %s %016"PRIx64,
208		  dso_name, addr);
209
210	fp = popen(cmd, "r");
211	if (fp == NULL) {
212		pr_warning("popen failed for %s\n", dso_name);
213		return 0;
214	}
215
216	if (getline(&filename, &len, fp) < 0 || !len) {
217		pr_warning("addr2line has no output for %s\n", dso_name);
218		goto out;
219	}
220
221	sep = strchr(filename, '\n');
222	if (sep)
223		*sep = '\0';
224
225	if (!strcmp(filename, "??:0")) {
226		pr_debug("no debugging info in %s\n", dso_name);
227		free(filename);
228		goto out;
229	}
230
231	sep = strchr(filename, ':');
232	if (sep) {
233		*sep++ = '\0';
234		*file = filename;
235		*line_nr = strtoul(sep, NULL, 0);
236		ret = 1;
237	}
238out:
239	pclose(fp);
240	return ret;
241}
242
243void dso__free_a2l(struct dso *dso __maybe_unused)
244{
245}
246
247#endif /* HAVE_LIBBFD_SUPPORT */
248
249/*
250 * Number of addr2line failures (without success) before disabling it for that
251 * dso.
252 */
253#define A2L_FAIL_LIMIT 123
254
255char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
256		  bool show_sym)
257{
258	char *file = NULL;
259	unsigned line = 0;
260	char *srcline;
261	const char *dso_name;
262
263	if (!dso->has_srcline)
264		goto out;
265
266	if (dso->symsrc_filename)
267		dso_name = dso->symsrc_filename;
268	else
269		dso_name = dso->long_name;
270
271	if (dso_name[0] == '[')
272		goto out;
273
274	if (!strncmp(dso_name, "/tmp/perf-", 10))
275		goto out;
276
277	if (!addr2line(dso_name, addr, &file, &line, dso))
278		goto out;
279
280	if (asprintf(&srcline, "%s:%u", basename(file), line) < 0) {
281		free(file);
282		goto out;
283	}
284
285	dso->a2l_fails = 0;
286
287	free(file);
288	return srcline;
289
290out:
291	if (dso->a2l_fails && ++dso->a2l_fails > A2L_FAIL_LIMIT) {
292		dso->has_srcline = 0;
293		dso__free_a2l(dso);
294	}
295	if (sym) {
296		if (asprintf(&srcline, "%s+%" PRIu64, show_sym ? sym->name : "",
297					addr - sym->start) < 0)
298			return SRCLINE_UNKNOWN;
299	} else if (asprintf(&srcline, "%s[%" PRIx64 "]", dso->short_name, addr) < 0)
300		return SRCLINE_UNKNOWN;
301	return srcline;
302}
303
304void free_srcline(char *srcline)
305{
306	if (srcline && strcmp(srcline, SRCLINE_UNKNOWN) != 0)
307		free(srcline);
308}
309