xref: /linux/tools/bpf/bpftool/jit_disasm.c (revision aec2f682d47c54ef434b2d440992626d80b1ebdc)
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 #ifndef _GNU_SOURCE
15 #define _GNU_SOURCE
16 #endif
17 #include <stdio.h>
18 #include <stdarg.h>
19 #include <stdint.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <string.h>
23 #include <sys/stat.h>
24 #include <limits.h>
25 #include <bpf/libbpf.h>
26 
27 #ifdef HAVE_LLVM_SUPPORT
28 #include <llvm-c/Core.h>
29 #include <llvm-c/Disassembler.h>
30 #include <llvm-c/Target.h>
31 #include <llvm-c/TargetMachine.h>
32 #endif
33 
34 #ifdef HAVE_LIBBFD_SUPPORT
35 #include <bfd.h>
36 #include <dis-asm.h>
37 #include <tools/dis-asm-compat.h>
38 #endif
39 
40 #include "json_writer.h"
41 #include "main.h"
42 
43 static int oper_count;
44 
45 #ifdef HAVE_LLVM_SUPPORT
46 #define DISASM_SPACER
47 
48 typedef LLVMDisasmContextRef disasm_ctx_t;
49 
50 static int printf_json(char *s)
51 {
52 	s = strtok(s, " \t");
53 	jsonw_string_field(json_wtr, "operation", s);
54 
55 	jsonw_name(json_wtr, "operands");
56 	jsonw_start_array(json_wtr);
57 	oper_count = 1;
58 
59 	while ((s = strtok(NULL, " \t,()")) != 0) {
60 		jsonw_string(json_wtr, s);
61 		oper_count++;
62 	}
63 	return 0;
64 }
65 
66 /* This callback to set the ref_type is necessary to have the LLVM disassembler
67  * print PC-relative addresses instead of byte offsets for branch instruction
68  * targets.
69  */
70 static const char *
71 symbol_lookup_callback(__maybe_unused void *disasm_info,
72 		       __maybe_unused uint64_t ref_value,
73 		       uint64_t *ref_type, __maybe_unused uint64_t ref_PC,
74 		       __maybe_unused const char **ref_name)
75 {
76 	*ref_type = LLVMDisassembler_ReferenceType_InOut_None;
77 	return NULL;
78 }
79 
80 static int
81 init_context(disasm_ctx_t *ctx, const char *arch,
82 	     __maybe_unused const char *disassembler_options,
83 	     __maybe_unused unsigned char *image, __maybe_unused ssize_t len,
84 	     __maybe_unused __u64 func_ksym)
85 {
86 	char *triple;
87 
88 	if (arch)
89 		triple = LLVMNormalizeTargetTriple(arch);
90 	else
91 		triple = LLVMGetDefaultTargetTriple();
92 	if (!triple) {
93 		p_err("Failed to retrieve triple");
94 		return -1;
95 	}
96 
97 	/*
98 	 * Enable all aarch64 ISA extensions so the disassembler can handle any
99 	 * instruction the kernel JIT might emit (e.g. ARM64 LSE atomics).
100 	 */
101 	if (!strncmp(triple, "aarch64", 7))
102 		*ctx = LLVMCreateDisasmCPUFeatures(triple, "", "+all", NULL, 0, NULL,
103 						   symbol_lookup_callback);
104 	else
105 		*ctx = LLVMCreateDisasm(triple, NULL, 0, NULL, symbol_lookup_callback);
106 	LLVMDisposeMessage(triple);
107 
108 	if (!*ctx) {
109 		p_err("Failed to create disassembler");
110 		return -1;
111 	}
112 
113 	return 0;
114 }
115 
116 static void destroy_context(disasm_ctx_t *ctx)
117 {
118 	LLVMDisposeMessage(*ctx);
119 }
120 
121 static int
122 disassemble_insn(disasm_ctx_t *ctx, unsigned char *image, ssize_t len, int pc,
123 		 __u64 func_ksym)
124 {
125 	char buf[256];
126 	int count;
127 
128 	count = LLVMDisasmInstruction(*ctx, image + pc, len - pc, func_ksym + pc,
129 				      buf, sizeof(buf));
130 	if (json_output)
131 		printf_json(buf);
132 	else
133 		printf("%s", buf);
134 
135 	return count;
136 }
137 
138 int disasm_init(void)
139 {
140 	LLVMInitializeAllTargetInfos();
141 	LLVMInitializeAllTargetMCs();
142 	LLVMInitializeAllDisassemblers();
143 	return 0;
144 }
145 #endif /* HAVE_LLVM_SUPPORT */
146 
147 #ifdef HAVE_LIBBFD_SUPPORT
148 #define DISASM_SPACER "\t"
149 
150 struct disasm_info {
151 	struct disassemble_info info;
152 	__u64 func_ksym;
153 };
154 
155 static void disasm_print_addr(bfd_vma addr, struct disassemble_info *info)
156 {
157 	struct disasm_info *dinfo = container_of(info, struct disasm_info, info);
158 
159 	addr += dinfo->func_ksym;
160 	generic_print_address(addr, info);
161 }
162 
163 typedef struct {
164 	struct disasm_info *info;
165 	disassembler_ftype disassemble;
166 	bfd *bfdf;
167 } disasm_ctx_t;
168 
169 static int get_exec_path(char *tpath, size_t size)
170 {
171 	const char *path = "/proc/self/exe";
172 	ssize_t len;
173 
174 	len = readlink(path, tpath, size - 1);
175 	if (len <= 0)
176 		return -1;
177 
178 	tpath[len] = 0;
179 
180 	return 0;
181 }
182 
183 static int printf_json(void *out, const char *fmt, va_list ap)
184 {
185 	char *s;
186 	int err;
187 
188 	err = vasprintf(&s, fmt, ap);
189 	if (err < 0)
190 		return -1;
191 
192 	if (!oper_count) {
193 		int i;
194 
195 		/* Strip trailing spaces */
196 		i = strlen(s) - 1;
197 		while (s[i] == ' ')
198 			s[i--] = '\0';
199 
200 		jsonw_string_field(json_wtr, "operation", s);
201 		jsonw_name(json_wtr, "operands");
202 		jsonw_start_array(json_wtr);
203 		oper_count++;
204 	} else if (!strcmp(fmt, ",")) {
205 		   /* Skip */
206 	} else {
207 		jsonw_string(json_wtr, s);
208 		oper_count++;
209 	}
210 	free(s);
211 	return 0;
212 }
213 
214 static int fprintf_json(void *out, const char *fmt, ...)
215 {
216 	va_list ap;
217 	int r;
218 
219 	va_start(ap, fmt);
220 	r = printf_json(out, fmt, ap);
221 	va_end(ap);
222 
223 	return r;
224 }
225 
226 static int fprintf_json_styled(void *out,
227 			       enum disassembler_style style __maybe_unused,
228 			       const char *fmt, ...)
229 {
230 	va_list ap;
231 	int r;
232 
233 	va_start(ap, fmt);
234 	r = printf_json(out, fmt, ap);
235 	va_end(ap);
236 
237 	return r;
238 }
239 
240 static int init_context(disasm_ctx_t *ctx, const char *arch,
241 			const char *disassembler_options,
242 			unsigned char *image, ssize_t len, __u64 func_ksym)
243 {
244 	struct disassemble_info *info;
245 	char tpath[PATH_MAX];
246 	bfd *bfdf;
247 
248 	memset(tpath, 0, sizeof(tpath));
249 	if (get_exec_path(tpath, sizeof(tpath))) {
250 		p_err("failed to create disassembler (get_exec_path)");
251 		return -1;
252 	}
253 
254 	ctx->bfdf = bfd_openr(tpath, NULL);
255 	if (!ctx->bfdf) {
256 		p_err("failed to create disassembler (bfd_openr)");
257 		return -1;
258 	}
259 	if (!bfd_check_format(ctx->bfdf, bfd_object)) {
260 		p_err("failed to create disassembler (bfd_check_format)");
261 		goto err_close;
262 	}
263 	bfdf = ctx->bfdf;
264 
265 	ctx->info = malloc(sizeof(struct disasm_info));
266 	if (!ctx->info) {
267 		p_err("mem alloc failed");
268 		goto err_close;
269 	}
270 	ctx->info->func_ksym = func_ksym;
271 	info = &ctx->info->info;
272 
273 	if (json_output)
274 		init_disassemble_info_compat(info, stdout,
275 					     (fprintf_ftype) fprintf_json,
276 					     fprintf_json_styled);
277 	else
278 		init_disassemble_info_compat(info, stdout,
279 					     (fprintf_ftype) fprintf,
280 					     fprintf_styled);
281 
282 	/* Update architecture info for offload. */
283 	if (arch) {
284 		const bfd_arch_info_type *inf = bfd_scan_arch(arch);
285 
286 		if (inf) {
287 			bfdf->arch_info = inf;
288 		} else {
289 			p_err("No libbfd support for %s", arch);
290 			goto err_free;
291 		}
292 	}
293 
294 	info->arch = bfd_get_arch(bfdf);
295 	info->mach = bfd_get_mach(bfdf);
296 	if (disassembler_options)
297 		info->disassembler_options = disassembler_options;
298 	info->buffer = image;
299 	info->buffer_length = len;
300 	info->print_address_func = disasm_print_addr;
301 
302 	disassemble_init_for_target(info);
303 
304 #ifdef DISASM_FOUR_ARGS_SIGNATURE
305 	ctx->disassemble = disassembler(info->arch,
306 					bfd_big_endian(bfdf),
307 					info->mach,
308 					bfdf);
309 #else
310 	ctx->disassemble = disassembler(bfdf);
311 #endif
312 	if (!ctx->disassemble) {
313 		p_err("failed to create disassembler");
314 		goto err_free;
315 	}
316 	return 0;
317 
318 err_free:
319 	free(info);
320 err_close:
321 	bfd_close(ctx->bfdf);
322 	return -1;
323 }
324 
325 static void destroy_context(disasm_ctx_t *ctx)
326 {
327 	free(ctx->info);
328 	bfd_close(ctx->bfdf);
329 }
330 
331 static int
332 disassemble_insn(disasm_ctx_t *ctx, __maybe_unused unsigned char *image,
333 		 __maybe_unused ssize_t len, int pc,
334 		 __maybe_unused __u64 func_ksym)
335 {
336 	return ctx->disassemble(pc, &ctx->info->info);
337 }
338 
339 int disasm_init(void)
340 {
341 	bfd_init();
342 	return 0;
343 }
344 #endif /* HAVE_LIBBPFD_SUPPORT */
345 
346 int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
347 		      const char *arch, const char *disassembler_options,
348 		      const struct btf *btf,
349 		      const struct bpf_prog_linfo *prog_linfo,
350 		      __u64 func_ksym, unsigned int func_idx,
351 		      bool linum)
352 {
353 	const struct bpf_line_info *linfo = NULL;
354 	unsigned int nr_skip = 0;
355 	int count, i;
356 	unsigned int pc = 0;
357 	disasm_ctx_t ctx;
358 
359 	if (!len)
360 		return -1;
361 
362 	if (init_context(&ctx, arch, disassembler_options, image, len, func_ksym))
363 		return -1;
364 
365 	if (json_output)
366 		jsonw_start_array(json_wtr);
367 	do {
368 		if (prog_linfo) {
369 			linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo,
370 								func_ksym + pc,
371 								func_idx,
372 								nr_skip);
373 			if (linfo)
374 				nr_skip++;
375 		}
376 
377 		if (json_output) {
378 			jsonw_start_object(json_wtr);
379 			oper_count = 0;
380 			if (linfo)
381 				btf_dump_linfo_json(btf, linfo, linum);
382 			jsonw_name(json_wtr, "pc");
383 			jsonw_printf(json_wtr, "\"0x%x\"", pc);
384 		} else {
385 			if (linfo)
386 				btf_dump_linfo_plain(btf, linfo, "; ",
387 						     linum);
388 			printf("%4x:" DISASM_SPACER, pc);
389 		}
390 
391 		count = disassemble_insn(&ctx, image, len, pc, func_ksym);
392 
393 		if (json_output) {
394 			/* Operand array, was started in fprintf_json. Before
395 			 * that, make sure we have a _null_ value if no operand
396 			 * other than operation code was present.
397 			 */
398 			if (oper_count == 1)
399 				jsonw_null(json_wtr);
400 			jsonw_end_array(json_wtr);
401 		}
402 
403 		if (opcodes) {
404 			if (json_output) {
405 				jsonw_name(json_wtr, "opcodes");
406 				jsonw_start_array(json_wtr);
407 				for (i = 0; i < count; ++i)
408 					jsonw_printf(json_wtr, "\"0x%02hhx\"",
409 						     (uint8_t)image[pc + i]);
410 				jsonw_end_array(json_wtr);
411 			} else {
412 				printf("\n\t");
413 				for (i = 0; i < count; ++i)
414 					printf("%02x ",
415 					       (uint8_t)image[pc + i]);
416 			}
417 		}
418 		if (json_output)
419 			jsonw_end_object(json_wtr);
420 		else
421 			printf("\n");
422 
423 		pc += count;
424 	} while (count > 0 && pc < len);
425 	if (json_output)
426 		jsonw_end_array(json_wtr);
427 
428 	destroy_context(&ctx);
429 
430 	return 0;
431 }
432