Line data Source code
1 : /* Disassembler for BPF.
2 : Copyright (C) 2016 Red Hat, Inc.
3 : This file is part of elfutils.
4 :
5 : This file is free software; you can redistribute it and/or modify
6 : it under the terms of either
7 :
8 : * the GNU Lesser General Public License as published by the Free
9 : Software Foundation; either version 3 of the License, or (at
10 : your option) any later version
11 :
12 : or
13 :
14 : * the GNU General Public License as published by the Free
15 : Software Foundation; either version 2 of the License, or (at
16 : your option) any later version
17 :
18 : or both in parallel, as here.
19 :
20 : elfutils is distributed in the hope that it will be useful, but
21 : WITHOUT ANY WARRANTY; without even the implied warranty of
22 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 : General Public License for more details.
24 :
25 : You should have received copies of the GNU General Public License and
26 : the GNU Lesser General Public License along with this program. If
27 : not, see <http://www.gnu.org/licenses/>. */
28 :
29 : #ifdef HAVE_CONFIG_H
30 : # include <config.h>
31 : #endif
32 :
33 : #include <assert.h>
34 : #include <string.h>
35 : #include <stdio.h>
36 : #include <gelf.h>
37 : #include <inttypes.h>
38 : #include "bpf.h"
39 :
40 : #include "../libelf/common.h"
41 : #include "../libebl/libeblP.h"
42 :
43 : static const char class_string[8][8] = {
44 : [BPF_LD] = "ld",
45 : [BPF_LDX] = "ldx",
46 : [BPF_ST] = "st",
47 : [BPF_STX] = "stx",
48 : [BPF_ALU] = "alu",
49 : [BPF_JMP] = "jmp",
50 : [BPF_RET] = "6", /* completely unused in ebpf */
51 : [BPF_ALU64] = "alu64",
52 : };
53 :
54 :
55 : #define REG(N) "r%" #N "$d"
56 : #define REGU(N) "(u32)" REG(N)
57 : #define REGS(N) "(s64)" REG(N)
58 :
59 : #define IMMS(N) "%" #N "$d"
60 : #define IMMX(N) "%" #N "$#x"
61 :
62 : #define OFF(N) "%" #N "$+d"
63 : #define JMP(N) "%" #N "$#x"
64 :
65 : #define A32(O, S) REG(1) " = " REGU(1) " " #O " " S
66 : #define A64(O, S) REG(1) " " #O "= " S
67 : #define J64(D, O, S) "if " D " " #O " " S " goto " JMP(3)
68 : #define LOAD(T) REG(1) " = *(" #T " *)(" REG(2) OFF(3) ")"
69 : #define STORE(T, S) "*(" #T " *)(" REG(1) OFF(3) ") = " S
70 : #define XADD(T, S) "lock *(" #T " *)(" REG(1) OFF(3) ") += " S
71 : #define LDSKB(T, S) "r0 = *(" #T " *)skb[" S "]"
72 :
73 : static void
74 : bswap_bpf_insn (struct bpf_insn *p)
75 : {
76 : /* Note that the dst_reg and src_reg fields are 4-bit bitfields.
77 : That means these two nibbles are (typically) layed out in the
78 : opposite order between big- and little-endian hosts. This is
79 : not required by any standard, but does happen to be true for
80 : at least ppc, s390, arm and mips as big-endian hosts. */
81 0 : int t = p->dst_reg;
82 0 : p->dst_reg = p->src_reg;
83 0 : p->src_reg = t;
84 :
85 : /* The other 2 and 4 byte fields are trivially converted. */
86 0 : CONVERT (p->off);
87 0 : CONVERT (p->imm);
88 : }
89 :
90 : int
91 1 : bpf_disasm (Ebl *ebl, const uint8_t **startp, const uint8_t *end,
92 : GElf_Addr addr, const char *fmt __attribute__((unused)),
93 : DisasmOutputCB_t outcb,
94 : DisasmGetSymCB_t symcb __attribute__((unused)),
95 : void *outcbarg,
96 : void *symcbarg __attribute__((unused)))
97 : {
98 1 : const bool need_bswap = MY_ELFDATA != ebl->data;
99 1 : const uint8_t *start = *startp;
100 : char buf[128];
101 1 : int len, retval = 0;
102 :
103 258 : while (start + sizeof(struct bpf_insn) <= end)
104 : {
105 : struct bpf_insn i;
106 : unsigned code, class, jmp;
107 : const char *code_fmt;
108 :
109 256 : memcpy(&i, start, sizeof(struct bpf_insn));
110 256 : if (need_bswap)
111 : bswap_bpf_insn (&i);
112 :
113 256 : start += sizeof(struct bpf_insn);
114 256 : addr += sizeof(struct bpf_insn);
115 256 : jmp = addr + i.off * sizeof(struct bpf_insn);
116 :
117 256 : code = i.code;
118 256 : switch (code)
119 : {
120 1 : case BPF_LD | BPF_IMM | BPF_DW:
121 1 : {
122 : struct bpf_insn i2;
123 : uint64_t imm64;
124 :
125 1 : if (start + sizeof(struct bpf_insn) > end)
126 : {
127 0 : start -= sizeof(struct bpf_insn);
128 0 : *startp = start;
129 : goto done;
130 : }
131 1 : memcpy(&i2, start, sizeof(struct bpf_insn));
132 1 : if (need_bswap)
133 : bswap_bpf_insn (&i2);
134 1 : start += sizeof(struct bpf_insn);
135 1 : addr += sizeof(struct bpf_insn);
136 :
137 1 : imm64 = (uint32_t)i.imm | ((uint64_t)i2.imm << 32);
138 1 : switch (i.src_reg)
139 : {
140 : case 0:
141 : code_fmt = REG(1) " = %2$#" PRIx64;
142 : break;
143 0 : case BPF_PSEUDO_MAP_FD:
144 0 : code_fmt = REG(1) " = map_fd(%2$#" PRIx64 ")";
145 0 : break;
146 0 : default:
147 0 : code_fmt = REG(1) " = ld_pseudo(%3$d, %2$#" PRIx64 ")";
148 0 : break;
149 : }
150 2 : len = snprintf(buf, sizeof(buf), code_fmt,
151 1 : i.dst_reg, imm64, i.src_reg);
152 : }
153 1 : break;
154 :
155 1 : case BPF_JMP | BPF_EXIT:
156 1 : len = snprintf(buf, sizeof(buf), "exit");
157 1 : break;
158 1 : case BPF_JMP | BPF_JA:
159 1 : len = snprintf(buf, sizeof(buf), "goto " JMP(1), jmp);
160 1 : break;
161 : case BPF_JMP | BPF_CALL:
162 : code_fmt = "call " IMMS(1);
163 : goto do_imm;
164 :
165 : case BPF_ALU | BPF_END | BPF_TO_LE:
166 : /* The imm field contains {16,32,64}. */
167 : code_fmt = REG(1) " = le" IMMS(2) "(" REG(1) ")";
168 : goto do_dst_imm;
169 1 : case BPF_ALU | BPF_END | BPF_TO_BE:
170 1 : code_fmt = REG(1) " = be" IMMS(2) "(" REG(1) ")";
171 1 : goto do_dst_imm;
172 :
173 1 : case BPF_ALU | BPF_ADD | BPF_K:
174 1 : code_fmt = A32(+, IMMS(2));
175 1 : goto do_dst_imm;
176 1 : case BPF_ALU | BPF_SUB | BPF_K:
177 1 : code_fmt = A32(-, IMMS(2));
178 1 : goto do_dst_imm;
179 1 : case BPF_ALU | BPF_MUL | BPF_K:
180 1 : code_fmt = A32(*, IMMS(2));
181 1 : goto do_dst_imm;
182 1 : case BPF_ALU | BPF_DIV | BPF_K:
183 1 : code_fmt = A32(/, IMMS(2));
184 1 : goto do_dst_imm;
185 1 : case BPF_ALU | BPF_OR | BPF_K:
186 1 : code_fmt = A32(|, IMMX(2));
187 1 : goto do_dst_imm;
188 1 : case BPF_ALU | BPF_AND | BPF_K:
189 1 : code_fmt = A32(&, IMMX(2));
190 1 : goto do_dst_imm;
191 1 : case BPF_ALU | BPF_LSH | BPF_K:
192 1 : code_fmt = A32(<<, IMMS(2));
193 1 : goto do_dst_imm;
194 1 : case BPF_ALU | BPF_RSH | BPF_K:
195 1 : code_fmt = A32(>>, IMMS(2));
196 1 : goto do_dst_imm;
197 1 : case BPF_ALU | BPF_MOD | BPF_K:
198 1 : code_fmt = A32(%%, IMMS(2));
199 1 : goto do_dst_imm;
200 1 : case BPF_ALU | BPF_XOR | BPF_K:
201 1 : code_fmt = A32(^, IMMX(2));
202 1 : goto do_dst_imm;
203 1 : case BPF_ALU | BPF_MOV | BPF_K:
204 1 : code_fmt = REG(1) " = " IMMX(2);
205 1 : goto do_dst_imm;
206 1 : case BPF_ALU | BPF_ARSH | BPF_K:
207 1 : code_fmt = REG(1) " = (u32)((s32)" REG(1) " >> " IMMS(2) ")";
208 1 : goto do_dst_imm;
209 :
210 : case BPF_ALU | BPF_ADD | BPF_X:
211 : code_fmt = A32(+, REGU(2));
212 : goto do_dst_src;
213 1 : case BPF_ALU | BPF_SUB | BPF_X:
214 1 : code_fmt = A32(-, REGU(2));
215 1 : goto do_dst_src;
216 1 : case BPF_ALU | BPF_MUL | BPF_X:
217 1 : code_fmt = A32(*, REGU(2));
218 1 : goto do_dst_src;
219 1 : case BPF_ALU | BPF_DIV | BPF_X:
220 1 : code_fmt = A32(/, REGU(2));
221 1 : goto do_dst_src;
222 1 : case BPF_ALU | BPF_OR | BPF_X:
223 1 : code_fmt = A32(|, REGU(2));
224 1 : goto do_dst_src;
225 1 : case BPF_ALU | BPF_AND | BPF_X:
226 1 : code_fmt = A32(&, REGU(2));
227 1 : goto do_dst_src;
228 1 : case BPF_ALU | BPF_LSH | BPF_X:
229 1 : code_fmt = A32(<<, REGU(2));
230 1 : goto do_dst_src;
231 1 : case BPF_ALU | BPF_RSH | BPF_X:
232 1 : code_fmt = A32(>>, REGU(2));
233 1 : goto do_dst_src;
234 1 : case BPF_ALU | BPF_MOD | BPF_X:
235 1 : code_fmt = A32(%%, REGU(2));
236 1 : goto do_dst_src;
237 1 : case BPF_ALU | BPF_XOR | BPF_X:
238 1 : code_fmt = A32(^, REGU(2));
239 1 : goto do_dst_src;
240 1 : case BPF_ALU | BPF_MOV | BPF_X:
241 1 : code_fmt = REG(1) " = " REGU(2);
242 1 : goto do_dst_src;
243 1 : case BPF_ALU | BPF_ARSH | BPF_X:
244 1 : code_fmt = REG(1) " = (u32)((s32)" REG(1) " >> " REG(2) ")";
245 1 : goto do_dst_src;
246 :
247 1 : case BPF_ALU64 | BPF_ADD | BPF_K:
248 1 : code_fmt = A64(+, IMMS(2));
249 1 : goto do_dst_imm;
250 1 : case BPF_ALU64 | BPF_SUB | BPF_K:
251 1 : code_fmt = A64(-, IMMS(2));
252 1 : goto do_dst_imm;
253 1 : case BPF_ALU64 | BPF_MUL | BPF_K:
254 1 : code_fmt = A64(*, IMMS(2));
255 1 : goto do_dst_imm;
256 1 : case BPF_ALU64 | BPF_DIV | BPF_K:
257 1 : code_fmt = A64(/, IMMS(2));
258 1 : goto do_dst_imm;
259 1 : case BPF_ALU64 | BPF_OR | BPF_K:
260 1 : code_fmt = A64(|, IMMS(2));
261 1 : goto do_dst_imm;
262 1 : case BPF_ALU64 | BPF_AND | BPF_K:
263 1 : code_fmt = A64(&, IMMS(2));
264 1 : goto do_dst_imm;
265 1 : case BPF_ALU64 | BPF_LSH | BPF_K:
266 1 : code_fmt = A64(<<, IMMS(2));
267 1 : goto do_dst_imm;
268 1 : case BPF_ALU64 | BPF_RSH | BPF_K:
269 1 : code_fmt = A64(>>, IMMS(2));
270 1 : goto do_dst_imm;
271 1 : case BPF_ALU64 | BPF_MOD | BPF_K:
272 1 : code_fmt = A64(%%, IMMS(2));
273 1 : goto do_dst_imm;
274 1 : case BPF_ALU64 | BPF_XOR | BPF_K:
275 1 : code_fmt = A64(^, IMMS(2));
276 1 : goto do_dst_imm;
277 1 : case BPF_ALU64 | BPF_MOV | BPF_K:
278 1 : code_fmt = REG(1) " = " IMMS(2);
279 1 : goto do_dst_imm;
280 1 : case BPF_ALU64 | BPF_ARSH | BPF_K:
281 1 : code_fmt = REG(1) " = (s64)" REG(1) " >> " IMMS(2);
282 1 : goto do_dst_imm;
283 :
284 1 : case BPF_ALU64 | BPF_ADD | BPF_X:
285 1 : code_fmt = A64(+, REG(2));
286 1 : goto do_dst_src;
287 1 : case BPF_ALU64 | BPF_SUB | BPF_X:
288 1 : code_fmt = A64(-, REG(2));
289 1 : goto do_dst_src;
290 1 : case BPF_ALU64 | BPF_MUL | BPF_X:
291 1 : code_fmt = A64(*, REG(2));
292 1 : goto do_dst_src;
293 1 : case BPF_ALU64 | BPF_DIV | BPF_X:
294 1 : code_fmt = A64(/, REG(2));
295 1 : goto do_dst_src;
296 1 : case BPF_ALU64 | BPF_OR | BPF_X:
297 1 : code_fmt = A64(|, REG(2));
298 1 : goto do_dst_src;
299 1 : case BPF_ALU64 | BPF_AND | BPF_X:
300 1 : code_fmt = A64(&, REG(2));
301 1 : goto do_dst_src;
302 1 : case BPF_ALU64 | BPF_LSH | BPF_X:
303 1 : code_fmt = A64(<<, REG(2));
304 1 : goto do_dst_src;
305 1 : case BPF_ALU64 | BPF_RSH | BPF_X:
306 1 : code_fmt = A64(>>, REG(2));
307 1 : goto do_dst_src;
308 1 : case BPF_ALU64 | BPF_MOD | BPF_X:
309 1 : code_fmt = A64(%%, REG(2));
310 1 : goto do_dst_src;
311 1 : case BPF_ALU64 | BPF_XOR | BPF_X:
312 1 : code_fmt = A64(^, REG(2));
313 1 : goto do_dst_src;
314 1 : case BPF_ALU64 | BPF_MOV | BPF_X:
315 1 : code_fmt = REG(1) " = " REG(2);
316 1 : goto do_dst_src;
317 1 : case BPF_ALU64 | BPF_ARSH | BPF_X:
318 1 : code_fmt = REG(1) " = (s64)" REG(1) " >> " REG(2);
319 1 : goto do_dst_src;
320 :
321 1 : case BPF_ALU | BPF_NEG:
322 1 : code_fmt = REG(1) " = (u32)-" REG(1);
323 1 : goto do_dst_src;
324 1 : case BPF_ALU64 | BPF_NEG:
325 1 : code_fmt = REG(1) " = -" REG(1);
326 1 : goto do_dst_src;
327 :
328 : case BPF_JMP | BPF_JEQ | BPF_K:
329 : code_fmt = J64(REG(1), ==, IMMS(2));
330 : goto do_dst_imm_jmp;
331 1 : case BPF_JMP | BPF_JGT | BPF_K:
332 1 : code_fmt = J64(REG(1), >, IMMS(2));
333 1 : goto do_dst_imm_jmp;
334 1 : case BPF_JMP | BPF_JGE | BPF_K:
335 1 : code_fmt = J64(REG(1), >=, IMMS(2));
336 1 : goto do_dst_imm_jmp;
337 1 : case BPF_JMP | BPF_JSET | BPF_K:
338 1 : code_fmt = J64(REG(1), &, IMMS(2));
339 1 : goto do_dst_imm_jmp;
340 1 : case BPF_JMP | BPF_JNE | BPF_K:
341 1 : code_fmt = J64(REG(1), !=, IMMS(2));
342 1 : goto do_dst_imm_jmp;
343 1 : case BPF_JMP | BPF_JSGT | BPF_K:
344 1 : code_fmt = J64(REGS(1), >, IMMS(2));
345 1 : goto do_dst_imm_jmp;
346 1 : case BPF_JMP | BPF_JSGE | BPF_K:
347 1 : code_fmt = J64(REGS(1), >=, IMMS(2));
348 1 : goto do_dst_imm_jmp;
349 :
350 : case BPF_JMP | BPF_JEQ | BPF_X:
351 : code_fmt = J64(REG(1), ==, REG(2));
352 : goto do_dst_src_jmp;
353 1 : case BPF_JMP | BPF_JGT | BPF_X:
354 1 : code_fmt = J64(REG(1), >, REG(2));
355 1 : goto do_dst_src_jmp;
356 1 : case BPF_JMP | BPF_JGE | BPF_X:
357 1 : code_fmt = J64(REG(1), >=, REG(2));
358 1 : goto do_dst_src_jmp;
359 1 : case BPF_JMP | BPF_JSET | BPF_X:
360 1 : code_fmt = J64(REG(1), &, REG(2));
361 1 : goto do_dst_src_jmp;
362 1 : case BPF_JMP | BPF_JNE | BPF_X:
363 1 : code_fmt = J64(REG(1), !=, REG(2));
364 1 : goto do_dst_src_jmp;
365 1 : case BPF_JMP | BPF_JSGT | BPF_X:
366 1 : code_fmt = J64(REGS(1), >, REGS(2));
367 1 : goto do_dst_src_jmp;
368 1 : case BPF_JMP | BPF_JSGE | BPF_X:
369 1 : code_fmt = J64(REGS(1), >=, REGS(2));
370 1 : goto do_dst_src_jmp;
371 :
372 : case BPF_LDX | BPF_MEM | BPF_B:
373 : code_fmt = LOAD(u8);
374 : goto do_dst_src_off;
375 1 : case BPF_LDX | BPF_MEM | BPF_H:
376 1 : code_fmt = LOAD(u16);
377 1 : goto do_dst_src_off;
378 1 : case BPF_LDX | BPF_MEM | BPF_W:
379 1 : code_fmt = LOAD(u32);
380 1 : goto do_dst_src_off;
381 1 : case BPF_LDX | BPF_MEM | BPF_DW:
382 1 : code_fmt = LOAD(u64);
383 1 : goto do_dst_src_off;
384 :
385 1 : case BPF_STX | BPF_MEM | BPF_B:
386 1 : code_fmt = STORE(u8, REG(2));
387 1 : goto do_dst_src_off;
388 1 : case BPF_STX | BPF_MEM | BPF_H:
389 1 : code_fmt = STORE(u16, REG(2));
390 1 : goto do_dst_src_off;
391 1 : case BPF_STX | BPF_MEM | BPF_W:
392 1 : code_fmt = STORE(u32, REG(2));
393 1 : goto do_dst_src_off;
394 1 : case BPF_STX | BPF_MEM | BPF_DW:
395 1 : code_fmt = STORE(u64, REG(2));
396 1 : goto do_dst_src_off;
397 :
398 1 : case BPF_STX | BPF_XADD | BPF_W:
399 1 : code_fmt = XADD(u32, REG(2));
400 1 : goto do_dst_src_off;
401 1 : case BPF_STX | BPF_XADD | BPF_DW:
402 1 : code_fmt = XADD(u64, REG(2));
403 1 : goto do_dst_src_off;
404 :
405 : case BPF_ST | BPF_MEM | BPF_B:
406 : code_fmt = STORE(u8, IMMS(2));
407 : goto do_dst_imm_off;
408 1 : case BPF_ST | BPF_MEM | BPF_H:
409 1 : code_fmt = STORE(u16, IMMS(2));
410 1 : goto do_dst_imm_off;
411 1 : case BPF_ST | BPF_MEM | BPF_W:
412 1 : code_fmt = STORE(u32, IMMS(2));
413 1 : goto do_dst_imm_off;
414 1 : case BPF_ST | BPF_MEM | BPF_DW:
415 1 : code_fmt = STORE(u64, IMMS(2));
416 1 : goto do_dst_imm_off;
417 :
418 1 : case BPF_LD | BPF_ABS | BPF_B:
419 1 : code_fmt = LDSKB(u8, IMMS(1));
420 1 : goto do_imm;
421 1 : case BPF_LD | BPF_ABS | BPF_H:
422 1 : code_fmt = LDSKB(u16, IMMS(1));
423 1 : goto do_imm;
424 1 : case BPF_LD | BPF_ABS | BPF_W:
425 1 : code_fmt = LDSKB(u32, IMMS(1));
426 1 : goto do_imm;
427 :
428 : case BPF_LD | BPF_IND | BPF_B:
429 : code_fmt = LDSKB(u8, REG(1) "+" IMMS(2));
430 : goto do_src_imm;
431 1 : case BPF_LD | BPF_IND | BPF_H:
432 1 : code_fmt = LDSKB(u16, REG(1) "+" IMMS(2));
433 1 : goto do_src_imm;
434 1 : case BPF_LD | BPF_IND | BPF_W:
435 1 : code_fmt = LDSKB(u32, REG(1) "+" IMMS(2));
436 1 : goto do_src_imm;
437 :
438 4 : do_imm:
439 8 : len = snprintf(buf, sizeof(buf), code_fmt, i.imm);
440 4 : break;
441 26 : do_dst_imm:
442 52 : len = snprintf(buf, sizeof(buf), code_fmt, i.dst_reg, i.imm);
443 26 : break;
444 3 : do_src_imm:
445 6 : len = snprintf(buf, sizeof(buf), code_fmt, i.src_reg, i.imm);
446 3 : break;
447 26 : do_dst_src:
448 52 : len = snprintf(buf, sizeof(buf), code_fmt, i.dst_reg, i.src_reg);
449 26 : break;
450 7 : do_dst_imm_jmp:
451 14 : len = snprintf(buf, sizeof(buf), code_fmt, i.dst_reg, i.imm, jmp);
452 7 : break;
453 7 : do_dst_src_jmp:
454 21 : len = snprintf(buf, sizeof(buf), code_fmt,
455 14 : i.dst_reg, i.src_reg, jmp);
456 7 : break;
457 4 : do_dst_imm_off:
458 8 : len = snprintf(buf, sizeof(buf), code_fmt, i.dst_reg, i.imm, i.off);
459 4 : break;
460 10 : do_dst_src_off:
461 40 : len = snprintf(buf, sizeof(buf), code_fmt,
462 20 : i.dst_reg, i.src_reg, i.off);
463 10 : break;
464 :
465 166 : default:
466 166 : class = BPF_CLASS(code);
467 166 : len = snprintf(buf, sizeof(buf), "invalid class %s",
468 166 : class_string[class]);
469 166 : break;
470 : }
471 :
472 256 : *startp = start;
473 256 : retval = outcb (buf, len, outcbarg);
474 256 : if (retval != 0)
475 : goto done;
476 : }
477 :
478 1 : done:
479 1 : return retval;
480 : }
|