| // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) |
| |
| #include <bpf/bpf.h> |
| #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); |
| } |