// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) #include #include "disasm.h" struct print_insn_context { char scratch[16]; 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); } static const char *print_call_cb(void *private_data, const struct bpf_insn *insn) { struct print_insn_context *ctx = private_data; /* For pseudo calls verifier.c:jit_subprogs() hides original * imm to insn->off and changes insn->imm to be an index of * the subprog instead. */ if (insn->src_reg == BPF_PSEUDO_CALL) { snprintf(ctx->scratch, sizeof(ctx->scratch), "%+d", insn->off); return ctx->scratch; } return NULL; } 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, .cb_call = print_call_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); }