]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
selftests/bpf: extract utility function for BPF disassembly
authorEduard Zingerman <eddyz87@gmail.com>
Mon, 22 Jul 2024 23:38:38 +0000 (16:38 -0700)
committerAndrii Nakryiko <andrii@kernel.org>
Mon, 29 Jul 2024 22:05:05 +0000 (15:05 -0700)
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>
tools/testing/selftests/bpf/Makefile
tools/testing/selftests/bpf/disasm_helpers.c [new file with mode: 0644]
tools/testing/selftests/bpf/disasm_helpers.h [new file with mode: 0644]
tools/testing/selftests/bpf/prog_tests/ctx_rewrite.c
tools/testing/selftests/bpf/testing_helpers.c

index 3318d5425d75059792b4a521a4bf1551fc95f2ba..888ba68e659211e9912d43966bd6254ce69f9b67 100644 (file)
@@ -661,6 +661,7 @@ TRUNNER_EXTRA_SOURCES := test_progs.c               \
                         test_loader.c          \
                         xsk.c                  \
                         disasm.c               \
+                        disasm_helpers.c       \
                         json_writer.c          \
                         flow_dissector_load.h  \
                         ip_check_defrag_frags.h
diff --git a/tools/testing/selftests/bpf/disasm_helpers.c b/tools/testing/selftests/bpf/disasm_helpers.c
new file mode 100644 (file)
index 0000000..96b1f2f
--- /dev/null
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+#include <bpf/bpf.h>
+#include "disasm.h"
+
+struct print_insn_context {
+       char *buf;
+       size_t sz;
+};
+
+static void print_insn_cb(void *private_data, const char *fmt, ...)
+{
+       struct print_insn_context *ctx = private_data;
+       va_list args;
+
+       va_start(args, fmt);
+       vsnprintf(ctx->buf, ctx->sz, fmt, args);
+       va_end(args);
+}
+
+struct bpf_insn *disasm_insn(struct bpf_insn *insn, char *buf, size_t buf_sz)
+{
+       struct print_insn_context ctx = {
+               .buf = buf,
+               .sz = buf_sz,
+       };
+       struct bpf_insn_cbs cbs = {
+               .cb_print       = print_insn_cb,
+               .private_data   = &ctx,
+       };
+       char *tmp, *pfx_end, *sfx_start;
+       bool double_insn;
+       int len;
+
+       print_bpf_insn(&cbs, insn, true);
+       /* We share code with kernel BPF disassembler, it adds '(FF) ' prefix
+        * for each instruction (FF stands for instruction `code` byte).
+        * Remove the prefix inplace, and also simplify call instructions.
+        * E.g.: "(85) call foo#10" -> "call foo".
+        * Also remove newline in the end (the 'max(strlen(buf) - 1, 0)' thing).
+        */
+       pfx_end = buf + 5;
+       sfx_start = buf + max((int)strlen(buf) - 1, 0);
+       if (strncmp(pfx_end, "call ", 5) == 0 && (tmp = strrchr(buf, '#')))
+               sfx_start = tmp;
+       len = sfx_start - pfx_end;
+       memmove(buf, pfx_end, len);
+       buf[len] = 0;
+       double_insn = insn->code == (BPF_LD | BPF_IMM | BPF_DW);
+       return insn + (double_insn ? 2 : 1);
+}
diff --git a/tools/testing/selftests/bpf/disasm_helpers.h b/tools/testing/selftests/bpf/disasm_helpers.h
new file mode 100644 (file)
index 0000000..7b26cab
--- /dev/null
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+
+#ifndef __DISASM_HELPERS_H
+#define __DISASM_HELPERS_H
+
+#include <stdlib.h>
+
+struct bpf_insn;
+
+struct bpf_insn *disasm_insn(struct bpf_insn *insn, char *buf, size_t buf_sz);
+
+#endif /* __DISASM_HELPERS_H */
index 08b6391f2f56ce195e9dcc9d795fb715cfb7e187..dd75ccb03770c5fe3853051207d8ffb94c19fdad 100644 (file)
@@ -10,7 +10,8 @@
 #include "bpf/btf.h"
 #include "bpf_util.h"
 #include "linux/filter.h"
