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

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.com


Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
parent 91b7fbf3
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
// 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 −0
Original line number Diff line number Diff line
/* 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 −64
Original line number Diff line number Diff line
@@ -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);
+1 −0
Original line number Diff line number Diff line
@@ -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>