Commit 424ebaa3 authored by Eduard Zingerman's avatar Eduard Zingerman Committed by Andrii Nakryiko

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: default avatarAndrii Nakryiko <andrii@kernel.org>
Signed-off-by: default avatarEduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20240722233844.1406874-5-eddyz87@gmail.comSigned-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
parent 91b7fbf3
...@@ -661,6 +661,7 @@ TRUNNER_EXTRA_SOURCES := test_progs.c \ ...@@ -661,6 +661,7 @@ TRUNNER_EXTRA_SOURCES := test_progs.c \
test_loader.c \ test_loader.c \
xsk.c \ xsk.c \
disasm.c \ disasm.c \
disasm_helpers.c \
json_writer.c \ json_writer.c \
flow_dissector_load.h \ flow_dissector_load.h \
ip_check_defrag_frags.h ip_check_defrag_frags.h
......
// 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);
}
/* 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 */
...@@ -10,7 +10,8 @@ ...@@ -10,7 +10,8 @@
#include "bpf/btf.h" #include "bpf/btf.h"
#include "bpf_util.h" #include "bpf_util.h"
#include "linux/filter.h" #include "linux/filter.h"
#include "disasm.h" #include "linux/kernel.h"
#include "disasm_helpers.h"
#define MAX_PROG_TEXT_SZ (32 * 1024) #define MAX_PROG_TEXT_SZ (32 * 1024)
...@@ -628,63 +629,6 @@ static bool match_pattern(struct btf *btf, char *pattern, char *text, char *reg_ ...@@ -628,63 +629,6 @@ static bool match_pattern(struct btf *btf, char *pattern, char *text, char *reg_
return false; 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 { struct prog_info {
char *prog_kind; char *prog_kind;
enum bpf_prog_type prog_type; enum bpf_prog_type prog_type;
...@@ -699,9 +643,10 @@ static void match_program(struct btf *btf, ...@@ -699,9 +643,10 @@ static void match_program(struct btf *btf,
char *reg_map[][2], char *reg_map[][2],
bool skip_first_insn) bool skip_first_insn)
{ {
struct bpf_insn *buf = NULL; struct bpf_insn *buf = NULL, *insn, *insn_end;
int err = 0, prog_fd = 0; int err = 0, prog_fd = 0;
FILE *prog_out = NULL; FILE *prog_out = NULL;
char insn_buf[64];
char *text = NULL; char *text = NULL;
__u32 cnt = 0; __u32 cnt = 0;
...@@ -739,12 +684,13 @@ static void match_program(struct btf *btf, ...@@ -739,12 +684,13 @@ static void match_program(struct btf *btf,
PRINT_FAIL("Can't open memory stream\n"); PRINT_FAIL("Can't open memory stream\n");
goto out; goto out;
} }
if (skip_first_insn) insn_end = buf + cnt;
print_xlated(prog_out, buf + 1, cnt - 1); insn = buf + (skip_first_insn ? 1 : 0);
else while (insn < insn_end) {
print_xlated(prog_out, buf, cnt); insn = disasm_insn(insn, insn_buf, sizeof(insn_buf));
fprintf(prog_out, "%s\n", insn_buf);
}
fclose(prog_out); fclose(prog_out);
remove_insn_prefix(text, MAX_PROG_TEXT_SZ);
ASSERT_TRUE(match_pattern(btf, pattern, text, reg_map), ASSERT_TRUE(match_pattern(btf, pattern, text, reg_map),
pinfo->prog_kind); pinfo->prog_kind);
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <errno.h> #include <errno.h>
#include <bpf/bpf.h> #include <bpf/bpf.h>
#include <bpf/libbpf.h> #include <bpf/libbpf.h>
#include "disasm.h"
#include "test_progs.h" #include "test_progs.h"
#include "testing_helpers.h" #include "testing_helpers.h"
#include <linux/membarrier.h> #include <linux/membarrier.h>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment