1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com
3 * Copyright (c) 2016 Facebook
10 #define __BPF_FUNC_STR_FN(x) [BPF_FUNC_ ## x] = __stringify(bpf_ ## x)
11 static const char * const func_id_str
[] = {
12 __BPF_FUNC_MAPPER(__BPF_FUNC_STR_FN
)
14 #undef __BPF_FUNC_STR_FN
16 static const char *__func_get_name(const struct bpf_insn_cbs
*cbs
,
17 const struct bpf_insn
*insn
,
18 char *buff
, size_t len
)
20 BUILD_BUG_ON(ARRAY_SIZE(func_id_str
) != __BPF_FUNC_MAX_ID
);
22 if (insn
->src_reg
!= BPF_PSEUDO_CALL
&&
23 insn
->imm
>= 0 && insn
->imm
< __BPF_FUNC_MAX_ID
&&
24 func_id_str
[insn
->imm
])
25 return func_id_str
[insn
->imm
];
27 if (cbs
&& cbs
->cb_call
)
28 return cbs
->cb_call(cbs
->private_data
, insn
);
30 if (insn
->src_reg
== BPF_PSEUDO_CALL
)
31 snprintf(buff
, len
, "%+d", insn
->imm
);
36 static const char *__func_imm_name(const struct bpf_insn_cbs
*cbs
,
37 const struct bpf_insn
*insn
,
38 u64 full_imm
, char *buff
, size_t len
)
40 if (cbs
&& cbs
->cb_imm
)
41 return cbs
->cb_imm(cbs
->private_data
, insn
, full_imm
);
43 snprintf(buff
, len
, "0x%llx", (unsigned long long)full_imm
);
47 const char *func_id_name(int id
)
49 if (id
>= 0 && id
< __BPF_FUNC_MAX_ID
&& func_id_str
[id
])
50 return func_id_str
[id
];
55 const char *const bpf_class_string
[8] = {
62 [BPF_JMP32
] = "jmp32",
63 [BPF_ALU64
] = "alu64",
66 const char *const bpf_alu_string
[16] = {
67 [BPF_ADD
>> 4] = "+=",
68 [BPF_SUB
>> 4] = "-=",
69 [BPF_MUL
>> 4] = "*=",
70 [BPF_DIV
>> 4] = "/=",
72 [BPF_AND
>> 4] = "&=",
73 [BPF_LSH
>> 4] = "<<=",
74 [BPF_RSH
>> 4] = ">>=",
75 [BPF_NEG
>> 4] = "neg",
76 [BPF_MOD
>> 4] = "%=",
77 [BPF_XOR
>> 4] = "^=",
79 [BPF_ARSH
>> 4] = "s>>=",
80 [BPF_END
>> 4] = "endian",
83 static const char *const bpf_ldst_string
[] = {
87 [BPF_DW
>> 3] = "u64",
90 static const char *const bpf_jmp_string
[16] = {
91 [BPF_JA
>> 4] = "jmp",
92 [BPF_JEQ
>> 4] = "==",
95 [BPF_JGE
>> 4] = ">=",
96 [BPF_JLE
>> 4] = "<=",
97 [BPF_JSET
>> 4] = "&",
98 [BPF_JNE
>> 4] = "!=",
99 [BPF_JSGT
>> 4] = "s>",
100 [BPF_JSLT
>> 4] = "s<",
101 [BPF_JSGE
>> 4] = "s>=",
102 [BPF_JSLE
>> 4] = "s<=",
103 [BPF_CALL
>> 4] = "call",
104 [BPF_EXIT
>> 4] = "exit",
107 static void print_bpf_end_insn(bpf_insn_print_t verbose
,
109 const struct bpf_insn
*insn
)
111 verbose(private_data
, "(%02x) r%d = %s%d r%d\n",
112 insn
->code
, insn
->dst_reg
,
113 BPF_SRC(insn
->code
) == BPF_TO_BE
? "be" : "le",
114 insn
->imm
, insn
->dst_reg
);
117 void print_bpf_insn(const struct bpf_insn_cbs
*cbs
,
118 const struct bpf_insn
*insn
,
119 bool allow_ptr_leaks
)
121 const bpf_insn_print_t verbose
= cbs
->cb_print
;
122 u8
class = BPF_CLASS(insn
->code
);
124 if (class == BPF_ALU
|| class == BPF_ALU64
) {
125 if (BPF_OP(insn
->code
) == BPF_END
) {
126 if (class == BPF_ALU64
)
127 verbose(cbs
->private_data
, "BUG_alu64_%02x\n", insn
->code
);
129 print_bpf_end_insn(verbose
, cbs
->private_data
, insn
);
130 } else if (BPF_OP(insn
->code
) == BPF_NEG
) {
131 verbose(cbs
->private_data
, "(%02x) %c%d = -%c%d\n",
132 insn
->code
, class == BPF_ALU
? 'w' : 'r',
133 insn
->dst_reg
, class == BPF_ALU
? 'w' : 'r',
135 } else if (BPF_SRC(insn
->code
) == BPF_X
) {
136 verbose(cbs
->private_data
, "(%02x) %c%d %s %c%d\n",
137 insn
->code
, class == BPF_ALU
? 'w' : 'r',
139 bpf_alu_string
[BPF_OP(insn
->code
) >> 4],
140 class == BPF_ALU
? 'w' : 'r',
143 verbose(cbs
->private_data
, "(%02x) %c%d %s %d\n",
144 insn
->code
, class == BPF_ALU
? 'w' : 'r',
146 bpf_alu_string
[BPF_OP(insn
->code
) >> 4],
149 } else if (class == BPF_STX
) {
150 if (BPF_MODE(insn
->code
) == BPF_MEM
)
151 verbose(cbs
->private_data
, "(%02x) *(%s *)(r%d %+d) = r%d\n",
153 bpf_ldst_string
[BPF_SIZE(insn
->code
) >> 3],
155 insn
->off
, insn
->src_reg
);
156 else if (BPF_MODE(insn
->code
) == BPF_XADD
)
157 verbose(cbs
->private_data
, "(%02x) lock *(%s *)(r%d %+d) += r%d\n",
159 bpf_ldst_string
[BPF_SIZE(insn
->code
) >> 3],
160 insn
->dst_reg
, insn
->off
,
163 verbose(cbs
->private_data
, "BUG_%02x\n", insn
->code
);
164 } else if (class == BPF_ST
) {
165 if (BPF_MODE(insn
->code
) != BPF_MEM
) {
166 verbose(cbs
->private_data
, "BUG_st_%02x\n", insn
->code
);
169 verbose(cbs
->private_data
, "(%02x) *(%s *)(r%d %+d) = %d\n",
171 bpf_ldst_string
[BPF_SIZE(insn
->code
) >> 3],
173 insn
->off
, insn
->imm
);
174 } else if (class == BPF_LDX
) {
175 if (BPF_MODE(insn
->code
) != BPF_MEM
) {
176 verbose(cbs
->private_data
, "BUG_ldx_%02x\n", insn
->code
);
179 verbose(cbs
->private_data
, "(%02x) r%d = *(%s *)(r%d %+d)\n",
180 insn
->code
, insn
->dst_reg
,
181 bpf_ldst_string
[BPF_SIZE(insn
->code
) >> 3],
182 insn
->src_reg
, insn
->off
);
183 } else if (class == BPF_LD
) {
184 if (BPF_MODE(insn
->code
) == BPF_ABS
) {
185 verbose(cbs
->private_data
, "(%02x) r0 = *(%s *)skb[%d]\n",
187 bpf_ldst_string
[BPF_SIZE(insn
->code
) >> 3],
189 } else if (BPF_MODE(insn
->code
) == BPF_IND
) {
190 verbose(cbs
->private_data
, "(%02x) r0 = *(%s *)skb[r%d + %d]\n",
192 bpf_ldst_string
[BPF_SIZE(insn
->code
) >> 3],
193 insn
->src_reg
, insn
->imm
);
194 } else if (BPF_MODE(insn
->code
) == BPF_IMM
&&
195 BPF_SIZE(insn
->code
) == BPF_DW
) {
196 /* At this point, we already made sure that the second
197 * part of the ldimm64 insn is accessible.
199 u64 imm
= ((u64
)(insn
+ 1)->imm
<< 32) | (u32
)insn
->imm
;
200 bool is_ptr
= insn
->src_reg
== BPF_PSEUDO_MAP_FD
||
201 insn
->src_reg
== BPF_PSEUDO_MAP_VALUE
;
204 if (is_ptr
&& !allow_ptr_leaks
)
207 verbose(cbs
->private_data
, "(%02x) r%d = %s\n",
208 insn
->code
, insn
->dst_reg
,
209 __func_imm_name(cbs
, insn
, imm
,
212 verbose(cbs
->private_data
, "BUG_ld_%02x\n", insn
->code
);
215 } else if (class == BPF_JMP32
|| class == BPF_JMP
) {
216 u8 opcode
= BPF_OP(insn
->code
);
218 if (opcode
== BPF_CALL
) {
221 if (insn
->src_reg
== BPF_PSEUDO_CALL
) {
222 verbose(cbs
->private_data
, "(%02x) call pc%s\n",
224 __func_get_name(cbs
, insn
,
227 strcpy(tmp
, "unknown");
228 verbose(cbs
->private_data
, "(%02x) call %s#%d\n", insn
->code
,
229 __func_get_name(cbs
, insn
,
233 } else if (insn
->code
== (BPF_JMP
| BPF_JA
)) {
234 verbose(cbs
->private_data
, "(%02x) goto pc%+d\n",
235 insn
->code
, insn
->off
);
236 } else if (insn
->code
== (BPF_JMP
| BPF_EXIT
)) {
237 verbose(cbs
->private_data
, "(%02x) exit\n", insn
->code
);
238 } else if (BPF_SRC(insn
->code
) == BPF_X
) {
239 verbose(cbs
->private_data
,
240 "(%02x) if %c%d %s %c%d goto pc%+d\n",
241 insn
->code
, class == BPF_JMP32
? 'w' : 'r',
243 bpf_jmp_string
[BPF_OP(insn
->code
) >> 4],
244 class == BPF_JMP32
? 'w' : 'r',
245 insn
->src_reg
, insn
->off
);
247 verbose(cbs
->private_data
,
248 "(%02x) if %c%d %s 0x%x goto pc%+d\n",
249 insn
->code
, class == BPF_JMP32
? 'w' : 'r',
251 bpf_jmp_string
[BPF_OP(insn
->code
) >> 4],
252 insn
->imm
, insn
->off
);
255 verbose(cbs
->private_data
, "(%02x) %s\n",
256 insn
->code
, bpf_class_string
[class]);