1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
5 * Minimal BPF JIT image disassembler
7 * Disassembles BPF JIT compiler emitted opcodes back to asm insn's for
8 * debugging or verification purposes.
11 * Licensed under the GNU General Public License, version 2.0 (GPLv2)
25 #include <bpf/libbpf.h>
27 #ifdef HAVE_LLVM_SUPPORT
28 #include <llvm-c/Core.h>
29 #include <llvm-c/Disassembler.h>
30 #include <llvm-c/Target.h>
31 #include <llvm-c/TargetMachine.h>
34 #ifdef HAVE_LIBBFD_SUPPORT
37 #include <tools/dis-asm-compat.h>
40 #include "json_writer.h"
43 static int oper_count;
45 #ifdef HAVE_LLVM_SUPPORT
48 typedef LLVMDisasmContextRef disasm_ctx_t;
50 static int printf_json(char *s)
53 jsonw_string_field(json_wtr, "operation", s);
55 jsonw_name(json_wtr, "operands");
56 jsonw_start_array(json_wtr);
59 while ((s = strtok(NULL, " \t,()")) != 0) {
60 jsonw_string(json_wtr, s);
66 /* This callback to set the ref_type is necessary to have the LLVM disassembler
67 * print PC-relative addresses instead of byte offsets for branch instruction
71 symbol_lookup_callback(__maybe_unused void *disasm_info,
72 __maybe_unused uint64_t ref_value,
73 uint64_t *ref_type, __maybe_unused uint64_t ref_PC,
74 __maybe_unused const char **ref_name)
76 *ref_type = LLVMDisassembler_ReferenceType_InOut_None;
81 init_context(disasm_ctx_t *ctx, const char *arch,
82 __maybe_unused const char *disassembler_options,
83 __maybe_unused unsigned char *image, __maybe_unused ssize_t len)
88 triple = LLVMNormalizeTargetTriple(arch);
90 triple = LLVMGetDefaultTargetTriple();
92 p_err("Failed to retrieve triple");
95 *ctx = LLVMCreateDisasm(triple, NULL, 0, NULL, symbol_lookup_callback);
96 LLVMDisposeMessage(triple);
99 p_err("Failed to create disassembler");
106 static void destroy_context(disasm_ctx_t *ctx)
108 LLVMDisposeMessage(*ctx);
112 disassemble_insn(disasm_ctx_t *ctx, unsigned char *image, ssize_t len, int pc)
117 count = LLVMDisasmInstruction(*ctx, image + pc, len - pc, pc,
127 int disasm_init(void)
129 LLVMInitializeAllTargetInfos();
130 LLVMInitializeAllTargetMCs();
131 LLVMInitializeAllDisassemblers();
134 #endif /* HAVE_LLVM_SUPPORT */
136 #ifdef HAVE_LIBBFD_SUPPORT
137 #define DISASM_SPACER "\t"
140 struct disassemble_info *info;
141 disassembler_ftype disassemble;
145 static int get_exec_path(char *tpath, size_t size)
147 const char *path = "/proc/self/exe";
150 len = readlink(path, tpath, size - 1);
159 static int printf_json(void *out, const char *fmt, va_list ap)
164 err = vasprintf(&s, fmt, ap);
171 /* Strip trailing spaces */
176 jsonw_string_field(json_wtr, "operation", s);
177 jsonw_name(json_wtr, "operands");
178 jsonw_start_array(json_wtr);
180 } else if (!strcmp(fmt, ",")) {
183 jsonw_string(json_wtr, s);
190 static int fprintf_json(void *out, const char *fmt, ...)
196 r = printf_json(out, fmt, ap);
202 static int fprintf_json_styled(void *out,
203 enum disassembler_style style __maybe_unused,
204 const char *fmt, ...)
210 r = printf_json(out, fmt, ap);
216 static int init_context(disasm_ctx_t *ctx, const char *arch,
217 const char *disassembler_options,
218 unsigned char *image, ssize_t len)
220 struct disassemble_info *info;
221 char tpath[PATH_MAX];
224 memset(tpath, 0, sizeof(tpath));
225 if (get_exec_path(tpath, sizeof(tpath))) {
226 p_err("failed to create disassembler (get_exec_path)");
230 ctx->bfdf = bfd_openr(tpath, NULL);
232 p_err("failed to create disassembler (bfd_openr)");
235 if (!bfd_check_format(ctx->bfdf, bfd_object)) {
236 p_err("failed to create disassembler (bfd_check_format)");
241 ctx->info = malloc(sizeof(struct disassemble_info));
243 p_err("mem alloc failed");
249 init_disassemble_info_compat(info, stdout,
250 (fprintf_ftype) fprintf_json,
251 fprintf_json_styled);
253 init_disassemble_info_compat(info, stdout,
254 (fprintf_ftype) fprintf,
257 /* Update architecture info for offload. */
259 const bfd_arch_info_type *inf = bfd_scan_arch(arch);
262 bfdf->arch_info = inf;
264 p_err("No libbfd support for %s", arch);
269 info->arch = bfd_get_arch(bfdf);
270 info->mach = bfd_get_mach(bfdf);
271 if (disassembler_options)
272 info->disassembler_options = disassembler_options;
273 info->buffer = image;
274 info->buffer_length = len;
276 disassemble_init_for_target(info);
278 #ifdef DISASM_FOUR_ARGS_SIGNATURE
279 ctx->disassemble = disassembler(info->arch,
280 bfd_big_endian(bfdf),
284 ctx->disassemble = disassembler(bfdf);
286 if (!ctx->disassemble) {
287 p_err("failed to create disassembler");
295 bfd_close(ctx->bfdf);
299 static void destroy_context(disasm_ctx_t *ctx)
302 bfd_close(ctx->bfdf);
306 disassemble_insn(disasm_ctx_t *ctx, __maybe_unused unsigned char *image,
307 __maybe_unused ssize_t len, int pc)
309 return ctx->disassemble(pc, ctx->info);
312 int disasm_init(void)
317 #endif /* HAVE_LIBBPFD_SUPPORT */
319 int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
320 const char *arch, const char *disassembler_options,
321 const struct btf *btf,
322 const struct bpf_prog_linfo *prog_linfo,
323 __u64 func_ksym, unsigned int func_idx,
326 const struct bpf_line_info *linfo = NULL;
327 unsigned int nr_skip = 0;
328 int count, i, pc = 0;
334 if (init_context(&ctx, arch, disassembler_options, image, len))
338 jsonw_start_array(json_wtr);
341 linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo,
350 jsonw_start_object(json_wtr);
353 btf_dump_linfo_json(btf, linfo, linum);
354 jsonw_name(json_wtr, "pc");
355 jsonw_printf(json_wtr, "\"0x%x\"", pc);
358 btf_dump_linfo_plain(btf, linfo, "; ",
360 printf("%4x:" DISASM_SPACER, pc);
363 count = disassemble_insn(&ctx, image, len, pc);
366 /* Operand array, was started in fprintf_json. Before
367 * that, make sure we have a _null_ value if no operand
368 * other than operation code was present.
371 jsonw_null(json_wtr);
372 jsonw_end_array(json_wtr);
377 jsonw_name(json_wtr, "opcodes");
378 jsonw_start_array(json_wtr);
379 for (i = 0; i < count; ++i)
380 jsonw_printf(json_wtr, "\"0x%02hhx\"",
381 (uint8_t)image[pc + i]);
382 jsonw_end_array(json_wtr);
385 for (i = 0; i < count; ++i)
387 (uint8_t)image[pc + i]);
391 jsonw_end_object(json_wtr);
396 } while (count > 0 && pc < len);
398 jsonw_end_array(json_wtr);
400 destroy_context(&ctx);