xref: /linux/tools/testing/selftests/bpf/disasm_helpers.c (revision 06a130e42a5bfc84795464bff023bff4c16f58c5)
1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2 
3 #include <bpf/bpf.h>
4 #include "disasm.h"
5 
6 struct print_insn_context {
7 	char scratch[16];
8 	char *buf;
9 	size_t sz;
10 };
11 
12 static void print_insn_cb(void *private_data, const char *fmt, ...)
13 {
14 	struct print_insn_context *ctx = private_data;
15 	va_list args;
16 
17 	va_start(args, fmt);
18 	vsnprintf(ctx->buf, ctx->sz, fmt, args);
19 	va_end(args);
20 }
21 
22 static const char *print_call_cb(void *private_data, const struct bpf_insn *insn)
23 {
24 	struct print_insn_context *ctx = private_data;
25 
26 	/* For pseudo calls verifier.c:jit_subprogs() hides original
27 	 * imm to insn->off and changes insn->imm to be an index of
28 	 * the subprog instead.
29 	 */
30 	if (insn->src_reg == BPF_PSEUDO_CALL) {
31 		snprintf(ctx->scratch, sizeof(ctx->scratch), "%+d", insn->off);
32 		return ctx->scratch;
33 	}
34 
35 	return NULL;
36 }
37 
38 struct bpf_insn *disasm_insn(struct bpf_insn *insn, char *buf, size_t buf_sz)
39 {
40 	struct print_insn_context ctx = {
41 		.buf = buf,
42 		.sz = buf_sz,
43 	};
44 	struct bpf_insn_cbs cbs = {
45 		.cb_print	= print_insn_cb,
46 		.cb_call	= print_call_cb,
47 		.private_data	= &ctx,
48 	};
49 	char *tmp, *pfx_end, *sfx_start;
50 	bool double_insn;
51 	int len;
52 
53 	print_bpf_insn(&cbs, insn, true);
54 	/* We share code with kernel BPF disassembler, it adds '(FF) ' prefix
55 	 * for each instruction (FF stands for instruction `code` byte).
56 	 * Remove the prefix inplace, and also simplify call instructions.
57 	 * E.g.: "(85) call foo#10" -> "call foo".
58 	 * Also remove newline in the end (the 'max(strlen(buf) - 1, 0)' thing).
59 	 */
60 	pfx_end = buf + 5;
61 	sfx_start = buf + max((int)strlen(buf) - 1, 0);
62 	if (strncmp(pfx_end, "call ", 5) == 0 && (tmp = strrchr(buf, '#')))
63 		sfx_start = tmp;
64 	len = sfx_start - pfx_end;
65 	memmove(buf, pfx_end, len);
66 	buf[len] = 0;
67 	double_insn = insn->code == (BPF_LD | BPF_IMM | BPF_DW);
68 	return insn + (double_insn ? 2 : 1);
69 }
70