xref: /DragonOS/kernel/crates/rbpf/src/disassembler.rs (revision 55e6f0b65f91b32638fd56581f711a816eccdcd1)
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]
19 fn alu_imm_str(name: &str, insn: &ebpf::Insn) -> String {
20     format!("{name} r{}, {:#x}", insn.dst, insn.imm)
21 }
22 
23 #[inline]
24 fn alu_reg_str(name: &str, insn: &ebpf::Insn) -> String {
25     format!("{name} r{}, r{}", insn.dst, insn.src)
26 }
27 
28 #[inline]
29 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]
38 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]
52 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]
66 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]
80 fn ldabs_str(name: &str, insn: &ebpf::Insn) -> String {
81     format!("{name} {:#x}", insn.imm)
82 }
83 
84 #[inline]
85 fn ldind_str(name: &str, insn: &ebpf::Insn) -> String {
86     format!("{name} r{}, {:#x}", insn.src, insn.imm)
87 }
88 
89 #[inline]
90 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]
104 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 /// ```
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 /// ```
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