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.
10 * Copyright 2013 Daniel Borkmann <daniel@iogearbox.net>
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
,
84 __maybe_unused __u64 func_ksym
)
89 triple
= LLVMNormalizeTargetTriple(arch
);
91 triple
= LLVMGetDefaultTargetTriple();
93 p_err("Failed to retrieve triple");
96 *ctx
= LLVMCreateDisasm(triple
, NULL
, 0, NULL
, symbol_lookup_callback
);
97 LLVMDisposeMessage(triple
);
100 p_err("Failed to create disassembler");
107 static void destroy_context(disasm_ctx_t
*ctx
)
109 LLVMDisposeMessage(*ctx
);
113 disassemble_insn(disasm_ctx_t
*ctx
, unsigned char *image
, ssize_t len
, int pc
,
119 count
= LLVMDisasmInstruction(*ctx
, image
+ pc
, len
- pc
, func_ksym
+ pc
,
129 int disasm_init(void)
131 LLVMInitializeAllTargetInfos();
132 LLVMInitializeAllTargetMCs();
133 LLVMInitializeAllDisassemblers();
136 #endif /* HAVE_LLVM_SUPPORT */
138 #ifdef HAVE_LIBBFD_SUPPORT
139 #define DISASM_SPACER "\t"
142 struct disassemble_info info
;
146 static void disasm_print_addr(bfd_vma addr
, struct disassemble_info
*info
)
148 struct disasm_info
*dinfo
= container_of(info
, struct disasm_info
, info
);
150 addr
+= dinfo
->func_ksym
;
151 generic_print_address(addr
, info
);
155 struct disasm_info
*info
;
156 disassembler_ftype disassemble
;
160 static int get_exec_path(char *tpath
, size_t size
)
162 const char *path
= "/proc/self/exe";
165 len
= readlink(path
, tpath
, size
- 1);
174 static int printf_json(void *out
, const char *fmt
, va_list ap
)
179 err
= vasprintf(&s
, fmt
, ap
);
186 /* Strip trailing spaces */
191 jsonw_string_field(json_wtr
, "operation", s
);
192 jsonw_name(json_wtr
, "operands");
193 jsonw_start_array(json_wtr
);
195 } else if (!strcmp(fmt
, ",")) {
198 jsonw_string(json_wtr
, s
);
205 static int fprintf_json(void *out
, const char *fmt
, ...)
211 r
= printf_json(out
, fmt
, ap
);
217 static int fprintf_json_styled(void *out
,
218 enum disassembler_style style __maybe_unused
,
219 const char *fmt
, ...)
225 r
= printf_json(out
, fmt
, ap
);
231 static int init_context(disasm_ctx_t
*ctx
, const char *arch
,
232 const char *disassembler_options
,
233 unsigned char *image
, ssize_t len
, __u64 func_ksym
)
235 struct disassemble_info
*info
;
236 char tpath
[PATH_MAX
];
239 memset(tpath
, 0, sizeof(tpath
));
240 if (get_exec_path(tpath
, sizeof(tpath
))) {
241 p_err("failed to create disassembler (get_exec_path)");
245 ctx
->bfdf
= bfd_openr(tpath
, NULL
);
247 p_err("failed to create disassembler (bfd_openr)");
250 if (!bfd_check_format(ctx
->bfdf
, bfd_object
)) {
251 p_err("failed to create disassembler (bfd_check_format)");
256 ctx
->info
= malloc(sizeof(struct disasm_info
));
258 p_err("mem alloc failed");
261 ctx
->info
->func_ksym
= func_ksym
;
262 info
= &ctx
->info
->info
;
265 init_disassemble_info_compat(info
, stdout
,
266 (fprintf_ftype
) fprintf_json
,
267 fprintf_json_styled
);
269 init_disassemble_info_compat(info
, stdout
,
270 (fprintf_ftype
) fprintf
,
273 /* Update architecture info for offload. */
275 const bfd_arch_info_type
*inf
= bfd_scan_arch(arch
);
278 bfdf
->arch_info
= inf
;
280 p_err("No libbfd support for %s", arch
);
285 info
->arch
= bfd_get_arch(bfdf
);
286 info
->mach
= bfd_get_mach(bfdf
);
287 if (disassembler_options
)
288 info
->disassembler_options
= disassembler_options
;
289 info
->buffer
= image
;
290 info
->buffer_length
= len
;
291 info
->print_address_func
= disasm_print_addr
;
293 disassemble_init_for_target(info
);
295 #ifdef DISASM_FOUR_ARGS_SIGNATURE
296 ctx
->disassemble
= disassembler(info
->arch
,
297 bfd_big_endian(bfdf
),
301 ctx
->disassemble
= disassembler(bfdf
);
303 if (!ctx
->disassemble
) {
304 p_err("failed to create disassembler");
312 bfd_close(ctx
->bfdf
);
316 static void destroy_context(disasm_ctx_t
*ctx
)
319 bfd_close(ctx
->bfdf
);
323 disassemble_insn(disasm_ctx_t
*ctx
, __maybe_unused
unsigned char *image
,
324 __maybe_unused ssize_t len
, int pc
,
325 __maybe_unused __u64 func_ksym
)
327 return ctx
->disassemble(pc
, &ctx
->info
->info
);
330 int disasm_init(void)
335 #endif /* HAVE_LIBBPFD_SUPPORT */
337 int disasm_print_insn(unsigned char *image
, ssize_t len
, int opcodes
,
338 const char *arch
, const char *disassembler_options
,
339 const struct btf
*btf
,
340 const struct bpf_prog_linfo
*prog_linfo
,
341 __u64 func_ksym
, unsigned int func_idx
,
344 const struct bpf_line_info
*linfo
= NULL
;
345 unsigned int nr_skip
= 0;
346 int count
, i
, pc
= 0;
352 if (init_context(&ctx
, arch
, disassembler_options
, image
, len
, func_ksym
))
356 jsonw_start_array(json_wtr
);
359 linfo
= bpf_prog_linfo__lfind_addr_func(prog_linfo
,
368 jsonw_start_object(json_wtr
);
371 btf_dump_linfo_json(btf
, linfo
, linum
);
372 jsonw_name(json_wtr
, "pc");
373 jsonw_printf(json_wtr
, "\"0x%x\"", pc
);
376 btf_dump_linfo_plain(btf
, linfo
, "; ",
378 printf("%4x:" DISASM_SPACER
, pc
);
381 count
= disassemble_insn(&ctx
, image
, len
, pc
, func_ksym
);
384 /* Operand array, was started in fprintf_json. Before
385 * that, make sure we have a _null_ value if no operand
386 * other than operation code was present.
389 jsonw_null(json_wtr
);
390 jsonw_end_array(json_wtr
);
395 jsonw_name(json_wtr
, "opcodes");
396 jsonw_start_array(json_wtr
);
397 for (i
= 0; i
< count
; ++i
)
398 jsonw_printf(json_wtr
, "\"0x%02hhx\"",
399 (uint8_t)image
[pc
+ i
]);
400 jsonw_end_array(json_wtr
);
403 for (i
= 0; i
< count
; ++i
)
405 (uint8_t)image
[pc
+ i
]);
409 jsonw_end_object(json_wtr
);
414 } while (count
> 0 && pc
< len
);
416 jsonw_end_array(json_wtr
);
418 destroy_context(&ctx
);