1 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
2 // Copyright 2017 6WIND S.A. <quentin.monnet@6wind.com>
3
4 //! Functions in this module are used to handle eBPF programs with a higher level representation,
5 //! for example to disassemble the code into a human-readable format.
6
7 use alloc::{
8 format,
9 string::{String, ToString},
10 vec,
11 vec::Vec,
12 };
13
14 use log::warn;
15
16 use crate::ebpf;
17
18 #[inline]
alu_imm_str(name: &str, insn: &ebpf::Insn) -> String19 fn alu_imm_str(name: &str, insn: &ebpf::Insn) -> String {
20 format!("{name} r{}, {:#x}", insn.dst, insn.imm)
21 }
22
23 #[inline]
alu_reg_str(name: &str, insn: &ebpf::Insn) -> String24 fn alu_reg_str(name: &str, insn: &ebpf::Insn) -> String {
25 format!("{name} r{}, r{}", insn.dst, insn.src)
26 }
27
28 #[inline]
byteswap_str(name: &str, insn: &ebpf::Insn) -> String29 fn byteswap_str(name: &str, insn: &ebpf::Insn) -> String {
30 match insn.imm {
31 16 | 32 | 64 => {}
32 _ => warn!("[Disassembler] Warning: Invalid offset value for {name} insn"),
33 }
34 format!("{name}{} r{}", insn.imm, insn.dst)
35 }
36
37 #[inline]
ld_st_imm_str(name: &str, insn: &ebpf::Insn) -> String38 fn ld_st_imm_str(name: &str, insn: &ebpf::Insn) -> String {
39 if insn.off >= 0 {
40 format!("{name} [r{}+{:#x}], {:#x}", insn.dst, insn.off, insn.imm)
41 } else {
42 format!(
43 "{name} [r{}-{:#x}], {:#x}",
44 insn.dst,
45 -(insn.off as isize),
46 insn.imm
47 )
48 }
49 }
50
51 #[inline]
ld_reg_str(name: &str, insn: &ebpf::Insn) -> String52 fn ld_reg_str(name: &str, insn: &ebpf::Insn) -> String {
53 if insn.off >= 0 {
54 format!("{name} r{}, [r{}+{:#x}]", insn.dst, insn.src, insn.off)
55 } else {
56 format!(
57 "{name} r{}, [r{}-{:#x}]",
58 insn.dst,
59 insn.src,
60 -(insn.off as isize)
61 )
62 }
63 }
64
65 #[inline]
st_reg_str(name: &str, insn: &ebpf::Insn) -> String66 fn st_reg_str(name: &str, insn: &ebpf::Insn) -> String {
67 if insn.off >= 0 {
68 format!("{name} [r{}+{:#x}], r{}", insn.dst, insn.off, insn.src)
69 } else {
70 format!(
71 "{name} [r{}-{:#x}], r{}",
72 insn.dst,
73 -(insn.off as isize),
74 insn.src
75 )
76 }
77 }
78
79 #[inline]
ldabs_str(name: &str, insn: &ebpf::Insn) -> String80 fn ldabs_str(name: &str, insn: &ebpf::Insn) -> String {
81 format!("{name} {:#x}", insn.imm)
82 }
83
84 #[inline]
ldind_str(name: &str, insn: &ebpf::Insn) -> String85 fn ldind_str(name: &str, insn: &ebpf::Insn) -> String {
86 format!("{name} r{}, {:#x}", insn.src, insn.imm)
87 }
88
89 #[inline]
jmp_imm_str(name: &str, insn: &ebpf::Insn) -> String90 fn jmp_imm_str(name: &str, insn: &ebpf::Insn) -> String {
91 if insn.off >= 0 {
92 format!("{name} r{}, {:#x}, +{:#x}", insn.dst, insn.imm, insn.off)
93 } else {
94 format!(
95 "{name} r{}, {:#x}, -{:#x}",
96 insn.dst,
97 insn.imm,
98 -(insn.off as isize)
99 )
100 }
101 }
102
103 #[inline]
jmp_reg_str(name: &str, insn: &ebpf::Insn) -> String104 fn jmp_reg_str(name: &str, insn: &ebpf::Insn) -> String {
105 if insn.off >= 0 {
106 format!("{name} r{}, r{}, +{:#x}", insn.dst, insn.src, insn.off)
107 } else {
108 format!(
109 "{name} r{}, r{}, -{:#x}",
110 insn.dst,
111 insn.src,
112 -(insn.off as isize)
113 )
114 }
115 }
116
117 /// High-level representation of an eBPF instruction.
118 ///
119 /// In addition to standard operation code and various operand, this struct has the following
120 /// properties:
121 ///
122 /// * It stores a name, corresponding to a mnemonic for the operation code.
123 /// * It also stores a description, which is a mnemonic for the full instruction, using the actual
124 /// values of the relevant operands, and that can be used for disassembling the eBPF program for
125 /// example.
126 /// * Immediate values are stored in an `i64` instead of a traditional i32, in order to merge the
127 /// two parts of (otherwise double-length) `LD_DW_IMM` instructions.
128 ///
129 /// See <https://www.kernel.org/doc/Documentation/networking/filter.txt> for the Linux kernel
130 /// documentation about eBPF, or <https://github.com/iovisor/bpf-docs/blob/master/eBPF.md> for a
131 /// more concise version.
132 #[derive(Debug, PartialEq, Eq)]
133 pub struct HLInsn {
134 /// Operation code.
135 pub opc: u8,
136 /// Name (mnemonic). This name is not canon.
137 pub name: String,
138 /// Description of the instruction. This is not canon.
139 pub desc: String,
140 /// Destination register operand.
141 pub dst: u8,
142 /// Source register operand.
143 pub src: u8,
144 /// Offset operand.
145 pub off: i16,
146 /// Immediate value operand. For `LD_DW_IMM` instructions, contains the whole value merged from
147 /// the two 8-bytes parts of the instruction.
148 pub imm: i64,
149 }
150
151 /// Return a vector of `struct HLInsn` built from an eBPF program.
152 ///
153 /// This is made public to provide a way to manipulate a program as a vector of instructions, in a
154 /// high-level format, for example for dumping the program instruction after instruction with a
155 /// custom format.
156 ///
157 /// Note that the two parts of `LD_DW_IMM` instructions (that have the size of two standard
158 /// instructions) are considered as making a single immediate value. As a consequence, the number
159 /// of instructions stored in the vector may not be equal to the size in bytes of the program
160 /// divided by the length of an instructions.
161 ///
162 /// To do so, the immediate value operand is stored as an `i64` instead as an i32, so be careful
163 /// when you use it (see example `examples/to_json.rs`).
164 ///
165 /// This is to oppose to `ebpf::to_insn_vec()` function, that treats instructions on a low-level
166 /// ground and do not merge the parts of `LD_DW_IMM`. Also, the version in `ebpf` module does not
167 /// use names or descriptions when storing the instructions.
168 ///
169 /// # Examples
170 ///
171 /// ```
172 /// use rbpf::disassembler;
173 ///
174 /// let prog = &[
175 /// 0x18, 0x00, 0x00, 0x00, 0x88, 0x77, 0x66, 0x55,
176 /// 0x00, 0x00, 0x00, 0x00, 0x44, 0x33, 0x22, 0x11,
177 /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
178 /// ];
179 ///
180 /// let v = disassembler::to_insn_vec(prog);
181 /// assert_eq!(v, vec![
182 /// disassembler::HLInsn {
183 /// opc: 0x18,
184 /// name: "lddw".to_string(),
185 /// desc: "lddw r0, 0x1122334455667788".to_string(),
186 /// dst: 0,
187 /// src: 0,
188 /// off: 0,
189 /// imm: 0x1122334455667788
190 /// },
191 /// disassembler::HLInsn {
192 /// opc: 0x95,
193 /// name: "exit".to_string(),
194 /// desc: "exit".to_string(),
195 /// dst: 0,
196 /// src: 0,
197 /// off: 0,
198 /// imm: 0
199 /// },
200 /// ]);
201 /// ```
to_insn_vec(prog: &[u8]) -> Vec<HLInsn>202 pub fn to_insn_vec(prog: &[u8]) -> Vec<HLInsn> {
203 if prog.len() % ebpf::INSN_SIZE != 0 {
204 panic!(
205 "[Disassembler] Error: eBPF program length must be a multiple of {:?} octets",
206 ebpf::INSN_SIZE
207 );
208 }
209 if prog.is_empty() {
210 return vec![];
211 }
212
213 let mut res = vec![];
214 let mut insn_ptr: usize = 0;
215
216 while insn_ptr * ebpf::INSN_SIZE < prog.len() {
217 let insn = ebpf::get_insn(prog, insn_ptr);
218
219 let name;
220 let desc;
221 let mut imm = insn.imm as i64;
222 match insn.opc {
223 // BPF_LD class
224 ebpf::LD_ABS_B => {
225 name = "ldabsb";
226 desc = ldabs_str(name, &insn);
227 }
228 ebpf::LD_ABS_H => {
229 name = "ldabsh";
230 desc = ldabs_str(name, &insn);
231 }
232 ebpf::LD_ABS_W => {
233 name = "ldabsw";
234 desc = ldabs_str(name, &insn);
235 }
236 ebpf::LD_ABS_DW => {
237 name = "ldabsdw";
238 desc = ldabs_str(name, &insn);
239 }
240 ebpf::LD_IND_B => {
241 name = "ldindb";
242 desc = ldind_str(name, &insn);
243 }
244 ebpf::LD_IND_H => {
245 name = "ldindh";
246 desc = ldind_str(name, &insn);
247 }
248 ebpf::LD_IND_W => {
249 name = "ldindw";
250 desc = ldind_str(name, &insn);
251 }
252 ebpf::LD_IND_DW => {
253 name = "ldinddw";
254 desc = ldind_str(name, &insn);
255 }
256
257 ebpf::LD_DW_IMM => {
258 insn_ptr += 1;
259 let next_insn = ebpf::get_insn(prog, insn_ptr);
260 imm = ((insn.imm as u32) as u64 + ((next_insn.imm as u64) << 32)) as i64;
261 name = "lddw";
262 desc = format!("{name} r{:}, {imm:#x}", insn.dst);
263 }
264
265 // BPF_LDX class
266 ebpf::LD_B_REG => {
267 name = "ldxb";
268 desc = ld_reg_str(name, &insn);
269 }
270 ebpf::LD_H_REG => {
271 name = "ldxh";
272 desc = ld_reg_str(name, &insn);
273 }
274 ebpf::LD_W_REG => {
275 name = "ldxw";
276 desc = ld_reg_str(name, &insn);
277 }
278 ebpf::LD_DW_REG => {
279 name = "ldxdw";
280 desc = ld_reg_str(name, &insn);
281 }
282
283 // BPF_ST class
284 ebpf::ST_B_IMM => {
285 name = "stb";
286 desc = ld_st_imm_str(name, &insn);
287 }
288 ebpf::ST_H_IMM => {
289 name = "sth";
290 desc = ld_st_imm_str(name, &insn);
291 }
292 ebpf::ST_W_IMM => {
293 name = "stw";
294 desc = ld_st_imm_str(name, &insn);
295 }
296 ebpf::ST_DW_IMM => {
297 name = "stdw";
298 desc = ld_st_imm_str(name, &insn);
299 }
300
301 // BPF_STX class
302 ebpf::ST_B_REG => {
303 name = "stxb";
304 desc = st_reg_str(name, &insn);
305 }
306 ebpf::ST_H_REG => {
307 name = "stxh";
308 desc = st_reg_str(name, &insn);
309 }
310 ebpf::ST_W_REG => {
311 name = "stxw";
312 desc = st_reg_str(name, &insn);
313 }
314 ebpf::ST_DW_REG => {
315 name = "stxdw";
316 desc = st_reg_str(name, &insn);
317 }
318 ebpf::ST_W_XADD => {
319 name = "stxxaddw";
320 desc = st_reg_str(name, &insn);
321 }
322 ebpf::ST_DW_XADD => {
323 name = "stxxadddw";
324 desc = st_reg_str(name, &insn);
325 }
326
327 // BPF_ALU class
328 ebpf::ADD32_IMM => {
329 name = "add32";
330 desc = alu_imm_str(name, &insn);
331 }
332 ebpf::ADD32_REG => {
333 name = "add32";
334 desc = alu_reg_str(name, &insn);
335 }
336 ebpf::SUB32_IMM => {
337 name = "sub32";
338 desc = alu_imm_str(name, &insn);
339 }
340 ebpf::SUB32_REG => {
341 name = "sub32";
342 desc = alu_reg_str(name, &insn);
343 }
344 ebpf::MUL32_IMM => {
345 name = "mul32";
346 desc = alu_imm_str(name, &insn);
347 }
348 ebpf::MUL32_REG => {
349 name = "mul32";
350 desc = alu_reg_str(name, &insn);
351 }
352 ebpf::DIV32_IMM => {
353 name = "div32";
354 desc = alu_imm_str(name, &insn);
355 }
356 ebpf::DIV32_REG => {
357 name = "div32";
358 desc = alu_reg_str(name, &insn);
359 }
360 ebpf::OR32_IMM => {
361 name = "or32";
362 desc = alu_imm_str(name, &insn);
363 }
364 ebpf::OR32_REG => {
365 name = "or32";
366 desc = alu_reg_str(name, &insn);
367 }
368 ebpf::AND32_IMM => {
369 name = "and32";
370 desc = alu_imm_str(name, &insn);
371 }
372 ebpf::AND32_REG => {
373 name = "and32";
374 desc = alu_reg_str(name, &insn);
375 }
376 ebpf::LSH32_IMM => {
377 name = "lsh32";
378 desc = alu_imm_str(name, &insn);
379 }
380 ebpf::LSH32_REG => {
381 name = "lsh32";
382 desc = alu_reg_str(name, &insn);
383 }
384 ebpf::RSH32_IMM => {
385 name = "rsh32";
386 desc = alu_imm_str(name, &insn);
387 }
388 ebpf::RSH32_REG => {
389 name = "rsh32";
390 desc = alu_reg_str(name, &insn);
391 }
392 ebpf::NEG32 => {
393 name = "neg32";
394 desc = format!("{name} r{:}", insn.dst);
395 }
396 ebpf::MOD32_IMM => {
397 name = "mod32";
398 desc = alu_imm_str(name, &insn);
399 }
400 ebpf::MOD32_REG => {
401 name = "mod32";
402 desc = alu_reg_str(name, &insn);
403 }
404 ebpf::XOR32_IMM => {
405 name = "xor32";
406 desc = alu_imm_str(name, &insn);
407 }
408 ebpf::XOR32_REG => {
409 name = "xor32";
410 desc = alu_reg_str(name, &insn);
411 }
412 ebpf::MOV32_IMM => {
413 name = "mov32";
414 desc = alu_imm_str(name, &insn);
415 }
416 ebpf::MOV32_REG => {
417 name = "mov32";
418 desc = alu_reg_str(name, &insn);
419 }
420 ebpf::ARSH32_IMM => {
421 name = "arsh32";
422 desc = alu_imm_str(name, &insn);
423 }
424 ebpf::ARSH32_REG => {
425 name = "arsh32";
426 desc = alu_reg_str(name, &insn);
427 }
428 ebpf::LE => {
429 name = "le";
430 desc = byteswap_str(name, &insn);
431 }
432 ebpf::BE => {
433 name = "be";
434 desc = byteswap_str(name, &insn);
435 }
436
437 // BPF_ALU64 class
438 ebpf::ADD64_IMM => {
439 name = "add64";
440 desc = alu_imm_str(name, &insn);
441 }
442 ebpf::ADD64_REG => {
443 name = "add64";
444 desc = alu_reg_str(name, &insn);
445 }
446 ebpf::SUB64_IMM => {
447 name = "sub64";
448 desc = alu_imm_str(name, &insn);
449 }
450 ebpf::SUB64_REG => {
451 name = "sub64";
452 desc = alu_reg_str(name, &insn);
453 }
454 ebpf::MUL64_IMM => {
455 name = "mul64";
456 desc = alu_imm_str(name, &insn);
457 }
458 ebpf::MUL64_REG => {
459 name = "mul64";
460 desc = alu_reg_str(name, &insn);
461 }
462 ebpf::DIV64_IMM => {
463 name = "div64";
464 desc = alu_imm_str(name, &insn);
465 }
466 ebpf::DIV64_REG => {
467 name = "div64";
468 desc = alu_reg_str(name, &insn);
469 }
470 ebpf::OR64_IMM => {
471 name = "or64";
472 desc = alu_imm_str(name, &insn);
473 }
474 ebpf::OR64_REG => {
475 name = "or64";
476 desc = alu_reg_str(name, &insn);
477 }
478 ebpf::AND64_IMM => {
479 name = "and64";
480 desc = alu_imm_str(name, &insn);
481 }
482 ebpf::AND64_REG => {
483 name = "and64";
484 desc = alu_reg_str(name, &insn);
485 }
486 ebpf::LSH64_IMM => {
487 name = "lsh64";
488 desc = alu_imm_str(name, &insn);
489 }
490 ebpf::LSH64_REG => {
491 name = "lsh64";
492 desc = alu_reg_str(name, &insn);
493 }
494 ebpf::RSH64_IMM => {
495 name = "rsh64";
496 desc = alu_imm_str(name, &insn);
497 }
498 ebpf::RSH64_REG => {
499 name = "rsh64";
500 desc = alu_reg_str(name, &insn);
501 }
502 ebpf::NEG64 => {
503 name = "neg64";
504 desc = format!("{name} r{:}", insn.dst);
505 }
506 ebpf::MOD64_IMM => {
507 name = "mod64";
508 desc = alu_imm_str(name, &insn);
509 }
510 ebpf::MOD64_REG => {
511 name = "mod64";
512 desc = alu_reg_str(name, &insn);
513 }
514 ebpf::XOR64_IMM => {
515 name = "xor64";
516 desc = alu_imm_str(name, &insn);
517 }
518 ebpf::XOR64_REG => {
519 name = "xor64";
520 desc = alu_reg_str(name, &insn);
521 }
522 ebpf::MOV64_IMM => {
523 name = "mov64";
524 desc = alu_imm_str(name, &insn);
525 }
526 ebpf::MOV64_REG => {
527 name = "mov64";
528 desc = alu_reg_str(name, &insn);
529 }
530 ebpf::ARSH64_IMM => {
531 name = "arsh64";
532 desc = alu_imm_str(name, &insn);
533 }
534 ebpf::ARSH64_REG => {
535 name = "arsh64";
536 desc = alu_reg_str(name, &insn);
537 }
538
539 // BPF_JMP class
540 ebpf::JA => {
541 name = "ja";
542 desc = if insn.off >= 0 {
543 format!("{name} +{:#x}", insn.off)
544 } else {
545 format!("{name} -{:#x}", -insn.off)
546 }
547 }
548 ebpf::JEQ_IMM => {
549 name = "jeq";
550 desc = jmp_imm_str(name, &insn);
551 }
552 ebpf::JEQ_REG => {
553 name = "jeq";
554 desc = jmp_reg_str(name, &insn);
555 }
556 ebpf::JGT_IMM => {
557 name = "jgt";
558 desc = jmp_imm_str(name, &insn);
559 }
560 ebpf::JGT_REG => {
561 name = "jgt";
562 desc = jmp_reg_str(name, &insn);
563 }
564 ebpf::JGE_IMM => {
565 name = "jge";
566 desc = jmp_imm_str(name, &insn);
567 }
568 ebpf::JGE_REG => {
569 name = "jge";
570 desc = jmp_reg_str(name, &insn);
571 }
572 ebpf::JLT_IMM => {
573 name = "jlt";
574 desc = jmp_imm_str(name, &insn);
575 }
576 ebpf::JLT_REG => {
577 name = "jlt";
578 desc = jmp_reg_str(name, &insn);
579 }
580 ebpf::JLE_IMM => {
581 name = "jle";
582 desc = jmp_imm_str(name, &insn);
583 }
584 ebpf::JLE_REG => {
585 name = "jle";
586 desc = jmp_reg_str(name, &insn);
587 }
588 ebpf::JSET_IMM => {
589 name = "jset";
590 desc = jmp_imm_str(name, &insn);
591 }
592 ebpf::JSET_REG => {
593 name = "jset";
594 desc = jmp_reg_str(name, &insn);
595 }
596 ebpf::JNE_IMM => {
597 name = "jne";
598 desc = jmp_imm_str(name, &insn);
599 }
600 ebpf::JNE_REG => {
601 name = "jne";
602 desc = jmp_reg_str(name, &insn);
603 }
604 ebpf::JSGT_IMM => {
605 name = "jsgt";
606 desc = jmp_imm_str(name, &insn);
607 }
608 ebpf::JSGT_REG => {
609 name = "jsgt";
610 desc = jmp_reg_str(name, &insn);
611 }
612 ebpf::JSGE_IMM => {
613 name = "jsge";
614 desc = jmp_imm_str(name, &insn);
615 }
616 ebpf::JSGE_REG => {
617 name = "jsge";
618 desc = jmp_reg_str(name, &insn);
619 }
620 ebpf::JSLT_IMM => {
621 name = "jslt";
622 desc = jmp_imm_str(name, &insn);
623 }
624 ebpf::JSLT_REG => {
625 name = "jslt";
626 desc = jmp_reg_str(name, &insn);
627 }
628 ebpf::JSLE_IMM => {
629 name = "jsle";
630 desc = jmp_imm_str(name, &insn);
631 }
632 ebpf::JSLE_REG => {
633 name = "jsle";
634 desc = jmp_reg_str(name, &insn);
635 }
636 ebpf::CALL => {
637 name = "call";
638 desc = format!("{name} {:#x}", insn.imm);
639 }
640 ebpf::TAIL_CALL => {
641 name = "tail_call";
642 desc = name.to_string();
643 }
644 ebpf::EXIT => {
645 name = "exit";
646 desc = name.to_string();
647 }
648
649 // BPF_JMP32 class
650 ebpf::JEQ_IMM32 => {
651 name = "jeq32";
652 desc = jmp_imm_str(name, &insn);
653 }
654 ebpf::JEQ_REG32 => {
655 name = "jeq32";
656 desc = jmp_reg_str(name, &insn);
657 }
658 ebpf::JGT_IMM32 => {
659 name = "jgt32";
660 desc = jmp_imm_str(name, &insn);
661 }
662 ebpf::JGT_REG32 => {
663 name = "jgt32";
664 desc = jmp_reg_str(name, &insn);
665 }
666 ebpf::JGE_IMM32 => {
667 name = "jge32";
668 desc = jmp_imm_str(name, &insn);
669 }
670 ebpf::JGE_REG32 => {
671 name = "jge32";
672 desc = jmp_reg_str(name, &insn);
673 }
674 ebpf::JLT_IMM32 => {
675 name = "jlt32";
676 desc = jmp_imm_str(name, &insn);
677 }
678 ebpf::JLT_REG32 => {
679 name = "jlt32";
680 desc = jmp_reg_str(name, &insn);
681 }
682 ebpf::JLE_IMM32 => {
683 name = "jle32";
684 desc = jmp_imm_str(name, &insn);
685 }
686 ebpf::JLE_REG32 => {
687 name = "jle32";
688 desc = jmp_reg_str(name, &insn);
689 }
690 ebpf::JSET_IMM32 => {
691 name = "jset32";
692 desc = jmp_imm_str(name, &insn);
693 }
694 ebpf::JSET_REG32 => {
695 name = "jset32";
696 desc = jmp_reg_str(name, &insn);
697 }
698 ebpf::JNE_IMM32 => {
699 name = "jne32";
700 desc = jmp_imm_str(name, &insn);
701 }
702 ebpf::JNE_REG32 => {
703 name = "jne32";
704 desc = jmp_reg_str(name, &insn);
705 }
706 ebpf::JSGT_IMM32 => {
707 name = "jsgt32";
708 desc = jmp_imm_str(name, &insn);
709 }
710 ebpf::JSGT_REG32 => {
711 name = "jsgt32";
712 desc = jmp_reg_str(name, &insn);
713 }
714 ebpf::JSGE_IMM32 => {
715 name = "jsge32";
716 desc = jmp_imm_str(name, &insn);
717 }
718 ebpf::JSGE_REG32 => {
719 name = "jsge32";
720 desc = jmp_reg_str(name, &insn);
721 }
722 ebpf::JSLT_IMM32 => {
723 name = "jslt32";
724 desc = jmp_imm_str(name, &insn);
725 }
726 ebpf::JSLT_REG32 => {
727 name = "jslt32";
728 desc = jmp_reg_str(name, &insn);
729 }
730 ebpf::JSLE_IMM32 => {
731 name = "jsle32";
732 desc = jmp_imm_str(name, &insn);
733 }
734 ebpf::JSLE_REG32 => {
735 name = "jsle32";
736 desc = jmp_reg_str(name, &insn);
737 }
738
739 _ => {
740 panic!(
741 "[Disassembler] Error: unknown eBPF opcode {:#2x} (insn #{:?})",
742 insn.opc, insn_ptr
743 );
744 }
745 };
746
747 let hl_insn = HLInsn {
748 opc: insn.opc,
749 name: name.to_string(),
750 desc,
751 dst: insn.dst,
752 src: insn.src,
753 off: insn.off,
754 imm,
755 };
756
757 res.push(hl_insn);
758
759 insn_ptr += 1;
760 }
761 res
762 }
763
764 /// Disassemble an eBPF program into human-readable instructions and prints it to standard output.
765 ///
766 /// The program is not checked for errors or inconsistencies.
767 ///
768 /// # Examples
769 ///
770 /// ```
771 /// use rbpf::disassembler;
772 /// let prog = &[
773 /// 0x07, 0x01, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00,
774 /// 0xb7, 0x02, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00,
775 /// 0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
776 /// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
777 /// 0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
778 /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
779 /// ];
780 /// disassembler::disassemble(prog);
781 /// # // "\nadd64 r1, 0x605\nmov64 r2, 0x32\nmov64 r1, r0\nbe16 r0\nneg64 r2\nexit"
782 /// ```
783 ///
784 /// This will produce the following output:
785 ///
786 /// ```test
787 /// add64 r1, 0x605
788 /// mov64 r2, 0x32
789 /// mov64 r1, r0
790 /// be16 r0
791 /// neg64 r2
792 /// exit
793 /// ```
disassemble(prog: &[u8])794 pub fn disassemble(prog: &[u8]) {
795 #[cfg(feature = "std")]
796 {
797 for insn in to_insn_vec(prog) {
798 println!("{}", insn.desc);
799 }
800 }
801 #[cfg(not(feature = "std"))]
802 {
803 for insn in to_insn_vec(prog) {
804 log::info!("{}", insn.desc);
805 }
806 }
807 }
808