mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-12-27 14:41:22 -05:00
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>
This commit is contained in:
committed by
Andrii Nakryiko
parent
91b7fbf393
commit
424ebaa367
@@ -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
|
||||
|
||||
51
tools/testing/selftests/bpf/disasm_helpers.c
Normal file
51
tools/testing/selftests/bpf/disasm_helpers.c
Normal file
@@ -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);
|
||||
}
|
||||
12
tools/testing/selftests/bpf/disasm_helpers.h
Normal file
12
tools/testing/selftests/bpf/disasm_helpers.h
Normal file
@@ -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 */
|
||||
@@ -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 @@ static bool match_pattern(struct btf *btf, char *pattern, char *text, char *reg_
|
||||
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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user