/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright 2023 Red Hat GmbH
* Author: Alice Frosi <afrosi@redhat.com>
*/
#define _GNU_SOURCE
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <linux/filter.h>
#include "disasm.h"
/* From linux tools/bpf/bpf_dbg.c */
#define BPF_LDX_B (BPF_LDX | BPF_B)
#define BPF_LDX_W (BPF_LDX | BPF_W)
#define BPF_JMP_JA (BPF_JMP | BPF_JA)
#define BPF_JMP_JEQ (BPF_JMP | BPF_JEQ)
#define BPF_JMP_JGT (BPF_JMP | BPF_JGT)
#define BPF_JMP_JGE (BPF_JMP | BPF_JGE)
#define BPF_JMP_JSET (BPF_JMP | BPF_JSET)
#define BPF_ALU_ADD (BPF_ALU | BPF_ADD)
#define BPF_ALU_SUB (BPF_ALU | BPF_SUB)
#define BPF_ALU_MUL (BPF_ALU | BPF_MUL)
#define BPF_ALU_DIV (BPF_ALU | BPF_DIV)
#define BPF_ALU_MOD (BPF_ALU | BPF_MOD)
#define BPF_ALU_NEG (BPF_ALU | BPF_NEG)
#define BPF_ALU_AND (BPF_ALU | BPF_AND)
#define BPF_ALU_OR (BPF_ALU | BPF_OR)
#define BPF_ALU_XOR (BPF_ALU | BPF_XOR)
#define BPF_ALU_LSH (BPF_ALU | BPF_LSH)
#define BPF_ALU_RSH (BPF_ALU | BPF_RSH)
#define BPF_MISC_TAX (BPF_MISC | BPF_TAX)
#define BPF_MISC_TXA (BPF_MISC | BPF_TXA)
#define BPF_LD_B (BPF_LD | BPF_B)
#define BPF_LD_H (BPF_LD | BPF_H)
#define BPF_LD_W (BPF_LD | BPF_W)
static const char *const op_table[] = {
[BPF_ST] = "st", [BPF_STX] = "stx", [BPF_LD_B] = "ldb",
[BPF_LD_H] = "ldh", [BPF_LD_W] = "ld", [BPF_LDX] = "ldx",
[BPF_LDX_B] = "ldxb", [BPF_JMP_JA] = "ja", [BPF_JMP_JEQ] = "jeq",
[BPF_JMP_JGT] = "jgt", [BPF_JMP_JGE] = "jge", [BPF_JMP_JSET] = "jset",
[BPF_ALU_ADD] = "add", [BPF_ALU_SUB] = "sub", [BPF_ALU_MUL] = "mul",
[BPF_ALU_DIV] = "div", [BPF_ALU_MOD] = "mod", [BPF_ALU_NEG] = "neg",
[BPF_ALU_AND] = "and", [BPF_ALU_OR] = "or", [BPF_ALU_XOR] = "xor",
[BPF_ALU_LSH] = "lsh", [BPF_ALU_RSH] = "rsh", [BPF_MISC_TAX] = "tax",
[BPF_MISC_TXA] = "txa", [BPF_RET] = "ret",
};
void bpf_disasm(const struct sock_filter f, unsigned int i)
{
const char *op, *fmt;
int val = f.k;
char buf[256];
switch (f.code) {
case BPF_RET | BPF_K:
op = op_table[BPF_RET];
fmt = "#%#x";
break;
case BPF_RET | BPF_A:
op = op_table[BPF_RET];
fmt = "a";
break;
case BPF_RET | BPF_X:
op = op_table[BPF_RET];
fmt = "x";
break;
case BPF_MISC_TAX:
op = op_table[BPF_MISC_TAX];
fmt = "";
break;
case BPF_MISC_TXA:
op = op_table[BPF_MISC_TXA];
fmt = "";
break;
case BPF_ST:
op = op_table[BPF_ST];
fmt = "M[%d]";
break;
case BPF_STX:
op = op_table[BPF_STX];
fmt = "M[%d]";
break;
case BPF_LD_W | BPF_ABS:
op = op_table[BPF_LD_W];
fmt = "[%d]";
break;
case BPF_LD_H | BPF_ABS:
op = op_table[BPF_LD_H];
fmt = "[%d]";
break;
case BPF_LD_B | BPF_ABS:
op = op_table[BPF_LD_B];
fmt = "[%d]";
break;
case BPF_LD_W | BPF_LEN:
op = op_table[BPF_LD_W];
fmt = "#len";
break;
case BPF_LD_W | BPF_IND:
op = op_table[BPF_LD_W];
fmt = "[x+%d]";
break;
case BPF_LD_H | BPF_IND:
op = op_table[BPF_LD_H];
fmt = "[x+%d]";
break;
case BPF_LD_B | BPF_IND:
op = op_table[BPF_LD_B];
fmt = "[x+%d]";
break;
case BPF_LD | BPF_IMM:
op = op_table[BPF_LD_W];
fmt = "#%#x";
break;
case BPF_LDX | BPF_IMM:
op = op_table[BPF_LDX];
fmt = "#%#x";
break;
case BPF_LDX_B | BPF_MSH:
op = op_table[BPF_LDX_B];
fmt = "4*([%d]&0xf)";
break;
case BPF_LD | BPF_MEM:
op = op_table[BPF_LD_W];
fmt = "M[%d]";
break;
case BPF_LDX | BPF_MEM:
op = op_table[BPF_LDX];
fmt = "M[%d]";
break;
case BPF_JMP_JA:
op = op_table[BPF_JMP_JA];
fmt = "%d";
val = i + 1 + f.k;
break;
case BPF_JMP_JGT | BPF_X:
op = op_table[BPF_JMP_JGT];
fmt = "x";
break;
case BPF_JMP_JGT | BPF_K:
op = op_table[BPF_JMP_JGT];
fmt = "#%#x";
break;
case BPF_JMP_JGE | BPF_X:
op = op_table[BPF_JMP_JGE];
fmt = "x";
break;
case BPF_JMP_JGE | BPF_K:
op = op_table[BPF_JMP_JGE];
fmt = "#%#x";
break;
case BPF_JMP_JEQ | BPF_X:
op = op_table[BPF_JMP_JEQ];
fmt = "x";
break;
case BPF_JMP_JEQ | BPF_K:
op = op_table[BPF_JMP_JEQ];
fmt = "#%#x";
break;
case BPF_JMP_JSET | BPF_X:
op = op_table[BPF_JMP_JSET];
fmt = "x";
break;
case BPF_JMP_JSET | BPF_K:
op = op_table[BPF_JMP_JSET];
fmt = "#%#x";
break;
case BPF_ALU_NEG:
op = op_table[BPF_ALU_NEG];
fmt = "";
break;
case BPF_ALU_LSH | BPF_X:
op = op_table[BPF_ALU_LSH];
fmt = "x";
break;
case BPF_ALU_LSH | BPF_K:
op = op_table[BPF_ALU_LSH];
fmt = "#%d";
break;
case BPF_ALU_RSH | BPF_X:
op = op_table[BPF_ALU_RSH];
fmt = "x";
break;
case BPF_ALU_RSH | BPF_K:
op = op_table[BPF_ALU_RSH];
fmt = "#%d";
break;
case BPF_ALU_ADD | BPF_X:
op = op_table[BPF_ALU_ADD];
fmt = "x";
break;
case BPF_ALU_ADD | BPF_K:
op = op_table[BPF_ALU_ADD];
fmt = "#%d";
break;
case BPF_ALU_SUB | BPF_X:
op = op_table[BPF_ALU_SUB];
fmt = "x";
break;
case BPF_ALU_SUB | BPF_K:
op = op_table[BPF_ALU_SUB];
fmt = "#%d";
break;
case BPF_ALU_MUL | BPF_X:
op = op_table[BPF_ALU_MUL];
fmt = "x";
break;
case BPF_ALU_MUL | BPF_K:
op = op_table[BPF_ALU_MUL];
fmt = "#%d";
break;
case BPF_ALU_DIV | BPF_X:
op = op_table[BPF_ALU_DIV];
fmt = "x";
break;
case BPF_ALU_DIV | BPF_K:
op = op_table[BPF_ALU_DIV];
fmt = "#%d";
break;
case BPF_ALU_MOD | BPF_X:
op = op_table[BPF_ALU_MOD];
fmt = "x";
break;
case BPF_ALU_MOD | BPF_K:
op = op_table[BPF_ALU_MOD];
fmt = "#%d";
break;
case BPF_ALU_AND | BPF_X:
op = op_table[BPF_ALU_AND];
fmt = "x";
break;
case BPF_ALU_AND | BPF_K:
op = op_table[BPF_ALU_AND];
fmt = "#%#x";
break;
case BPF_ALU_OR | BPF_X:
op = op_table[BPF_ALU_OR];
fmt = "x";
break;
case BPF_ALU_OR | BPF_K:
op = op_table[BPF_ALU_OR];
fmt = "#%#x";
break;
case BPF_ALU_XOR | BPF_X:
op = op_table[BPF_ALU_XOR];
fmt = "x";
break;
case BPF_ALU_XOR | BPF_K:
op = op_table[BPF_ALU_XOR];
fmt = "#%#x";
break;
default:
op = "nosup";
fmt = "%#x";
val = f.code;
break;
}
memset(buf, 0, sizeof(buf));
snprintf(buf, sizeof(buf), fmt, val);
buf[sizeof(buf) - 1] = 0;
if ((BPF_CLASS(f.code) == BPF_JMP && BPF_OP(f.code) != BPF_JA)) {
printf("l%d:\t%s %s, l%d, l%d\n", i, op, buf, i + 1 + f.jt,
i + 1 + f.jf);
} else {
printf("l%d:\t%s %s\n", i, op, buf);
}
}
void bpf_disasm_all(const struct sock_filter *f, unsigned int len)
{
unsigned int i;
for (i = 0; i < len; i++)
bpf_disasm(f[i], i);
}