-#include "disasm.h"
+#include "linux/kernel.h"
+#include "disasm_helpers.h"
 
 #define MAX_PROG_TEXT_SZ (32 * 1024)
 
@@ -628,63 +629,6 @@ err:
        return false;
 }
 
-static void print_insn(void *private_data, const char *fmt, ...)
-{
-       va_list args;
-
-       va_start(args, fmt);
-       vfprintf((FILE *)private_data, fmt, args);
-       va_end(args);
-}
-
-/* Disassemble instructions to a stream */
-static void print_xlated(FILE *out, struct bpf_insn *insn, __u32 len)
-{
-       const struct bpf_insn_cbs cbs = {
-               .cb_print       = print_insn,
-               .cb_call        = NULL,
-               .cb_imm         = NULL,
-               .private_data   = out,
-       };
-       bool double_insn = false;
-       int i;
-
-       for (i = 0; i < len; i++) {
-               if (double_insn) {
-                       double_insn = false;
-                       continue;
-               }
-
-               double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
-               print_bpf_insn(&cbs, insn + i, true);
-       }
-}
-
-/* We share code with kernel BPF disassembler, it adds '(FF) ' prefix
- * for each instruction (FF stands for instruction `code` byte).
- * This function removes the prefix inplace for each line in `str`.
- */
-static void remove_insn_prefix(char *str, int size)
-{
-       const int prefix_size = 5;
-
-       int write_pos = 0, read_pos = prefix_size;
-       int len = strlen(str);
-       char c;
-
-       size = min(size, len);
-
-       while (read_pos < size) {
-               c = str[read_pos++];
-               if (c == 0)
-                       break;
-               str[write_pos++] = c;
-               if (c == '\n')
-                       read_pos += prefix_size;
-       }
-       str[write_pos] = 0;
-}
-
 struct prog_info {
        char *prog_kind;
        enum bpf_prog_type prog_type;
@@ -699,9 +643,10 @@ static void match_program(struct btf *btf,
                          char *reg_map[][2],
                          bool skip_first_insn)
 {
-       struct bpf_insn *buf = NULL;
+       struct bpf_insn *buf = NULL, *insn, *insn_end;
        int err = 0, prog_fd = 0;
        FILE *prog_out = NULL;
+       char insn_buf[64];
        char *text = NULL;
        __u32 cnt = 0;
 
@@ -739,12 +684,13 @@ static void match_program(struct btf *btf,
                PRINT_FAIL("Can't open memory stream\n");
                goto out;
        }
-       if (skip_first_insn)
-               print_xlated(prog_out, buf + 1, cnt - 1);
-       else
-               print_xlated(prog_out, buf, cnt);
+       insn_end = buf + cnt;
+       insn = buf + (skip_first_insn ? 1 : 0);
+       while (insn < insn_end) {
+               insn = disasm_insn(insn, insn_buf, sizeof(insn_buf));
+               fprintf(prog_out, "%s\n", insn_buf);
+       }
        fclose(prog_out);
-       remove_insn_prefix(text, MAX_PROG_TEXT_SZ);
 
        ASSERT_TRUE(match_pattern(btf, pattern, text, reg_map),
                    pinfo->prog_kind);
index d5379a0e6da8043878b6e40f33011071fe3445ea..ac7c66f4fc7ba230f7215631c994ea77420c8aca 100644 (file)
@@ -7,6 +7,7 @@
 #include <errno.h>
 #include <bpf/bpf.h>
 #include <bpf/libbpf.h>
+#include "disasm.h"
 #include "test_progs.h"
 #include "testing_helpers.h"
 #include <linux/membarrier.h>