Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: extract utility function for BPF disassembly

struct bpf_insn *disasm_insn(struct bpf_insn *insn, char *buf, size_t buf_sz);

Disassembles instruction 'insn' to a text buffer 'buf'.
Removes insn->code hex prefix added by kernel disassembly routine.
Returns a pointer to the next instruction
(increments insn by either 1 or 2).

Acked-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20240722233844.1406874-5-eddyz87@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>

authored by

Eduard Zingerman and committed by
Andrii Nakryiko
424ebaa3 91b7fbf3

+75 -64
+1
tools/testing/selftests/bpf/Makefile
··· 661 661 test_loader.c \ 662 662 xsk.c \ 663 663 disasm.c \ 664 + disasm_helpers.c \ 664 665 json_writer.c \ 665 666 flow_dissector_load.h \ 666 667 ip_check_defrag_frags.h
+51
tools/testing/selftests/bpf/disasm_helpers.c
··· 1 + // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 + 3 + #include <bpf/bpf.h> 4 + #include "disasm.h" 5 + 6 + struct print_insn_context { 7 + char *buf; 8 + size_t sz; 9 + }; 10 + 11 + static void print_insn_cb(void *private_data, const char *fmt, ...) 12 + { 13 + struct print_insn_context *ctx = private_data; 14 + va_list args; 15 + 16 + va_start(args, fmt); 17 + vsnprintf(ctx->buf, ctx->sz, fmt, args); 18 + va_end(args); 19 + } 20 + 21 + struct bpf_insn *disasm_insn(struct bpf_insn *insn, char *buf, size_t buf_sz) 22 + { 23 + struct print_insn_context ctx = { 24 + .buf = buf, 25 + .sz = buf_sz, 26 + }; 27 + struct bpf_insn_cbs cbs = { 28 + .cb_print = print_insn_cb, 29 + .private_data = &ctx, 30 + }; 31 + char *tmp, *pfx_end, *sfx_start; 32 + bool double_insn; 33 + int len; 34 + 35 + print_bpf_insn(&cbs, insn, true); 36 + /* We share code with kernel BPF disassembler, it adds '(FF) ' prefix 37 + * for each instruction (FF stands for instruction `code` byte). 38 + * Remove the prefix inplace, and also simplify call instructions. 39 + * E.g.: "(85) call foo#10" -> "call foo". 40 + * Also remove newline in the end (the 'max(strlen(buf) - 1, 0)' thing). 41 + */ 42 + pfx_end = buf + 5; 43 + sfx_start = buf + max((int)strlen(buf) - 1, 0); 44 + if (strncmp(pfx_end, "call ", 5) == 0 && (tmp = strrchr(buf, '#'))) 45 + sfx_start = tmp; 46 + len = sfx_start - pfx_end; 47 + memmove(buf, pfx_end, len); 48 + buf[len] = 0; 49 + double_insn = insn->code == (BPF_LD | BPF_IMM | BPF_DW); 50 + return insn + (double_insn ? 2 : 1); 51 + }
+12
tools/testing/selftests/bpf/disasm_helpers.h
··· 1 + /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 + 3 + #ifndef __DISASM_HELPERS_H 4 + #define __DISASM_HELPERS_H 5 + 6 + #include <stdlib.h> 7 + 8 + struct bpf_insn; 9 + 10 + struct bpf_insn *disasm_insn(struct bpf_insn *insn, char *buf, size_t buf_sz); 11 + 12 + #endif /* __DISASM_HELPERS_H */
+10 -64
tools/testing/selftests/bpf/prog_tests/ctx_rewrite.c
··· 10 10 #include "bpf/btf.h" 11 11 #include "bpf_util.h" 12 12 #include "linux/filter.h" 13 - #include "disasm.h" 13 + #include "linux/kernel.h" 14 + #include "disasm_helpers.h" 14 15 15 16 #define MAX_PROG_TEXT_SZ (32 * 1024) 16 17 ··· 629 628 return false; 630 629 } 631 630 632 - static void print_insn(void *private_data, const char *fmt, ...) 633 - { 634 - va_list args; 635 - 636 - va_start(args, fmt); 637 - vfprintf((FILE *)private_data, fmt, args); 638 - va_end(args); 639 - } 640 - 641 - /* Disassemble instructions to a stream */ 642 - static void print_xlated(FILE *out, struct bpf_insn *insn, __u32 len) 643 - { 644 - const struct bpf_insn_cbs cbs = { 645 - .cb_print = print_insn, 646 - .cb_call = NULL, 647 - .cb_imm = NULL, 648 - .private_data = out, 649 - }; 650 - bool double_insn = false; 651 - int i; 652 - 653 - for (i = 0; i < len; i++) { 654 - if (double_insn) { 655 - double_insn = false; 656 - continue; 657 - } 658 - 659 - double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); 660 - print_bpf_insn(&cbs, insn + i, true); 661 - } 662 - } 663 - 664 - /* We share code with kernel BPF disassembler, it adds '(FF) ' prefix 665 - * for each instruction (FF stands for instruction `code` byte). 666 - * This function removes the prefix inplace for each line in `str`. 667 - */ 668 - static void remove_insn_prefix(char *str, int size) 669 - { 670 - const int prefix_size = 5; 671 - 672 - int write_pos = 0, read_pos = prefix_size; 673 - int len = strlen(str); 674 - char c; 675 - 676 - size = min(size, len); 677 - 678 - while (read_pos < size) { 679 - c = str[read_pos++]; 680 - if (c == 0) 681 - break; 682 - str[write_pos++] = c; 683 - if (c == '\n') 684 - read_pos += prefix_size; 685 - } 686 - str[write_pos] = 0; 687 - } 688 - 689 631 struct prog_info { 690 632 char *prog_kind; 691 633 enum bpf_prog_type prog_type; ··· 643 699 char *reg_map[][2], 644 700 bool skip_first_insn) 645 701 { 646 - struct bpf_insn *buf = NULL; 702 + struct bpf_insn *buf = NULL, *insn, *insn_end; 647 703 int err = 0, prog_fd = 0; 648 704 FILE *prog_out = NULL; 705 + char insn_buf[64]; 649 706 char *text = NULL; 650 707 __u32 cnt = 0; 651 708 ··· 684 739 PRINT_FAIL("Can't open memory stream\n"); 685 740 goto out; 686 741 } 687 - if (skip_first_insn) 688 - print_xlated(prog_out, buf + 1, cnt - 1); 689 - else 690 - print_xlated(prog_out, buf, cnt); 742 + insn_end = buf + cnt; 743 + insn = buf + (skip_first_insn ? 1 : 0); 744 + while (insn < insn_end) { 745 + insn = disasm_insn(insn, insn_buf, sizeof(insn_buf)); 746 + fprintf(prog_out, "%s\n", insn_buf); 747 + } 691 748 fclose(prog_out); 692 - remove_insn_prefix(text, MAX_PROG_TEXT_SZ); 693 749 694 750 ASSERT_TRUE(match_pattern(btf, pattern, text, reg_map), 695 751 pinfo->prog_kind);
+1
tools/testing/selftests/bpf/testing_helpers.c
··· 7 7 #include <errno.h> 8 8 #include <bpf/bpf.h> 9 9 #include <bpf/libbpf.h> 10 + #include "disasm.h" 10 11 #include "test_progs.h" 11 12 #include "testing_helpers.h" 12 13 #include <linux/membarrier.h>