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