xref: /linux/tools/perf/util/disasm_bpf.c (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
1 // SPDX-License-Identifier: GPL-2.0-only
2 
3 #include "util/annotate.h"
4 #include "util/disasm_bpf.h"
5 #include "util/symbol.h"
6 #include <linux/zalloc.h>
7 #include <string.h>
8 
9 #if defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT)
10 #define PACKAGE "perf"
11 #include <bfd.h>
12 #include <bpf/bpf.h>
13 #include <bpf/btf.h>
14 #include <bpf/libbpf.h>
15 #include <dis-asm.h>
16 #include <errno.h>
17 #include <linux/btf.h>
18 #include <tools/dis-asm-compat.h>
19 
20 #include "util/bpf-event.h"
21 #include "util/bpf-utils.h"
22 #include "util/debug.h"
23 #include "util/dso.h"
24 #include "util/map.h"
25 #include "util/env.h"
26 #include "util/util.h"
27 
28 int symbol__disassemble_bpf(struct symbol *sym, struct annotate_args *args)
29 {
30 	struct annotation *notes = symbol__annotation(sym);
31 	struct bpf_prog_linfo *prog_linfo = NULL;
32 	struct bpf_prog_info_node *info_node;
33 	int len = sym->end - sym->start;
34 	disassembler_ftype disassemble;
35 	struct map *map = args->ms.map;
36 	struct perf_bpil *info_linear;
37 	struct disassemble_info info;
38 	struct dso *dso = map__dso(map);
39 	int pc = 0, count, sub_id;
40 	struct btf *btf = NULL;
41 	char tpath[PATH_MAX];
42 	size_t buf_size;
43 	int nr_skip = 0;
44 	char *buf;
45 	bfd *bfdf;
46 	int ret;
47 	FILE *s;
48 
49 	if (dso__binary_type(dso) != DSO_BINARY_TYPE__BPF_PROG_INFO)
50 		return SYMBOL_ANNOTATE_ERRNO__BPF_INVALID_FILE;
51 
52 	pr_debug("%s: handling sym %s addr %" PRIx64 " len %" PRIx64 "\n", __func__,
53 		  sym->name, sym->start, sym->end - sym->start);
54 
55 	memset(tpath, 0, sizeof(tpath));
56 	perf_exe(tpath, sizeof(tpath));
57 
58 	bfdf = bfd_openr(tpath, NULL);
59 	if (bfdf == NULL)
60 		abort();
61 
62 	if (!bfd_check_format(bfdf, bfd_object))
63 		abort();
64 
65 	s = open_memstream(&buf, &buf_size);
66 	if (!s) {
67 		ret = errno;
68 		goto out;
69 	}
70 	init_disassemble_info_compat(&info, s,
71 				     (fprintf_ftype) fprintf,
72 				     fprintf_styled);
73 	info.arch = bfd_get_arch(bfdf);
74 	info.mach = bfd_get_mach(bfdf);
75 
76 	info_node = perf_env__find_bpf_prog_info(dso__bpf_prog(dso)->env,
77 						 dso__bpf_prog(dso)->id);
78 	if (!info_node) {
79 		ret = SYMBOL_ANNOTATE_ERRNO__BPF_MISSING_BTF;
80 		goto out;
81 	}
82 	info_linear = info_node->info_linear;
83 	sub_id = dso__bpf_prog(dso)->sub_id;
84 
85 	info.buffer = (void *)(uintptr_t)(info_linear->info.jited_prog_insns);
86 	info.buffer_length = info_linear->info.jited_prog_len;
87 
88 	if (info_linear->info.nr_line_info)
89 		prog_linfo = bpf_prog_linfo__new(&info_linear->info);
90 
91 	if (info_linear->info.btf_id) {
92 		struct btf_node *node;
93 
94 		node = perf_env__find_btf(dso__bpf_prog(dso)->env,
95 					  info_linear->info.btf_id);
96 		if (node)
97 			btf = btf__new((__u8 *)(node->data),
98 				       node->data_size);
99 	}
100 
101 	disassemble_init_for_target(&info);
102 
103 #ifdef DISASM_FOUR_ARGS_SIGNATURE
104 	disassemble = disassembler(info.arch,
105 				   bfd_big_endian(bfdf),
106 				   info.mach,
107 				   bfdf);
108 #else
109 	disassemble = disassembler(bfdf);
110 #endif
111 	if (disassemble == NULL)
112 		abort();
113 
114 	fflush(s);
115 	do {
116 		const struct bpf_line_info *linfo = NULL;
117 		struct disasm_line *dl;
118 		size_t prev_buf_size;
119 		const char *srcline;
120 		u64 addr;
121 
122 		addr = pc + ((u64 *)(uintptr_t)(info_linear->info.jited_ksyms))[sub_id];
123 		count = disassemble(pc, &info);
124 
125 		if (prog_linfo)
126 			linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo,
127 								addr, sub_id,
128 								nr_skip);
129 
130 		if (linfo && btf) {
131 			srcline = btf__name_by_offset(btf, linfo->line_off);
132 			nr_skip++;
133 		} else
134 			srcline = NULL;
135 
136 		fprintf(s, "\n");
137 		prev_buf_size = buf_size;
138 		fflush(s);
139 
140 		if (!annotate_opts.hide_src_code && srcline) {
141 			args->offset = -1;
142 			args->line = strdup(srcline);
143 			args->line_nr = 0;
144 			args->fileloc = NULL;
145 			args->ms.sym  = sym;
146 			dl = disasm_line__new(args);
147 			if (dl) {
148 				annotation_line__add(&dl->al,
149 						     &notes->src->source);
150 			}
151 		}
152 
153 		args->offset = pc;
154 		args->line = buf + prev_buf_size;
155 		args->line_nr = 0;
156 		args->fileloc = NULL;
157 		args->ms.sym  = sym;
158 		dl = disasm_line__new(args);
159 		if (dl)
160 			annotation_line__add(&dl->al, &notes->src->source);
161 
162 		pc += count;
163 	} while (count > 0 && pc < len);
164 
165 	ret = 0;
166 out:
167 	free(prog_linfo);
168 	btf__free(btf);
169 	fclose(s);
170 	bfd_close(bfdf);
171 	return ret;
172 }
173 #else // defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT)
174 int symbol__disassemble_bpf(struct symbol *sym __maybe_unused, struct annotate_args *args __maybe_unused)
175 {
176 	return SYMBOL_ANNOTATE_ERRNO__NO_LIBOPCODES_FOR_BPF;
177 }
178 #endif // defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT)
179 
180 int symbol__disassemble_bpf_image(struct symbol *sym, struct annotate_args *args)
181 {
182 	struct annotation *notes = symbol__annotation(sym);
183 	struct disasm_line *dl;
184 
185 	args->offset = -1;
186 	args->line = strdup("to be implemented");
187 	args->line_nr = 0;
188 	args->fileloc = NULL;
189 	dl = disasm_line__new(args);
190 	if (dl)
191 		annotation_line__add(&dl->al, &notes->src->source);
192 
193 	zfree(&args->line);
194 	return 0;
195 }
196