xref: /linux/tools/bpf/bpftool/jit_disasm.c (revision ec63e2a4897075e427c121d863bd89c44578094f)
1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2 /*
3  * Based on:
4  *
5  * Minimal BPF JIT image disassembler
6  *
7  * Disassembles BPF JIT compiler emitted opcodes back to asm insn's for
8  * debugging or verification purposes.
9  *
10  * Copyright 2013 Daniel Borkmann <daniel@iogearbox.net>
11  * Licensed under the GNU General Public License, version 2.0 (GPLv2)
12  */
13 
14 #include <stdarg.h>
15 #include <stdint.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <assert.h>
19 #include <unistd.h>
20 #include <string.h>
21 #include <bfd.h>
22 #include <dis-asm.h>
23 #include <sys/stat.h>
24 #include <limits.h>
25 #include <libbpf.h>
26 
27 #include "json_writer.h"
28 #include "main.h"
29 
30 static void get_exec_path(char *tpath, size_t size)
31 {
32 	const char *path = "/proc/self/exe";
33 	ssize_t len;
34 
35 	len = readlink(path, tpath, size - 1);
36 	assert(len > 0);
37 	tpath[len] = 0;
38 }
39 
40 static int oper_count;
41 static int fprintf_json(void *out, const char *fmt, ...)
42 {
43 	va_list ap;
44 	char *s;
45 
46 	va_start(ap, fmt);
47 	if (!oper_count) {
48 		int i;
49 
50 		s = va_arg(ap, char *);
51 
52 		/* Strip trailing spaces */
53 		i = strlen(s) - 1;
54 		while (s[i] == ' ')
55 			s[i--] = '\0';
56 
57 		jsonw_string_field(json_wtr, "operation", s);
58 		jsonw_name(json_wtr, "operands");
59 		jsonw_start_array(json_wtr);
60 		oper_count++;
61 	} else if (!strcmp(fmt, ",")) {
62 		   /* Skip */
63 	} else {
64 		s = va_arg(ap, char *);
65 		jsonw_string(json_wtr, s);
66 		oper_count++;
67 	}
68 	va_end(ap);
69 	return 0;
70 }
71 
72 void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
73 		       const char *arch, const char *disassembler_options,
74 		       const struct btf *btf,
75 		       const struct bpf_prog_linfo *prog_linfo,
76 		       __u64 func_ksym, unsigned int func_idx,
77 		       bool linum)
78 {
79 	const struct bpf_line_info *linfo = NULL;
80 	disassembler_ftype disassemble;
81 	struct disassemble_info info;
82 	unsigned int nr_skip = 0;
83 	int count, i, pc = 0;
84 	char tpath[PATH_MAX];
85 	bfd *bfdf;
86 
87 	if (!len)
88 		return;
89 
90 	memset(tpath, 0, sizeof(tpath));
91 	get_exec_path(tpath, sizeof(tpath));
92 
93 	bfdf = bfd_openr(tpath, NULL);
94 	assert(bfdf);
95 	assert(bfd_check_format(bfdf, bfd_object));
96 
97 	if (json_output)
98 		init_disassemble_info(&info, stdout,
99 				      (fprintf_ftype) fprintf_json);
100 	else
101 		init_disassemble_info(&info, stdout,
102 				      (fprintf_ftype) fprintf);
103 
104 	/* Update architecture info for offload. */
105 	if (arch) {
106 		const bfd_arch_info_type *inf = bfd_scan_arch(arch);
107 
108 		if (inf) {
109 			bfdf->arch_info = inf;
110 		} else {
111 			p_err("No libbfd support for %s", arch);
112 			return;
113 		}
114 	}
115 
116 	info.arch = bfd_get_arch(bfdf);
117 	info.mach = bfd_get_mach(bfdf);
118 	if (disassembler_options)
119 		info.disassembler_options = disassembler_options;
120 	info.buffer = image;
121 	info.buffer_length = len;
122 
123 	disassemble_init_for_target(&info);
124 
125 #ifdef DISASM_FOUR_ARGS_SIGNATURE
126 	disassemble = disassembler(info.arch,
127 				   bfd_big_endian(bfdf),
128 				   info.mach,
129 				   bfdf);
130 #else
131 	disassemble = disassembler(bfdf);
132 #endif
133 	assert(disassemble);
134 
135 	if (json_output)
136 		jsonw_start_array(json_wtr);
137 	do {
138 		if (prog_linfo) {
139 			linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo,
140 								func_ksym + pc,
141 								func_idx,
142 								nr_skip);
143 			if (linfo)
144 				nr_skip++;
145 		}
146 
147 		if (json_output) {
148 			jsonw_start_object(json_wtr);
149 			oper_count = 0;
150 			if (linfo)
151 				btf_dump_linfo_json(btf, linfo, linum);
152 			jsonw_name(json_wtr, "pc");
153 			jsonw_printf(json_wtr, "\"0x%x\"", pc);
154 		} else {
155 			if (linfo)
156 				btf_dump_linfo_plain(btf, linfo, "; ",
157 						     linum);
158 			printf("%4x:\t", pc);
159 		}
160 
161 		count = disassemble(pc, &info);
162 		if (json_output) {
163 			/* Operand array, was started in fprintf_json. Before
164 			 * that, make sure we have a _null_ value if no operand
165 			 * other than operation code was present.
166 			 */
167 			if (oper_count == 1)
168 				jsonw_null(json_wtr);
169 			jsonw_end_array(json_wtr);
170 		}
171 
172 		if (opcodes) {
173 			if (json_output) {
174 				jsonw_name(json_wtr, "opcodes");
175 				jsonw_start_array(json_wtr);
176 				for (i = 0; i < count; ++i)
177 					jsonw_printf(json_wtr, "\"0x%02hhx\"",
178 						     (uint8_t)image[pc + i]);
179 				jsonw_end_array(json_wtr);
180 			} else {
181 				printf("\n\t");
182 				for (i = 0; i < count; ++i)
183 					printf("%02x ",
184 					       (uint8_t)image[pc + i]);
185 			}
186 		}
187 		if (json_output)
188 			jsonw_end_object(json_wtr);
189 		else
190 			printf("\n");
191 
192 		pc += count;
193 	} while (count > 0 && pc < len);
194 	if (json_output)
195 		jsonw_end_array(json_wtr);
196 
197 	bfd_close(bfdf);
198 }
199 
200 int disasm_init(void)
201 {
202 	bfd_init();
203 	return 0;
204 }
205