1907b2236SJakub Kicinski // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
271bb428fSJakub Kicinski /*
371bb428fSJakub Kicinski * Based on:
471bb428fSJakub Kicinski *
571bb428fSJakub Kicinski * Minimal BPF JIT image disassembler
671bb428fSJakub Kicinski *
771bb428fSJakub Kicinski * Disassembles BPF JIT compiler emitted opcodes back to asm insn's for
871bb428fSJakub Kicinski * debugging or verification purposes.
971bb428fSJakub Kicinski *
1071bb428fSJakub Kicinski * Copyright 2013 Daniel Borkmann <daniel@iogearbox.net>
1171bb428fSJakub Kicinski * Licensed under the GNU General Public License, version 2.0 (GPLv2)
1271bb428fSJakub Kicinski */
1371bb428fSJakub Kicinski
14b3d84af7SQuentin Monnet #ifndef _GNU_SOURCE
15aa52bcbeSJiri Olsa #define _GNU_SOURCE
16b3d84af7SQuentin Monnet #endif
17aa52bcbeSJiri Olsa #include <stdio.h>
18107f0412SQuentin Monnet #include <stdarg.h>
1971bb428fSJakub Kicinski #include <stdint.h>
2071bb428fSJakub Kicinski #include <stdlib.h>
2171bb428fSJakub Kicinski #include <unistd.h>
2271bb428fSJakub Kicinski #include <string.h>
2371bb428fSJakub Kicinski #include <sys/stat.h>
24cdc89c91SPrashant Bhole #include <limits.h>
25229c3b47SToke Høiland-Jørgensen #include <bpf/libbpf.h>
26eb9d1acfSQuentin Monnet
27eb9d1acfSQuentin Monnet #ifdef HAVE_LLVM_SUPPORT
28eb9d1acfSQuentin Monnet #include <llvm-c/Core.h>
29eb9d1acfSQuentin Monnet #include <llvm-c/Disassembler.h>
30eb9d1acfSQuentin Monnet #include <llvm-c/Target.h>
31eb9d1acfSQuentin Monnet #include <llvm-c/TargetMachine.h>
32eb9d1acfSQuentin Monnet #endif
33eb9d1acfSQuentin Monnet
34eb9d1acfSQuentin Monnet #ifdef HAVE_LIBBFD_SUPPORT
35eb9d1acfSQuentin Monnet #include <bfd.h>
36eb9d1acfSQuentin Monnet #include <dis-asm.h>
37600b7b26SAndres Freund #include <tools/dis-asm-compat.h>
38eb9d1acfSQuentin Monnet #endif
3971bb428fSJakub Kicinski
40107f0412SQuentin Monnet #include "json_writer.h"
41107f0412SQuentin Monnet #include "main.h"
42107f0412SQuentin Monnet
43e1947c75SQuentin Monnet static int oper_count;
44e1947c75SQuentin Monnet
45eb9d1acfSQuentin Monnet #ifdef HAVE_LLVM_SUPPORT
46eb9d1acfSQuentin Monnet #define DISASM_SPACER
47eb9d1acfSQuentin Monnet
48eb9d1acfSQuentin Monnet typedef LLVMDisasmContextRef disasm_ctx_t;
49eb9d1acfSQuentin Monnet
printf_json(char * s)50eb9d1acfSQuentin Monnet static int printf_json(char *s)
51eb9d1acfSQuentin Monnet {
52eb9d1acfSQuentin Monnet s = strtok(s, " \t");
53eb9d1acfSQuentin Monnet jsonw_string_field(json_wtr, "operation", s);
54eb9d1acfSQuentin Monnet
55eb9d1acfSQuentin Monnet jsonw_name(json_wtr, "operands");
56eb9d1acfSQuentin Monnet jsonw_start_array(json_wtr);
57eb9d1acfSQuentin Monnet oper_count = 1;
58eb9d1acfSQuentin Monnet
59eb9d1acfSQuentin Monnet while ((s = strtok(NULL, " \t,()")) != 0) {
60eb9d1acfSQuentin Monnet jsonw_string(json_wtr, s);
61eb9d1acfSQuentin Monnet oper_count++;
62eb9d1acfSQuentin Monnet }
63eb9d1acfSQuentin Monnet return 0;
64eb9d1acfSQuentin Monnet }
65eb9d1acfSQuentin Monnet
66eb9d1acfSQuentin Monnet /* This callback to set the ref_type is necessary to have the LLVM disassembler
67eb9d1acfSQuentin Monnet * print PC-relative addresses instead of byte offsets for branch instruction
68eb9d1acfSQuentin Monnet * targets.
69eb9d1acfSQuentin Monnet */
70eb9d1acfSQuentin Monnet static const char *
symbol_lookup_callback(__maybe_unused void * disasm_info,__maybe_unused uint64_t ref_value,uint64_t * ref_type,__maybe_unused uint64_t ref_PC,__maybe_unused const char ** ref_name)71eb9d1acfSQuentin Monnet symbol_lookup_callback(__maybe_unused void *disasm_info,
72eb9d1acfSQuentin Monnet __maybe_unused uint64_t ref_value,
73eb9d1acfSQuentin Monnet uint64_t *ref_type, __maybe_unused uint64_t ref_PC,
74eb9d1acfSQuentin Monnet __maybe_unused const char **ref_name)
75eb9d1acfSQuentin Monnet {
76eb9d1acfSQuentin Monnet *ref_type = LLVMDisassembler_ReferenceType_InOut_None;
77eb9d1acfSQuentin Monnet return NULL;
78eb9d1acfSQuentin Monnet }
79eb9d1acfSQuentin Monnet
80eb9d1acfSQuentin Monnet static int
init_context(disasm_ctx_t * ctx,const char * arch,__maybe_unused const char * disassembler_options,__maybe_unused unsigned char * image,__maybe_unused ssize_t len,__maybe_unused __u64 func_ksym)81eb9d1acfSQuentin Monnet init_context(disasm_ctx_t *ctx, const char *arch,
82eb9d1acfSQuentin Monnet __maybe_unused const char *disassembler_options,
83*41f0b320SLeon Hwang __maybe_unused unsigned char *image, __maybe_unused ssize_t len,
84*41f0b320SLeon Hwang __maybe_unused __u64 func_ksym)
85eb9d1acfSQuentin Monnet {
86eb9d1acfSQuentin Monnet char *triple;
87eb9d1acfSQuentin Monnet
88ce4f6608SQuentin Monnet if (arch)
89ce4f6608SQuentin Monnet triple = LLVMNormalizeTargetTriple(arch);
90ce4f6608SQuentin Monnet else
91eb9d1acfSQuentin Monnet triple = LLVMGetDefaultTargetTriple();
92eb9d1acfSQuentin Monnet if (!triple) {
93eb9d1acfSQuentin Monnet p_err("Failed to retrieve triple");
94eb9d1acfSQuentin Monnet return -1;
95eb9d1acfSQuentin Monnet }
96eb9d1acfSQuentin Monnet *ctx = LLVMCreateDisasm(triple, NULL, 0, NULL, symbol_lookup_callback);
97eb9d1acfSQuentin Monnet LLVMDisposeMessage(triple);
98eb9d1acfSQuentin Monnet
99eb9d1acfSQuentin Monnet if (!*ctx) {
100eb9d1acfSQuentin Monnet p_err("Failed to create disassembler");
101eb9d1acfSQuentin Monnet return -1;
102eb9d1acfSQuentin Monnet }
103eb9d1acfSQuentin Monnet
104eb9d1acfSQuentin Monnet return 0;
105eb9d1acfSQuentin Monnet }
106eb9d1acfSQuentin Monnet
destroy_context(disasm_ctx_t * ctx)107eb9d1acfSQuentin Monnet static void destroy_context(disasm_ctx_t *ctx)
108eb9d1acfSQuentin Monnet {
109eb9d1acfSQuentin Monnet LLVMDisposeMessage(*ctx);
110eb9d1acfSQuentin Monnet }
111eb9d1acfSQuentin Monnet
112eb9d1acfSQuentin Monnet static int
disassemble_insn(disasm_ctx_t * ctx,unsigned char * image,ssize_t len,int pc,__u64 func_ksym)113*41f0b320SLeon Hwang disassemble_insn(disasm_ctx_t *ctx, unsigned char *image, ssize_t len, int pc,
114*41f0b320SLeon Hwang __u64 func_ksym)
115eb9d1acfSQuentin Monnet {
116eb9d1acfSQuentin Monnet char buf[256];
117eb9d1acfSQuentin Monnet int count;
118eb9d1acfSQuentin Monnet
119*41f0b320SLeon Hwang count = LLVMDisasmInstruction(*ctx, image + pc, len - pc, func_ksym + pc,
120eb9d1acfSQuentin Monnet buf, sizeof(buf));
121eb9d1acfSQuentin Monnet if (json_output)
122eb9d1acfSQuentin Monnet printf_json(buf);
123eb9d1acfSQuentin Monnet else
124eb9d1acfSQuentin Monnet printf("%s", buf);
125eb9d1acfSQuentin Monnet
126eb9d1acfSQuentin Monnet return count;
127eb9d1acfSQuentin Monnet }
128eb9d1acfSQuentin Monnet
disasm_init(void)129eb9d1acfSQuentin Monnet int disasm_init(void)
130eb9d1acfSQuentin Monnet {
131ce4f6608SQuentin Monnet LLVMInitializeAllTargetInfos();
132ce4f6608SQuentin Monnet LLVMInitializeAllTargetMCs();
133ce4f6608SQuentin Monnet LLVMInitializeAllDisassemblers();
134eb9d1acfSQuentin Monnet return 0;
135eb9d1acfSQuentin Monnet }
136eb9d1acfSQuentin Monnet #endif /* HAVE_LLVM_SUPPORT */
137eb9d1acfSQuentin Monnet
138eb9d1acfSQuentin Monnet #ifdef HAVE_LIBBFD_SUPPORT
139eb9d1acfSQuentin Monnet #define DISASM_SPACER "\t"
140eb9d1acfSQuentin Monnet
141*41f0b320SLeon Hwang struct disasm_info {
142*41f0b320SLeon Hwang struct disassemble_info info;
143*41f0b320SLeon Hwang __u64 func_ksym;
144*41f0b320SLeon Hwang };
145*41f0b320SLeon Hwang
disasm_print_addr(bfd_vma addr,struct disassemble_info * info)146*41f0b320SLeon Hwang static void disasm_print_addr(bfd_vma addr, struct disassemble_info *info)
147*41f0b320SLeon Hwang {
148*41f0b320SLeon Hwang struct disasm_info *dinfo = container_of(info, struct disasm_info, info);
149*41f0b320SLeon Hwang
150*41f0b320SLeon Hwang addr += dinfo->func_ksym;
151*41f0b320SLeon Hwang generic_print_address(addr, info);
152*41f0b320SLeon Hwang }
153*41f0b320SLeon Hwang
154e1947c75SQuentin Monnet typedef struct {
155*41f0b320SLeon Hwang struct disasm_info *info;
156e1947c75SQuentin Monnet disassembler_ftype disassemble;
157e1947c75SQuentin Monnet bfd *bfdf;
158e1947c75SQuentin Monnet } disasm_ctx_t;
159e1947c75SQuentin Monnet
get_exec_path(char * tpath,size_t size)16055b4de58SQuentin Monnet static int get_exec_path(char *tpath, size_t size)
16171bb428fSJakub Kicinski {
162327e5dabSQuentin Monnet const char *path = "/proc/self/exe";
16371bb428fSJakub Kicinski ssize_t len;
16471bb428fSJakub Kicinski
16571bb428fSJakub Kicinski len = readlink(path, tpath, size - 1);
16655b4de58SQuentin Monnet if (len <= 0)
16755b4de58SQuentin Monnet return -1;
16855b4de58SQuentin Monnet
16971bb428fSJakub Kicinski tpath[len] = 0;
17055b4de58SQuentin Monnet
17155b4de58SQuentin Monnet return 0;
17271bb428fSJakub Kicinski }
17371bb428fSJakub Kicinski
printf_json(void * out,const char * fmt,va_list ap)174600b7b26SAndres Freund static int printf_json(void *out, const char *fmt, va_list ap)
175107f0412SQuentin Monnet {
176107f0412SQuentin Monnet char *s;
177bc832065SGu Shengxian int err;
178107f0412SQuentin Monnet
179bc832065SGu Shengxian err = vasprintf(&s, fmt, ap);
180bc832065SGu Shengxian if (err < 0)
181bc832065SGu Shengxian return -1;
182aa52bcbeSJiri Olsa
183107f0412SQuentin Monnet if (!oper_count) {
184107f0412SQuentin Monnet int i;
185107f0412SQuentin Monnet
186107f0412SQuentin Monnet /* Strip trailing spaces */
187107f0412SQuentin Monnet i = strlen(s) - 1;
188107f0412SQuentin Monnet while (s[i] == ' ')
189107f0412SQuentin Monnet s[i--] = '\0';
190107f0412SQuentin Monnet
191107f0412SQuentin Monnet jsonw_string_field(json_wtr, "operation", s);
192107f0412SQuentin Monnet jsonw_name(json_wtr, "operands");
193107f0412SQuentin Monnet jsonw_start_array(json_wtr);
194107f0412SQuentin Monnet oper_count++;
195107f0412SQuentin Monnet } else if (!strcmp(fmt, ",")) {
196107f0412SQuentin Monnet /* Skip */
197107f0412SQuentin Monnet } else {
198107f0412SQuentin Monnet jsonw_string(json_wtr, s);
199107f0412SQuentin Monnet oper_count++;
200107f0412SQuentin Monnet }
201aa52bcbeSJiri Olsa free(s);
202107f0412SQuentin Monnet return 0;
203107f0412SQuentin Monnet }
204107f0412SQuentin Monnet
fprintf_json(void * out,const char * fmt,...)205600b7b26SAndres Freund static int fprintf_json(void *out, const char *fmt, ...)
206600b7b26SAndres Freund {
207600b7b26SAndres Freund va_list ap;
208600b7b26SAndres Freund int r;
209600b7b26SAndres Freund
210600b7b26SAndres Freund va_start(ap, fmt);
211600b7b26SAndres Freund r = printf_json(out, fmt, ap);
212600b7b26SAndres Freund va_end(ap);
213600b7b26SAndres Freund
214600b7b26SAndres Freund return r;
215600b7b26SAndres Freund }
216600b7b26SAndres Freund
fprintf_json_styled(void * out,enum disassembler_style style __maybe_unused,const char * fmt,...)217600b7b26SAndres Freund static int fprintf_json_styled(void *out,
218600b7b26SAndres Freund enum disassembler_style style __maybe_unused,
219600b7b26SAndres Freund const char *fmt, ...)
220600b7b26SAndres Freund {
221600b7b26SAndres Freund va_list ap;
222600b7b26SAndres Freund int r;
223600b7b26SAndres Freund
224600b7b26SAndres Freund va_start(ap, fmt);
225600b7b26SAndres Freund r = printf_json(out, fmt, ap);
226600b7b26SAndres Freund va_end(ap);
227600b7b26SAndres Freund
228600b7b26SAndres Freund return r;
229600b7b26SAndres Freund }
230600b7b26SAndres Freund
init_context(disasm_ctx_t * ctx,const char * arch,const char * disassembler_options,unsigned char * image,ssize_t len,__u64 func_ksym)231e1947c75SQuentin Monnet static int init_context(disasm_ctx_t *ctx, const char *arch,
232e1947c75SQuentin Monnet const char *disassembler_options,
233*41f0b320SLeon Hwang unsigned char *image, ssize_t len, __u64 func_ksym)
23471bb428fSJakub Kicinski {
235e1947c75SQuentin Monnet struct disassemble_info *info;
236cdc89c91SPrashant Bhole char tpath[PATH_MAX];
23771bb428fSJakub Kicinski bfd *bfdf;
23871bb428fSJakub Kicinski
23971bb428fSJakub Kicinski memset(tpath, 0, sizeof(tpath));
24055b4de58SQuentin Monnet if (get_exec_path(tpath, sizeof(tpath))) {
24196f341a4SColin Ian King p_err("failed to create disassembler (get_exec_path)");
24255b4de58SQuentin Monnet return -1;
24355b4de58SQuentin Monnet }
24471bb428fSJakub Kicinski
245e1947c75SQuentin Monnet ctx->bfdf = bfd_openr(tpath, NULL);
246e1947c75SQuentin Monnet if (!ctx->bfdf) {
24755b4de58SQuentin Monnet p_err("failed to create disassembler (bfd_openr)");
24855b4de58SQuentin Monnet return -1;
24955b4de58SQuentin Monnet }
250e1947c75SQuentin Monnet if (!bfd_check_format(ctx->bfdf, bfd_object)) {
25155b4de58SQuentin Monnet p_err("failed to create disassembler (bfd_check_format)");
252e1947c75SQuentin Monnet goto err_close;
25355b4de58SQuentin Monnet }
254e1947c75SQuentin Monnet bfdf = ctx->bfdf;
255e1947c75SQuentin Monnet
256*41f0b320SLeon Hwang ctx->info = malloc(sizeof(struct disasm_info));
257e1947c75SQuentin Monnet if (!ctx->info) {
258e1947c75SQuentin Monnet p_err("mem alloc failed");
259e1947c75SQuentin Monnet goto err_close;
260e1947c75SQuentin Monnet }
261*41f0b320SLeon Hwang ctx->info->func_ksym = func_ksym;
262*41f0b320SLeon Hwang info = &ctx->info->info;
26371bb428fSJakub Kicinski
264107f0412SQuentin Monnet if (json_output)
265e1947c75SQuentin Monnet init_disassemble_info_compat(info, stdout,
266600b7b26SAndres Freund (fprintf_ftype) fprintf_json,
267600b7b26SAndres Freund fprintf_json_styled);
268107f0412SQuentin Monnet else
269e1947c75SQuentin Monnet init_disassemble_info_compat(info, stdout,
270600b7b26SAndres Freund (fprintf_ftype) fprintf,
271600b7b26SAndres Freund fprintf_styled);
272e6593596SJiong Wang
273e6593596SJiong Wang /* Update architecture info for offload. */
274e6593596SJiong Wang if (arch) {
275e6593596SJiong Wang const bfd_arch_info_type *inf = bfd_scan_arch(arch);
276e6593596SJiong Wang
277e6593596SJiong Wang if (inf) {
278e6593596SJiong Wang bfdf->arch_info = inf;
279e6593596SJiong Wang } else {
28029a9c10eSStanislav Fomichev p_err("No libbfd support for %s", arch);
281e1947c75SQuentin Monnet goto err_free;
282e6593596SJiong Wang }
283e6593596SJiong Wang }
284e6593596SJiong Wang
285e1947c75SQuentin Monnet info->arch = bfd_get_arch(bfdf);
286e1947c75SQuentin Monnet info->mach = bfd_get_mach(bfdf);
2873ddeac67SJakub Kicinski if (disassembler_options)
288e1947c75SQuentin Monnet info->disassembler_options = disassembler_options;
289e1947c75SQuentin Monnet info->buffer = image;
290e1947c75SQuentin Monnet info->buffer_length = len;
291*41f0b320SLeon Hwang info->print_address_func = disasm_print_addr;
29271bb428fSJakub Kicinski
293e1947c75SQuentin Monnet disassemble_init_for_target(info);
29471bb428fSJakub Kicinski
295fb982666SRoman Gushchin #ifdef DISASM_FOUR_ARGS_SIGNATURE
296e1947c75SQuentin Monnet ctx->disassemble = disassembler(info->arch,
297fb982666SRoman Gushchin bfd_big_endian(bfdf),
298e1947c75SQuentin Monnet info->mach,
299fb982666SRoman Gushchin bfdf);
300fb982666SRoman Gushchin #else
301e1947c75SQuentin Monnet ctx->disassemble = disassembler(bfdf);
302fb982666SRoman Gushchin #endif
303e1947c75SQuentin Monnet if (!ctx->disassemble) {
30455b4de58SQuentin Monnet p_err("failed to create disassembler");
305e1947c75SQuentin Monnet goto err_free;
30655b4de58SQuentin Monnet }
307e1947c75SQuentin Monnet return 0;
308e1947c75SQuentin Monnet
309e1947c75SQuentin Monnet err_free:
310e1947c75SQuentin Monnet free(info);
311e1947c75SQuentin Monnet err_close:
312e1947c75SQuentin Monnet bfd_close(ctx->bfdf);
313e1947c75SQuentin Monnet return -1;
314e1947c75SQuentin Monnet }
315e1947c75SQuentin Monnet
destroy_context(disasm_ctx_t * ctx)316e1947c75SQuentin Monnet static void destroy_context(disasm_ctx_t *ctx)
317e1947c75SQuentin Monnet {
318e1947c75SQuentin Monnet free(ctx->info);
319e1947c75SQuentin Monnet bfd_close(ctx->bfdf);
320e1947c75SQuentin Monnet }
321e1947c75SQuentin Monnet
322e1947c75SQuentin Monnet static int
disassemble_insn(disasm_ctx_t * ctx,__maybe_unused unsigned char * image,__maybe_unused ssize_t len,int pc,__maybe_unused __u64 func_ksym)323e1947c75SQuentin Monnet disassemble_insn(disasm_ctx_t *ctx, __maybe_unused unsigned char *image,
324*41f0b320SLeon Hwang __maybe_unused ssize_t len, int pc,
325*41f0b320SLeon Hwang __maybe_unused __u64 func_ksym)
326e1947c75SQuentin Monnet {
327*41f0b320SLeon Hwang return ctx->disassemble(pc, &ctx->info->info);
328e1947c75SQuentin Monnet }
329e1947c75SQuentin Monnet
disasm_init(void)330e1947c75SQuentin Monnet int disasm_init(void)
331e1947c75SQuentin Monnet {
332e1947c75SQuentin Monnet bfd_init();
333e1947c75SQuentin Monnet return 0;
334e1947c75SQuentin Monnet }
335eb9d1acfSQuentin Monnet #endif /* HAVE_LIBBPFD_SUPPORT */
336e1947c75SQuentin Monnet
disasm_print_insn(unsigned char * image,ssize_t len,int opcodes,const char * arch,const char * disassembler_options,const struct btf * btf,const struct bpf_prog_linfo * prog_linfo,__u64 func_ksym,unsigned int func_idx,bool linum)337e1947c75SQuentin Monnet int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
338e1947c75SQuentin Monnet const char *arch, const char *disassembler_options,
339e1947c75SQuentin Monnet const struct btf *btf,
340e1947c75SQuentin Monnet const struct bpf_prog_linfo *prog_linfo,
341e1947c75SQuentin Monnet __u64 func_ksym, unsigned int func_idx,
342e1947c75SQuentin Monnet bool linum)
343e1947c75SQuentin Monnet {
344e1947c75SQuentin Monnet const struct bpf_line_info *linfo = NULL;
345e1947c75SQuentin Monnet unsigned int nr_skip = 0;
346e1947c75SQuentin Monnet int count, i, pc = 0;
347e1947c75SQuentin Monnet disasm_ctx_t ctx;
348e1947c75SQuentin Monnet
349e1947c75SQuentin Monnet if (!len)
350e1947c75SQuentin Monnet return -1;
351e1947c75SQuentin Monnet
352*41f0b320SLeon Hwang if (init_context(&ctx, arch, disassembler_options, image, len, func_ksym))
353e1947c75SQuentin Monnet return -1;
35471bb428fSJakub Kicinski
355107f0412SQuentin Monnet if (json_output)
356107f0412SQuentin Monnet jsonw_start_array(json_wtr);
35771bb428fSJakub Kicinski do {
358b053b439SMartin KaFai Lau if (prog_linfo) {
359b053b439SMartin KaFai Lau linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo,
360b053b439SMartin KaFai Lau func_ksym + pc,
361b053b439SMartin KaFai Lau func_idx,
362b053b439SMartin KaFai Lau nr_skip);
363b053b439SMartin KaFai Lau if (linfo)
364b053b439SMartin KaFai Lau nr_skip++;
365b053b439SMartin KaFai Lau }
366b053b439SMartin KaFai Lau
367107f0412SQuentin Monnet if (json_output) {
368107f0412SQuentin Monnet jsonw_start_object(json_wtr);
369107f0412SQuentin Monnet oper_count = 0;
370b053b439SMartin KaFai Lau if (linfo)
371b053b439SMartin KaFai Lau btf_dump_linfo_json(btf, linfo, linum);
372107f0412SQuentin Monnet jsonw_name(json_wtr, "pc");
373107f0412SQuentin Monnet jsonw_printf(json_wtr, "\"0x%x\"", pc);
374107f0412SQuentin Monnet } else {
375b053b439SMartin KaFai Lau if (linfo)
376b053b439SMartin KaFai Lau btf_dump_linfo_plain(btf, linfo, "; ",
377b053b439SMartin KaFai Lau linum);
378eb9d1acfSQuentin Monnet printf("%4x:" DISASM_SPACER, pc);
379107f0412SQuentin Monnet }
38071bb428fSJakub Kicinski
381*41f0b320SLeon Hwang count = disassemble_insn(&ctx, image, len, pc, func_ksym);
382e1947c75SQuentin Monnet
383107f0412SQuentin Monnet if (json_output) {
384107f0412SQuentin Monnet /* Operand array, was started in fprintf_json. Before
385107f0412SQuentin Monnet * that, make sure we have a _null_ value if no operand
386107f0412SQuentin Monnet * other than operation code was present.
387107f0412SQuentin Monnet */
388107f0412SQuentin Monnet if (oper_count == 1)
389107f0412SQuentin Monnet jsonw_null(json_wtr);
390107f0412SQuentin Monnet jsonw_end_array(json_wtr);
391107f0412SQuentin Monnet }
39271bb428fSJakub Kicinski
39371bb428fSJakub Kicinski if (opcodes) {
394107f0412SQuentin Monnet if (json_output) {
395107f0412SQuentin Monnet jsonw_name(json_wtr, "opcodes");
396107f0412SQuentin Monnet jsonw_start_array(json_wtr);
397107f0412SQuentin Monnet for (i = 0; i < count; ++i)
398107f0412SQuentin Monnet jsonw_printf(json_wtr, "\"0x%02hhx\"",
399107f0412SQuentin Monnet (uint8_t)image[pc + i]);
400107f0412SQuentin Monnet jsonw_end_array(json_wtr);
401107f0412SQuentin Monnet } else {
40271bb428fSJakub Kicinski printf("\n\t");
40371bb428fSJakub Kicinski for (i = 0; i < count; ++i)
404107f0412SQuentin Monnet printf("%02x ",
405107f0412SQuentin Monnet (uint8_t)image[pc + i]);
40671bb428fSJakub Kicinski }
407107f0412SQuentin Monnet }
408107f0412SQuentin Monnet if (json_output)
409107f0412SQuentin Monnet jsonw_end_object(json_wtr);
410107f0412SQuentin Monnet else
41171bb428fSJakub Kicinski printf("\n");
41271bb428fSJakub Kicinski
41371bb428fSJakub Kicinski pc += count;
41471bb428fSJakub Kicinski } while (count > 0 && pc < len);
415107f0412SQuentin Monnet if (json_output)
416107f0412SQuentin Monnet jsonw_end_array(json_wtr);
41771bb428fSJakub Kicinski
418e1947c75SQuentin Monnet destroy_context(&ctx);
41955b4de58SQuentin Monnet
42029a9c10eSStanislav Fomichev return 0;
42129a9c10eSStanislav Fomichev }
422