| 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) |
| 2 | |
| 3 | #include <bpf/bpf.h> |
| 4 | #include "disasm.h" |
| 5 | |
| 6 | struct print_insn_context { |
| 7 | char scratch[16]; |
| 8 | char *buf; |
| 9 | size_t sz; |
| 10 | }; |
| 11 | |
| 12 | static void print_insn_cb(void *private_data, const char *fmt, ...) |
| 13 | { |
| 14 | struct print_insn_context *ctx = private_data; |
| 15 | va_list args; |
| 16 | |
| 17 | va_start(args, fmt); |
| 18 | vsnprintf(buf: ctx->buf, size: ctx->sz, fmt, args); |
| 19 | va_end(args); |
| 20 | } |
| 21 | |
| 22 | static const char *print_call_cb(void *private_data, const struct bpf_insn *insn) |
| 23 | { |
| 24 | struct print_insn_context *ctx = private_data; |
| 25 | |
| 26 | /* For pseudo calls verifier.c:jit_subprogs() hides original |
| 27 | * imm to insn->off and changes insn->imm to be an index of |
| 28 | * the subprog instead. |
| 29 | */ |
| 30 | if (insn->src_reg == BPF_PSEUDO_CALL) { |
| 31 | snprintf(buf: ctx->scratch, size: sizeof(ctx->scratch), fmt: "%+d" , insn->off); |
| 32 | return ctx->scratch; |
| 33 | } |
| 34 | |
| 35 | return NULL; |
| 36 | } |
| 37 | |
| 38 | struct bpf_insn *disasm_insn(struct bpf_insn *insn, char *buf, size_t buf_sz) |
| 39 | { |
| 40 | struct print_insn_context ctx = { |
| 41 | .buf = buf, |
| 42 | .sz = buf_sz, |
| 43 | }; |
| 44 | struct bpf_insn_cbs cbs = { |
| 45 | .cb_print = print_insn_cb, |
| 46 | .cb_call = print_call_cb, |
| 47 | .private_data = &ctx, |
| 48 | }; |
| 49 | char *tmp, *pfx_end, *sfx_start; |
| 50 | bool double_insn; |
| 51 | int len; |
| 52 | |
| 53 | print_bpf_insn(cbs: &cbs, insn, allow_ptr_leaks: true); |
| 54 | /* We share code with kernel BPF disassembler, it adds '(FF) ' prefix |
| 55 | * for each instruction (FF stands for instruction `code` byte). |
| 56 | * Remove the prefix inplace, and also simplify call instructions. |
| 57 | * E.g.: "(85) call foo#10" -> "call foo". |
| 58 | * Also remove newline in the end (the 'max(strlen(buf) - 1, 0)' thing). |
| 59 | */ |
| 60 | pfx_end = buf + 5; |
| 61 | sfx_start = buf + max((int)strlen(buf) - 1, 0); |
| 62 | if (strncmp(pfx_end, "call " , 5) == 0 && (tmp = strrchr(buf, '#'))) |
| 63 | sfx_start = tmp; |
| 64 | len = sfx_start - pfx_end; |
| 65 | memmove(buf, pfx_end, len); |
| 66 | buf[len] = 0; |
| 67 | double_insn = insn->code == (BPF_LD | BPF_IMM | BPF_DW); |
| 68 | return insn + (double_insn ? 2 : 1); |
| 69 | } |
| 70 | |